diff options
Diffstat (limited to 'drivers/usb/core/hcd-pci.c')
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 202 |
1 files changed, 136 insertions, 66 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 1cf2d1e79a5..c3f98543caa 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c | |||
@@ -66,10 +66,7 @@ static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd, | |||
66 | * vice versa. | 66 | * vice versa. |
67 | */ | 67 | */ |
68 | companion = NULL; | 68 | companion = NULL; |
69 | for (;;) { | 69 | for_each_pci_dev(companion) { |
70 | companion = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, companion); | ||
71 | if (!companion) | ||
72 | break; | ||
73 | if (companion->bus != pdev->bus || | 70 | if (companion->bus != pdev->bus || |
74 | PCI_SLOT(companion->devfn) != slot) | 71 | PCI_SLOT(companion->devfn) != slot) |
75 | continue; | 72 | continue; |
@@ -250,6 +247,9 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) | |||
250 | if (retval != 0) | 247 | if (retval != 0) |
251 | goto err4; | 248 | goto err4; |
252 | set_hs_companion(dev, hcd); | 249 | set_hs_companion(dev, hcd); |
250 | |||
251 | if (pci_dev_run_wake(dev)) | ||
252 | pm_runtime_put_noidle(&dev->dev); | ||
253 | return retval; | 253 | return retval; |
254 | 254 | ||
255 | err4: | 255 | err4: |
@@ -292,6 +292,17 @@ void usb_hcd_pci_remove(struct pci_dev *dev) | |||
292 | if (!hcd) | 292 | if (!hcd) |
293 | return; | 293 | return; |
294 | 294 | ||
295 | if (pci_dev_run_wake(dev)) | ||
296 | pm_runtime_get_noresume(&dev->dev); | ||
297 | |||
298 | /* Fake an interrupt request in order to give the driver a chance | ||
299 | * to test whether the controller hardware has been removed (e.g., | ||
300 | * cardbus physical eject). | ||
301 | */ | ||
302 | local_irq_disable(); | ||
303 | usb_hcd_irq(0, hcd); | ||
304 | local_irq_enable(); | ||
305 | |||
295 | usb_remove_hcd(hcd); | 306 | usb_remove_hcd(hcd); |
296 | if (hcd->driver->flags & HCD_MEMORY) { | 307 | if (hcd->driver->flags & HCD_MEMORY) { |
297 | iounmap(hcd->regs); | 308 | iounmap(hcd->regs); |
@@ -317,12 +328,34 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev) | |||
317 | if (!hcd) | 328 | if (!hcd) |
318 | return; | 329 | return; |
319 | 330 | ||
320 | if (hcd->driver->shutdown) | 331 | if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && |
332 | hcd->driver->shutdown) | ||
321 | hcd->driver->shutdown(hcd); | 333 | hcd->driver->shutdown(hcd); |
322 | } | 334 | } |
323 | EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); | 335 | EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); |
324 | 336 | ||
325 | #ifdef CONFIG_PM_SLEEP | 337 | #ifdef CONFIG_PM_OPS |
338 | |||
339 | #ifdef CONFIG_PPC_PMAC | ||
340 | static void powermac_set_asic(struct pci_dev *pci_dev, int enable) | ||
341 | { | ||
342 | /* Enanble or disable ASIC clocks for USB */ | ||
343 | if (machine_is(powermac)) { | ||
344 | struct device_node *of_node; | ||
345 | |||
346 | of_node = pci_device_to_OF_node(pci_dev); | ||
347 | if (of_node) | ||
348 | pmac_call_feature(PMAC_FTR_USB_ENABLE, | ||
349 | of_node, 0, enable); | ||
350 | } | ||
351 | } | ||
352 | |||
353 | #else | ||
354 | |||
355 | static inline void powermac_set_asic(struct pci_dev *pci_dev, int enable) | ||
356 | {} | ||
357 | |||
358 | #endif /* CONFIG_PPC_PMAC */ | ||
326 | 359 | ||
327 | static int check_root_hub_suspended(struct device *dev) | 360 | static int check_root_hub_suspended(struct device *dev) |
328 | { | 361 | { |
@@ -337,7 +370,7 @@ static int check_root_hub_suspended(struct device *dev) | |||
337 | return 0; | 370 | return 0; |
338 | } | 371 | } |
339 | 372 | ||
340 | static int hcd_pci_suspend(struct device *dev) | 373 | static int suspend_common(struct device *dev, bool do_wakeup) |
341 | { | 374 | { |
342 | struct pci_dev *pci_dev = to_pci_dev(dev); | 375 | struct pci_dev *pci_dev = to_pci_dev(dev); |
343 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); | 376 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); |
@@ -352,13 +385,21 @@ static int hcd_pci_suspend(struct device *dev) | |||
352 | if (retval) | 385 | if (retval) |
353 | return retval; | 386 | return retval; |
354 | 387 | ||
355 | /* We might already be suspended (runtime PM -- not yet written) */ | ||
356 | if (pci_dev->current_state != PCI_D0) | ||
357 | return retval; | ||
358 | |||
359 | if (hcd->driver->pci_suspend) { | 388 | if (hcd->driver->pci_suspend) { |
360 | retval = hcd->driver->pci_suspend(hcd); | 389 | /* Optimization: Don't suspend if a root-hub wakeup is |
390 | * pending and it would cause the HCD to wake up anyway. | ||
391 | */ | ||
392 | if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) | ||
393 | return -EBUSY; | ||
394 | retval = hcd->driver->pci_suspend(hcd, do_wakeup); | ||
361 | suspend_report_result(hcd->driver->pci_suspend, retval); | 395 | suspend_report_result(hcd->driver->pci_suspend, retval); |
396 | |||
397 | /* Check again in case wakeup raced with pci_suspend */ | ||
398 | if (retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) { | ||
399 | if (hcd->driver->pci_resume) | ||
400 | hcd->driver->pci_resume(hcd, false); | ||
401 | retval = -EBUSY; | ||
402 | } | ||
362 | if (retval) | 403 | if (retval) |
363 | return retval; | 404 | return retval; |
364 | } | 405 | } |
@@ -374,6 +415,48 @@ static int hcd_pci_suspend(struct device *dev) | |||
374 | return retval; | 415 | return retval; |
375 | } | 416 | } |
376 | 417 | ||
418 | static int resume_common(struct device *dev, int event) | ||
419 | { | ||
420 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
421 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); | ||
422 | int retval; | ||
423 | |||
424 | if (hcd->state != HC_STATE_SUSPENDED) { | ||
425 | dev_dbg(dev, "can't resume, not suspended!\n"); | ||
426 | return 0; | ||
427 | } | ||
428 | |||
429 | retval = pci_enable_device(pci_dev); | ||
430 | if (retval < 0) { | ||
431 | dev_err(dev, "can't re-enable after resume, %d!\n", retval); | ||
432 | return retval; | ||
433 | } | ||
434 | |||
435 | pci_set_master(pci_dev); | ||
436 | |||
437 | clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); | ||
438 | |||
439 | if (hcd->driver->pci_resume) { | ||
440 | if (event != PM_EVENT_AUTO_RESUME) | ||
441 | wait_for_companions(pci_dev, hcd); | ||
442 | |||
443 | retval = hcd->driver->pci_resume(hcd, | ||
444 | event == PM_EVENT_RESTORE); | ||
445 | if (retval) { | ||
446 | dev_err(dev, "PCI post-resume error %d!\n", retval); | ||
447 | usb_hc_died(hcd); | ||
448 | } | ||
449 | } | ||
450 | return retval; | ||
451 | } | ||
452 | |||
453 | #ifdef CONFIG_PM_SLEEP | ||
454 | |||
455 | static int hcd_pci_suspend(struct device *dev) | ||
456 | { | ||
457 | return suspend_common(dev, device_may_wakeup(dev)); | ||
458 | } | ||
459 | |||
377 | static int hcd_pci_suspend_noirq(struct device *dev) | 460 | static int hcd_pci_suspend_noirq(struct device *dev) |
378 | { | 461 | { |
379 | struct pci_dev *pci_dev = to_pci_dev(dev); | 462 | struct pci_dev *pci_dev = to_pci_dev(dev); |
@@ -408,16 +491,7 @@ static int hcd_pci_suspend_noirq(struct device *dev) | |||
408 | return retval; | 491 | return retval; |
409 | } | 492 | } |
410 | 493 | ||
411 | #ifdef CONFIG_PPC_PMAC | 494 | powermac_set_asic(pci_dev, 0); |
412 | /* Disable ASIC clocks for USB */ | ||
413 | if (machine_is(powermac)) { | ||
414 | struct device_node *of_node; | ||
415 | |||
416 | of_node = pci_device_to_OF_node(pci_dev); | ||
417 | if (of_node) | ||
418 | pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); | ||
419 | } | ||
420 | #endif | ||
421 | return retval; | 495 | return retval; |
422 | } | 496 | } |
423 | 497 | ||
@@ -425,69 +499,63 @@ static int hcd_pci_resume_noirq(struct device *dev) | |||
425 | { | 499 | { |
426 | struct pci_dev *pci_dev = to_pci_dev(dev); | 500 | struct pci_dev *pci_dev = to_pci_dev(dev); |
427 | 501 | ||
428 | #ifdef CONFIG_PPC_PMAC | 502 | powermac_set_asic(pci_dev, 1); |
429 | /* Reenable ASIC clocks for USB */ | ||
430 | if (machine_is(powermac)) { | ||
431 | struct device_node *of_node; | ||
432 | |||
433 | of_node = pci_device_to_OF_node(pci_dev); | ||
434 | if (of_node) | ||
435 | pmac_call_feature(PMAC_FTR_USB_ENABLE, | ||
436 | of_node, 0, 1); | ||
437 | } | ||
438 | #endif | ||
439 | 503 | ||
440 | /* Go back to D0 and disable remote wakeup */ | 504 | /* Go back to D0 and disable remote wakeup */ |
441 | pci_back_from_sleep(pci_dev); | 505 | pci_back_from_sleep(pci_dev); |
442 | return 0; | 506 | return 0; |
443 | } | 507 | } |
444 | 508 | ||
445 | static int resume_common(struct device *dev, bool hibernated) | 509 | static int hcd_pci_resume(struct device *dev) |
446 | { | 510 | { |
447 | struct pci_dev *pci_dev = to_pci_dev(dev); | 511 | return resume_common(dev, PM_EVENT_RESUME); |
448 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); | 512 | } |
449 | int retval; | ||
450 | 513 | ||
451 | if (hcd->state != HC_STATE_SUSPENDED) { | 514 | static int hcd_pci_restore(struct device *dev) |
452 | dev_dbg(dev, "can't resume, not suspended!\n"); | 515 | { |
453 | return 0; | 516 | return resume_common(dev, PM_EVENT_RESTORE); |
454 | } | 517 | } |
455 | 518 | ||
456 | retval = pci_enable_device(pci_dev); | 519 | #else |
457 | if (retval < 0) { | ||
458 | dev_err(dev, "can't re-enable after resume, %d!\n", retval); | ||
459 | return retval; | ||
460 | } | ||
461 | 520 | ||
462 | pci_set_master(pci_dev); | 521 | #define hcd_pci_suspend NULL |
522 | #define hcd_pci_suspend_noirq NULL | ||
523 | #define hcd_pci_resume_noirq NULL | ||
524 | #define hcd_pci_resume NULL | ||
525 | #define hcd_pci_restore NULL | ||
463 | 526 | ||
464 | clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); | 527 | #endif /* CONFIG_PM_SLEEP */ |
465 | 528 | ||
466 | if (hcd->driver->pci_resume) { | 529 | #ifdef CONFIG_PM_RUNTIME |
467 | /* This call should be made only during system resume, | ||
468 | * not during runtime resume. | ||
469 | */ | ||
470 | wait_for_companions(pci_dev, hcd); | ||
471 | 530 | ||
472 | retval = hcd->driver->pci_resume(hcd, hibernated); | 531 | static int hcd_pci_runtime_suspend(struct device *dev) |
473 | if (retval) { | 532 | { |
474 | dev_err(dev, "PCI post-resume error %d!\n", retval); | 533 | int retval; |
475 | usb_hc_died(hcd); | 534 | |
476 | } | 535 | retval = suspend_common(dev, true); |
477 | } | 536 | if (retval == 0) |
537 | powermac_set_asic(to_pci_dev(dev), 0); | ||
538 | dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval); | ||
478 | return retval; | 539 | return retval; |
479 | } | 540 | } |
480 | 541 | ||
481 | static int hcd_pci_resume(struct device *dev) | 542 | static int hcd_pci_runtime_resume(struct device *dev) |
482 | { | 543 | { |
483 | return resume_common(dev, false); | 544 | int retval; |
484 | } | ||
485 | 545 | ||
486 | static int hcd_pci_restore(struct device *dev) | 546 | powermac_set_asic(to_pci_dev(dev), 1); |
487 | { | 547 | retval = resume_common(dev, PM_EVENT_AUTO_RESUME); |
488 | return resume_common(dev, true); | 548 | dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval); |
549 | return retval; | ||
489 | } | 550 | } |
490 | 551 | ||
552 | #else | ||
553 | |||
554 | #define hcd_pci_runtime_suspend NULL | ||
555 | #define hcd_pci_runtime_resume NULL | ||
556 | |||
557 | #endif /* CONFIG_PM_RUNTIME */ | ||
558 | |||
491 | const struct dev_pm_ops usb_hcd_pci_pm_ops = { | 559 | const struct dev_pm_ops usb_hcd_pci_pm_ops = { |
492 | .suspend = hcd_pci_suspend, | 560 | .suspend = hcd_pci_suspend, |
493 | .suspend_noirq = hcd_pci_suspend_noirq, | 561 | .suspend_noirq = hcd_pci_suspend_noirq, |
@@ -501,7 +569,9 @@ const struct dev_pm_ops usb_hcd_pci_pm_ops = { | |||
501 | .poweroff_noirq = hcd_pci_suspend_noirq, | 569 | .poweroff_noirq = hcd_pci_suspend_noirq, |
502 | .restore_noirq = hcd_pci_resume_noirq, | 570 | .restore_noirq = hcd_pci_resume_noirq, |
503 | .restore = hcd_pci_restore, | 571 | .restore = hcd_pci_restore, |
572 | .runtime_suspend = hcd_pci_runtime_suspend, | ||
573 | .runtime_resume = hcd_pci_runtime_resume, | ||
504 | }; | 574 | }; |
505 | EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops); | 575 | EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops); |
506 | 576 | ||
507 | #endif /* CONFIG_PM_SLEEP */ | 577 | #endif /* CONFIG_PM_OPS */ |