[adm5120] USB driver updates, the driver passes usbtests 1-10 now
[openwrt.git] / target / linux / adm5120 / files / drivers / usb / host / adm5120-q.c
index 24542d2..24acc87 100644 (file)
@@ -299,6 +299,7 @@ static struct ed *ed_get(struct admhcd *ahcd,       struct usb_host_endpoint *ep,
        unsigned long           flags;
 
        spin_lock_irqsave(&ahcd->lock, flags);
+
        ed = ep->hcpriv;
        if (!ed) {
                u32             info;
@@ -316,6 +317,7 @@ static struct ed *ed_get(struct admhcd *ahcd,       struct usb_host_endpoint *ep,
                if (ed)
                        ep->hcpriv = ed;
        }
+
        spin_unlock_irqrestore(&ahcd->lock, flags);
 
        return ed;
@@ -555,11 +557,14 @@ static void td_submit_urb(struct admhcd *ahcd, struct urb *urb)
 /* calculate transfer length/status and update the urb
  * PRECONDITION:  irqsafe (only for urb->status locking)
  */
-static void td_done(struct admhcd *ahcd, struct urb *urb, struct td *td)
+static int td_done(struct admhcd *ahcd, struct urb *urb, struct td *td)
 {
+       struct urb_priv *urb_priv = urb->hcpriv;
        u32     info = hc32_to_cpup(ahcd, &td->hwINFO);
        int     type = usb_pipetype(urb->pipe);
-       int     cc = TD_CC_NOERROR;
+       int     cc;
+
+       cc = TD_CC_GET(info);
 
        /* ISO ... drivers see per-TD length/status */
        if (type == PIPE_ISOCHRONOUS) {
@@ -600,15 +605,12 @@ static void td_done(struct admhcd *ahcd, struct urb *urb, struct td *td)
                u32     bl = TD_BL_GET(hc32_to_cpup(ahcd, &td->hwCBL));
                u32     tdDBP = hc32_to_cpup(ahcd, &td->hwDBP);
 
-               cc = TD_CC_GET(info);
-
                /* update packet status if needed (short is normally ok) */
                if (cc == TD_CC_DATAUNDERRUN
                                && !(urb->transfer_flags & URB_SHORT_NOT_OK))
                        cc = TD_CC_NOERROR;
 
                if (cc != TD_CC_NOERROR && cc < TD_CC_HCD0) {
-                       admhc_dump_ed(ahcd, "CC ERROR", td->ed, 1);
                        spin_lock(&urb->lock);
                        if (urb->status == -EINPROGRESS)
                                urb->status = cc_to_error[cc];
@@ -629,7 +631,9 @@ static void td_done(struct admhcd *ahcd, struct urb *urb, struct td *td)
        }
 
        list_del(&td->td_list);
+       urb_priv->td_idx++;
 
+       return cc;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -647,7 +651,7 @@ ed_halted(struct admhcd *ahcd, struct td *td, int cc, struct td *rev)
         * until its urb->complete() has a chance to clean up.
         */
        ed->hwINFO |= cpu_to_hc32 (ahcd, ED_SKIP);
-       wmb ();
+       wmb();
        ed->hwHeadP &= ~cpu_to_hc32 (ahcd, ED_H);
 
        /* put any later tds from this urb onto the donelist, after 'td',
@@ -674,8 +678,8 @@ ed_halted(struct admhcd *ahcd, struct td *td, int cc, struct td *rev)
                info = next->hwINFO;
 #if 0          /* FIXME */
                info |= cpu_to_hc32 (ahcd, TD_DONE);
-               info &= ~cpu_to_hc32 (ahcd, TD_CC);
 #endif
+               info &= ~cpu_to_hc32 (ahcd, TD_CC);
                next->hwINFO = info;
 
                next->next_dl_td = rev;
@@ -694,7 +698,7 @@ ed_halted(struct admhcd *ahcd, struct td *td, int cc, struct td *rev)
                        break;
                /* fallthrough */
        case TD_CC_STALL:
-               if (usb_pipecontrol (urb->pipe))
+               if (usb_pipecontrol(urb->pipe))
                        break;
                /* fallthrough */
        default:
@@ -791,9 +795,8 @@ rescan_this:
                        *prev = td->hwNextTD | savebits;
 
                        /* HC may have partly processed this TD */
-                       urb_print(urb, "PARTIAL",1);
+                       urb_print(urb, "PARTIAL", 1);
                        td_done(ahcd, urb, td);
-                       urb_priv->td_idx++;
 
                        /* if URB is done, clean up */
                        if (urb_priv->td_idx == urb_priv->td_cnt) {
@@ -832,23 +835,73 @@ rescan_this:
  * scanning the (re-reversed) donelist as this does.
  */
 
+static void ed_unhalt(struct admhcd *ahcd, struct ed *ed, struct urb *urb)
+{
+       struct list_head *entry,*tmp;
+       struct urb_priv *urb_priv = urb->hcpriv;
+       __hc32 toggle = ed->hwHeadP & cpu_to_hc32 (ahcd, ED_C);
+
+
+#ifdef ADMHC_VERBOSE_DEBUG
+       admhc_dump_ed(ahcd, "UNHALT", ed, 0);
+#endif
+       /* clear ed halt; this is the td that caused it, but keep it inactive
+        * until its urb->complete() has a chance to clean up.
+        */
+       ed->hwINFO |= cpu_to_hc32 (ahcd, ED_SKIP);
+       wmb();
+       ed->hwHeadP &= ~cpu_to_hc32 (ahcd, ED_H);
+
+       list_for_each_safe(entry, tmp, &ed->td_list) {
+               struct td *td = list_entry(entry, struct td, td_list);
+               __hc32 info;
+
+               if (td->urb != urb)
+                       break;
+
+               info = td->hwINFO;
+               info &= ~cpu_to_hc32(ahcd, TD_CC | TD_OWN);
+               td->hwINFO = info;
+
+               ed->hwHeadP = td->hwNextTD | toggle;
+               wmb();
+       }
+
+}
+
+static inline int is_ed_halted(struct admhcd *ahcd, struct ed *ed)
+{
+       return ((hc32_to_cpup(ahcd, &ed->hwHeadP) & ED_H) == ED_H);
+}
+
+static inline int is_td_halted(struct admhcd *ahcd, struct ed *ed,
+               struct td *td)
+{
+       return ((hc32_to_cpup(ahcd, &ed->hwHeadP) & TD_MASK) ==
+               (hc32_to_cpup(ahcd, &td->hwNextTD) & TD_MASK));
+}
+
 static void ed_update(struct admhcd *ahcd, struct ed *ed)
 {
        struct list_head *entry,*tmp;
 
-       admhc_dump_ed(ahcd, "ed update", ed, 1);
+#ifdef ADMHC_VERBOSE_DEBUG
+       admhc_dump_ed(ahcd, "UPDATE", ed, 0);
+#endif
 
        list_for_each_safe(entry, tmp, &ed->td_list) {
                struct td *td = list_entry(entry, struct td, td_list);
                struct urb *urb = td->urb;
                struct urb_priv *urb_priv = urb->hcpriv;
+               int cc;
 
                if (hc32_to_cpup(ahcd, &td->hwINFO) & TD_OWN)
                        break;
 
                /* update URB's length and status from TD */
-               td_done(ahcd, urb, td);
-               urb_priv->td_idx++;
+               cc = td_done(ahcd, urb, td);
+               if (is_ed_halted(ahcd, ed) && is_td_halted(ahcd, ed, td))
+                       ed_unhalt(ahcd, ed, urb);
 
                /* If all this urb's TDs are done, call complete() */
                if (urb_priv->td_idx == urb_priv->td_cnt)
@@ -888,11 +941,6 @@ static void ed_update(struct admhcd *ahcd, struct ed *ed)
        }
 }
 
-static void ed_halt(struct admhcd *ahcd, struct ed *ed)
-{
-       admhc_dump_ed(ahcd, "ed_halt", ed, 1);
-}
-
 /* there are some tds completed; called in_irq(), with HCD locked */
 static void admhc_td_complete(struct admhcd *ahcd)
 {
@@ -902,14 +950,6 @@ static void admhc_td_complete(struct admhcd *ahcd)
                if (ed->state != ED_OPER)
                        continue;
 
-               if (hc32_to_cpup(ahcd, &ed->hwINFO) & ED_SKIP)
-                       continue;
-
-               if (hc32_to_cpup(ahcd, &ed->hwHeadP) & ED_H) {
-                       ed_halt(ahcd, ed);
-                       continue;
-               }
-
                ed_update(ahcd, ed);
        }
 }
This page took 0.033866 seconds and 4 git commands to generate.