aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/host/ehci-hcd.c13
-rw-r--r--drivers/usb/host/ehci-q.c68
-rw-r--r--drivers/usb/host/ehci-timer.c49
-rw-r--r--drivers/usb/host/ehci.h5
4 files changed, 86 insertions, 49 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index fd7ae16f77be..21d6fbc0a327 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -95,7 +95,6 @@ static const char hcd_name [] = "ehci_hcd";
95 95
96#define EHCI_IAA_MSECS 10 /* arbitrary */ 96#define EHCI_IAA_MSECS 10 /* arbitrary */
97#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ 97#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
98#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
99#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) 98#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1)
100 /* 5-ms async qh unlink delay */ 99 /* 5-ms async qh unlink delay */
101 100
@@ -137,7 +136,7 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
137 * SHRINK were pending, OFF would never be requested. 136 * SHRINK were pending, OFF would never be requested.
138 */ 137 */
139 if (timer_pending(&ehci->watchdog) 138 if (timer_pending(&ehci->watchdog)
140 && ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF)) 139 && (BIT(TIMER_ASYNC_SHRINK)
141 & ehci->actions)) 140 & ehci->actions))
142 return; 141 return;
143 142
@@ -150,9 +149,6 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
150 return; 149 return;
151 t = EHCI_IO_JIFFIES; 150 t = EHCI_IO_JIFFIES;
152 break; 151 break;
153 case TIMER_ASYNC_OFF:
154 t = EHCI_ASYNC_JIFFIES;
155 break;
156 /* case TIMER_ASYNC_SHRINK: */ 152 /* case TIMER_ASYNC_SHRINK: */
157 default: 153 default:
158 t = EHCI_SHRINK_JIFFIES; 154 t = EHCI_SHRINK_JIFFIES;
@@ -376,10 +372,6 @@ static void ehci_watchdog(unsigned long param)
376 372
377 spin_lock_irqsave(&ehci->lock, flags); 373 spin_lock_irqsave(&ehci->lock, flags);
378 374
379 /* stop async processing after it's idled a bit */
380 if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
381 start_unlink_async (ehci, ehci->async);
382
383 /* ehci could run by timer, without IRQs ... */ 375 /* ehci could run by timer, without IRQs ... */
384 ehci_work (ehci); 376 ehci_work (ehci);
385 377
@@ -470,7 +462,8 @@ static void ehci_work (struct ehci_hcd *ehci)
470 if (ehci->scanning) 462 if (ehci->scanning)
471 return; 463 return;
472 ehci->scanning = 1; 464 ehci->scanning = 1;
473 scan_async (ehci); 465 if (ehci->async_count)
466 scan_async(ehci);
474 if (ehci->next_uframe != -1) 467 if (ehci->next_uframe != -1)
475 scan_periodic (ehci); 468 scan_periodic (ehci);
476 ehci->scanning = 0; 469 ehci->scanning = 0;
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 285d5a0f3f70..d68764ef4476 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -964,6 +964,30 @@ done:
964 964
965/*-------------------------------------------------------------------------*/ 965/*-------------------------------------------------------------------------*/
966 966
967static void enable_async(struct ehci_hcd *ehci)
968{
969 if (ehci->async_count++)
970 return;
971
972 /* Stop waiting to turn off the async schedule */
973 ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_ASYNC);
974
975 /* Don't start the schedule until ASS is 0 */
976 ehci_poll_ASS(ehci);
977}
978
979static void disable_async(struct ehci_hcd *ehci)
980{
981 if (--ehci->async_count)
982 return;
983
984 /* The async schedule and async_unlink list are supposed to be empty */
985 WARN_ON(ehci->async->qh_next.qh || ehci->async_unlink);
986
987 /* Don't turn off the schedule until ASS is 1 */
988 ehci_poll_ASS(ehci);
989}
990
967/* move qh (and its qtds) onto async queue; maybe enable queue. */ 991/* move qh (and its qtds) onto async queue; maybe enable queue. */
968 992
969static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) 993static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
@@ -977,24 +1001,11 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
977 1001
978 WARN_ON(qh->qh_state != QH_STATE_IDLE); 1002 WARN_ON(qh->qh_state != QH_STATE_IDLE);
979 1003
980 /* (re)start the async schedule? */
981 head = ehci->async;
982 timer_action_done (ehci, TIMER_ASYNC_OFF);
983 if (!head->qh_next.qh) {
984 if (!(ehci->command & CMD_ASE)) {
985 /* in case a clear of CMD_ASE didn't take yet */
986 (void)handshake(ehci, &ehci->regs->status,
987 STS_ASS, 0, 150);
988 ehci->command |= CMD_ASE;
989 ehci_writel(ehci, ehci->command, &ehci->regs->command);
990 /* posted write need not be known to HC yet ... */
991 }
992 }
993
994 /* clear halt and/or toggle; and maybe recover from silicon quirk */ 1004 /* clear halt and/or toggle; and maybe recover from silicon quirk */
995 qh_refresh(ehci, qh); 1005 qh_refresh(ehci, qh);
996 1006
997 /* splice right after start */ 1007 /* splice right after start */
1008 head = ehci->async;
998 qh->qh_next = head->qh_next; 1009 qh->qh_next = head->qh_next;
999 qh->hw->hw_next = head->hw->hw_next; 1010 qh->hw->hw_next = head->hw->hw_next;
1000 wmb (); 1011 wmb ();
@@ -1005,6 +1016,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
1005 qh->xacterrs = 0; 1016 qh->xacterrs = 0;
1006 qh->qh_state = QH_STATE_LINKED; 1017 qh->qh_state = QH_STATE_LINKED;
1007 /* qtd completions reported later by interrupt */ 1018 /* qtd completions reported later by interrupt */
1019
1020 enable_async(ehci);
1008} 1021}
1009 1022
1010/*-------------------------------------------------------------------------*/ 1023/*-------------------------------------------------------------------------*/
@@ -1173,16 +1186,10 @@ static void end_unlink_async (struct ehci_hcd *ehci)
1173 1186
1174 qh_completions (ehci, qh); 1187 qh_completions (ehci, qh);
1175 1188
1176 if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) { 1189 if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING)
1177 qh_link_async (ehci, qh); 1190 qh_link_async (ehci, qh);
1178 } else { 1191
1179 /* it's not free to turn the async schedule on/off; leave it 1192 disable_async(ehci);
1180 * active but idle for a while once it empties.
1181 */
1182 if (ehci->rh_state == EHCI_RH_RUNNING
1183 && ehci->async->qh_next.qh == NULL)
1184 timer_action (ehci, TIMER_ASYNC_OFF);
1185 }
1186 1193
1187 if (next) { 1194 if (next) {
1188 ehci->async_unlink = NULL; 1195 ehci->async_unlink = NULL;
@@ -1210,21 +1217,6 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
1210 BUG (); 1217 BUG ();
1211#endif 1218#endif
1212 1219
1213 /* stop async schedule right now? */
1214 if (unlikely (qh == ehci->async)) {
1215 /* can't get here without STS_ASS set */
1216 if (ehci->rh_state != EHCI_RH_HALTED
1217 && !ehci->async_unlink) {
1218 /* ... and CMD_IAAD clear */
1219 ehci->command &= ~CMD_ASE;
1220 ehci_writel(ehci, ehci->command, &ehci->regs->command);
1221 wmb ();
1222 // handshake later, if we need to
1223 timer_action_done (ehci, TIMER_ASYNC_OFF);
1224 }
1225 return;
1226 }
1227
1228 qh->qh_state = QH_STATE_UNLINK; 1220 qh->qh_state = QH_STATE_UNLINK;
1229 ehci->async_unlink = qh; 1221 ehci->async_unlink = qh;
1230 if (!qh->unlink_next) 1222 if (!qh->unlink_next)
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c
index ecd3296157c6..1e907dd3bb1b 100644
--- a/drivers/usb/host/ehci-timer.c
+++ b/drivers/usb/host/ehci-timer.c
@@ -67,8 +67,10 @@ static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit)
67 * the event types indexed by enum ehci_hrtimer_event in ehci.h. 67 * the event types indexed by enum ehci_hrtimer_event in ehci.h.
68 */ 68 */
69static unsigned event_delays_ns[] = { 69static unsigned event_delays_ns[] = {
70 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */
70 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ 71 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */
71 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 72 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */
73 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */
72}; 74};
73 75
74/* Enable a pending hrtimer event */ 76/* Enable a pending hrtimer event */
@@ -91,6 +93,51 @@ static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event,
91} 93}
92 94
93 95
96/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */
97static void ehci_poll_ASS(struct ehci_hcd *ehci)
98{
99 unsigned actual, want;
100
101 /* Don't enable anything if the controller isn't running (e.g., died) */
102 if (ehci->rh_state != EHCI_RH_RUNNING)
103 return;
104
105 want = (ehci->command & CMD_ASE) ? STS_ASS : 0;
106 actual = ehci_readl(ehci, &ehci->regs->status) & STS_ASS;
107
108 if (want != actual) {
109
110 /* Poll again later, but give up after about 20 ms */
111 if (ehci->ASS_poll_count++ < 20) {
112 ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true);
113 return;
114 }
115 ehci_warn(ehci, "Waited too long for the async schedule status, giving up\n");
116 }
117 ehci->ASS_poll_count = 0;
118
119 /* The status is up-to-date; restart or stop the schedule as needed */
120 if (want == 0) { /* Stopped */
121 if (ehci->async_count > 0)
122 ehci_set_command_bit(ehci, CMD_ASE);
123
124 } else { /* Running */
125 if (ehci->async_count == 0) {
126
127 /* Turn off the schedule after a while */
128 ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_ASYNC,
129 true);
130 }
131 }
132}
133
134/* Turn off the async schedule after a brief delay */
135static void ehci_disable_ASE(struct ehci_hcd *ehci)
136{
137 ehci_clear_command_bit(ehci, CMD_ASE);
138}
139
140
94/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ 141/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */
95static void ehci_poll_PSS(struct ehci_hcd *ehci) 142static void ehci_poll_PSS(struct ehci_hcd *ehci)
96{ 143{
@@ -151,8 +198,10 @@ static void ehci_disable_PSE(struct ehci_hcd *ehci)
151 * enum ehci_hrtimer_event in ehci.h. 198 * enum ehci_hrtimer_event in ehci.h.
152 */ 199 */
153static void (*event_handlers[])(struct ehci_hcd *) = { 200static void (*event_handlers[])(struct ehci_hcd *) = {
201 ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */
154 ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ 202 ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */
155 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 203 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */
204 ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */
156}; 205};
157 206
158static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) 207static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t)
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index da2e0ab23850..bf06bbb77ba4 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -79,8 +79,10 @@ enum ehci_rh_state {
79 * ehci-timer.c) in parallel with this list. 79 * ehci-timer.c) in parallel with this list.
80 */ 80 */
81enum ehci_hrtimer_event { 81enum ehci_hrtimer_event {
82 EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */
82 EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ 83 EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */
83 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ 84 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
85 EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
84 EHCI_HRTIMER_NUM_EVENTS /* Must come last */ 86 EHCI_HRTIMER_NUM_EVENTS /* Must come last */
85}; 87};
86#define EHCI_HRTIMER_NO_EVENT 99 88#define EHCI_HRTIMER_NO_EVENT 99
@@ -93,6 +95,7 @@ struct ehci_hcd { /* one per controller */
93 struct hrtimer hrtimer; 95 struct hrtimer hrtimer;
94 96
95 int PSS_poll_count; 97 int PSS_poll_count;
98 int ASS_poll_count;
96 99
97 /* glue to PCI and HCD framework */ 100 /* glue to PCI and HCD framework */
98 struct ehci_caps __iomem *caps; 101 struct ehci_caps __iomem *caps;
@@ -110,6 +113,7 @@ struct ehci_hcd { /* one per controller */
110 struct ehci_qh *async_unlink_last; 113 struct ehci_qh *async_unlink_last;
111 struct ehci_qh *qh_scan_next; 114 struct ehci_qh *qh_scan_next;
112 unsigned scanning : 1; 115 unsigned scanning : 1;
116 unsigned async_count; /* async activity count */
113 117
114 /* periodic schedule support */ 118 /* periodic schedule support */
115#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ 119#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
@@ -229,7 +233,6 @@ static inline void iaa_watchdog_done(struct ehci_hcd *ehci)
229enum ehci_timer_action { 233enum ehci_timer_action {
230 TIMER_IO_WATCHDOG, 234 TIMER_IO_WATCHDOG,
231 TIMER_ASYNC_SHRINK, 235 TIMER_ASYNC_SHRINK,
232 TIMER_ASYNC_OFF,
233}; 236};
234 237
235static inline void 238static inline void