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

cdns3-plat.c (7512B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Cadence USBSS DRD Driver.
      4 *
      5 * Copyright (C) 2018-2020 Cadence.
      6 * Copyright (C) 2017-2018 NXP
      7 * Copyright (C) 2019 Texas Instruments
      8 *
      9 *
     10 * Author: Peter Chen <peter.chen@nxp.com>
     11 *         Pawel Laszczak <pawell@cadence.com>
     12 *         Roger Quadros <rogerq@ti.com>
     13 */
     14
     15#include <linux/module.h>
     16#include <linux/irq.h>
     17#include <linux/kernel.h>
     18#include <linux/platform_device.h>
     19#include <linux/pm_runtime.h>
     20
     21#include "core.h"
     22#include "gadget-export.h"
     23#include "drd.h"
     24
     25static int set_phy_power_on(struct cdns *cdns)
     26{
     27	int ret;
     28
     29	ret = phy_power_on(cdns->usb2_phy);
     30	if (ret)
     31		return ret;
     32
     33	ret = phy_power_on(cdns->usb3_phy);
     34	if (ret)
     35		phy_power_off(cdns->usb2_phy);
     36
     37	return ret;
     38}
     39
     40static void set_phy_power_off(struct cdns *cdns)
     41{
     42	phy_power_off(cdns->usb3_phy);
     43	phy_power_off(cdns->usb2_phy);
     44}
     45
     46/**
     47 * cdns3_plat_probe - probe for cdns3 core device
     48 * @pdev: Pointer to cdns3 core platform device
     49 *
     50 * Returns 0 on success otherwise negative errno
     51 */
     52static int cdns3_plat_probe(struct platform_device *pdev)
     53{
     54	struct device *dev = &pdev->dev;
     55	struct resource	*res;
     56	struct cdns *cdns;
     57	void __iomem *regs;
     58	int ret;
     59
     60	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
     61	if (!cdns)
     62		return -ENOMEM;
     63
     64	cdns->dev = dev;
     65	cdns->pdata = dev_get_platdata(dev);
     66
     67	platform_set_drvdata(pdev, cdns);
     68
     69	ret = platform_get_irq_byname(pdev, "host");
     70	if (ret < 0)
     71		return ret;
     72
     73	cdns->xhci_res[0].start = ret;
     74	cdns->xhci_res[0].end = ret;
     75	cdns->xhci_res[0].flags = IORESOURCE_IRQ | irq_get_trigger_type(ret);
     76	cdns->xhci_res[0].name = "host";
     77
     78	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "xhci");
     79	if (!res) {
     80		dev_err(dev, "couldn't get xhci resource\n");
     81		return -ENXIO;
     82	}
     83
     84	cdns->xhci_res[1] = *res;
     85
     86	cdns->dev_irq = platform_get_irq_byname(pdev, "peripheral");
     87
     88	if (cdns->dev_irq < 0)
     89		return cdns->dev_irq;
     90
     91	regs = devm_platform_ioremap_resource_byname(pdev, "dev");
     92	if (IS_ERR(regs))
     93		return PTR_ERR(regs);
     94	cdns->dev_regs	= regs;
     95
     96	cdns->otg_irq = platform_get_irq_byname(pdev, "otg");
     97	if (cdns->otg_irq < 0)
     98		return cdns->otg_irq;
     99
    100	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg");
    101	if (!res) {
    102		dev_err(dev, "couldn't get otg resource\n");
    103		return -ENXIO;
    104	}
    105
    106	cdns->phyrst_a_enable = device_property_read_bool(dev, "cdns,phyrst-a-enable");
    107
    108	cdns->otg_res = *res;
    109
    110	cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup");
    111	if (cdns->wakeup_irq == -EPROBE_DEFER)
    112		return cdns->wakeup_irq;
    113	else if (cdns->wakeup_irq == 0)
    114		return -EINVAL;
    115
    116	if (cdns->wakeup_irq < 0) {
    117		dev_dbg(dev, "couldn't get wakeup irq\n");
    118		cdns->wakeup_irq = 0x0;
    119	}
    120
    121	cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy");
    122	if (IS_ERR(cdns->usb2_phy))
    123		return PTR_ERR(cdns->usb2_phy);
    124
    125	ret = phy_init(cdns->usb2_phy);
    126	if (ret)
    127		return ret;
    128
    129	cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy");
    130	if (IS_ERR(cdns->usb3_phy))
    131		return PTR_ERR(cdns->usb3_phy);
    132
    133	ret = phy_init(cdns->usb3_phy);
    134	if (ret)
    135		goto err_phy3_init;
    136
    137	ret = set_phy_power_on(cdns);
    138	if (ret)
    139		goto err_phy_power_on;
    140
    141	cdns->gadget_init = cdns3_gadget_init;
    142
    143	ret = cdns_init(cdns);
    144	if (ret)
    145		goto err_cdns_init;
    146
    147	device_set_wakeup_capable(dev, true);
    148	pm_runtime_set_active(dev);
    149	pm_runtime_enable(dev);
    150	if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)))
    151		pm_runtime_forbid(dev);
    152
    153	/*
    154	 * The controller needs less time between bus and controller suspend,
    155	 * and we also needs a small delay to avoid frequently entering low
    156	 * power mode.
    157	 */
    158	pm_runtime_set_autosuspend_delay(dev, 20);
    159	pm_runtime_mark_last_busy(dev);
    160	pm_runtime_use_autosuspend(dev);
    161
    162	return 0;
    163
    164err_cdns_init:
    165	set_phy_power_off(cdns);
    166err_phy_power_on:
    167	phy_exit(cdns->usb3_phy);
    168err_phy3_init:
    169	phy_exit(cdns->usb2_phy);
    170
    171	return ret;
    172}
    173
    174/**
    175 * cdns3_plat_remove() - unbind drd driver and clean up
    176 * @pdev: Pointer to Linux platform device
    177 *
    178 * Returns 0 on success otherwise negative errno
    179 */
    180static int cdns3_plat_remove(struct platform_device *pdev)
    181{
    182	struct cdns *cdns = platform_get_drvdata(pdev);
    183	struct device *dev = cdns->dev;
    184
    185	pm_runtime_get_sync(dev);
    186	pm_runtime_disable(dev);
    187	pm_runtime_put_noidle(dev);
    188	cdns_remove(cdns);
    189	set_phy_power_off(cdns);
    190	phy_exit(cdns->usb2_phy);
    191	phy_exit(cdns->usb3_phy);
    192	return 0;
    193}
    194
    195#ifdef CONFIG_PM
    196
    197static int cdns3_set_platform_suspend(struct device *dev,
    198				      bool suspend, bool wakeup)
    199{
    200	struct cdns *cdns = dev_get_drvdata(dev);
    201	int ret = 0;
    202
    203	if (cdns->pdata && cdns->pdata->platform_suspend)
    204		ret = cdns->pdata->platform_suspend(dev, suspend, wakeup);
    205
    206	return ret;
    207}
    208
    209static int cdns3_controller_suspend(struct device *dev, pm_message_t msg)
    210{
    211	struct cdns *cdns = dev_get_drvdata(dev);
    212	bool wakeup;
    213	unsigned long flags;
    214
    215	if (cdns->in_lpm)
    216		return 0;
    217
    218	if (PMSG_IS_AUTO(msg))
    219		wakeup = true;
    220	else
    221		wakeup = device_may_wakeup(dev);
    222
    223	cdns3_set_platform_suspend(cdns->dev, true, wakeup);
    224	set_phy_power_off(cdns);
    225	spin_lock_irqsave(&cdns->lock, flags);
    226	cdns->in_lpm = true;
    227	spin_unlock_irqrestore(&cdns->lock, flags);
    228	dev_dbg(cdns->dev, "%s ends\n", __func__);
    229
    230	return 0;
    231}
    232
    233static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
    234{
    235	struct cdns *cdns = dev_get_drvdata(dev);
    236	int ret;
    237	unsigned long flags;
    238
    239	if (!cdns->in_lpm)
    240		return 0;
    241
    242	if (cdns_power_is_lost(cdns)) {
    243		phy_exit(cdns->usb2_phy);
    244		ret = phy_init(cdns->usb2_phy);
    245		if (ret)
    246			return ret;
    247
    248		phy_exit(cdns->usb3_phy);
    249		ret = phy_init(cdns->usb3_phy);
    250		if (ret)
    251			return ret;
    252	}
    253
    254	ret = set_phy_power_on(cdns);
    255	if (ret)
    256		return ret;
    257
    258	cdns3_set_platform_suspend(cdns->dev, false, false);
    259
    260	spin_lock_irqsave(&cdns->lock, flags);
    261	cdns_resume(cdns, !PMSG_IS_AUTO(msg));
    262	cdns->in_lpm = false;
    263	spin_unlock_irqrestore(&cdns->lock, flags);
    264	if (cdns->wakeup_pending) {
    265		cdns->wakeup_pending = false;
    266		enable_irq(cdns->wakeup_irq);
    267	}
    268	dev_dbg(cdns->dev, "%s ends\n", __func__);
    269
    270	return ret;
    271}
    272
    273static int cdns3_plat_runtime_suspend(struct device *dev)
    274{
    275	return cdns3_controller_suspend(dev, PMSG_AUTO_SUSPEND);
    276}
    277
    278static int cdns3_plat_runtime_resume(struct device *dev)
    279{
    280	return cdns3_controller_resume(dev, PMSG_AUTO_RESUME);
    281}
    282
    283#ifdef CONFIG_PM_SLEEP
    284
    285static int cdns3_plat_suspend(struct device *dev)
    286{
    287	struct cdns *cdns = dev_get_drvdata(dev);
    288	int ret;
    289
    290	cdns_suspend(cdns);
    291
    292	ret = cdns3_controller_suspend(dev, PMSG_SUSPEND);
    293	if (ret)
    294		return ret;
    295
    296	if (device_may_wakeup(dev) && cdns->wakeup_irq)
    297		enable_irq_wake(cdns->wakeup_irq);
    298
    299	return ret;
    300}
    301
    302static int cdns3_plat_resume(struct device *dev)
    303{
    304	return cdns3_controller_resume(dev, PMSG_RESUME);
    305}
    306#endif /* CONFIG_PM_SLEEP */
    307#endif /* CONFIG_PM */
    308
    309static const struct dev_pm_ops cdns3_pm_ops = {
    310	SET_SYSTEM_SLEEP_PM_OPS(cdns3_plat_suspend, cdns3_plat_resume)
    311	SET_RUNTIME_PM_OPS(cdns3_plat_runtime_suspend,
    312			   cdns3_plat_runtime_resume, NULL)
    313};
    314
    315#ifdef CONFIG_OF
    316static const struct of_device_id of_cdns3_match[] = {
    317	{ .compatible = "cdns,usb3" },
    318	{ },
    319};
    320MODULE_DEVICE_TABLE(of, of_cdns3_match);
    321#endif
    322
    323static struct platform_driver cdns3_driver = {
    324	.probe		= cdns3_plat_probe,
    325	.remove		= cdns3_plat_remove,
    326	.driver		= {
    327		.name	= "cdns-usb3",
    328		.of_match_table	= of_match_ptr(of_cdns3_match),
    329		.pm	= &cdns3_pm_ops,
    330	},
    331};
    332
    333module_platform_driver(cdns3_driver);
    334
    335MODULE_ALIAS("platform:cdns3");
    336MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
    337MODULE_LICENSE("GPL v2");
    338MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");