]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/host/ehci-hcd.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
[linux-2.6-omap-h63xx.git] / drivers / usb / host / ehci-hcd.c
index 5ac918591131a5a11e08d75bc87a7d1d40363aec..025d333136817da006700cfc7852fe06111caabf 100644 (file)
@@ -111,7 +111,7 @@ static const char   hcd_name [] = "ehci_hcd";
 #define        EHCI_TUNE_MULT_TT       1
 #define        EHCI_TUNE_FLS           2       /* (small) 256 frame schedule */
 
-#define EHCI_IAA_MSECS         10              /* arbitrary */
+#define EHCI_IAA_JIFFIES       (HZ/100)        /* arbitrary; ~10 msec */
 #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 */
@@ -126,6 +126,11 @@ static unsigned park = 0;
 module_param (park, uint, S_IRUGO);
 MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets");
 
+/* for flakey hardware, ignore overcurrent indicators */
+static int ignore_oc = 0;
+module_param (ignore_oc, bool, S_IRUGO);
+MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
+
 #define        INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
 
 /*-------------------------------------------------------------------------*/
@@ -254,8 +259,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
 
 /*-------------------------------------------------------------------------*/
 
-static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs);
-static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
+static void ehci_work(struct ehci_hcd *ehci);
 
 #include "ehci-hub.c"
 #include "ehci-mem.c"
@@ -264,42 +268,30 @@ static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
 
 /*-------------------------------------------------------------------------*/
 
-static void ehci_iaa_watchdog (unsigned long param)
+static void ehci_watchdog (unsigned long param)
 {
        struct ehci_hcd         *ehci = (struct ehci_hcd *) param;
        unsigned long           flags;
-       u32                     status;
 
        spin_lock_irqsave (&ehci->lock, flags);
-       WARN_ON(!ehci->reclaim);
 
-       /* lost IAA irqs wedge things badly; seen first with a vt8235 */
+       /* lost IAA irqs wedge things badly; seen with a vt8235 */
        if (ehci->reclaim) {
-               status = readl (&ehci->regs->status);
+               u32             status = readl (&ehci->regs->status);
                if (status & STS_IAA) {
                        ehci_vdbg (ehci, "lost IAA\n");
                        COUNT (ehci->stats.lost_iaa);
                        writel (STS_IAA, &ehci->regs->status);
-                       end_unlink_async (ehci, NULL);
+                       ehci->reclaim_ready = 1;
                }
        }
 
-       spin_unlock_irqrestore (&ehci->lock, flags);
-}
-
-static void ehci_watchdog (unsigned long param)
-{
-       struct ehci_hcd         *ehci = (struct ehci_hcd *) param;
-       unsigned long           flags;
-
-       spin_lock_irqsave (&ehci->lock, flags);
-
-       /* stop async processing after it's idled a bit */
+       /* stop async processing after it's idled a bit */
        if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
                start_unlink_async (ehci, ehci->async);
 
        /* ehci could run by timer, without IRQs ... */
-       ehci_work (ehci, NULL);
+       ehci_work (ehci);
 
        spin_unlock_irqrestore (&ehci->lock, flags);
 }
@@ -342,9 +334,11 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
  * ehci_work is called from some interrupts, timers, and so on.
  * it calls driver completion functions, after dropping ehci->lock.
  */
-static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
+static void ehci_work (struct ehci_hcd *ehci)
 {
        timer_action_done (ehci, TIMER_IO_WATCHDOG);
+       if (ehci->reclaim_ready)
+               end_unlink_async (ehci);
 
        /* another CPU may drop ehci->lock during a schedule scan while
         * it reports urb completions.  this flag guards against bogus
@@ -353,9 +347,9 @@ static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
        if (ehci->scanning)
                return;
        ehci->scanning = 1;
-       scan_async (ehci, regs);
+       scan_async (ehci);
        if (ehci->next_uframe != -1)
-               scan_periodic (ehci, regs);
+               scan_periodic (ehci);
        ehci->scanning = 0;
 
        /* the IO watchdog guards against hardware or driver bugs that
@@ -379,7 +373,6 @@ static void ehci_stop (struct usb_hcd *hcd)
 
        /* no more interrupts ... */
        del_timer_sync (&ehci->watchdog);
-       del_timer_sync (&ehci->iaa_watchdog);
 
        spin_lock_irq(&ehci->lock);
        if (HC_IS_RUNNING (hcd->state))
@@ -397,7 +390,7 @@ static void ehci_stop (struct usb_hcd *hcd)
        /* root hub is shut down separately (first, when possible) */
        spin_lock_irq (&ehci->lock);
        if (ehci->async)
-               ehci_work (ehci, NULL);
+               ehci_work (ehci);
        spin_unlock_irq (&ehci->lock);
        ehci_mem_cleanup (ehci);
 
@@ -426,10 +419,6 @@ static int ehci_init(struct usb_hcd *hcd)
        ehci->watchdog.function = ehci_watchdog;
        ehci->watchdog.data = (unsigned long) ehci;
 
-       init_timer(&ehci->iaa_watchdog);
-       ehci->iaa_watchdog.function = ehci_iaa_watchdog;
-       ehci->iaa_watchdog.data = (unsigned long) ehci;
-
        /*
         * hw default: 1K periodic list heads, one per frame.
         * periodic_size can shrink by USBCMD update if hcc_params allows.
@@ -446,6 +435,7 @@ static int ehci_init(struct usb_hcd *hcd)
                ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
 
        ehci->reclaim = NULL;
+       ehci->reclaim_ready = 0;
        ehci->next_uframe = -1;
 
        /*
@@ -556,9 +546,10 @@ static int ehci_run (struct usb_hcd *hcd)
 
        temp = HC_VERSION(readl (&ehci->caps->hc_capbase));
        ehci_info (ehci,
-               "USB %x.%x started, EHCI %x.%02x, driver %s\n",
+               "USB %x.%x started, EHCI %x.%02x, driver %s%s\n",
                ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f),
-               temp >> 8, temp & 0xff, DRIVER_VERSION);
+               temp >> 8, temp & 0xff, DRIVER_VERSION,
+               ignore_oc ? ", overcurrent ignored" : "");
 
        writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */
 
@@ -573,7 +564,7 @@ static int ehci_run (struct usb_hcd *hcd)
 
 /*-------------------------------------------------------------------------*/
 
-static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
+static irqreturn_t ehci_irq (struct usb_hcd *hcd)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci (hcd);
        u32                     status;
@@ -619,7 +610,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
        /* complete the unlinking of some qh [4.15.2.3] */
        if (status & STS_IAA) {
                COUNT (ehci->stats.reclaim);
-               end_unlink_async (ehci, regs);
+               ehci->reclaim_ready = 1;
                bh = 1;
        }
 
@@ -628,9 +619,8 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
                unsigned        i = HCS_N_PORTS (ehci->hcs_params);
 
                /* resume root hub? */
-               status = readl (&ehci->regs->command);
-               if (!(status & CMD_RUN))
-                       writel (status | CMD_RUN, &ehci->regs->command);
+               if (!(readl(&ehci->regs->command) & CMD_RUN))
+                       usb_hcd_resume_root_hub(hcd);
 
                while (i--) {
                        int pstatus = readl (&ehci->regs->port_status [i]);
@@ -647,7 +637,6 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
                         */
                        ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
                        ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
-                       usb_hcd_resume_root_hub(hcd);
                }
        }
 
@@ -670,7 +659,7 @@ dead:
        }
 
        if (bh)
-               ehci_work (ehci, regs);
+               ehci_work (ehci);
        spin_unlock (&ehci->lock);
        return IRQ_HANDLED;
 }
@@ -723,14 +712,10 @@ static int ehci_urb_enqueue (
 
 static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-       // BUG_ON(qh->qh_state != QH_STATE_LINKED);
-
-       /* failfast */
-       if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
-               end_unlink_async (ehci, NULL);
-
-       /* defer till later if busy */
-       else if (ehci->reclaim) {
+       /* if we need to use IAA and it's busy, defer */
+       if (qh->qh_state == QH_STATE_LINKED
+                       && ehci->reclaim
+                       && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) {
                struct ehci_qh          *last;
 
                for (last = ehci->reclaim;
@@ -740,8 +725,12 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
                qh->qh_state = QH_STATE_UNLINK_WAIT;
                last->reclaim = qh;
 
-       /* start IAA cycle */
-       } else
+       /* bypass IAA if the hc can't care */
+       } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim)
+               end_unlink_async (ehci);
+
+       /* something else might have unlinked the qh by now */
+       if (qh->qh_state == QH_STATE_LINKED)
                start_unlink_async (ehci, qh);
 }
 
@@ -763,19 +752,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
                qh = (struct ehci_qh *) urb->hcpriv;
                if (!qh)
                        break;
-               switch (qh->qh_state) {
-               case QH_STATE_LINKED:
-               case QH_STATE_COMPLETING:
-                       unlink_async (ehci, qh);
-                       break;
-               case QH_STATE_UNLINK:
-               case QH_STATE_UNLINK_WAIT:
-                       /* already started */
-                       break;
-               case QH_STATE_IDLE:
-                       WARN_ON(1);
-                       break;
-               }
+               unlink_async (ehci, qh);
                break;
 
        case PIPE_INTERRUPT:
@@ -787,7 +764,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
                        intr_deschedule (ehci, qh);
                        /* FALL THROUGH */
                case QH_STATE_IDLE:
-                       qh_completions (ehci, qh, NULL);
+                       qh_completions (ehci, qh);
                        break;
                default:
                        ehci_dbg (ehci, "bogus qh %p state %d\n",
@@ -867,7 +844,6 @@ rescan:
                unlink_async (ehci, qh);
                /* FALL THROUGH */
        case QH_STATE_UNLINK:           /* wait for hw to finish? */
-       case QH_STATE_UNLINK_WAIT:
 idle_timeout:
                spin_unlock_irqrestore (&ehci->lock, flags);
                schedule_timeout_uninterruptible(1);