diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2012-07-11 11:23:10 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-07-16 19:56:48 -0400 |
commit | c4f3476436f7452b97c8accb5dd7d53219a11a3f (patch) | |
tree | 95711a24bb4357dab53d4641aae5a09292974fb8 /drivers/usb/host/ehci-hcd.c | |
parent | f42890782241a60d107f23d08089a4a12b507a11 (diff) |
USB: EHCI: fix up locking
This patch (as1588) adjusts the locking in ehci-hcd's various halt,
shutdown, and suspend/resume pathways. We want to hold the spinlock
while writing device registers and accessing shared variables, but not
while polling in a loop.
In addition, there's no need to call ehci_work() at times when no URBs
can be active, i.e., in ehci_stop() and ehci_bus_suspend().
Finally, ehci_adjust_port_wakeup_flags() is called only in situations
where interrupts are enabled; therefore it can use spin_lock_irq
rather than spin_lock_irqsave.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host/ehci-hcd.c')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 48 |
1 files changed, 33 insertions, 15 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 340c9c4894bf..ac4c8ddde20a 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -167,21 +167,24 @@ static int tdi_in_host_mode (struct ehci_hcd *ehci) | |||
167 | return (tmp & 3) == USBMODE_CM_HC; | 167 | return (tmp & 3) == USBMODE_CM_HC; |
168 | } | 168 | } |
169 | 169 | ||
170 | /* force HC to halt state from unknown (EHCI spec section 2.3) */ | 170 | /* |
171 | * Force HC to halt state from unknown (EHCI spec section 2.3). | ||
172 | * Must be called with interrupts enabled and the lock not held. | ||
173 | */ | ||
171 | static int ehci_halt (struct ehci_hcd *ehci) | 174 | static int ehci_halt (struct ehci_hcd *ehci) |
172 | { | 175 | { |
173 | u32 temp = ehci_readl(ehci, &ehci->regs->status); | 176 | u32 temp; |
177 | |||
178 | spin_lock_irq(&ehci->lock); | ||
174 | 179 | ||
175 | /* disable any irqs left enabled by previous code */ | 180 | /* disable any irqs left enabled by previous code */ |
176 | ehci_writel(ehci, 0, &ehci->regs->intr_enable); | 181 | ehci_writel(ehci, 0, &ehci->regs->intr_enable); |
177 | 182 | ||
178 | if (ehci_is_TDI(ehci) && tdi_in_host_mode(ehci) == 0) { | 183 | if (ehci_is_TDI(ehci) && !tdi_in_host_mode(ehci)) { |
184 | spin_unlock_irq(&ehci->lock); | ||
179 | return 0; | 185 | return 0; |
180 | } | 186 | } |
181 | 187 | ||
182 | if ((temp & STS_HALT) != 0) | ||
183 | return 0; | ||
184 | |||
185 | /* | 188 | /* |
186 | * This routine gets called during probe before ehci->command | 189 | * This routine gets called during probe before ehci->command |
187 | * has been initialized, so we can't rely on its value. | 190 | * has been initialized, so we can't rely on its value. |
@@ -190,7 +193,11 @@ static int ehci_halt (struct ehci_hcd *ehci) | |||
190 | temp = ehci_readl(ehci, &ehci->regs->command); | 193 | temp = ehci_readl(ehci, &ehci->regs->command); |
191 | temp &= ~(CMD_RUN | CMD_IAAD); | 194 | temp &= ~(CMD_RUN | CMD_IAAD); |
192 | ehci_writel(ehci, temp, &ehci->regs->command); | 195 | ehci_writel(ehci, temp, &ehci->regs->command); |
193 | return handshake (ehci, &ehci->regs->status, | 196 | |
197 | spin_unlock_irq(&ehci->lock); | ||
198 | synchronize_irq(ehci_to_hcd(ehci)->irq); | ||
199 | |||
200 | return handshake(ehci, &ehci->regs->status, | ||
194 | STS_HALT, STS_HALT, 16 * 125); | 201 | STS_HALT, STS_HALT, 16 * 125); |
195 | } | 202 | } |
196 | 203 | ||
@@ -210,7 +217,10 @@ static void tdi_reset (struct ehci_hcd *ehci) | |||
210 | ehci_writel(ehci, tmp, &ehci->regs->usbmode); | 217 | ehci_writel(ehci, tmp, &ehci->regs->usbmode); |
211 | } | 218 | } |
212 | 219 | ||
213 | /* reset a non-running (STS_HALT == 1) controller */ | 220 | /* |
221 | * Reset a non-running (STS_HALT == 1) controller. | ||
222 | * Must be called with interrupts enabled and the lock not held. | ||
223 | */ | ||
214 | static int ehci_reset (struct ehci_hcd *ehci) | 224 | static int ehci_reset (struct ehci_hcd *ehci) |
215 | { | 225 | { |
216 | int retval; | 226 | int retval; |
@@ -248,7 +258,10 @@ static int ehci_reset (struct ehci_hcd *ehci) | |||
248 | return retval; | 258 | return retval; |
249 | } | 259 | } |
250 | 260 | ||
251 | /* idle the controller (from running) */ | 261 | /* |
262 | * Idle the controller (turn off the schedules). | ||
263 | * Must be called with interrupts enabled and the lock not held. | ||
264 | */ | ||
252 | static void ehci_quiesce (struct ehci_hcd *ehci) | 265 | static void ehci_quiesce (struct ehci_hcd *ehci) |
253 | { | 266 | { |
254 | u32 temp; | 267 | u32 temp; |
@@ -261,8 +274,10 @@ static void ehci_quiesce (struct ehci_hcd *ehci) | |||
261 | handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, temp, 16 * 125); | 274 | handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, temp, 16 * 125); |
262 | 275 | ||
263 | /* then disable anything that's still active */ | 276 | /* then disable anything that's still active */ |
277 | spin_lock_irq(&ehci->lock); | ||
264 | ehci->command &= ~(CMD_ASE | CMD_PSE); | 278 | ehci->command &= ~(CMD_ASE | CMD_PSE); |
265 | ehci_writel(ehci, ehci->command, &ehci->regs->command); | 279 | ehci_writel(ehci, ehci->command, &ehci->regs->command); |
280 | spin_unlock_irq(&ehci->lock); | ||
266 | 281 | ||
267 | /* hardware can take 16 microframes to turn off ... */ | 282 | /* hardware can take 16 microframes to turn off ... */ |
268 | handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, 0, 16 * 125); | 283 | handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, 0, 16 * 125); |
@@ -301,11 +316,14 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci) | |||
301 | 316 | ||
302 | /* | 317 | /* |
303 | * Halt HC, turn off all ports, and let the BIOS use the companion controllers. | 318 | * Halt HC, turn off all ports, and let the BIOS use the companion controllers. |
304 | * Should be called with ehci->lock held. | 319 | * Must be called with interrupts enabled and the lock not held. |
305 | */ | 320 | */ |
306 | static void ehci_silence_controller(struct ehci_hcd *ehci) | 321 | static void ehci_silence_controller(struct ehci_hcd *ehci) |
307 | { | 322 | { |
308 | ehci_halt(ehci); | 323 | ehci_halt(ehci); |
324 | |||
325 | spin_lock_irq(&ehci->lock); | ||
326 | ehci->rh_state = EHCI_RH_HALTED; | ||
309 | ehci_turn_off_all_ports(ehci); | 327 | ehci_turn_off_all_ports(ehci); |
310 | 328 | ||
311 | /* make BIOS/etc use companion controller during reboot */ | 329 | /* make BIOS/etc use companion controller during reboot */ |
@@ -313,6 +331,7 @@ static void ehci_silence_controller(struct ehci_hcd *ehci) | |||
313 | 331 | ||
314 | /* unblock posted writes */ | 332 | /* unblock posted writes */ |
315 | ehci_readl(ehci, &ehci->regs->configured_flag); | 333 | ehci_readl(ehci, &ehci->regs->configured_flag); |
334 | spin_unlock_irq(&ehci->lock); | ||
316 | } | 335 | } |
317 | 336 | ||
318 | /* ehci_shutdown kick in for silicon on any bus (not just pci, etc). | 337 | /* ehci_shutdown kick in for silicon on any bus (not just pci, etc). |
@@ -325,10 +344,11 @@ static void ehci_shutdown(struct usb_hcd *hcd) | |||
325 | 344 | ||
326 | spin_lock_irq(&ehci->lock); | 345 | spin_lock_irq(&ehci->lock); |
327 | ehci->rh_state = EHCI_RH_STOPPING; | 346 | ehci->rh_state = EHCI_RH_STOPPING; |
328 | ehci_silence_controller(ehci); | ||
329 | ehci->enabled_hrtimer_events = 0; | 347 | ehci->enabled_hrtimer_events = 0; |
330 | spin_unlock_irq(&ehci->lock); | 348 | spin_unlock_irq(&ehci->lock); |
331 | 349 | ||
350 | ehci_silence_controller(ehci); | ||
351 | |||
332 | hrtimer_cancel(&ehci->hrtimer); | 352 | hrtimer_cancel(&ehci->hrtimer); |
333 | } | 353 | } |
334 | 354 | ||
@@ -400,11 +420,11 @@ static void ehci_stop (struct usb_hcd *hcd) | |||
400 | 420 | ||
401 | spin_lock_irq(&ehci->lock); | 421 | spin_lock_irq(&ehci->lock); |
402 | ehci->enabled_hrtimer_events = 0; | 422 | ehci->enabled_hrtimer_events = 0; |
403 | ehci_quiesce(ehci); | 423 | spin_unlock_irq(&ehci->lock); |
404 | 424 | ||
425 | ehci_quiesce(ehci); | ||
405 | ehci_silence_controller(ehci); | 426 | ehci_silence_controller(ehci); |
406 | ehci_reset (ehci); | 427 | ehci_reset (ehci); |
407 | spin_unlock_irq(&ehci->lock); | ||
408 | 428 | ||
409 | hrtimer_cancel(&ehci->hrtimer); | 429 | hrtimer_cancel(&ehci->hrtimer); |
410 | remove_sysfs_files(ehci); | 430 | remove_sysfs_files(ehci); |
@@ -412,8 +432,6 @@ static void ehci_stop (struct usb_hcd *hcd) | |||
412 | 432 | ||
413 | /* root hub is shut down separately (first, when possible) */ | 433 | /* root hub is shut down separately (first, when possible) */ |
414 | spin_lock_irq (&ehci->lock); | 434 | spin_lock_irq (&ehci->lock); |
415 | if (ehci->async) | ||
416 | ehci_work (ehci); | ||
417 | end_free_itds(ehci); | 435 | end_free_itds(ehci); |
418 | spin_unlock_irq (&ehci->lock); | 436 | spin_unlock_irq (&ehci->lock); |
419 | ehci_mem_cleanup (ehci); | 437 | ehci_mem_cleanup (ehci); |