diff options
-rw-r--r-- | drivers/acpi/device_pm.c | 317 | ||||
-rw-r--r-- | include/linux/acpi.h | 34 |
2 files changed, 351 insertions, 0 deletions
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 7ddd93463a2e..a8e059f69d50 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c | |||
@@ -225,6 +225,22 @@ EXPORT_SYMBOL(acpi_pm_device_sleep_state); | |||
225 | 225 | ||
226 | #ifdef CONFIG_PM_RUNTIME | 226 | #ifdef CONFIG_PM_RUNTIME |
227 | /** | 227 | /** |
228 | * acpi_wakeup_device - Wakeup notification handler for ACPI devices. | ||
229 | * @handle: ACPI handle of the device the notification is for. | ||
230 | * @event: Type of the signaled event. | ||
231 | * @context: Device corresponding to @handle. | ||
232 | */ | ||
233 | static void acpi_wakeup_device(acpi_handle handle, u32 event, void *context) | ||
234 | { | ||
235 | struct device *dev = context; | ||
236 | |||
237 | if (event == ACPI_NOTIFY_DEVICE_WAKE && dev) { | ||
238 | pm_wakeup_event(dev, 0); | ||
239 | pm_runtime_resume(dev); | ||
240 | } | ||
241 | } | ||
242 | |||
243 | /** | ||
228 | * __acpi_device_run_wake - Enable/disable runtime remote wakeup for device. | 244 | * __acpi_device_run_wake - Enable/disable runtime remote wakeup for device. |
229 | * @adev: ACPI device to enable/disable the remote wakeup for. | 245 | * @adev: ACPI device to enable/disable the remote wakeup for. |
230 | * @enable: Whether to enable or disable the wakeup functionality. | 246 | * @enable: Whether to enable or disable the wakeup functionality. |
@@ -283,6 +299,9 @@ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable) | |||
283 | return __acpi_device_run_wake(adev, enable); | 299 | return __acpi_device_run_wake(adev, enable); |
284 | } | 300 | } |
285 | EXPORT_SYMBOL(acpi_pm_device_run_wake); | 301 | EXPORT_SYMBOL(acpi_pm_device_run_wake); |
302 | #else | ||
303 | static inline void acpi_wakeup_device(acpi_handle handle, u32 event, | ||
304 | void *context) {} | ||
286 | #endif /* CONFIG_PM_RUNTIME */ | 305 | #endif /* CONFIG_PM_RUNTIME */ |
287 | 306 | ||
288 | #ifdef CONFIG_PM_SLEEP | 307 | #ifdef CONFIG_PM_SLEEP |
@@ -329,3 +348,301 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable) | |||
329 | return error; | 348 | return error; |
330 | } | 349 | } |
331 | #endif /* CONFIG_PM_SLEEP */ | 350 | #endif /* CONFIG_PM_SLEEP */ |
351 | |||
352 | /** | ||
353 | * acpi_dev_pm_get_node - Get ACPI device node for the given physical device. | ||
354 | * @dev: Device to get the ACPI node for. | ||
355 | */ | ||
356 | static struct acpi_device *acpi_dev_pm_get_node(struct device *dev) | ||
357 | { | ||
358 | acpi_handle handle = DEVICE_ACPI_HANDLE(dev); | ||
359 | struct acpi_device *adev; | ||
360 | |||
361 | return handle && ACPI_SUCCESS(acpi_bus_get_device(handle, &adev)) ? | ||
362 | adev : NULL; | ||
363 | } | ||
364 | |||
365 | /** | ||
366 | * acpi_dev_pm_low_power - Put ACPI device into a low-power state. | ||
367 | * @dev: Device to put into a low-power state. | ||
368 | * @adev: ACPI device node corresponding to @dev. | ||
369 | * @system_state: System state to choose the device state for. | ||
370 | */ | ||
371 | static int acpi_dev_pm_low_power(struct device *dev, struct acpi_device *adev, | ||
372 | u32 system_state) | ||
373 | { | ||
374 | int power_state; | ||
375 | |||
376 | if (!acpi_device_power_manageable(adev)) | ||
377 | return 0; | ||
378 | |||
379 | power_state = acpi_device_power_state(dev, adev, system_state, | ||
380 | ACPI_STATE_D3, NULL); | ||
381 | if (power_state < ACPI_STATE_D0 || power_state > ACPI_STATE_D3) | ||
382 | return -EIO; | ||
383 | |||
384 | return acpi_device_set_power(adev, power_state); | ||
385 | } | ||
386 | |||
387 | /** | ||
388 | * acpi_dev_pm_full_power - Put ACPI device into the full-power state. | ||
389 | * @adev: ACPI device node to put into the full-power state. | ||
390 | */ | ||
391 | static int acpi_dev_pm_full_power(struct acpi_device *adev) | ||
392 | { | ||
393 | return acpi_device_power_manageable(adev) ? | ||
394 | acpi_device_set_power(adev, ACPI_STATE_D0) : 0; | ||
395 | } | ||
396 | |||
397 | #ifdef CONFIG_PM_RUNTIME | ||
398 | /** | ||
399 | * acpi_dev_runtime_suspend - Put device into a low-power state using ACPI. | ||
400 | * @dev: Device to put into a low-power state. | ||
401 | * | ||
402 | * Put the given device into a runtime low-power state using the standard ACPI | ||
403 | * mechanism. Set up remote wakeup if desired, choose the state to put the | ||
404 | * device into (this checks if remote wakeup is expected to work too), and set | ||
405 | * the power state of the device. | ||
406 | */ | ||
407 | int acpi_dev_runtime_suspend(struct device *dev) | ||
408 | { | ||
409 | struct acpi_device *adev = acpi_dev_pm_get_node(dev); | ||
410 | bool remote_wakeup; | ||
411 | int error; | ||
412 | |||
413 | if (!adev) | ||
414 | return 0; | ||
415 | |||
416 | remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) > | ||
417 | PM_QOS_FLAGS_NONE; | ||
418 | error = __acpi_device_run_wake(adev, remote_wakeup); | ||
419 | if (remote_wakeup && error) | ||
420 | return -EAGAIN; | ||
421 | |||
422 | error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0); | ||
423 | if (error) | ||
424 | __acpi_device_run_wake(adev, false); | ||
425 | |||
426 | return error; | ||
427 | } | ||
428 | EXPORT_SYMBOL_GPL(acpi_dev_runtime_suspend); | ||
429 | |||
430 | /** | ||
431 | * acpi_dev_runtime_resume - Put device into the full-power state using ACPI. | ||
432 | * @dev: Device to put into the full-power state. | ||
433 | * | ||
434 | * Put the given device into the full-power state using the standard ACPI | ||
435 | * mechanism at run time. Set the power state of the device to ACPI D0 and | ||
436 | * disable remote wakeup. | ||
437 | */ | ||
438 | int acpi_dev_runtime_resume(struct device *dev) | ||
439 | { | ||
440 | struct acpi_device *adev = acpi_dev_pm_get_node(dev); | ||
441 | int error; | ||
442 | |||
443 | if (!adev) | ||
444 | return 0; | ||
445 | |||
446 | error = acpi_dev_pm_full_power(adev); | ||
447 | __acpi_device_run_wake(adev, false); | ||
448 | return error; | ||
449 | } | ||
450 | EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume); | ||
451 | |||
452 | /** | ||
453 | * acpi_subsys_runtime_suspend - Suspend device using ACPI. | ||
454 | * @dev: Device to suspend. | ||
455 | * | ||
456 | * Carry out the generic runtime suspend procedure for @dev and use ACPI to put | ||
457 | * it into a runtime low-power state. | ||
458 | */ | ||
459 | int acpi_subsys_runtime_suspend(struct device *dev) | ||
460 | { | ||
461 | int ret = pm_generic_runtime_suspend(dev); | ||
462 | return ret ? ret : acpi_dev_runtime_suspend(dev); | ||
463 | } | ||
464 | EXPORT_SYMBOL_GPL(acpi_subsys_runtime_suspend); | ||
465 | |||
466 | /** | ||
467 | * acpi_subsys_runtime_resume - Resume device using ACPI. | ||
468 | * @dev: Device to Resume. | ||
469 | * | ||
470 | * Use ACPI to put the given device into the full-power state and carry out the | ||
471 | * generic runtime resume procedure for it. | ||
472 | */ | ||
473 | int acpi_subsys_runtime_resume(struct device *dev) | ||
474 | { | ||
475 | int ret = acpi_dev_runtime_resume(dev); | ||
476 | return ret ? ret : pm_generic_runtime_resume(dev); | ||
477 | } | ||
478 | EXPORT_SYMBOL_GPL(acpi_subsys_runtime_resume); | ||
479 | #endif /* CONFIG_PM_RUNTIME */ | ||
480 | |||
481 | #ifdef CONFIG_PM_SLEEP | ||
482 | /** | ||
483 | * acpi_dev_suspend_late - Put device into a low-power state using ACPI. | ||
484 | * @dev: Device to put into a low-power state. | ||
485 | * | ||
486 | * Put the given device into a low-power state during system transition to a | ||
487 | * sleep state using the standard ACPI mechanism. Set up system wakeup if | ||
488 | * desired, choose the state to put the device into (this checks if system | ||
489 | * wakeup is expected to work too), and set the power state of the device. | ||
490 | */ | ||
491 | int acpi_dev_suspend_late(struct device *dev) | ||
492 | { | ||
493 | struct acpi_device *adev = acpi_dev_pm_get_node(dev); | ||
494 | u32 target_state; | ||
495 | bool wakeup; | ||
496 | int error; | ||
497 | |||
498 | if (!adev) | ||
499 | return 0; | ||
500 | |||
501 | target_state = acpi_target_system_state(); | ||
502 | wakeup = device_may_wakeup(dev); | ||
503 | error = __acpi_device_sleep_wake(adev, target_state, wakeup); | ||
504 | if (wakeup && error) | ||
505 | return error; | ||
506 | |||
507 | error = acpi_dev_pm_low_power(dev, adev, target_state); | ||
508 | if (error) | ||
509 | __acpi_device_sleep_wake(adev, ACPI_STATE_UNKNOWN, false); | ||
510 | |||
511 | return error; | ||
512 | } | ||
513 | EXPORT_SYMBOL_GPL(acpi_dev_suspend_late); | ||
514 | |||
515 | /** | ||
516 | * acpi_dev_resume_early - Put device into the full-power state using ACPI. | ||
517 | * @dev: Device to put into the full-power state. | ||
518 | * | ||
519 | * Put the given device into the full-power state using the standard ACPI | ||
520 | * mechanism during system transition to the working state. Set the power | ||
521 | * state of the device to ACPI D0 and disable remote wakeup. | ||
522 | */ | ||
523 | int acpi_dev_resume_early(struct device *dev) | ||
524 | { | ||
525 | struct acpi_device *adev = acpi_dev_pm_get_node(dev); | ||
526 | int error; | ||
527 | |||
528 | if (!adev) | ||
529 | return 0; | ||
530 | |||
531 | error = acpi_dev_pm_full_power(adev); | ||
532 | __acpi_device_sleep_wake(adev, ACPI_STATE_UNKNOWN, false); | ||
533 | return error; | ||
534 | } | ||
535 | EXPORT_SYMBOL_GPL(acpi_dev_resume_early); | ||
536 | |||
537 | /** | ||
538 | * acpi_subsys_prepare - Prepare device for system transition to a sleep state. | ||
539 | * @dev: Device to prepare. | ||
540 | */ | ||
541 | int acpi_subsys_prepare(struct device *dev) | ||
542 | { | ||
543 | /* | ||
544 | * Follow PCI and resume devices suspended at run time before running | ||
545 | * their system suspend callbacks. | ||
546 | */ | ||
547 | pm_runtime_resume(dev); | ||
548 | return pm_generic_prepare(dev); | ||
549 | } | ||
550 | EXPORT_SYMBOL_GPL(acpi_subsys_prepare); | ||
551 | |||
552 | /** | ||
553 | * acpi_subsys_suspend_late - Suspend device using ACPI. | ||
554 | * @dev: Device to suspend. | ||
555 | * | ||
556 | * Carry out the generic late suspend procedure for @dev and use ACPI to put | ||
557 | * it into a low-power state during system transition into a sleep state. | ||
558 | */ | ||
559 | int acpi_subsys_suspend_late(struct device *dev) | ||
560 | { | ||
561 | int ret = pm_generic_suspend_late(dev); | ||
562 | return ret ? ret : acpi_dev_suspend_late(dev); | ||
563 | } | ||
564 | EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late); | ||
565 | |||
566 | /** | ||
567 | * acpi_subsys_resume_early - Resume device using ACPI. | ||
568 | * @dev: Device to Resume. | ||
569 | * | ||
570 | * Use ACPI to put the given device into the full-power state and carry out the | ||
571 | * generic early resume procedure for it during system transition into the | ||
572 | * working state. | ||
573 | */ | ||
574 | int acpi_subsys_resume_early(struct device *dev) | ||
575 | { | ||
576 | int ret = acpi_dev_resume_early(dev); | ||
577 | return ret ? ret : pm_generic_resume_early(dev); | ||
578 | } | ||
579 | EXPORT_SYMBOL_GPL(acpi_subsys_resume_early); | ||
580 | #endif /* CONFIG_PM_SLEEP */ | ||
581 | |||
582 | static struct dev_pm_domain acpi_general_pm_domain = { | ||
583 | .ops = { | ||
584 | #ifdef CONFIG_PM_RUNTIME | ||
585 | .runtime_suspend = acpi_subsys_runtime_suspend, | ||
586 | .runtime_resume = acpi_subsys_runtime_resume, | ||
587 | .runtime_idle = pm_generic_runtime_idle, | ||
588 | #endif | ||
589 | #ifdef CONFIG_PM_SLEEP | ||
590 | .prepare = acpi_subsys_prepare, | ||
591 | .suspend_late = acpi_subsys_suspend_late, | ||
592 | .resume_early = acpi_subsys_resume_early, | ||
593 | .poweroff_late = acpi_subsys_suspend_late, | ||
594 | .restore_early = acpi_subsys_resume_early, | ||
595 | #endif | ||
596 | }, | ||
597 | }; | ||
598 | |||
599 | /** | ||
600 | * acpi_dev_pm_attach - Prepare device for ACPI power management. | ||
601 | * @dev: Device to prepare. | ||
602 | * | ||
603 | * If @dev has a valid ACPI handle that has a valid struct acpi_device object | ||
604 | * attached to it, install a wakeup notification handler for the device and | ||
605 | * add it to the general ACPI PM domain. | ||
606 | * | ||
607 | * This assumes that the @dev's bus type uses generic power management callbacks | ||
608 | * (or doesn't use any power management callbacks at all). | ||
609 | * | ||
610 | * Callers must ensure proper synchronization of this function with power | ||
611 | * management callbacks. | ||
612 | */ | ||
613 | int acpi_dev_pm_attach(struct device *dev) | ||
614 | { | ||
615 | struct acpi_device *adev = acpi_dev_pm_get_node(dev); | ||
616 | |||
617 | if (!adev) | ||
618 | return -ENODEV; | ||
619 | |||
620 | if (dev->pm_domain) | ||
621 | return -EEXIST; | ||
622 | |||
623 | acpi_add_pm_notifier(adev, acpi_wakeup_device, dev); | ||
624 | dev->pm_domain = &acpi_general_pm_domain; | ||
625 | return 0; | ||
626 | } | ||
627 | EXPORT_SYMBOL_GPL(acpi_dev_pm_attach); | ||
628 | |||
629 | /** | ||
630 | * acpi_dev_pm_detach - Remove ACPI power management from the device. | ||
631 | * @dev: Device to take care of. | ||
632 | * | ||
633 | * Remove the device from the general ACPI PM domain and remove its wakeup | ||
634 | * notifier. | ||
635 | * | ||
636 | * Callers must ensure proper synchronization of this function with power | ||
637 | * management callbacks. | ||
638 | */ | ||
639 | void acpi_dev_pm_detach(struct device *dev) | ||
640 | { | ||
641 | struct acpi_device *adev = acpi_dev_pm_get_node(dev); | ||
642 | |||
643 | if (adev && dev->pm_domain == &acpi_general_pm_domain) { | ||
644 | dev->pm_domain = NULL; | ||
645 | acpi_remove_pm_notifier(adev, acpi_wakeup_device); | ||
646 | } | ||
647 | } | ||
648 | EXPORT_SYMBOL_GPL(acpi_dev_pm_detach); | ||
diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 90be98981102..0676b6ac57fa 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h | |||
@@ -430,4 +430,38 @@ acpi_status acpi_os_prepare_sleep(u8 sleep_state, | |||
430 | #define acpi_os_set_prepare_sleep(func, pm1a_ctrl, pm1b_ctrl) do { } while (0) | 430 | #define acpi_os_set_prepare_sleep(func, pm1a_ctrl, pm1b_ctrl) do { } while (0) |
431 | #endif | 431 | #endif |
432 | 432 | ||
433 | #if defined(CONFIG_ACPI) && defined(CONFIG_PM_RUNTIME) | ||
434 | int acpi_dev_runtime_suspend(struct device *dev); | ||
435 | int acpi_dev_runtime_resume(struct device *dev); | ||
436 | int acpi_subsys_runtime_suspend(struct device *dev); | ||
437 | int acpi_subsys_runtime_resume(struct device *dev); | ||
438 | #else | ||
439 | static inline int acpi_dev_runtime_suspend(struct device *dev) { return 0; } | ||
440 | static inline int acpi_dev_runtime_resume(struct device *dev) { return 0; } | ||
441 | static inline int acpi_subsys_runtime_suspend(struct device *dev) { return 0; } | ||
442 | static inline int acpi_subsys_runtime_resume(struct device *dev) { return 0; } | ||
443 | #endif | ||
444 | |||
445 | #ifdef CONFIG_ACPI_SLEEP | ||
446 | int acpi_dev_suspend_late(struct device *dev); | ||
447 | int acpi_dev_resume_early(struct device *dev); | ||
448 | int acpi_subsys_prepare(struct device *dev); | ||
449 | int acpi_subsys_suspend_late(struct device *dev); | ||
450 | int acpi_subsys_resume_early(struct device *dev); | ||
451 | #else | ||
452 | static inline int acpi_dev_suspend_late(struct device *dev) { return 0; } | ||
453 | static inline int acpi_dev_resume_early(struct device *dev) { return 0; } | ||
454 | static inline int acpi_subsys_prepare(struct device *dev) { return 0; } | ||
455 | static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; } | ||
456 | static inline int acpi_subsys_resume_early(struct device *dev) { return 0; } | ||
457 | #endif | ||
458 | |||
459 | #if defined(CONFIG_ACPI) && defined(CONFIG_PM) | ||
460 | int acpi_dev_pm_attach(struct device *dev); | ||
461 | int acpi_dev_pm_detach(struct device *dev); | ||
462 | #else | ||
463 | static inline int acpi_dev_pm_attach(struct device *dev) { return -ENODEV; } | ||
464 | static inline void acpi_dev_pm_detach(struct device *dev) {} | ||
465 | #endif | ||
466 | |||
433 | #endif /*_LINUX_ACPI_H*/ | 467 | #endif /*_LINUX_ACPI_H*/ |