aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2006-09-18 20:03:16 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-09-27 14:59:00 -0400
commit26f953fd884ea4879585287917f855c63c6b2666 (patch)
tree78e6bd71dc1bb4089bc8589eb995765d64d4797d /drivers/usb
parent353a4098c61272b33a02ec5802fb3859fec91a0e (diff)
USB: EHCI update VIA workaround
This revamps handling of the hardware "async advance" IRQ, and its watchdog timer. Basically it dis-entangles that important timeout from the others, simplifying the associated state and code to make it more robust. This reportedly improves behavior of EHCI on some systems with VIA chips, and AFAIK won't affect non-VIA hardware. VIA systems need this code to recover from silcon bugs whereby the "async advance" IRQ isn't issued. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/ehci-dbg.c4
-rw-r--r--drivers/usb/host/ehci-hcd.c73
-rw-r--r--drivers/usb/host/ehci-hub.c2
-rw-r--r--drivers/usb/host/ehci-pci.c2
-rw-r--r--drivers/usb/host/ehci-q.c6
-rw-r--r--drivers/usb/host/ehci.h22
6 files changed, 70 insertions, 39 deletions
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 34b7a31cd85b..23b95b2bfe15 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -754,9 +754,7 @@ show_registers (struct class_device *class_dev, char *buf)
754 } 754 }
755 755
756 if (ehci->reclaim) { 756 if (ehci->reclaim) {
757 temp = scnprintf (next, size, "reclaim qh %p%s\n", 757 temp = scnprintf (next, size, "reclaim qh %p\n", ehci->reclaim);
758 ehci->reclaim,
759 ehci->reclaim_ready ? " ready" : "");
760 size -= temp; 758 size -= temp;
761 next += temp; 759 next += temp;
762 } 760 }
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 4e1a8c308893..5ac918591131 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -111,7 +111,7 @@ static const char hcd_name [] = "ehci_hcd";
111#define EHCI_TUNE_MULT_TT 1 111#define EHCI_TUNE_MULT_TT 1
112#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ 112#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
113 113
114#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ 114#define EHCI_IAA_MSECS 10 /* arbitrary */
115#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ 115#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
116#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ 116#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
117#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ 117#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
@@ -254,6 +254,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
254 254
255/*-------------------------------------------------------------------------*/ 255/*-------------------------------------------------------------------------*/
256 256
257static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs);
257static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs); 258static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
258 259
259#include "ehci-hub.c" 260#include "ehci-hub.c"
@@ -263,28 +264,39 @@ static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
263 264
264/*-------------------------------------------------------------------------*/ 265/*-------------------------------------------------------------------------*/
265 266
266static void ehci_watchdog (unsigned long param) 267static void ehci_iaa_watchdog (unsigned long param)
267{ 268{
268 struct ehci_hcd *ehci = (struct ehci_hcd *) param; 269 struct ehci_hcd *ehci = (struct ehci_hcd *) param;
269 unsigned long flags; 270 unsigned long flags;
271 u32 status;
270 272
271 spin_lock_irqsave (&ehci->lock, flags); 273 spin_lock_irqsave (&ehci->lock, flags);
274 WARN_ON(!ehci->reclaim);
272 275
273 /* lost IAA irqs wedge things badly; seen with a vt8235 */ 276 /* lost IAA irqs wedge things badly; seen first with a vt8235 */
274 if (ehci->reclaim) { 277 if (ehci->reclaim) {
275 u32 status = readl (&ehci->regs->status); 278 status = readl (&ehci->regs->status);
276
277 if (status & STS_IAA) { 279 if (status & STS_IAA) {
278 ehci_vdbg (ehci, "lost IAA\n"); 280 ehci_vdbg (ehci, "lost IAA\n");
279 COUNT (ehci->stats.lost_iaa); 281 COUNT (ehci->stats.lost_iaa);
280 writel (STS_IAA, &ehci->regs->status); 282 writel (STS_IAA, &ehci->regs->status);
281 ehci->reclaim_ready = 1; 283 end_unlink_async (ehci, NULL);
282 } 284 }
283 } 285 }
284 286
285 /* stop async processing after it's idled a bit */ 287 spin_unlock_irqrestore (&ehci->lock, flags);
288}
289
290static void ehci_watchdog (unsigned long param)
291{
292 struct ehci_hcd *ehci = (struct ehci_hcd *) param;
293 unsigned long flags;
294
295 spin_lock_irqsave (&ehci->lock, flags);
296
297 /* stop async processing after it's idled a bit */
286 if (test_bit (TIMER_ASYNC_OFF, &ehci->actions)) 298 if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
287 start_unlink_async (ehci, ehci->async); 299 start_unlink_async (ehci, ehci->async);
288 300
289 /* ehci could run by timer, without IRQs ... */ 301 /* ehci could run by timer, without IRQs ... */
290 ehci_work (ehci, NULL); 302 ehci_work (ehci, NULL);
@@ -333,8 +345,6 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
333static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs) 345static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
334{ 346{
335 timer_action_done (ehci, TIMER_IO_WATCHDOG); 347 timer_action_done (ehci, TIMER_IO_WATCHDOG);
336 if (ehci->reclaim_ready)
337 end_unlink_async (ehci, regs);
338 348
339 /* another CPU may drop ehci->lock during a schedule scan while 349 /* another CPU may drop ehci->lock during a schedule scan while
340 * it reports urb completions. this flag guards against bogus 350 * it reports urb completions. this flag guards against bogus
@@ -369,6 +379,7 @@ static void ehci_stop (struct usb_hcd *hcd)
369 379
370 /* no more interrupts ... */ 380 /* no more interrupts ... */
371 del_timer_sync (&ehci->watchdog); 381 del_timer_sync (&ehci->watchdog);
382 del_timer_sync (&ehci->iaa_watchdog);
372 383
373 spin_lock_irq(&ehci->lock); 384 spin_lock_irq(&ehci->lock);
374 if (HC_IS_RUNNING (hcd->state)) 385 if (HC_IS_RUNNING (hcd->state))
@@ -415,6 +426,10 @@ static int ehci_init(struct usb_hcd *hcd)
415 ehci->watchdog.function = ehci_watchdog; 426 ehci->watchdog.function = ehci_watchdog;
416 ehci->watchdog.data = (unsigned long) ehci; 427 ehci->watchdog.data = (unsigned long) ehci;
417 428
429 init_timer(&ehci->iaa_watchdog);
430 ehci->iaa_watchdog.function = ehci_iaa_watchdog;
431 ehci->iaa_watchdog.data = (unsigned long) ehci;
432
418 /* 433 /*
419 * hw default: 1K periodic list heads, one per frame. 434 * hw default: 1K periodic list heads, one per frame.
420 * periodic_size can shrink by USBCMD update if hcc_params allows. 435 * periodic_size can shrink by USBCMD update if hcc_params allows.
@@ -431,7 +446,6 @@ static int ehci_init(struct usb_hcd *hcd)
431 ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); 446 ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
432 447
433 ehci->reclaim = NULL; 448 ehci->reclaim = NULL;
434 ehci->reclaim_ready = 0;
435 ehci->next_uframe = -1; 449 ehci->next_uframe = -1;
436 450
437 /* 451 /*
@@ -605,7 +619,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
605 /* complete the unlinking of some qh [4.15.2.3] */ 619 /* complete the unlinking of some qh [4.15.2.3] */
606 if (status & STS_IAA) { 620 if (status & STS_IAA) {
607 COUNT (ehci->stats.reclaim); 621 COUNT (ehci->stats.reclaim);
608 ehci->reclaim_ready = 1; 622 end_unlink_async (ehci, regs);
609 bh = 1; 623 bh = 1;
610 } 624 }
611 625
@@ -709,10 +723,14 @@ static int ehci_urb_enqueue (
709 723
710static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) 724static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
711{ 725{
712 /* if we need to use IAA and it's busy, defer */ 726 // BUG_ON(qh->qh_state != QH_STATE_LINKED);
713 if (qh->qh_state == QH_STATE_LINKED 727
714 && ehci->reclaim 728 /* failfast */
715 && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) { 729 if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
730 end_unlink_async (ehci, NULL);
731
732 /* defer till later if busy */
733 else if (ehci->reclaim) {
716 struct ehci_qh *last; 734 struct ehci_qh *last;
717 735
718 for (last = ehci->reclaim; 736 for (last = ehci->reclaim;
@@ -722,12 +740,8 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
722 qh->qh_state = QH_STATE_UNLINK_WAIT; 740 qh->qh_state = QH_STATE_UNLINK_WAIT;
723 last->reclaim = qh; 741 last->reclaim = qh;
724 742
725 /* bypass IAA if the hc can't care */ 743 /* start IAA cycle */
726 } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim) 744 } else
727 end_unlink_async (ehci, NULL);
728
729 /* something else might have unlinked the qh by now */
730 if (qh->qh_state == QH_STATE_LINKED)
731 start_unlink_async (ehci, qh); 745 start_unlink_async (ehci, qh);
732} 746}
733 747
@@ -749,7 +763,19 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
749 qh = (struct ehci_qh *) urb->hcpriv; 763 qh = (struct ehci_qh *) urb->hcpriv;
750 if (!qh) 764 if (!qh)
751 break; 765 break;
752 unlink_async (ehci, qh); 766 switch (qh->qh_state) {
767 case QH_STATE_LINKED:
768 case QH_STATE_COMPLETING:
769 unlink_async (ehci, qh);
770 break;
771 case QH_STATE_UNLINK:
772 case QH_STATE_UNLINK_WAIT:
773 /* already started */
774 break;
775 case QH_STATE_IDLE:
776 WARN_ON(1);
777 break;
778 }
753 break; 779 break;
754 780
755 case PIPE_INTERRUPT: 781 case PIPE_INTERRUPT:
@@ -841,6 +867,7 @@ rescan:
841 unlink_async (ehci, qh); 867 unlink_async (ehci, qh);
842 /* FALL THROUGH */ 868 /* FALL THROUGH */
843 case QH_STATE_UNLINK: /* wait for hw to finish? */ 869 case QH_STATE_UNLINK: /* wait for hw to finish? */
870 case QH_STATE_UNLINK_WAIT:
844idle_timeout: 871idle_timeout:
845 spin_unlock_irqrestore (&ehci->lock, flags); 872 spin_unlock_irqrestore (&ehci->lock, flags);
846 schedule_timeout_uninterruptible(1); 873 schedule_timeout_uninterruptible(1);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index a5eeb9cd6ab2..b2ee13c58517 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -48,7 +48,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
48 } 48 }
49 ehci->command = readl (&ehci->regs->command); 49 ehci->command = readl (&ehci->regs->command);
50 if (ehci->reclaim) 50 if (ehci->reclaim)
51 ehci->reclaim_ready = 1; 51 end_unlink_async (ehci, NULL);
52 ehci_work(ehci, NULL); 52 ehci_work(ehci, NULL);
53 53
54 /* suspend any active/unsuspended ports, maybe allow wakeup */ 54 /* suspend any active/unsuspended ports, maybe allow wakeup */
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index e6a3bcddd55b..08d0472d4f57 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -303,7 +303,7 @@ restart:
303 /* emptying the schedule aborts any urbs */ 303 /* emptying the schedule aborts any urbs */
304 spin_lock_irq(&ehci->lock); 304 spin_lock_irq(&ehci->lock);
305 if (ehci->reclaim) 305 if (ehci->reclaim)
306 ehci->reclaim_ready = 1; 306 end_unlink_async (ehci, NULL);
307 ehci_work(ehci, NULL); 307 ehci_work(ehci, NULL);
308 spin_unlock_irq(&ehci->lock); 308 spin_unlock_irq(&ehci->lock);
309 309
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index c0da40bbfa35..7fc25b6bd7d2 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -967,7 +967,7 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
967 struct ehci_qh *qh = ehci->reclaim; 967 struct ehci_qh *qh = ehci->reclaim;
968 struct ehci_qh *next; 968 struct ehci_qh *next;
969 969
970 timer_action_done (ehci, TIMER_IAA_WATCHDOG); 970 iaa_watchdog_done (ehci);
971 971
972 // qh->hw_next = cpu_to_le32 (qh->qh_dma); 972 // qh->hw_next = cpu_to_le32 (qh->qh_dma);
973 qh->qh_state = QH_STATE_IDLE; 973 qh->qh_state = QH_STATE_IDLE;
@@ -977,7 +977,6 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
977 /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ 977 /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
978 next = qh->reclaim; 978 next = qh->reclaim;
979 ehci->reclaim = next; 979 ehci->reclaim = next;
980 ehci->reclaim_ready = 0;
981 qh->reclaim = NULL; 980 qh->reclaim = NULL;
982 981
983 qh_completions (ehci, qh, regs); 982 qh_completions (ehci, qh, regs);
@@ -1052,11 +1051,10 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
1052 return; 1051 return;
1053 } 1052 }
1054 1053
1055 ehci->reclaim_ready = 0;
1056 cmd |= CMD_IAAD; 1054 cmd |= CMD_IAAD;
1057 writel (cmd, &ehci->regs->command); 1055 writel (cmd, &ehci->regs->command);
1058 (void) readl (&ehci->regs->command); 1056 (void) readl (&ehci->regs->command);
1059 timer_action (ehci, TIMER_IAA_WATCHDOG); 1057 iaa_watchdog_start (ehci);
1060} 1058}
1061 1059
1062/*-------------------------------------------------------------------------*/ 1060/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index bbc3082a73d7..6aac39f50e07 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -58,7 +58,6 @@ struct ehci_hcd { /* one per controller */
58 /* async schedule support */ 58 /* async schedule support */
59 struct ehci_qh *async; 59 struct ehci_qh *async;
60 struct ehci_qh *reclaim; 60 struct ehci_qh *reclaim;
61 unsigned reclaim_ready : 1;
62 unsigned scanning : 1; 61 unsigned scanning : 1;
63 62
64 /* periodic schedule support */ 63 /* periodic schedule support */
@@ -81,6 +80,7 @@ struct ehci_hcd { /* one per controller */
81 struct dma_pool *itd_pool; /* itd per iso urb */ 80 struct dma_pool *itd_pool; /* itd per iso urb */
82 struct dma_pool *sitd_pool; /* sitd per split iso urb */ 81 struct dma_pool *sitd_pool; /* sitd per split iso urb */
83 82
83 struct timer_list iaa_watchdog;
84 struct timer_list watchdog; 84 struct timer_list watchdog;
85 unsigned long actions; 85 unsigned long actions;
86 unsigned stamp; 86 unsigned stamp;
@@ -114,9 +114,21 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
114} 114}
115 115
116 116
117static inline void
118iaa_watchdog_start (struct ehci_hcd *ehci)
119{
120 WARN_ON(timer_pending(&ehci->iaa_watchdog));
121 mod_timer (&ehci->iaa_watchdog,
122 jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
123}
124
125static inline void iaa_watchdog_done (struct ehci_hcd *ehci)
126{
127 del_timer (&ehci->iaa_watchdog);
128}
129
117enum ehci_timer_action { 130enum ehci_timer_action {
118 TIMER_IO_WATCHDOG, 131 TIMER_IO_WATCHDOG,
119 TIMER_IAA_WATCHDOG,
120 TIMER_ASYNC_SHRINK, 132 TIMER_ASYNC_SHRINK,
121 TIMER_ASYNC_OFF, 133 TIMER_ASYNC_OFF,
122}; 134};
@@ -134,9 +146,6 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
134 unsigned long t; 146 unsigned long t;
135 147
136 switch (action) { 148 switch (action) {
137 case TIMER_IAA_WATCHDOG:
138 t = EHCI_IAA_JIFFIES;
139 break;
140 case TIMER_IO_WATCHDOG: 149 case TIMER_IO_WATCHDOG:
141 t = EHCI_IO_JIFFIES; 150 t = EHCI_IO_JIFFIES;
142 break; 151 break;
@@ -153,8 +162,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
153 // async queue SHRINK often precedes IAA. while it's ready 162 // async queue SHRINK often precedes IAA. while it's ready
154 // to go OFF neither can matter, and afterwards the IO 163 // to go OFF neither can matter, and afterwards the IO
155 // watchdog stops unless there's still periodic traffic. 164 // watchdog stops unless there's still periodic traffic.
156 if (action != TIMER_IAA_WATCHDOG 165 if (time_before_eq(t, ehci->watchdog.expires)
157 && t > ehci->watchdog.expires
158 && timer_pending (&ehci->watchdog)) 166 && timer_pending (&ehci->watchdog))
159 return; 167 return;
160 mod_timer (&ehci->watchdog, t); 168 mod_timer (&ehci->watchdog, t);