EHCI: maintain the ehci->command value properly
authorAlan Stern <stern@rowland.harvard.edu>
Mon, 23 Apr 2012 17:54:36 +0000 (13:54 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 23 Apr 2012 19:05:44 +0000 (12:05 -0700)
The ehci-hcd driver is a little haphazard about keeping track of the
state of the USBCMD register.  The ehci->command field is supposed to
hold the register's value (apart from a few special bits) at all
times, but it isn't maintained properly.

This patch (as1543) cleans up the situation.  It keeps ehci->command
up-to-date, and uses that value rather than reading the register from
the hardware whenever possible.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/ehci-dbg.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-sched.c

index 680e1a31fb87c2ae9538e8667c8dab0861a6410f..7561966fbdc40e13fb696fc181d159fba4149612 100644 (file)
@@ -1025,10 +1025,8 @@ static ssize_t debug_lpm_write(struct file *file, const char __user *user_buf,
                if (strict_strtoul(buf + 5, 16, &hird))
                        return -EINVAL;
                printk(KERN_INFO "setting hird %s %lu\n", buf + 6, hird);
-               temp = ehci_readl(ehci, &ehci->regs->command);
-               temp &= ~CMD_HIRD;
-               temp |= hird << 24;
-               ehci_writel(ehci, temp, &ehci->regs->command);
+               ehci->command = (ehci->command & ~CMD_HIRD) | (hird << 24);
+               ehci_writel(ehci, ehci->command, &ehci->regs->command);
        } else if (strncmp(buf, "disable", 7) == 0) {
                if (strict_strtoul(buf + 8, 10, &port))
                        return -EINVAL;
index a87c0573c860c7c2edc3e01bd0c0f5bca0fd89d9..de1e689d3df08f0fdf8b8388bb684ebb92d294b7 100644 (file)
@@ -226,8 +226,13 @@ static int ehci_halt (struct ehci_hcd *ehci)
        if ((temp & STS_HALT) != 0)
                return 0;
 
+       /*
+        * This routine gets called during probe before ehci->command
+        * has been initialized, so we can't rely on its value.
+        */
+       ehci->command &= ~CMD_RUN;
        temp = ehci_readl(ehci, &ehci->regs->command);
-       temp &= ~CMD_RUN;
+       temp &= ~(CMD_RUN | CMD_IAAD);
        ehci_writel(ehci, temp, &ehci->regs->command);
        return handshake (ehci, &ehci->regs->status,
                          STS_HALT, STS_HALT, 16 * 125);
@@ -347,6 +352,7 @@ static int ehci_reset (struct ehci_hcd *ehci)
        if (ehci->debug)
                dbgp_external_startup();
 
+       ehci->command = ehci_readl(ehci, &ehci->regs->command);
        ehci->port_c_suspend = ehci->suspended_ports =
                        ehci->resuming_ports = 0;
        return retval;
@@ -363,16 +369,14 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
 #endif
 
        /* wait for any schedule enables/disables to take effect */
-       temp = ehci_readl(ehci, &ehci->regs->command) << 10;
-       temp &= STS_ASS | STS_PSS;
+       temp = (ehci->command << 10) & (STS_ASS | STS_PSS);
        if (handshake_on_error_set_halt(ehci, &ehci->regs->status,
                                        STS_ASS | STS_PSS, temp, 16 * 125))
                return;
 
        /* then disable anything that's still active */
-       temp = ehci_readl(ehci, &ehci->regs->command);
-       temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE);
-       ehci_writel(ehci, temp, &ehci->regs->command);
+       ehci->command &= ~(CMD_ASE | CMD_PSE);
+       ehci_writel(ehci, ehci->command, &ehci->regs->command);
 
        /* hardware can take 16 microframes to turn off ... */
        handshake_on_error_set_halt(ehci, &ehci->regs->status,
index 402e766df2fe5c262128955c3053d34fec4ebab4..fc9e7cc6ac9b90c7914625f90d12875263ceb202 100644 (file)
@@ -233,7 +233,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
        /* stop schedules, clean any completed work */
        if (ehci->rh_state == EHCI_RH_RUNNING)
                ehci_quiesce (ehci);
-       ehci->command = ehci_readl(ehci, &ehci->regs->command);
        ehci_work(ehci);
 
        /* Unlike other USB host controller types, EHCI doesn't have
@@ -374,6 +373,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
        ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next);
 
        /* restore CMD_RUN, framelist size, and irq threshold */
+       ehci->command |= CMD_RUN;
        ehci_writel(ehci, ehci->command, &ehci->regs->command);
        ehci->rh_state = EHCI_RH_RUNNING;
 
index 36ca5077cdf79df7898880024470f788b7bac45f..13f4f980841af48c113de5cc70aad0b0ba61aedb 100644 (file)
@@ -981,14 +981,12 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
        head = ehci->async;
        timer_action_done (ehci, TIMER_ASYNC_OFF);
        if (!head->qh_next.qh) {
-               u32     cmd = ehci_readl(ehci, &ehci->regs->command);
-
-               if (!(cmd & CMD_ASE)) {
+               if (!(ehci->command & CMD_ASE)) {
                        /* in case a clear of CMD_ASE didn't take yet */
                        (void)handshake(ehci, &ehci->regs->status,
                                        STS_ASS, 0, 150);
-                       cmd |= CMD_ASE;
-                       ehci_writel(ehci, cmd, &ehci->regs->command);
+                       ehci->command |= CMD_ASE;
+                       ehci_writel(ehci, ehci->command, &ehci->regs->command);
                        /* posted write need not be known to HC yet ... */
                }
        }
@@ -1204,7 +1202,6 @@ static void end_unlink_async (struct ehci_hcd *ehci)
 
 static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-       int             cmd = ehci_readl(ehci, &ehci->regs->command);
        struct ehci_qh  *prev;
 
 #ifdef DEBUG
@@ -1222,8 +1219,8 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
                if (ehci->rh_state != EHCI_RH_HALTED
                                && !ehci->reclaim) {
                        /* ... and CMD_IAAD clear */
-                       ehci_writel(ehci, cmd & ~CMD_ASE,
-                                   &ehci->regs->command);
+                       ehci->command &= ~CMD_ASE;
+                       ehci_writel(ehci, ehci->command, &ehci->regs->command);
                        wmb ();
                        // handshake later, if we need to
                        timer_action_done (ehci, TIMER_ASYNC_OFF);
@@ -1253,8 +1250,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
                return;
        }
 
-       cmd |= CMD_IAAD;
-       ehci_writel(ehci, cmd, &ehci->regs->command);
+       ehci_writel(ehci, ehci->command | CMD_IAAD, &ehci->regs->command);
        (void)ehci_readl(ehci, &ehci->regs->command);
        iaa_watchdog_start(ehci);
 }
index a60679cbbf858e3c97a978218e16d931635ae1a3..ffe8fc3bc7efa91f2ec6c38c1fb19a0bb4c03ea1 100644 (file)
@@ -481,7 +481,6 @@ static int tt_no_collision (
 
 static int enable_periodic (struct ehci_hcd *ehci)
 {
-       u32     cmd;
        int     status;
 
        if (ehci->periodic_sched++)
@@ -497,8 +496,8 @@ static int enable_periodic (struct ehci_hcd *ehci)
                return status;
        }
 
-       cmd = ehci_readl(ehci, &ehci->regs->command) | CMD_PSE;
-       ehci_writel(ehci, cmd, &ehci->regs->command);
+       ehci->command |= CMD_PSE;
+       ehci_writel(ehci, ehci->command, &ehci->regs->command);
        /* posted write ... PSS happens later */
 
        /* make sure ehci_work scans these */
@@ -511,7 +510,6 @@ static int enable_periodic (struct ehci_hcd *ehci)
 
 static int disable_periodic (struct ehci_hcd *ehci)
 {
-       u32     cmd;
        int     status;
 
        if (--ehci->periodic_sched)
@@ -537,8 +535,8 @@ static int disable_periodic (struct ehci_hcd *ehci)
                return status;
        }
 
-       cmd = ehci_readl(ehci, &ehci->regs->command) & ~CMD_PSE;
-       ehci_writel(ehci, cmd, &ehci->regs->command);
+       ehci->command &= ~CMD_PSE;
+       ehci_writel(ehci, ehci->command, &ehci->regs->command);
        /* posted write ... */
 
        free_cached_lists(ehci);