.. | .. |
---|
4 | 4 | * Inspired by dwc3-of-simple.c |
---|
5 | 5 | */ |
---|
6 | 6 | |
---|
| 7 | +#include <linux/acpi.h> |
---|
7 | 8 | #include <linux/io.h> |
---|
8 | 9 | #include <linux/of.h> |
---|
9 | 10 | #include <linux/clk.h> |
---|
10 | 11 | #include <linux/irq.h> |
---|
11 | | -#include <linux/clk-provider.h> |
---|
| 12 | +#include <linux/of_clk.h> |
---|
12 | 13 | #include <linux/module.h> |
---|
13 | 14 | #include <linux/kernel.h> |
---|
14 | 15 | #include <linux/extcon.h> |
---|
| 16 | +#include <linux/interconnect.h> |
---|
15 | 17 | #include <linux/of_platform.h> |
---|
16 | 18 | #include <linux/platform_device.h> |
---|
17 | 19 | #include <linux/phy/phy.h> |
---|
.. | .. |
---|
38 | 40 | #define PWR_EVNT_LPM_IN_L2_MASK BIT(4) |
---|
39 | 41 | #define PWR_EVNT_LPM_OUT_L2_MASK BIT(5) |
---|
40 | 42 | |
---|
| 43 | +#define SDM845_QSCRATCH_BASE_OFFSET 0xf8800 |
---|
| 44 | +#define SDM845_QSCRATCH_SIZE 0x400 |
---|
| 45 | +#define SDM845_DWC3_CORE_SIZE 0xcd00 |
---|
| 46 | + |
---|
| 47 | +/* Interconnect path bandwidths in MBps */ |
---|
| 48 | +#define USB_MEMORY_AVG_HS_BW MBps_to_icc(240) |
---|
| 49 | +#define USB_MEMORY_PEAK_HS_BW MBps_to_icc(700) |
---|
| 50 | +#define USB_MEMORY_AVG_SS_BW MBps_to_icc(1000) |
---|
| 51 | +#define USB_MEMORY_PEAK_SS_BW MBps_to_icc(2500) |
---|
| 52 | +#define APPS_USB_AVG_BW 0 |
---|
| 53 | +#define APPS_USB_PEAK_BW MBps_to_icc(40) |
---|
| 54 | + |
---|
| 55 | +struct dwc3_acpi_pdata { |
---|
| 56 | + u32 qscratch_base_offset; |
---|
| 57 | + u32 qscratch_base_size; |
---|
| 58 | + u32 dwc3_core_base_size; |
---|
| 59 | + int hs_phy_irq_index; |
---|
| 60 | + int dp_hs_phy_irq_index; |
---|
| 61 | + int dm_hs_phy_irq_index; |
---|
| 62 | + int ss_phy_irq_index; |
---|
| 63 | + bool is_urs; |
---|
| 64 | +}; |
---|
| 65 | + |
---|
41 | 66 | struct dwc3_qcom { |
---|
42 | 67 | struct device *dev; |
---|
43 | 68 | void __iomem *qscratch_base; |
---|
44 | 69 | struct platform_device *dwc3; |
---|
| 70 | + struct platform_device *urs_usb; |
---|
45 | 71 | struct clk **clks; |
---|
46 | 72 | int num_clocks; |
---|
47 | 73 | struct reset_control *resets; |
---|
.. | .. |
---|
56 | 82 | struct notifier_block vbus_nb; |
---|
57 | 83 | struct notifier_block host_nb; |
---|
58 | 84 | |
---|
| 85 | + const struct dwc3_acpi_pdata *acpi_pdata; |
---|
| 86 | + |
---|
59 | 87 | enum usb_dr_mode mode; |
---|
60 | 88 | bool is_suspended; |
---|
61 | 89 | bool pm_suspended; |
---|
| 90 | + struct icc_path *icc_path_ddr; |
---|
| 91 | + struct icc_path *icc_path_apps; |
---|
62 | 92 | }; |
---|
63 | 93 | |
---|
64 | 94 | static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val) |
---|
.. | .. |
---|
85 | 115 | readl(base + offset); |
---|
86 | 116 | } |
---|
87 | 117 | |
---|
88 | | -static void dwc3_qcom_vbus_overrride_enable(struct dwc3_qcom *qcom, bool enable) |
---|
| 118 | +static void dwc3_qcom_vbus_override_enable(struct dwc3_qcom *qcom, bool enable) |
---|
89 | 119 | { |
---|
90 | 120 | if (enable) { |
---|
91 | 121 | dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_SS_PHY_CTRL, |
---|
.. | .. |
---|
106 | 136 | struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, vbus_nb); |
---|
107 | 137 | |
---|
108 | 138 | /* enable vbus override for device mode */ |
---|
109 | | - dwc3_qcom_vbus_overrride_enable(qcom, event); |
---|
| 139 | + dwc3_qcom_vbus_override_enable(qcom, event); |
---|
110 | 140 | qcom->mode = event ? USB_DR_MODE_PERIPHERAL : USB_DR_MODE_HOST; |
---|
111 | 141 | |
---|
112 | 142 | return NOTIFY_DONE; |
---|
.. | .. |
---|
118 | 148 | struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, host_nb); |
---|
119 | 149 | |
---|
120 | 150 | /* disable vbus override in host mode */ |
---|
121 | | - dwc3_qcom_vbus_overrride_enable(qcom, !event); |
---|
| 151 | + dwc3_qcom_vbus_override_enable(qcom, !event); |
---|
122 | 152 | qcom->mode = event ? USB_DR_MODE_HOST : USB_DR_MODE_PERIPHERAL; |
---|
123 | 153 | |
---|
124 | 154 | return NOTIFY_DONE; |
---|
.. | .. |
---|
173 | 203 | return 0; |
---|
174 | 204 | } |
---|
175 | 205 | |
---|
| 206 | +static int dwc3_qcom_interconnect_enable(struct dwc3_qcom *qcom) |
---|
| 207 | +{ |
---|
| 208 | + int ret; |
---|
| 209 | + |
---|
| 210 | + ret = icc_enable(qcom->icc_path_ddr); |
---|
| 211 | + if (ret) |
---|
| 212 | + return ret; |
---|
| 213 | + |
---|
| 214 | + ret = icc_enable(qcom->icc_path_apps); |
---|
| 215 | + if (ret) |
---|
| 216 | + icc_disable(qcom->icc_path_ddr); |
---|
| 217 | + |
---|
| 218 | + return ret; |
---|
| 219 | +} |
---|
| 220 | + |
---|
| 221 | +static int dwc3_qcom_interconnect_disable(struct dwc3_qcom *qcom) |
---|
| 222 | +{ |
---|
| 223 | + int ret; |
---|
| 224 | + |
---|
| 225 | + ret = icc_disable(qcom->icc_path_ddr); |
---|
| 226 | + if (ret) |
---|
| 227 | + return ret; |
---|
| 228 | + |
---|
| 229 | + ret = icc_disable(qcom->icc_path_apps); |
---|
| 230 | + if (ret) |
---|
| 231 | + icc_enable(qcom->icc_path_ddr); |
---|
| 232 | + |
---|
| 233 | + return ret; |
---|
| 234 | +} |
---|
| 235 | + |
---|
| 236 | +/** |
---|
| 237 | + * dwc3_qcom_interconnect_init() - Get interconnect path handles |
---|
| 238 | + * and set bandwidhth. |
---|
| 239 | + * @qcom: Pointer to the concerned usb core. |
---|
| 240 | + * |
---|
| 241 | + */ |
---|
| 242 | +static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom) |
---|
| 243 | +{ |
---|
| 244 | + struct device *dev = qcom->dev; |
---|
| 245 | + int ret; |
---|
| 246 | + |
---|
| 247 | + if (has_acpi_companion(dev)) |
---|
| 248 | + return 0; |
---|
| 249 | + |
---|
| 250 | + qcom->icc_path_ddr = of_icc_get(dev, "usb-ddr"); |
---|
| 251 | + if (IS_ERR(qcom->icc_path_ddr)) { |
---|
| 252 | + dev_err(dev, "failed to get usb-ddr path: %ld\n", |
---|
| 253 | + PTR_ERR(qcom->icc_path_ddr)); |
---|
| 254 | + return PTR_ERR(qcom->icc_path_ddr); |
---|
| 255 | + } |
---|
| 256 | + |
---|
| 257 | + qcom->icc_path_apps = of_icc_get(dev, "apps-usb"); |
---|
| 258 | + if (IS_ERR(qcom->icc_path_apps)) { |
---|
| 259 | + dev_err(dev, "failed to get apps-usb path: %ld\n", |
---|
| 260 | + PTR_ERR(qcom->icc_path_apps)); |
---|
| 261 | + ret = PTR_ERR(qcom->icc_path_apps); |
---|
| 262 | + goto put_path_ddr; |
---|
| 263 | + } |
---|
| 264 | + |
---|
| 265 | + if (usb_get_maximum_speed(&qcom->dwc3->dev) >= USB_SPEED_SUPER || |
---|
| 266 | + usb_get_maximum_speed(&qcom->dwc3->dev) == USB_SPEED_UNKNOWN) |
---|
| 267 | + ret = icc_set_bw(qcom->icc_path_ddr, |
---|
| 268 | + USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW); |
---|
| 269 | + else |
---|
| 270 | + ret = icc_set_bw(qcom->icc_path_ddr, |
---|
| 271 | + USB_MEMORY_AVG_HS_BW, USB_MEMORY_PEAK_HS_BW); |
---|
| 272 | + |
---|
| 273 | + if (ret) { |
---|
| 274 | + dev_err(dev, "failed to set bandwidth for usb-ddr path: %d\n", ret); |
---|
| 275 | + goto put_path_apps; |
---|
| 276 | + } |
---|
| 277 | + |
---|
| 278 | + ret = icc_set_bw(qcom->icc_path_apps, |
---|
| 279 | + APPS_USB_AVG_BW, APPS_USB_PEAK_BW); |
---|
| 280 | + if (ret) { |
---|
| 281 | + dev_err(dev, "failed to set bandwidth for apps-usb path: %d\n", ret); |
---|
| 282 | + goto put_path_apps; |
---|
| 283 | + } |
---|
| 284 | + |
---|
| 285 | + return 0; |
---|
| 286 | + |
---|
| 287 | +put_path_apps: |
---|
| 288 | + icc_put(qcom->icc_path_apps); |
---|
| 289 | +put_path_ddr: |
---|
| 290 | + icc_put(qcom->icc_path_ddr); |
---|
| 291 | + return ret; |
---|
| 292 | +} |
---|
| 293 | + |
---|
| 294 | +/** |
---|
| 295 | + * dwc3_qcom_interconnect_exit() - Release interconnect path handles |
---|
| 296 | + * @qcom: Pointer to the concerned usb core. |
---|
| 297 | + * |
---|
| 298 | + * This function is used to release interconnect path handle. |
---|
| 299 | + */ |
---|
| 300 | +static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom) |
---|
| 301 | +{ |
---|
| 302 | + icc_put(qcom->icc_path_ddr); |
---|
| 303 | + icc_put(qcom->icc_path_apps); |
---|
| 304 | +} |
---|
| 305 | + |
---|
| 306 | +/* Only usable in contexts where the role can not change. */ |
---|
| 307 | +static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom) |
---|
| 308 | +{ |
---|
| 309 | + struct dwc3 *dwc; |
---|
| 310 | + |
---|
| 311 | + /* |
---|
| 312 | + * FIXME: Fix this layering violation. |
---|
| 313 | + */ |
---|
| 314 | + dwc = platform_get_drvdata(qcom->dwc3); |
---|
| 315 | + |
---|
| 316 | + /* Core driver may not have probed yet. */ |
---|
| 317 | + if (!dwc) |
---|
| 318 | + return false; |
---|
| 319 | + |
---|
| 320 | + return dwc->xhci; |
---|
| 321 | +} |
---|
| 322 | + |
---|
176 | 323 | static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom) |
---|
177 | 324 | { |
---|
178 | 325 | if (qcom->hs_phy_irq) { |
---|
.. | .. |
---|
219 | 366 | } |
---|
220 | 367 | } |
---|
221 | 368 | |
---|
222 | | -static int dwc3_qcom_suspend(struct dwc3_qcom *qcom) |
---|
| 369 | +static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup) |
---|
223 | 370 | { |
---|
224 | 371 | u32 val; |
---|
225 | | - int i; |
---|
| 372 | + int i, ret; |
---|
226 | 373 | |
---|
227 | 374 | if (qcom->is_suspended) |
---|
228 | 375 | return 0; |
---|
.. | .. |
---|
234 | 381 | for (i = qcom->num_clocks - 1; i >= 0; i--) |
---|
235 | 382 | clk_disable_unprepare(qcom->clks[i]); |
---|
236 | 383 | |
---|
237 | | - if (device_may_wakeup(qcom->dev)) |
---|
| 384 | + ret = dwc3_qcom_interconnect_disable(qcom); |
---|
| 385 | + if (ret) |
---|
| 386 | + dev_warn(qcom->dev, "failed to disable interconnect: %d\n", ret); |
---|
| 387 | + |
---|
| 388 | + if (wakeup) |
---|
238 | 389 | dwc3_qcom_enable_interrupts(qcom); |
---|
239 | 390 | |
---|
240 | 391 | qcom->is_suspended = true; |
---|
.. | .. |
---|
242 | 393 | return 0; |
---|
243 | 394 | } |
---|
244 | 395 | |
---|
245 | | -static int dwc3_qcom_resume(struct dwc3_qcom *qcom) |
---|
| 396 | +static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup) |
---|
246 | 397 | { |
---|
247 | 398 | int ret; |
---|
248 | 399 | int i; |
---|
.. | .. |
---|
250 | 401 | if (!qcom->is_suspended) |
---|
251 | 402 | return 0; |
---|
252 | 403 | |
---|
253 | | - if (device_may_wakeup(qcom->dev)) |
---|
| 404 | + if (wakeup) |
---|
254 | 405 | dwc3_qcom_disable_interrupts(qcom); |
---|
255 | 406 | |
---|
256 | 407 | for (i = 0; i < qcom->num_clocks; i++) { |
---|
.. | .. |
---|
261 | 412 | return ret; |
---|
262 | 413 | } |
---|
263 | 414 | } |
---|
| 415 | + |
---|
| 416 | + ret = dwc3_qcom_interconnect_enable(qcom); |
---|
| 417 | + if (ret) |
---|
| 418 | + dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret); |
---|
264 | 419 | |
---|
265 | 420 | /* Clear existing events from PHY related to L2 in/out */ |
---|
266 | 421 | dwc3_qcom_setbits(qcom->qscratch_base, PWR_EVNT_IRQ_STAT_REG, |
---|
.. | .. |
---|
280 | 435 | if (qcom->pm_suspended) |
---|
281 | 436 | return IRQ_HANDLED; |
---|
282 | 437 | |
---|
283 | | - if (dwc->xhci) |
---|
| 438 | + /* |
---|
| 439 | + * This is safe as role switching is done from a freezable workqueue |
---|
| 440 | + * and the wakeup interrupts are disabled as part of resume. |
---|
| 441 | + */ |
---|
| 442 | + if (dwc3_qcom_is_host(qcom)) |
---|
284 | 443 | pm_runtime_resume(&dwc->xhci->dev); |
---|
285 | 444 | |
---|
286 | 445 | return IRQ_HANDLED; |
---|
.. | .. |
---|
303 | 462 | PIPE_UTMI_CLK_DIS); |
---|
304 | 463 | } |
---|
305 | 464 | |
---|
| 465 | +static int dwc3_qcom_get_irq(struct platform_device *pdev, |
---|
| 466 | + const char *name, int num) |
---|
| 467 | +{ |
---|
| 468 | + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); |
---|
| 469 | + struct platform_device *pdev_irq = qcom->urs_usb ? qcom->urs_usb : pdev; |
---|
| 470 | + struct device_node *np = pdev->dev.of_node; |
---|
| 471 | + int ret; |
---|
| 472 | + |
---|
| 473 | + if (np) |
---|
| 474 | + ret = platform_get_irq_byname_optional(pdev_irq, name); |
---|
| 475 | + else |
---|
| 476 | + ret = platform_get_irq_optional(pdev_irq, num); |
---|
| 477 | + |
---|
| 478 | + return ret; |
---|
| 479 | +} |
---|
| 480 | + |
---|
306 | 481 | static int dwc3_qcom_setup_irq(struct platform_device *pdev) |
---|
307 | 482 | { |
---|
308 | 483 | struct dwc3_qcom *qcom = platform_get_drvdata(pdev); |
---|
309 | | - int irq, ret; |
---|
| 484 | + const struct dwc3_acpi_pdata *pdata = qcom->acpi_pdata; |
---|
| 485 | + int irq; |
---|
| 486 | + int ret; |
---|
310 | 487 | |
---|
311 | | - irq = platform_get_irq_byname(pdev, "hs_phy_irq"); |
---|
| 488 | + irq = dwc3_qcom_get_irq(pdev, "hs_phy_irq", |
---|
| 489 | + pdata ? pdata->hs_phy_irq_index : -1); |
---|
312 | 490 | if (irq > 0) { |
---|
313 | 491 | /* Keep wakeup interrupts disabled until suspend */ |
---|
314 | 492 | irq_set_status_flags(irq, IRQ_NOAUTOEN); |
---|
.. | .. |
---|
323 | 501 | qcom->hs_phy_irq = irq; |
---|
324 | 502 | } |
---|
325 | 503 | |
---|
326 | | - irq = platform_get_irq_byname(pdev, "dp_hs_phy_irq"); |
---|
| 504 | + irq = dwc3_qcom_get_irq(pdev, "dp_hs_phy_irq", |
---|
| 505 | + pdata ? pdata->dp_hs_phy_irq_index : -1); |
---|
327 | 506 | if (irq > 0) { |
---|
328 | 507 | irq_set_status_flags(irq, IRQ_NOAUTOEN); |
---|
329 | 508 | ret = devm_request_threaded_irq(qcom->dev, irq, NULL, |
---|
.. | .. |
---|
337 | 516 | qcom->dp_hs_phy_irq = irq; |
---|
338 | 517 | } |
---|
339 | 518 | |
---|
340 | | - irq = platform_get_irq_byname(pdev, "dm_hs_phy_irq"); |
---|
| 519 | + irq = dwc3_qcom_get_irq(pdev, "dm_hs_phy_irq", |
---|
| 520 | + pdata ? pdata->dm_hs_phy_irq_index : -1); |
---|
341 | 521 | if (irq > 0) { |
---|
342 | 522 | irq_set_status_flags(irq, IRQ_NOAUTOEN); |
---|
343 | 523 | ret = devm_request_threaded_irq(qcom->dev, irq, NULL, |
---|
.. | .. |
---|
351 | 531 | qcom->dm_hs_phy_irq = irq; |
---|
352 | 532 | } |
---|
353 | 533 | |
---|
354 | | - irq = platform_get_irq_byname(pdev, "ss_phy_irq"); |
---|
| 534 | + irq = dwc3_qcom_get_irq(pdev, "ss_phy_irq", |
---|
| 535 | + pdata ? pdata->ss_phy_irq_index : -1); |
---|
355 | 536 | if (irq > 0) { |
---|
356 | 537 | irq_set_status_flags(irq, IRQ_NOAUTOEN); |
---|
357 | 538 | ret = devm_request_threaded_irq(qcom->dev, irq, NULL, |
---|
.. | .. |
---|
374 | 555 | struct device_node *np = dev->of_node; |
---|
375 | 556 | int i; |
---|
376 | 557 | |
---|
377 | | - qcom->num_clocks = count; |
---|
378 | | - |
---|
379 | | - if (!count) |
---|
| 558 | + if (!np || !count) |
---|
380 | 559 | return 0; |
---|
| 560 | + |
---|
| 561 | + if (count < 0) |
---|
| 562 | + return count; |
---|
| 563 | + |
---|
| 564 | + qcom->num_clocks = count; |
---|
381 | 565 | |
---|
382 | 566 | qcom->clks = devm_kcalloc(dev, qcom->num_clocks, |
---|
383 | 567 | sizeof(struct clk *), GFP_KERNEL); |
---|
.. | .. |
---|
412 | 596 | return 0; |
---|
413 | 597 | } |
---|
414 | 598 | |
---|
415 | | -static int dwc3_qcom_probe(struct platform_device *pdev) |
---|
| 599 | +static const struct property_entry dwc3_qcom_acpi_properties[] = { |
---|
| 600 | + PROPERTY_ENTRY_STRING("dr_mode", "host"), |
---|
| 601 | + {} |
---|
| 602 | +}; |
---|
| 603 | + |
---|
| 604 | +static int dwc3_qcom_acpi_register_core(struct platform_device *pdev) |
---|
416 | 605 | { |
---|
| 606 | + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); |
---|
| 607 | + struct device *dev = &pdev->dev; |
---|
| 608 | + struct resource *res, *child_res = NULL; |
---|
| 609 | + struct platform_device *pdev_irq = qcom->urs_usb ? qcom->urs_usb : |
---|
| 610 | + pdev; |
---|
| 611 | + int irq; |
---|
| 612 | + int ret; |
---|
| 613 | + |
---|
| 614 | + qcom->dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO); |
---|
| 615 | + if (!qcom->dwc3) |
---|
| 616 | + return -ENOMEM; |
---|
| 617 | + |
---|
| 618 | + qcom->dwc3->dev.parent = dev; |
---|
| 619 | + qcom->dwc3->dev.type = dev->type; |
---|
| 620 | + qcom->dwc3->dev.dma_mask = dev->dma_mask; |
---|
| 621 | + qcom->dwc3->dev.dma_parms = dev->dma_parms; |
---|
| 622 | + qcom->dwc3->dev.coherent_dma_mask = dev->coherent_dma_mask; |
---|
| 623 | + |
---|
| 624 | + child_res = kcalloc(2, sizeof(*child_res), GFP_KERNEL); |
---|
| 625 | + if (!child_res) |
---|
| 626 | + return -ENOMEM; |
---|
| 627 | + |
---|
| 628 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
| 629 | + if (!res) { |
---|
| 630 | + dev_err(&pdev->dev, "failed to get memory resource\n"); |
---|
| 631 | + ret = -ENODEV; |
---|
| 632 | + goto out; |
---|
| 633 | + } |
---|
| 634 | + |
---|
| 635 | + child_res[0].flags = res->flags; |
---|
| 636 | + child_res[0].start = res->start; |
---|
| 637 | + child_res[0].end = child_res[0].start + |
---|
| 638 | + qcom->acpi_pdata->dwc3_core_base_size; |
---|
| 639 | + |
---|
| 640 | + irq = platform_get_irq(pdev_irq, 0); |
---|
| 641 | + if (irq < 0) { |
---|
| 642 | + ret = irq; |
---|
| 643 | + goto out; |
---|
| 644 | + } |
---|
| 645 | + child_res[1].flags = IORESOURCE_IRQ; |
---|
| 646 | + child_res[1].start = child_res[1].end = irq; |
---|
| 647 | + |
---|
| 648 | + ret = platform_device_add_resources(qcom->dwc3, child_res, 2); |
---|
| 649 | + if (ret) { |
---|
| 650 | + dev_err(&pdev->dev, "failed to add resources\n"); |
---|
| 651 | + goto out; |
---|
| 652 | + } |
---|
| 653 | + |
---|
| 654 | + ret = platform_device_add_properties(qcom->dwc3, |
---|
| 655 | + dwc3_qcom_acpi_properties); |
---|
| 656 | + if (ret < 0) { |
---|
| 657 | + dev_err(&pdev->dev, "failed to add properties\n"); |
---|
| 658 | + goto out; |
---|
| 659 | + } |
---|
| 660 | + |
---|
| 661 | + ret = platform_device_add(qcom->dwc3); |
---|
| 662 | + if (ret) |
---|
| 663 | + dev_err(&pdev->dev, "failed to add device\n"); |
---|
| 664 | + |
---|
| 665 | +out: |
---|
| 666 | + kfree(child_res); |
---|
| 667 | + return ret; |
---|
| 668 | +} |
---|
| 669 | + |
---|
| 670 | +static int dwc3_qcom_of_register_core(struct platform_device *pdev) |
---|
| 671 | +{ |
---|
| 672 | + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); |
---|
417 | 673 | struct device_node *np = pdev->dev.of_node, *dwc3_np; |
---|
418 | 674 | struct device *dev = &pdev->dev; |
---|
| 675 | + int ret; |
---|
| 676 | + |
---|
| 677 | + dwc3_np = of_get_child_by_name(np, "dwc3"); |
---|
| 678 | + if (!dwc3_np) { |
---|
| 679 | + dev_err(dev, "failed to find dwc3 core child\n"); |
---|
| 680 | + return -ENODEV; |
---|
| 681 | + } |
---|
| 682 | + |
---|
| 683 | + ret = of_platform_populate(np, NULL, NULL, dev); |
---|
| 684 | + if (ret) { |
---|
| 685 | + dev_err(dev, "failed to register dwc3 core - %d\n", ret); |
---|
| 686 | + goto node_put; |
---|
| 687 | + } |
---|
| 688 | + |
---|
| 689 | + qcom->dwc3 = of_find_device_by_node(dwc3_np); |
---|
| 690 | + if (!qcom->dwc3) { |
---|
| 691 | + ret = -ENODEV; |
---|
| 692 | + dev_err(dev, "failed to get dwc3 platform device\n"); |
---|
| 693 | + } |
---|
| 694 | + |
---|
| 695 | +node_put: |
---|
| 696 | + of_node_put(dwc3_np); |
---|
| 697 | + |
---|
| 698 | + return ret; |
---|
| 699 | +} |
---|
| 700 | + |
---|
| 701 | +static struct platform_device * |
---|
| 702 | +dwc3_qcom_create_urs_usb_platdev(struct device *dev) |
---|
| 703 | +{ |
---|
| 704 | + struct fwnode_handle *fwh; |
---|
| 705 | + struct acpi_device *adev; |
---|
| 706 | + char name[8]; |
---|
| 707 | + int ret; |
---|
| 708 | + int id; |
---|
| 709 | + |
---|
| 710 | + /* Figure out device id */ |
---|
| 711 | + ret = sscanf(fwnode_get_name(dev->fwnode), "URS%d", &id); |
---|
| 712 | + if (!ret) |
---|
| 713 | + return NULL; |
---|
| 714 | + |
---|
| 715 | + /* Find the child using name */ |
---|
| 716 | + snprintf(name, sizeof(name), "USB%d", id); |
---|
| 717 | + fwh = fwnode_get_named_child_node(dev->fwnode, name); |
---|
| 718 | + if (!fwh) |
---|
| 719 | + return NULL; |
---|
| 720 | + |
---|
| 721 | + adev = to_acpi_device_node(fwh); |
---|
| 722 | + if (!adev) |
---|
| 723 | + return NULL; |
---|
| 724 | + |
---|
| 725 | + return acpi_create_platform_device(adev, NULL); |
---|
| 726 | +} |
---|
| 727 | + |
---|
| 728 | +static int dwc3_qcom_probe(struct platform_device *pdev) |
---|
| 729 | +{ |
---|
| 730 | + struct device_node *np = pdev->dev.of_node; |
---|
| 731 | + struct device *dev = &pdev->dev; |
---|
419 | 732 | struct dwc3_qcom *qcom; |
---|
420 | | - struct resource *res; |
---|
| 733 | + struct resource *res, *parent_res = NULL; |
---|
| 734 | + struct resource local_res; |
---|
421 | 735 | int ret, i; |
---|
422 | 736 | bool ignore_pipe_clk; |
---|
423 | 737 | |
---|
.. | .. |
---|
427 | 741 | |
---|
428 | 742 | platform_set_drvdata(pdev, qcom); |
---|
429 | 743 | qcom->dev = &pdev->dev; |
---|
| 744 | + |
---|
| 745 | + if (has_acpi_companion(dev)) { |
---|
| 746 | + qcom->acpi_pdata = acpi_device_get_match_data(dev); |
---|
| 747 | + if (!qcom->acpi_pdata) { |
---|
| 748 | + dev_err(&pdev->dev, "no supporting ACPI device data\n"); |
---|
| 749 | + return -EINVAL; |
---|
| 750 | + } |
---|
| 751 | + } |
---|
430 | 752 | |
---|
431 | 753 | qcom->resets = devm_reset_control_array_get_optional_exclusive(dev); |
---|
432 | 754 | if (IS_ERR(qcom->resets)) { |
---|
.. | .. |
---|
449 | 771 | goto reset_assert; |
---|
450 | 772 | } |
---|
451 | 773 | |
---|
452 | | - ret = dwc3_qcom_clk_init(qcom, of_count_phandle_with_args(np, |
---|
453 | | - "clocks", "#clock-cells")); |
---|
| 774 | + ret = dwc3_qcom_clk_init(qcom, of_clk_get_parent_count(np)); |
---|
454 | 775 | if (ret) { |
---|
455 | 776 | dev_err(dev, "failed to get clocks\n"); |
---|
456 | 777 | goto reset_assert; |
---|
457 | 778 | } |
---|
458 | 779 | |
---|
459 | 780 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
460 | | - qcom->qscratch_base = devm_ioremap_resource(dev, res); |
---|
| 781 | + |
---|
| 782 | + if (np) { |
---|
| 783 | + parent_res = res; |
---|
| 784 | + } else { |
---|
| 785 | + memcpy(&local_res, res, sizeof(struct resource)); |
---|
| 786 | + parent_res = &local_res; |
---|
| 787 | + |
---|
| 788 | + parent_res->start = res->start + |
---|
| 789 | + qcom->acpi_pdata->qscratch_base_offset; |
---|
| 790 | + parent_res->end = parent_res->start + |
---|
| 791 | + qcom->acpi_pdata->qscratch_base_size; |
---|
| 792 | + |
---|
| 793 | + if (qcom->acpi_pdata->is_urs) { |
---|
| 794 | + qcom->urs_usb = dwc3_qcom_create_urs_usb_platdev(dev); |
---|
| 795 | + if (IS_ERR_OR_NULL(qcom->urs_usb)) { |
---|
| 796 | + dev_err(dev, "failed to create URS USB platdev\n"); |
---|
| 797 | + if (!qcom->urs_usb) |
---|
| 798 | + ret = -ENODEV; |
---|
| 799 | + else |
---|
| 800 | + ret = PTR_ERR(qcom->urs_usb); |
---|
| 801 | + goto clk_disable; |
---|
| 802 | + } |
---|
| 803 | + } |
---|
| 804 | + } |
---|
| 805 | + |
---|
| 806 | + qcom->qscratch_base = devm_ioremap_resource(dev, parent_res); |
---|
461 | 807 | if (IS_ERR(qcom->qscratch_base)) { |
---|
462 | 808 | dev_err(dev, "failed to map qscratch, err=%d\n", ret); |
---|
463 | 809 | ret = PTR_ERR(qcom->qscratch_base); |
---|
.. | .. |
---|
465 | 811 | } |
---|
466 | 812 | |
---|
467 | 813 | ret = dwc3_qcom_setup_irq(pdev); |
---|
468 | | - if (ret) |
---|
469 | | - goto clk_disable; |
---|
470 | | - |
---|
471 | | - dwc3_np = of_get_child_by_name(np, "dwc3"); |
---|
472 | | - if (!dwc3_np) { |
---|
473 | | - dev_err(dev, "failed to find dwc3 core child\n"); |
---|
474 | | - ret = -ENODEV; |
---|
| 814 | + if (ret) { |
---|
| 815 | + dev_err(dev, "failed to setup IRQs, err=%d\n", ret); |
---|
475 | 816 | goto clk_disable; |
---|
476 | 817 | } |
---|
477 | 818 | |
---|
.. | .. |
---|
484 | 825 | if (ignore_pipe_clk) |
---|
485 | 826 | dwc3_qcom_select_utmi_clk(qcom); |
---|
486 | 827 | |
---|
487 | | - ret = of_platform_populate(np, NULL, NULL, dev); |
---|
488 | | - if (ret) { |
---|
489 | | - dev_err(dev, "failed to register dwc3 core - %d\n", ret); |
---|
490 | | - goto clk_disable; |
---|
491 | | - } |
---|
| 828 | + if (np) |
---|
| 829 | + ret = dwc3_qcom_of_register_core(pdev); |
---|
| 830 | + else |
---|
| 831 | + ret = dwc3_qcom_acpi_register_core(pdev); |
---|
492 | 832 | |
---|
493 | | - qcom->dwc3 = of_find_device_by_node(dwc3_np); |
---|
494 | | - if (!qcom->dwc3) { |
---|
495 | | - dev_err(&pdev->dev, "failed to get dwc3 platform device\n"); |
---|
496 | | - ret = -ENODEV; |
---|
| 833 | + if (ret) { |
---|
| 834 | + dev_err(dev, "failed to register DWC3 Core, err=%d\n", ret); |
---|
497 | 835 | goto depopulate; |
---|
498 | 836 | } |
---|
| 837 | + |
---|
| 838 | + ret = dwc3_qcom_interconnect_init(qcom); |
---|
| 839 | + if (ret) |
---|
| 840 | + goto depopulate; |
---|
499 | 841 | |
---|
500 | 842 | qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev); |
---|
501 | 843 | |
---|
502 | 844 | /* enable vbus override for device mode */ |
---|
503 | | - if (qcom->mode == USB_DR_MODE_PERIPHERAL) |
---|
504 | | - dwc3_qcom_vbus_overrride_enable(qcom, true); |
---|
| 845 | + if (qcom->mode != USB_DR_MODE_HOST) |
---|
| 846 | + dwc3_qcom_vbus_override_enable(qcom, true); |
---|
505 | 847 | |
---|
506 | 848 | /* register extcon to override sw_vbus on Vbus change later */ |
---|
507 | 849 | ret = dwc3_qcom_register_extcon(qcom); |
---|
508 | 850 | if (ret) |
---|
509 | | - goto depopulate; |
---|
| 851 | + goto interconnect_exit; |
---|
510 | 852 | |
---|
511 | 853 | device_init_wakeup(&pdev->dev, 1); |
---|
512 | 854 | qcom->is_suspended = false; |
---|
.. | .. |
---|
516 | 858 | |
---|
517 | 859 | return 0; |
---|
518 | 860 | |
---|
| 861 | +interconnect_exit: |
---|
| 862 | + dwc3_qcom_interconnect_exit(qcom); |
---|
519 | 863 | depopulate: |
---|
520 | | - of_platform_depopulate(&pdev->dev); |
---|
| 864 | + if (np) |
---|
| 865 | + of_platform_depopulate(&pdev->dev); |
---|
| 866 | + else |
---|
| 867 | + platform_device_put(pdev); |
---|
521 | 868 | clk_disable: |
---|
522 | 869 | for (i = qcom->num_clocks - 1; i >= 0; i--) { |
---|
523 | 870 | clk_disable_unprepare(qcom->clks[i]); |
---|
.. | .. |
---|
532 | 879 | static int dwc3_qcom_remove(struct platform_device *pdev) |
---|
533 | 880 | { |
---|
534 | 881 | struct dwc3_qcom *qcom = platform_get_drvdata(pdev); |
---|
| 882 | + struct device_node *np = pdev->dev.of_node; |
---|
535 | 883 | struct device *dev = &pdev->dev; |
---|
536 | 884 | int i; |
---|
537 | 885 | |
---|
538 | | - of_platform_depopulate(dev); |
---|
| 886 | + if (np) |
---|
| 887 | + of_platform_depopulate(&pdev->dev); |
---|
| 888 | + else |
---|
| 889 | + platform_device_put(pdev); |
---|
539 | 890 | |
---|
540 | 891 | for (i = qcom->num_clocks - 1; i >= 0; i--) { |
---|
541 | 892 | clk_disable_unprepare(qcom->clks[i]); |
---|
.. | .. |
---|
543 | 894 | } |
---|
544 | 895 | qcom->num_clocks = 0; |
---|
545 | 896 | |
---|
| 897 | + dwc3_qcom_interconnect_exit(qcom); |
---|
546 | 898 | reset_control_assert(qcom->resets); |
---|
547 | 899 | |
---|
548 | 900 | pm_runtime_allow(dev); |
---|
.. | .. |
---|
554 | 906 | static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev) |
---|
555 | 907 | { |
---|
556 | 908 | struct dwc3_qcom *qcom = dev_get_drvdata(dev); |
---|
| 909 | + bool wakeup = device_may_wakeup(dev); |
---|
557 | 910 | int ret = 0; |
---|
558 | 911 | |
---|
559 | | - ret = dwc3_qcom_suspend(qcom); |
---|
| 912 | + |
---|
| 913 | + ret = dwc3_qcom_suspend(qcom, wakeup); |
---|
560 | 914 | if (!ret) |
---|
561 | 915 | qcom->pm_suspended = true; |
---|
562 | 916 | |
---|
.. | .. |
---|
566 | 920 | static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev) |
---|
567 | 921 | { |
---|
568 | 922 | struct dwc3_qcom *qcom = dev_get_drvdata(dev); |
---|
| 923 | + bool wakeup = device_may_wakeup(dev); |
---|
569 | 924 | int ret; |
---|
570 | 925 | |
---|
571 | | - ret = dwc3_qcom_resume(qcom); |
---|
| 926 | + ret = dwc3_qcom_resume(qcom, wakeup); |
---|
572 | 927 | if (!ret) |
---|
573 | 928 | qcom->pm_suspended = false; |
---|
574 | 929 | |
---|
.. | .. |
---|
579 | 934 | { |
---|
580 | 935 | struct dwc3_qcom *qcom = dev_get_drvdata(dev); |
---|
581 | 936 | |
---|
582 | | - return dwc3_qcom_suspend(qcom); |
---|
| 937 | + return dwc3_qcom_suspend(qcom, true); |
---|
583 | 938 | } |
---|
584 | 939 | |
---|
585 | 940 | static int __maybe_unused dwc3_qcom_runtime_resume(struct device *dev) |
---|
586 | 941 | { |
---|
587 | 942 | struct dwc3_qcom *qcom = dev_get_drvdata(dev); |
---|
588 | 943 | |
---|
589 | | - return dwc3_qcom_resume(qcom); |
---|
| 944 | + return dwc3_qcom_resume(qcom, true); |
---|
590 | 945 | } |
---|
591 | 946 | |
---|
592 | 947 | static const struct dev_pm_ops dwc3_qcom_dev_pm_ops = { |
---|
.. | .. |
---|
598 | 953 | static const struct of_device_id dwc3_qcom_of_match[] = { |
---|
599 | 954 | { .compatible = "qcom,dwc3" }, |
---|
600 | 955 | { .compatible = "qcom,msm8996-dwc3" }, |
---|
| 956 | + { .compatible = "qcom,msm8998-dwc3" }, |
---|
601 | 957 | { .compatible = "qcom,sdm845-dwc3" }, |
---|
602 | 958 | { } |
---|
603 | 959 | }; |
---|
604 | 960 | MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match); |
---|
| 961 | + |
---|
| 962 | +#ifdef CONFIG_ACPI |
---|
| 963 | +static const struct dwc3_acpi_pdata sdm845_acpi_pdata = { |
---|
| 964 | + .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET, |
---|
| 965 | + .qscratch_base_size = SDM845_QSCRATCH_SIZE, |
---|
| 966 | + .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE, |
---|
| 967 | + .hs_phy_irq_index = 1, |
---|
| 968 | + .dp_hs_phy_irq_index = 4, |
---|
| 969 | + .dm_hs_phy_irq_index = 3, |
---|
| 970 | + .ss_phy_irq_index = 2 |
---|
| 971 | +}; |
---|
| 972 | + |
---|
| 973 | +static const struct dwc3_acpi_pdata sdm845_acpi_urs_pdata = { |
---|
| 974 | + .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET, |
---|
| 975 | + .qscratch_base_size = SDM845_QSCRATCH_SIZE, |
---|
| 976 | + .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE, |
---|
| 977 | + .hs_phy_irq_index = 1, |
---|
| 978 | + .dp_hs_phy_irq_index = 4, |
---|
| 979 | + .dm_hs_phy_irq_index = 3, |
---|
| 980 | + .ss_phy_irq_index = 2, |
---|
| 981 | + .is_urs = true, |
---|
| 982 | +}; |
---|
| 983 | + |
---|
| 984 | +static const struct acpi_device_id dwc3_qcom_acpi_match[] = { |
---|
| 985 | + { "QCOM2430", (unsigned long)&sdm845_acpi_pdata }, |
---|
| 986 | + { "QCOM0304", (unsigned long)&sdm845_acpi_urs_pdata }, |
---|
| 987 | + { "QCOM0497", (unsigned long)&sdm845_acpi_urs_pdata }, |
---|
| 988 | + { "QCOM04A6", (unsigned long)&sdm845_acpi_pdata }, |
---|
| 989 | + { }, |
---|
| 990 | +}; |
---|
| 991 | +MODULE_DEVICE_TABLE(acpi, dwc3_qcom_acpi_match); |
---|
| 992 | +#endif |
---|
605 | 993 | |
---|
606 | 994 | static struct platform_driver dwc3_qcom_driver = { |
---|
607 | 995 | .probe = dwc3_qcom_probe, |
---|
.. | .. |
---|
610 | 998 | .name = "dwc3-qcom", |
---|
611 | 999 | .pm = &dwc3_qcom_dev_pm_ops, |
---|
612 | 1000 | .of_match_table = dwc3_qcom_of_match, |
---|
| 1001 | + .acpi_match_table = ACPI_PTR(dwc3_qcom_acpi_match), |
---|
613 | 1002 | }, |
---|
614 | 1003 | }; |
---|
615 | 1004 | |
---|