aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2006-06-05 12:28:57 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-06-21 18:04:16 -0400
commitc5e3b741a3fec6077a480aa65ded29d79ded8898 (patch)
tree13d5bf2fe7d2cc14500bafbb2c804da2ed23b102 /drivers/usb
parente323de46e83b6df2f330651907ac823f8d53308a (diff)
[PATCH] UHCI: Improve FSBR-off timing
This patch (as707) improves the FSBR operation in uhci-hcd by turning it off more quickly when it isn't needed. FSBR puts a noticeable load on a computer's PCI bus, so it should be disabled as soon as possible when it isn't in use. The patch leaves it running for only 10 ms after the last URB stops using it, on the theory that this should be long enough for a driver to submit another URB if it wants keep FSBR going. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/uhci-hcd.c5
-rw-r--r--drivers/usb/host/uhci-hcd.h8
-rw-r--r--drivers/usb/host/uhci-q.c71
3 files changed, 59 insertions, 25 deletions
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 025b969f95e8..7b48567622ef 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -497,9 +497,9 @@ static int uhci_start(struct usb_hcd *hcd)
497 hcd->uses_new_polling = 1; 497 hcd->uses_new_polling = 1;
498 498
499 spin_lock_init(&uhci->lock); 499 spin_lock_init(&uhci->lock);
500 500 setup_timer(&uhci->fsbr_timer, uhci_fsbr_timeout,
501 (unsigned long) uhci);
501 INIT_LIST_HEAD(&uhci->idle_qh_list); 502 INIT_LIST_HEAD(&uhci->idle_qh_list);
502
503 init_waitqueue_head(&uhci->waitqh); 503 init_waitqueue_head(&uhci->waitqh);
504 504
505 if (DEBUG_CONFIGURED) { 505 if (DEBUG_CONFIGURED) {
@@ -675,6 +675,7 @@ static void uhci_stop(struct usb_hcd *hcd)
675 uhci_scan_schedule(uhci, NULL); 675 uhci_scan_schedule(uhci, NULL);
676 spin_unlock_irq(&uhci->lock); 676 spin_unlock_irq(&uhci->lock);
677 677
678 del_timer_sync(&uhci->fsbr_timer);
678 release_uhci(uhci); 679 release_uhci(uhci);
679} 680}
680 681
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index 619d704f4e8f..108e3de2dc26 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -86,7 +86,7 @@
86 86
87/* When no queues need Full-Speed Bandwidth Reclamation, 87/* When no queues need Full-Speed Bandwidth Reclamation,
88 * delay this long before turning FSBR off */ 88 * delay this long before turning FSBR off */
89#define FSBR_OFF_DELAY msecs_to_jiffies(400) 89#define FSBR_OFF_DELAY msecs_to_jiffies(10)
90 90
91/* If a queue hasn't advanced after this much time, assume it is stuck */ 91/* If a queue hasn't advanced after this much time, assume it is stuck */
92#define QH_WAIT_TIMEOUT msecs_to_jiffies(200) 92#define QH_WAIT_TIMEOUT msecs_to_jiffies(200)
@@ -382,8 +382,6 @@ struct uhci_hcd {
382 __le32 *frame; 382 __le32 *frame;
383 void **frame_cpu; /* CPU's frame list */ 383 void **frame_cpu; /* CPU's frame list */
384 384
385 unsigned long fsbr_jiffies; /* Time when FSBR was last wanted */
386
387 enum uhci_rh_state rh_state; 385 enum uhci_rh_state rh_state;
388 unsigned long auto_stop_time; /* When to AUTO_STOP */ 386 unsigned long auto_stop_time; /* When to AUTO_STOP */
389 387
@@ -400,6 +398,10 @@ struct uhci_hcd {
400 need to be polled */ 398 need to be polled */
401 unsigned int is_initialized:1; /* Data structure is usable */ 399 unsigned int is_initialized:1; /* Data structure is usable */
402 unsigned int fsbr_is_on:1; /* FSBR is turned on */ 400 unsigned int fsbr_is_on:1; /* FSBR is turned on */
401 unsigned int fsbr_is_wanted:1; /* Does any URB want FSBR? */
402 unsigned int fsbr_expiring:1; /* FSBR is timing out */
403
404 struct timer_list fsbr_timer; /* For turning off FBSR */
403 405
404 /* Support for port suspend/resume/reset */ 406 /* Support for port suspend/resume/reset */
405 unsigned long port_c_suspend; /* Bit-arrays of ports */ 407 unsigned long port_c_suspend; /* Bit-arrays of ports */
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index b173d914d748..c9d72ac0a1d7 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -64,16 +64,30 @@ static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
64 urbp->fsbr = 1; 64 urbp->fsbr = 1;
65} 65}
66 66
67static void uhci_qh_wants_fsbr(struct uhci_hcd *uhci, struct uhci_qh *qh) 67static void uhci_urbp_wants_fsbr(struct uhci_hcd *uhci, struct urb_priv *urbp)
68{ 68{
69 struct urb_priv *urbp =
70 list_entry(qh->queue.next, struct urb_priv, node);
71
72 if (urbp->fsbr) { 69 if (urbp->fsbr) {
73 uhci->fsbr_jiffies = jiffies; 70 uhci->fsbr_is_wanted = 1;
74 if (!uhci->fsbr_is_on) 71 if (!uhci->fsbr_is_on)
75 uhci_fsbr_on(uhci); 72 uhci_fsbr_on(uhci);
73 else if (uhci->fsbr_expiring) {
74 uhci->fsbr_expiring = 0;
75 del_timer(&uhci->fsbr_timer);
76 }
77 }
78}
79
80static void uhci_fsbr_timeout(unsigned long _uhci)
81{
82 struct uhci_hcd *uhci = (struct uhci_hcd *) _uhci;
83 unsigned long flags;
84
85 spin_lock_irqsave(&uhci->lock, flags);
86 if (uhci->fsbr_expiring) {
87 uhci->fsbr_expiring = 0;
88 uhci_fsbr_off(uhci);
76 } 89 }
90 spin_unlock_irqrestore(&uhci->lock, flags);
77} 91}
78 92
79 93
@@ -287,7 +301,7 @@ static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh,
287 if (qh->type == USB_ENDPOINT_XFER_ISOC) { 301 if (qh->type == USB_ENDPOINT_XFER_ISOC) {
288 ret = (uhci->frame_number + uhci->is_stopped != 302 ret = (uhci->frame_number + uhci->is_stopped !=
289 qh->unlink_frame); 303 qh->unlink_frame);
290 return ret; 304 goto done;
291 } 305 }
292 306
293 /* If the URB isn't first on its queue, adjust the link pointer 307 /* If the URB isn't first on its queue, adjust the link pointer
@@ -304,24 +318,26 @@ static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh,
304 td = list_entry(urbp->td_list.prev, struct uhci_td, 318 td = list_entry(urbp->td_list.prev, struct uhci_td,
305 list); 319 list);
306 ptd->link = td->link; 320 ptd->link = td->link;
307 return ret; 321 goto done;
308 } 322 }
309 323
310 /* If the QH element pointer is UHCI_PTR_TERM then then currently 324 /* If the QH element pointer is UHCI_PTR_TERM then then currently
311 * executing URB has already been unlinked, so this one isn't it. */ 325 * executing URB has already been unlinked, so this one isn't it. */
312 if (qh_element(qh) == UHCI_PTR_TERM) 326 if (qh_element(qh) == UHCI_PTR_TERM)
313 return ret; 327 goto done;
314 qh->element = UHCI_PTR_TERM; 328 qh->element = UHCI_PTR_TERM;
315 329
316 /* Control pipes have to worry about toggles */ 330 /* Control pipes have to worry about toggles */
317 if (qh->type == USB_ENDPOINT_XFER_CONTROL) 331 if (qh->type == USB_ENDPOINT_XFER_CONTROL)
318 return ret; 332 goto done;
319 333
320 /* Save the next toggle value */ 334 /* Save the next toggle value */
321 WARN_ON(list_empty(&urbp->td_list)); 335 WARN_ON(list_empty(&urbp->td_list));
322 td = list_entry(urbp->td_list.next, struct uhci_td, list); 336 td = list_entry(urbp->td_list.next, struct uhci_td, list);
323 qh->needs_fixup = 1; 337 qh->needs_fixup = 1;
324 qh->initial_toggle = uhci_toggle(td_token(td)); 338 qh->initial_toggle = uhci_toggle(td_token(td));
339
340done:
325 return ret; 341 return ret;
326} 342}
327 343
@@ -1175,7 +1191,7 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd,
1175 * queue isn't stopped. */ 1191 * queue isn't stopped. */
1176 if (qh->queue.next == &urbp->node && !qh->is_stopped) { 1192 if (qh->queue.next == &urbp->node && !qh->is_stopped) {
1177 uhci_activate_qh(uhci, qh); 1193 uhci_activate_qh(uhci, qh);
1178 uhci_qh_wants_fsbr(uhci, qh); 1194 uhci_urbp_wants_fsbr(uhci, urbp);
1179 } 1195 }
1180 goto done; 1196 goto done;
1181 1197
@@ -1404,7 +1420,7 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
1404 unsigned status; 1420 unsigned status;
1405 1421
1406 if (qh->type == USB_ENDPOINT_XFER_ISOC) 1422 if (qh->type == USB_ENDPOINT_XFER_ISOC)
1407 return ret; 1423 goto done;
1408 1424
1409 /* Treat an UNLINKING queue as though it hasn't advanced. 1425 /* Treat an UNLINKING queue as though it hasn't advanced.
1410 * This is okay because reactivation will treat it as though 1426 * This is okay because reactivation will treat it as though
@@ -1427,21 +1443,24 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
1427 /* We're okay, the queue has advanced */ 1443 /* We're okay, the queue has advanced */
1428 qh->wait_expired = 0; 1444 qh->wait_expired = 0;
1429 qh->advance_jiffies = jiffies; 1445 qh->advance_jiffies = jiffies;
1430 return ret; 1446 goto done;
1431 } 1447 }
1432 ret = 0; 1448 ret = 0;
1433 } 1449 }
1434 1450
1435 /* The queue hasn't advanced; check for timeout */ 1451 /* The queue hasn't advanced; check for timeout */
1436 if (!qh->wait_expired && time_after(jiffies, 1452 if (qh->wait_expired)
1437 qh->advance_jiffies + QH_WAIT_TIMEOUT)) { 1453 goto done;
1454
1455 if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
1438 1456
1439 /* Detect the Intel bug and work around it */ 1457 /* Detect the Intel bug and work around it */
1440 if (qh->post_td && qh_element(qh) == 1458 if (qh->post_td && qh_element(qh) ==
1441 cpu_to_le32(qh->post_td->dma_handle)) { 1459 cpu_to_le32(qh->post_td->dma_handle)) {
1442 qh->element = qh->post_td->link; 1460 qh->element = qh->post_td->link;
1443 qh->advance_jiffies = jiffies; 1461 qh->advance_jiffies = jiffies;
1444 return 1; 1462 ret = 1;
1463 goto done;
1445 } 1464 }
1446 1465
1447 qh->wait_expired = 1; 1466 qh->wait_expired = 1;
@@ -1452,7 +1471,14 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
1452 * starts moving again. */ 1471 * starts moving again. */
1453 if (urbp && urbp->fsbr && !(status & TD_CTRL_IOC)) 1472 if (urbp && urbp->fsbr && !(status & TD_CTRL_IOC))
1454 uhci_unlink_qh(uhci, qh); 1473 uhci_unlink_qh(uhci, qh);
1474
1475 } else {
1476 /* Unmoving but not-yet-expired queues keep FSBR alive */
1477 if (urbp)
1478 uhci_urbp_wants_fsbr(uhci, urbp);
1455 } 1479 }
1480
1481done:
1456 return ret; 1482 return ret;
1457} 1483}
1458 1484
@@ -1472,6 +1498,7 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
1472 uhci->scan_in_progress = 1; 1498 uhci->scan_in_progress = 1;
1473rescan: 1499rescan:
1474 uhci->need_rescan = 0; 1500 uhci->need_rescan = 0;
1501 uhci->fsbr_is_wanted = 0;
1475 1502
1476 uhci_clear_next_interrupt(uhci); 1503 uhci_clear_next_interrupt(uhci);
1477 uhci_get_current_frame_number(uhci); 1504 uhci_get_current_frame_number(uhci);
@@ -1487,8 +1514,10 @@ rescan:
1487 1514
1488 if (uhci_advance_check(uhci, qh)) { 1515 if (uhci_advance_check(uhci, qh)) {
1489 uhci_scan_qh(uhci, qh, regs); 1516 uhci_scan_qh(uhci, qh, regs);
1490 if (qh->state == QH_STATE_ACTIVE) 1517 if (qh->state == QH_STATE_ACTIVE) {
1491 uhci_qh_wants_fsbr(uhci, qh); 1518 uhci_urbp_wants_fsbr(uhci,
1519 list_entry(qh->queue.next, struct urb_priv, node));
1520 }
1492 } 1521 }
1493 } 1522 }
1494 } 1523 }
@@ -1498,9 +1527,11 @@ rescan:
1498 goto rescan; 1527 goto rescan;
1499 uhci->scan_in_progress = 0; 1528 uhci->scan_in_progress = 0;
1500 1529
1501 if (uhci->fsbr_is_on && time_after(jiffies, 1530 if (uhci->fsbr_is_on && !uhci->fsbr_is_wanted &&
1502 uhci->fsbr_jiffies + FSBR_OFF_DELAY)) 1531 !uhci->fsbr_expiring) {
1503 uhci_fsbr_off(uhci); 1532 uhci->fsbr_expiring = 1;
1533 mod_timer(&uhci->fsbr_timer, jiffies + FSBR_OFF_DELAY);
1534 }
1504 1535
1505 if (list_empty(&uhci->skel_unlink_qh->node)) 1536 if (list_empty(&uhci->skel_unlink_qh->node))
1506 uhci_clear_next_interrupt(uhci); 1537 uhci_clear_next_interrupt(uhci);