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");