diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2005-04-21 16:04:58 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-06-27 17:43:44 -0400 |
commit | 6c1b445c226dd82d0961725dec8051b95003723a (patch) | |
tree | 1e812a2e9e2d63879555bb48303a8bc344be3864 /drivers/usb/host/uhci-hcd.c | |
parent | 4daaa87c8f19c5f1978470e9e91b74d9e0fb0f8e (diff) |
[PATCH] USB UHCI: Use root-hub IRQs while suspended
This patch, which has as478b as a prerequisite, enables the uhci-hcd
driver to take advantage of root-hub IRQs rather than polling during the
time it is suspended. (Unfortunately the hardware doesn't support
port-change interrupts while the controller is running.) It also turns
off the driver's private timer while the controller is suspended, as it
isn't needed then. The combined elimination of polling interrupts and
timer interrupts ought to be enough to allow some systems to save a
noticeable amount of power while they are otherwise idle.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/uhci-hcd.c')
-rw-r--r-- | drivers/usb/host/uhci-hcd.c | 70 |
1 files changed, 23 insertions, 47 deletions
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 82e608a4bbd0..25a718eb1d0f 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c | |||
@@ -84,6 +84,8 @@ static char *errbuf; | |||
84 | 84 | ||
85 | static kmem_cache_t *uhci_up_cachep; /* urb_priv */ | 85 | static kmem_cache_t *uhci_up_cachep; /* urb_priv */ |
86 | 86 | ||
87 | static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state); | ||
88 | static void wakeup_rh(struct uhci_hcd *uhci); | ||
87 | static void uhci_get_current_frame_number(struct uhci_hcd *uhci); | 89 | static void uhci_get_current_frame_number(struct uhci_hcd *uhci); |
88 | 90 | ||
89 | /* If a transfer is still active after this much time, turn off FSBR */ | 91 | /* If a transfer is still active after this much time, turn off FSBR */ |
@@ -133,12 +135,12 @@ static void reset_hc(struct uhci_hcd *uhci) | |||
133 | outw(0, uhci->io_addr + USBINTR); | 135 | outw(0, uhci->io_addr + USBINTR); |
134 | outw(0, uhci->io_addr + USBCMD); | 136 | outw(0, uhci->io_addr + USBCMD); |
135 | 137 | ||
136 | uhci->resume_detect = 0; | ||
137 | uhci->port_c_suspend = uhci->suspended_ports = | 138 | uhci->port_c_suspend = uhci->suspended_ports = |
138 | uhci->resuming_ports = 0; | 139 | uhci->resuming_ports = 0; |
139 | uhci->rh_state = UHCI_RH_RESET; | 140 | uhci->rh_state = UHCI_RH_RESET; |
140 | uhci->is_stopped = UHCI_IS_STOPPED; | 141 | uhci->is_stopped = UHCI_IS_STOPPED; |
141 | uhci_to_hcd(uhci)->state = HC_STATE_HALT; | 142 | uhci_to_hcd(uhci)->state = HC_STATE_HALT; |
143 | uhci_to_hcd(uhci)->poll_rh = 0; | ||
142 | } | 144 | } |
143 | 145 | ||
144 | /* | 146 | /* |
@@ -148,6 +150,7 @@ static void hc_died(struct uhci_hcd *uhci) | |||
148 | { | 150 | { |
149 | reset_hc(uhci); | 151 | reset_hc(uhci); |
150 | uhci->hc_inaccessible = 1; | 152 | uhci->hc_inaccessible = 1; |
153 | del_timer(&uhci->stall_timer); | ||
151 | } | 154 | } |
152 | 155 | ||
153 | /* | 156 | /* |
@@ -302,14 +305,14 @@ __acquires(uhci->lock) | |||
302 | 305 | ||
303 | uhci->rh_state = new_state; | 306 | uhci->rh_state = new_state; |
304 | uhci->is_stopped = UHCI_IS_STOPPED; | 307 | uhci->is_stopped = UHCI_IS_STOPPED; |
305 | uhci->resume_detect = 0; | 308 | del_timer(&uhci->stall_timer); |
309 | uhci_to_hcd(uhci)->poll_rh = !int_enable; | ||
306 | 310 | ||
307 | uhci_scan_schedule(uhci, NULL); | 311 | uhci_scan_schedule(uhci, NULL); |
308 | } | 312 | } |
309 | 313 | ||
310 | static void start_rh(struct uhci_hcd *uhci) | 314 | static void start_rh(struct uhci_hcd *uhci) |
311 | { | 315 | { |
312 | uhci->rh_state = UHCI_RH_RUNNING; | ||
313 | uhci->is_stopped = 0; | 316 | uhci->is_stopped = 0; |
314 | smp_wmb(); | 317 | smp_wmb(); |
315 | 318 | ||
@@ -320,6 +323,9 @@ static void start_rh(struct uhci_hcd *uhci) | |||
320 | outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, | 323 | outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, |
321 | uhci->io_addr + USBINTR); | 324 | uhci->io_addr + USBINTR); |
322 | mb(); | 325 | mb(); |
326 | uhci->rh_state = UHCI_RH_RUNNING; | ||
327 | uhci_to_hcd(uhci)->poll_rh = 1; | ||
328 | restart_timer(uhci); | ||
323 | } | 329 | } |
324 | 330 | ||
325 | static void wakeup_rh(struct uhci_hcd *uhci) | 331 | static void wakeup_rh(struct uhci_hcd *uhci) |
@@ -353,36 +359,9 @@ __acquires(uhci->lock) | |||
353 | } | 359 | } |
354 | 360 | ||
355 | start_rh(uhci); | 361 | start_rh(uhci); |
356 | } | ||
357 | |||
358 | static void rh_state_transitions(struct uhci_hcd *uhci) | ||
359 | { | ||
360 | switch (uhci->rh_state) { | ||
361 | case UHCI_RH_RUNNING: | ||
362 | /* are any devices attached? */ | ||
363 | if (!any_ports_active(uhci)) { | ||
364 | uhci->rh_state = UHCI_RH_RUNNING_NODEVS; | ||
365 | uhci->auto_stop_time = jiffies + HZ; | ||
366 | } | ||
367 | break; | ||
368 | 362 | ||
369 | case UHCI_RH_RUNNING_NODEVS: | 363 | /* Restart root hub polling */ |
370 | /* auto-stop if nothing connected for 1 second */ | 364 | mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies); |
371 | if (any_ports_active(uhci)) | ||
372 | uhci->rh_state = UHCI_RH_RUNNING; | ||
373 | else if (time_after_eq(jiffies, uhci->auto_stop_time)) | ||
374 | suspend_rh(uhci, UHCI_RH_AUTO_STOPPED); | ||
375 | break; | ||
376 | |||
377 | case UHCI_RH_AUTO_STOPPED: | ||
378 | /* wakeup if requested by a device */ | ||
379 | if (uhci->resume_detect) | ||
380 | wakeup_rh(uhci); | ||
381 | break; | ||
382 | |||
383 | default: | ||
384 | break; | ||
385 | } | ||
386 | } | 365 | } |
387 | 366 | ||
388 | static void stall_callback(unsigned long _uhci) | 367 | static void stall_callback(unsigned long _uhci) |
@@ -394,14 +373,8 @@ static void stall_callback(unsigned long _uhci) | |||
394 | uhci_scan_schedule(uhci, NULL); | 373 | uhci_scan_schedule(uhci, NULL); |
395 | check_fsbr(uhci); | 374 | check_fsbr(uhci); |
396 | 375 | ||
397 | /* Poll for and perform state transitions */ | 376 | if (!uhci->is_stopped) |
398 | if (!uhci->hc_inaccessible) { | 377 | restart_timer(uhci); |
399 | rh_state_transitions(uhci); | ||
400 | if (uhci->suspended_ports) | ||
401 | uhci_check_ports(uhci); | ||
402 | } | ||
403 | |||
404 | restart_timer(uhci); | ||
405 | spin_unlock_irqrestore(&uhci->lock, flags); | 378 | spin_unlock_irqrestore(&uhci->lock, flags); |
406 | } | 379 | } |
407 | 380 | ||
@@ -443,7 +416,7 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) | |||
443 | } | 416 | } |
444 | 417 | ||
445 | if (status & USBSTS_RD) | 418 | if (status & USBSTS_RD) |
446 | uhci->resume_detect = 1; | 419 | usb_hcd_poll_rh_status(hcd); |
447 | 420 | ||
448 | spin_lock_irqsave(&uhci->lock, flags); | 421 | spin_lock_irqsave(&uhci->lock, flags); |
449 | uhci_scan_schedule(uhci, regs); | 422 | uhci_scan_schedule(uhci, regs); |
@@ -542,6 +515,7 @@ static int uhci_start(struct usb_hcd *hcd) | |||
542 | struct dentry *dentry; | 515 | struct dentry *dentry; |
543 | 516 | ||
544 | io_size = (unsigned) hcd->rsrc_len; | 517 | io_size = (unsigned) hcd->rsrc_len; |
518 | hcd->uses_new_polling = 1; | ||
545 | if (pci_find_capability(to_pci_dev(uhci_dev(uhci)), PCI_CAP_ID_PM)) | 519 | if (pci_find_capability(to_pci_dev(uhci_dev(uhci)), PCI_CAP_ID_PM)) |
546 | hcd->can_wakeup = 1; /* Assume it supports PME# */ | 520 | hcd->can_wakeup = 1; /* Assume it supports PME# */ |
547 | 521 | ||
@@ -714,8 +688,6 @@ static int uhci_start(struct usb_hcd *hcd) | |||
714 | configure_hc(uhci); | 688 | configure_hc(uhci); |
715 | start_rh(uhci); | 689 | start_rh(uhci); |
716 | 690 | ||
717 | restart_timer(uhci); | ||
718 | |||
719 | udev->speed = USB_SPEED_FULL; | 691 | udev->speed = USB_SPEED_FULL; |
720 | 692 | ||
721 | if (usb_hcd_register_root_hub(udev, hcd) != 0) { | 693 | if (usb_hcd_register_root_hub(udev, hcd) != 0) { |
@@ -730,8 +702,8 @@ static int uhci_start(struct usb_hcd *hcd) | |||
730 | * error exits: | 702 | * error exits: |
731 | */ | 703 | */ |
732 | err_start_root_hub: | 704 | err_start_root_hub: |
733 | del_timer_sync(&uhci->stall_timer); | ||
734 | reset_hc(uhci); | 705 | reset_hc(uhci); |
706 | del_timer_sync(&uhci->stall_timer); | ||
735 | 707 | ||
736 | err_alloc_skelqh: | 708 | err_alloc_skelqh: |
737 | for (i = 0; i < UHCI_NUM_SKELQH; i++) | 709 | for (i = 0; i < UHCI_NUM_SKELQH; i++) |
@@ -771,13 +743,12 @@ static void uhci_stop(struct usb_hcd *hcd) | |||
771 | { | 743 | { |
772 | struct uhci_hcd *uhci = hcd_to_uhci(hcd); | 744 | struct uhci_hcd *uhci = hcd_to_uhci(hcd); |
773 | 745 | ||
774 | del_timer_sync(&uhci->stall_timer); | ||
775 | |||
776 | spin_lock_irq(&uhci->lock); | 746 | spin_lock_irq(&uhci->lock); |
777 | reset_hc(uhci); | 747 | reset_hc(uhci); |
778 | uhci_scan_schedule(uhci, NULL); | 748 | uhci_scan_schedule(uhci, NULL); |
779 | spin_unlock_irq(&uhci->lock); | 749 | spin_unlock_irq(&uhci->lock); |
780 | 750 | ||
751 | del_timer_sync(&uhci->stall_timer); | ||
781 | release_uhci(uhci); | 752 | release_uhci(uhci); |
782 | } | 753 | } |
783 | 754 | ||
@@ -844,6 +815,8 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message) | |||
844 | 815 | ||
845 | done: | 816 | done: |
846 | spin_unlock_irq(&uhci->lock); | 817 | spin_unlock_irq(&uhci->lock); |
818 | if (rc == 0) | ||
819 | del_timer_sync(&hcd->rh_timer); | ||
847 | return rc; | 820 | return rc; |
848 | } | 821 | } |
849 | 822 | ||
@@ -875,6 +848,9 @@ static int uhci_resume(struct usb_hcd *hcd) | |||
875 | suspend_rh(uhci, UHCI_RH_SUSPENDED); | 848 | suspend_rh(uhci, UHCI_RH_SUSPENDED); |
876 | 849 | ||
877 | spin_unlock_irq(&uhci->lock); | 850 | spin_unlock_irq(&uhci->lock); |
851 | |||
852 | if (hcd->poll_rh) | ||
853 | usb_hcd_poll_rh_status(hcd); | ||
878 | return 0; | 854 | return 0; |
879 | } | 855 | } |
880 | #endif | 856 | #endif |