X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fehci-q.c;h=b284c4c896af851e564b1377cd4567ad4c8e29ff;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=b80a2f70d9728afdedabf6ab7554ef5ded6433d2;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index b80a2f70d..b284c4c89 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -83,19 +83,59 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, /*-------------------------------------------------------------------------*/ -/* update halted (but potentially linked) qh */ - static inline void qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) { + /* writes to an active overlay are unsafe */ + BUG_ON(qh->qh_state != QH_STATE_IDLE); + qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma); qh->hw_alt_next = EHCI_LIST_END; + /* Except for control endpoints, we make hardware maintain data + * toggle (like OHCI) ... here (re)initialize the toggle in the QH, + * and set the pseudo-toggle in udev. Only usb_clear_halt() will + * ever clear it. + */ + if (!(qh->hw_info1 & cpu_to_le32(1 << 14))) { + unsigned is_out, epnum; + + is_out = !(qtd->hw_token & cpu_to_le32(1 << 8)); + epnum = (le32_to_cpup(&qh->hw_info1) >> 8) & 0x0f; + if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) { + qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE); + usb_settoggle (qh->dev, epnum, is_out, 1); + } + } + /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ wmb (); qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING); } +/* if it weren't for a common silicon quirk (writing the dummy into the qh + * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault + * recovery (including urb dequeue) would need software changes to a QH... + */ +static void +qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + struct ehci_qtd *qtd; + + if (list_empty (&qh->qtd_list)) + qtd = qh->dummy; + else { + qtd = list_entry (qh->qtd_list.next, + struct ehci_qtd, qtd_list); + /* first qtd may already be partially processed */ + if (cpu_to_le32 (qtd->qtd_dma) == qh->hw_current) + qtd = NULL; + } + + if (qtd) + qh_update (ehci, qh, qtd); +} + /*-------------------------------------------------------------------------*/ static void qtd_copy_status ( @@ -160,7 +200,7 @@ static void qtd_copy_status ( || QTD_CERR(token) == 0) && (!ehci_is_ARC(ehci) || urb->dev->tt->hub != - ehci->hcd.self.root_hub)) { + ehci_to_hcd(ehci)->self.root_hub)) { #ifdef DEBUG struct usb_device *tt = urb->dev->tt->hub; dev_dbg (&tt->dev, @@ -175,15 +215,17 @@ static void qtd_copy_status ( static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs) +__releases(ehci->lock) +__acquires(ehci->lock) { - if (likely (urb->hcpriv != 0)) { + if (likely (urb->hcpriv != NULL)) { struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; /* S-mask in a QH means it's an interrupt urb */ if ((qh->hw_info2 & __constant_cpu_to_le32 (0x00ff)) != 0) { /* ... update hc-wide periodic stats (for usbfs) */ - hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--; + ehci_to_hcd(ehci)->self.bandwidth_int_reqs--; } qh_put (qh); } @@ -220,10 +262,15 @@ ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs) /* complete() can reenter this HCD */ spin_unlock (&ehci->lock); - usb_hcd_giveback_urb (&ehci->hcd, urb, regs); + usb_hcd_giveback_urb (ehci_to_hcd(ehci), urb, regs); spin_lock (&ehci->lock); } +static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); +static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); + +static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh); +static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); /* * Process and free completed qtds for a qh, returning URBs to drivers. @@ -301,12 +348,15 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs) /* stop scanning when we reach qtds the hc is using */ } else if (likely (!stopped - && HCD_IS_RUNNING (ehci->hcd.state))) { + && HCD_IS_RUNNING (ehci_to_hcd(ehci)->state))) { break; } else { stopped = 1; + if (unlikely (!HCD_IS_RUNNING (ehci_to_hcd(ehci)->state))) + urb->status = -ESHUTDOWN; + /* ignore active urbs unless some previous qtd * for the urb faulted (including short read) or * its urb was canceled. we may patch qh or qtds. @@ -355,7 +405,7 @@ halt: } /* last urb's completion might still need calling */ - if (likely (last != 0)) { + if (likely (last != NULL)) { ehci_urb_done (ehci, last->urb, regs); count++; ehci_qtd_free (ehci, last); @@ -364,21 +414,27 @@ halt: /* restore original state; caller must unlink or relink */ qh->qh_state = state; - /* update qh after fault cleanup */ - if (unlikely (stopped != 0) - /* some EHCI 0.95 impls will overlay dummy qtds */ - || qh->hw_qtd_next == EHCI_LIST_END) { - if (list_empty (&qh->qtd_list)) - end = qh->dummy; - else { - end = list_entry (qh->qtd_list.next, - struct ehci_qtd, qtd_list); - /* first qtd may already be partially processed */ - if (cpu_to_le32 (end->qtd_dma) == qh->hw_current) - end = NULL; + /* be sure the hardware's done with the qh before refreshing + * it after fault cleanup, or recovering from silicon wrongly + * overlaying the dummy qtd (which reduces DMA chatter). + */ + if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END) { + switch (state) { + case QH_STATE_IDLE: + qh_refresh(ehci, qh); + break; + case QH_STATE_LINKED: + /* should be rare for periodic transfers, + * except maybe high bandwidth ... + */ + if (qh->period) { + intr_deschedule (ehci, qh); + (void) qh_schedule (ehci, qh); + } else + unlink_async (ehci, qh); + break; + /* otherwise, unlink already started */ } - if (end) - qh_update (ehci, qh, end); } return count; @@ -552,21 +608,6 @@ cleanup: /*-------------------------------------------------------------------------*/ -/* - * Hardware maintains data toggle (like OHCI) ... here we (re)initialize - * the hardware data toggle in the QH, and set the pseudo-toggle in udev - * so we can see if usb_clear_halt() was called. NOP for control, since - * we set up qh->hw_info1 to always use the QTD toggle bits. - */ -static inline void -clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh) -{ - vdbg ("clear toggle, dev %d ep 0x%x-%s", - udev->devnum, ep, is_out ? "out" : "in"); - qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE); - usb_settoggle (udev, ep, is_out, 1); -} - // Would be best to create all qh's from config descriptors, // when each interface/altsetting is established. Unlink // any previous qh and cancel its urbs first; endpoints are @@ -623,9 +664,12 @@ qh_make ( qh->c_usecs = 0; qh->gap_uf = 0; - /* FIXME handle HS periods of less than 1 frame. */ qh->period = urb->interval >> 3; - if (qh->period < 1) { + if (qh->period == 0 && urb->interval != 1) { + /* NOTE interval 2 or 4 uframes could work. + * But interval 1 scheduling is simpler, and + * includes high bandwidth. + */ dbg ("intr period %d uframes, NYET!", urb->interval); goto done; @@ -646,11 +690,11 @@ qh_make ( qh->period = urb->interval; } - - /* support for tt scheduling */ - qh->dev = usb_get_dev (urb->dev); } + /* support for tt scheduling, and access to toggles */ + qh->dev = usb_get_dev (urb->dev); + /* using TT? */ switch (urb->dev->speed) { case USB_SPEED_LOW: @@ -674,7 +718,8 @@ qh_make ( * root hub tt, leave it zeroed. */ if (!ehci_is_ARC(ehci) - || urb->dev->tt->hub != ehci->hcd.self.root_hub) + || urb->dev->tt->hub != + ehci_to_hcd(ehci)->self.root_hub) info2 |= urb->dev->tt->hub->devnum << 16; /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ @@ -710,8 +755,8 @@ done: qh->qh_state = QH_STATE_IDLE; qh->hw_info1 = cpu_to_le32 (info1); qh->hw_info2 = cpu_to_le32 (info2); - qh_update (ehci, qh, qh->dummy); usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); + qh_refresh (ehci, qh); return qh; } @@ -735,12 +780,14 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) (void) handshake (&ehci->regs->status, STS_ASS, 0, 150); cmd |= CMD_ASE | CMD_RUN; writel (cmd, &ehci->regs->command); - ehci->hcd.state = USB_STATE_RUNNING; + ehci_to_hcd(ehci)->state = USB_STATE_RUNNING; /* posted write need not be known to HC yet ... */ } } - qh->hw_token &= ~HALT_BIT; + /* clear halt and/or toggle; and maybe recover from silicon quirk */ + if (qh->qh_state == QH_STATE_IDLE) + qh_refresh (ehci, qh); /* splice right after start */ qh->qh_next = head->qh_next; @@ -789,55 +836,18 @@ static struct ehci_qh *qh_append_tds ( qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); - /* control qh may need patching after enumeration */ + /* control qh may need patching ... */ if (unlikely (epnum == 0)) { - /* set_address changes the address */ - if ((qh->hw_info1 & QH_ADDR_MASK) == 0) - qh->hw_info1 |= cpu_to_le32 ( - usb_pipedevice (urb->pipe)); - - /* for full speed, ep0 maxpacket can grow */ - else if (!(qh->hw_info1 - & __constant_cpu_to_le32 (0x3 << 12))) { - u32 info, max; - - info = le32_to_cpu (qh->hw_info1); - max = urb->dev->descriptor.bMaxPacketSize0; - if (max > (0x07ff & (info >> 16))) { - info &= ~(0x07ff << 16); - info |= max << 16; - qh->hw_info1 = cpu_to_le32 (info); - } - } /* usb_reset_device() briefly reverts to address 0 */ if (usb_pipedevice (urb->pipe) == 0) qh->hw_info1 &= ~QH_ADDR_MASK; } - /* usb_clear_halt() means qh data toggle gets reset */ - if (unlikely (!usb_gettoggle (urb->dev, - (epnum & 0x0f), !(epnum & 0x10))) - && !usb_pipecontrol (urb->pipe)) { - /* "never happens": drivers do stall cleanup right */ - if (qh->qh_state != QH_STATE_IDLE - && !list_empty (&qh->qtd_list) - && qh->qh_state != QH_STATE_COMPLETING) - ehci_warn (ehci, "clear toggle dev%d " - "ep%d%s: not idle\n", - usb_pipedevice (urb->pipe), - epnum & 0x0f, - usb_pipein (urb->pipe) - ? "in" : "out"); - /* else we know this overlay write is safe */ - clear_toggle (urb->dev, - epnum & 0x0f, !(epnum & 0x10), qh); - } - /* just one way to queue requests: swap with the dummy qtd. - * only hc or qh_completions() usually modify the overlay. + * only hc or qh_refresh() ever modify the overlay. */ - if (likely (qtd != 0)) { + if (likely (qtd != NULL)) { struct ehci_qtd *dummy; dma_addr_t dma; __le32 token; @@ -884,43 +894,40 @@ static struct ehci_qh *qh_append_tds ( static int submit_async ( struct ehci_hcd *ehci, + struct usb_host_endpoint *ep, struct urb *urb, struct list_head *qtd_list, int mem_flags ) { struct ehci_qtd *qtd; - struct hcd_dev *dev; int epnum; unsigned long flags; struct ehci_qh *qh = NULL; qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); - dev = (struct hcd_dev *)urb->dev->hcpriv; - epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe)) - epnum |= 0x10; + epnum = ep->desc.bEndpointAddress; #ifdef EHCI_URB_TRACE ehci_dbg (ehci, "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", __FUNCTION__, urb->dev->devpath, urb, - epnum & 0x0f, usb_pipein (urb->pipe) ? "in" : "out", + epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out", urb->transfer_buffer_length, - qtd, dev ? dev->ep [epnum] : (void *)~0); + qtd, ep->hcpriv); #endif spin_lock_irqsave (&ehci->lock, flags); - qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]); + qh = qh_append_tds (ehci, urb, qtd_list, epnum, &ep->hcpriv); /* Control/bulk operations through TTs don't need scheduling, * the HC and TT handle it when the TT has a buffer ready. */ - if (likely (qh != 0)) { + if (likely (qh != NULL)) { if (likely (qh->qh_state == QH_STATE_IDLE)) qh_link_async (ehci, qh_get (qh)); } spin_unlock_irqrestore (&ehci->lock, flags); - if (unlikely (qh == 0)) { + if (unlikely (qh == NULL)) { qtd_list_free (ehci, urb, qtd_list); return -ENOMEM; } @@ -931,8 +938,6 @@ submit_async ( /* the async qh for the qtds being reclaimed are now unlinked from the HC */ -static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); - static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs) { struct ehci_qh *qh = ehci->reclaim; @@ -954,7 +959,7 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs) qh_completions (ehci, qh, regs); if (!list_empty (&qh->qtd_list) - && HCD_IS_RUNNING (ehci->hcd.state)) + && HCD_IS_RUNNING (ehci_to_hcd(ehci)->state)) qh_link_async (ehci, qh); else { qh_put (qh); // refcount from async list @@ -962,8 +967,8 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs) /* it's not free to turn the async schedule on/off; leave it * active but idle for a while once it empties. */ - if (HCD_IS_RUNNING (ehci->hcd.state) - && ehci->async->qh_next.qh == 0) + if (HCD_IS_RUNNING (ehci_to_hcd(ehci)->state) + && ehci->async->qh_next.qh == NULL) timer_action (ehci, TIMER_ASYNC_OFF); } @@ -982,13 +987,10 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) struct ehci_qh *prev; #ifdef DEBUG + assert_spin_locked(&ehci->lock); if (ehci->reclaim || (qh->qh_state != QH_STATE_LINKED && qh->qh_state != QH_STATE_UNLINK_WAIT) -#ifdef CONFIG_SMP -// this macro lies except on SMP compiles - || !spin_is_locked (&ehci->lock) -#endif ) BUG (); #endif @@ -996,7 +998,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) /* stop async schedule right now? */ if (unlikely (qh == ehci->async)) { /* can't get here without STS_ASS set */ - if (ehci->hcd.state != USB_STATE_HALT) { + if (ehci_to_hcd(ehci)->state != USB_STATE_HALT) { writel (cmd & ~CMD_ASE, &ehci->regs->command); wmb (); // handshake later, if we need to @@ -1016,7 +1018,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) prev->qh_next = qh->qh_next; wmb (); - if (unlikely (ehci->hcd.state == USB_STATE_HALT)) { + if (unlikely (ehci_to_hcd(ehci)->state == USB_STATE_HALT)) { /* if (unlikely (qh->reclaim != 0)) * this will recurse, probably not much */ @@ -1044,7 +1046,7 @@ scan_async (struct ehci_hcd *ehci, struct pt_regs *regs) timer_action_done (ehci, TIMER_ASYNC_SHRINK); rescan: qh = ehci->async->qh_next.qh; - if (likely (qh != 0)) { + if (likely (qh != NULL)) { do { /* clean any finished work for this qh */ if (!list_empty (&qh->qtd_list)