aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/host/ehci-hcd.c16
-rw-r--r--drivers/usb/host/ehci-hub.c1
-rw-r--r--drivers/usb/host/ehci-q.c78
-rw-r--r--drivers/usb/host/ehci-timer.c2
-rw-r--r--drivers/usb/host/ehci.h4
5 files changed, 59 insertions, 42 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 86e8ee169c67..74ffd20edff8 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -94,8 +94,6 @@ static const char hcd_name [] = "ehci_hcd";
94#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */ 94#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */
95 95
96#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ 96#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
97#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1)
98 /* 5-ms async qh unlink delay */
99 97
100/* Initial IRQ latency: faster than hw default */ 98/* Initial IRQ latency: faster than hw default */
101static int log2_irq_thresh = 0; // 0 to 6 99static int log2_irq_thresh = 0; // 0 to 6
@@ -130,15 +128,6 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us");
130static void 128static void
131timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) 129timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
132{ 130{
133 /* Don't override timeouts which shrink or (later) disable
134 * the async ring; just the I/O watchdog. Note that if a
135 * SHRINK were pending, OFF would never be requested.
136 */
137 if (timer_pending(&ehci->watchdog)
138 && (BIT(TIMER_ASYNC_SHRINK)
139 & ehci->actions))
140 return;
141
142 if (!test_and_set_bit(action, &ehci->actions)) { 131 if (!test_and_set_bit(action, &ehci->actions)) {
143 unsigned long t; 132 unsigned long t;
144 133
@@ -148,10 +137,6 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
148 return; 137 return;
149 t = EHCI_IO_JIFFIES; 138 t = EHCI_IO_JIFFIES;
150 break; 139 break;
151 /* case TIMER_ASYNC_SHRINK: */
152 default:
153 t = EHCI_SHRINK_JIFFIES;
154 break;
155 } 140 }
156 mod_timer(&ehci->watchdog, t + jiffies); 141 mod_timer(&ehci->watchdog, t + jiffies);
157 } 142 }
@@ -307,6 +292,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
307/*-------------------------------------------------------------------------*/ 292/*-------------------------------------------------------------------------*/
308 293
309static void end_unlink_async(struct ehci_hcd *ehci); 294static void end_unlink_async(struct ehci_hcd *ehci);
295static void unlink_empty_async(struct ehci_hcd *ehci);
310static void ehci_work(struct ehci_hcd *ehci); 296static void ehci_work(struct ehci_hcd *ehci);
311static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); 297static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
312static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); 298static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index a3822700e496..5d84562e2716 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -300,6 +300,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
300 ehci->rh_state = EHCI_RH_SUSPENDED; 300 ehci->rh_state = EHCI_RH_SUSPENDED;
301 301
302 end_unlink_async(ehci); 302 end_unlink_async(ehci);
303 unlink_empty_async(ehci);
303 ehci_handle_intr_unlinks(ehci); 304 ehci_handle_intr_unlinks(ehci);
304 end_free_itds(ehci); 305 end_free_itds(ehci);
305 306
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 181832921c53..bae931767825 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1205,7 +1205,7 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested)
1205 end_unlink_async(ehci); 1205 end_unlink_async(ehci);
1206 1206
1207 /* Otherwise start a new IAA cycle */ 1207 /* Otherwise start a new IAA cycle */
1208 } else { 1208 } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) {
1209 /* Make sure the unlinks are all visible to the hardware */ 1209 /* Make sure the unlinks are all visible to the hardware */
1210 wmb(); 1210 wmb();
1211 1211
@@ -1253,6 +1253,39 @@ static void end_unlink_async(struct ehci_hcd *ehci)
1253 } 1253 }
1254} 1254}
1255 1255
1256static void unlink_empty_async(struct ehci_hcd *ehci)
1257{
1258 struct ehci_qh *qh, *next;
1259 bool stopped = (ehci->rh_state < EHCI_RH_RUNNING);
1260 bool check_unlinks_later = false;
1261
1262 /* Unlink all the async QHs that have been empty for a timer cycle */
1263 next = ehci->async->qh_next.qh;
1264 while (next) {
1265 qh = next;
1266 next = qh->qh_next.qh;
1267
1268 if (list_empty(&qh->qtd_list) &&
1269 qh->qh_state == QH_STATE_LINKED) {
1270 if (!stopped && qh->unlink_cycle ==
1271 ehci->async_unlink_cycle)
1272 check_unlinks_later = true;
1273 else
1274 single_unlink_async(ehci, qh);
1275 }
1276 }
1277
1278 /* Start a new IAA cycle if any QHs are waiting for it */
1279 if (ehci->async_unlink)
1280 start_iaa_cycle(ehci, false);
1281
1282 /* QHs that haven't been empty for long enough will be handled later */
1283 if (check_unlinks_later) {
1284 ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
1285 ++ehci->async_unlink_cycle;
1286 }
1287}
1288
1256/* makes sure the async qh will become idle */ 1289/* makes sure the async qh will become idle */
1257/* caller must own ehci->lock */ 1290/* caller must own ehci->lock */
1258 1291
@@ -1277,12 +1310,8 @@ static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
1277 1310
1278static void scan_async (struct ehci_hcd *ehci) 1311static void scan_async (struct ehci_hcd *ehci)
1279{ 1312{
1280 bool stopped;
1281 struct ehci_qh *qh; 1313 struct ehci_qh *qh;
1282 enum ehci_timer_action action = TIMER_IO_WATCHDOG; 1314 bool check_unlinks_later = false;
1283
1284 timer_action_done (ehci, TIMER_ASYNC_SHRINK);
1285 stopped = (ehci->rh_state < EHCI_RH_RUNNING);
1286 1315
1287 ehci->qh_scan_next = ehci->async->qh_next.qh; 1316 ehci->qh_scan_next = ehci->async->qh_next.qh;
1288 while (ehci->qh_scan_next) { 1317 while (ehci->qh_scan_next) {
@@ -1301,28 +1330,27 @@ static void scan_async (struct ehci_hcd *ehci)
1301 * in single_unlink_async(). 1330 * in single_unlink_async().
1302 */ 1331 */
1303 temp = qh_completions(ehci, qh); 1332 temp = qh_completions(ehci, qh);
1304 if (qh->needs_rescan) 1333 if (qh->needs_rescan) {
1305 start_unlink_async(ehci, qh); 1334 start_unlink_async(ehci, qh);
1306 qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES; 1335 } else if (list_empty(&qh->qtd_list)
1307 if (temp != 0) 1336 && qh->qh_state == QH_STATE_LINKED) {
1337 qh->unlink_cycle = ehci->async_unlink_cycle;
1338 check_unlinks_later = true;
1339 } else if (temp != 0)
1308 goto rescan; 1340 goto rescan;
1309 } 1341 }
1342 }
1310 1343
1311 /* unlink idle entries, reducing DMA usage as well 1344 /*
1312 * as HCD schedule-scanning costs. delay for any qh 1345 * Unlink empty entries, reducing DMA usage as well
1313 * we just scanned, there's a not-unusual case that it 1346 * as HCD schedule-scanning costs. Delay for any qh
1314 * doesn't stay idle for long. 1347 * we just scanned, there's a not-unusual case that it
1315 * (plus, avoids some kind of re-activation race.) 1348 * doesn't stay idle for long.
1316 */ 1349 */
1317 if (list_empty(&qh->qtd_list) 1350 if (check_unlinks_later && ehci->rh_state == EHCI_RH_RUNNING &&
1318 && qh->qh_state == QH_STATE_LINKED) { 1351 !(ehci->enabled_hrtimer_events &
1319 if (!ehci->async_unlink && (stopped || 1352 BIT(EHCI_HRTIMER_ASYNC_UNLINKS))) {
1320 time_after_eq(jiffies, qh->unlink_time))) 1353 ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
1321 start_unlink_async(ehci, qh); 1354 ++ehci->async_unlink_cycle;
1322 else
1323 action = TIMER_ASYNC_SHRINK;
1324 }
1325 } 1355 }
1326 if (action == TIMER_ASYNC_SHRINK)
1327 timer_action (ehci, TIMER_ASYNC_SHRINK);
1328} 1356}
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c
index 8ca5f152f5bd..a823290b5139 100644
--- a/drivers/usb/host/ehci-timer.c
+++ b/drivers/usb/host/ehci-timer.c
@@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = {
72 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 72 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */
73 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 73 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */
74 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */ 74 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */
75 6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */
75 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ 76 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */
76 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 77 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */
77 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ 78 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */
@@ -347,6 +348,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = {
347 ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ 348 ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */
348 ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ 349 ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */
349 end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ 350 end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */
351 unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */
350 ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ 352 ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */
351 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 353 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */
352 ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ 354 ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 1a782775881b..303c36cc99c9 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -84,6 +84,7 @@ enum ehci_hrtimer_event {
84 EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ 84 EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
85 EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ 85 EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
86 EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ 86 EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */
87 EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */
87 EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ 88 EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */
88 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ 89 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
89 EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ 90 EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
@@ -123,6 +124,7 @@ struct ehci_hcd { /* one per controller */
123 struct ehci_qh *async_unlink_last; 124 struct ehci_qh *async_unlink_last;
124 struct ehci_qh *async_iaa; 125 struct ehci_qh *async_iaa;
125 struct ehci_qh *qh_scan_next; 126 struct ehci_qh *qh_scan_next;
127 unsigned async_unlink_cycle;
126 unsigned async_count; /* async activity count */ 128 unsigned async_count; /* async activity count */
127 129
128 /* periodic schedule support */ 130 /* periodic schedule support */
@@ -232,7 +234,6 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
232 234
233enum ehci_timer_action { 235enum ehci_timer_action {
234 TIMER_IO_WATCHDOG, 236 TIMER_IO_WATCHDOG,
235 TIMER_ASYNC_SHRINK,
236}; 237};
237 238
238static inline void 239static inline void
@@ -382,7 +383,6 @@ struct ehci_qh {
382 struct ehci_qtd *dummy; 383 struct ehci_qtd *dummy;
383 struct ehci_qh *unlink_next; /* next on unlink list */ 384 struct ehci_qh *unlink_next; /* next on unlink list */
384 385
385 unsigned long unlink_time;
386 unsigned unlink_cycle; 386 unsigned unlink_cycle;
387 unsigned stamp; 387 unsigned stamp;
388 388