aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/musb
diff options
context:
space:
mode:
authorDaniel Mack <zonque@gmail.com>2013-12-18 14:23:46 -0500
committerFelipe Balbi <balbi@ti.com>2013-12-19 11:31:11 -0500
commit8ed1fb790ea24bb223e3b30e2b22bccf5b0a76c9 (patch)
treeeaa291bdb62bc8773df7f62d760c8ecf6fa25a65 /drivers/usb/musb
parent56b1b909d7afa5e0415363fafec3df0fc34b95c5 (diff)
usb: musb: finish suspend/reset work independently from musb_hub_control()
Currently, resume and reset is completed when the USB core calls back the root hub, asking for the port's state. This results in unpredictable timing of state assertion, which in turn renders some USB devices unusable after resume. Fix this by moving the logic to end the reset and suspend state out of musb_hub_control() into separate functions called from delayed workers. GetPortStatus only reports the current state now, without taking any real action. The rh_timeout variable is kept in order to define a minimum time gap between reset and resume only. FWIW, in my case, a Verbatim "STORE N GO" mass storage device won't resume cleanly without this patch. Signed-off-by: Daniel Mack <zonque@gmail.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/musb')
-rw-r--r--drivers/usb/musb/musb_core.c25
-rw-r--r--drivers/usb/musb/musb_core.h3
-rw-r--r--drivers/usb/musb/musb_host.h2
-rw-r--r--drivers/usb/musb/musb_virthub.c65
4 files changed, 61 insertions, 34 deletions
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 1ecdb94f3812..0e7f4a00ca7b 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -478,8 +478,8 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
478 musb->port1_status |= 478 musb->port1_status |=
479 (USB_PORT_STAT_C_SUSPEND << 16) 479 (USB_PORT_STAT_C_SUSPEND << 16)
480 | MUSB_PORT_STAT_RESUME; 480 | MUSB_PORT_STAT_RESUME;
481 musb->rh_timer = jiffies 481 schedule_delayed_work(
482 + msecs_to_jiffies(20); 482 &musb->finish_resume_work, 20);
483 483
484 musb->xceiv->state = OTG_STATE_A_HOST; 484 musb->xceiv->state = OTG_STATE_A_HOST;
485 musb->is_active = 1; 485 musb->is_active = 1;
@@ -1813,6 +1813,21 @@ static void musb_free(struct musb *musb)
1813 musb_host_free(musb); 1813 musb_host_free(musb);
1814} 1814}
1815 1815
1816static void musb_deassert_reset(struct work_struct *work)
1817{
1818 struct musb *musb;
1819 unsigned long flags;
1820
1821 musb = container_of(work, struct musb, deassert_reset_work.work);
1822
1823 spin_lock_irqsave(&musb->lock, flags);
1824
1825 if (musb->port1_status & USB_PORT_STAT_RESET)
1826 musb_port_reset(musb, false);
1827
1828 spin_unlock_irqrestore(&musb->lock, flags);
1829}
1830
1816/* 1831/*
1817 * Perform generic per-controller initialization. 1832 * Perform generic per-controller initialization.
1818 * 1833 *
@@ -1897,6 +1912,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
1897 1912
1898 /* Init IRQ workqueue before request_irq */ 1913 /* Init IRQ workqueue before request_irq */
1899 INIT_WORK(&musb->irq_work, musb_irq_work); 1914 INIT_WORK(&musb->irq_work, musb_irq_work);
1915 INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset);
1916 INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
1900 1917
1901 /* setup musb parts of the core (especially endpoints) */ 1918 /* setup musb parts of the core (especially endpoints) */
1902 status = musb_core_init(plat->config->multipoint 1919 status = musb_core_init(plat->config->multipoint
@@ -1990,6 +2007,8 @@ fail4:
1990 2007
1991fail3: 2008fail3:
1992 cancel_work_sync(&musb->irq_work); 2009 cancel_work_sync(&musb->irq_work);
2010 cancel_delayed_work_sync(&musb->finish_resume_work);
2011 cancel_delayed_work_sync(&musb->deassert_reset_work);
1993 if (musb->dma_controller) 2012 if (musb->dma_controller)
1994 dma_controller_destroy(musb->dma_controller); 2013 dma_controller_destroy(musb->dma_controller);
1995fail2_5: 2014fail2_5:
@@ -2053,6 +2072,8 @@ static int musb_remove(struct platform_device *pdev)
2053 dma_controller_destroy(musb->dma_controller); 2072 dma_controller_destroy(musb->dma_controller);
2054 2073
2055 cancel_work_sync(&musb->irq_work); 2074 cancel_work_sync(&musb->irq_work);
2075 cancel_delayed_work_sync(&musb->finish_resume_work);
2076 cancel_delayed_work_sync(&musb->deassert_reset_work);
2056 musb_free(musb); 2077 musb_free(musb);
2057 device_init_wakeup(dev, 0); 2078 device_init_wakeup(dev, 0);
2058 return 0; 2079 return 0;
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 29f7cd7c7964..7083e82776ff 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -47,6 +47,7 @@
47#include <linux/usb/otg.h> 47#include <linux/usb/otg.h>
48#include <linux/usb/musb.h> 48#include <linux/usb/musb.h>
49#include <linux/phy/phy.h> 49#include <linux/phy/phy.h>
50#include <linux/workqueue.h>
50 51
51struct musb; 52struct musb;
52struct musb_hw_ep; 53struct musb_hw_ep;
@@ -295,6 +296,8 @@ struct musb {
295 296
296 irqreturn_t (*isr)(int, void *); 297 irqreturn_t (*isr)(int, void *);
297 struct work_struct irq_work; 298 struct work_struct irq_work;
299 struct delayed_work deassert_reset_work;
300 struct delayed_work finish_resume_work;
298 u16 hwvers; 301 u16 hwvers;
299 302
300 u16 intrrxe; 303 u16 intrrxe;
diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h
index 7436c24af0ff..909ba49ef069 100644
--- a/drivers/usb/musb/musb_host.h
+++ b/drivers/usb/musb/musb_host.h
@@ -94,6 +94,7 @@ extern void musb_host_resume_root_hub(struct musb *musb);
94extern void musb_host_poke_root_hub(struct musb *musb); 94extern void musb_host_poke_root_hub(struct musb *musb);
95extern void musb_port_suspend(struct musb *musb, bool do_suspend); 95extern void musb_port_suspend(struct musb *musb, bool do_suspend);
96extern void musb_port_reset(struct musb *musb, bool do_reset); 96extern void musb_port_reset(struct musb *musb, bool do_reset);
97extern void musb_host_finish_resume(struct work_struct *work);
97#else 98#else
98static inline struct musb *hcd_to_musb(struct usb_hcd *hcd) 99static inline struct musb *hcd_to_musb(struct usb_hcd *hcd)
99{ 100{
@@ -125,6 +126,7 @@ static inline void musb_host_poll_rh_status(struct musb *musb) {}
125static inline void musb_host_poke_root_hub(struct musb *musb) {} 126static inline void musb_host_poke_root_hub(struct musb *musb) {}
126static inline void musb_port_suspend(struct musb *musb, bool do_suspend) {} 127static inline void musb_port_suspend(struct musb *musb, bool do_suspend) {}
127static inline void musb_port_reset(struct musb *musb) {} 128static inline void musb_port_reset(struct musb *musb) {}
129static inline void musb_host_finish_resume(struct work_struct *work) {}
128#endif 130#endif
129 131
130struct usb_hcd; 132struct usb_hcd;
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 24e46c0c84b5..ad417fd0e1a7 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -44,6 +44,37 @@
44 44
45#include "musb_core.h" 45#include "musb_core.h"
46 46
47void musb_host_finish_resume(struct work_struct *work)
48{
49 struct musb *musb;
50 unsigned long flags;
51 u8 power;
52
53 musb = container_of(work, struct musb, deassert_reset_work.work);
54
55 spin_lock_irqsave(&musb->lock, flags);
56
57 power = musb_readb(musb->mregs, MUSB_POWER);
58 power &= ~MUSB_POWER_RESUME;
59 dev_dbg(musb->controller, "root port resume stopped, power %02x\n",
60 power);
61 musb_writeb(musb->mregs, MUSB_POWER, power);
62
63 /*
64 * ISSUE: DaVinci (RTL 1.300) disconnects after
65 * resume of high speed peripherals (but not full
66 * speed ones).
67 */
68 musb->is_active = 1;
69 musb->port1_status &= ~(USB_PORT_STAT_SUSPEND | MUSB_PORT_STAT_RESUME);
70 musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
71 usb_hcd_poll_rh_status(musb->hcd);
72 /* NOTE: it might really be A_WAIT_BCON ... */
73 musb->xceiv->state = OTG_STATE_A_HOST;
74
75 spin_unlock_irqrestore(&musb->lock, flags);
76}
77
47void musb_port_suspend(struct musb *musb, bool do_suspend) 78void musb_port_suspend(struct musb *musb, bool do_suspend)
48{ 79{
49 struct usb_otg *otg = musb->xceiv->otg; 80 struct usb_otg *otg = musb->xceiv->otg;
@@ -105,7 +136,7 @@ void musb_port_suspend(struct musb *musb, bool do_suspend)
105 136
106 /* later, GetPortStatus will stop RESUME signaling */ 137 /* later, GetPortStatus will stop RESUME signaling */
107 musb->port1_status |= MUSB_PORT_STAT_RESUME; 138 musb->port1_status |= MUSB_PORT_STAT_RESUME;
108 musb->rh_timer = jiffies + msecs_to_jiffies(20); 139 schedule_delayed_work(&musb->finish_resume_work, 20);
109 } 140 }
110} 141}
111 142
@@ -150,7 +181,7 @@ void musb_port_reset(struct musb *musb, bool do_reset)
150 181
151 musb->port1_status |= USB_PORT_STAT_RESET; 182 musb->port1_status |= USB_PORT_STAT_RESET;
152 musb->port1_status &= ~USB_PORT_STAT_ENABLE; 183 musb->port1_status &= ~USB_PORT_STAT_ENABLE;
153 musb->rh_timer = jiffies + msecs_to_jiffies(50); 184 schedule_delayed_work(&musb->deassert_reset_work, 50);
154 } else { 185 } else {
155 dev_dbg(musb->controller, "root port reset stopped\n"); 186 dev_dbg(musb->controller, "root port reset stopped\n");
156 musb_writeb(mbase, MUSB_POWER, 187 musb_writeb(mbase, MUSB_POWER,
@@ -325,36 +356,6 @@ int musb_hub_control(
325 if (wIndex != 1) 356 if (wIndex != 1)
326 goto error; 357 goto error;
327 358
328 /* finish RESET signaling? */
329 if ((musb->port1_status & USB_PORT_STAT_RESET)
330 && time_after_eq(jiffies, musb->rh_timer))
331 musb_port_reset(musb, false);
332
333 /* finish RESUME signaling? */
334 if ((musb->port1_status & MUSB_PORT_STAT_RESUME)
335 && time_after_eq(jiffies, musb->rh_timer)) {
336 u8 power;
337
338 power = musb_readb(musb->mregs, MUSB_POWER);
339 power &= ~MUSB_POWER_RESUME;
340 dev_dbg(musb->controller, "root port resume stopped, power %02x\n",
341 power);
342 musb_writeb(musb->mregs, MUSB_POWER, power);
343
344 /* ISSUE: DaVinci (RTL 1.300) disconnects after
345 * resume of high speed peripherals (but not full
346 * speed ones).
347 */
348
349 musb->is_active = 1;
350 musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
351 | MUSB_PORT_STAT_RESUME);
352 musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
353 usb_hcd_poll_rh_status(musb->hcd);
354 /* NOTE: it might really be A_WAIT_BCON ... */
355 musb->xceiv->state = OTG_STATE_A_HOST;
356 }
357
358 put_unaligned(cpu_to_le32(musb->port1_status 359 put_unaligned(cpu_to_le32(musb->port1_status
359 & ~MUSB_PORT_STAT_RESUME), 360 & ~MUSB_PORT_STAT_RESUME),
360 (__le32 *) buf); 361 (__le32 *) buf);