aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2008-04-03 18:03:17 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2008-04-25 00:16:48 -0400
commit43bbb7e015c4380064796c5868b536437b165615 (patch)
tree6f4b8e184904917677ce00f600ebf3c8839f47f0 /drivers/usb/host
parent7be7d7418776a41badce7ca00246e270d408e4b9 (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/usb/host')
-rw-r--r--drivers/usb/host/ohci-at91.c1
-rw-r--r--drivers/usb/host/ohci-ep93xx.c2
-rw-r--r--drivers/usb/host/ohci-hub.c43
-rw-r--r--drivers/usb/host/ohci-omap.c5
-rw-r--r--drivers/usb/host/ohci-pci.c43
-rw-r--r--drivers/usb/host/ohci-pxa27x.c3
-rw-r--r--drivers/usb/host/ohci-sm501.c5
-rw-r--r--drivers/usb/host/ohci-ssb.c1
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 */
330static 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 */
330static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, 373static 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
511static int ohci_omap_resume(struct platform_device *dev) 511static 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 */
254static 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
272static 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
279static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) 243static 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
313static int ohci_pci_resume (struct usb_hcd *hcd) 277static 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)
231static int ohci_sm501_resume(struct platform_device *pdev) 231static 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