aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/uhci-hub.c
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 /drivers/usb/host/uhci-hub.c
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>
Diffstat (limited to 'drivers/usb/host/uhci-hub.c')
-rw-r--r--drivers/usb/host/uhci-hub.c67
1 files changed, 60 insertions, 7 deletions
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
index 13652de5220..4eace2b19dd 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)