[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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
198b95170f
commit
10f6524a8e
@@ -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)
|
|||||||
/* 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)
|
|||||||
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)
|
|||||||
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 (
|
|||||||
|
|
||||||
// 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)
|
|||||||
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 (
|
|||||||
&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 (
|
|||||||
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 (
|
|||||||
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 (
|
|||||||
|
|
||||||
/* 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 (
|
|||||||
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 (
|
|||||||
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 {
|
|||||||
#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"
|
||||||
|
|||||||
Reference in New Issue
Block a user