cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ohci-st.c (7679B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * ST OHCI driver
      4 *
      5 * Copyright (C) 2014 STMicroelectronics – All Rights Reserved
      6 *
      7 * Author: Peter Griffin <peter.griffin@linaro.org>
      8 *
      9 * Derived from ohci-platform.c
     10 */
     11
     12#include <linux/clk.h>
     13#include <linux/dma-mapping.h>
     14#include <linux/hrtimer.h>
     15#include <linux/io.h>
     16#include <linux/kernel.h>
     17#include <linux/module.h>
     18#include <linux/err.h>
     19#include <linux/phy/phy.h>
     20#include <linux/platform_device.h>
     21#include <linux/reset.h>
     22#include <linux/usb/ohci_pdriver.h>
     23#include <linux/usb.h>
     24#include <linux/usb/hcd.h>
     25
     26#include "ohci.h"
     27
     28#define USB_MAX_CLKS 3
     29
     30struct st_ohci_platform_priv {
     31	struct clk *clks[USB_MAX_CLKS];
     32	struct clk *clk48;
     33	struct reset_control *rst;
     34	struct reset_control *pwr;
     35	struct phy *phy;
     36};
     37
     38#define DRIVER_DESC "OHCI STMicroelectronics driver"
     39
     40#define hcd_to_ohci_priv(h) \
     41	((struct st_ohci_platform_priv *)hcd_to_ohci(h)->priv)
     42
     43static const char hcd_name[] = "ohci-st";
     44
     45static int st_ohci_platform_power_on(struct platform_device *dev)
     46{
     47	struct usb_hcd *hcd = platform_get_drvdata(dev);
     48	struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
     49	int clk, ret;
     50
     51	ret = reset_control_deassert(priv->pwr);
     52	if (ret)
     53		return ret;
     54
     55	ret = reset_control_deassert(priv->rst);
     56	if (ret)
     57		goto err_assert_power;
     58
     59	/* some SoCs don't have a dedicated 48Mhz clock, but those that do
     60	   need the rate to be explicitly set */
     61	if (priv->clk48) {
     62		ret = clk_set_rate(priv->clk48, 48000000);
     63		if (ret)
     64			goto err_assert_reset;
     65	}
     66
     67	for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++) {
     68		ret = clk_prepare_enable(priv->clks[clk]);
     69		if (ret)
     70			goto err_disable_clks;
     71	}
     72
     73	ret = phy_init(priv->phy);
     74	if (ret)
     75		goto err_disable_clks;
     76
     77	ret = phy_power_on(priv->phy);
     78	if (ret)
     79		goto err_exit_phy;
     80
     81	return 0;
     82
     83err_exit_phy:
     84	phy_exit(priv->phy);
     85err_disable_clks:
     86	while (--clk >= 0)
     87		clk_disable_unprepare(priv->clks[clk]);
     88err_assert_reset:
     89	reset_control_assert(priv->rst);
     90err_assert_power:
     91	reset_control_assert(priv->pwr);
     92
     93	return ret;
     94}
     95
     96static void st_ohci_platform_power_off(struct platform_device *dev)
     97{
     98	struct usb_hcd *hcd = platform_get_drvdata(dev);
     99	struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
    100
    101	int clk;
    102
    103	reset_control_assert(priv->pwr);
    104
    105	reset_control_assert(priv->rst);
    106
    107	phy_power_off(priv->phy);
    108
    109	phy_exit(priv->phy);
    110
    111	for (clk = USB_MAX_CLKS - 1; clk >= 0; clk--)
    112		if (priv->clks[clk])
    113			clk_disable_unprepare(priv->clks[clk]);
    114}
    115
    116static struct hc_driver __read_mostly ohci_platform_hc_driver;
    117
    118static const struct ohci_driver_overrides platform_overrides __initconst = {
    119	.product_desc =		"ST OHCI controller",
    120	.extra_priv_size =	sizeof(struct st_ohci_platform_priv),
    121};
    122
    123static struct usb_ohci_pdata ohci_platform_defaults = {
    124	.power_on =		st_ohci_platform_power_on,
    125	.power_suspend =	st_ohci_platform_power_off,
    126	.power_off =		st_ohci_platform_power_off,
    127};
    128
    129static int st_ohci_platform_probe(struct platform_device *dev)
    130{
    131	struct usb_hcd *hcd;
    132	struct resource *res_mem;
    133	struct usb_ohci_pdata *pdata = &ohci_platform_defaults;
    134	struct st_ohci_platform_priv *priv;
    135	int err, irq, clk = 0;
    136
    137	if (usb_disabled())
    138		return -ENODEV;
    139
    140	irq = platform_get_irq(dev, 0);
    141	if (irq < 0)
    142		return irq;
    143
    144	res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
    145	if (!res_mem) {
    146		dev_err(&dev->dev, "no memory resource provided");
    147		return -ENXIO;
    148	}
    149
    150	hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
    151			dev_name(&dev->dev));
    152	if (!hcd)
    153		return -ENOMEM;
    154
    155	platform_set_drvdata(dev, hcd);
    156	dev->dev.platform_data = pdata;
    157	priv = hcd_to_ohci_priv(hcd);
    158
    159	priv->phy = devm_phy_get(&dev->dev, "usb");
    160	if (IS_ERR(priv->phy)) {
    161		err = PTR_ERR(priv->phy);
    162		goto err_put_hcd;
    163	}
    164
    165	for (clk = 0; clk < USB_MAX_CLKS; clk++) {
    166		priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
    167		if (IS_ERR(priv->clks[clk])) {
    168			err = PTR_ERR(priv->clks[clk]);
    169			if (err == -EPROBE_DEFER)
    170				goto err_put_clks;
    171			priv->clks[clk] = NULL;
    172			break;
    173		}
    174	}
    175
    176	/* some SoCs don't have a dedicated 48Mhz clock, but those that
    177	   do need the rate to be explicitly set */
    178	priv->clk48 = devm_clk_get(&dev->dev, "clk48");
    179	if (IS_ERR(priv->clk48)) {
    180		dev_info(&dev->dev, "48MHz clk not found\n");
    181		priv->clk48 = NULL;
    182	}
    183
    184	priv->pwr =
    185		devm_reset_control_get_optional_shared(&dev->dev, "power");
    186	if (IS_ERR(priv->pwr)) {
    187		err = PTR_ERR(priv->pwr);
    188		goto err_put_clks;
    189	}
    190
    191	priv->rst =
    192		devm_reset_control_get_optional_shared(&dev->dev, "softreset");
    193	if (IS_ERR(priv->rst)) {
    194		err = PTR_ERR(priv->rst);
    195		goto err_put_clks;
    196	}
    197
    198	if (pdata->power_on) {
    199		err = pdata->power_on(dev);
    200		if (err < 0)
    201			goto err_power;
    202	}
    203
    204	hcd->rsrc_start = res_mem->start;
    205	hcd->rsrc_len = resource_size(res_mem);
    206
    207	hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
    208	if (IS_ERR(hcd->regs)) {
    209		err = PTR_ERR(hcd->regs);
    210		goto err_power;
    211	}
    212	err = usb_add_hcd(hcd, irq, IRQF_SHARED);
    213	if (err)
    214		goto err_power;
    215
    216	device_wakeup_enable(hcd->self.controller);
    217
    218	platform_set_drvdata(dev, hcd);
    219
    220	return err;
    221
    222err_power:
    223	if (pdata->power_off)
    224		pdata->power_off(dev);
    225
    226err_put_clks:
    227	while (--clk >= 0)
    228		clk_put(priv->clks[clk]);
    229err_put_hcd:
    230	if (pdata == &ohci_platform_defaults)
    231		dev->dev.platform_data = NULL;
    232
    233	usb_put_hcd(hcd);
    234
    235	return err;
    236}
    237
    238static int st_ohci_platform_remove(struct platform_device *dev)
    239{
    240	struct usb_hcd *hcd = platform_get_drvdata(dev);
    241	struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
    242	struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
    243	int clk;
    244
    245	usb_remove_hcd(hcd);
    246
    247	if (pdata->power_off)
    248		pdata->power_off(dev);
    249
    250
    251	for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++)
    252		clk_put(priv->clks[clk]);
    253
    254	usb_put_hcd(hcd);
    255
    256	if (pdata == &ohci_platform_defaults)
    257		dev->dev.platform_data = NULL;
    258
    259	return 0;
    260}
    261
    262#ifdef CONFIG_PM_SLEEP
    263
    264static int st_ohci_suspend(struct device *dev)
    265{
    266	struct usb_hcd *hcd = dev_get_drvdata(dev);
    267	struct usb_ohci_pdata *pdata = dev->platform_data;
    268	struct platform_device *pdev = to_platform_device(dev);
    269	bool do_wakeup = device_may_wakeup(dev);
    270	int ret;
    271
    272	ret = ohci_suspend(hcd, do_wakeup);
    273	if (ret)
    274		return ret;
    275
    276	if (pdata->power_suspend)
    277		pdata->power_suspend(pdev);
    278
    279	return ret;
    280}
    281
    282static int st_ohci_resume(struct device *dev)
    283{
    284	struct usb_hcd *hcd = dev_get_drvdata(dev);
    285	struct usb_ohci_pdata *pdata = dev_get_platdata(dev);
    286	struct platform_device *pdev = to_platform_device(dev);
    287	int err;
    288
    289	if (pdata->power_on) {
    290		err = pdata->power_on(pdev);
    291		if (err < 0)
    292			return err;
    293	}
    294
    295	ohci_resume(hcd, false);
    296	return 0;
    297}
    298
    299static SIMPLE_DEV_PM_OPS(st_ohci_pm_ops, st_ohci_suspend, st_ohci_resume);
    300
    301#endif /* CONFIG_PM_SLEEP */
    302
    303static const struct of_device_id st_ohci_platform_ids[] = {
    304	{ .compatible = "st,st-ohci-300x", },
    305	{ /* sentinel */ }
    306};
    307MODULE_DEVICE_TABLE(of, st_ohci_platform_ids);
    308
    309static struct platform_driver ohci_platform_driver = {
    310	.probe		= st_ohci_platform_probe,
    311	.remove		= st_ohci_platform_remove,
    312	.shutdown	= usb_hcd_platform_shutdown,
    313	.driver		= {
    314		.name	= "st-ohci",
    315#ifdef CONFIG_PM_SLEEP
    316		.pm	= &st_ohci_pm_ops,
    317#endif
    318		.of_match_table = st_ohci_platform_ids,
    319	}
    320};
    321
    322static int __init ohci_platform_init(void)
    323{
    324	if (usb_disabled())
    325		return -ENODEV;
    326
    327	pr_info("%s: " DRIVER_DESC "\n", hcd_name);
    328
    329	ohci_init_driver(&ohci_platform_hc_driver, &platform_overrides);
    330	return platform_driver_register(&ohci_platform_driver);
    331}
    332module_init(ohci_platform_init);
    333
    334static void __exit ohci_platform_cleanup(void)
    335{
    336	platform_driver_unregister(&ohci_platform_driver);
    337}
    338module_exit(ohci_platform_cleanup);
    339
    340MODULE_DESCRIPTION(DRIVER_DESC);
    341MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>");
    342MODULE_LICENSE("GPL");