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