aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-05-04 11:52:40 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-12 19:29:47 -0400
commit383975d765523a56dc43a6cd6d52e9b376800cf2 (patch)
treed6ecbfe620d7d5fba372211d7af185e7c44e5097 /drivers
parent0458d5b4c9cc4ca0f62625d0144ddc4b4bc97a3c (diff)
USB: EHCI, OHCI: handover changes
This patch (as887) changes the way ehci-hcd and ohci-hcd handle a loss of VBUS power during suspend. In order for the USB-persist facility to work correctly, it is necessary for low- and full-speed devices attached to a high-speed port to be handed back to the companion controller during resume processing. This entails three changes: adding code to ehci-hcd to perform the handover, removing code from ohci-hcd to turn off ports during root-hub reinit, and adding code to ohci-hcd to turn on ports during PCI controller resume. (Other bus glue resume methods for platforms supporting high-speed controllers would need a similar change, if any existed.) Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/host/ehci-hcd.c2
-rw-r--r--drivers/usb/host/ehci-hub.c99
-rw-r--r--drivers/usb/host/ehci-pci.c7
-rw-r--r--drivers/usb/host/ehci.h3
-rw-r--r--drivers/usb/host/ohci-hcd.c21
-rw-r--r--drivers/usb/host/ohci-pci.c41
6 files changed, 145 insertions, 28 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 99ab31e9778b..b5a7aa90209a 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -399,6 +399,8 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
399 is_on ? SetPortFeature : ClearPortFeature, 399 is_on ? SetPortFeature : ClearPortFeature,
400 USB_PORT_FEAT_POWER, 400 USB_PORT_FEAT_POWER,
401 port--, NULL, 0); 401 port--, NULL, 0);
402 /* Flush those writes */
403 ehci_readl(ehci, &ehci->regs->command);
402 msleep(20); 404 msleep(20);
403} 405}
404 406
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index f4d301bc83b9..3e80de7c7f5b 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -28,6 +28,87 @@
28 28
29/*-------------------------------------------------------------------------*/ 29/*-------------------------------------------------------------------------*/
30 30
31#ifdef CONFIG_USB_PERSIST
32
33static int ehci_hub_control(
34 struct usb_hcd *hcd,
35 u16 typeReq,
36 u16 wValue,
37 u16 wIndex,
38 char *buf,
39 u16 wLength
40);
41
42/* After a power loss, ports that were owned by the companion must be
43 * reset so that the companion can still own them.
44 */
45static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
46{
47 u32 __iomem *reg;
48 u32 status;
49 int port;
50 __le32 buf;
51 struct usb_hcd *hcd = ehci_to_hcd(ehci);
52
53 if (!ehci->owned_ports)
54 return;
55
56 /* Give the connections some time to appear */
57 msleep(20);
58
59 port = HCS_N_PORTS(ehci->hcs_params);
60 while (port--) {
61 if (test_bit(port, &ehci->owned_ports)) {
62 reg = &ehci->regs->port_status[port];
63 status = ehci_readl(ehci, reg);
64
65 /* Port already owned by companion? */
66 if (status & PORT_OWNER)
67 clear_bit(port, &ehci->owned_ports);
68 else
69 ehci_hub_control(hcd, SetPortFeature,
70 USB_PORT_FEAT_RESET, port + 1,
71 NULL, 0);
72 }
73 }
74
75 if (!ehci->owned_ports)
76 return;
77 msleep(90); /* Wait for resets to complete */
78
79 port = HCS_N_PORTS(ehci->hcs_params);
80 while (port--) {
81 if (test_bit(port, &ehci->owned_ports)) {
82 ehci_hub_control(hcd, GetPortStatus,
83 0, port + 1,
84 (char *) &buf, sizeof(buf));
85
86 /* The companion should now own the port,
87 * but if something went wrong the port must not
88 * remain enabled.
89 */
90 reg = &ehci->regs->port_status[port];
91 status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
92 if (status & PORT_OWNER)
93 ehci_writel(ehci, status | PORT_CSC, reg);
94 else {
95 ehci_dbg(ehci, "failed handover port %d: %x\n",
96 port + 1, status);
97 ehci_writel(ehci, status & ~PORT_PE, reg);
98 }
99 }
100 }
101
102 ehci->owned_ports = 0;
103}
104
105#else /* CONFIG_USB_PERSIST */
106
107static inline void ehci_handover_companion_ports(struct ehci_hcd *ehci)
108{ }
109
110#endif
111
31#ifdef CONFIG_PM 112#ifdef CONFIG_PM
32 113
33static int ehci_bus_suspend (struct usb_hcd *hcd) 114static int ehci_bus_suspend (struct usb_hcd *hcd)
@@ -60,14 +141,16 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
60 * then manually resume them in the bus_resume() routine. 141 * then manually resume them in the bus_resume() routine.
61 */ 142 */
62 ehci->bus_suspended = 0; 143 ehci->bus_suspended = 0;
144 ehci->owned_ports = 0;
63 while (port--) { 145 while (port--) {
64 u32 __iomem *reg = &ehci->regs->port_status [port]; 146 u32 __iomem *reg = &ehci->regs->port_status [port];
65 u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; 147 u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
66 u32 t2 = t1; 148 u32 t2 = t1;
67 149
68 /* keep track of which ports we suspend */ 150 /* keep track of which ports we suspend */
69 if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) && 151 if (t1 & PORT_OWNER)
70 !(t1 & PORT_SUSPEND)) { 152 set_bit(port, &ehci->owned_ports);
153 else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) {
71 t2 |= PORT_SUSPEND; 154 t2 |= PORT_SUSPEND;
72 set_bit(port, &ehci->bus_suspended); 155 set_bit(port, &ehci->bus_suspended);
73 } 156 }
@@ -108,6 +191,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
108{ 191{
109 struct ehci_hcd *ehci = hcd_to_ehci (hcd); 192 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
110 u32 temp; 193 u32 temp;
194 u32 power_okay;
111 int i; 195 int i;
112 196
113 if (time_before (jiffies, ehci->next_statechange)) 197 if (time_before (jiffies, ehci->next_statechange))
@@ -120,8 +204,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
120 * the last user of the controller, not reset/pm hardware keeping 204 * the last user of the controller, not reset/pm hardware keeping
121 * state we gave to it. 205 * state we gave to it.
122 */ 206 */
123 temp = ehci_readl(ehci, &ehci->regs->intr_enable); 207 power_okay = ehci_readl(ehci, &ehci->regs->intr_enable);
124 ehci_dbg(ehci, "resume root hub%s\n", temp ? "" : " after power loss"); 208 ehci_dbg(ehci, "resume root hub%s\n",
209 power_okay ? "" : " after power loss");
125 210
126 /* at least some APM implementations will try to deliver 211 /* at least some APM implementations will try to deliver
127 * IRQs right away, so delay them until we're ready. 212 * IRQs right away, so delay them until we're ready.
@@ -184,6 +269,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
184 ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); 269 ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
185 270
186 spin_unlock_irq (&ehci->lock); 271 spin_unlock_irq (&ehci->lock);
272
273 if (!power_okay)
274 ehci_handover_companion_ports(ehci);
187 return 0; 275 return 0;
188} 276}
189 277
@@ -448,7 +536,8 @@ static int ehci_hub_control (
448) { 536) {
449 struct ehci_hcd *ehci = hcd_to_ehci (hcd); 537 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
450 int ports = HCS_N_PORTS (ehci->hcs_params); 538 int ports = HCS_N_PORTS (ehci->hcs_params);
451 u32 __iomem *status_reg = &ehci->regs->port_status[wIndex - 1]; 539 u32 __iomem *status_reg = &ehci->regs->port_status[
540 (wIndex & 0xff) - 1];
452 u32 temp, status; 541 u32 temp, status;
453 unsigned long flags; 542 unsigned long flags;
454 int retval = 0; 543 int retval = 0;
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 966965f72338..a7816e392a85 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -312,13 +312,14 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
312 ehci_work(ehci); 312 ehci_work(ehci);
313 spin_unlock_irq(&ehci->lock); 313 spin_unlock_irq(&ehci->lock);
314 314
315 /* here we "know" root ports should always stay powered */
316 ehci_port_power(ehci, 1);
317
318 ehci_writel(ehci, ehci->command, &ehci->regs->command); 315 ehci_writel(ehci, ehci->command, &ehci->regs->command);
319 ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); 316 ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
320 ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ 317 ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
321 318
319 /* here we "know" root ports should always stay powered */
320 ehci_port_power(ehci, 1);
321 ehci_handover_companion_ports(ehci);
322
322 hcd->state = HC_STATE_SUSPENDED; 323 hcd->state = HC_STATE_SUSPENDED;
323 return 0; 324 return 0;
324} 325}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 6ef9d775775b..4d617108f552 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -96,11 +96,14 @@ struct ehci_hcd { /* one per controller */
96 96
97 /* per root hub port */ 97 /* per root hub port */
98 unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; 98 unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
99
99 /* bit vectors (one bit per port) */ 100 /* bit vectors (one bit per port) */
100 unsigned long bus_suspended; /* which ports were 101 unsigned long bus_suspended; /* which ports were
101 already suspended at the start of a bus suspend */ 102 already suspended at the start of a bus suspend */
102 unsigned long companion_ports; /* which ports are 103 unsigned long companion_ports; /* which ports are
103 dedicated to the companion controller */ 104 dedicated to the companion controller */
105 unsigned long owned_ports; /* which ports are
106 owned by the companion during a bus suspend */
104 107
105 /* per-HC memory pools (could be per-bus, but ...) */ 108 /* per-HC memory pools (could be per-bus, but ...) */
106 struct dma_pool *qh_pool; /* qh per active urb */ 109 struct dma_pool *qh_pool; /* qh per active urb */
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index a66637e725f3..ce05e5f7bed6 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -510,15 +510,7 @@ static int ohci_run (struct ohci_hcd *ohci)
510 // flush the writes 510 // flush the writes
511 (void) ohci_readl (ohci, &ohci->regs->control); 511 (void) ohci_readl (ohci, &ohci->regs->control);
512 msleep(temp); 512 msleep(temp);
513 temp = roothub_a (ohci); 513
514 if (!(temp & RH_A_NPS)) {
515 /* power down each port */
516 for (temp = 0; temp < ohci->num_ports; temp++)
517 ohci_writel (ohci, RH_PS_LSDA,
518 &ohci->regs->roothub.portstatus [temp]);
519 }
520 // flush those writes
521 (void) ohci_readl (ohci, &ohci->regs->control);
522 memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); 514 memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
523 515
524 /* 2msec timelimit here means no irqs/preempt */ 516 /* 2msec timelimit here means no irqs/preempt */
@@ -826,17 +818,8 @@ static int ohci_restart (struct ohci_hcd *ohci)
826 if ((temp = ohci_run (ohci)) < 0) { 818 if ((temp = ohci_run (ohci)) < 0) {
827 ohci_err (ohci, "can't restart, %d\n", temp); 819 ohci_err (ohci, "can't restart, %d\n", temp);
828 return temp; 820 return temp;
829 } else {
830 /* here we "know" root ports should always stay powered,
831 * and that if we try to turn them back on the root hub
832 * will respond to CSC processing.
833 */
834 i = ohci->num_ports;
835 while (i--)
836 ohci_writel (ohci, RH_PS_PSS,
837 &ohci->regs->roothub.portstatus [i]);
838 ohci_dbg (ohci, "restart complete\n");
839 } 821 }
822 ohci_dbg(ohci, "restart complete\n");
840 return 0; 823 return 0;
841} 824}
842#endif 825#endif
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index ca62cb583221..15013f4519ad 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -202,6 +202,42 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd)
202 return ret; 202 return ret;
203} 203}
204 204
205#if defined(CONFIG_USB_PERSIST) && (defined(CONFIG_USB_EHCI_HCD) || \
206 defined(CONFIG_USB_EHCI_HCD_MODULE))
207
208/* Following a power loss, we must prepare to regain control of the ports
209 * we used to own. This means turning on the port power before ehci-hcd
210 * tries to switch ownership.
211 *
212 * This isn't a 100% perfect solution. On most systems the OHCI controllers
213 * lie at lower PCI addresses than the EHCI controller, so they will be
214 * discovered (and hence resumed) first. But there is no guarantee things
215 * will always work this way. If the EHCI controller is resumed first and
216 * the OHCI ports are unpowered, then the handover will fail.
217 */
218static void prepare_for_handover(struct usb_hcd *hcd)
219{
220 struct ohci_hcd *ohci = hcd_to_ohci(hcd);
221 int port;
222
223 /* Here we "know" root ports should always stay powered */
224 ohci_dbg(ohci, "powerup ports\n");
225 for (port = 0; port < ohci->num_ports; port++)
226 ohci_writel(ohci, RH_PS_PPS,
227 &ohci->regs->roothub.portstatus[port]);
228
229 /* Flush those writes */
230 ohci_readl(ohci, &ohci->regs->control);
231 msleep(20);
232}
233
234#else
235
236static inline void prepare_for_handover(struct usb_hcd *hcd)
237{ }
238
239#endif /* CONFIG_USB_PERSIST etc. */
240
205#ifdef CONFIG_PM 241#ifdef CONFIG_PM
206 242
207static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) 243static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
@@ -241,7 +277,10 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
241static int ohci_pci_resume (struct usb_hcd *hcd) 277static int ohci_pci_resume (struct usb_hcd *hcd)
242{ 278{
243 set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); 279 set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
244 usb_hcd_resume_root_hub(hcd); 280
281 /* FIXME: we should try to detect loss of VBUS power here */
282 prepare_for_handover(hcd);
283
245 return 0; 284 return 0;
246} 285}
247 286