Page MenuHomeFreeBSD

D48734.1775374406.diff
No OneTemporary

Size
13 KB
Referenced Files
None
Subscribers
None

D48734.1775374406.diff

diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -64,6 +64,7 @@
#endif
#include <machine/resource.h>
#include <machine/bus.h>
+#include <machine/intr_machdep.h>
#include <sys/rman.h>
#include <isa/isavar.h>
#include <isa/pnpvar.h>
@@ -679,21 +680,26 @@
#endif
/*
- * Probe all supported ACPI sleep states. Awake (S0) is always supported.
+ * Probe all supported ACPI sleep states. Awake (S0) and suspend-to-idle
+ * are always supported.
*/
- acpi_supported_sstates[ACPI_STATE_S0] = TRUE;
+ acpi_supported_sstates[ACPI_STATE_S0] = true;
acpi_supported_stypes[POWER_STYPE_AWAKE] = true;
+ acpi_supported_stypes[POWER_STYPE_SUSPEND_TO_IDLE] = true;
for (state = ACPI_STATE_S1; state <= ACPI_STATE_S5; state++)
if (ACPI_SUCCESS(AcpiEvaluateObject(ACPI_ROOT_OBJECT,
__DECONST(char *, AcpiGbl_SleepStateNames[state]), NULL, NULL)) &&
ACPI_SUCCESS(AcpiGetSleepTypeData(state, &TypeA, &TypeB))) {
- acpi_supported_sstates[state] = TRUE;
+ acpi_supported_sstates[state] = true;
acpi_supported_stypes[acpi_sstate_to_stype(state)] = true;
}
/*
* Dispatch the default sleep type to devices. The lid switch is set
- * to UNKNOWN by default to avoid surprising users.
+ * to UNKNOWN by default to avoid surprising users. The sleep button
+ * prefers s2mem instead of s2idle at the moment as s2idle may not work
+ * reliably on all machines. In the future, we should set this to s2idle
+ * when ACPI_FADT_LOW_POWER_S0 is set.
*/
sc->acpi_power_button_stype = acpi_supported_stypes[POWER_STYPE_POWEROFF] ?
POWER_STYPE_POWEROFF : POWER_STYPE_UNKNOWN;
@@ -705,13 +711,19 @@
else if (acpi_supported_sstates[ACPI_STATE_S2])
sc->acpi_standby_sx = ACPI_STATE_S2;
- /* Pick the first valid sleep type for the sleep button default. */
+ /*
+ * Pick the first valid sleep type for the sleep button default. If that
+ * type was hibernate and we support s2idle, set it to that.
+ */
sc->acpi_sleep_button_stype = POWER_STYPE_UNKNOWN;
for (stype = POWER_STYPE_STANDBY; stype <= POWER_STYPE_HIBERNATE; stype++)
if (acpi_supported_stypes[stype]) {
sc->acpi_sleep_button_stype = stype;
break;
}
+ if (sc->acpi_sleep_button_stype == POWER_STYPE_HIBERNATE &&
+ acpi_supported_stypes[POWER_STYPE_SUSPEND_TO_IDLE])
+ sc->acpi_sleep_button_stype = POWER_STYPE_SUSPEND_TO_IDLE;
acpi_enable_fixed_events(sc);
@@ -3314,7 +3326,8 @@
return (0);
#else
- /* This platform does not support acpi suspend/resume. */
+ device_printf(sc->acpi_dev, "ACPI suspend not supported on this platform"
+ "(TODO suspend to idle should be, however)\n");
return (EOPNOTSUPP);
#endif
}
@@ -3378,7 +3391,8 @@
}
return (ret);
#else
- /* This platform does not support acpi suspend/resume. */
+ device_printf(sc->acpi_dev, "ACPI suspend not supported on this platform"
+ "(TODO suspend to idle should be, however)\n");
return (EOPNOTSUPP);
#endif
}
@@ -3417,27 +3431,133 @@
}
enum acpi_sleep_state {
- ACPI_SS_NONE,
- ACPI_SS_GPE_SET,
- ACPI_SS_DEV_SUSPEND,
- ACPI_SS_SLP_PREP,
- ACPI_SS_SLEPT,
+ ACPI_SS_NONE = 0,
+ ACPI_SS_GPE_SET = 1 << 0,
+ ACPI_SS_DEV_SUSPEND = 1 << 1,
+ ACPI_SS_SLP_PREP = 1 << 2,
+ ACPI_SS_SLEPT = 1 << 3,
};
+static void
+do_standby(struct acpi_softc *sc, enum acpi_sleep_state *slp_state,
+ register_t rflags)
+{
+ ACPI_STATUS status;
+
+ status = AcpiEnterSleepState(sc->acpi_standby_sx);
+ intr_restore(rflags);
+ AcpiLeaveSleepStatePrep(sc->acpi_standby_sx);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->acpi_dev, "AcpiEnterSleepState failed - %s\n",
+ AcpiFormatException(status));
+ return;
+ }
+ *slp_state |= ACPI_SS_SLEPT;
+}
+
+static void
+do_sleep(struct acpi_softc *sc, enum acpi_sleep_state *slp_state,
+ register_t rflags, int state)
+{
+ int sleep_result;
+ ACPI_EVENT_STATUS power_button_status;
+
+ MPASS(state == ACPI_STATE_S3 || state == ACPI_STATE_S4);
+
+ sleep_result = acpi_sleep_machdep(sc, state);
+ acpi_wakeup_machdep(sc, state, sleep_result, 0);
+
+ if (sleep_result == 1 && state != ACPI_STATE_S4) {
+ /*
+ * XXX According to ACPI specification SCI_EN bit should be restored
+ * by ACPI platform (BIOS, firmware) to its pre-sleep state.
+ * Unfortunately some BIOSes fail to do that and that leads to
+ * unexpected and serious consequences during wake up like a system
+ * getting stuck in SMI handlers.
+ * This hack is picked up from Linux, which claims that it follows
+ * Windows behavior.
+ */
+ AcpiWriteBitRegister(ACPI_BITREG_SCI_ENABLE, ACPI_ENABLE_EVENT);
+ }
+
+ if (sleep_result == 1 && state == ACPI_STATE_S3) {
+ /*
+ * Prevent misinterpretation of the wakeup by power button
+ * as a request for power off.
+ * Ideally we should post an appropriate wakeup event,
+ * perhaps using acpi_event_power_button_wake or alike.
+ *
+ * Clearing of power button status after wakeup is mandated
+ * by ACPI specification in section "Fixed Power Button".
+ *
+ * XXX As of ACPICA 20121114 AcpiGetEventStatus provides
+ * status as 0/1 corresponding to inactive/active despite
+ * its type being ACPI_EVENT_STATUS. In other words,
+ * we should not test for ACPI_EVENT_FLAG_SET for time being.
+ */
+ if (ACPI_SUCCESS(AcpiGetEventStatus(ACPI_EVENT_POWER_BUTTON,
+ &power_button_status)) && power_button_status != 0) {
+ AcpiClearEvent(ACPI_EVENT_POWER_BUTTON);
+ device_printf(sc->acpi_dev, "cleared fixed power button status\n");
+ }
+ }
+
+ intr_restore(rflags);
+
+ /* call acpi_wakeup_machdep() again with interrupt enabled */
+ acpi_wakeup_machdep(sc, state, sleep_result, 1);
+
+ AcpiLeaveSleepStatePrep(state);
+
+ if (sleep_result == -1)
+ return;
+
+ /* Re-enable ACPI hardware on wakeup from sleep state 4. */
+ if (state == ACPI_STATE_S4)
+ AcpiEnable();
+ *slp_state |= ACPI_SS_SLEPT;
+}
+
+static void
+do_idle(struct acpi_softc *sc, enum acpi_sleep_state *slp_state,
+ register_t rflags)
+{
+
+ intr_suspend();
+
+ /*
+ * The CPU will exit idle when interrupted, so we want to minimize the
+ * number of interrupts it can receive while idle. We do this by only
+ * allowing SCI (system control interrupt) interrupts, which are used by
+ * the ACPI firmware to send wake GPEs to the OS.
+ *
+ * XXX We might still receive other spurious non-wake GPEs from noisy
+ * devices that can't be disabled, so this will need to end up being a
+ * suspend-to-idle loop which, when breaking out of idle, will check the
+ * reason for the wakeup and immediately idle the CPU again if it was not a
+ * proper wake event.
+ */
+ intr_enable_src(AcpiGbl_FADT.SciInterrupt);
+
+ cpu_idle(0);
+
+ intr_resume(false);
+ intr_restore(rflags);
+ *slp_state |= ACPI_SS_SLEPT;
+}
+
/*
* Enter the desired system sleep state.
*
- * Currently we support S1-S5 but S4 is only S4BIOS
+ * Currently we support S1-S5 and suspend-to-idle, but S4 is only S4BIOS.
*/
static ACPI_STATUS
acpi_EnterSleepState(struct acpi_softc *sc, enum power_stype stype)
{
register_t intr;
ACPI_STATUS status;
- ACPI_EVENT_STATUS power_button_status;
enum acpi_sleep_state slp_state;
int acpi_sstate;
- int sleep_result;
ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, stype);
@@ -3497,7 +3617,7 @@
/* Enable any GPEs as appropriate and requested by the user. */
acpi_wake_prep_walk(sc, stype);
- slp_state = ACPI_SS_GPE_SET;
+ slp_state |= ACPI_SS_GPE_SET;
/*
* Inform all devices that we are going to sleep. If at least one
@@ -3508,113 +3628,80 @@
* bus interface does not provide for this.
*/
if (DEVICE_SUSPEND(root_bus) != 0) {
- device_printf(sc->acpi_dev, "device_suspend failed\n");
- goto backout;
+ device_printf(sc->acpi_dev, "device_suspend failed\n");
+ goto backout;
}
- slp_state = ACPI_SS_DEV_SUSPEND;
+ slp_state |= ACPI_SS_DEV_SUSPEND;
+ AcpiOsSleep(1000);
- status = AcpiEnterSleepStatePrep(acpi_sstate);
- if (ACPI_FAILURE(status)) {
- device_printf(sc->acpi_dev, "AcpiEnterSleepStatePrep failed - %s\n",
- AcpiFormatException(status));
- goto backout;
+ if (stype != POWER_STYPE_SUSPEND_TO_IDLE) {
+ status = AcpiEnterSleepStatePrep(acpi_sstate);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->acpi_dev, "AcpiEnterSleepStatePrep failed - %s\n",
+ AcpiFormatException(status));
+ goto backout;
+ }
}
- slp_state = ACPI_SS_SLP_PREP;
+ slp_state |= ACPI_SS_SLP_PREP;
if (sc->acpi_sleep_delay > 0)
DELAY(sc->acpi_sleep_delay * 1000000);
suspendclock();
intr = intr_disable();
- if (stype != POWER_STYPE_STANDBY) {
- sleep_result = acpi_sleep_machdep(sc, acpi_sstate);
- acpi_wakeup_machdep(sc, acpi_sstate, sleep_result, 0);
-
- /*
- * XXX According to ACPI specification SCI_EN bit should be restored
- * by ACPI platform (BIOS, firmware) to its pre-sleep state.
- * Unfortunately some BIOSes fail to do that and that leads to
- * unexpected and serious consequences during wake up like a system
- * getting stuck in SMI handlers.
- * This hack is picked up from Linux, which claims that it follows
- * Windows behavior.
- */
- if (sleep_result == 1 && stype != POWER_STYPE_HIBERNATE)
- AcpiWriteBitRegister(ACPI_BITREG_SCI_ENABLE, ACPI_ENABLE_EVENT);
-
- if (sleep_result == 1 && stype == POWER_STYPE_SUSPEND_TO_MEM) {
- /*
- * Prevent mis-interpretation of the wakeup by power button
- * as a request for power off.
- * Ideally we should post an appropriate wakeup event,
- * perhaps using acpi_event_power_button_wake or alike.
- *
- * Clearing of power button status after wakeup is mandated
- * by ACPI specification in section "Fixed Power Button".
- *
- * XXX As of ACPICA 20121114 AcpiGetEventStatus provides
- * status as 0/1 corressponding to inactive/active despite
- * its type being ACPI_EVENT_STATUS. In other words,
- * we should not test for ACPI_EVENT_FLAG_SET for time being.
- */
- if (ACPI_SUCCESS(AcpiGetEventStatus(ACPI_EVENT_POWER_BUTTON,
- &power_button_status)) && power_button_status != 0) {
- AcpiClearEvent(ACPI_EVENT_POWER_BUTTON);
- device_printf(sc->acpi_dev,
- "cleared fixed power button status\n");
- }
- }
-
- intr_restore(intr);
-
- /* call acpi_wakeup_machdep() again with interrupt enabled */
- acpi_wakeup_machdep(sc, acpi_sstate, sleep_result, 1);
-
- AcpiLeaveSleepStatePrep(acpi_sstate);
-
- if (sleep_result == -1)
- goto backout;
-
- /* Re-enable ACPI hardware on wakeup from hibernate. */
- if (stype == POWER_STYPE_HIBERNATE)
- AcpiEnable();
- } else {
- status = AcpiEnterSleepState(acpi_sstate);
- intr_restore(intr);
- AcpiLeaveSleepStatePrep(acpi_sstate);
- if (ACPI_FAILURE(status)) {
- device_printf(sc->acpi_dev, "AcpiEnterSleepState failed - %s\n",
- AcpiFormatException(status));
- goto backout;
- }
+ switch (stype) {
+ case POWER_STYPE_STANDBY:
+ do_standby(sc, &slp_state, intr);
+ break;
+ case POWER_STYPE_SUSPEND_TO_MEM:
+ case POWER_STYPE_HIBERNATE:
+ do_sleep(sc, &slp_state, intr, acpi_sstate);
+ break;
+ case POWER_STYPE_SUSPEND_TO_IDLE:
+ do_idle(sc, &slp_state, intr);
+ break;
+ case POWER_STYPE_AWAKE:
+ case POWER_STYPE_POWEROFF:
+ case POWER_STYPE_COUNT:
+ case POWER_STYPE_UNKNOWN:
+ __unreachable();
}
- slp_state = ACPI_SS_SLEPT;
/*
* Back out state according to how far along we got in the suspend
* process. This handles both the error and success cases.
*/
backout:
- if (slp_state >= ACPI_SS_SLP_PREP)
+ if (slp_state & ACPI_SS_SLP_PREP) {
resumeclock();
- if (slp_state >= ACPI_SS_GPE_SET) {
+ slp_state &= ~ACPI_SS_SLP_PREP;
+ }
+ if (slp_state & ACPI_SS_GPE_SET) {
acpi_wake_prep_walk(sc, stype);
sc->acpi_stype = POWER_STYPE_AWAKE;
+ slp_state &= ~ACPI_SS_GPE_SET;
}
- if (slp_state >= ACPI_SS_DEV_SUSPEND)
+ if (slp_state & ACPI_SS_DEV_SUSPEND) {
DEVICE_RESUME(root_bus);
- if (slp_state >= ACPI_SS_SLP_PREP)
+ slp_state &= ~ACPI_SS_DEV_SUSPEND;
+ }
+ if (stype != POWER_STYPE_SUSPEND_TO_IDLE && slp_state & ACPI_SS_SLP_PREP) {
AcpiLeaveSleepState(acpi_sstate);
- if (slp_state >= ACPI_SS_SLEPT) {
+ slp_state &= ~ACPI_SS_SLP_PREP;
+ }
+ if (slp_state & ACPI_SS_SLEPT) {
#if defined(__i386__) || defined(__amd64__)
/* NB: we are still using ACPI timecounter at this point. */
resume_TSC();
#endif
acpi_resync_clock(sc);
acpi_enable_fixed_events(sc);
+ slp_state &= ~ACPI_SS_SLEPT;
}
sc->acpi_next_stype = POWER_STYPE_AWAKE;
+ MPASS(slp_state == ACPI_SS_NONE);
+
bus_topo_unlock();
#ifdef EARLY_AP_STARTUP
diff --git a/sys/x86/include/intr_machdep.h b/sys/x86/include/intr_machdep.h
--- a/sys/x86/include/intr_machdep.h
+++ b/sys/x86/include/intr_machdep.h
@@ -150,6 +150,7 @@
int intr_remove_handler(void *cookie);
void intr_resume(bool suspend_cancelled);
void intr_suspend(void);
+void intr_enable_src(u_int irq);
void intr_reprogram(void);
void intrcnt_add(const char *name, u_long **countp);
void nexus_add_irq(u_long irq);
diff --git a/sys/x86/x86/intr_machdep.c b/sys/x86/x86/intr_machdep.c
--- a/sys/x86/x86/intr_machdep.c
+++ b/sys/x86/x86/intr_machdep.c
@@ -385,6 +385,15 @@
mtx_unlock(&intrpic_lock);
}
+void
+intr_enable_src(u_int irq)
+{
+ struct intsrc *is;
+
+ is = interrupt_sources[irq];
+ is->is_pic->pic_enable_source(is);
+}
+
static int
intr_assign_cpu(void *arg, int cpu)
{

File Metadata

Mime Type
text/plain
Expires
Sun, Apr 5, 7:33 AM (13 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28258649
Default Alt Text
D48734.1775374406.diff (13 KB)

Event Timeline