diff options
author | Douglas Anderson <dianders@chromium.org> | 2019-05-20 13:56:04 -0400 |
---|---|---|
committer | Felipe Balbi <felipe.balbi@linux.intel.com> | 2019-06-18 04:58:28 -0400 |
commit | c846b03ff767149d75d4d8dca6d3d4945a21074a (patch) | |
tree | eb0e2dcdb7b697427ee610c252268fe08efd7b01 | |
parent | 1d390437f605db28596ad4c4bfeca2fed052c025 (diff) |
USB: dwc2: Don't turn off the usbphy in suspend if wakeup is enabled
If the 'snps,need-phy-for-wake' is set in the device tree then:
- We know that we can wakeup, so call device_set_wakeup_capable().
The USB core will use this knowledge to enable wakeup by default.
- We know that we should keep the PHY on during suspend if something
on our root hub needs remote wakeup. This requires the patch (USB:
Export usb_wakeup_enabled_descendants()). Note that we don't keep
the PHY on at suspend time if it's not needed because it would be a
power draw.
If we later find some users of dwc2 that can support wakeup without
keeping the PHY on we may want to add a way to call
device_set_wakeup_capable() without keeping the PHY on at suspend
time.
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Chris Zhong <zyw@rock-chips.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
-rw-r--r-- | drivers/usb/dwc2/core.h | 8 | ||||
-rw-r--r-- | drivers/usb/dwc2/hcd.c | 19 | ||||
-rw-r--r-- | drivers/usb/dwc2/platform.c | 23 |
3 files changed, 47 insertions, 3 deletions
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 152ac41dfb2d..d08d070a0fb6 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h | |||
@@ -861,6 +861,9 @@ struct dwc2_hregs_backup { | |||
861 | * @hibernated: True if core is hibernated | 861 | * @hibernated: True if core is hibernated |
862 | * @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a | 862 | * @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a |
863 | * remote wakeup. | 863 | * remote wakeup. |
864 | * @phy_off_for_suspend: Status of whether we turned the PHY off at suspend. | ||
865 | * @need_phy_for_wake: Quirk saying that we should keep the PHY on at | ||
866 | * suspend if we need USB to wake us up. | ||
864 | * @frame_number: Frame number read from the core. For both device | 867 | * @frame_number: Frame number read from the core. For both device |
865 | * and host modes. The value ranges are from 0 | 868 | * and host modes. The value ranges are from 0 |
866 | * to HFNUM_MAX_FRNUM. | 869 | * to HFNUM_MAX_FRNUM. |
@@ -1049,6 +1052,8 @@ struct dwc2_hsotg { | |||
1049 | unsigned int ll_hw_enabled:1; | 1052 | unsigned int ll_hw_enabled:1; |
1050 | unsigned int hibernated:1; | 1053 | unsigned int hibernated:1; |
1051 | unsigned int reset_phy_on_wake:1; | 1054 | unsigned int reset_phy_on_wake:1; |
1055 | unsigned int need_phy_for_wake:1; | ||
1056 | unsigned int phy_off_for_suspend:1; | ||
1052 | u16 frame_number; | 1057 | u16 frame_number; |
1053 | 1058 | ||
1054 | struct phy *phy; | 1059 | struct phy *phy; |
@@ -1438,6 +1443,7 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg); | |||
1438 | int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg); | 1443 | int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg); |
1439 | int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, | 1444 | int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, |
1440 | int rem_wakeup, int reset); | 1445 | int rem_wakeup, int reset); |
1446 | bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2); | ||
1441 | static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) | 1447 | static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) |
1442 | { schedule_work(&hsotg->phy_reset_work); } | 1448 | { schedule_work(&hsotg->phy_reset_work); } |
1443 | #else | 1449 | #else |
@@ -1463,6 +1469,8 @@ static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) | |||
1463 | static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, | 1469 | static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, |
1464 | int rem_wakeup, int reset) | 1470 | int rem_wakeup, int reset) |
1465 | { return 0; } | 1471 | { return 0; } |
1472 | static inline bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2) | ||
1473 | { return false; } | ||
1466 | static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {} | 1474 | static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {} |
1467 | 1475 | ||
1468 | #endif | 1476 | #endif |
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 2192a2873c7c..4c78a390c958 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c | |||
@@ -5587,3 +5587,22 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, | |||
5587 | dev_dbg(hsotg->dev, "Host hibernation restore complete\n"); | 5587 | dev_dbg(hsotg->dev, "Host hibernation restore complete\n"); |
5588 | return ret; | 5588 | return ret; |
5589 | } | 5589 | } |
5590 | |||
5591 | bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2) | ||
5592 | { | ||
5593 | struct usb_device *root_hub = dwc2_hsotg_to_hcd(dwc2)->self.root_hub; | ||
5594 | |||
5595 | /* If the controller isn't allowed to wakeup then we can power off. */ | ||
5596 | if (!device_may_wakeup(dwc2->dev)) | ||
5597 | return true; | ||
5598 | |||
5599 | /* | ||
5600 | * We don't want to power off the PHY if something under the | ||
5601 | * root hub has wakeup enabled. | ||
5602 | */ | ||
5603 | if (usb_wakeup_enabled_descendants(root_hub)) | ||
5604 | return false; | ||
5605 | |||
5606 | /* No reason to keep the PHY powered, so allow poweroff */ | ||
5607 | return true; | ||
5608 | } | ||
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index d10a7f8daec3..3e6c3c8a32ff 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c | |||
@@ -447,6 +447,10 @@ static int dwc2_driver_probe(struct platform_device *dev) | |||
447 | if (retval) | 447 | if (retval) |
448 | goto error; | 448 | goto error; |
449 | 449 | ||
450 | hsotg->need_phy_for_wake = | ||
451 | of_property_read_bool(dev->dev.of_node, | ||
452 | "snps,need-phy-for-wake"); | ||
453 | |||
450 | /* | 454 | /* |
451 | * Reset before dwc2_get_hwparams() then it could get power-on real | 455 | * Reset before dwc2_get_hwparams() then it could get power-on real |
452 | * reset value form registers. | 456 | * reset value form registers. |
@@ -478,6 +482,14 @@ static int dwc2_driver_probe(struct platform_device *dev) | |||
478 | hsotg->gadget_enabled = 1; | 482 | hsotg->gadget_enabled = 1; |
479 | } | 483 | } |
480 | 484 | ||
485 | /* | ||
486 | * If we need PHY for wakeup we must be wakeup capable. | ||
487 | * When we have a device that can wake without the PHY we | ||
488 | * can adjust this condition. | ||
489 | */ | ||
490 | if (hsotg->need_phy_for_wake) | ||
491 | device_set_wakeup_capable(&dev->dev, true); | ||
492 | |||
481 | hsotg->reset_phy_on_wake = | 493 | hsotg->reset_phy_on_wake = |
482 | of_property_read_bool(dev->dev.of_node, | 494 | of_property_read_bool(dev->dev.of_node, |
483 | "snps,reset-phy-on-wake"); | 495 | "snps,reset-phy-on-wake"); |
@@ -516,13 +528,17 @@ error: | |||
516 | static int __maybe_unused dwc2_suspend(struct device *dev) | 528 | static int __maybe_unused dwc2_suspend(struct device *dev) |
517 | { | 529 | { |
518 | struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); | 530 | struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); |
531 | bool is_device_mode = dwc2_is_device_mode(dwc2); | ||
519 | int ret = 0; | 532 | int ret = 0; |
520 | 533 | ||
521 | if (dwc2_is_device_mode(dwc2)) | 534 | if (is_device_mode) |
522 | dwc2_hsotg_suspend(dwc2); | 535 | dwc2_hsotg_suspend(dwc2); |
523 | 536 | ||
524 | if (dwc2->ll_hw_enabled) | 537 | if (dwc2->ll_hw_enabled && |
538 | (is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) { | ||
525 | ret = __dwc2_lowlevel_hw_disable(dwc2); | 539 | ret = __dwc2_lowlevel_hw_disable(dwc2); |
540 | dwc2->phy_off_for_suspend = true; | ||
541 | } | ||
526 | 542 | ||
527 | return ret; | 543 | return ret; |
528 | } | 544 | } |
@@ -532,11 +548,12 @@ static int __maybe_unused dwc2_resume(struct device *dev) | |||
532 | struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); | 548 | struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); |
533 | int ret = 0; | 549 | int ret = 0; |
534 | 550 | ||
535 | if (dwc2->ll_hw_enabled) { | 551 | if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) { |
536 | ret = __dwc2_lowlevel_hw_enable(dwc2); | 552 | ret = __dwc2_lowlevel_hw_enable(dwc2); |
537 | if (ret) | 553 | if (ret) |
538 | return ret; | 554 | return ret; |
539 | } | 555 | } |
556 | dwc2->phy_off_for_suspend = false; | ||
540 | 557 | ||
541 | if (dwc2_is_device_mode(dwc2)) | 558 | if (dwc2_is_device_mode(dwc2)) |
542 | ret = dwc2_hsotg_resume(dwc2); | 559 | ret = dwc2_hsotg_resume(dwc2); |