diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2005-04-09 17:27:32 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-06-27 17:43:43 -0400 |
commit | c8f4fe4358c5e0a79b4bd47b814d19f1d1d06f21 (patch) | |
tree | 0c12fec97ac524e77489d3e8460bd9bb4817c0d5 /drivers/usb/host/uhci-hcd.c | |
parent | f5946f8220a866dcdb8edc6abe23c1443e252425 (diff) |
[PATCH] USB UHCI: Add root hub states
This patch starts making some serious changes to the UHCI driver.
There's a set of private states for the root hub, and the internal
routines for suspending and resuming work completely differently, with
transitions based on the new states. Now the driver distinguishes
between a privately auto-stopped state and a publicly suspended state,
and it will properly suspend controllers with broken resume-detect
interrupts instead of resetting them.
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 | 285 |
1 files changed, 149 insertions, 136 deletions
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index c17bd7ebc021..57b36dcea5d0 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c | |||
@@ -64,7 +64,7 @@ | |||
64 | /* | 64 | /* |
65 | * Version Information | 65 | * Version Information |
66 | */ | 66 | */ |
67 | #define DRIVER_VERSION "v2.2" | 67 | #define DRIVER_VERSION "v2.3" |
68 | #define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, \ | 68 | #define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, \ |
69 | Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, \ | 69 | Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, \ |
70 | Alan Stern" | 70 | Alan Stern" |
@@ -109,33 +109,6 @@ static inline void restart_timer(struct uhci_hcd *uhci) | |||
109 | #include "uhci-debug.c" | 109 | #include "uhci-debug.c" |
110 | #include "uhci-q.c" | 110 | #include "uhci-q.c" |
111 | 111 | ||
112 | static int suspend_allowed(struct uhci_hcd *uhci) | ||
113 | { | ||
114 | unsigned long io_addr = uhci->io_addr; | ||
115 | int i; | ||
116 | |||
117 | if (to_pci_dev(uhci_dev(uhci))->vendor != PCI_VENDOR_ID_INTEL) | ||
118 | return 1; | ||
119 | |||
120 | /* Some of Intel's USB controllers have a bug that causes false | ||
121 | * resume indications if any port has an over current condition. | ||
122 | * To prevent problems, we will not allow a global suspend if | ||
123 | * any ports are OC. | ||
124 | * | ||
125 | * Some motherboards using Intel's chipsets (but not using all | ||
126 | * the USB ports) appear to hardwire the over current inputs active | ||
127 | * to disable the USB ports. | ||
128 | */ | ||
129 | |||
130 | /* check for over current condition on any port */ | ||
131 | for (i = 0; i < uhci->rh_numports; i++) { | ||
132 | if (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_OC) | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | return 1; | ||
137 | } | ||
138 | |||
139 | static void reset_hc(struct uhci_hcd *uhci) | 112 | static void reset_hc(struct uhci_hcd *uhci) |
140 | { | 113 | { |
141 | unsigned long io_addr = uhci->io_addr; | 114 | unsigned long io_addr = uhci->io_addr; |
@@ -147,7 +120,6 @@ static void reset_hc(struct uhci_hcd *uhci) | |||
147 | outw(0, uhci->io_addr + USBINTR); | 120 | outw(0, uhci->io_addr + USBINTR); |
148 | 121 | ||
149 | /* Global reset for 50ms */ | 122 | /* Global reset for 50ms */ |
150 | uhci->state = UHCI_RESET; | ||
151 | outw(USBCMD_GRESET, io_addr + USBCMD); | 123 | outw(USBCMD_GRESET, io_addr + USBCMD); |
152 | msleep(50); | 124 | msleep(50); |
153 | outw(0, io_addr + USBCMD); | 125 | outw(0, io_addr + USBCMD); |
@@ -156,63 +128,130 @@ static void reset_hc(struct uhci_hcd *uhci) | |||
156 | msleep(10); | 128 | msleep(10); |
157 | uhci->resume_detect = 0; | 129 | uhci->resume_detect = 0; |
158 | uhci->is_stopped = UHCI_IS_STOPPED; | 130 | uhci->is_stopped = UHCI_IS_STOPPED; |
131 | uhci->rh_state = UHCI_RH_RESET; | ||
159 | } | 132 | } |
160 | 133 | ||
161 | static void suspend_hc(struct uhci_hcd *uhci) | 134 | static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) |
162 | { | 135 | { |
163 | unsigned long io_addr = uhci->io_addr; | 136 | int port; |
164 | 137 | ||
165 | dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__); | 138 | switch (to_pci_dev(uhci_dev(uhci))->vendor) { |
166 | uhci->state = UHCI_SUSPENDED; | 139 | default: |
167 | uhci->resume_detect = 0; | 140 | break; |
168 | outw(USBCMD_EGSM, io_addr + USBCMD); | 141 | |
142 | case PCI_VENDOR_ID_GENESYS: | ||
143 | /* Genesys Logic's GL880S controllers don't generate | ||
144 | * resume-detect interrupts. | ||
145 | */ | ||
146 | return 1; | ||
147 | |||
148 | case PCI_VENDOR_ID_INTEL: | ||
149 | /* Some of Intel's USB controllers have a bug that causes | ||
150 | * resume-detect interrupts if any port has an over-current | ||
151 | * condition. To make matters worse, some motherboards | ||
152 | * hardwire unused USB ports' over-current inputs active! | ||
153 | * To prevent problems, we will not enable resume-detect | ||
154 | * interrupts if any ports are OC. | ||
155 | */ | ||
156 | for (port = 0; port < uhci->rh_numports; ++port) { | ||
157 | if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & | ||
158 | USBPORTSC_OC) | ||
159 | return 1; | ||
160 | } | ||
161 | break; | ||
162 | } | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static void suspend_hc(struct uhci_hcd *uhci, enum uhci_rh_state new_state) | ||
167 | __releases(uhci->lock) | ||
168 | __acquires(uhci->lock) | ||
169 | { | ||
170 | int auto_stop; | ||
171 | int int_enable; | ||
172 | |||
173 | auto_stop = (new_state == UHCI_RH_AUTO_STOPPED); | ||
174 | dev_dbg(uhci_dev(uhci), "%s%s\n", __FUNCTION__, | ||
175 | (auto_stop ? " (auto-stop)" : "")); | ||
176 | |||
177 | /* If we get a suspend request when we're already auto-stopped | ||
178 | * then there's nothing to do. | ||
179 | */ | ||
180 | if (uhci->rh_state == UHCI_RH_AUTO_STOPPED) { | ||
181 | uhci->rh_state = new_state; | ||
182 | return; | ||
183 | } | ||
184 | |||
185 | /* Enable resume-detect interrupts if they work. | ||
186 | * Then enter Global Suspend mode, still configured. | ||
187 | */ | ||
188 | int_enable = (resume_detect_interrupts_are_broken(uhci) ? | ||
189 | 0 : USBINTR_RESUME); | ||
190 | outw(int_enable, uhci->io_addr + USBINTR); | ||
191 | outw(USBCMD_EGSM | USBCMD_CF, uhci->io_addr + USBCMD); | ||
192 | udelay(5); | ||
193 | |||
194 | /* If we're auto-stopping then no devices have been attached | ||
195 | * for a while, so there shouldn't be any active URBs and the | ||
196 | * controller should stop after a few microseconds. Otherwise | ||
197 | * we will give the controller one frame to stop. | ||
198 | */ | ||
199 | if (!auto_stop && !(inw(uhci->io_addr + USBSTS) & USBSTS_HCH)) { | ||
200 | uhci->rh_state = UHCI_RH_SUSPENDING; | ||
201 | spin_unlock_irq(&uhci->lock); | ||
202 | msleep(1); | ||
203 | spin_lock_irq(&uhci->lock); | ||
204 | } | ||
205 | if (!(inw(uhci->io_addr + USBSTS) & USBSTS_HCH)) | ||
206 | dev_warn(uhci_dev(uhci), "Controller not stopped yet!\n"); | ||
169 | 207 | ||
170 | /* FIXME: Wait for the controller to actually stop */ | ||
171 | uhci_get_current_frame_number(uhci); | 208 | uhci_get_current_frame_number(uhci); |
209 | smp_wmb(); | ||
210 | |||
211 | uhci->rh_state = new_state; | ||
172 | uhci->is_stopped = UHCI_IS_STOPPED; | 212 | uhci->is_stopped = UHCI_IS_STOPPED; |
213 | uhci->resume_detect = 0; | ||
173 | 214 | ||
174 | uhci_scan_schedule(uhci, NULL); | 215 | uhci_scan_schedule(uhci, NULL); |
175 | } | 216 | } |
176 | 217 | ||
177 | static void wakeup_hc(struct uhci_hcd *uhci) | 218 | static void wakeup_hc(struct uhci_hcd *uhci) |
219 | __releases(uhci->lock) | ||
220 | __acquires(uhci->lock) | ||
178 | { | 221 | { |
179 | unsigned long io_addr = uhci->io_addr; | 222 | dev_dbg(uhci_dev(uhci), "%s%s\n", __FUNCTION__, |
223 | uhci->rh_state == UHCI_RH_AUTO_STOPPED ? | ||
224 | " (auto-start)" : ""); | ||
180 | 225 | ||
181 | switch (uhci->state) { | 226 | /* If we are auto-stopped then no devices are attached so there's |
182 | case UHCI_SUSPENDED: /* Start the resume */ | 227 | * no need for wakeup signals. Otherwise we send Global Resume |
183 | dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__); | 228 | * for 20 ms. |
184 | 229 | */ | |
185 | /* Global resume for >= 20ms */ | 230 | if (uhci->rh_state == UHCI_RH_SUSPENDED) { |
186 | outw(USBCMD_FGR | USBCMD_EGSM, io_addr + USBCMD); | 231 | uhci->rh_state = UHCI_RH_RESUMING; |
187 | uhci->state = UHCI_RESUMING_1; | 232 | outw(USBCMD_FGR | USBCMD_EGSM | USBCMD_CF, |
188 | uhci->state_end = jiffies + msecs_to_jiffies(20); | 233 | uhci->io_addr + USBCMD); |
189 | uhci->is_stopped = 0; | 234 | spin_unlock_irq(&uhci->lock); |
190 | break; | 235 | msleep(20); |
236 | spin_lock_irq(&uhci->lock); | ||
191 | 237 | ||
192 | case UHCI_RESUMING_1: /* End global resume */ | 238 | /* End Global Resume and wait for EOP to be sent */ |
193 | uhci->state = UHCI_RESUMING_2; | 239 | outw(USBCMD_CF, uhci->io_addr + USBCMD); |
194 | outw(0, io_addr + USBCMD); | 240 | udelay(4); |
195 | /* Falls through */ | 241 | if (inw(uhci->io_addr + USBCMD) & USBCMD_FGR) |
196 | 242 | dev_warn(uhci_dev(uhci), "FGR not stopped yet!\n"); | |
197 | case UHCI_RESUMING_2: /* Wait for EOP to be sent */ | 243 | } |
198 | if (inw(io_addr + USBCMD) & USBCMD_FGR) | ||
199 | break; | ||
200 | |||
201 | /* Run for at least 1 second, and | ||
202 | * mark it configured with a 64-byte max packet */ | ||
203 | uhci->state = UHCI_RUNNING_GRACE; | ||
204 | uhci->state_end = jiffies + HZ; | ||
205 | outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, | ||
206 | io_addr + USBCMD); | ||
207 | break; | ||
208 | 244 | ||
209 | case UHCI_RUNNING_GRACE: /* Now allowed to suspend */ | 245 | uhci->rh_state = UHCI_RH_RUNNING; |
210 | uhci->state = UHCI_RUNNING; | 246 | uhci->is_stopped = 0; |
211 | break; | 247 | smp_wmb(); |
212 | 248 | ||
213 | default: | 249 | /* Mark it configured and running with a 64-byte max packet. |
214 | break; | 250 | * All interrupts are enabled, even though RD won't do anything. |
215 | } | 251 | */ |
252 | outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, uhci->io_addr + USBCMD); | ||
253 | outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, | ||
254 | uhci->io_addr + USBINTR); | ||
216 | } | 255 | } |
217 | 256 | ||
218 | static int start_hc(struct uhci_hcd *uhci) | 257 | static int start_hc(struct uhci_hcd *uhci) |
@@ -249,49 +288,40 @@ static int start_hc(struct uhci_hcd *uhci) | |||
249 | outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD); | 288 | outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD); |
250 | 289 | ||
251 | /* Run and mark it configured with a 64-byte max packet */ | 290 | /* Run and mark it configured with a 64-byte max packet */ |
252 | uhci->state = UHCI_RUNNING_GRACE; | 291 | uhci->rh_state = UHCI_RH_RUNNING; |
253 | uhci->state_end = jiffies + HZ; | ||
254 | outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); | 292 | outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); |
255 | uhci->is_stopped = 0; | 293 | uhci->is_stopped = 0; |
256 | 294 | ||
257 | return 0; | 295 | return 0; |
258 | } | 296 | } |
259 | 297 | ||
260 | static void hc_state_transitions(struct uhci_hcd *uhci) | 298 | static void rh_state_transitions(struct uhci_hcd *uhci) |
261 | { | 299 | { |
262 | switch (uhci->state) { | 300 | switch (uhci->rh_state) { |
263 | case UHCI_RUNNING: | 301 | case UHCI_RH_RUNNING: |
264 | 302 | /* are any devices attached? */ | |
265 | /* global suspend if nothing connected for 1 second */ | 303 | if (!any_ports_active(uhci)) { |
266 | if (!any_ports_active(uhci) && suspend_allowed(uhci)) { | 304 | uhci->rh_state = UHCI_RH_RUNNING_NODEVS; |
267 | uhci->state = UHCI_SUSPENDING_GRACE; | 305 | uhci->auto_stop_time = jiffies + HZ; |
268 | uhci->state_end = jiffies + HZ; | 306 | } |
269 | } | 307 | break; |
270 | break; | 308 | |
271 | 309 | case UHCI_RH_RUNNING_NODEVS: | |
272 | case UHCI_SUSPENDING_GRACE: | 310 | /* auto-stop if nothing connected for 1 second */ |
273 | if (any_ports_active(uhci)) | 311 | if (any_ports_active(uhci)) |
274 | uhci->state = UHCI_RUNNING; | 312 | uhci->rh_state = UHCI_RH_RUNNING; |
275 | else if (time_after_eq(jiffies, uhci->state_end)) | 313 | else if (time_after_eq(jiffies, uhci->auto_stop_time)) |
276 | suspend_hc(uhci); | 314 | suspend_hc(uhci, UHCI_RH_AUTO_STOPPED); |
277 | break; | 315 | break; |
278 | 316 | ||
279 | case UHCI_SUSPENDED: | 317 | case UHCI_RH_AUTO_STOPPED: |
280 | 318 | /* wakeup if requested by a device */ | |
281 | /* wakeup if requested by a device */ | 319 | if (uhci->resume_detect) |
282 | if (uhci->resume_detect) | 320 | wakeup_hc(uhci); |
283 | wakeup_hc(uhci); | 321 | break; |
284 | break; | 322 | |
285 | 323 | default: | |
286 | case UHCI_RESUMING_1: | 324 | break; |
287 | case UHCI_RESUMING_2: | ||
288 | case UHCI_RUNNING_GRACE: | ||
289 | if (time_after_eq(jiffies, uhci->state_end)) | ||
290 | wakeup_hc(uhci); | ||
291 | break; | ||
292 | |||
293 | default: | ||
294 | break; | ||
295 | } | 325 | } |
296 | } | 326 | } |
297 | 327 | ||
@@ -305,8 +335,8 @@ static void stall_callback(unsigned long _uhci) | |||
305 | check_fsbr(uhci); | 335 | check_fsbr(uhci); |
306 | 336 | ||
307 | /* Poll for and perform state transitions */ | 337 | /* Poll for and perform state transitions */ |
308 | hc_state_transitions(uhci); | 338 | rh_state_transitions(uhci); |
309 | if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED)) | 339 | if (unlikely(uhci->suspended_ports)) |
310 | uhci_check_ports(uhci); | 340 | uhci_check_ports(uhci); |
311 | 341 | ||
312 | restart_timer(uhci); | 342 | restart_timer(uhci); |
@@ -336,7 +366,8 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) | |||
336 | if (status & USBSTS_HCPE) | 366 | if (status & USBSTS_HCPE) |
337 | dev_err(uhci_dev(uhci), "host controller process " | 367 | dev_err(uhci_dev(uhci), "host controller process " |
338 | "error, something bad happened!\n"); | 368 | "error, something bad happened!\n"); |
339 | if ((status & USBSTS_HCH) && uhci->state > 0) { | 369 | if ((status & USBSTS_HCH) && |
370 | uhci->rh_state >= UHCI_RH_RUNNING) { | ||
340 | dev_err(uhci_dev(uhci), "host controller halted, " | 371 | dev_err(uhci_dev(uhci), "host controller halted, " |
341 | "very bad!\n"); | 372 | "very bad!\n"); |
342 | /* FIXME: Reset the controller, fix the offending TD */ | 373 | /* FIXME: Reset the controller, fix the offending TD */ |
@@ -683,17 +714,7 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message) | |||
683 | struct uhci_hcd *uhci = hcd_to_uhci(hcd); | 714 | struct uhci_hcd *uhci = hcd_to_uhci(hcd); |
684 | 715 | ||
685 | spin_lock_irq(&uhci->lock); | 716 | spin_lock_irq(&uhci->lock); |
686 | 717 | suspend_hc(uhci, UHCI_RH_SUSPENDED); | |
687 | /* Don't try to suspend broken motherboards, reset instead */ | ||
688 | if (suspend_allowed(uhci)) | ||
689 | suspend_hc(uhci); | ||
690 | else { | ||
691 | spin_unlock_irq(&uhci->lock); | ||
692 | reset_hc(uhci); | ||
693 | spin_lock_irq(&uhci->lock); | ||
694 | uhci_scan_schedule(uhci, NULL); | ||
695 | } | ||
696 | |||
697 | spin_unlock_irq(&uhci->lock); | 718 | spin_unlock_irq(&uhci->lock); |
698 | return 0; | 719 | return 0; |
699 | } | 720 | } |
@@ -701,13 +722,9 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message) | |||
701 | static int uhci_resume(struct usb_hcd *hcd) | 722 | static int uhci_resume(struct usb_hcd *hcd) |
702 | { | 723 | { |
703 | struct uhci_hcd *uhci = hcd_to_uhci(hcd); | 724 | struct uhci_hcd *uhci = hcd_to_uhci(hcd); |
704 | int rc; | ||
705 | |||
706 | pci_set_master(to_pci_dev(uhci_dev(uhci))); | ||
707 | 725 | ||
708 | spin_lock_irq(&uhci->lock); | 726 | spin_lock_irq(&uhci->lock); |
709 | 727 | if (uhci->rh_state == UHCI_RH_SUSPENDED) { | |
710 | if (uhci->state == UHCI_SUSPENDED) { | ||
711 | 728 | ||
712 | /* | 729 | /* |
713 | * Some systems don't maintain the UHCI register values | 730 | * Some systems don't maintain the UHCI register values |
@@ -721,19 +738,13 @@ static int uhci_resume(struct usb_hcd *hcd) | |||
721 | outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD); | 738 | outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD); |
722 | outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | | 739 | outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | |
723 | USBINTR_SP, uhci->io_addr + USBINTR); | 740 | USBINTR_SP, uhci->io_addr + USBINTR); |
724 | uhci->resume_detect = 1; | ||
725 | pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, | 741 | pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, |
726 | USBLEGSUP_DEFAULT); | 742 | USBLEGSUP_DEFAULT); |
727 | } else { | 743 | wakeup_hc(uhci); |
728 | spin_unlock_irq(&uhci->lock); | ||
729 | reset_hc(uhci); | ||
730 | if ((rc = start_hc(uhci)) != 0) | ||
731 | return rc; | ||
732 | spin_lock_irq(&uhci->lock); | ||
733 | } | 744 | } |
734 | hcd->state = HC_STATE_RUNNING; | ||
735 | |||
736 | spin_unlock_irq(&uhci->lock); | 745 | spin_unlock_irq(&uhci->lock); |
746 | |||
747 | hcd->state = HC_STATE_RUNNING; | ||
737 | return 0; | 748 | return 0; |
738 | } | 749 | } |
739 | #endif | 750 | #endif |
@@ -750,13 +761,15 @@ static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd, | |||
750 | static int uhci_hcd_get_frame_number(struct usb_hcd *hcd) | 761 | static int uhci_hcd_get_frame_number(struct usb_hcd *hcd) |
751 | { | 762 | { |
752 | struct uhci_hcd *uhci = hcd_to_uhci(hcd); | 763 | struct uhci_hcd *uhci = hcd_to_uhci(hcd); |
753 | int frame_number; | ||
754 | unsigned long flags; | 764 | unsigned long flags; |
765 | int is_stopped; | ||
766 | int frame_number; | ||
755 | 767 | ||
756 | /* Minimize latency by avoiding the spinlock */ | 768 | /* Minimize latency by avoiding the spinlock */ |
757 | local_irq_save(flags); | 769 | local_irq_save(flags); |
758 | rmb(); | 770 | is_stopped = uhci->is_stopped; |
759 | frame_number = (uhci->is_stopped ? uhci->frame_number : | 771 | smp_rmb(); |
772 | frame_number = (is_stopped ? uhci->frame_number : | ||
760 | inw(uhci->io_addr + USBFRNUM)); | 773 | inw(uhci->io_addr + USBFRNUM)); |
761 | local_irq_restore(flags); | 774 | local_irq_restore(flags); |
762 | return frame_number; | 775 | return frame_number; |