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