]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/host/ehci-hcd.c
USB: ehci: fix some ehci hangs and crashes
[linux-2.6-omap-h63xx.git] / drivers / usb / host / ehci-hcd.c
index 40f7391bf2aa7bb9540821e072c3e48260e63dc9..8409e0705d6365c34e6db7252cc39eb60575877a 100644 (file)
@@ -84,7 +84,7 @@ static const char     hcd_name [] = "ehci_hcd";
 #define EHCI_IAA_MSECS         10              /* arbitrary */
 #define EHCI_IO_JIFFIES                (HZ/10)         /* io watchdog > irq_thresh */
 #define EHCI_ASYNC_JIFFIES     (HZ/20)         /* async idle timeout */
-#define EHCI_SHRINK_JIFFIES    (HZ/200)        /* async qh unlink delay */
+#define EHCI_SHRINK_FRAMES     5               /* async qh unlink delay */
 
 /* Initial IRQ latency:  faster than hw default */
 static int log2_irq_thresh = 0;                // 0 to 6
@@ -145,16 +145,6 @@ static int handshake (struct ehci_hcd *ehci, void __iomem *ptr,
        return -ETIMEDOUT;
 }
 
-static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr,
-                                      u32 mask, u32 done, int usec)
-{
-       int error = handshake(ehci, ptr, mask, done, usec);
-       if (error)
-               ehci_to_hcd(ehci)->state = HC_STATE_HALT;
-
-       return error;
-}
-
 /* force HC to halt state from unknown (EHCI spec section 2.3) */
 static int ehci_halt (struct ehci_hcd *ehci)
 {
@@ -173,6 +163,22 @@ static int ehci_halt (struct ehci_hcd *ehci)
                          STS_HALT, STS_HALT, 16 * 125);
 }
 
+static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr,
+                                      u32 mask, u32 done, int usec)
+{
+       int error;
+
+       error = handshake(ehci, ptr, mask, done, usec);
+       if (error) {
+               ehci_halt(ehci);
+               ehci_to_hcd(ehci)->state = HC_STATE_HALT;
+               ehci_err(ehci, "force halt; handhake %p %08x %08x -> %d\n",
+                       ptr, mask, done, error);
+       }
+
+       return error;
+}
+
 /* put TDI/ARC silicon into EHCI mode */
 static void tdi_reset (struct ehci_hcd *ehci)
 {
@@ -331,17 +337,13 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci)
                                &ehci->regs->port_status[port]);
 }
 
-/* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
- * This forcibly disables dma and IRQs, helping kexec and other cases
- * where the next system software may expect clean state.
+/*
+ * Halt HC, turn off all ports, and let the BIOS use the companion controllers.
+ * Should be called with ehci->lock held.
  */
-static void
-ehci_shutdown (struct usb_hcd *hcd)
+static void ehci_silence_controller(struct ehci_hcd *ehci)
 {
-       struct ehci_hcd *ehci;
-
-       ehci = hcd_to_ehci (hcd);
-       (void) ehci_halt (ehci);
+       ehci_halt(ehci);
        ehci_turn_off_all_ports(ehci);
 
        /* make BIOS/etc use companion controller during reboot */
@@ -351,6 +353,22 @@ ehci_shutdown (struct usb_hcd *hcd)
        ehci_readl(ehci, &ehci->regs->configured_flag);
 }
 
+/* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
+ * This forcibly disables dma and IRQs, helping kexec and other cases
+ * where the next system software may expect clean state.
+ */
+static void ehci_shutdown(struct usb_hcd *hcd)
+{
+       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+       del_timer_sync(&ehci->watchdog);
+       del_timer_sync(&ehci->iaa_watchdog);
+
+       spin_lock_irq(&ehci->lock);
+       ehci_silence_controller(ehci);
+       spin_unlock_irq(&ehci->lock);
+}
+
 static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
 {
        unsigned port;
@@ -401,15 +419,15 @@ static void ehci_work (struct ehci_hcd *ehci)
                timer_action (ehci, TIMER_IO_WATCHDOG);
 }
 
+/*
+ * Called when the ehci_hcd module is removed.
+ */
 static void ehci_stop (struct usb_hcd *hcd)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci (hcd);
 
        ehci_dbg (ehci, "stop\n");
 
-       /* Turn off port power on all root hub ports. */
-       ehci_port_power (ehci, 0);
-
        /* no more interrupts ... */
        del_timer_sync (&ehci->watchdog);
        del_timer_sync(&ehci->iaa_watchdog);
@@ -418,13 +436,10 @@ static void ehci_stop (struct usb_hcd *hcd)
        if (HC_IS_RUNNING (hcd->state))
                ehci_quiesce (ehci);
 
+       ehci_silence_controller(ehci);
        ehci_reset (ehci);
-       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
        spin_unlock_irq(&ehci->lock);
 
-       /* let companion controllers work when we aren't */
-       ehci_writel(ehci, 0, &ehci->regs->configured_flag);
-
        remove_companion_file(ehci);
        remove_debug_files (ehci);
 
@@ -686,6 +701,8 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
        /* remote wakeup [4.3.1] */
        if (status & STS_PCD) {
                unsigned        i = HCS_N_PORTS (ehci->hcs_params);
+
+               /* kick root hub later */
                pcd_status = status;
 
                /* resume root hub? */
@@ -714,8 +731,6 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
 
        /* PCI errors [4.15.2.4] */
        if (unlikely ((status & STS_FATAL) != 0)) {
-               /* bogus "fatal" IRQs appear on some chips... why?  */
-               status = ehci_readl(ehci, &ehci->regs->status);
                dbg_cmd (ehci, "fatal", ehci_readl(ehci,
                                                   &ehci->regs->command));
                dbg_status (ehci, "fatal", status);
@@ -734,7 +749,7 @@ dead:
        if (bh)
                ehci_work (ehci);
        spin_unlock (&ehci->lock);
-       if (pcd_status & STS_PCD)
+       if (pcd_status)
                usb_hcd_poll_rh_status(hcd);
        return IRQ_HANDLED;
 }
@@ -764,8 +779,14 @@ static int ehci_urb_enqueue (
        INIT_LIST_HEAD (&qtd_list);
 
        switch (usb_pipetype (urb->pipe)) {
-       // case PIPE_CONTROL:
-       // case PIPE_BULK:
+       case PIPE_CONTROL:
+               /* qh_completions() code doesn't handle all the fault cases
+                * in multi-TD control transfers.  Even 1KB is rare anyway.
+                */
+               if (urb->transfer_buffer_length > (16 * 1024))
+                       return -EMSGSIZE;
+               /* FALLTHROUGH */
+       /* case PIPE_BULK: */
        default:
                if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
                        return -ENOMEM;