| .. | .. |
|---|
| 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 | |
|---|