Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F108576682
D17675.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
34 KB
Referenced Files
None
Subscribers
None
D17675.diff
View Options
Index: sys/dev/acpica/Osd/OsdInterrupt.c
===================================================================
--- sys/dev/acpica/Osd/OsdInterrupt.c
+++ sys/dev/acpica/Osd/OsdInterrupt.c
@@ -218,3 +218,9 @@
InterruptOverride = InterruptNumber;
return_ACPI_STATUS (AE_OK);
}
+
+UINT32
+acpi_GetSciInterrupt(void)
+{
+ return InterruptOverride ?: AcpiGbl_FADT.SciInterrupt;
+}
Index: sys/dev/acpica/acpi.c
===================================================================
--- sys/dev/acpica/acpi.c
+++ sys/dev/acpica/acpi.c
@@ -54,6 +54,7 @@
#if defined(__i386__) || defined(__amd64__)
#include <machine/clock.h>
#include <machine/pci_cfgreg.h>
+#include <machine/intr_machdep.h>
#endif
#include <machine/resource.h>
#include <machine/bus.h>
@@ -95,6 +96,14 @@
int num;
};
+enum s2idle_enable {
+ POWER_BUTTON_S2I,
+ SLEEP_BUTTON_S2I,
+ LID_SWITCH_S2I,
+ STANDBY_S2I,
+ SUSPEND_S2I
+};
+
static char *sysres_ids[] = { "PNP0C01", "PNP0C02", NULL };
static char *pcilink_ids[] = { "PNP0C0F", NULL };
@@ -157,7 +166,7 @@
void *context, void **status);
static void acpi_sleep_enable(void *arg);
static ACPI_STATUS acpi_sleep_disable(struct acpi_softc *sc);
-static ACPI_STATUS acpi_EnterSleepState(struct acpi_softc *sc, int state);
+static ACPI_STATUS acpi_EnterSleepState(struct acpi_softc *sc, enum sleep_type stype);
static void acpi_shutdown_final(void *arg, int howto);
static void acpi_enable_fixed_events(struct acpi_softc *sc);
static BOOLEAN acpi_has_hid(ACPI_HANDLE handle);
@@ -167,8 +176,9 @@
static int acpi_wake_prep_walk(int sstate);
static int acpi_wake_sysctl_walk(device_t dev);
static int acpi_wake_set_sysctl(SYSCTL_HANDLER_ARGS);
-static void acpi_system_eventhandler_sleep(void *arg, int state);
-static void acpi_system_eventhandler_wakeup(void *arg, int state);
+static void acpi_system_eventhandler_sleep(void *arg, enum sleep_type type);
+static void acpi_system_eventhandler_wakeup(void *arg, enum sleep_type type);
+static int acpi_stype2sstate(enum sleep_type type);
static int acpi_sname2sstate(const char *sname);
static const char *acpi_sstate2sname(int sstate);
static int acpi_supported_sleep_state_sysctl(SYSCTL_HANDLER_ARGS);
@@ -561,22 +571,22 @@
"List supported ACPI sleep states.");
SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
OID_AUTO, "power_button_state", CTLTYPE_STRING | CTLFLAG_RW,
- &sc->acpi_power_button_sx, 0, acpi_sleep_state_sysctl, "A",
- "Power button ACPI sleep state.");
+ &sc->acpi_power_button.sx, POWER_BUTTON_S2I, acpi_sleep_state_sysctl,
+ "A", "Power button ACPI sleep state.");
SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
OID_AUTO, "sleep_button_state", CTLTYPE_STRING | CTLFLAG_RW,
- &sc->acpi_sleep_button_sx, 0, acpi_sleep_state_sysctl, "A",
- "Sleep button ACPI sleep state.");
+ &sc->acpi_sleep_button.sx, SLEEP_BUTTON_S2I, acpi_sleep_state_sysctl,
+ "A", "Sleep button ACPI sleep state.");
SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
OID_AUTO, "lid_switch_state", CTLTYPE_STRING | CTLFLAG_RW,
- &sc->acpi_lid_switch_sx, 0, acpi_sleep_state_sysctl, "A",
+ &sc->acpi_lid_switch.sx, LID_SWITCH_S2I, acpi_sleep_state_sysctl, "A",
"Lid ACPI sleep state. Set to S3 if you want to suspend your laptop when close the Lid.");
SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
OID_AUTO, "standby_state", CTLTYPE_STRING | CTLFLAG_RW,
- &sc->acpi_standby_sx, 0, acpi_sleep_state_sysctl, "A", "");
+ &sc->acpi_standby.sx, STANDBY_S2I, acpi_sleep_state_sysctl, "A", "");
SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
OID_AUTO, "suspend_state", CTLTYPE_STRING | CTLFLAG_RW,
- &sc->acpi_suspend_sx, 0, acpi_sleep_state_sysctl, "A", "");
+ &sc->acpi_suspend.sx, SUSPEND_S2I, acpi_sleep_state_sysctl, "A", "");
SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
OID_AUTO, "sleep_delay", CTLFLAG_RW, &sc->acpi_sleep_delay, 0,
"sleep delay in seconds");
@@ -626,19 +636,19 @@
* Dispatch the default sleep state to devices. The lid switch is set
* to UNKNOWN by default to avoid surprising users.
*/
- sc->acpi_power_button_sx = acpi_sleep_states[ACPI_STATE_S5] ?
+ sc->acpi_power_button.sx = acpi_sleep_states[ACPI_STATE_S5] ?
ACPI_STATE_S5 : ACPI_STATE_UNKNOWN;
- sc->acpi_lid_switch_sx = ACPI_STATE_UNKNOWN;
- sc->acpi_standby_sx = acpi_sleep_states[ACPI_STATE_S1] ?
+ sc->acpi_lid_switch.sx = ACPI_STATE_UNKNOWN;
+ sc->acpi_standby.sx = acpi_sleep_states[ACPI_STATE_S1] ?
ACPI_STATE_S1 : ACPI_STATE_UNKNOWN;
- sc->acpi_suspend_sx = acpi_sleep_states[ACPI_STATE_S3] ?
+ sc->acpi_suspend.sx = acpi_sleep_states[ACPI_STATE_S3] ?
ACPI_STATE_S3 : ACPI_STATE_UNKNOWN;
/* Pick the first valid sleep state for the sleep button default. */
- sc->acpi_sleep_button_sx = ACPI_STATE_UNKNOWN;
+ sc->acpi_sleep_button.sx = ACPI_STATE_UNKNOWN;
for (state = ACPI_STATE_S1; state <= ACPI_STATE_S4; state++)
if (acpi_sleep_states[state]) {
- sc->acpi_sleep_button_sx = state;
+ sc->acpi_sleep_button.sx = state;
break;
}
@@ -663,8 +673,8 @@
/* Flag our initial states. */
sc->acpi_enabled = TRUE;
- sc->acpi_sstate = ACPI_STATE_S0;
- sc->acpi_sleep_disabled = TRUE;
+ sc->acpi_sstate = AWAKE;
+ sc->acpi_repressed_states.flags |= 1 << ACPI_SLEEP_DISABLED;
/* Create the control device */
sc->acpi_dev_t = make_dev(&acpi_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0664,
@@ -1704,7 +1714,10 @@
* Note illegal _S0D is evaluated because some systems expect this.
*/
sc = device_get_softc(bus);
- snprintf(sxd, sizeof(sxd), "_S%dD", sc->acpi_sstate);
+
+ snprintf(sxd, sizeof(sxd), "_S%dD",
+ sc->acpi_sstate != SUSPEND_TO_IDLE ?: SUSPEND);
+ MPASS(sxd[2] < '4'); /* only up to S3 supported */
status = acpi_GetInteger(handle, sxd, dstate);
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
device_printf(dev, "failed to get %s on %s: %s\n", sxd,
@@ -2751,7 +2764,8 @@
"warning: acpi_SetSleepState() deprecated, need to update your software\n");
once = 1;
}
- return (acpi_EnterSleepState(sc, state));
+ MPASS(state < ACPI_S_STATES_MAX);
+ return (acpi_EnterSleepState(sc, (enum sleep_type) state));
}
#if defined(__amd64__) || defined(__i386__)
@@ -2780,8 +2794,58 @@
acpi_sleep_force_task, sc)))
device_printf(sc->acpi_dev, "AcpiOsExecute() for sleeping failed\n");
}
+
+static enum sleep_type
+select_sleep_type(struct acpi_softc *sc, int state)
+{
+ MPASS(state == ACPI_STATE_S3 || state == ACPI_STATE_S1);
+
+ /* The user explicitly requested */
+ if (state == ACPI_STATE_S3 &&
+ sc->acpi_suspend.sx == SUSPEND_TO_IDLE)
+ return SUSPEND_TO_IDLE;
+
+ if (state == ACPI_STATE_S1 &&
+ sc->acpi_standby.sx == SUSPEND_TO_IDLE)
+ return SUSPEND_TO_IDLE;
+
+ /* The system supports the HW state, and that's probably best */
+ if (acpi_sleep_states[state])
+ return (enum sleep_type)state;
+
+ /* idle is the best we can do. */
+ device_printf(sc->acpi_dev,
+ "Requested S%d, but using S0IDLE instead\n", state);
+ return SUSPEND_TO_IDLE;
+}
+#else
+static enum sleep_type
+select_sleep_type(struct acpi_softc *sc)
+{
+ return AWAKE;
+}
#endif
+/* Given a sleep type and a state, figures out if an error is warranted */
+static int
+sanitize_sstate(struct acpi_softc *sc, enum sleep_type stype, int state,
+ bool acpi_err)
+{
+ if (stype == SUSPEND_TO_IDLE)
+ goto out;
+
+ if (state < ACPI_STATE_S1 || state > ACPI_S_STATES_MAX)
+ return acpi_err ? (AE_BAD_PARAMETER) : (EINVAL);
+ if (!acpi_sleep_states[state]) {
+ device_printf(sc->acpi_dev,
+ "Sleep state S%d not supported by BIOS\n", state);
+ return acpi_err ? (AE_SUPPORT) : (EOPNOTSUPP);
+ }
+
+out:
+ return acpi_err ? (AE_OK) : (0);
+}
+
/*
* Request that the system enter the given suspend state. All /dev/apm
* devices and devd(8) will be notified. Userland then has a chance to
@@ -2789,38 +2853,40 @@
* acks are in.
*/
int
-acpi_ReqSleepState(struct acpi_softc *sc, int state)
+acpi_ReqSleepState(struct acpi_softc *sc, enum sleep_type stype)
{
#if defined(__amd64__) || defined(__i386__)
struct apm_clone_data *clone;
ACPI_STATUS status;
+ int sstate, err;
- if (state < ACPI_STATE_S1 || state > ACPI_S_STATES_MAX)
- return (EINVAL);
- if (!acpi_sleep_states[state])
- return (EOPNOTSUPP);
+ sstate = acpi_stype2sstate(stype);
+
+ err = sanitize_sstate(sc, stype, sstate, false);
+ if (err)
+ return err;
/*
* If a reboot/shutdown/suspend request is already in progress or
* suspend is blocked due to an upcoming shutdown, just return.
*/
- if (rebooting || sc->acpi_next_sstate != 0 || suspend_blocked) {
+ if (rebooting || sc->acpi_next_sstate != AWAKE || suspend_blocked) {
return (0);
}
/* Wait until sleep is enabled. */
- while (sc->acpi_sleep_disabled) {
+ while (sc->acpi_repressed_states.flags & (1 <<ACPI_SLEEP_DISABLED)) {
AcpiOsSleep(1000);
}
ACPI_LOCK(acpi);
- sc->acpi_next_sstate = state;
+ sc->acpi_next_sstate = stype;
/* S5 (soft-off) should be entered directly with no waiting. */
- if (state == ACPI_STATE_S5) {
+ if (stype == POWEROFF) {
ACPI_UNLOCK(acpi);
- status = acpi_EnterSleepState(sc, state);
+ status = acpi_EnterSleepState(sc, stype);
return (ACPI_SUCCESS(status) ? 0 : ENXIO);
}
@@ -2836,7 +2902,7 @@
/* If devd(8) is not running, immediately enter the sleep state. */
if (!devctl_process_running()) {
ACPI_UNLOCK(acpi);
- status = acpi_EnterSleepState(sc, state);
+ status = acpi_EnterSleepState(sc, stype);
return (ACPI_SUCCESS(status) ? 0 : ENXIO);
}
@@ -2851,11 +2917,14 @@
ACPI_UNLOCK(acpi);
/* Now notify devd(8) also. */
- acpi_UserNotify("Suspend", ACPI_ROOT_OBJECT, state);
+ acpi_UserNotify("Suspend", ACPI_ROOT_OBJECT, stype);
return (0);
#else
- /* This platform does not support acpi suspend/resume. */
+ /*
+ * This platform does not support acpi suspend/resume.
+ * TODO: Support s2idle here since it's platform agnostic.
+ */
return (EOPNOTSUPP);
#endif
}
@@ -2877,14 +2946,14 @@
/* If no pending sleep state, return an error. */
ACPI_LOCK(acpi);
sc = clone->acpi_sc;
- if (sc->acpi_next_sstate == 0) {
+ if (sc->acpi_next_sstate == AWAKE) {
ACPI_UNLOCK(acpi);
return (ENXIO);
}
/* Caller wants to abort suspend process. */
if (error) {
- sc->acpi_next_sstate = 0;
+ sc->acpi_next_sstate = AWAKE;
callout_stop(&sc->susp_force_to);
device_printf(sc->acpi_dev,
"listener on %s cancelled the pending suspend\n",
@@ -2919,7 +2988,10 @@
}
return (ret);
#else
- /* This platform does not support acpi suspend/resume. */
+ /*
+ * This platform does not support acpi suspend/resume.
+ * TODO: Support s2idle here since it's platform agnostic.
+ */
return (EOPNOTSUPP);
#endif
}
@@ -2937,24 +3009,20 @@
return;
}
- sc->acpi_sleep_disabled = FALSE;
+ atomic_clear_int(&sc->acpi_repressed_states.flags, 1 << ACPI_SLEEP_DISABLED);
}
static ACPI_STATUS
acpi_sleep_disable(struct acpi_softc *sc)
{
- ACPI_STATUS status;
-
/* Fail if the system is not fully up and running. */
if (!AcpiGbl_SystemAwakeAndRunning)
return (AE_ERROR);
- ACPI_LOCK(acpi);
- status = sc->acpi_sleep_disabled ? AE_ERROR : AE_OK;
- sc->acpi_sleep_disabled = TRUE;
- ACPI_UNLOCK(acpi);
+ if (atomic_testandset_int(&sc->acpi_repressed_states.flags, ACPI_SLEEP_DISABLED))
+ return AE_ERROR;
- return (status);
+ return AE_OK;
}
enum acpi_sleep_state {
@@ -2965,29 +3033,119 @@
ACPI_SS_SLEPT,
};
+static void
+acpi_state_transition_disable(struct acpi_softc *sc)
+{
+ int tmp;
+ tmp = atomic_swap_int(&sc->acpi_repressed_states.flags, ~0);
+
+ /* Disallow re-entrancy. Disabling sleep with this is allowed though. */
+ if (tmp & ~(1 << ACPI_SLEEP_DISABLED)) {
+ device_printf(sc->acpi_dev, "Invalid saved power flags");
+ tmp &= (1 << ACPI_SLEEP_DISABLED);
+ }
+ sc->acpi_repressed_states.saved_flags = tmp;
+}
+
+static void
+acpi_state_transition_enable(struct acpi_softc *sc)
+{
+#ifdef INVARIANTS
+ int tmp;
+ tmp = atomic_swap_int(&sc->acpi_repressed_states.flags, sc->acpi_repressed_states.saved_flags);
+ MPASS(tmp == ~0);
+#else
+ atomic_store_int(&sc->acpi_repressed_states.flags, sc->sc->acpi_repressed_states.saved_flags);
+#endif
+
+ sc->acpi_repressed_states.saved_flags = 0;
+}
+
+BOOLEAN
+acpi_PowerTransitionIsEnabled()
+{
+ struct acpi_softc *sc = devclass_get_softc(devclass_find("acpi"), 0);
+ MPASS(sc != NULL);
+ return (sc->acpi_repressed_states.flags & (1 << ACPI_POWER_DISABLED)) == 0;
+}
+
+static void
+__do_idle(struct acpi_softc *sc)
+{
+ register_t intr;
+
+ intr = intr_disable();
+ intr_suspend();
+ /* XXX: Is this actually required? */
+ intr_enable_src(acpi_GetSciInterrupt());
+ acpi_state_transition_disable(sc);
+ cpu_idle(0);
+ acpi_state_transition_enable(sc);
+ intr_resume(false);
+ intr_restore(intr);
+}
+
+static void
+__do_sleep(struct acpi_softc *sc, int state, enum acpi_sleep_state *pass)
+{
+ int sleep_result;
+ register_t intr;
+
+ MPASS(state == ACPI_STATE_S3 || state == ACPI_STATE_S4);
+
+ intr = intr_disable();
+
+ sleep_result = acpi_sleep_machdep(sc, state);
+ acpi_wakeup_machdep(sc, state, sleep_result, 0);
+
+ if (sleep_result == 1 && state == ACPI_STATE_S3) {
+ /*
+ * 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);
+ }
+
+ intr_restore(intr);
+
+ /* 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();
+
+ *pass = ACPI_SS_SLEPT;
+}
+
/*
* Enter the desired system sleep state.
*
* Currently we support S1-S5 but S4 is only S4BIOS
*/
static ACPI_STATUS
-acpi_EnterSleepState(struct acpi_softc *sc, int state)
+acpi_EnterSleepState(struct acpi_softc *sc, enum sleep_type stype)
{
- register_t intr;
ACPI_STATUS status;
- ACPI_EVENT_STATUS power_button_status;
enum acpi_sleep_state slp_state;
- int sleep_result;
+ int state, err;
- ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state);
+ ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, stype);
- if (state < ACPI_STATE_S1 || state > ACPI_S_STATES_MAX)
- return_ACPI_STATUS (AE_BAD_PARAMETER);
- if (!acpi_sleep_states[state]) {
- device_printf(sc->acpi_dev, "Sleep state S%d not supported by BIOS\n",
- state);
- return (AE_SUPPORT);
- }
+ state = acpi_stype2sstate(stype);
+
+ err = sanitize_sstate(sc, stype, state, true);
+ if (err)
+ return_ACPI_STATUS (err);
/* Re-entry once we're suspending is not allowed. */
status = acpi_sleep_disable(sc);
@@ -2997,7 +3155,7 @@
return (status);
}
- if (state == ACPI_STATE_S5) {
+ if (stype == POWEROFF) {
/*
* Shut down cleanly and power off. This will call us back through the
* shutdown handlers.
@@ -3010,18 +3168,12 @@
stop_all_proc();
EVENTHANDLER_INVOKE(power_suspend);
-#ifdef EARLY_AP_STARTUP
MPASS(mp_ncpus == 1 || smp_started);
- thread_lock(curthread);
- sched_bind(curthread, 0);
- thread_unlock(curthread);
-#else
if (smp_started) {
thread_lock(curthread);
sched_bind(curthread, 0);
thread_unlock(curthread);
}
-#endif
/*
* Be sure to hold Giant across DEVICE_SUSPEND/RESUME since non-MPSAFE
@@ -3031,7 +3183,7 @@
slp_state = ACPI_SS_NONE;
- sc->acpi_sstate = state;
+ sc->acpi_sstate = stype;
/* Enable any GPEs as appropriate and requested by the user. */
acpi_wake_prep_walk(state);
@@ -3051,11 +3203,13 @@
}
slp_state = ACPI_SS_DEV_SUSPEND;
- status = AcpiEnterSleepStatePrep(state);
- if (ACPI_FAILURE(status)) {
- device_printf(sc->acpi_dev, "AcpiEnterSleepStatePrep failed - %s\n",
- AcpiFormatException(status));
- goto backout;
+ if (stype != SUSPEND_TO_IDLE) {
+ status = AcpiEnterSleepStatePrep(state);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->acpi_dev, "AcpiEnterSleepStatePrep failed - %s\n",
+ AcpiFormatException(status));
+ goto backout;
+ }
}
slp_state = ACPI_SS_SLP_PREP;
@@ -3063,70 +3217,31 @@
DELAY(sc->acpi_sleep_delay * 1000000);
suspendclock();
- intr = intr_disable();
- if (state != ACPI_STATE_S1) {
- sleep_result = acpi_sleep_machdep(sc, state);
- acpi_wakeup_machdep(sc, state, 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 && state != ACPI_STATE_S4)
- AcpiWriteBitRegister(ACPI_BITREG_SCI_ENABLE, ACPI_ENABLE_EVENT);
-
- if (sleep_result == 1 && state == ACPI_STATE_S3) {
- /*
- * 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, state, sleep_result, 1);
-
- AcpiLeaveSleepStatePrep(state);
-
- if (sleep_result == -1)
- goto backout;
-
- /* Re-enable ACPI hardware on wakeup from sleep state 4. */
- if (state == ACPI_STATE_S4)
- AcpiEnable();
- } else {
- status = AcpiEnterSleepState(state);
- intr_restore(intr);
- AcpiLeaveSleepStatePrep(state);
- if (ACPI_FAILURE(status)) {
- device_printf(sc->acpi_dev, "AcpiEnterSleepState failed - %s\n",
- AcpiFormatException(status));
- goto backout;
+ switch (stype) {
+ case STANDBY:
+ {
+ register_t intr = intr_disable();
+ status = AcpiEnterSleepState(state);
+ intr_restore(intr);
+ AcpiLeaveSleepStatePrep(state);
+ if (ACPI_FAILURE(status))
+ device_printf(sc->acpi_dev, "AcpiEnterSleepState failed - %s\n",
+ AcpiFormatException(status));
+ slp_state = ACPI_SS_SLEPT;
}
+ break;
+ case SUSPEND:
+ case HIBERNATE:
+ __do_sleep(sc, state, &slp_state);
+ break;
+ case SUSPEND_TO_IDLE:
+ __do_idle(sc);
+ break;
+ case AWAKE:
+ case POWEROFF:
+ default:
+ __unreachable();
}
- slp_state = ACPI_SS_SLEPT;
/*
* Back out state according to how far along we got in the suspend
@@ -3137,11 +3252,12 @@
resumeclock();
if (slp_state >= ACPI_SS_GPE_SET) {
acpi_wake_prep_walk(state);
- sc->acpi_sstate = ACPI_STATE_S0;
+ sc->acpi_sstate = AWAKE;
}
if (slp_state >= ACPI_SS_DEV_SUSPEND)
DEVICE_RESUME(root_bus);
- if (slp_state >= ACPI_SS_SLP_PREP)
+
+ if (stype != SUSPEND_TO_IDLE && (slp_state >= ACPI_SS_SLP_PREP))
AcpiLeaveSleepState(state);
if (slp_state >= ACPI_SS_SLEPT) {
#if defined(__i386__) || defined(__amd64__)
@@ -3151,21 +3267,15 @@
acpi_resync_clock(sc);
acpi_enable_fixed_events(sc);
}
- sc->acpi_next_sstate = 0;
+ sc->acpi_next_sstate = AWAKE;
mtx_unlock(&Giant);
-#ifdef EARLY_AP_STARTUP
- thread_lock(curthread);
- sched_unbind(curthread);
- thread_unlock(curthread);
-#else
if (smp_started) {
thread_lock(curthread);
sched_unbind(curthread);
thread_unlock(curthread);
}
-#endif
resume_all_proc();
@@ -3478,33 +3588,42 @@
/* System Event Handlers (registered by EVENTHANDLER_REGISTER) */
static void
-acpi_system_eventhandler_sleep(void *arg, int state)
+acpi_system_eventhandler_sleep(void *arg, enum sleep_type type)
{
struct acpi_softc *sc = (struct acpi_softc *)arg;
int ret;
- ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state);
+ ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, type);
/* Check if button action is disabled or unknown. */
- if (state == ACPI_STATE_UNKNOWN)
+ if (type > SUSPEND_TO_IDLE)
+ return;
+
+ if (!acpi_PowerTransitionIsEnabled())
return;
/* Request that the system prepare to enter the given suspend state. */
- ret = acpi_ReqSleepState(sc, state);
- if (ret != 0)
+ ret = acpi_ReqSleepState(sc, type);
+ if (ret != 0) {
+ char state[2] = { 0 };
+ state[0] = type + '0';
device_printf(sc->acpi_dev,
- "request to enter state S%d failed (err %d)\n", state, ret);
+ "request to enter state S%s failed (err %d)\n",
+ type == SUSPEND_TO_IDLE ? "0IDLE" : state,
+ ret);
+ }
return_VOID;
}
static void
-acpi_system_eventhandler_wakeup(void *arg, int state)
+acpi_system_eventhandler_wakeup(void *arg, enum sleep_type type)
{
- ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state);
+ ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, type);
/* Currently, nothing to do for wakeup. */
+ MPASS(acpi_PowerTransitionIsEnabled());
return_VOID;
}
@@ -3534,7 +3653,7 @@
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER,
- acpi_invoke_sleep_eventhandler, &sc->acpi_power_button_sx)))
+ acpi_invoke_sleep_eventhandler, &sc->acpi_power_button.sx)))
return_VALUE (ACPI_INTERRUPT_NOT_HANDLED);
return_VALUE (ACPI_INTERRUPT_HANDLED);
}
@@ -3542,13 +3661,23 @@
UINT32
acpi_event_power_button_wake(void *context)
{
+ ACPI_STATUS status;
+ ACPI_EVENT_STATUS power_button_status;
struct acpi_softc *sc = (struct acpi_softc *)context;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
- if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER,
- acpi_invoke_wake_eventhandler, &sc->acpi_power_button_sx)))
+ status = AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_invoke_wake_eventhandler,
+ &sc->acpi_power_button.sx);
+ if (ACPI_FAILURE(status))
return_VALUE (ACPI_INTERRUPT_NOT_HANDLED);
+
+ status = AcpiGetEventStatus(ACPI_EVENT_POWER_BUTTON, &power_button_status);
+ if (ACPI_SUCCESS(status) && power_button_status != 0) {
+ AcpiClearEvent(ACPI_EVENT_POWER_BUTTON);
+ device_printf(sc->acpi_dev, "cleared fixed power button status\n");
+ }
+
return_VALUE (ACPI_INTERRUPT_HANDLED);
}
@@ -3560,7 +3689,7 @@
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER,
- acpi_invoke_sleep_eventhandler, &sc->acpi_sleep_button_sx)))
+ acpi_invoke_sleep_eventhandler, &sc->acpi_sleep_button.sx)))
return_VALUE (ACPI_INTERRUPT_NOT_HANDLED);
return_VALUE (ACPI_INTERRUPT_HANDLED);
}
@@ -3573,7 +3702,7 @@
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER,
- acpi_invoke_wake_eventhandler, &sc->acpi_sleep_button_sx)))
+ acpi_invoke_wake_eventhandler, &sc->acpi_sleep_button.sx)))
return_VALUE (ACPI_INTERRUPT_NOT_HANDLED);
return_VALUE (ACPI_INTERRUPT_HANDLED);
}
@@ -3801,7 +3930,7 @@
case ACPIIO_REQSLPSTATE:
state = *(int *)addr;
if (state != ACPI_STATE_S5)
- return (acpi_ReqSleepState(sc, state));
+ return (acpi_ReqSleepState(sc, select_sleep_type(sc, state)));
device_printf(sc->acpi_dev, "power off via acpi ioctl not supported\n");
error = EOPNOTSUPP;
break;
@@ -3815,7 +3944,7 @@
return (EINVAL);
if (!acpi_sleep_states[state])
return (EOPNOTSUPP);
- if (ACPI_FAILURE(acpi_SetSleepState(sc, state)))
+ if (ACPI_FAILURE(acpi_SetSleepState(sc, (enum sleep_type) state)))
error = ENXIO;
break;
default:
@@ -3826,6 +3955,15 @@
return (error);
}
+static int
+acpi_stype2sstate(enum sleep_type type)
+{
+ if (type == SUSPEND_TO_IDLE)
+ return ACPI_STATE_UNKNOWN;
+
+ return (int)type;
+}
+
static int
acpi_sname2sstate(const char *sname)
{
@@ -3864,6 +4002,10 @@
for (state = ACPI_STATE_S1; state < ACPI_S_STATE_COUNT; state++)
if (acpi_sleep_states[state])
sbuf_printf(&sb, "%s ", acpi_sstate2sname(state));
+#if defined(__i386__) || defined(__amd64__)
+ sbuf_printf(&sb, "S0IDLE ");
+#endif
+
sbuf_trim(&sb);
sbuf_finish(&sb);
error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
@@ -3871,9 +4013,87 @@
return (error);
}
+static int
+update_state(struct sysctl_oid *oidp, int new_state, int old_state)
+{
+ if (new_state < ACPI_STATE_S1)
+ return (EINVAL);
+ if (new_state < ACPI_S_STATE_COUNT && !acpi_sleep_states[new_state])
+ return (EOPNOTSUPP);
+ if (new_state != old_state)
+ *(int *)oidp->oid_arg1 = new_state;
+
+ return (0);
+}
+
static int
acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS)
{
+#if defined(__i386__) || defined(__amd64__)
+ struct acpi_softc *sc;
+ char sleep_state[10];
+ int error, new_state, old_state;
+ int *sx, *saved_sx;
+
+ old_state = *(int *)oidp->oid_arg1;
+
+#define SETUP_VARIABLES(event) do { \
+ sc = __containerof(__containerof(arg1, __typeof(sc->acpi_##event), sx), \
+ struct acpi_softc, acpi_##event); \
+ MPASS(sc); \
+ sx = &sc->acpi_##event.sx; \
+ MPASS(sx); \
+ saved_sx = &sc->acpi_##event.saved_sx; \
+ MPASS(saved_sx); \
+} while (0)
+
+ switch (arg2) {
+ case POWER_BUTTON_S2I:
+ SETUP_VARIABLES(power_button);
+ break;
+ case SLEEP_BUTTON_S2I:
+ SETUP_VARIABLES(sleep_button);
+ break;
+ case LID_SWITCH_S2I:
+ SETUP_VARIABLES(lid_switch);
+ break;
+ case STANDBY_S2I:
+ SETUP_VARIABLES(standby);
+ break;
+ case SUSPEND_S2I:
+ SETUP_VARIABLES(suspend);
+ break;
+ default:
+ __unreachable();
+ };
+#undef SETUP_VARIABLES
+
+ if (*sx == SUSPEND_TO_IDLE)
+ strlcpy(sleep_state, "S0IDLE", 7);
+ else
+ strlcpy(sleep_state, acpi_sstate2sname(old_state), sizeof(sleep_state));
+
+ error = sysctl_handle_string(oidp, sleep_state, sizeof(sleep_state), req);
+ if (error || req->newptr == NULL)
+ return (error);
+
+ if (*sx == acpi_sname2sstate(sleep_state))
+ return (0);
+
+ /* When "enabling" s0idle, leave don't touch the default */
+ if (!strncasecmp(sleep_state, "s0idle", 6) ||
+ !strncasecmp(sleep_state, "s2idle", 6)) {
+ *saved_sx = *sx;
+ *sx = SUSPEND_TO_IDLE;
+ return (0);
+ } else {
+ new_state = acpi_sname2sstate(sleep_state);
+ error = update_state(oidp, new_state, old_state);
+ if (!error)
+ *saved_sx = *sx;
+ return (error);
+ }
+#else
char sleep_state[10];
int error, new_state, old_state;
@@ -3882,14 +4102,10 @@
error = sysctl_handle_string(oidp, sleep_state, sizeof(sleep_state), req);
if (error == 0 && req->newptr != NULL) {
new_state = acpi_sname2sstate(sleep_state);
- if (new_state < ACPI_STATE_S1)
- return (EINVAL);
- if (new_state < ACPI_S_STATE_COUNT && !acpi_sleep_states[new_state])
- return (EOPNOTSUPP);
- if (new_state != old_state)
- *(int *)oidp->oid_arg1 = new_state;
+ error = update_state(oidp, new_state, old_state);
}
return (error);
+#endif
}
/* Inform devctl(4) when we receive a Notify. */
@@ -4256,12 +4472,13 @@
switch (state) {
case POWER_SLEEP_STATE_STANDBY:
- acpi_state = sc->acpi_standby_sx;
+ acpi_state = sc->acpi_standby.sx;
break;
case POWER_SLEEP_STATE_SUSPEND:
- acpi_state = sc->acpi_suspend_sx;
+ acpi_state = sc->acpi_suspend.sx;
break;
case POWER_SLEEP_STATE_HIBERNATE:
+ /* FIXME: Needs support for s2idle */
acpi_state = ACPI_STATE_S4;
break;
default:
Index: sys/dev/acpica/acpi_lid.c
===================================================================
--- sys/dev/acpica/acpi_lid.c
+++ sys/dev/acpica/acpi_lid.c
@@ -176,9 +176,9 @@
acpi_UserNotify("Lid", sc->lid_handle, sc->lid_status);
if (sc->lid_status == 0)
- EVENTHANDLER_INVOKE(acpi_sleep_event, acpi_sc->acpi_lid_switch_sx);
+ EVENTHANDLER_INVOKE(acpi_sleep_event, acpi_sc->acpi_lid_switch.sx);
else
- EVENTHANDLER_INVOKE(acpi_wakeup_event, acpi_sc->acpi_lid_switch_sx);
+ EVENTHANDLER_INVOKE(acpi_wakeup_event, acpi_sc->acpi_lid_switch.sx);
out:
ACPI_SERIAL_END(lid);
Index: sys/dev/acpica/acpivar.h
===================================================================
--- sys/dev/acpica/acpivar.h
+++ sys/dev/acpica/acpivar.h
@@ -49,35 +49,64 @@
#include <machine/bus.h>
#include <machine/resource.h>
+enum sleep_type {
+ AWAKE = ACPI_STATE_S0,
+ STANDBY = ACPI_STATE_S1,
+ SUSPEND = ACPI_STATE_S3,
+ HIBERNATE = ACPI_STATE_S4,
+ POWEROFF = ACPI_STATE_S5,
+ SUSPEND_TO_IDLE
+};
+
struct apm_clone_data;
struct acpi_softc {
device_t acpi_dev;
struct cdev *acpi_dev_t;
int acpi_enabled;
- int acpi_sstate;
- int acpi_sleep_disabled;
+ enum sleep_type acpi_sstate;
int acpi_resources_reserved;
struct sysctl_ctx_list acpi_sysctl_ctx;
struct sysctl_oid *acpi_sysctl_tree;
- int acpi_power_button_sx;
- int acpi_sleep_button_sx;
- int acpi_lid_switch_sx;
- int acpi_standby_sx;
- int acpi_suspend_sx;
+ struct {
+ int sx;
+ int saved_sx;
+ } acpi_power_button;
+ struct {
+ int sx;
+ int saved_sx;
+ } acpi_sleep_button;
+ struct {
+ int sx;
+ int saved_sx;
+ } acpi_lid_switch;
+ struct {
+ int sx;
+ int saved_sx;
+ } acpi_standby;
+ struct {
+ int sx;
+ int saved_sx;
+ } acpi_suspend;
int acpi_sleep_delay;
int acpi_s4bios;
int acpi_do_disable;
int acpi_verbose;
int acpi_handle_reboot;
+#define ACPI_SLEEP_DISABLED (0) /* S3, S4 */
+#define ACPI_POWER_DISABLED (1) /* S0, S5 */
+ struct {
+ int flags;
+ int saved_flags;
+ } acpi_repressed_states;
vm_offset_t acpi_wakeaddr;
vm_paddr_t acpi_wakephys;
- int acpi_next_sstate; /* Next suspend Sx state. */
+ enum sleep_type acpi_next_sstate; /* Next "sleep" state. */
struct apm_clone_data *acpi_clone; /* Pseudo-dev for devd(8). */
STAILQ_HEAD(,apm_clone_data) apm_cdevs; /* All apm/apmctl/acpi cdevs. */
struct callout susp_force_to; /* Force suspend if no acks. */
@@ -357,12 +386,15 @@
int revision, int count, uint32_t *caps_in,
uint32_t *caps_out, bool query);
ACPI_STATUS acpi_OverrideInterruptLevel(UINT32 InterruptNumber);
+UINT32 acpi_GetSciInterrupt(void);
ACPI_STATUS acpi_SetIntrModel(int model);
-int acpi_ReqSleepState(struct acpi_softc *sc, int state);
+int acpi_ReqSleepState(struct acpi_softc *sc,
+ enum sleep_type stype);
int acpi_AckSleepState(struct apm_clone_data *clone, int error);
ACPI_STATUS acpi_SetSleepState(struct acpi_softc *sc, int state);
int acpi_wake_set_enable(device_t dev, int enable);
int acpi_parse_prw(ACPI_HANDLE h, struct acpi_prw_data *prw);
+BOOLEAN acpi_PowerTransitionIsEnabled(void);
ACPI_STATUS acpi_Startup(void);
void acpi_UserNotify(const char *subsystem, ACPI_HANDLE h,
uint8_t notify);
Index: sys/x86/acpica/acpi_apm.c
===================================================================
--- sys/x86/acpica/acpi_apm.c
+++ sys/x86/acpica/acpi_apm.c
@@ -238,7 +238,7 @@
acpi_sc = clone->acpi_sc;
/* We are about to lose a reference so check if suspend should occur */
- if (acpi_sc->acpi_next_sstate != 0 &&
+ if (acpi_sc->acpi_next_sstate != AWAKE &&
clone->notify_status != APM_EV_ACKED)
acpi_AckSleepState(clone, 0);
@@ -286,10 +286,10 @@
case APMIO_SUSPEND:
if ((flag & FWRITE) == 0)
return (EPERM);
- if (acpi_sc->acpi_next_sstate == 0) {
- if (acpi_sc->acpi_suspend_sx != ACPI_STATE_S5) {
+ if (acpi_sc->acpi_next_sstate == AWAKE) {
+ if (acpi_sc->acpi_suspend.sx != ACPI_STATE_S5) {
error = acpi_ReqSleepState(acpi_sc,
- acpi_sc->acpi_suspend_sx);
+ acpi_sc->acpi_suspend.sx);
} else {
printf(
"power off via apm suspend not supported\n");
@@ -301,10 +301,10 @@
case APMIO_STANDBY:
if ((flag & FWRITE) == 0)
return (EPERM);
- if (acpi_sc->acpi_next_sstate == 0) {
- if (acpi_sc->acpi_standby_sx != ACPI_STATE_S5) {
+ if (acpi_sc->acpi_next_sstate == AWAKE) {
+ if (acpi_sc->acpi_standby.sx != ACPI_STATE_S5) {
error = acpi_ReqSleepState(acpi_sc,
- acpi_sc->acpi_standby_sx);
+ acpi_sc->acpi_standby.sx);
} else {
printf(
"power off via apm standby not supported\n");
@@ -316,10 +316,10 @@
case APMIO_NEXTEVENT:
printf("apm nextevent start\n");
ACPI_LOCK(acpi);
- if (acpi_sc->acpi_next_sstate != 0 && clone->notify_status ==
- APM_EV_NONE) {
+ if (acpi_sc->acpi_next_sstate != AWAKE &&
+ clone->notify_status == APM_EV_NONE) {
ev_info = (struct apm_event_info *)addr;
- if (acpi_sc->acpi_next_sstate <= ACPI_STATE_S3)
+ if (acpi_sc->acpi_next_sstate <= SUSPEND)
ev_info->type = PMEV_STANDBYREQ;
else
ev_info->type = PMEV_SUSPENDREQ;
Index: sys/x86/include/intr_machdep.h
===================================================================
--- sys/x86/include/intr_machdep.h
+++ sys/x86/include/intr_machdep.h
@@ -162,6 +162,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);
Index: sys/x86/x86/intr_machdep.c
===================================================================
--- sys/x86/x86/intr_machdep.c
+++ sys/x86/x86/intr_machdep.c
@@ -396,6 +396,19 @@
mtx_unlock(&intrpic_lock);
}
+/*
+ * This should not be called while pics are being removed, or while
+ * interrupt_sources is changing.
+ */
+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
Details
Attached
Mime Type
text/plain
Expires
Mon, Jan 27, 1:21 PM (5 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16197402
Default Alt Text
D17675.diff (34 KB)
Attached To
Mode
D17675: Suspend to idle support
Attached
Detach File
Event Timeline
Log In to Comment