165 lines
5.8 KiB
Diff
165 lines
5.8 KiB
Diff
|
From c846b03ff767149d75d4d8dca6d3d4945a21074a Mon Sep 17 00:00:00 2001
|
||
|
From: Douglas Anderson <dianders@chromium.org>
|
||
|
Date: Mon, 20 May 2019 10:56:04 -0700
|
||
|
Subject: [PATCH 51/53] 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>
|
||
|
---
|
||
|
drivers/usb/dwc2/core.h | 8 ++++++++
|
||
|
drivers/usb/dwc2/hcd.c | 19 +++++++++++++++++++
|
||
|
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 {
|
||
|
* @hibernated: True if core is hibernated
|
||
|
* @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a
|
||
|
* remote wakeup.
|
||
|
+ * @phy_off_for_suspend: Status of whether we turned the PHY off at suspend.
|
||
|
+ * @need_phy_for_wake: Quirk saying that we should keep the PHY on at
|
||
|
+ * suspend if we need USB to wake us up.
|
||
|
* @frame_number: Frame number read from the core. For both device
|
||
|
* and host modes. The value ranges are from 0
|
||
|
* to HFNUM_MAX_FRNUM.
|
||
|
@@ -1049,6 +1052,8 @@ struct dwc2_hsotg {
|
||
|
unsigned int ll_hw_enabled:1;
|
||
|
unsigned int hibernated:1;
|
||
|
unsigned int reset_phy_on_wake:1;
|
||
|
+ unsigned int need_phy_for_wake:1;
|
||
|
+ unsigned int phy_off_for_suspend:1;
|
||
|
u16 frame_number;
|
||
|
|
||
|
struct phy *phy;
|
||
|
@@ -1438,6 +1443,7 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg);
|
||
|
int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg);
|
||
|
int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg,
|
||
|
int rem_wakeup, int reset);
|
||
|
+bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2);
|
||
|
static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg)
|
||
|
{ schedule_work(&hsotg->phy_reset_work); }
|
||
|
#else
|
||
|
@@ -1463,6 +1469,8 @@ static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
|
||
|
static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg,
|
||
|
int rem_wakeup, int reset)
|
||
|
{ return 0; }
|
||
|
+static inline bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2)
|
||
|
+{ return false; }
|
||
|
static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {}
|
||
|
|
||
|
#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,
|
||
|
dev_dbg(hsotg->dev, "Host hibernation restore complete\n");
|
||
|
return ret;
|
||
|
}
|
||
|
+
|
||
|
+bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2)
|
||
|
+{
|
||
|
+ struct usb_device *root_hub = dwc2_hsotg_to_hcd(dwc2)->self.root_hub;
|
||
|
+
|
||
|
+ /* If the controller isn't allowed to wakeup then we can power off. */
|
||
|
+ if (!device_may_wakeup(dwc2->dev))
|
||
|
+ return true;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * We don't want to power off the PHY if something under the
|
||
|
+ * root hub has wakeup enabled.
|
||
|
+ */
|
||
|
+ if (usb_wakeup_enabled_descendants(root_hub))
|
||
|
+ return false;
|
||
|
+
|
||
|
+ /* No reason to keep the PHY powered, so allow poweroff */
|
||
|
+ return true;
|
||
|
+}
|
||
|
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)
|
||
|
if (retval)
|
||
|
goto error;
|
||
|
|
||
|
+ hsotg->need_phy_for_wake =
|
||
|
+ of_property_read_bool(dev->dev.of_node,
|
||
|
+ "snps,need-phy-for-wake");
|
||
|
+
|
||
|
/*
|
||
|
* Reset before dwc2_get_hwparams() then it could get power-on real
|
||
|
* reset value form registers.
|
||
|
@@ -478,6 +482,14 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||
|
hsotg->gadget_enabled = 1;
|
||
|
}
|
||
|
|
||
|
+ /*
|
||
|
+ * If we need PHY for wakeup we must be wakeup capable.
|
||
|
+ * When we have a device that can wake without the PHY we
|
||
|
+ * can adjust this condition.
|
||
|
+ */
|
||
|
+ if (hsotg->need_phy_for_wake)
|
||
|
+ device_set_wakeup_capable(&dev->dev, true);
|
||
|
+
|
||
|
hsotg->reset_phy_on_wake =
|
||
|
of_property_read_bool(dev->dev.of_node,
|
||
|
"snps,reset-phy-on-wake");
|
||
|
@@ -516,13 +528,17 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||
|
static int __maybe_unused dwc2_suspend(struct device *dev)
|
||
|
{
|
||
|
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
|
||
|
+ bool is_device_mode = dwc2_is_device_mode(dwc2);
|
||
|
int ret = 0;
|
||
|
|
||
|
- if (dwc2_is_device_mode(dwc2))
|
||
|
+ if (is_device_mode)
|
||
|
dwc2_hsotg_suspend(dwc2);
|
||
|
|
||
|
- if (dwc2->ll_hw_enabled)
|
||
|
+ if (dwc2->ll_hw_enabled &&
|
||
|
+ (is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) {
|
||
|
ret = __dwc2_lowlevel_hw_disable(dwc2);
|
||
|
+ dwc2->phy_off_for_suspend = true;
|
||
|
+ }
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
@@ -532,11 +548,12 @@ static int __maybe_unused dwc2_resume(struct device *dev)
|
||
|
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
|
||
|
int ret = 0;
|
||
|
|
||
|
- if (dwc2->ll_hw_enabled) {
|
||
|
+ if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) {
|
||
|
ret = __dwc2_lowlevel_hw_enable(dwc2);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
}
|
||
|
+ dwc2->phy_off_for_suspend = false;
|
||
|
|
||
|
if (dwc2_is_device_mode(dwc2))
|
||
|
ret = dwc2_hsotg_resume(dwc2);
|
||
|
--
|
||
|
2.11.0
|
||
|
|