1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * dwc3-imx8mp.c - NXP imx8mp Specific Glue layer 4 * 5 * Copyright (c) 2020 NXP. 6 */ 7 8 #include <linux/clk.h> 9 #include <linux/interrupt.h> 10 #include <linux/io.h> 11 #include <linux/kernel.h> 12 #include <linux/module.h> 13 #include <linux/of_platform.h> 14 #include <linux/platform_device.h> 15 #include <linux/pm_runtime.h> 16 17 #include "core.h" 18 19 /* USB wakeup registers */ 20 #define USB_WAKEUP_CTRL 0x00 21 22 /* Global wakeup interrupt enable, also used to clear interrupt */ 23 #define USB_WAKEUP_EN BIT(31) 24 /* Wakeup from connect or disconnect, only for superspeed */ 25 #define USB_WAKEUP_SS_CONN BIT(5) 26 /* 0 select vbus_valid, 1 select sessvld */ 27 #define USB_WAKEUP_VBUS_SRC_SESS_VAL BIT(4) 28 /* Enable signal for wake up from u3 state */ 29 #define USB_WAKEUP_U3_EN BIT(3) 30 /* Enable signal for wake up from id change */ 31 #define USB_WAKEUP_ID_EN BIT(2) 32 /* Enable signal for wake up from vbus change */ 33 #define USB_WAKEUP_VBUS_EN BIT(1) 34 /* Enable signal for wake up from dp/dm change */ 35 #define USB_WAKEUP_DPDM_EN BIT(0) 36 37 #define USB_WAKEUP_EN_MASK GENMASK(5, 0) 38 39 /* USB glue registers */ 40 #define USB_CTRL0 0x00 41 #define USB_CTRL1 0x04 42 43 #define USB_CTRL0_PORTPWR_EN BIT(12) /* 1 - PPC enabled (default) */ 44 #define USB_CTRL0_USB3_FIXED BIT(22) /* 1 - USB3 permanent attached */ 45 #define USB_CTRL0_USB2_FIXED BIT(23) /* 1 - USB2 permanent attached */ 46 47 #define USB_CTRL1_OC_POLARITY BIT(16) /* 0 - HIGH / 1 - LOW */ 48 #define USB_CTRL1_PWR_POLARITY BIT(17) /* 0 - HIGH / 1 - LOW */ 49 50 struct dwc3_imx8mp { 51 struct device *dev; 52 struct platform_device *dwc3; 53 void __iomem *hsio_blk_base; 54 void __iomem *glue_base; 55 struct clk *hsio_clk; 56 struct clk *suspend_clk; 57 int irq; 58 bool pm_suspended; 59 bool wakeup_pending; 60 }; 61 62 static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx) 63 { 64 struct device *dev = dwc3_imx->dev; 65 u32 value; 66 67 if (!dwc3_imx->glue_base) 68 return; 69 70 value = readl(dwc3_imx->glue_base + USB_CTRL0); 71 72 if (device_property_read_bool(dev, "fsl,permanently-attached")) 73 value |= (USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED); 74 else 75 value &= ~(USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED); 76 77 if (device_property_read_bool(dev, "fsl,disable-port-power-control")) 78 value &= ~(USB_CTRL0_PORTPWR_EN); 79 else 80 value |= USB_CTRL0_PORTPWR_EN; 81 82 writel(value, dwc3_imx->glue_base + USB_CTRL0); 83 84 value = readl(dwc3_imx->glue_base + USB_CTRL1); 85 if (device_property_read_bool(dev, "fsl,over-current-active-low")) 86 value |= USB_CTRL1_OC_POLARITY; 87 else 88 value &= ~USB_CTRL1_OC_POLARITY; 89 90 if (device_property_read_bool(dev, "fsl,power-active-low")) 91 value |= USB_CTRL1_PWR_POLARITY; 92 else 93 value &= ~USB_CTRL1_PWR_POLARITY; 94 95 writel(value, dwc3_imx->glue_base + USB_CTRL1); 96 } 97 98 static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx) 99 { 100 struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3); 101 u32 val; 102 103 if (!dwc3) 104 return; 105 106 val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL); 107 108 if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->xhci) 109 val |= USB_WAKEUP_EN | USB_WAKEUP_SS_CONN | 110 USB_WAKEUP_U3_EN | USB_WAKEUP_DPDM_EN; 111 else if (dwc3->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) 112 val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN | 113 USB_WAKEUP_VBUS_SRC_SESS_VAL; 114 115 writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL); 116 } 117 118 static void dwc3_imx8mp_wakeup_disable(struct dwc3_imx8mp *dwc3_imx) 119 { 120 u32 val; 121 122 val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL); 123 val &= ~(USB_WAKEUP_EN | USB_WAKEUP_EN_MASK); 124 writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL); 125 } 126 127 static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx) 128 { 129 struct dwc3_imx8mp *dwc3_imx = _dwc3_imx; 130 struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3); 131 132 if (!dwc3_imx->pm_suspended) 133 return IRQ_HANDLED; 134 135 disable_irq_nosync(dwc3_imx->irq); 136 dwc3_imx->wakeup_pending = true; 137 138 if ((dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc->xhci) 139 pm_runtime_resume(&dwc->xhci->dev); 140 else if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) 141 pm_runtime_get(dwc->dev); 142 143 return IRQ_HANDLED; 144 } 145 146 static int dwc3_imx8mp_probe(struct platform_device *pdev) 147 { 148 struct device *dev = &pdev->dev; 149 struct device_node *dwc3_np, *node = dev->of_node; 150 struct dwc3_imx8mp *dwc3_imx; 151 struct resource *res; 152 int err, irq; 153 154 if (!node) { 155 dev_err(dev, "device node not found\n"); 156 return -EINVAL; 157 } 158 159 dwc3_imx = devm_kzalloc(dev, sizeof(*dwc3_imx), GFP_KERNEL); 160 if (!dwc3_imx) 161 return -ENOMEM; 162 163 platform_set_drvdata(pdev, dwc3_imx); 164 165 dwc3_imx->dev = dev; 166 167 dwc3_imx->hsio_blk_base = devm_platform_ioremap_resource(pdev, 0); 168 if (IS_ERR(dwc3_imx->hsio_blk_base)) 169 return PTR_ERR(dwc3_imx->hsio_blk_base); 170 171 res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 172 if (!res) { 173 dev_warn(dev, "Base address for glue layer missing. Continuing without, some features are missing though."); 174 } else { 175 dwc3_imx->glue_base = devm_ioremap_resource(dev, res); 176 if (IS_ERR(dwc3_imx->glue_base)) 177 return PTR_ERR(dwc3_imx->glue_base); 178 } 179 180 dwc3_imx->hsio_clk = devm_clk_get(dev, "hsio"); 181 if (IS_ERR(dwc3_imx->hsio_clk)) { 182 err = PTR_ERR(dwc3_imx->hsio_clk); 183 dev_err(dev, "Failed to get hsio clk, err=%d\n", err); 184 return err; 185 } 186 187 err = clk_prepare_enable(dwc3_imx->hsio_clk); 188 if (err) { 189 dev_err(dev, "Failed to enable hsio clk, err=%d\n", err); 190 return err; 191 } 192 193 dwc3_imx->suspend_clk = devm_clk_get(dev, "suspend"); 194 if (IS_ERR(dwc3_imx->suspend_clk)) { 195 err = PTR_ERR(dwc3_imx->suspend_clk); 196 dev_err(dev, "Failed to get suspend clk, err=%d\n", err); 197 goto disable_hsio_clk; 198 } 199 200 err = clk_prepare_enable(dwc3_imx->suspend_clk); 201 if (err) { 202 dev_err(dev, "Failed to enable suspend clk, err=%d\n", err); 203 goto disable_hsio_clk; 204 } 205 206 irq = platform_get_irq(pdev, 0); 207 if (irq < 0) { 208 err = irq; 209 goto disable_clks; 210 } 211 dwc3_imx->irq = irq; 212 213 imx8mp_configure_glue(dwc3_imx); 214 215 pm_runtime_set_active(dev); 216 pm_runtime_enable(dev); 217 err = pm_runtime_get_sync(dev); 218 if (err < 0) 219 goto disable_rpm; 220 221 dwc3_np = of_get_compatible_child(node, "snps,dwc3"); 222 if (!dwc3_np) { 223 err = -ENODEV; 224 dev_err(dev, "failed to find dwc3 core child\n"); 225 goto disable_rpm; 226 } 227 228 err = of_platform_populate(node, NULL, NULL, dev); 229 if (err) { 230 dev_err(&pdev->dev, "failed to create dwc3 core\n"); 231 goto err_node_put; 232 } 233 234 dwc3_imx->dwc3 = of_find_device_by_node(dwc3_np); 235 if (!dwc3_imx->dwc3) { 236 dev_err(dev, "failed to get dwc3 platform device\n"); 237 err = -ENODEV; 238 goto depopulate; 239 } 240 of_node_put(dwc3_np); 241 242 err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt, 243 IRQF_ONESHOT, dev_name(dev), dwc3_imx); 244 if (err) { 245 dev_err(dev, "failed to request IRQ #%d --> %d\n", irq, err); 246 goto depopulate; 247 } 248 249 device_set_wakeup_capable(dev, true); 250 pm_runtime_put(dev); 251 252 return 0; 253 254 depopulate: 255 of_platform_depopulate(dev); 256 err_node_put: 257 of_node_put(dwc3_np); 258 disable_rpm: 259 pm_runtime_disable(dev); 260 pm_runtime_put_noidle(dev); 261 disable_clks: 262 clk_disable_unprepare(dwc3_imx->suspend_clk); 263 disable_hsio_clk: 264 clk_disable_unprepare(dwc3_imx->hsio_clk); 265 266 return err; 267 } 268 269 static int dwc3_imx8mp_remove(struct platform_device *pdev) 270 { 271 struct dwc3_imx8mp *dwc3_imx = platform_get_drvdata(pdev); 272 struct device *dev = &pdev->dev; 273 274 pm_runtime_get_sync(dev); 275 of_platform_depopulate(dev); 276 277 clk_disable_unprepare(dwc3_imx->suspend_clk); 278 clk_disable_unprepare(dwc3_imx->hsio_clk); 279 280 pm_runtime_disable(dev); 281 pm_runtime_put_noidle(dev); 282 platform_set_drvdata(pdev, NULL); 283 284 return 0; 285 } 286 287 static int __maybe_unused dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx, 288 pm_message_t msg) 289 { 290 if (dwc3_imx->pm_suspended) 291 return 0; 292 293 /* Wakeup enable */ 294 if (PMSG_IS_AUTO(msg) || device_may_wakeup(dwc3_imx->dev)) 295 dwc3_imx8mp_wakeup_enable(dwc3_imx); 296 297 dwc3_imx->pm_suspended = true; 298 299 return 0; 300 } 301 302 static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, 303 pm_message_t msg) 304 { 305 struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3); 306 int ret = 0; 307 308 if (!dwc3_imx->pm_suspended) 309 return 0; 310 311 /* Wakeup disable */ 312 dwc3_imx8mp_wakeup_disable(dwc3_imx); 313 dwc3_imx->pm_suspended = false; 314 315 /* Upon power loss any previous configuration is lost, restore it */ 316 imx8mp_configure_glue(dwc3_imx); 317 318 if (dwc3_imx->wakeup_pending) { 319 dwc3_imx->wakeup_pending = false; 320 if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) { 321 pm_runtime_mark_last_busy(dwc->dev); 322 pm_runtime_put_autosuspend(dwc->dev); 323 } else { 324 /* 325 * Add wait for xhci switch from suspend 326 * clock to normal clock to detect connection. 327 */ 328 usleep_range(9000, 10000); 329 } 330 enable_irq(dwc3_imx->irq); 331 } 332 333 return ret; 334 } 335 336 static int __maybe_unused dwc3_imx8mp_pm_suspend(struct device *dev) 337 { 338 struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev); 339 int ret; 340 341 ret = dwc3_imx8mp_suspend(dwc3_imx, PMSG_SUSPEND); 342 343 if (device_may_wakeup(dwc3_imx->dev)) 344 enable_irq_wake(dwc3_imx->irq); 345 else 346 clk_disable_unprepare(dwc3_imx->suspend_clk); 347 348 clk_disable_unprepare(dwc3_imx->hsio_clk); 349 dev_dbg(dev, "dwc3 imx8mp pm suspend.\n"); 350 351 return ret; 352 } 353 354 static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev) 355 { 356 struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev); 357 int ret; 358 359 if (device_may_wakeup(dwc3_imx->dev)) { 360 disable_irq_wake(dwc3_imx->irq); 361 } else { 362 ret = clk_prepare_enable(dwc3_imx->suspend_clk); 363 if (ret) 364 return ret; 365 } 366 367 ret = clk_prepare_enable(dwc3_imx->hsio_clk); 368 if (ret) 369 return ret; 370 371 ret = dwc3_imx8mp_resume(dwc3_imx, PMSG_RESUME); 372 373 pm_runtime_disable(dev); 374 pm_runtime_set_active(dev); 375 pm_runtime_enable(dev); 376 377 dev_dbg(dev, "dwc3 imx8mp pm resume.\n"); 378 379 return ret; 380 } 381 382 static int __maybe_unused dwc3_imx8mp_runtime_suspend(struct device *dev) 383 { 384 struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev); 385 386 dev_dbg(dev, "dwc3 imx8mp runtime suspend.\n"); 387 388 return dwc3_imx8mp_suspend(dwc3_imx, PMSG_AUTO_SUSPEND); 389 } 390 391 static int __maybe_unused dwc3_imx8mp_runtime_resume(struct device *dev) 392 { 393 struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev); 394 395 dev_dbg(dev, "dwc3 imx8mp runtime resume.\n"); 396 397 return dwc3_imx8mp_resume(dwc3_imx, PMSG_AUTO_RESUME); 398 } 399 400 static const struct dev_pm_ops dwc3_imx8mp_dev_pm_ops = { 401 SET_SYSTEM_SLEEP_PM_OPS(dwc3_imx8mp_pm_suspend, dwc3_imx8mp_pm_resume) 402 SET_RUNTIME_PM_OPS(dwc3_imx8mp_runtime_suspend, 403 dwc3_imx8mp_runtime_resume, NULL) 404 }; 405 406 static const struct of_device_id dwc3_imx8mp_of_match[] = { 407 { .compatible = "fsl,imx8mp-dwc3", }, 408 {}, 409 }; 410 MODULE_DEVICE_TABLE(of, dwc3_imx8mp_of_match); 411 412 static struct platform_driver dwc3_imx8mp_driver = { 413 .probe = dwc3_imx8mp_probe, 414 .remove = dwc3_imx8mp_remove, 415 .driver = { 416 .name = "imx8mp-dwc3", 417 .pm = &dwc3_imx8mp_dev_pm_ops, 418 .of_match_table = dwc3_imx8mp_of_match, 419 }, 420 }; 421 422 module_platform_driver(dwc3_imx8mp_driver); 423 424 MODULE_ALIAS("platform:imx8mp-dwc3"); 425 MODULE_AUTHOR("jun.li@nxp.com"); 426 MODULE_LICENSE("GPL v2"); 427 MODULE_DESCRIPTION("DesignWare USB3 imx8mp Glue Layer"); 428