Commit 10f6524a authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[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: default avatarJordan Crouse <jordan.crouse@amd.com>
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 198b9517
...@@ -759,12 +759,16 @@ static int ehci_resume (struct usb_hcd *hcd) ...@@ -759,12 +759,16 @@ static int ehci_resume (struct usb_hcd *hcd)
if (time_before (jiffies, ehci->next_statechange)) if (time_before (jiffies, ehci->next_statechange))
msleep (100); msleep (100);
/* If any port is suspended, we know we can/must resume the HC. */ /* If any port is suspended (or owned by the companion),
* we know we can/must resume the HC (and mustn't reset it).
*/
for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) { for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) {
u32 status; u32 status;
port--; port--;
status = readl (&ehci->regs->port_status [port]); status = readl (&ehci->regs->port_status [port]);
if (status & PORT_SUSPEND) { if (!(status & PORT_POWER))
continue;
if (status & (PORT_SUSPEND | PORT_OWNER)) {
down (&hcd->self.root_hub->serialize); down (&hcd->self.root_hub->serialize);
retval = ehci_hub_resume (hcd); retval = ehci_hub_resume (hcd);
up (&hcd->self.root_hub->serialize); up (&hcd->self.root_hub->serialize);
......
...@@ -54,7 +54,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd) ...@@ -54,7 +54,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd)
/* suspend any active/unsuspended ports, maybe allow wakeup */ /* suspend any active/unsuspended ports, maybe allow wakeup */
while (port--) { while (port--) {
u32 __iomem *reg = &ehci->regs->port_status [port]; u32 __iomem *reg = &ehci->regs->port_status [port];
u32 t1 = readl (reg); u32 t1 = readl (reg) & ~PORT_RWC_BITS;
u32 t2 = t1; u32 t2 = t1;
if ((t1 & PORT_PE) && !(t1 & PORT_OWNER)) if ((t1 & PORT_PE) && !(t1 & PORT_OWNER))
...@@ -115,7 +115,8 @@ static int ehci_hub_resume (struct usb_hcd *hcd) ...@@ -115,7 +115,8 @@ static int ehci_hub_resume (struct usb_hcd *hcd)
i = HCS_N_PORTS (ehci->hcs_params); i = HCS_N_PORTS (ehci->hcs_params);
while (i--) { while (i--) {
temp = readl (&ehci->regs->port_status [i]); temp = readl (&ehci->regs->port_status [i]);
temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); temp &= ~(PORT_RWC_BITS
| PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E);
if (temp & PORT_SUSPEND) { if (temp & PORT_SUSPEND) {
ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
temp |= PORT_RESUME; temp |= PORT_RESUME;
...@@ -128,7 +129,7 @@ static int ehci_hub_resume (struct usb_hcd *hcd) ...@@ -128,7 +129,7 @@ static int ehci_hub_resume (struct usb_hcd *hcd)
temp = readl (&ehci->regs->port_status [i]); temp = readl (&ehci->regs->port_status [i]);
if ((temp & PORT_SUSPEND) == 0) if ((temp & PORT_SUSPEND) == 0)
continue; continue;
temp &= ~PORT_RESUME; temp &= ~(PORT_RWC_BITS | PORT_RESUME);
writel (temp, &ehci->regs->port_status [i]); writel (temp, &ehci->regs->port_status [i]);
ehci_vdbg (ehci, "resumed port %d\n", i + 1); ehci_vdbg (ehci, "resumed port %d\n", i + 1);
} }
...@@ -191,6 +192,7 @@ static int check_reset_complete ( ...@@ -191,6 +192,7 @@ static int check_reset_complete (
// what happens if HCS_N_CC(params) == 0 ? // what happens if HCS_N_CC(params) == 0 ?
port_status |= PORT_OWNER; port_status |= PORT_OWNER;
port_status &= ~PORT_RWC_BITS;
writel (port_status, &ehci->regs->port_status [index]); writel (port_status, &ehci->regs->port_status [index]);
} else } else
...@@ -233,7 +235,8 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -233,7 +235,8 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
if (temp & PORT_OWNER) { if (temp & PORT_OWNER) {
/* don't report this in GetPortStatus */ /* don't report this in GetPortStatus */
if (temp & PORT_CSC) { if (temp & PORT_CSC) {
temp &= ~PORT_CSC; temp &= ~PORT_RWC_BITS;
temp |= PORT_CSC;
writel (temp, &ehci->regs->port_status [i]); writel (temp, &ehci->regs->port_status [i]);
} }
continue; continue;
...@@ -343,7 +346,7 @@ static int ehci_hub_control ( ...@@ -343,7 +346,7 @@ static int ehci_hub_control (
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
break; break;
case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_ENABLE:
writel (temp | PORT_PEC, writel((temp & ~PORT_RWC_BITS) | PORT_PEC,
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
break; break;
case USB_PORT_FEAT_SUSPEND: case USB_PORT_FEAT_SUSPEND:
...@@ -353,7 +356,8 @@ static int ehci_hub_control ( ...@@ -353,7 +356,8 @@ static int ehci_hub_control (
if ((temp & PORT_PE) == 0) if ((temp & PORT_PE) == 0)
goto error; goto error;
/* resume signaling for 20 msec */ /* resume signaling for 20 msec */
writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME, temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
writel (temp | PORT_RESUME,
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
ehci->reset_done [wIndex] = jiffies ehci->reset_done [wIndex] = jiffies
+ msecs_to_jiffies (20); + msecs_to_jiffies (20);
...@@ -364,15 +368,15 @@ static int ehci_hub_control ( ...@@ -364,15 +368,15 @@ static int ehci_hub_control (
break; break;
case USB_PORT_FEAT_POWER: case USB_PORT_FEAT_POWER:
if (HCS_PPC (ehci->hcs_params)) if (HCS_PPC (ehci->hcs_params))
writel (temp & ~PORT_POWER, writel (temp & ~(PORT_RWC_BITS | PORT_POWER),
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
break; break;
case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_CONNECTION:
writel (temp | PORT_CSC, writel((temp & ~PORT_RWC_BITS) | PORT_CSC,
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
break; break;
case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_OVER_CURRENT:
writel (temp | PORT_OCC, writel((temp & ~PORT_RWC_BITS) | PORT_OCC,
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
break; break;
case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_RESET:
...@@ -416,7 +420,7 @@ static int ehci_hub_control ( ...@@ -416,7 +420,7 @@ static int ehci_hub_control (
/* stop resume signaling */ /* stop resume signaling */
temp = readl (&ehci->regs->port_status [wIndex]); temp = readl (&ehci->regs->port_status [wIndex]);
writel (temp & ~PORT_RESUME, writel (temp & ~(PORT_RWC_BITS | PORT_RESUME),
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
retval = handshake ( retval = handshake (
&ehci->regs->port_status [wIndex], &ehci->regs->port_status [wIndex],
...@@ -437,7 +441,7 @@ static int ehci_hub_control ( ...@@ -437,7 +441,7 @@ static int ehci_hub_control (
ehci->reset_done [wIndex] = 0; ehci->reset_done [wIndex] = 0;
/* force reset to complete */ /* force reset to complete */
writel (temp & ~PORT_RESET, writel (temp & ~(PORT_RWC_BITS | PORT_RESET),
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
/* REVISIT: some hardware needs 550+ usec to clear /* REVISIT: some hardware needs 550+ usec to clear
* this bit; seems too long to spin routinely... * this bit; seems too long to spin routinely...
...@@ -500,6 +504,7 @@ static int ehci_hub_control ( ...@@ -500,6 +504,7 @@ static int ehci_hub_control (
if (temp & PORT_OWNER) if (temp & PORT_OWNER)
break; break;
temp &= ~PORT_RWC_BITS;
switch (wValue) { switch (wValue) {
case USB_PORT_FEAT_SUSPEND: case USB_PORT_FEAT_SUSPEND:
if ((temp & PORT_PE) == 0 if ((temp & PORT_PE) == 0
......
...@@ -263,6 +263,7 @@ struct ehci_regs { ...@@ -263,6 +263,7 @@ struct ehci_regs {
#define PORT_PE (1<<2) /* port enable */ #define PORT_PE (1<<2) /* port enable */
#define PORT_CSC (1<<1) /* connect status change */ #define PORT_CSC (1<<1) /* connect status change */
#define PORT_CONNECT (1<<0) /* device connected */ #define PORT_CONNECT (1<<0) /* device connected */
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
} __attribute__ ((packed)); } __attribute__ ((packed));
/* Appendix C, Debug port ... intended for use with special "debug devices" /* Appendix C, Debug port ... intended for use with special "debug devices"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment