aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2005-04-21 16:04:58 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2005-06-27 17:43:44 -0400
commit6c1b445c226dd82d0961725dec8051b95003723a (patch)
tree1e812a2e9e2d63879555bb48303a8bc344be3864
parent4daaa87c8f19c5f1978470e9e91b74d9e0fb0f8e (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>
-rw-r--r--drivers/usb/host/uhci-hcd.c70
-rw-r--r--drivers/usb/host/uhci-hcd.h12
-rw-r--r--drivers/usb/host/uhci-hub.c67
-rw-r--r--drivers/usb/host/uhci-q.c3
4 files changed, 92 insertions, 60 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
85static kmem_cache_t *uhci_up_cachep; /* urb_priv */ 85static kmem_cache_t *uhci_up_cachep; /* urb_priv */
86 86
87static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state);
88static void wakeup_rh(struct uhci_hcd *uhci);
87static void uhci_get_current_frame_number(struct uhci_hcd *uhci); 89static 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
310static void start_rh(struct uhci_hcd *uhci) 314static 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
325static void wakeup_rh(struct uhci_hcd *uhci) 331static 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
358static 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
388static void stall_callback(unsigned long _uhci) 367static 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 */
732err_start_root_hub: 704err_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
736err_alloc_skelqh: 708err_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
845done: 816done:
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
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index 827df5e06800..d7c67b73eb7a 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -327,18 +327,19 @@ static inline int __interval_to_skel(int interval)
327 * driver learns to autosuspend.) 327 * driver learns to autosuspend.)
328 */ 328 */
329enum uhci_rh_state { 329enum uhci_rh_state {
330 /* In the next 4 states the HC must be halted */ 330 /* In the following states the HC must be halted.
331 UHCI_RH_RESET, /* These two must come first */ 331 * These two must come first */
332 UHCI_RH_RESET,
332 UHCI_RH_SUSPENDED, 333 UHCI_RH_SUSPENDED,
333 334
334 UHCI_RH_AUTO_STOPPED, 335 UHCI_RH_AUTO_STOPPED,
335 UHCI_RH_RESUMING, 336 UHCI_RH_RESUMING,
336 337
337 /* In the next state the HC changes from running to halted, so it 338 /* In this state the HC changes from running to halted,
338 * can legally appear either way */ 339 * so it can legally appear either way. */
339 UHCI_RH_SUSPENDING, 340 UHCI_RH_SUSPENDING,
340 341
341 /* In the next two states it's an error if the HC is halted. 342 /* In the following states it's an error if the HC is halted.
342 * These two must come last */ 343 * These two must come last */
343 UHCI_RH_RUNNING, /* The normal state */ 344 UHCI_RH_RUNNING, /* The normal state */
344 UHCI_RH_RUNNING_NODEVS, /* Running with no devices attached */ 345 UHCI_RH_RUNNING_NODEVS, /* Running with no devices attached */
@@ -380,7 +381,6 @@ struct uhci_hcd {
380 381
381 unsigned int scan_in_progress:1; /* Schedule scan is running */ 382 unsigned int scan_in_progress:1; /* Schedule scan is running */
382 unsigned int need_rescan:1; /* Redo the schedule scan */ 383 unsigned int need_rescan:1; /* Redo the schedule scan */
383 unsigned int resume_detect:1; /* Need a Global Resume */
384 unsigned int hc_inaccessible:1; /* HC is suspended or dead */ 384 unsigned int hc_inaccessible:1; /* HC is suspended or dead */
385 385
386 /* Support for port suspend/resume/reset */ 386 /* Support for port suspend/resume/reset */
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
index 13652de52203..4eace2b19ddb 100644
--- a/drivers/usb/host/uhci-hub.c
+++ b/drivers/usb/host/uhci-hub.c
@@ -49,22 +49,16 @@ static int any_ports_active(struct uhci_hcd *uhci)
49 return 0; 49 return 0;
50} 50}
51 51
52static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) 52static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf)
53{ 53{
54 struct uhci_hcd *uhci = hcd_to_uhci(hcd);
55 int port; 54 int port;
56 55
57 if (uhci->hc_inaccessible)
58 return 0;
59
60 *buf = 0; 56 *buf = 0;
61 for (port = 0; port < uhci->rh_numports; ++port) { 57 for (port = 0; port < uhci->rh_numports; ++port) {
62 if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) || 58 if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) ||
63 test_bit(port, &uhci->port_c_suspend)) 59 test_bit(port, &uhci->port_c_suspend))
64 *buf |= (1 << (port + 1)); 60 *buf |= (1 << (port + 1));
65 } 61 }
66 if (*buf && uhci->is_stopped)
67 uhci->resume_detect = 1;
68 return !!*buf; 62 return !!*buf;
69} 63}
70 64
@@ -134,6 +128,11 @@ static void uhci_check_ports(struct uhci_hcd *uhci)
134 set_bit(port, &uhci->resuming_ports); 128 set_bit(port, &uhci->resuming_ports);
135 uhci->ports_timeout = jiffies + 129 uhci->ports_timeout = jiffies +
136 msecs_to_jiffies(20); 130 msecs_to_jiffies(20);
131
132 /* Make sure we see the port again
133 * after the resuming period is over. */
134 mod_timer(&uhci_to_hcd(uhci)->rh_timer,
135 uhci->ports_timeout);
137 } else if (time_after_eq(jiffies, 136 } else if (time_after_eq(jiffies,
138 uhci->ports_timeout)) { 137 uhci->ports_timeout)) {
139 uhci_finish_suspend(uhci, port, port_addr); 138 uhci_finish_suspend(uhci, port, port_addr);
@@ -142,6 +141,60 @@ static void uhci_check_ports(struct uhci_hcd *uhci)
142 } 141 }
143} 142}
144 143
144static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
145{
146 struct uhci_hcd *uhci = hcd_to_uhci(hcd);
147 unsigned long flags;
148 int status;
149
150 spin_lock_irqsave(&uhci->lock, flags);
151 if (uhci->hc_inaccessible) {
152 status = 0;
153 goto done;
154 }
155
156 uhci_check_ports(uhci);
157 status = get_hub_status_data(uhci, buf);
158
159 switch (uhci->rh_state) {
160 case UHCI_RH_SUSPENDING:
161 case UHCI_RH_SUSPENDED:
162 /* if port change, ask to be resumed */
163 if (status)
164 usb_hcd_resume_root_hub(hcd);
165 break;
166
167 case UHCI_RH_AUTO_STOPPED:
168 /* if port change, auto start */
169 if (status)
170 wakeup_rh(uhci);
171 break;
172
173 case UHCI_RH_RUNNING:
174 /* are any devices attached? */
175 if (!any_ports_active(uhci)) {
176 uhci->rh_state = UHCI_RH_RUNNING_NODEVS;
177 uhci->auto_stop_time = jiffies + HZ;
178 }
179 break;
180
181 case UHCI_RH_RUNNING_NODEVS:
182 /* auto-stop if nothing connected for 1 second */
183 if (any_ports_active(uhci))
184 uhci->rh_state = UHCI_RH_RUNNING;
185 else if (time_after_eq(jiffies, uhci->auto_stop_time))
186 suspend_rh(uhci, UHCI_RH_AUTO_STOPPED);
187 break;
188
189 default:
190 break;
191 }
192
193done:
194 spin_unlock_irqrestore(&uhci->lock, flags);
195 return status;
196}
197
145/* size of returned buffer is part of USB spec */ 198/* size of returned buffer is part of USB spec */
146static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, 199static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
147 u16 wIndex, char *buf, u16 wLength) 200 u16 wIndex, char *buf, u16 wLength)
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index f5c75885f7be..77f264851e98 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -32,6 +32,8 @@ static void uhci_free_pending_tds(struct uhci_hcd *uhci);
32 */ 32 */
33static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci) 33static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci)
34{ 34{
35 if (uhci->is_stopped)
36 mod_timer(&uhci->stall_timer, jiffies);
35 uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); 37 uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC);
36} 38}
37 39
@@ -1497,6 +1499,7 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
1497 rescan: 1499 rescan:
1498 uhci->need_rescan = 0; 1500 uhci->need_rescan = 0;
1499 1501
1502 uhci_clear_next_interrupt(uhci);
1500 uhci_get_current_frame_number(uhci); 1503 uhci_get_current_frame_number(uhci);
1501 1504
1502 if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age) 1505 if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age)