aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-12-11 16:05:30 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2008-02-01 17:34:55 -0500
commit07d29b63ef6b39963ab37818653284d861cf55af (patch)
tree10460d9d13ad7284b644181af66ecdbf416cc5ba /drivers/usb
parent2e2eb83ffd1aeb92bf8793eea892b5bc05a993ea (diff)
USB: EHCI: add separate IAA watchdog timer
This patch (as1028) was mostly written by David Brownell; I made only a few changes (extra log info and a small bug fix -- which might account for why David's version had to be reverted). It adds a new watchdog timer to the ehci-hcd driver to be used exclusively for detecting lost or missing IAA notifications. Previously a shared timer had been used, which may have led to some problems as reported by Christian Hoffmann. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: David Brownell <david-b@pacbell.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.c77
-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, 75 insertions, 38 deletions
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 39673f8194d9..64ebfc5548a3 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -786,9 +786,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
786 } 786 }
787 787
788 if (ehci->reclaim) { 788 if (ehci->reclaim) {
789 temp = scnprintf (next, size, "reclaim qh %p%s\n", 789 temp = scnprintf(next, size, "reclaim qh %p\n", ehci->reclaim);
790 ehci->reclaim,
791 ehci->reclaim_ready ? " ready" : "");
792 size -= temp; 790 size -= temp;
793 next += temp; 791 next += temp;
794 } 792 }
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 264182654f4c..5917c6b7ce33 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -110,7 +110,7 @@ static const char hcd_name [] = "ehci_hcd";
110#define EHCI_TUNE_MULT_TT 1 110#define EHCI_TUNE_MULT_TT 1
111#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ 111#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
112 112
113#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ 113#define EHCI_IAA_MSECS 10 /* arbitrary */
114#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ 114#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
115#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ 115#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
116#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ 116#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
@@ -267,6 +267,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
267 267
268/*-------------------------------------------------------------------------*/ 268/*-------------------------------------------------------------------------*/
269 269
270static void end_unlink_async(struct ehci_hcd *ehci);
270static void ehci_work(struct ehci_hcd *ehci); 271static void ehci_work(struct ehci_hcd *ehci);
271 272
272#include "ehci-hub.c" 273#include "ehci-hub.c"
@@ -276,25 +277,41 @@ static void ehci_work(struct ehci_hcd *ehci);
276 277
277/*-------------------------------------------------------------------------*/ 278/*-------------------------------------------------------------------------*/
278 279
279static void ehci_watchdog (unsigned long param) 280static void ehci_iaa_watchdog(unsigned long param)
280{ 281{
281 struct ehci_hcd *ehci = (struct ehci_hcd *) param; 282 struct ehci_hcd *ehci = (struct ehci_hcd *) param;
282 unsigned long flags; 283 unsigned long flags;
284 u32 status, cmd;
283 285
284 spin_lock_irqsave (&ehci->lock, flags); 286 spin_lock_irqsave (&ehci->lock, flags);
287 WARN_ON(!ehci->reclaim);
285 288
286 /* lost IAA irqs wedge things badly; seen with a vt8235 */ 289 status = ehci_readl(ehci, &ehci->regs->status);
290 cmd = ehci_readl(ehci, &ehci->regs->command);
291 ehci_dbg(ehci, "IAA watchdog: status %x cmd %x\n", status, cmd);
292
293 /* lost IAA irqs wedge things badly; seen first with a vt8235 */
287 if (ehci->reclaim) { 294 if (ehci->reclaim) {
288 u32 status = ehci_readl(ehci, &ehci->regs->status);
289 if (status & STS_IAA) { 295 if (status & STS_IAA) {
290 ehci_vdbg (ehci, "lost IAA\n"); 296 ehci_vdbg (ehci, "lost IAA\n");
291 COUNT (ehci->stats.lost_iaa); 297 COUNT (ehci->stats.lost_iaa);
292 ehci_writel(ehci, STS_IAA, &ehci->regs->status); 298 ehci_writel(ehci, STS_IAA, &ehci->regs->status);
293 ehci->reclaim_ready = 1;
294 } 299 }
300 ehci_writel(ehci, cmd & ~CMD_IAAD, &ehci->regs->command);
301 end_unlink_async(ehci);
295 } 302 }
296 303
297 /* stop async processing after it's idled a bit */ 304 spin_unlock_irqrestore(&ehci->lock, flags);
305}
306
307static void ehci_watchdog(unsigned long param)
308{
309 struct ehci_hcd *ehci = (struct ehci_hcd *) param;
310 unsigned long flags;
311
312 spin_lock_irqsave(&ehci->lock, flags);
313
314 /* stop async processing after it's idled a bit */
298 if (test_bit (TIMER_ASYNC_OFF, &ehci->actions)) 315 if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
299 start_unlink_async (ehci, ehci->async); 316 start_unlink_async (ehci, ehci->async);
300 317
@@ -364,8 +381,6 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
364static void ehci_work (struct ehci_hcd *ehci) 381static void ehci_work (struct ehci_hcd *ehci)
365{ 382{
366 timer_action_done (ehci, TIMER_IO_WATCHDOG); 383 timer_action_done (ehci, TIMER_IO_WATCHDOG);
367 if (ehci->reclaim_ready)
368 end_unlink_async (ehci);
369 384
370 /* another CPU may drop ehci->lock during a schedule scan while 385 /* another CPU may drop ehci->lock during a schedule scan while
371 * it reports urb completions. this flag guards against bogus 386 * it reports urb completions. this flag guards against bogus
@@ -400,6 +415,7 @@ static void ehci_stop (struct usb_hcd *hcd)
400 415
401 /* no more interrupts ... */ 416 /* no more interrupts ... */
402 del_timer_sync (&ehci->watchdog); 417 del_timer_sync (&ehci->watchdog);
418 del_timer_sync(&ehci->iaa_watchdog);
403 419
404 spin_lock_irq(&ehci->lock); 420 spin_lock_irq(&ehci->lock);
405 if (HC_IS_RUNNING (hcd->state)) 421 if (HC_IS_RUNNING (hcd->state))
@@ -448,6 +464,10 @@ static int ehci_init(struct usb_hcd *hcd)
448 ehci->watchdog.function = ehci_watchdog; 464 ehci->watchdog.function = ehci_watchdog;
449 ehci->watchdog.data = (unsigned long) ehci; 465 ehci->watchdog.data = (unsigned long) ehci;
450 466
467 init_timer(&ehci->iaa_watchdog);
468 ehci->iaa_watchdog.function = ehci_iaa_watchdog;
469 ehci->iaa_watchdog.data = (unsigned long) ehci;
470
451 /* 471 /*
452 * hw default: 1K periodic list heads, one per frame. 472 * hw default: 1K periodic list heads, one per frame.
453 * periodic_size can shrink by USBCMD update if hcc_params allows. 473 * periodic_size can shrink by USBCMD update if hcc_params allows.
@@ -464,7 +484,6 @@ static int ehci_init(struct usb_hcd *hcd)
464 ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); 484 ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
465 485
466 ehci->reclaim = NULL; 486 ehci->reclaim = NULL;
467 ehci->reclaim_ready = 0;
468 ehci->next_uframe = -1; 487 ehci->next_uframe = -1;
469 488
470 /* 489 /*
@@ -655,8 +674,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
655 /* complete the unlinking of some qh [4.15.2.3] */ 674 /* complete the unlinking of some qh [4.15.2.3] */
656 if (status & STS_IAA) { 675 if (status & STS_IAA) {
657 COUNT (ehci->stats.reclaim); 676 COUNT (ehci->stats.reclaim);
658 ehci->reclaim_ready = 1; 677 end_unlink_async(ehci);
659 bh = 1;
660 } 678 }
661 679
662 /* remote wakeup [4.3.1] */ 680 /* remote wakeup [4.3.1] */
@@ -762,10 +780,16 @@ static int ehci_urb_enqueue (
762 780
763static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) 781static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
764{ 782{
765 /* if we need to use IAA and it's busy, defer */ 783 /* failfast */
766 if (qh->qh_state == QH_STATE_LINKED 784 if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state))
767 && ehci->reclaim 785 end_unlink_async(ehci);
768 && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) { 786
787 /* if it's not linked then there's nothing to do */
788 if (qh->qh_state != QH_STATE_LINKED)
789 ;
790
791 /* defer till later if busy */
792 else if (ehci->reclaim) {
769 struct ehci_qh *last; 793 struct ehci_qh *last;
770 794
771 for (last = ehci->reclaim; 795 for (last = ehci->reclaim;
@@ -775,12 +799,8 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
775 qh->qh_state = QH_STATE_UNLINK_WAIT; 799 qh->qh_state = QH_STATE_UNLINK_WAIT;
776 last->reclaim = qh; 800 last->reclaim = qh;
777 801
778 /* bypass IAA if the hc can't care */ 802 /* start IAA cycle */
779 } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim) 803 } else
780 end_unlink_async (ehci);
781
782 /* something else might have unlinked the qh by now */
783 if (qh->qh_state == QH_STATE_LINKED)
784 start_unlink_async (ehci, qh); 804 start_unlink_async (ehci, qh);
785} 805}
786 806
@@ -807,7 +827,19 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
807 qh = (struct ehci_qh *) urb->hcpriv; 827 qh = (struct ehci_qh *) urb->hcpriv;
808 if (!qh) 828 if (!qh)
809 break; 829 break;
810 unlink_async (ehci, qh); 830 switch (qh->qh_state) {
831 case QH_STATE_LINKED:
832 case QH_STATE_COMPLETING:
833 unlink_async(ehci, qh);
834 break;
835 case QH_STATE_UNLINK:
836 case QH_STATE_UNLINK_WAIT:
837 /* already started */
838 break;
839 case QH_STATE_IDLE:
840 WARN_ON(1);
841 break;
842 }
811 break; 843 break;
812 844
813 case PIPE_INTERRUPT: 845 case PIPE_INTERRUPT:
@@ -899,6 +931,7 @@ rescan:
899 unlink_async (ehci, qh); 931 unlink_async (ehci, qh);
900 /* FALL THROUGH */ 932 /* FALL THROUGH */
901 case QH_STATE_UNLINK: /* wait for hw to finish? */ 933 case QH_STATE_UNLINK: /* wait for hw to finish? */
934 case QH_STATE_UNLINK_WAIT:
902idle_timeout: 935idle_timeout:
903 spin_unlock_irqrestore (&ehci->lock, flags); 936 spin_unlock_irqrestore (&ehci->lock, flags);
904 schedule_timeout_uninterruptible(1); 937 schedule_timeout_uninterruptible(1);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index a165e0a0961c..a249d03a5024 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -134,7 +134,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
134 } 134 }
135 ehci->command = ehci_readl(ehci, &ehci->regs->command); 135 ehci->command = ehci_readl(ehci, &ehci->regs->command);
136 if (ehci->reclaim) 136 if (ehci->reclaim)
137 ehci->reclaim_ready = 1; 137 end_unlink_async(ehci);
138 ehci_work(ehci); 138 ehci_work(ehci);
139 139
140 /* Unlike other USB host controller types, EHCI doesn't have 140 /* Unlike other USB host controller types, EHCI doesn't have
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 45e040000280..3ba01664f821 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -305,7 +305,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
305 /* emptying the schedule aborts any urbs */ 305 /* emptying the schedule aborts any urbs */
306 spin_lock_irq(&ehci->lock); 306 spin_lock_irq(&ehci->lock);
307 if (ehci->reclaim) 307 if (ehci->reclaim)
308 ehci->reclaim_ready = 1; 308 end_unlink_async(ehci);
309 ehci_work(ehci); 309 ehci_work(ehci);
310 spin_unlock_irq(&ehci->lock); 310 spin_unlock_irq(&ehci->lock);
311 311
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index b10f39c047e9..853e5e6396a5 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -973,7 +973,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
973 struct ehci_qh *qh = ehci->reclaim; 973 struct ehci_qh *qh = ehci->reclaim;
974 struct ehci_qh *next; 974 struct ehci_qh *next;
975 975
976 timer_action_done (ehci, TIMER_IAA_WATCHDOG); 976 iaa_watchdog_done(ehci);
977 977
978 // qh->hw_next = cpu_to_hc32(qh->qh_dma); 978 // qh->hw_next = cpu_to_hc32(qh->qh_dma);
979 qh->qh_state = QH_STATE_IDLE; 979 qh->qh_state = QH_STATE_IDLE;
@@ -983,7 +983,6 @@ static void end_unlink_async (struct ehci_hcd *ehci)
983 /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ 983 /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
984 next = qh->reclaim; 984 next = qh->reclaim;
985 ehci->reclaim = next; 985 ehci->reclaim = next;
986 ehci->reclaim_ready = 0;
987 qh->reclaim = NULL; 986 qh->reclaim = NULL;
988 987
989 qh_completions (ehci, qh); 988 qh_completions (ehci, qh);
@@ -1059,11 +1058,10 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
1059 return; 1058 return;
1060 } 1059 }
1061 1060
1062 ehci->reclaim_ready = 0;
1063 cmd |= CMD_IAAD; 1061 cmd |= CMD_IAAD;
1064 ehci_writel(ehci, cmd, &ehci->regs->command); 1062 ehci_writel(ehci, cmd, &ehci->regs->command);
1065 (void)ehci_readl(ehci, &ehci->regs->command); 1063 (void)ehci_readl(ehci, &ehci->regs->command);
1066 timer_action (ehci, TIMER_IAA_WATCHDOG); 1064 iaa_watchdog_start(ehci);
1067} 1065}
1068 1066
1069/*-------------------------------------------------------------------------*/ 1067/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 10e71417c352..eeda4c88ebae 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -74,7 +74,6 @@ struct ehci_hcd { /* one per controller */
74 /* async schedule support */ 74 /* async schedule support */
75 struct ehci_qh *async; 75 struct ehci_qh *async;
76 struct ehci_qh *reclaim; 76 struct ehci_qh *reclaim;
77 unsigned reclaim_ready : 1;
78 unsigned scanning : 1; 77 unsigned scanning : 1;
79 78
80 /* periodic schedule support */ 79 /* periodic schedule support */
@@ -105,6 +104,7 @@ struct ehci_hcd { /* one per controller */
105 struct dma_pool *itd_pool; /* itd per iso urb */ 104 struct dma_pool *itd_pool; /* itd per iso urb */
106 struct dma_pool *sitd_pool; /* sitd per split iso urb */ 105 struct dma_pool *sitd_pool; /* sitd per split iso urb */
107 106
107 struct timer_list iaa_watchdog;
108 struct timer_list watchdog; 108 struct timer_list watchdog;
109 unsigned long actions; 109 unsigned long actions;
110 unsigned stamp; 110 unsigned stamp;
@@ -148,9 +148,21 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
148} 148}
149 149
150 150
151static inline void
152iaa_watchdog_start(struct ehci_hcd *ehci)
153{
154 WARN_ON(timer_pending(&ehci->iaa_watchdog));
155 mod_timer(&ehci->iaa_watchdog,
156 jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
157}
158
159static inline void iaa_watchdog_done(struct ehci_hcd *ehci)
160{
161 del_timer(&ehci->iaa_watchdog);
162}
163
151enum ehci_timer_action { 164enum ehci_timer_action {
152 TIMER_IO_WATCHDOG, 165 TIMER_IO_WATCHDOG,
153 TIMER_IAA_WATCHDOG,
154 TIMER_ASYNC_SHRINK, 166 TIMER_ASYNC_SHRINK,
155 TIMER_ASYNC_OFF, 167 TIMER_ASYNC_OFF,
156}; 168};
@@ -168,9 +180,6 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
168 unsigned long t; 180 unsigned long t;
169 181
170 switch (action) { 182 switch (action) {
171 case TIMER_IAA_WATCHDOG:
172 t = EHCI_IAA_JIFFIES;
173 break;
174 case TIMER_IO_WATCHDOG: 183 case TIMER_IO_WATCHDOG:
175 t = EHCI_IO_JIFFIES; 184 t = EHCI_IO_JIFFIES;
176 break; 185 break;
@@ -187,8 +196,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
187 // async queue SHRINK often precedes IAA. while it's ready 196 // async queue SHRINK often precedes IAA. while it's ready
188 // to go OFF neither can matter, and afterwards the IO 197 // to go OFF neither can matter, and afterwards the IO
189 // watchdog stops unless there's still periodic traffic. 198 // watchdog stops unless there's still periodic traffic.
190 if (action != TIMER_IAA_WATCHDOG 199 if (time_before_eq(t, ehci->watchdog.expires)
191 && t > ehci->watchdog.expires
192 && timer_pending (&ehci->watchdog)) 200 && timer_pending (&ehci->watchdog))
193 return; 201 return;
194 mod_timer (&ehci->watchdog, t); 202 mod_timer (&ehci->watchdog, t);