[PATCH] USB UHCI: improved reset handling

This patch improves the strategy uhci-hcd uses for performing controller
resets and checking whether they are needed.

        The HCRESET command doesn't affect the Suspend, Resume,
        or Reset bits in the port status & control registers, so
        the driver must clear them by itself.  This means the
        code to figure out how many ports there are has to be moved
        to an earlier spot in the driver.

        The R/WC bits in the USBLEGSUP register can be set by the
        hardware even in the absence of BIOS meddling with legacy
        support features.  Hence it's not a good idea to check them
        while trying to determine whether the BIOS has altered the
        controller's state.

Signed-off-by: Alan Stern <[EMAIL PROTECTED]>
Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>

---
commit c074b416b94c0aa4a371f24bf6cc13d8cf1fab59
tree 51cfe6ca9293b9484108ed757ec3c43eb5328474
parent 6c1b445c226dd82d0961725dec8051b95003723a
author Alan Stern <[EMAIL PROTECTED]> Fri, 22 Apr 2005 14:39:12 -0400
committer Greg Kroah-Hartman <[EMAIL PROTECTED]> Mon, 27 Jun 2005 14:43:44 -0700

 drivers/usb/host/uhci-hcd.c |   69 +++++++++++++++++++++++++------------------
 1 files changed, 40 insertions(+), 29 deletions(-)

diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -112,6 +112,8 @@ static inline void restart_timer(struct 
  */
 static void reset_hc(struct uhci_hcd *uhci)
 {
+       int port;
+
        /* Turn off PIRQ enable and SMI enable.  (This also turns off the
         * BIOS's USB Legacy Support.)  Turn off all the R/WC bits too.
         */
@@ -135,6 +137,13 @@ static void reset_hc(struct uhci_hcd *uh
        outw(0, uhci->io_addr + USBINTR);
        outw(0, uhci->io_addr + USBCMD);
 
+       /* HCRESET doesn't affect the Suspend, Reset, and Resume Detect
+        * bits in the port status and control registers.
+        * We have to clear them by hand.
+        */
+       for (port = 0; port < uhci->rh_numports; ++port)
+               outw(0, uhci->io_addr + USBPORTSC1 + (port * 2));
+
        uhci->port_c_suspend = uhci->suspended_ports =
                        uhci->resuming_ports = 0;
        uhci->rh_state = UHCI_RH_RESET;
@@ -166,14 +175,14 @@ static void check_and_reset_hc(struct uh
         * When restarting a suspended controller, we expect all the
         * settings to be the same as we left them:
         *
-        *      PIRQ and SMI disabled, no R/WC bits set in USBLEGSUP;
+        *      PIRQ and SMI disabled, no R/W bits set in USBLEGSUP;
         *      Controller is stopped and configured with EGSM set;
         *      No interrupts enabled except possibly Resume Detect.
         *
         * If any of these conditions are violated we do a complete reset.
         */
        pci_read_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, &legsup);
-       if (legsup & ~USBLEGSUP_RO) {
+       if (legsup & ~(USBLEGSUP_RO | USBLEGSUP_RWC)) {
                dev_dbg(uhci_dev(uhci), "%s: legsup = 0x%04x\n",
                                __FUNCTION__, legsup);
                goto reset_needed;
@@ -478,9 +487,37 @@ static void release_uhci(struct uhci_hcd
 static int uhci_reset(struct usb_hcd *hcd)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+       unsigned io_size = (unsigned) hcd->rsrc_len;
+       int port;
 
        uhci->io_addr = (unsigned long) hcd->rsrc_start;
 
+       /* The UHCI spec says devices must have 2 ports, and goes on to say
+        * they may have more but gives no way to determine how many there
+        * are.  However, according to the UHCI spec, Bit 7 of the port
+        * status and control register is always set to 1.  So we try to
+        * use this to our advantage.
+        */
+       for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) {
+               unsigned int portstatus;
+
+               portstatus = inw(uhci->io_addr + USBPORTSC1 + (port * 2));
+               if (!(portstatus & 0x0080))
+                       break;
+       }
+       if (debug)
+               dev_info(uhci_dev(uhci), "detected %d ports\n", port);
+
+       /* Anything less than 2 or greater than 7 is weird,
+        * so we'll ignore it.
+        */
+       if (port < 2 || port > UHCI_RH_MAXCHILD) {
+               dev_info(uhci_dev(uhci), "port count misdetected? "
+                               "forcing to 2 ports\n");
+               port = 2;
+       }
+       uhci->rh_numports = port;
+
        /* Kick BIOS off this hardware and reset if the controller
         * isn't already safely quiescent.
         */
@@ -508,13 +545,11 @@ static int uhci_start(struct usb_hcd *hc
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
        int retval = -EBUSY;
-       int i, port;
-       unsigned io_size;
+       int i;
        dma_addr_t dma_handle;
        struct usb_device *udev;
        struct dentry *dentry;
 
-       io_size = (unsigned) hcd->rsrc_len;
        hcd->uses_new_polling = 1;
        if (pci_find_capability(to_pci_dev(uhci_dev(uhci)), PCI_CAP_ID_PM))
                hcd->can_wakeup = 1;            /* Assume it supports PME# */
@@ -578,30 +613,6 @@ static int uhci_start(struct usb_hcd *hc
 
        /* Initialize the root hub */
 
-       /* UHCI specs says devices must have 2 ports, but goes on to say */
-       /*  they may have more but give no way to determine how many they */
-       /*  have. However, according to the UHCI spec, Bit 7 is always set */
-       /*  to 1. So we try to use this to our advantage */
-       for (port = 0; port < (io_size - 0x10) / 2; port++) {
-               unsigned int portstatus;
-
-               portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
-               if (!(portstatus & 0x0080))
-                       break;
-       }
-       if (debug)
-               dev_info(uhci_dev(uhci), "detected %d ports\n", port);
-
-       /* This is experimental so anything less than 2 or greater than 8 is */
-       /*  something weird and we'll ignore it */
-       if (port < 2 || port > UHCI_RH_MAXCHILD) {
-               dev_info(uhci_dev(uhci), "port count misdetected? "
-                               "forcing to 2 ports\n");
-               port = 2;
-       }
-
-       uhci->rh_numports = port;
-
        udev = usb_alloc_dev(NULL, &hcd->self, 0);
        if (!udev) {
                dev_err(uhci_dev(uhci), "unable to allocate root hub\n");



-------------------------------------------------------
SF.Net email is sponsored by: Discover Easy Linux Migration Strategies
from IBM. Find simple to follow Roadmaps, straightforward articles,
informative Webcasts and more! Get everything you need to get up to
speed, fast. http://ads.osdn.com/?ad_idt77&alloc_id492&op=click
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to