aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorWu Liang feng <wulf@rock-chips.com>2014-12-24 05:22:19 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-01-09 15:25:54 -0500
commit314b41b16a71ee824f55e2791fcb92997672da37 (patch)
treea9df21d88f343ee33f85b093a535fcb8780ebe98 /drivers/usb
parent96e418543d27212d73f2fec76490f833baa27e3b (diff)
USB: ehci-platform: Support ehci reset after resume quirk
The Rockchip rk3288 EHCI controller doesn't properly detect the case when a device is removed during suspend. Specifically, when usb resume from suspend, the EHCI controller maintaining the USB state (FLAG_CF is 1, Current Connect Status is 1), but a USB device (like a USB camera on rk3288) may have been disconnected actually. Let's add a quirk to force ehci to go into the usb_root_hub_lost_power() path and reset after resume. This should generally reset the whole controller and all ports and initialize everything cleanly again, and bring the devices back up. As part of this, rename the "hibernation" paramter of ehci_resume() to force_reset since hibernation is simply another case where we can't trust the autodetected status and need to force a reset of devices. Signed-off-by: Wu Liang feng <wulf@rock-chips.com> Reviewed-by: Julius Werner <jwerner@google.com> Reviewed-by: Doug Anderson <dianders@google.com> Reviewed-by: Tomasz Figa <tfiga@google.com> Reviewed-by: Pawel Osciak <posciak@google.com> Reviewed-by: Sonny Rao <sonnyrao@google.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Tested-by: Doug Anderson <dianders@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/ehci-hcd.c6
-rw-r--r--drivers/usb/host/ehci-platform.c6
-rw-r--r--drivers/usb/host/ehci.h2
3 files changed, 9 insertions, 5 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 38bfeedae1d0..85e56d1abd23 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1110,7 +1110,7 @@ int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup)
1110EXPORT_SYMBOL_GPL(ehci_suspend); 1110EXPORT_SYMBOL_GPL(ehci_suspend);
1111 1111
1112/* Returns 0 if power was preserved, 1 if power was lost */ 1112/* Returns 0 if power was preserved, 1 if power was lost */
1113int ehci_resume(struct usb_hcd *hcd, bool hibernated) 1113int ehci_resume(struct usb_hcd *hcd, bool force_reset)
1114{ 1114{
1115 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 1115 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
1116 1116
@@ -1124,12 +1124,12 @@ int ehci_resume(struct usb_hcd *hcd, bool hibernated)
1124 return 0; /* Controller is dead */ 1124 return 0; /* Controller is dead */
1125 1125
1126 /* 1126 /*
1127 * If CF is still set and we aren't resuming from hibernation 1127 * If CF is still set and reset isn't forced
1128 * then we maintained suspend power. 1128 * then we maintained suspend power.
1129 * Just undo the effect of ehci_suspend(). 1129 * Just undo the effect of ehci_suspend().
1130 */ 1130 */
1131 if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF && 1131 if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
1132 !hibernated) { 1132 !force_reset) {
1133 int mask = INTR_MASK; 1133 int mask = INTR_MASK;
1134 1134
1135 ehci_prepare_ports_for_controller_resume(ehci); 1135 ehci_prepare_ports_for_controller_resume(ehci);
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index 8557803e6154..db5c29edf6db 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -185,6 +185,10 @@ static int ehci_platform_probe(struct platform_device *dev)
185 if (of_property_read_bool(dev->dev.of_node, "big-endian")) 185 if (of_property_read_bool(dev->dev.of_node, "big-endian"))
186 ehci->big_endian_mmio = ehci->big_endian_desc = 1; 186 ehci->big_endian_mmio = ehci->big_endian_desc = 1;
187 187
188 if (of_property_read_bool(dev->dev.of_node,
189 "needs-reset-on-resume"))
190 pdata->reset_on_resume = 1;
191
188 priv->phy = devm_phy_get(&dev->dev, "usb"); 192 priv->phy = devm_phy_get(&dev->dev, "usb");
189 if (IS_ERR(priv->phy)) { 193 if (IS_ERR(priv->phy)) {
190 err = PTR_ERR(priv->phy); 194 err = PTR_ERR(priv->phy);
@@ -340,7 +344,7 @@ static int ehci_platform_resume(struct device *dev)
340 return err; 344 return err;
341 } 345 }
342 346
343 ehci_resume(hcd, false); 347 ehci_resume(hcd, pdata->reset_on_resume);
344 return 0; 348 return 0;
345} 349}
346#endif /* CONFIG_PM_SLEEP */ 350#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 6f0577b0a5ae..52ef0844a180 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -871,7 +871,7 @@ extern int ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr,
871 871
872#ifdef CONFIG_PM 872#ifdef CONFIG_PM
873extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup); 873extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup);
874extern int ehci_resume(struct usb_hcd *hcd, bool hibernated); 874extern int ehci_resume(struct usb_hcd *hcd, bool force_reset);
875#endif /* CONFIG_PM */ 875#endif /* CONFIG_PM */
876 876
877extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, 877extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,