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