diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2008-04-03 18:03:17 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-04-25 00:16:48 -0400 |
commit | 43bbb7e015c4380064796c5868b536437b165615 (patch) | |
tree | 6f4b8e184904917677ce00f600ebf3c8839f47f0 /drivers | |
parent | 7be7d7418776a41badce7ca00246e270d408e4b9 (diff) |
USB: OHCI: host-controller resumes leave root hub suspended
Drivers in the ohci-hcd family should perform certain tasks whenever
their controller device is resumed. These include checking for loss
of power during suspend, turning on port power, and enabling interrupt
requests.
Until now these jobs have been carried out when the root hub is
resumed, not when the controller is. Many drivers work around the
resulting awkwardness by automatically resuming their root hub
whenever the controller is resumed. But this is wasteful and
unnecessary.
To simplify the situation, this patch (as1066) adds a new core
routine, ohci_finish_controller_resume(), which can be used by all the
OHCI-variant drivers. They can call the new routine instead of
resuming their root hubs. And ohci-pci.c can call it instead of using
its own special-purpose handler.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/host/ohci-at91.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ohci-ep93xx.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hub.c | 43 | ||||
-rw-r--r-- | drivers/usb/host/ohci-omap.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/ohci-pci.c | 43 | ||||
-rw-r--r-- | drivers/usb/host/ohci-pxa27x.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/ohci-sm501.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/ohci-ssb.c | 1 |
8 files changed, 54 insertions, 49 deletions
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index d72dc07dda01..e534f9de0f05 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c | |||
@@ -348,6 +348,7 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev) | |||
348 | if (!clocked) | 348 | if (!clocked) |
349 | at91_start_clock(); | 349 | at91_start_clock(); |
350 | 350 | ||
351 | ohci_finish_controller_resume(hcd); | ||
351 | return 0; | 352 | return 0; |
352 | } | 353 | } |
353 | #else | 354 | #else |
diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c index 40c683f8987d..5adaf36e47d0 100644 --- a/drivers/usb/host/ohci-ep93xx.c +++ b/drivers/usb/host/ohci-ep93xx.c | |||
@@ -192,8 +192,8 @@ static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev) | |||
192 | ohci->next_statechange = jiffies; | 192 | ohci->next_statechange = jiffies; |
193 | 193 | ||
194 | ep93xx_start_hc(&pdev->dev); | 194 | ep93xx_start_hc(&pdev->dev); |
195 | usb_hcd_resume_root_hub(hcd); | ||
196 | 195 | ||
196 | ohci_finish_controller_resume(hcd); | ||
197 | return 0; | 197 | return 0; |
198 | } | 198 | } |
199 | #endif | 199 | #endif |
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index c638e6b33c43..28d6d775eb5f 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c | |||
@@ -326,6 +326,49 @@ static int ohci_bus_resume (struct usb_hcd *hcd) | |||
326 | return rc; | 326 | return rc; |
327 | } | 327 | } |
328 | 328 | ||
329 | /* Carry out the final steps of resuming the controller device */ | ||
330 | static void ohci_finish_controller_resume(struct usb_hcd *hcd) | ||
331 | { | ||
332 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
333 | int port; | ||
334 | bool need_reinit = false; | ||
335 | |||
336 | /* See if the controller is already running or has been reset */ | ||
337 | ohci->hc_control = ohci_readl(ohci, &ohci->regs->control); | ||
338 | if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { | ||
339 | need_reinit = true; | ||
340 | } else { | ||
341 | switch (ohci->hc_control & OHCI_CTRL_HCFS) { | ||
342 | case OHCI_USB_OPER: | ||
343 | case OHCI_USB_RESET: | ||
344 | need_reinit = true; | ||
345 | } | ||
346 | } | ||
347 | |||
348 | /* If needed, reinitialize and suspend the root hub */ | ||
349 | if (need_reinit) { | ||
350 | spin_lock_irq(&ohci->lock); | ||
351 | hcd->state = HC_STATE_RESUMING; | ||
352 | ohci_rh_resume(ohci); | ||
353 | hcd->state = HC_STATE_QUIESCING; | ||
354 | ohci_rh_suspend(ohci, 0); | ||
355 | hcd->state = HC_STATE_SUSPENDED; | ||
356 | spin_unlock_irq(&ohci->lock); | ||
357 | } | ||
358 | |||
359 | /* Normally just turn on port power and enable interrupts */ | ||
360 | else { | ||
361 | ohci_dbg(ohci, "powerup ports\n"); | ||
362 | for (port = 0; port < ohci->num_ports; port++) | ||
363 | ohci_writel(ohci, RH_PS_PPS, | ||
364 | &ohci->regs->roothub.portstatus[port]); | ||
365 | |||
366 | ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrenable); | ||
367 | ohci_readl(ohci, &ohci->regs->intrenable); | ||
368 | msleep(20); | ||
369 | } | ||
370 | } | ||
371 | |||
329 | /* Carry out polling-, autostop-, and autoresume-related state changes */ | 372 | /* Carry out polling-, autostop-, and autoresume-related state changes */ |
330 | static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, | 373 | static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, |
331 | int any_connected) | 374 | int any_connected) |
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 2aafa7b6c81f..3a7c24c03671 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c | |||
@@ -510,14 +510,15 @@ static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message) | |||
510 | 510 | ||
511 | static int ohci_omap_resume(struct platform_device *dev) | 511 | static int ohci_omap_resume(struct platform_device *dev) |
512 | { | 512 | { |
513 | struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(dev)); | 513 | struct usb_hcd *hcd = platform_get_drvdata(dev); |
514 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
514 | 515 | ||
515 | if (time_before(jiffies, ohci->next_statechange)) | 516 | if (time_before(jiffies, ohci->next_statechange)) |
516 | msleep(5); | 517 | msleep(5); |
517 | ohci->next_statechange = jiffies; | 518 | ohci->next_statechange = jiffies; |
518 | 519 | ||
519 | omap_ohci_clock_power(1); | 520 | omap_ohci_clock_power(1); |
520 | usb_hcd_resume_root_hub(platform_get_drvdata(dev)); | 521 | ohci_finish_controller_resume(hcd); |
521 | return 0; | 522 | return 0; |
522 | } | 523 | } |
523 | 524 | ||
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 40b62a35fd3c..4696cc912e16 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c | |||
@@ -238,42 +238,6 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd) | |||
238 | return ret; | 238 | return ret; |
239 | } | 239 | } |
240 | 240 | ||
241 | #if defined(CONFIG_USB_PERSIST) && (defined(CONFIG_USB_EHCI_HCD) || \ | ||
242 | defined(CONFIG_USB_EHCI_HCD_MODULE)) | ||
243 | |||
244 | /* Following a power loss, we must prepare to regain control of the ports | ||
245 | * we used to own. This means turning on the port power before ehci-hcd | ||
246 | * tries to switch ownership. | ||
247 | * | ||
248 | * This isn't a 100% perfect solution. On most systems the OHCI controllers | ||
249 | * lie at lower PCI addresses than the EHCI controller, so they will be | ||
250 | * discovered (and hence resumed) first. But there is no guarantee things | ||
251 | * will always work this way. If the EHCI controller is resumed first and | ||
252 | * the OHCI ports are unpowered, then the handover will fail. | ||
253 | */ | ||
254 | static void prepare_for_handover(struct usb_hcd *hcd) | ||
255 | { | ||
256 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
257 | int port; | ||
258 | |||
259 | /* Here we "know" root ports should always stay powered */ | ||
260 | ohci_dbg(ohci, "powerup ports\n"); | ||
261 | for (port = 0; port < ohci->num_ports; port++) | ||
262 | ohci_writel(ohci, RH_PS_PPS, | ||
263 | &ohci->regs->roothub.portstatus[port]); | ||
264 | |||
265 | /* Flush those writes */ | ||
266 | ohci_readl(ohci, &ohci->regs->control); | ||
267 | msleep(20); | ||
268 | } | ||
269 | |||
270 | #else | ||
271 | |||
272 | static inline void prepare_for_handover(struct usb_hcd *hcd) | ||
273 | { } | ||
274 | |||
275 | #endif /* CONFIG_USB_PERSIST etc. */ | ||
276 | |||
277 | #ifdef CONFIG_PM | 241 | #ifdef CONFIG_PM |
278 | 242 | ||
279 | static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) | 243 | static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) |
@@ -312,13 +276,8 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) | |||
312 | 276 | ||
313 | static int ohci_pci_resume (struct usb_hcd *hcd) | 277 | static int ohci_pci_resume (struct usb_hcd *hcd) |
314 | { | 278 | { |
315 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
316 | |||
317 | set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | 279 | set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); |
318 | 280 | ohci_finish_controller_resume(hcd); | |
319 | /* FIXME: we should try to detect loss of VBUS power here */ | ||
320 | prepare_for_handover(hcd); | ||
321 | ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrenable); | ||
322 | return 0; | 281 | return 0; |
323 | } | 282 | } |
324 | 283 | ||
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 5d470263eed8..d4ee27d92be8 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c | |||
@@ -356,8 +356,7 @@ static int ohci_hcd_pxa27x_drv_resume(struct platform_device *pdev) | |||
356 | if ((status = pxa27x_start_hc(&pdev->dev)) < 0) | 356 | if ((status = pxa27x_start_hc(&pdev->dev)) < 0) |
357 | return status; | 357 | return status; |
358 | 358 | ||
359 | usb_hcd_resume_root_hub(hcd); | 359 | ohci_finish_controller_resume(hcd); |
360 | |||
361 | return 0; | 360 | return 0; |
362 | } | 361 | } |
363 | #endif | 362 | #endif |
diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index 54b6ac2e3e4a..4a11e1816017 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c | |||
@@ -231,14 +231,15 @@ static int ohci_sm501_suspend(struct platform_device *pdev, pm_message_t msg) | |||
231 | static int ohci_sm501_resume(struct platform_device *pdev) | 231 | static int ohci_sm501_resume(struct platform_device *pdev) |
232 | { | 232 | { |
233 | struct device *dev = &pdev->dev; | 233 | struct device *dev = &pdev->dev; |
234 | struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(pdev)); | 234 | struct usb_hcd *hcd = platform_get_drvdata(pdev); |
235 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
235 | 236 | ||
236 | if (time_before(jiffies, ohci->next_statechange)) | 237 | if (time_before(jiffies, ohci->next_statechange)) |
237 | msleep(5); | 238 | msleep(5); |
238 | ohci->next_statechange = jiffies; | 239 | ohci->next_statechange = jiffies; |
239 | 240 | ||
240 | sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 1); | 241 | sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 1); |
241 | usb_hcd_resume_root_hub(platform_get_drvdata(pdev)); | 242 | ohci_finish_controller_resume(hcd); |
242 | return 0; | 243 | return 0; |
243 | } | 244 | } |
244 | #else | 245 | #else |
diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c index 7879f2fdad84..7275186db315 100644 --- a/drivers/usb/host/ohci-ssb.c +++ b/drivers/usb/host/ohci-ssb.c | |||
@@ -189,6 +189,7 @@ static int ssb_ohci_resume(struct ssb_device *dev) | |||
189 | 189 | ||
190 | ssb_device_enable(dev, ohcidev->enable_flags); | 190 | ssb_device_enable(dev, ohcidev->enable_flags); |
191 | 191 | ||
192 | ohci_finish_controller_resume(hcd); | ||
192 | return 0; | 193 | return 0; |
193 | } | 194 | } |
194 | 195 | ||