diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ehci-dbg.c | 4 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 70 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci-q.c | 6 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 22 | ||||
-rw-r--r-- | drivers/usb/host/ohci-pnx4008.c | 9 | ||||
-rw-r--r-- | drivers/usb/host/uhci-hcd.c | 44 |
8 files changed, 82 insertions, 77 deletions
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 23b95b2bfe15..34b7a31cd85b 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c | |||
@@ -754,7 +754,9 @@ 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\n", ehci->reclaim); | 757 | temp = scnprintf (next, size, "reclaim qh %p%s\n", |
758 | ehci->reclaim, | ||
759 | ehci->reclaim_ready ? " ready" : ""); | ||
758 | size -= temp; | 760 | size -= temp; |
759 | next += temp; | 761 | next += temp; |
760 | } | 762 | } |
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index aac6ec5dd7cf..9030994aba98 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_MSECS 10 /* arbitrary */ | 114 | #define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ |
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,7 +254,6 @@ static void ehci_quiesce (struct ehci_hcd *ehci) | |||
254 | 254 | ||
255 | /*-------------------------------------------------------------------------*/ | 255 | /*-------------------------------------------------------------------------*/ |
256 | 256 | ||
257 | static void end_unlink_async (struct ehci_hcd *ehci); | ||
258 | static void ehci_work(struct ehci_hcd *ehci); | 257 | static void ehci_work(struct ehci_hcd *ehci); |
259 | 258 | ||
260 | #include "ehci-hub.c" | 259 | #include "ehci-hub.c" |
@@ -264,37 +263,25 @@ static void ehci_work(struct ehci_hcd *ehci); | |||
264 | 263 | ||
265 | /*-------------------------------------------------------------------------*/ | 264 | /*-------------------------------------------------------------------------*/ |
266 | 265 | ||
267 | static void ehci_iaa_watchdog (unsigned long param) | 266 | static void ehci_watchdog (unsigned long param) |
268 | { | 267 | { |
269 | struct ehci_hcd *ehci = (struct ehci_hcd *) param; | 268 | struct ehci_hcd *ehci = (struct ehci_hcd *) param; |
270 | unsigned long flags; | 269 | unsigned long flags; |
271 | u32 status; | ||
272 | 270 | ||
273 | spin_lock_irqsave (&ehci->lock, flags); | 271 | spin_lock_irqsave (&ehci->lock, flags); |
274 | WARN_ON(!ehci->reclaim); | ||
275 | 272 | ||
276 | /* lost IAA irqs wedge things badly; seen first with a vt8235 */ | 273 | /* lost IAA irqs wedge things badly; seen with a vt8235 */ |
277 | if (ehci->reclaim) { | 274 | if (ehci->reclaim) { |
278 | status = readl (&ehci->regs->status); | 275 | u32 status = readl (&ehci->regs->status); |
279 | if (status & STS_IAA) { | 276 | if (status & STS_IAA) { |
280 | ehci_vdbg (ehci, "lost IAA\n"); | 277 | ehci_vdbg (ehci, "lost IAA\n"); |
281 | COUNT (ehci->stats.lost_iaa); | 278 | COUNT (ehci->stats.lost_iaa); |
282 | writel (STS_IAA, &ehci->regs->status); | 279 | writel (STS_IAA, &ehci->regs->status); |
283 | end_unlink_async (ehci); | 280 | ehci->reclaim_ready = 1; |
284 | } | 281 | } |
285 | } | 282 | } |
286 | 283 | ||
287 | spin_unlock_irqrestore (&ehci->lock, flags); | 284 | /* stop async processing after it's idled a bit */ |
288 | } | ||
289 | |||
290 | static 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 */ | ||
298 | if (test_bit (TIMER_ASYNC_OFF, &ehci->actions)) | 285 | if (test_bit (TIMER_ASYNC_OFF, &ehci->actions)) |
299 | start_unlink_async (ehci, ehci->async); | 286 | start_unlink_async (ehci, ehci->async); |
300 | 287 | ||
@@ -345,6 +332,8 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on) | |||
345 | static void ehci_work (struct ehci_hcd *ehci) | 332 | static void ehci_work (struct ehci_hcd *ehci) |
346 | { | 333 | { |
347 | timer_action_done (ehci, TIMER_IO_WATCHDOG); | 334 | timer_action_done (ehci, TIMER_IO_WATCHDOG); |
335 | if (ehci->reclaim_ready) | ||
336 | end_unlink_async (ehci); | ||
348 | 337 | ||
349 | /* another CPU may drop ehci->lock during a schedule scan while | 338 | /* another CPU may drop ehci->lock during a schedule scan while |
350 | * it reports urb completions. this flag guards against bogus | 339 | * it reports urb completions. this flag guards against bogus |
@@ -379,7 +368,6 @@ static void ehci_stop (struct usb_hcd *hcd) | |||
379 | 368 | ||
380 | /* no more interrupts ... */ | 369 | /* no more interrupts ... */ |
381 | del_timer_sync (&ehci->watchdog); | 370 | del_timer_sync (&ehci->watchdog); |
382 | del_timer_sync (&ehci->iaa_watchdog); | ||
383 | 371 | ||
384 | spin_lock_irq(&ehci->lock); | 372 | spin_lock_irq(&ehci->lock); |
385 | if (HC_IS_RUNNING (hcd->state)) | 373 | if (HC_IS_RUNNING (hcd->state)) |
@@ -426,10 +414,6 @@ static int ehci_init(struct usb_hcd *hcd) | |||
426 | ehci->watchdog.function = ehci_watchdog; | 414 | ehci->watchdog.function = ehci_watchdog; |
427 | ehci->watchdog.data = (unsigned long) ehci; | 415 | ehci->watchdog.data = (unsigned long) ehci; |
428 | 416 | ||
429 | init_timer(&ehci->iaa_watchdog); | ||
430 | ehci->iaa_watchdog.function = ehci_iaa_watchdog; | ||
431 | ehci->iaa_watchdog.data = (unsigned long) ehci; | ||
432 | |||
433 | /* | 417 | /* |
434 | * hw default: 1K periodic list heads, one per frame. | 418 | * hw default: 1K periodic list heads, one per frame. |
435 | * periodic_size can shrink by USBCMD update if hcc_params allows. | 419 | * periodic_size can shrink by USBCMD update if hcc_params allows. |
@@ -446,6 +430,7 @@ static int ehci_init(struct usb_hcd *hcd) | |||
446 | ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); | 430 | ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); |
447 | 431 | ||
448 | ehci->reclaim = NULL; | 432 | ehci->reclaim = NULL; |
433 | ehci->reclaim_ready = 0; | ||
449 | ehci->next_uframe = -1; | 434 | ehci->next_uframe = -1; |
450 | 435 | ||
451 | /* | 436 | /* |
@@ -619,7 +604,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) | |||
619 | /* complete the unlinking of some qh [4.15.2.3] */ | 604 | /* complete the unlinking of some qh [4.15.2.3] */ |
620 | if (status & STS_IAA) { | 605 | if (status & STS_IAA) { |
621 | COUNT (ehci->stats.reclaim); | 606 | COUNT (ehci->stats.reclaim); |
622 | end_unlink_async (ehci); | 607 | ehci->reclaim_ready = 1; |
623 | bh = 1; | 608 | bh = 1; |
624 | } | 609 | } |
625 | 610 | ||
@@ -723,14 +708,10 @@ static int ehci_urb_enqueue ( | |||
723 | 708 | ||
724 | static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | 709 | static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) |
725 | { | 710 | { |
726 | // BUG_ON(qh->qh_state != QH_STATE_LINKED); | 711 | /* if we need to use IAA and it's busy, defer */ |
727 | 712 | if (qh->qh_state == QH_STATE_LINKED | |
728 | /* failfast */ | 713 | && ehci->reclaim |
729 | if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) | 714 | && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) { |
730 | end_unlink_async (ehci); | ||
731 | |||
732 | /* defer till later if busy */ | ||
733 | else if (ehci->reclaim) { | ||
734 | struct ehci_qh *last; | 715 | struct ehci_qh *last; |
735 | 716 | ||
736 | for (last = ehci->reclaim; | 717 | for (last = ehci->reclaim; |
@@ -740,8 +721,12 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
740 | qh->qh_state = QH_STATE_UNLINK_WAIT; | 721 | qh->qh_state = QH_STATE_UNLINK_WAIT; |
741 | last->reclaim = qh; | 722 | last->reclaim = qh; |
742 | 723 | ||
743 | /* start IAA cycle */ | 724 | /* bypass IAA if the hc can't care */ |
744 | } else | 725 | } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim) |
726 | end_unlink_async (ehci); | ||
727 | |||
728 | /* something else might have unlinked the qh by now */ | ||
729 | if (qh->qh_state == QH_STATE_LINKED) | ||
745 | start_unlink_async (ehci, qh); | 730 | start_unlink_async (ehci, qh); |
746 | } | 731 | } |
747 | 732 | ||
@@ -763,19 +748,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) | |||
763 | qh = (struct ehci_qh *) urb->hcpriv; | 748 | qh = (struct ehci_qh *) urb->hcpriv; |
764 | if (!qh) | 749 | if (!qh) |
765 | break; | 750 | break; |
766 | switch (qh->qh_state) { | 751 | unlink_async (ehci, qh); |
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 | } | ||
779 | break; | 752 | break; |
780 | 753 | ||
781 | case PIPE_INTERRUPT: | 754 | case PIPE_INTERRUPT: |
@@ -867,7 +840,6 @@ rescan: | |||
867 | unlink_async (ehci, qh); | 840 | unlink_async (ehci, qh); |
868 | /* FALL THROUGH */ | 841 | /* FALL THROUGH */ |
869 | case QH_STATE_UNLINK: /* wait for hw to finish? */ | 842 | case QH_STATE_UNLINK: /* wait for hw to finish? */ |
870 | case QH_STATE_UNLINK_WAIT: | ||
871 | idle_timeout: | 843 | idle_timeout: |
872 | spin_unlock_irqrestore (&ehci->lock, flags); | 844 | spin_unlock_irqrestore (&ehci->lock, flags); |
873 | schedule_timeout_uninterruptible(1); | 845 | schedule_timeout_uninterruptible(1); |
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 2012213c0a25..1b20722c102b 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 | end_unlink_async (ehci); | 51 | ehci->reclaim_ready = 1; |
52 | ehci_work(ehci); | 52 | ehci_work(ehci); |
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 35e3fab6fc4e..e51c1ed81ac4 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 | end_unlink_async (ehci); | 306 | ehci->reclaim_ready = 1; |
307 | ehci_work(ehci); | 307 | ehci_work(ehci); |
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 46327272f614..62e46dc60e86 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) | |||
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 | iaa_watchdog_done (ehci); | 970 | timer_action_done (ehci, TIMER_IAA_WATCHDOG); |
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,6 +977,7 @@ static void end_unlink_async (struct ehci_hcd *ehci) | |||
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; | ||
980 | qh->reclaim = NULL; | 981 | qh->reclaim = NULL; |
981 | 982 | ||
982 | qh_completions (ehci, qh); | 983 | qh_completions (ehci, qh); |
@@ -1051,10 +1052,11 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
1051 | return; | 1052 | return; |
1052 | } | 1053 | } |
1053 | 1054 | ||
1055 | ehci->reclaim_ready = 0; | ||
1054 | cmd |= CMD_IAAD; | 1056 | cmd |= CMD_IAAD; |
1055 | writel (cmd, &ehci->regs->command); | 1057 | writel (cmd, &ehci->regs->command); |
1056 | (void) readl (&ehci->regs->command); | 1058 | (void) readl (&ehci->regs->command); |
1057 | iaa_watchdog_start (ehci); | 1059 | timer_action (ehci, TIMER_IAA_WATCHDOG); |
1058 | } | 1060 | } |
1059 | 1061 | ||
1060 | /*-------------------------------------------------------------------------*/ | 1062 | /*-------------------------------------------------------------------------*/ |
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 6aac39f50e07..bbc3082a73d7 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
@@ -58,6 +58,7 @@ 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; | ||
61 | unsigned scanning : 1; | 62 | unsigned scanning : 1; |
62 | 63 | ||
63 | /* periodic schedule support */ | 64 | /* periodic schedule support */ |
@@ -80,7 +81,6 @@ struct ehci_hcd { /* one per controller */ | |||
80 | struct dma_pool *itd_pool; /* itd per iso urb */ | 81 | struct dma_pool *itd_pool; /* itd per iso urb */ |
81 | struct dma_pool *sitd_pool; /* sitd per split iso urb */ | 82 | struct dma_pool *sitd_pool; /* sitd per split iso urb */ |
82 | 83 | ||
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,21 +114,9 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci) | |||
114 | } | 114 | } |
115 | 115 | ||
116 | 116 | ||
117 | static inline void | ||
118 | iaa_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 | |||
125 | static inline void iaa_watchdog_done (struct ehci_hcd *ehci) | ||
126 | { | ||
127 | del_timer (&ehci->iaa_watchdog); | ||
128 | } | ||
129 | |||
130 | enum ehci_timer_action { | 117 | enum ehci_timer_action { |
131 | TIMER_IO_WATCHDOG, | 118 | TIMER_IO_WATCHDOG, |
119 | TIMER_IAA_WATCHDOG, | ||
132 | TIMER_ASYNC_SHRINK, | 120 | TIMER_ASYNC_SHRINK, |
133 | TIMER_ASYNC_OFF, | 121 | TIMER_ASYNC_OFF, |
134 | }; | 122 | }; |
@@ -146,6 +134,9 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) | |||
146 | unsigned long t; | 134 | unsigned long t; |
147 | 135 | ||
148 | switch (action) { | 136 | switch (action) { |
137 | case TIMER_IAA_WATCHDOG: | ||
138 | t = EHCI_IAA_JIFFIES; | ||
139 | break; | ||
149 | case TIMER_IO_WATCHDOG: | 140 | case TIMER_IO_WATCHDOG: |
150 | t = EHCI_IO_JIFFIES; | 141 | t = EHCI_IO_JIFFIES; |
151 | break; | 142 | break; |
@@ -162,7 +153,8 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) | |||
162 | // async queue SHRINK often precedes IAA. while it's ready | 153 | // async queue SHRINK often precedes IAA. while it's ready |
163 | // to go OFF neither can matter, and afterwards the IO | 154 | // to go OFF neither can matter, and afterwards the IO |
164 | // watchdog stops unless there's still periodic traffic. | 155 | // watchdog stops unless there's still periodic traffic. |
165 | if (time_before_eq(t, ehci->watchdog.expires) | 156 | if (action != TIMER_IAA_WATCHDOG |
157 | && t > ehci->watchdog.expires | ||
166 | && timer_pending (&ehci->watchdog)) | 158 | && timer_pending (&ehci->watchdog)) |
167 | return; | 159 | return; |
168 | mod_timer (&ehci->watchdog, t); | 160 | mod_timer (&ehci->watchdog, t); |
diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c index 82cb22f002e7..2dbb77414905 100644 --- a/drivers/usb/host/ohci-pnx4008.c +++ b/drivers/usb/host/ohci-pnx4008.c | |||
@@ -262,6 +262,7 @@ static const struct hc_driver ohci_pnx4008_hc_driver = { | |||
262 | */ | 262 | */ |
263 | .start = ohci_pnx4008_start, | 263 | .start = ohci_pnx4008_start, |
264 | .stop = ohci_stop, | 264 | .stop = ohci_stop, |
265 | .shutdown = ohci_shutdown, | ||
265 | 266 | ||
266 | /* | 267 | /* |
267 | * managing i/o requests and associated device resources | 268 | * managing i/o requests and associated device resources |
@@ -280,7 +281,11 @@ static const struct hc_driver ohci_pnx4008_hc_driver = { | |||
280 | */ | 281 | */ |
281 | .hub_status_data = ohci_hub_status_data, | 282 | .hub_status_data = ohci_hub_status_data, |
282 | .hub_control = ohci_hub_control, | 283 | .hub_control = ohci_hub_control, |
283 | 284 | .hub_irq_enable = ohci_rhsc_enable, | |
285 | #ifdef CONFIG_PM | ||
286 | .bus_suspend = ohci_bus_suspend, | ||
287 | .bus_resume = ohci_bus_resume, | ||
288 | #endif | ||
284 | .start_port_reset = ohci_start_port_reset, | 289 | .start_port_reset = ohci_start_port_reset, |
285 | }; | 290 | }; |
286 | 291 | ||
@@ -410,8 +415,6 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev) | |||
410 | goto out4; | 415 | goto out4; |
411 | } | 416 | } |
412 | 417 | ||
413 | hcd->self.hcpriv = (void *)hcd; | ||
414 | |||
415 | pnx4008_start_hc(); | 418 | pnx4008_start_hc(); |
416 | platform_set_drvdata(pdev, hcd); | 419 | platform_set_drvdata(pdev, hcd); |
417 | ohci = hcd_to_ohci(hcd); | 420 | ohci = hcd_to_ohci(hcd); |
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 45ee6920a850..226bf3de8edd 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <linux/dma-mapping.h> | 40 | #include <linux/dma-mapping.h> |
41 | #include <linux/usb.h> | 41 | #include <linux/usb.h> |
42 | #include <linux/bitops.h> | 42 | #include <linux/bitops.h> |
43 | #include <linux/dmi.h> | ||
43 | 44 | ||
44 | #include <asm/uaccess.h> | 45 | #include <asm/uaccess.h> |
45 | #include <asm/io.h> | 46 | #include <asm/io.h> |
@@ -196,12 +197,42 @@ static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) | |||
196 | return 0; | 197 | return 0; |
197 | } | 198 | } |
198 | 199 | ||
200 | static int remote_wakeup_is_broken(struct uhci_hcd *uhci) | ||
201 | { | ||
202 | static struct dmi_system_id broken_wakeup_table[] = { | ||
203 | { | ||
204 | .ident = "Asus A7V8X", | ||
205 | .matches = { | ||
206 | DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK"), | ||
207 | DMI_MATCH(DMI_BOARD_NAME, "A7V8X"), | ||
208 | DMI_MATCH(DMI_BOARD_VERSION, "REV 1.xx"), | ||
209 | } | ||
210 | }, | ||
211 | { } | ||
212 | }; | ||
213 | int port; | ||
214 | |||
215 | /* One of Asus's motherboards has a bug which causes it to | ||
216 | * wake up immediately from suspend-to-RAM if any of the ports | ||
217 | * are connected. In such cases we will not set EGSM. | ||
218 | */ | ||
219 | if (dmi_check_system(broken_wakeup_table)) { | ||
220 | for (port = 0; port < uhci->rh_numports; ++port) { | ||
221 | if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & | ||
222 | USBPORTSC_CCS) | ||
223 | return 1; | ||
224 | } | ||
225 | } | ||
226 | |||
227 | return 0; | ||
228 | } | ||
229 | |||
199 | static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state) | 230 | static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state) |
200 | __releases(uhci->lock) | 231 | __releases(uhci->lock) |
201 | __acquires(uhci->lock) | 232 | __acquires(uhci->lock) |
202 | { | 233 | { |
203 | int auto_stop; | 234 | int auto_stop; |
204 | int int_enable; | 235 | int int_enable, egsm_enable; |
205 | 236 | ||
206 | auto_stop = (new_state == UHCI_RH_AUTO_STOPPED); | 237 | auto_stop = (new_state == UHCI_RH_AUTO_STOPPED); |
207 | dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev, | 238 | dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev, |
@@ -217,15 +248,18 @@ __acquires(uhci->lock) | |||
217 | } | 248 | } |
218 | 249 | ||
219 | /* Enable resume-detect interrupts if they work. | 250 | /* Enable resume-detect interrupts if they work. |
220 | * Then enter Global Suspend mode, still configured. | 251 | * Then enter Global Suspend mode if _it_ works, still configured. |
221 | */ | 252 | */ |
253 | egsm_enable = USBCMD_EGSM; | ||
222 | uhci->working_RD = 1; | 254 | uhci->working_RD = 1; |
223 | int_enable = USBINTR_RESUME; | 255 | int_enable = USBINTR_RESUME; |
224 | if (resume_detect_interrupts_are_broken(uhci)) { | 256 | if (remote_wakeup_is_broken(uhci)) |
257 | egsm_enable = 0; | ||
258 | if (resume_detect_interrupts_are_broken(uhci) || !egsm_enable) | ||
225 | uhci->working_RD = int_enable = 0; | 259 | uhci->working_RD = int_enable = 0; |
226 | } | 260 | |
227 | outw(int_enable, uhci->io_addr + USBINTR); | 261 | outw(int_enable, uhci->io_addr + USBINTR); |
228 | outw(USBCMD_EGSM | USBCMD_CF, uhci->io_addr + USBCMD); | 262 | outw(egsm_enable | USBCMD_CF, uhci->io_addr + USBCMD); |
229 | mb(); | 263 | mb(); |
230 | udelay(5); | 264 | udelay(5); |
231 | 265 | ||