[PATCH] USB: EHCI port tweaks
authorDavid Brownell <david-b@pacbell.net>
Wed, 31 Aug 2005 17:55:38 +0000 (10:55 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 12 Sep 2005 19:23:42 +0000 (12:23 -0700)
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>
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci.h

index 2507e898af09b3cc591826d95fb562e32d1af6de..2f7037c62e88e19a792daddcf53b79875e1e90e8 100644 (file)
@@ -759,12 +759,16 @@ static int ehci_resume (struct usb_hcd *hcd)
        if (time_before (jiffies, ehci->next_statechange))
                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; ) {
                u32     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);
                        retval = ehci_hub_resume (hcd);
                        up (&hcd->self.root_hub->serialize);
index 36cc1f2218d55d203434aafb0698773291b48e4b..18d3f22703160af0c8ac55fd8f425a5d20e8c369 100644 (file)
@@ -54,7 +54,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd)
        /* suspend any active/unsuspended ports, maybe allow wakeup */
        while (port--) {
                u32 __iomem     *reg = &ehci->regs->port_status [port];
-               u32             t1 = readl (reg);
+               u32             t1 = readl (reg) & ~PORT_RWC_BITS;
                u32             t2 = t1;
 
                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);
        while (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) {
                        ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
                        temp |= PORT_RESUME;
@@ -128,7 +129,7 @@ static int ehci_hub_resume (struct usb_hcd *hcd)
                temp = readl (&ehci->regs->port_status [i]);
                if ((temp & PORT_SUSPEND) == 0)
                        continue;
-               temp &= ~PORT_RESUME;
+               temp &= ~(PORT_RWC_BITS | PORT_RESUME);
                writel (temp, &ehci->regs->port_status [i]);
                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 ?
                port_status |= PORT_OWNER;
+               port_status &= ~PORT_RWC_BITS;
                writel (port_status, &ehci->regs->port_status [index]);
 
        } else
@@ -233,7 +235,8 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
                if (temp & PORT_OWNER) {
                        /* don't report this in GetPortStatus */
                        if (temp & PORT_CSC) {
-                               temp &= ~PORT_CSC;
+                               temp &= ~PORT_RWC_BITS;
+                               temp |= PORT_CSC;
                                writel (temp, &ehci->regs->port_status [i]);
                        }
                        continue;
@@ -343,7 +346,7 @@ static int ehci_hub_control (
                                &ehci->regs->port_status [wIndex]);
                        break;
                case USB_PORT_FEAT_C_ENABLE:
-                       writel (temp | PORT_PEC,
+                       writel((temp & ~PORT_RWC_BITS) | PORT_PEC,
                                &ehci->regs->port_status [wIndex]);
                        break;
                case USB_PORT_FEAT_SUSPEND:
@@ -353,7 +356,8 @@ static int ehci_hub_control (
                                if ((temp & PORT_PE) == 0)
                                        goto error;
                                /* 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->reset_done [wIndex] = jiffies
                                                + msecs_to_jiffies (20);
@@ -364,15 +368,15 @@ static int ehci_hub_control (
                        break;
                case USB_PORT_FEAT_POWER:
                        if (HCS_PPC (ehci->hcs_params))
-                               writel (temp & ~PORT_POWER,
+                               writel (temp & ~(PORT_RWC_BITS | PORT_POWER),
                                        &ehci->regs->port_status [wIndex]);
                        break;
                case USB_PORT_FEAT_C_CONNECTION:
-                       writel (temp | PORT_CSC,
+                       writel((temp & ~PORT_RWC_BITS) | PORT_CSC,
                                &ehci->regs->port_status [wIndex]);
                        break;
                case USB_PORT_FEAT_C_OVER_CURRENT:
-                       writel (temp | PORT_OCC,
+                       writel((temp & ~PORT_RWC_BITS) | PORT_OCC,
                                &ehci->regs->port_status [wIndex]);
                        break;
                case USB_PORT_FEAT_C_RESET:
@@ -416,7 +420,7 @@ static int ehci_hub_control (
 
                        /* stop resume signaling */
                        temp = readl (&ehci->regs->port_status [wIndex]);
-                       writel (temp & ~PORT_RESUME,
+                       writel (temp & ~(PORT_RWC_BITS | PORT_RESUME),
                                &ehci->regs->port_status [wIndex]);
                        retval = handshake (
                                        &ehci->regs->port_status [wIndex],
@@ -437,7 +441,7 @@ static int ehci_hub_control (
                        ehci->reset_done [wIndex] = 0;
 
                        /* force reset to complete */
-                       writel (temp & ~PORT_RESET,
+                       writel (temp & ~(PORT_RWC_BITS | PORT_RESET),
                                        &ehci->regs->port_status [wIndex]);
                        /* REVISIT:  some hardware needs 550+ usec to clear
                         * this bit; seems too long to spin routinely...
@@ -500,6 +504,7 @@ static int ehci_hub_control (
                if (temp & PORT_OWNER)
                        break;
 
+               temp &= ~PORT_RWC_BITS;
                switch (wValue) {
                case USB_PORT_FEAT_SUSPEND:
                        if ((temp & PORT_PE) == 0
index 20c9b550097dc70e0bb3b326f144aa9628059b79..f34a0516d35ff2b63ae0355080db4016b91343a8 100644 (file)
@@ -263,6 +263,7 @@ struct ehci_regs {
 #define PORT_PE                (1<<2)          /* port enable */
 #define PORT_CSC       (1<<1)          /* connect status change */
 #define PORT_CONNECT   (1<<0)          /* device connected */
+#define PORT_RWC_BITS   (PORT_CSC | PORT_PEC | PORT_OCC)
 } __attribute__ ((packed));
 
 /* Appendix C, Debug port ... intended for use with special "debug devices"