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