diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci-mem.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-q.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/ehci-sched.c | 66 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 6 | ||||
-rw-r--r-- | drivers/usb/host/ohci-pci.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/uhci-hcd.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/whci/asl.c | 13 | ||||
-rw-r--r-- | drivers/usb/host/whci/hw.c | 15 | ||||
-rw-r--r-- | drivers/usb/host/whci/pzl.c | 13 | ||||
-rw-r--r-- | drivers/usb/host/whci/whcd.h | 1 |
12 files changed, 97 insertions, 26 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 4725d15..e551bb3 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -485,6 +485,7 @@ static int ehci_init(struct usb_hcd *hcd) * periodic_size can shrink by USBCMD update if hcc_params allows. */ ehci->periodic_size = DEFAULT_I_TDPS; + INIT_LIST_HEAD(&ehci->cached_itd_list); if ((retval = ehci_mem_init(ehci, GFP_KERNEL)) < 0) return retval; @@ -497,6 +498,7 @@ static int ehci_init(struct usb_hcd *hcd) ehci->reclaim = NULL; ehci->next_uframe = -1; + ehci->clock_frame = -1; /* * dedicate a qh for the async ring head, since we couldn't unlink diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 0431397..10d5291 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -128,6 +128,7 @@ static inline void qh_put (struct ehci_qh *qh) static void ehci_mem_cleanup (struct ehci_hcd *ehci) { + free_cached_itd_list(ehci); if (ehci->async) qh_put (ehci->async); ehci->async = NULL; diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index bb21fb0..abb9a77 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -432,7 +432,6 @@ static struct pci_driver ehci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif .shutdown = usb_hcd_pci_shutdown, diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 3712b92..ecc9b66 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1095,7 +1095,8 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) prev->qh_next = qh->qh_next; wmb (); - if (unlikely (ehci_to_hcd(ehci)->state == HC_STATE_HALT)) { + /* If the controller isn't running, we don't have to wait for it */ + if (unlikely(!HC_IS_RUNNING(ehci_to_hcd(ehci)->state))) { /* if (unlikely (qh->reclaim != 0)) * this will recurse, probably not much */ diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index a081ee6..1d0b49e 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1004,7 +1004,8 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream) is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0; stream->bEndpointAddress &= 0x0f; - stream->ep->hcpriv = NULL; + if (stream->ep) + stream->ep->hcpriv = NULL; if (stream->rescheduled) { ehci_info (ehci, "ep%d%s-iso rescheduled " @@ -1535,7 +1536,7 @@ itd_link_urb ( struct ehci_itd, itd_list); list_move_tail (&itd->itd_list, &stream->td_list); itd->stream = iso_stream_get (stream); - itd->urb = usb_get_urb (urb); + itd->urb = urb; itd_init (ehci, stream, itd); } @@ -1644,7 +1645,7 @@ itd_complete ( (void) disable_periodic(ehci); ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; - if (unlikely (list_empty (&stream->td_list))) { + if (unlikely(list_is_singular(&stream->td_list))) { ehci_to_hcd(ehci)->self.bandwidth_allocated -= stream->bandwidth; ehci_vdbg (ehci, @@ -1653,14 +1654,27 @@ itd_complete ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } iso_stream_put (ehci, stream); - /* OK to recycle this ITD now that its completion callback ran. */ + done: - usb_put_urb(urb); itd->urb = NULL; - itd->stream = NULL; - list_move(&itd->itd_list, &stream->free_list); - iso_stream_put(ehci, stream); - + if (ehci->clock_frame != itd->frame || itd->index[7] != -1) { + /* OK to recycle this ITD now. */ + itd->stream = NULL; + list_move(&itd->itd_list, &stream->free_list); + iso_stream_put(ehci, stream); + } else { + /* HW might remember this ITD, so we can't recycle it yet. + * Move it to a safe place until a new frame starts. + */ + list_move(&itd->itd_list, &ehci->cached_itd_list); + if (stream->refcount == 2) { + /* If iso_stream_put() were called here, stream + * would be freed. Instead, just prevent reuse. + */ + stream->ep->hcpriv = NULL; + stream->ep = NULL; + } + } return retval; } @@ -1934,7 +1948,7 @@ sitd_link_urb ( struct ehci_sitd, sitd_list); list_move_tail (&sitd->sitd_list, &stream->td_list); sitd->stream = iso_stream_get (stream); - sitd->urb = usb_get_urb (urb); + sitd->urb = urb; sitd_patch(ehci, stream, sitd, sched, packet); sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size, @@ -2019,7 +2033,7 @@ sitd_complete ( (void) disable_periodic(ehci); ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; - if (list_empty (&stream->td_list)) { + if (list_is_singular(&stream->td_list)) { ehci_to_hcd(ehci)->self.bandwidth_allocated -= stream->bandwidth; ehci_vdbg (ehci, @@ -2030,7 +2044,6 @@ sitd_complete ( iso_stream_put (ehci, stream); /* OK to recycle this SITD now that its completion callback ran. */ done: - usb_put_urb(urb); sitd->urb = NULL; sitd->stream = NULL; list_move(&sitd->sitd_list, &stream->free_list); @@ -2101,6 +2114,20 @@ done: /*-------------------------------------------------------------------------*/ +static void free_cached_itd_list(struct ehci_hcd *ehci) +{ + struct ehci_itd *itd, *n; + + list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { + struct ehci_iso_stream *stream = itd->stream; + itd->stream = NULL; + list_move(&itd->itd_list, &stream->free_list); + iso_stream_put(ehci, stream); + } +} + +/*-------------------------------------------------------------------------*/ + static void scan_periodic (struct ehci_hcd *ehci) { @@ -2115,10 +2142,17 @@ scan_periodic (struct ehci_hcd *ehci) * Touches as few pages as possible: cache-friendly. */ now_uframe = ehci->next_uframe; - if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) + if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { clock = ehci_readl(ehci, &ehci->regs->frame_index); - else + clock_frame = (clock >> 3) % ehci->periodic_size; + } else { clock = now_uframe + mod - 1; + clock_frame = -1; + } + if (ehci->clock_frame != clock_frame) { + free_cached_itd_list(ehci); + ehci->clock_frame = clock_frame; + } clock %= mod; clock_frame = clock >> 3; @@ -2277,6 +2311,10 @@ restart: /* rescan the rest of this frame, then ... */ clock = now; clock_frame = clock >> 3; + if (ehci->clock_frame != clock_frame) { + free_cached_itd_list(ehci); + ehci->clock_frame = clock_frame; + } } else { now_uframe++; now_uframe %= mod; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index fb7054cc..262b00c 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -87,6 +87,10 @@ struct ehci_hcd { /* one per controller */ int next_uframe; /* scan periodic, start here */ unsigned periodic_sched; /* periodic activity count */ + /* list of itds completed while clock_frame was still active */ + struct list_head cached_itd_list; + unsigned clock_frame; + /* per root hub port */ unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; @@ -220,6 +224,8 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) } } +static void free_cached_itd_list(struct ehci_hcd *ehci); + /*-------------------------------------------------------------------------*/ #include <linux/usb/ehci_def.h> diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 5d625c3..f9961b4 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -487,7 +487,6 @@ static struct pci_driver ohci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 944f7e0..cf5e4cf 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -942,7 +942,6 @@ static struct pci_driver uhci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif /* PM */ }; diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c index 577c0d2..958751c 100644 --- a/drivers/usb/host/whci/asl.c +++ b/drivers/usb/host/whci/asl.c @@ -170,12 +170,17 @@ void asl_stop(struct whc *whc) void asl_update(struct whc *whc, uint32_t wusbcmd) { struct wusbhc *wusbhc = &whc->wusbhc; + long t; mutex_lock(&wusbhc->mutex); if (wusbhc->active) { whc_write_wusbcmd(whc, wusbcmd, wusbcmd); - wait_event(whc->async_list_wq, - (le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0); + t = wait_event_timeout( + whc->async_list_wq, + (le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0, + msecs_to_jiffies(1000)); + if (t == 0) + whc_hw_error(whc, "ASL update timeout"); } mutex_unlock(&wusbhc->mutex); } @@ -222,13 +227,13 @@ void scan_async_work(struct work_struct *work) * Now that the ASL is updated, complete the removal of any * removed qsets. */ - spin_lock(&whc->lock); + spin_lock_irq(&whc->lock); list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) { qset_remove_complete(whc, qset); } - spin_unlock(&whc->lock); + spin_unlock_irq(&whc->lock); } /** diff --git a/drivers/usb/host/whci/hw.c b/drivers/usb/host/whci/hw.c index d498e72..6afa2e3 100644 --- a/drivers/usb/host/whci/hw.c +++ b/drivers/usb/host/whci/hw.c @@ -87,3 +87,18 @@ out: return ret; } + +/** + * whc_hw_error - recover from a hardware error + * @whc: the WHCI HC that broke. + * @reason: a description of the failure. + * + * Recover from broken hardware with a full reset. + */ +void whc_hw_error(struct whc *whc, const char *reason) +{ + struct wusbhc *wusbhc = &whc->wusbhc; + + dev_err(&whc->umc->dev, "hardware error: %s\n", reason); + wusbhc_reset_all(wusbhc); +} diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c index 2ae5abf..df8b85f 100644 --- a/drivers/usb/host/whci/pzl.c +++ b/drivers/usb/host/whci/pzl.c @@ -183,12 +183,17 @@ void pzl_stop(struct whc *whc) void pzl_update(struct whc *whc, uint32_t wusbcmd) { struct wusbhc *wusbhc = &whc->wusbhc; + long t; mutex_lock(&wusbhc->mutex); if (wusbhc->active) { whc_write_wusbcmd(whc, wusbcmd, wusbcmd); - wait_event(whc->periodic_list_wq, - (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0); + t = wait_event_timeout( + whc->periodic_list_wq, + (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0, + msecs_to_jiffies(1000)); + if (t == 0) + whc_hw_error(whc, "PZL update timeout"); } mutex_unlock(&wusbhc->mutex); } @@ -250,13 +255,13 @@ void scan_periodic_work(struct work_struct *work) * Now that the PZL is updated, complete the removal of any * removed qsets. */ - spin_lock(&whc->lock); + spin_lock_irq(&whc->lock); list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) { qset_remove_complete(whc, qset); } - spin_unlock(&whc->lock); + spin_unlock_irq(&whc->lock); } /** diff --git a/drivers/usb/host/whci/whcd.h b/drivers/usb/host/whci/whcd.h index 0f3540f..d3543a1 100644 --- a/drivers/usb/host/whci/whcd.h +++ b/drivers/usb/host/whci/whcd.h @@ -137,6 +137,7 @@ void whc_clean_up(struct whc *whc); /* hw.c */ void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val); int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len); +void whc_hw_error(struct whc *whc, const char *reason); /* wusb.c */ int whc_wusbhc_start(struct wusbhc *wusbhc); |