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