diff options
author | David Brownell <david-b@pacbell.net> | 2005-08-31 13:55:38 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-09-12 15:23:42 -0400 |
commit | 10f6524a8ef1413a8cbd952673997013183fe2a9 (patch) | |
tree | d5702bdccbeb57a7158643f978e47bcd2106e71f /drivers/usb | |
parent | 198b95170f2c7ad56b4ba92fe3d4d896f5be5c7e (diff) |
[PATCH] USB: EHCI port tweaks
One change may improve some S1 or S3 resume cases, and the other
seems mostly to explain some strange state "lsusb" would show.
Two fixes:
- On resume, don't think about resuming any unpowered port, or
resetting any port with OWNER set to the OHCI/UHCI companion.
This will make some S1 and S3 resume scenarios work better.
- PORT_CSC was not being cleared correctly in ehci_hub_status_data.
This was visible at least through current versions of "lsusb",
and might have caused some other hub related strangeness.
The fix addresses all three write-to-clear bits, using the same
approach that UHCI happens to use: a mask of bits that are
cleared in most writes to that port status register.
Original patch seems to have been from from William.Morrow@amd.com
and this version (from David) finishes the write-to-clear changes.
Signed-off-by: Jordan Crouse <jordan.crouse@amd.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 8 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 27 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 1 |
3 files changed, 23 insertions, 13 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2507e898af09..2f7037c62e88 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -759,12 +759,16 @@ static int ehci_resume (struct usb_hcd *hcd) | |||
759 | if (time_before (jiffies, ehci->next_statechange)) | 759 | if (time_before (jiffies, ehci->next_statechange)) |
760 | msleep (100); | 760 | msleep (100); |
761 | 761 | ||
762 | /* If any port is suspended, we know we can/must resume the HC. */ | 762 | /* If any port is suspended (or owned by the companion), |
763 | * we know we can/must resume the HC (and mustn't reset it). | ||
764 | */ | ||
763 | for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) { | 765 | for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) { |
764 | u32 status; | 766 | u32 status; |
765 | port--; | 767 | port--; |
766 | status = readl (&ehci->regs->port_status [port]); | 768 | status = readl (&ehci->regs->port_status [port]); |
767 | if (status & PORT_SUSPEND) { | 769 | if (!(status & PORT_POWER)) |
770 | continue; | ||
771 | if (status & (PORT_SUSPEND | PORT_OWNER)) { | ||
768 | down (&hcd->self.root_hub->serialize); | 772 | down (&hcd->self.root_hub->serialize); |
769 | retval = ehci_hub_resume (hcd); | 773 | retval = ehci_hub_resume (hcd); |
770 | up (&hcd->self.root_hub->serialize); | 774 | up (&hcd->self.root_hub->serialize); |
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 36cc1f2218d5..18d3f2270316 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
@@ -54,7 +54,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd) | |||
54 | /* suspend any active/unsuspended ports, maybe allow wakeup */ | 54 | /* suspend any active/unsuspended ports, maybe allow wakeup */ |
55 | while (port--) { | 55 | while (port--) { |
56 | u32 __iomem *reg = &ehci->regs->port_status [port]; | 56 | u32 __iomem *reg = &ehci->regs->port_status [port]; |
57 | u32 t1 = readl (reg); | 57 | u32 t1 = readl (reg) & ~PORT_RWC_BITS; |
58 | u32 t2 = t1; | 58 | u32 t2 = t1; |
59 | 59 | ||
60 | if ((t1 & PORT_PE) && !(t1 & PORT_OWNER)) | 60 | if ((t1 & PORT_PE) && !(t1 & PORT_OWNER)) |
@@ -115,7 +115,8 @@ static int ehci_hub_resume (struct usb_hcd *hcd) | |||
115 | i = HCS_N_PORTS (ehci->hcs_params); | 115 | i = HCS_N_PORTS (ehci->hcs_params); |
116 | while (i--) { | 116 | while (i--) { |
117 | temp = readl (&ehci->regs->port_status [i]); | 117 | temp = readl (&ehci->regs->port_status [i]); |
118 | temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); | 118 | temp &= ~(PORT_RWC_BITS |
119 | | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); | ||
119 | if (temp & PORT_SUSPEND) { | 120 | if (temp & PORT_SUSPEND) { |
120 | ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); | 121 | ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); |
121 | temp |= PORT_RESUME; | 122 | temp |= PORT_RESUME; |
@@ -128,7 +129,7 @@ static int ehci_hub_resume (struct usb_hcd *hcd) | |||
128 | temp = readl (&ehci->regs->port_status [i]); | 129 | temp = readl (&ehci->regs->port_status [i]); |
129 | if ((temp & PORT_SUSPEND) == 0) | 130 | if ((temp & PORT_SUSPEND) == 0) |
130 | continue; | 131 | continue; |
131 | temp &= ~PORT_RESUME; | 132 | temp &= ~(PORT_RWC_BITS | PORT_RESUME); |
132 | writel (temp, &ehci->regs->port_status [i]); | 133 | writel (temp, &ehci->regs->port_status [i]); |
133 | ehci_vdbg (ehci, "resumed port %d\n", i + 1); | 134 | ehci_vdbg (ehci, "resumed port %d\n", i + 1); |
134 | } | 135 | } |
@@ -191,6 +192,7 @@ static int check_reset_complete ( | |||
191 | 192 | ||
192 | // what happens if HCS_N_CC(params) == 0 ? | 193 | // what happens if HCS_N_CC(params) == 0 ? |
193 | port_status |= PORT_OWNER; | 194 | port_status |= PORT_OWNER; |
195 | port_status &= ~PORT_RWC_BITS; | ||
194 | writel (port_status, &ehci->regs->port_status [index]); | 196 | writel (port_status, &ehci->regs->port_status [index]); |
195 | 197 | ||
196 | } else | 198 | } else |
@@ -233,7 +235,8 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
233 | if (temp & PORT_OWNER) { | 235 | if (temp & PORT_OWNER) { |
234 | /* don't report this in GetPortStatus */ | 236 | /* don't report this in GetPortStatus */ |
235 | if (temp & PORT_CSC) { | 237 | if (temp & PORT_CSC) { |
236 | temp &= ~PORT_CSC; | 238 | temp &= ~PORT_RWC_BITS; |
239 | temp |= PORT_CSC; | ||
237 | writel (temp, &ehci->regs->port_status [i]); | 240 | writel (temp, &ehci->regs->port_status [i]); |
238 | } | 241 | } |
239 | continue; | 242 | continue; |
@@ -343,7 +346,7 @@ static int ehci_hub_control ( | |||
343 | &ehci->regs->port_status [wIndex]); | 346 | &ehci->regs->port_status [wIndex]); |
344 | break; | 347 | break; |
345 | case USB_PORT_FEAT_C_ENABLE: | 348 | case USB_PORT_FEAT_C_ENABLE: |
346 | writel (temp | PORT_PEC, | 349 | writel((temp & ~PORT_RWC_BITS) | PORT_PEC, |
347 | &ehci->regs->port_status [wIndex]); | 350 | &ehci->regs->port_status [wIndex]); |
348 | break; | 351 | break; |
349 | case USB_PORT_FEAT_SUSPEND: | 352 | case USB_PORT_FEAT_SUSPEND: |
@@ -353,7 +356,8 @@ static int ehci_hub_control ( | |||
353 | if ((temp & PORT_PE) == 0) | 356 | if ((temp & PORT_PE) == 0) |
354 | goto error; | 357 | goto error; |
355 | /* resume signaling for 20 msec */ | 358 | /* resume signaling for 20 msec */ |
356 | writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME, | 359 | temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); |
360 | writel (temp | PORT_RESUME, | ||
357 | &ehci->regs->port_status [wIndex]); | 361 | &ehci->regs->port_status [wIndex]); |
358 | ehci->reset_done [wIndex] = jiffies | 362 | ehci->reset_done [wIndex] = jiffies |
359 | + msecs_to_jiffies (20); | 363 | + msecs_to_jiffies (20); |
@@ -364,15 +368,15 @@ static int ehci_hub_control ( | |||
364 | break; | 368 | break; |
365 | case USB_PORT_FEAT_POWER: | 369 | case USB_PORT_FEAT_POWER: |
366 | if (HCS_PPC (ehci->hcs_params)) | 370 | if (HCS_PPC (ehci->hcs_params)) |
367 | writel (temp & ~PORT_POWER, | 371 | writel (temp & ~(PORT_RWC_BITS | PORT_POWER), |
368 | &ehci->regs->port_status [wIndex]); | 372 | &ehci->regs->port_status [wIndex]); |
369 | break; | 373 | break; |
370 | case USB_PORT_FEAT_C_CONNECTION: | 374 | case USB_PORT_FEAT_C_CONNECTION: |
371 | writel (temp | PORT_CSC, | 375 | writel((temp & ~PORT_RWC_BITS) | PORT_CSC, |
372 | &ehci->regs->port_status [wIndex]); | 376 | &ehci->regs->port_status [wIndex]); |
373 | break; | 377 | break; |
374 | case USB_PORT_FEAT_C_OVER_CURRENT: | 378 | case USB_PORT_FEAT_C_OVER_CURRENT: |
375 | writel (temp | PORT_OCC, | 379 | writel((temp & ~PORT_RWC_BITS) | PORT_OCC, |
376 | &ehci->regs->port_status [wIndex]); | 380 | &ehci->regs->port_status [wIndex]); |
377 | break; | 381 | break; |
378 | case USB_PORT_FEAT_C_RESET: | 382 | case USB_PORT_FEAT_C_RESET: |
@@ -416,7 +420,7 @@ static int ehci_hub_control ( | |||
416 | 420 | ||
417 | /* stop resume signaling */ | 421 | /* stop resume signaling */ |
418 | temp = readl (&ehci->regs->port_status [wIndex]); | 422 | temp = readl (&ehci->regs->port_status [wIndex]); |
419 | writel (temp & ~PORT_RESUME, | 423 | writel (temp & ~(PORT_RWC_BITS | PORT_RESUME), |
420 | &ehci->regs->port_status [wIndex]); | 424 | &ehci->regs->port_status [wIndex]); |
421 | retval = handshake ( | 425 | retval = handshake ( |
422 | &ehci->regs->port_status [wIndex], | 426 | &ehci->regs->port_status [wIndex], |
@@ -437,7 +441,7 @@ static int ehci_hub_control ( | |||
437 | ehci->reset_done [wIndex] = 0; | 441 | ehci->reset_done [wIndex] = 0; |
438 | 442 | ||
439 | /* force reset to complete */ | 443 | /* force reset to complete */ |
440 | writel (temp & ~PORT_RESET, | 444 | writel (temp & ~(PORT_RWC_BITS | PORT_RESET), |
441 | &ehci->regs->port_status [wIndex]); | 445 | &ehci->regs->port_status [wIndex]); |
442 | /* REVISIT: some hardware needs 550+ usec to clear | 446 | /* REVISIT: some hardware needs 550+ usec to clear |
443 | * this bit; seems too long to spin routinely... | 447 | * this bit; seems too long to spin routinely... |
@@ -500,6 +504,7 @@ static int ehci_hub_control ( | |||
500 | if (temp & PORT_OWNER) | 504 | if (temp & PORT_OWNER) |
501 | break; | 505 | break; |
502 | 506 | ||
507 | temp &= ~PORT_RWC_BITS; | ||
503 | switch (wValue) { | 508 | switch (wValue) { |
504 | case USB_PORT_FEAT_SUSPEND: | 509 | case USB_PORT_FEAT_SUSPEND: |
505 | if ((temp & PORT_PE) == 0 | 510 | if ((temp & PORT_PE) == 0 |
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 20c9b550097d..f34a0516d35f 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
@@ -263,6 +263,7 @@ struct ehci_regs { | |||
263 | #define PORT_PE (1<<2) /* port enable */ | 263 | #define PORT_PE (1<<2) /* port enable */ |
264 | #define PORT_CSC (1<<1) /* connect status change */ | 264 | #define PORT_CSC (1<<1) /* connect status change */ |
265 | #define PORT_CONNECT (1<<0) /* device connected */ | 265 | #define PORT_CONNECT (1<<0) /* device connected */ |
266 | #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) | ||
266 | } __attribute__ ((packed)); | 267 | } __attribute__ ((packed)); |
267 | 268 | ||
268 | /* Appendix C, Debug port ... intended for use with special "debug devices" | 269 | /* Appendix C, Debug port ... intended for use with special "debug devices" |