diff options
Diffstat (limited to 'drivers/usb/host')
| -rw-r--r-- | drivers/usb/host/uhci-debug.c | 32 | ||||
| -rw-r--r-- | drivers/usb/host/uhci-hcd.c | 285 | ||||
| -rw-r--r-- | drivers/usb/host/uhci-hcd.h | 49 | ||||
| -rw-r--r-- | drivers/usb/host/uhci-hub.c | 2 |
4 files changed, 212 insertions, 156 deletions
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 24c73c5a3435..4538a98b6f9d 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c | |||
| @@ -237,6 +237,37 @@ static int uhci_show_sc(int port, unsigned short status, char *buf, int len) | |||
| 237 | return out - buf; | 237 | return out - buf; |
| 238 | } | 238 | } |
| 239 | 239 | ||
| 240 | static int uhci_show_root_hub_state(struct uhci_hcd *uhci, char *buf, int len) | ||
| 241 | { | ||
| 242 | char *out = buf; | ||
| 243 | char *rh_state; | ||
| 244 | |||
| 245 | /* Try to make sure there's enough memory */ | ||
| 246 | if (len < 60) | ||
| 247 | return 0; | ||
| 248 | |||
| 249 | switch (uhci->rh_state) { | ||
| 250 | case UHCI_RH_RESET: | ||
| 251 | rh_state = "reset"; break; | ||
| 252 | case UHCI_RH_SUSPENDED: | ||
| 253 | rh_state = "suspended"; break; | ||
| 254 | case UHCI_RH_AUTO_STOPPED: | ||
| 255 | rh_state = "auto-stopped"; break; | ||
| 256 | case UHCI_RH_RESUMING: | ||
| 257 | rh_state = "resuming"; break; | ||
| 258 | case UHCI_RH_SUSPENDING: | ||
| 259 | rh_state = "suspending"; break; | ||
| 260 | case UHCI_RH_RUNNING: | ||
| 261 | rh_state = "running"; break; | ||
| 262 | case UHCI_RH_RUNNING_NODEVS: | ||
| 263 | rh_state = "running, no devs"; break; | ||
| 264 | default: | ||
| 265 | rh_state = "?"; break; | ||
| 266 | } | ||
| 267 | out += sprintf(out, "Root-hub state: %s\n", rh_state); | ||
| 268 | return out - buf; | ||
| 269 | } | ||
| 270 | |||
| 240 | static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len) | 271 | static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len) |
| 241 | { | 272 | { |
| 242 | char *out = buf; | 273 | char *out = buf; |
| @@ -408,6 +439,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) | |||
| 408 | 439 | ||
| 409 | spin_lock_irqsave(&uhci->lock, flags); | 440 | spin_lock_irqsave(&uhci->lock, flags); |
| 410 | 441 | ||
| 442 | out += uhci_show_root_hub_state(uhci, out, len - (out - buf)); | ||
| 411 | out += sprintf(out, "HC status\n"); | 443 | out += sprintf(out, "HC status\n"); |
| 412 | out += uhci_show_status(uhci, out, len - (out - buf)); | 444 | out += uhci_show_status(uhci, out, len - (out - buf)); |
| 413 | 445 | ||
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; |
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 02255d69e1fe..4bac57c74ec2 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h | |||
| @@ -314,26 +314,29 @@ static inline int __interval_to_skel(int interval) | |||
| 314 | } | 314 | } |
| 315 | 315 | ||
| 316 | /* | 316 | /* |
| 317 | * Device states for the host controller. | 317 | * States for the root hub. |
| 318 | * | 318 | * |
| 319 | * To prevent "bouncing" in the presence of electrical noise, | 319 | * To prevent "bouncing" in the presence of electrical noise, |
| 320 | * we insist on a 1-second "grace" period, before switching to | 320 | * when there are no devices attached we delay for 1 second in the |
| 321 | * the RUNNING or SUSPENDED states, during which the state is | 321 | * RUNNING_NODEVS state before switching to the AUTO_STOPPED state. |
| 322 | * not allowed to change. | 322 | * |
| 323 | * | 323 | * (Note that the AUTO_STOPPED state won't be necessary once the hub |
| 324 | * The resume process is divided into substates in order to avoid | 324 | * driver learns to autosuspend.) |
| 325 | * potentially length delays during the timer handler. | ||
| 326 | * | ||
| 327 | * States in which the host controller is halted must have values <= 0. | ||
| 328 | */ | 325 | */ |
| 329 | enum uhci_state { | 326 | enum uhci_rh_state { |
| 330 | UHCI_RESET, | 327 | /* In the next 4 states the HC must be halted */ |
| 331 | UHCI_RUNNING_GRACE, /* Before RUNNING */ | 328 | UHCI_RH_RESET, |
| 332 | UHCI_RUNNING, /* The normal state */ | 329 | UHCI_RH_SUSPENDED, |
| 333 | UHCI_SUSPENDING_GRACE, /* Before SUSPENDED */ | 330 | UHCI_RH_AUTO_STOPPED, |
| 334 | UHCI_SUSPENDED = -10, /* When no devices are attached */ | 331 | UHCI_RH_RESUMING, |
| 335 | UHCI_RESUMING_1, | 332 | |
| 336 | UHCI_RESUMING_2 | 333 | /* In the next state the HC changes from running to halted, so it |
| 334 | * can legally appear either way */ | ||
| 335 | UHCI_RH_SUSPENDING, | ||
| 336 | |||
| 337 | /* In the next two states it's an error if the HC is halted */ | ||
| 338 | UHCI_RH_RUNNING, /* The normal state */ | ||
| 339 | UHCI_RH_RUNNING_NODEVS, /* Running with no devices attached */ | ||
| 337 | }; | 340 | }; |
| 338 | 341 | ||
| 339 | /* | 342 | /* |
| @@ -363,8 +366,9 @@ struct uhci_hcd { | |||
| 363 | int fsbr; /* Full-speed bandwidth reclamation */ | 366 | int fsbr; /* Full-speed bandwidth reclamation */ |
| 364 | unsigned long fsbrtimeout; /* FSBR delay */ | 367 | unsigned long fsbrtimeout; /* FSBR delay */ |
| 365 | 368 | ||
| 366 | enum uhci_state state; /* FIXME: needs a spinlock */ | 369 | enum uhci_rh_state rh_state; |
| 367 | unsigned long state_end; /* Time of next transition */ | 370 | unsigned long auto_stop_time; /* When to AUTO_STOP */ |
| 371 | |||
| 368 | unsigned int frame_number; /* As of last check */ | 372 | unsigned int frame_number; /* As of last check */ |
| 369 | unsigned int is_stopped; | 373 | unsigned int is_stopped; |
| 370 | #define UHCI_IS_STOPPED 9999 /* Larger than a frame # */ | 374 | #define UHCI_IS_STOPPED 9999 /* Larger than a frame # */ |
| @@ -451,4 +455,11 @@ struct urb_priv { | |||
| 451 | * #2 urb->lock | 455 | * #2 urb->lock |
| 452 | */ | 456 | */ |
| 453 | 457 | ||
| 458 | |||
| 459 | /* Some special IDs */ | ||
| 460 | |||
| 461 | #define PCI_VENDOR_ID_GENESYS 0x17a0 | ||
| 462 | #define PCI_DEVICE_ID_GL880S_UHCI 0x8083 | ||
| 463 | #define PCI_DEVICE_ID_GL880S_EHCI 0x8084 | ||
| 464 | |||
| 454 | #endif | 465 | #endif |
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index ddb19cbf4b75..fc34fee2ab07 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c | |||
| @@ -60,7 +60,7 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) | |||
| 60 | test_bit(port, &uhci->port_c_suspend)) | 60 | test_bit(port, &uhci->port_c_suspend)) |
| 61 | *buf |= (1 << (port + 1)); | 61 | *buf |= (1 << (port + 1)); |
| 62 | } | 62 | } |
| 63 | if (*buf && uhci->state == UHCI_SUSPENDED) | 63 | if (*buf && uhci->is_stopped) |
| 64 | uhci->resume_detect = 1; | 64 | uhci->resume_detect = 1; |
| 65 | return !!*buf; | 65 | return !!*buf; |
| 66 | } | 66 | } |
