| .. | .. |
|---|
| 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) |
|---|
| .. | .. |
|---|
| 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 | + return PTR_ERR(qcom->icc_path_apps); |
|---|
| 262 | + } |
|---|
| 263 | + |
|---|
| 264 | + if (usb_get_maximum_speed(&qcom->dwc3->dev) >= USB_SPEED_SUPER || |
|---|
| 265 | + usb_get_maximum_speed(&qcom->dwc3->dev) == USB_SPEED_UNKNOWN) |
|---|
| 266 | + ret = icc_set_bw(qcom->icc_path_ddr, |
|---|
| 267 | + USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW); |
|---|
| 268 | + else |
|---|
| 269 | + ret = icc_set_bw(qcom->icc_path_ddr, |
|---|
| 270 | + USB_MEMORY_AVG_HS_BW, USB_MEMORY_PEAK_HS_BW); |
|---|
| 271 | + |
|---|
| 272 | + if (ret) { |
|---|
| 273 | + dev_err(dev, "failed to set bandwidth for usb-ddr path: %d\n", ret); |
|---|
| 274 | + return ret; |
|---|
| 275 | + } |
|---|
| 276 | + |
|---|
| 277 | + ret = icc_set_bw(qcom->icc_path_apps, |
|---|
| 278 | + APPS_USB_AVG_BW, APPS_USB_PEAK_BW); |
|---|
| 279 | + if (ret) { |
|---|
| 280 | + dev_err(dev, "failed to set bandwidth for apps-usb path: %d\n", ret); |
|---|
| 281 | + return ret; |
|---|
| 282 | + } |
|---|
| 283 | + |
|---|
| 284 | + return 0; |
|---|
| 285 | +} |
|---|
| 286 | + |
|---|
| 287 | +/** |
|---|
| 288 | + * dwc3_qcom_interconnect_exit() - Release interconnect path handles |
|---|
| 289 | + * @qcom: Pointer to the concerned usb core. |
|---|
| 290 | + * |
|---|
| 291 | + * This function is used to release interconnect path handle. |
|---|
| 292 | + */ |
|---|
| 293 | +static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom) |
|---|
| 294 | +{ |
|---|
| 295 | + icc_put(qcom->icc_path_ddr); |
|---|
| 296 | + icc_put(qcom->icc_path_apps); |
|---|
| 297 | +} |
|---|
| 298 | + |
|---|
| 299 | +/* Only usable in contexts where the role can not change. */ |
|---|
| 300 | +static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom) |
|---|
| 301 | +{ |
|---|
| 302 | + struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3); |
|---|
| 303 | + |
|---|
| 304 | + return dwc->xhci; |
|---|
| 305 | +} |
|---|
| 306 | + |
|---|
| 176 | 307 | static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom) |
|---|
| 177 | 308 | { |
|---|
| 178 | 309 | if (qcom->hs_phy_irq) { |
|---|
| .. | .. |
|---|
| 219 | 350 | } |
|---|
| 220 | 351 | } |
|---|
| 221 | 352 | |
|---|
| 222 | | -static int dwc3_qcom_suspend(struct dwc3_qcom *qcom) |
|---|
| 353 | +static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup) |
|---|
| 223 | 354 | { |
|---|
| 224 | 355 | u32 val; |
|---|
| 225 | | - int i; |
|---|
| 356 | + int i, ret; |
|---|
| 226 | 357 | |
|---|
| 227 | 358 | if (qcom->is_suspended) |
|---|
| 228 | 359 | return 0; |
|---|
| .. | .. |
|---|
| 234 | 365 | for (i = qcom->num_clocks - 1; i >= 0; i--) |
|---|
| 235 | 366 | clk_disable_unprepare(qcom->clks[i]); |
|---|
| 236 | 367 | |
|---|
| 237 | | - if (device_may_wakeup(qcom->dev)) |
|---|
| 368 | + ret = dwc3_qcom_interconnect_disable(qcom); |
|---|
| 369 | + if (ret) |
|---|
| 370 | + dev_warn(qcom->dev, "failed to disable interconnect: %d\n", ret); |
|---|
| 371 | + |
|---|
| 372 | + if (wakeup) |
|---|
| 238 | 373 | dwc3_qcom_enable_interrupts(qcom); |
|---|
| 239 | 374 | |
|---|
| 240 | 375 | qcom->is_suspended = true; |
|---|
| .. | .. |
|---|
| 242 | 377 | return 0; |
|---|
| 243 | 378 | } |
|---|
| 244 | 379 | |
|---|
| 245 | | -static int dwc3_qcom_resume(struct dwc3_qcom *qcom) |
|---|
| 380 | +static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup) |
|---|
| 246 | 381 | { |
|---|
| 247 | 382 | int ret; |
|---|
| 248 | 383 | int i; |
|---|
| .. | .. |
|---|
| 250 | 385 | if (!qcom->is_suspended) |
|---|
| 251 | 386 | return 0; |
|---|
| 252 | 387 | |
|---|
| 253 | | - if (device_may_wakeup(qcom->dev)) |
|---|
| 388 | + if (wakeup) |
|---|
| 254 | 389 | dwc3_qcom_disable_interrupts(qcom); |
|---|
| 255 | 390 | |
|---|
| 256 | 391 | for (i = 0; i < qcom->num_clocks; i++) { |
|---|
| .. | .. |
|---|
| 261 | 396 | return ret; |
|---|
| 262 | 397 | } |
|---|
| 263 | 398 | } |
|---|
| 399 | + |
|---|
| 400 | + ret = dwc3_qcom_interconnect_enable(qcom); |
|---|
| 401 | + if (ret) |
|---|
| 402 | + dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret); |
|---|
| 264 | 403 | |
|---|
| 265 | 404 | /* Clear existing events from PHY related to L2 in/out */ |
|---|
| 266 | 405 | dwc3_qcom_setbits(qcom->qscratch_base, PWR_EVNT_IRQ_STAT_REG, |
|---|
| .. | .. |
|---|
| 280 | 419 | if (qcom->pm_suspended) |
|---|
| 281 | 420 | return IRQ_HANDLED; |
|---|
| 282 | 421 | |
|---|
| 283 | | - if (dwc->xhci) |
|---|
| 422 | + /* |
|---|
| 423 | + * This is safe as role switching is done from a freezable workqueue |
|---|
| 424 | + * and the wakeup interrupts are disabled as part of resume. |
|---|
| 425 | + */ |
|---|
| 426 | + if (dwc3_qcom_is_host(qcom)) |
|---|
| 284 | 427 | pm_runtime_resume(&dwc->xhci->dev); |
|---|
| 285 | 428 | |
|---|
| 286 | 429 | return IRQ_HANDLED; |
|---|
| .. | .. |
|---|
| 303 | 446 | PIPE_UTMI_CLK_DIS); |
|---|
| 304 | 447 | } |
|---|
| 305 | 448 | |
|---|
| 449 | +static int dwc3_qcom_get_irq(struct platform_device *pdev, |
|---|
| 450 | + const char *name, int num) |
|---|
| 451 | +{ |
|---|
| 452 | + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); |
|---|
| 453 | + struct platform_device *pdev_irq = qcom->urs_usb ? qcom->urs_usb : pdev; |
|---|
| 454 | + struct device_node *np = pdev->dev.of_node; |
|---|
| 455 | + int ret; |
|---|
| 456 | + |
|---|
| 457 | + if (np) |
|---|
| 458 | + ret = platform_get_irq_byname_optional(pdev_irq, name); |
|---|
| 459 | + else |
|---|
| 460 | + ret = platform_get_irq_optional(pdev_irq, num); |
|---|
| 461 | + |
|---|
| 462 | + return ret; |
|---|
| 463 | +} |
|---|
| 464 | + |
|---|
| 306 | 465 | static int dwc3_qcom_setup_irq(struct platform_device *pdev) |
|---|
| 307 | 466 | { |
|---|
| 308 | 467 | struct dwc3_qcom *qcom = platform_get_drvdata(pdev); |
|---|
| 309 | | - int irq, ret; |
|---|
| 468 | + const struct dwc3_acpi_pdata *pdata = qcom->acpi_pdata; |
|---|
| 469 | + int irq; |
|---|
| 470 | + int ret; |
|---|
| 310 | 471 | |
|---|
| 311 | | - irq = platform_get_irq_byname(pdev, "hs_phy_irq"); |
|---|
| 472 | + irq = dwc3_qcom_get_irq(pdev, "hs_phy_irq", |
|---|
| 473 | + pdata ? pdata->hs_phy_irq_index : -1); |
|---|
| 312 | 474 | if (irq > 0) { |
|---|
| 313 | 475 | /* Keep wakeup interrupts disabled until suspend */ |
|---|
| 314 | 476 | irq_set_status_flags(irq, IRQ_NOAUTOEN); |
|---|
| .. | .. |
|---|
| 323 | 485 | qcom->hs_phy_irq = irq; |
|---|
| 324 | 486 | } |
|---|
| 325 | 487 | |
|---|
| 326 | | - irq = platform_get_irq_byname(pdev, "dp_hs_phy_irq"); |
|---|
| 488 | + irq = dwc3_qcom_get_irq(pdev, "dp_hs_phy_irq", |
|---|
| 489 | + pdata ? pdata->dp_hs_phy_irq_index : -1); |
|---|
| 327 | 490 | if (irq > 0) { |
|---|
| 328 | 491 | irq_set_status_flags(irq, IRQ_NOAUTOEN); |
|---|
| 329 | 492 | ret = devm_request_threaded_irq(qcom->dev, irq, NULL, |
|---|
| .. | .. |
|---|
| 337 | 500 | qcom->dp_hs_phy_irq = irq; |
|---|
| 338 | 501 | } |
|---|
| 339 | 502 | |
|---|
| 340 | | - irq = platform_get_irq_byname(pdev, "dm_hs_phy_irq"); |
|---|
| 503 | + irq = dwc3_qcom_get_irq(pdev, "dm_hs_phy_irq", |
|---|
| 504 | + pdata ? pdata->dm_hs_phy_irq_index : -1); |
|---|
| 341 | 505 | if (irq > 0) { |
|---|
| 342 | 506 | irq_set_status_flags(irq, IRQ_NOAUTOEN); |
|---|
| 343 | 507 | ret = devm_request_threaded_irq(qcom->dev, irq, NULL, |
|---|
| .. | .. |
|---|
| 351 | 515 | qcom->dm_hs_phy_irq = irq; |
|---|
| 352 | 516 | } |
|---|
| 353 | 517 | |
|---|
| 354 | | - irq = platform_get_irq_byname(pdev, "ss_phy_irq"); |
|---|
| 518 | + irq = dwc3_qcom_get_irq(pdev, "ss_phy_irq", |
|---|
| 519 | + pdata ? pdata->ss_phy_irq_index : -1); |
|---|
| 355 | 520 | if (irq > 0) { |
|---|
| 356 | 521 | irq_set_status_flags(irq, IRQ_NOAUTOEN); |
|---|
| 357 | 522 | ret = devm_request_threaded_irq(qcom->dev, irq, NULL, |
|---|
| .. | .. |
|---|
| 374 | 539 | struct device_node *np = dev->of_node; |
|---|
| 375 | 540 | int i; |
|---|
| 376 | 541 | |
|---|
| 377 | | - qcom->num_clocks = count; |
|---|
| 378 | | - |
|---|
| 379 | | - if (!count) |
|---|
| 542 | + if (!np || !count) |
|---|
| 380 | 543 | return 0; |
|---|
| 544 | + |
|---|
| 545 | + if (count < 0) |
|---|
| 546 | + return count; |
|---|
| 547 | + |
|---|
| 548 | + qcom->num_clocks = count; |
|---|
| 381 | 549 | |
|---|
| 382 | 550 | qcom->clks = devm_kcalloc(dev, qcom->num_clocks, |
|---|
| 383 | 551 | sizeof(struct clk *), GFP_KERNEL); |
|---|
| .. | .. |
|---|
| 412 | 580 | return 0; |
|---|
| 413 | 581 | } |
|---|
| 414 | 582 | |
|---|
| 415 | | -static int dwc3_qcom_probe(struct platform_device *pdev) |
|---|
| 583 | +static const struct property_entry dwc3_qcom_acpi_properties[] = { |
|---|
| 584 | + PROPERTY_ENTRY_STRING("dr_mode", "host"), |
|---|
| 585 | + {} |
|---|
| 586 | +}; |
|---|
| 587 | + |
|---|
| 588 | +static int dwc3_qcom_acpi_register_core(struct platform_device *pdev) |
|---|
| 416 | 589 | { |
|---|
| 590 | + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); |
|---|
| 591 | + struct device *dev = &pdev->dev; |
|---|
| 592 | + struct resource *res, *child_res = NULL; |
|---|
| 593 | + struct platform_device *pdev_irq = qcom->urs_usb ? qcom->urs_usb : |
|---|
| 594 | + pdev; |
|---|
| 595 | + int irq; |
|---|
| 596 | + int ret; |
|---|
| 597 | + |
|---|
| 598 | + qcom->dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO); |
|---|
| 599 | + if (!qcom->dwc3) |
|---|
| 600 | + return -ENOMEM; |
|---|
| 601 | + |
|---|
| 602 | + qcom->dwc3->dev.parent = dev; |
|---|
| 603 | + qcom->dwc3->dev.type = dev->type; |
|---|
| 604 | + qcom->dwc3->dev.dma_mask = dev->dma_mask; |
|---|
| 605 | + qcom->dwc3->dev.dma_parms = dev->dma_parms; |
|---|
| 606 | + qcom->dwc3->dev.coherent_dma_mask = dev->coherent_dma_mask; |
|---|
| 607 | + |
|---|
| 608 | + child_res = kcalloc(2, sizeof(*child_res), GFP_KERNEL); |
|---|
| 609 | + if (!child_res) |
|---|
| 610 | + return -ENOMEM; |
|---|
| 611 | + |
|---|
| 612 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 613 | + if (!res) { |
|---|
| 614 | + dev_err(&pdev->dev, "failed to get memory resource\n"); |
|---|
| 615 | + ret = -ENODEV; |
|---|
| 616 | + goto out; |
|---|
| 617 | + } |
|---|
| 618 | + |
|---|
| 619 | + child_res[0].flags = res->flags; |
|---|
| 620 | + child_res[0].start = res->start; |
|---|
| 621 | + child_res[0].end = child_res[0].start + |
|---|
| 622 | + qcom->acpi_pdata->dwc3_core_base_size; |
|---|
| 623 | + |
|---|
| 624 | + irq = platform_get_irq(pdev_irq, 0); |
|---|
| 625 | + if (irq < 0) { |
|---|
| 626 | + ret = irq; |
|---|
| 627 | + goto out; |
|---|
| 628 | + } |
|---|
| 629 | + child_res[1].flags = IORESOURCE_IRQ; |
|---|
| 630 | + child_res[1].start = child_res[1].end = irq; |
|---|
| 631 | + |
|---|
| 632 | + ret = platform_device_add_resources(qcom->dwc3, child_res, 2); |
|---|
| 633 | + if (ret) { |
|---|
| 634 | + dev_err(&pdev->dev, "failed to add resources\n"); |
|---|
| 635 | + goto out; |
|---|
| 636 | + } |
|---|
| 637 | + |
|---|
| 638 | + ret = platform_device_add_properties(qcom->dwc3, |
|---|
| 639 | + dwc3_qcom_acpi_properties); |
|---|
| 640 | + if (ret < 0) { |
|---|
| 641 | + dev_err(&pdev->dev, "failed to add properties\n"); |
|---|
| 642 | + goto out; |
|---|
| 643 | + } |
|---|
| 644 | + |
|---|
| 645 | + ret = platform_device_add(qcom->dwc3); |
|---|
| 646 | + if (ret) |
|---|
| 647 | + dev_err(&pdev->dev, "failed to add device\n"); |
|---|
| 648 | + |
|---|
| 649 | +out: |
|---|
| 650 | + kfree(child_res); |
|---|
| 651 | + return ret; |
|---|
| 652 | +} |
|---|
| 653 | + |
|---|
| 654 | +static int dwc3_qcom_of_register_core(struct platform_device *pdev) |
|---|
| 655 | +{ |
|---|
| 656 | + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); |
|---|
| 417 | 657 | struct device_node *np = pdev->dev.of_node, *dwc3_np; |
|---|
| 418 | 658 | struct device *dev = &pdev->dev; |
|---|
| 659 | + int ret; |
|---|
| 660 | + |
|---|
| 661 | + dwc3_np = of_get_child_by_name(np, "dwc3"); |
|---|
| 662 | + if (!dwc3_np) { |
|---|
| 663 | + dev_err(dev, "failed to find dwc3 core child\n"); |
|---|
| 664 | + return -ENODEV; |
|---|
| 665 | + } |
|---|
| 666 | + |
|---|
| 667 | + ret = of_platform_populate(np, NULL, NULL, dev); |
|---|
| 668 | + if (ret) { |
|---|
| 669 | + dev_err(dev, "failed to register dwc3 core - %d\n", ret); |
|---|
| 670 | + goto node_put; |
|---|
| 671 | + } |
|---|
| 672 | + |
|---|
| 673 | + qcom->dwc3 = of_find_device_by_node(dwc3_np); |
|---|
| 674 | + if (!qcom->dwc3) { |
|---|
| 675 | + ret = -ENODEV; |
|---|
| 676 | + dev_err(dev, "failed to get dwc3 platform device\n"); |
|---|
| 677 | + } |
|---|
| 678 | + |
|---|
| 679 | +node_put: |
|---|
| 680 | + of_node_put(dwc3_np); |
|---|
| 681 | + |
|---|
| 682 | + return ret; |
|---|
| 683 | +} |
|---|
| 684 | + |
|---|
| 685 | +static struct platform_device * |
|---|
| 686 | +dwc3_qcom_create_urs_usb_platdev(struct device *dev) |
|---|
| 687 | +{ |
|---|
| 688 | + struct fwnode_handle *fwh; |
|---|
| 689 | + struct acpi_device *adev; |
|---|
| 690 | + char name[8]; |
|---|
| 691 | + int ret; |
|---|
| 692 | + int id; |
|---|
| 693 | + |
|---|
| 694 | + /* Figure out device id */ |
|---|
| 695 | + ret = sscanf(fwnode_get_name(dev->fwnode), "URS%d", &id); |
|---|
| 696 | + if (!ret) |
|---|
| 697 | + return NULL; |
|---|
| 698 | + |
|---|
| 699 | + /* Find the child using name */ |
|---|
| 700 | + snprintf(name, sizeof(name), "USB%d", id); |
|---|
| 701 | + fwh = fwnode_get_named_child_node(dev->fwnode, name); |
|---|
| 702 | + if (!fwh) |
|---|
| 703 | + return NULL; |
|---|
| 704 | + |
|---|
| 705 | + adev = to_acpi_device_node(fwh); |
|---|
| 706 | + if (!adev) |
|---|
| 707 | + return NULL; |
|---|
| 708 | + |
|---|
| 709 | + return acpi_create_platform_device(adev, NULL); |
|---|
| 710 | +} |
|---|
| 711 | + |
|---|
| 712 | +static int dwc3_qcom_probe(struct platform_device *pdev) |
|---|
| 713 | +{ |
|---|
| 714 | + struct device_node *np = pdev->dev.of_node; |
|---|
| 715 | + struct device *dev = &pdev->dev; |
|---|
| 419 | 716 | struct dwc3_qcom *qcom; |
|---|
| 420 | | - struct resource *res; |
|---|
| 717 | + struct resource *res, *parent_res = NULL; |
|---|
| 421 | 718 | int ret, i; |
|---|
| 422 | 719 | bool ignore_pipe_clk; |
|---|
| 423 | 720 | |
|---|
| .. | .. |
|---|
| 427 | 724 | |
|---|
| 428 | 725 | platform_set_drvdata(pdev, qcom); |
|---|
| 429 | 726 | qcom->dev = &pdev->dev; |
|---|
| 727 | + |
|---|
| 728 | + if (has_acpi_companion(dev)) { |
|---|
| 729 | + qcom->acpi_pdata = acpi_device_get_match_data(dev); |
|---|
| 730 | + if (!qcom->acpi_pdata) { |
|---|
| 731 | + dev_err(&pdev->dev, "no supporting ACPI device data\n"); |
|---|
| 732 | + return -EINVAL; |
|---|
| 733 | + } |
|---|
| 734 | + } |
|---|
| 430 | 735 | |
|---|
| 431 | 736 | qcom->resets = devm_reset_control_array_get_optional_exclusive(dev); |
|---|
| 432 | 737 | if (IS_ERR(qcom->resets)) { |
|---|
| .. | .. |
|---|
| 449 | 754 | goto reset_assert; |
|---|
| 450 | 755 | } |
|---|
| 451 | 756 | |
|---|
| 452 | | - ret = dwc3_qcom_clk_init(qcom, of_count_phandle_with_args(np, |
|---|
| 453 | | - "clocks", "#clock-cells")); |
|---|
| 757 | + ret = dwc3_qcom_clk_init(qcom, of_clk_get_parent_count(np)); |
|---|
| 454 | 758 | if (ret) { |
|---|
| 455 | 759 | dev_err(dev, "failed to get clocks\n"); |
|---|
| 456 | 760 | goto reset_assert; |
|---|
| 457 | 761 | } |
|---|
| 458 | 762 | |
|---|
| 459 | 763 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 460 | | - qcom->qscratch_base = devm_ioremap_resource(dev, res); |
|---|
| 764 | + |
|---|
| 765 | + if (np) { |
|---|
| 766 | + parent_res = res; |
|---|
| 767 | + } else { |
|---|
| 768 | + parent_res = kmemdup(res, sizeof(struct resource), GFP_KERNEL); |
|---|
| 769 | + if (!parent_res) |
|---|
| 770 | + return -ENOMEM; |
|---|
| 771 | + |
|---|
| 772 | + parent_res->start = res->start + |
|---|
| 773 | + qcom->acpi_pdata->qscratch_base_offset; |
|---|
| 774 | + parent_res->end = parent_res->start + |
|---|
| 775 | + qcom->acpi_pdata->qscratch_base_size; |
|---|
| 776 | + |
|---|
| 777 | + if (qcom->acpi_pdata->is_urs) { |
|---|
| 778 | + qcom->urs_usb = dwc3_qcom_create_urs_usb_platdev(dev); |
|---|
| 779 | + if (IS_ERR_OR_NULL(qcom->urs_usb)) { |
|---|
| 780 | + dev_err(dev, "failed to create URS USB platdev\n"); |
|---|
| 781 | + if (!qcom->urs_usb) |
|---|
| 782 | + return -ENODEV; |
|---|
| 783 | + else |
|---|
| 784 | + return PTR_ERR(qcom->urs_usb); |
|---|
| 785 | + } |
|---|
| 786 | + } |
|---|
| 787 | + } |
|---|
| 788 | + |
|---|
| 789 | + qcom->qscratch_base = devm_ioremap_resource(dev, parent_res); |
|---|
| 461 | 790 | if (IS_ERR(qcom->qscratch_base)) { |
|---|
| 462 | 791 | dev_err(dev, "failed to map qscratch, err=%d\n", ret); |
|---|
| 463 | 792 | ret = PTR_ERR(qcom->qscratch_base); |
|---|
| .. | .. |
|---|
| 465 | 794 | } |
|---|
| 466 | 795 | |
|---|
| 467 | 796 | 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; |
|---|
| 797 | + if (ret) { |
|---|
| 798 | + dev_err(dev, "failed to setup IRQs, err=%d\n", ret); |
|---|
| 475 | 799 | goto clk_disable; |
|---|
| 476 | 800 | } |
|---|
| 477 | 801 | |
|---|
| .. | .. |
|---|
| 484 | 808 | if (ignore_pipe_clk) |
|---|
| 485 | 809 | dwc3_qcom_select_utmi_clk(qcom); |
|---|
| 486 | 810 | |
|---|
| 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 | | - } |
|---|
| 811 | + if (np) |
|---|
| 812 | + ret = dwc3_qcom_of_register_core(pdev); |
|---|
| 813 | + else |
|---|
| 814 | + ret = dwc3_qcom_acpi_register_core(pdev); |
|---|
| 492 | 815 | |
|---|
| 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; |
|---|
| 816 | + if (ret) { |
|---|
| 817 | + dev_err(dev, "failed to register DWC3 Core, err=%d\n", ret); |
|---|
| 497 | 818 | goto depopulate; |
|---|
| 498 | 819 | } |
|---|
| 820 | + |
|---|
| 821 | + ret = dwc3_qcom_interconnect_init(qcom); |
|---|
| 822 | + if (ret) |
|---|
| 823 | + goto depopulate; |
|---|
| 499 | 824 | |
|---|
| 500 | 825 | qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev); |
|---|
| 501 | 826 | |
|---|
| .. | .. |
|---|
| 506 | 831 | /* register extcon to override sw_vbus on Vbus change later */ |
|---|
| 507 | 832 | ret = dwc3_qcom_register_extcon(qcom); |
|---|
| 508 | 833 | if (ret) |
|---|
| 509 | | - goto depopulate; |
|---|
| 834 | + goto interconnect_exit; |
|---|
| 510 | 835 | |
|---|
| 511 | 836 | device_init_wakeup(&pdev->dev, 1); |
|---|
| 512 | 837 | qcom->is_suspended = false; |
|---|
| .. | .. |
|---|
| 516 | 841 | |
|---|
| 517 | 842 | return 0; |
|---|
| 518 | 843 | |
|---|
| 844 | +interconnect_exit: |
|---|
| 845 | + dwc3_qcom_interconnect_exit(qcom); |
|---|
| 519 | 846 | depopulate: |
|---|
| 520 | | - of_platform_depopulate(&pdev->dev); |
|---|
| 847 | + if (np) |
|---|
| 848 | + of_platform_depopulate(&pdev->dev); |
|---|
| 849 | + else |
|---|
| 850 | + platform_device_put(pdev); |
|---|
| 521 | 851 | clk_disable: |
|---|
| 522 | 852 | for (i = qcom->num_clocks - 1; i >= 0; i--) { |
|---|
| 523 | 853 | clk_disable_unprepare(qcom->clks[i]); |
|---|
| .. | .. |
|---|
| 543 | 873 | } |
|---|
| 544 | 874 | qcom->num_clocks = 0; |
|---|
| 545 | 875 | |
|---|
| 876 | + dwc3_qcom_interconnect_exit(qcom); |
|---|
| 546 | 877 | reset_control_assert(qcom->resets); |
|---|
| 547 | 878 | |
|---|
| 548 | 879 | pm_runtime_allow(dev); |
|---|
| .. | .. |
|---|
| 554 | 885 | static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev) |
|---|
| 555 | 886 | { |
|---|
| 556 | 887 | struct dwc3_qcom *qcom = dev_get_drvdata(dev); |
|---|
| 888 | + bool wakeup = device_may_wakeup(dev); |
|---|
| 557 | 889 | int ret = 0; |
|---|
| 558 | 890 | |
|---|
| 559 | | - ret = dwc3_qcom_suspend(qcom); |
|---|
| 891 | + |
|---|
| 892 | + ret = dwc3_qcom_suspend(qcom, wakeup); |
|---|
| 560 | 893 | if (!ret) |
|---|
| 561 | 894 | qcom->pm_suspended = true; |
|---|
| 562 | 895 | |
|---|
| .. | .. |
|---|
| 566 | 899 | static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev) |
|---|
| 567 | 900 | { |
|---|
| 568 | 901 | struct dwc3_qcom *qcom = dev_get_drvdata(dev); |
|---|
| 902 | + bool wakeup = device_may_wakeup(dev); |
|---|
| 569 | 903 | int ret; |
|---|
| 570 | 904 | |
|---|
| 571 | | - ret = dwc3_qcom_resume(qcom); |
|---|
| 905 | + ret = dwc3_qcom_resume(qcom, wakeup); |
|---|
| 572 | 906 | if (!ret) |
|---|
| 573 | 907 | qcom->pm_suspended = false; |
|---|
| 574 | 908 | |
|---|
| .. | .. |
|---|
| 579 | 913 | { |
|---|
| 580 | 914 | struct dwc3_qcom *qcom = dev_get_drvdata(dev); |
|---|
| 581 | 915 | |
|---|
| 582 | | - return dwc3_qcom_suspend(qcom); |
|---|
| 916 | + return dwc3_qcom_suspend(qcom, true); |
|---|
| 583 | 917 | } |
|---|
| 584 | 918 | |
|---|
| 585 | 919 | static int __maybe_unused dwc3_qcom_runtime_resume(struct device *dev) |
|---|
| 586 | 920 | { |
|---|
| 587 | 921 | struct dwc3_qcom *qcom = dev_get_drvdata(dev); |
|---|
| 588 | 922 | |
|---|
| 589 | | - return dwc3_qcom_resume(qcom); |
|---|
| 923 | + return dwc3_qcom_resume(qcom, true); |
|---|
| 590 | 924 | } |
|---|
| 591 | 925 | |
|---|
| 592 | 926 | static const struct dev_pm_ops dwc3_qcom_dev_pm_ops = { |
|---|
| .. | .. |
|---|
| 598 | 932 | static const struct of_device_id dwc3_qcom_of_match[] = { |
|---|
| 599 | 933 | { .compatible = "qcom,dwc3" }, |
|---|
| 600 | 934 | { .compatible = "qcom,msm8996-dwc3" }, |
|---|
| 935 | + { .compatible = "qcom,msm8998-dwc3" }, |
|---|
| 601 | 936 | { .compatible = "qcom,sdm845-dwc3" }, |
|---|
| 602 | 937 | { } |
|---|
| 603 | 938 | }; |
|---|
| 604 | 939 | MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match); |
|---|
| 940 | + |
|---|
| 941 | +#ifdef CONFIG_ACPI |
|---|
| 942 | +static const struct dwc3_acpi_pdata sdm845_acpi_pdata = { |
|---|
| 943 | + .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET, |
|---|
| 944 | + .qscratch_base_size = SDM845_QSCRATCH_SIZE, |
|---|
| 945 | + .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE, |
|---|
| 946 | + .hs_phy_irq_index = 1, |
|---|
| 947 | + .dp_hs_phy_irq_index = 4, |
|---|
| 948 | + .dm_hs_phy_irq_index = 3, |
|---|
| 949 | + .ss_phy_irq_index = 2 |
|---|
| 950 | +}; |
|---|
| 951 | + |
|---|
| 952 | +static const struct dwc3_acpi_pdata sdm845_acpi_urs_pdata = { |
|---|
| 953 | + .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET, |
|---|
| 954 | + .qscratch_base_size = SDM845_QSCRATCH_SIZE, |
|---|
| 955 | + .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE, |
|---|
| 956 | + .hs_phy_irq_index = 1, |
|---|
| 957 | + .dp_hs_phy_irq_index = 4, |
|---|
| 958 | + .dm_hs_phy_irq_index = 3, |
|---|
| 959 | + .ss_phy_irq_index = 2, |
|---|
| 960 | + .is_urs = true, |
|---|
| 961 | +}; |
|---|
| 962 | + |
|---|
| 963 | +static const struct acpi_device_id dwc3_qcom_acpi_match[] = { |
|---|
| 964 | + { "QCOM2430", (unsigned long)&sdm845_acpi_pdata }, |
|---|
| 965 | + { "QCOM0304", (unsigned long)&sdm845_acpi_urs_pdata }, |
|---|
| 966 | + { "QCOM0497", (unsigned long)&sdm845_acpi_urs_pdata }, |
|---|
| 967 | + { "QCOM04A6", (unsigned long)&sdm845_acpi_pdata }, |
|---|
| 968 | + { }, |
|---|
| 969 | +}; |
|---|
| 970 | +MODULE_DEVICE_TABLE(acpi, dwc3_qcom_acpi_match); |
|---|
| 971 | +#endif |
|---|
| 605 | 972 | |
|---|
| 606 | 973 | static struct platform_driver dwc3_qcom_driver = { |
|---|
| 607 | 974 | .probe = dwc3_qcom_probe, |
|---|
| .. | .. |
|---|
| 610 | 977 | .name = "dwc3-qcom", |
|---|
| 611 | 978 | .pm = &dwc3_qcom_dev_pm_ops, |
|---|
| 612 | 979 | .of_match_table = dwc3_qcom_of_match, |
|---|
| 980 | + .acpi_match_table = ACPI_PTR(dwc3_qcom_acpi_match), |
|---|
| 613 | 981 | }, |
|---|
| 614 | 982 | }; |
|---|
| 615 | 983 | |
|---|