aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/sleep.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/sleep.c')
-rw-r--r--drivers/acpi/sleep.c152
1 files changed, 147 insertions, 5 deletions
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 097d630ab886..be17664736b2 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -650,38 +650,165 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = {
650 .recover = acpi_pm_finish, 650 .recover = acpi_pm_finish,
651}; 651};
652 652
653static bool s2idle_in_progress;
654static bool s2idle_wakeup;
655
656/*
657 * On platforms supporting the Low Power S0 Idle interface there is an ACPI
658 * device object with the PNP0D80 compatible device ID (System Power Management
659 * Controller) and a specific _DSM method under it. That method, if present,
660 * can be used to indicate to the platform that the OS is transitioning into a
661 * low-power state in which certain types of activity are not desirable or that
662 * it is leaving such a state, which allows the platform to adjust its operation
663 * mode accordingly.
664 */
665static const struct acpi_device_id lps0_device_ids[] = {
666 {"PNP0D80", },
667 {"", },
668};
669
670#define ACPI_LPS0_DSM_UUID "c4eb40a0-6cd2-11e2-bcfd-0800200c9a66"
671
672#define ACPI_LPS0_SCREEN_OFF 3
673#define ACPI_LPS0_SCREEN_ON 4
674#define ACPI_LPS0_ENTRY 5
675#define ACPI_LPS0_EXIT 6
676
677#define ACPI_S2IDLE_FUNC_MASK ((1 << ACPI_LPS0_ENTRY) | (1 << ACPI_LPS0_EXIT))
678
679static acpi_handle lps0_device_handle;
680static guid_t lps0_dsm_guid;
681static char lps0_dsm_func_mask;
682
683static void acpi_sleep_run_lps0_dsm(unsigned int func)
684{
685 union acpi_object *out_obj;
686
687 if (!(lps0_dsm_func_mask & (1 << func)))
688 return;
689
690 out_obj = acpi_evaluate_dsm(lps0_device_handle, &lps0_dsm_guid, 1, func, NULL);
691 ACPI_FREE(out_obj);
692
693 acpi_handle_debug(lps0_device_handle, "_DSM function %u evaluation %s\n",
694 func, out_obj ? "successful" : "failed");
695}
696
697static int lps0_device_attach(struct acpi_device *adev,
698 const struct acpi_device_id *not_used)
699{
700 union acpi_object *out_obj;
701
702 if (lps0_device_handle)
703 return 0;
704
705 if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
706 return 0;
707
708 guid_parse(ACPI_LPS0_DSM_UUID, &lps0_dsm_guid);
709 /* Check if the _DSM is present and as expected. */
710 out_obj = acpi_evaluate_dsm(adev->handle, &lps0_dsm_guid, 1, 0, NULL);
711 if (out_obj && out_obj->type == ACPI_TYPE_BUFFER) {
712 char bitmask = *(char *)out_obj->buffer.pointer;
713
714 if ((bitmask & ACPI_S2IDLE_FUNC_MASK) == ACPI_S2IDLE_FUNC_MASK) {
715 lps0_dsm_func_mask = bitmask;
716 lps0_device_handle = adev->handle;
717 }
718
719 acpi_handle_debug(adev->handle, "_DSM function mask: 0x%x\n",
720 bitmask);
721 } else {
722 acpi_handle_debug(adev->handle,
723 "_DSM function 0 evaluation failed\n");
724 }
725 ACPI_FREE(out_obj);
726 return 0;
727}
728
729static struct acpi_scan_handler lps0_handler = {
730 .ids = lps0_device_ids,
731 .attach = lps0_device_attach,
732};
733
653static int acpi_freeze_begin(void) 734static int acpi_freeze_begin(void)
654{ 735{
655 acpi_scan_lock_acquire(); 736 acpi_scan_lock_acquire();
737 s2idle_in_progress = true;
656 return 0; 738 return 0;
657} 739}
658 740
659static int acpi_freeze_prepare(void) 741static int acpi_freeze_prepare(void)
660{ 742{
661 acpi_enable_wakeup_devices(ACPI_STATE_S0); 743 if (lps0_device_handle) {
662 acpi_enable_all_wakeup_gpes(); 744 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF);
663 acpi_os_wait_events_complete(); 745 acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY);
746 } else {
747 /*
748 * The configuration of GPEs is changed here to avoid spurious
749 * wakeups, but that should not be necessary if this is a
750 * "low-power S0" platform and the low-power S0 _DSM is present.
751 */
752 acpi_enable_all_wakeup_gpes();
753 acpi_os_wait_events_complete();
754 }
664 if (acpi_sci_irq_valid()) 755 if (acpi_sci_irq_valid())
665 enable_irq_wake(acpi_sci_irq); 756 enable_irq_wake(acpi_sci_irq);
757
666 return 0; 758 return 0;
667} 759}
668 760
761static void acpi_freeze_wake(void)
762{
763 /*
764 * If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means
765 * that the SCI has triggered while suspended, so cancel the wakeup in
766 * case it has not been a wakeup event (the GPEs will be checked later).
767 */
768 if (acpi_sci_irq_valid() &&
769 !irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) {
770 pm_system_cancel_wakeup();
771 s2idle_wakeup = true;
772 }
773}
774
775static void acpi_freeze_sync(void)
776{
777 /*
778 * Process all pending events in case there are any wakeup ones.
779 *
780 * The EC driver uses the system workqueue, so that one needs to be
781 * flushed too.
782 */
783 acpi_os_wait_events_complete();
784 flush_scheduled_work();
785 s2idle_wakeup = false;
786}
787
669static void acpi_freeze_restore(void) 788static void acpi_freeze_restore(void)
670{ 789{
671 acpi_disable_wakeup_devices(ACPI_STATE_S0);
672 if (acpi_sci_irq_valid()) 790 if (acpi_sci_irq_valid())
673 disable_irq_wake(acpi_sci_irq); 791 disable_irq_wake(acpi_sci_irq);
674 acpi_enable_all_runtime_gpes(); 792
793 if (lps0_device_handle) {
794 acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT);
795 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON);
796 } else {
797 acpi_enable_all_runtime_gpes();
798 }
675} 799}
676 800
677static void acpi_freeze_end(void) 801static void acpi_freeze_end(void)
678{ 802{
803 s2idle_in_progress = false;
679 acpi_scan_lock_release(); 804 acpi_scan_lock_release();
680} 805}
681 806
682static const struct platform_freeze_ops acpi_freeze_ops = { 807static const struct platform_freeze_ops acpi_freeze_ops = {
683 .begin = acpi_freeze_begin, 808 .begin = acpi_freeze_begin,
684 .prepare = acpi_freeze_prepare, 809 .prepare = acpi_freeze_prepare,
810 .wake = acpi_freeze_wake,
811 .sync = acpi_freeze_sync,
685 .restore = acpi_freeze_restore, 812 .restore = acpi_freeze_restore,
686 .end = acpi_freeze_end, 813 .end = acpi_freeze_end,
687}; 814};
@@ -696,13 +823,28 @@ static void acpi_sleep_suspend_setup(void)
696 823
697 suspend_set_ops(old_suspend_ordering ? 824 suspend_set_ops(old_suspend_ordering ?
698 &acpi_suspend_ops_old : &acpi_suspend_ops); 825 &acpi_suspend_ops_old : &acpi_suspend_ops);
826
827 acpi_scan_add_handler(&lps0_handler);
699 freeze_set_ops(&acpi_freeze_ops); 828 freeze_set_ops(&acpi_freeze_ops);
700} 829}
701 830
702#else /* !CONFIG_SUSPEND */ 831#else /* !CONFIG_SUSPEND */
832#define s2idle_in_progress (false)
833#define s2idle_wakeup (false)
834#define lps0_device_handle (NULL)
703static inline void acpi_sleep_suspend_setup(void) {} 835static inline void acpi_sleep_suspend_setup(void) {}
704#endif /* !CONFIG_SUSPEND */ 836#endif /* !CONFIG_SUSPEND */
705 837
838bool acpi_s2idle_wakeup(void)
839{
840 return s2idle_wakeup;
841}
842
843bool acpi_sleep_no_ec_events(void)
844{
845 return !s2idle_in_progress || !lps0_device_handle;
846}
847
706#ifdef CONFIG_PM_SLEEP 848#ifdef CONFIG_PM_SLEEP
707static u32 saved_bm_rld; 849static u32 saved_bm_rld;
708 850