Page MenuHomeFreeBSD

D17675.diff
No OneTemporary

D17675.diff

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

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)

Event Timeline