summaryrefslogtreecommitdiffstats
path: root/sys/dev/acpica/acpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/acpica/acpi.c')
-rw-r--r--sys/dev/acpica/acpi.c199
1 files changed, 92 insertions, 107 deletions
diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
index 76a07e9..e7ffbe7 100644
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -123,10 +123,9 @@ static ACPI_STATUS acpi_probe_child(ACPI_HANDLE handle, UINT32 level,
static BOOLEAN acpi_MatchHid(ACPI_HANDLE h, const char *hid);
static void acpi_shutdown_final(void *arg, int howto);
static void acpi_enable_fixed_events(struct acpi_softc *sc);
-static int acpi_parse_prw(ACPI_HANDLE h, struct acpi_prw_data *prw);
-static ACPI_STATUS acpi_wake_limit(ACPI_HANDLE h, UINT32 level, void *context,
- void **status);
-static int acpi_wake_limit_walk(int sstate);
+static int acpi_wake_sleep_prep(ACPI_HANDLE handle, int sstate);
+static int acpi_wake_run_prep(ACPI_HANDLE handle, int sstate);
+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);
@@ -633,8 +632,9 @@ acpi_shutdown(device_t dev)
/* Allow children to shutdown first. */
bus_generic_shutdown(dev);
- /* Disable all wake GPEs not appropriate for reboot/poweroff. */
- acpi_wake_limit_walk(ACPI_STATE_S5);
+ /* Enable any GPEs that are able to power-on the system (i.e., RTC). */
+ acpi_wake_prep_walk(ACPI_STATE_S5);
+
return (0);
}
@@ -742,6 +742,9 @@ acpi_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
case ACPI_IVAR_PRIVATE:
*(void **)result = ad->ad_private;
break;
+ case ACPI_IVAR_FLAGS:
+ *(int *)result = ad->ad_flags;
+ break;
case ISA_IVAR_VENDORID:
case ISA_IVAR_SERIAL:
case ISA_IVAR_COMPATID:
@@ -777,6 +780,9 @@ acpi_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
case ACPI_IVAR_PRIVATE:
ad->ad_private = (void *)value;
break;
+ case ACPI_IVAR_FLAGS:
+ ad->ad_flags = (int)value;
+ break;
default:
panic("bad ivar write request (%d)", index);
return (ENOENT);
@@ -1216,10 +1222,6 @@ acpi_probe_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status)
acpi_set_handle(child, handle);
AcpiAttachData(handle, acpi_fake_objhandler, child);
- /* Check if the device can generate wake events. */
- if (ACPI_SUCCESS(AcpiEvaluateObject(handle, "_PRW", NULL, NULL)))
- device_set_flags(child, ACPI_FLAG_WAKE_CAPABLE);
-
/*
* Check that the device is present. If it's not present,
* leave it disabled (so that we have a device_t attached to
@@ -1766,8 +1768,8 @@ acpi_SetSleepState(struct acpi_softc *sc, int state)
sc->acpi_sstate = state;
sc->acpi_sleep_disabled = 1;
- /* Disable all wake GPEs not appropriate for this state. */
- acpi_wake_limit_walk(state);
+ /* Enable any GPEs as appropriate and requested by the user. */
+ acpi_wake_prep_walk(state);
/* Inform all devices that we are going to sleep. */
if (DEVICE_SUSPEND(root_bus) != 0) {
@@ -1813,6 +1815,9 @@ acpi_SetSleepState(struct acpi_softc *sc, int state)
break;
}
}
+
+ /* Resume devices, re-enable GPEs and fixed events. */
+ acpi_wake_prep_walk(state);
AcpiLeaveSleepState((UINT8)state);
DEVICE_RESUME(root_bus);
sc->acpi_sstate = ACPI_STATE_S0;
@@ -1844,10 +1849,6 @@ acpi_wake_init(device_t dev, int type)
{
struct acpi_prw_data prw;
- /* Check that the device can wake the system. */
- if ((device_get_flags(dev) & ACPI_FLAG_WAKE_CAPABLE) == 0)
- return (ENXIO);
-
/* Evaluate _PRW to find the GPE. */
if (acpi_parse_prw(acpi_get_handle(dev), &prw) != 0)
return (ENXIO);
@@ -1870,149 +1871,124 @@ acpi_wake_set_enable(device_t dev, int enable)
ACPI_STATUS status;
int flags;
- /* Make sure the device supports waking the system. */
- flags = device_get_flags(dev);
+ /* Make sure the device supports waking the system and get the GPE. */
handle = acpi_get_handle(dev);
- if ((flags & ACPI_FLAG_WAKE_CAPABLE) == 0 || handle == NULL)
- return (ENXIO);
-
- /* Evaluate _PRW to find the GPE. */
if (acpi_parse_prw(handle, &prw) != 0)
return (ENXIO);
+ flags = acpi_get_flags(dev);
if (enable) {
status = AcpiEnableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
if (ACPI_FAILURE(status)) {
device_printf(dev, "enable wake failed\n");
return (ENXIO);
}
- device_set_flags(dev, flags | ACPI_FLAG_WAKE_ENABLED);
+ acpi_set_flags(dev, flags | ACPI_FLAG_WAKE_ENABLED);
} else {
status = AcpiDisableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
if (ACPI_FAILURE(status)) {
device_printf(dev, "disable wake failed\n");
return (ENXIO);
}
- device_set_flags(dev, flags & ~ACPI_FLAG_WAKE_ENABLED);
+ acpi_set_flags(dev, flags & ~ACPI_FLAG_WAKE_ENABLED);
}
return (0);
}
-/* Configure a device's GPE appropriately for the new sleep state. */
-int
-acpi_wake_sleep_prep(device_t dev, int sstate)
+static int
+acpi_wake_sleep_prep(ACPI_HANDLE handle, int sstate)
{
struct acpi_prw_data prw;
- ACPI_HANDLE handle;
- int flags;
-
- /* Check that this is an ACPI device and get its GPE. */
- flags = device_get_flags(dev);
- handle = acpi_get_handle(dev);
- if ((flags & ACPI_FLAG_WAKE_CAPABLE) == 0 || handle == NULL)
- return (ENXIO);
+ device_t dev;
- /* Evaluate _PRW to find the GPE. */
+ /* Check that this is a wake-capable device and get its GPE. */
if (acpi_parse_prw(handle, &prw) != 0)
return (ENXIO);
+ dev = acpi_get_device(handle);
/*
- * TBD: All Power Resources referenced by elements 2 through N
- * of the _PRW object are put into the ON state.
- */
-
- /*
- * If the user requested that this device wake the system and the next
- * sleep state is valid for this GPE, enable it and the device's wake
- * capability. The sleep state must be less than (i.e., higher power)
- * or equal to the value specified by _PRW. Return early, leaving
- * the appropriate power resources enabled.
+ * The destination sleep state must be less than (i.e., higher power)
+ * or equal to the value specified by _PRW. If this GPE cannot be
+ * enabled for the next sleep state, then disable it. If it can and
+ * the user requested it be enabled, turn on any required power resources
+ * and set _PSW.
*/
- if ((flags & ACPI_FLAG_WAKE_ENABLED) != 0 &&
- sstate <= prw.lowest_wake) {
+ if (sstate > prw.lowest_wake) {
+ AcpiDisableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
if (bootverbose)
- device_printf(dev, "wake_prep enabled gpe %#x for state %d\n",
- prw.gpe_bit, sstate);
- AcpiEnableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
+ device_printf(dev, "wake_prep disabled wake for %s (S%d)\n",
+ acpi_name(handle), sstate);
+ } else if (dev && (acpi_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) != 0) {
+ acpi_pwr_wake_enable(handle, 1);
acpi_SetInteger(handle, "_PSW", 1);
- return (0);
+ if (bootverbose)
+ device_printf(dev, "wake_prep enabled for %s (S%d)\n",
+ acpi_name(handle), sstate);
}
- /*
- * If the device wake was disabled or this sleep state is too low for
- * this device, disable its wake capability and GPE.
- */
- AcpiDisableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
- acpi_SetInteger(handle, "_PSW", 0);
- if (bootverbose)
- device_printf(dev, "wake_prep disabled gpe %#x for state %d\n",
- prw.gpe_bit, sstate);
-
- /*
- * TBD: All Power Resources referenced by elements 2 through N
- * of the _PRW object are put into the OFF state.
- */
-
return (0);
}
-/* Re-enable GPEs after wake. */
-int
-acpi_wake_run_prep(device_t dev)
+static int
+acpi_wake_run_prep(ACPI_HANDLE handle, int sstate)
{
struct acpi_prw_data prw;
- ACPI_HANDLE handle;
- int flags;
-
- /* Check that this is an ACPI device and get its GPE. */
- flags = device_get_flags(dev);
- handle = acpi_get_handle(dev);
- if ((flags & ACPI_FLAG_WAKE_CAPABLE) == 0 || handle == NULL)
- return (ENXIO);
+ device_t dev;
- /* Evaluate _PRW to find the GPE. */
+ /*
+ * Check that this is a wake-capable device and get its GPE. Return
+ * now if the user didn't enable this device for wake.
+ */
if (acpi_parse_prw(handle, &prw) != 0)
return (ENXIO);
+ dev = acpi_get_device(handle);
+ if (dev == NULL || (acpi_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) == 0)
+ return (0);
/*
- * TBD: Be sure all Power Resources referenced by elements 2 through N
- * of the _PRW object are in the ON state.
+ * If this GPE couldn't be enabled for the previous sleep state, it was
+ * disabled before going to sleep so re-enable it. If it was enabled,
+ * clear _PSW and turn off any power resources it used.
*/
-
- /* Disable wake capability and if the user requested, enable the GPE. */
- acpi_SetInteger(handle, "_PSW", 0);
- if ((flags & ACPI_FLAG_WAKE_ENABLED) != 0)
+ if (sstate > prw.lowest_wake) {
AcpiEnableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
+ if (bootverbose)
+ device_printf(dev, "run_prep re-enabled %s\n", acpi_name(handle));
+ } else {
+ acpi_SetInteger(handle, "_PSW", 0);
+ acpi_pwr_wake_enable(handle, 0);
+ if (bootverbose)
+ device_printf(dev, "run_prep cleaned up for %s\n",
+ acpi_name(handle));
+ }
+
return (0);
}
static ACPI_STATUS
-acpi_wake_limit(ACPI_HANDLE h, UINT32 level, void *context, void **status)
+acpi_wake_prep(ACPI_HANDLE handle, UINT32 level, void *context, void **status)
{
- struct acpi_prw_data prw;
- int *sstate;
-
- /* It's ok not to have _PRW if the device can't wake the system. */
- if (acpi_parse_prw(h, &prw) != 0)
- return (AE_OK);
-
- sstate = (int *)context;
- if (*sstate > prw.lowest_wake)
- AcpiDisableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
+ int sstate;
+ /* If suspending, run the sleep prep function, otherwise wake. */
+ sstate = *(int *)context;
+ if (AcpiGbl_SystemAwakeAndRunning)
+ acpi_wake_sleep_prep(handle, sstate);
+ else
+ acpi_wake_run_prep(handle, sstate);
return (AE_OK);
}
-/* Walk all system devices, disabling them if necessary for sstate. */
+/* Walk the tree rooted at acpi0 to prep devices for suspend/resume. */
static int
-acpi_wake_limit_walk(int sstate)
+acpi_wake_prep_walk(int sstate)
{
ACPI_HANDLE sb_handle;
if (ACPI_SUCCESS(AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &sb_handle)))
- AcpiWalkNamespace(ACPI_TYPE_ANY, sb_handle, 100,
- acpi_wake_limit, &sstate, NULL);
+ AcpiWalkNamespace(ACPI_TYPE_DEVICE, sb_handle, 100,
+ acpi_wake_prep, &sstate, NULL);
return (0);
}
@@ -2023,21 +1999,23 @@ acpi_wake_sysctl_walk(device_t dev)
int error, i, numdevs;
device_t *devlist;
device_t child;
+ ACPI_STATUS status;
error = device_get_children(dev, &devlist, &numdevs);
if (error != 0 || numdevs == 0)
return (error);
for (i = 0; i < numdevs; i++) {
child = devlist[i];
+ acpi_wake_sysctl_walk(child);
if (!device_is_attached(child))
continue;
- if (device_get_flags(child) & ACPI_FLAG_WAKE_CAPABLE) {
+ status = AcpiEvaluateObject(acpi_get_handle(child), "_PRW", NULL, NULL);
+ if (ACPI_SUCCESS(status)) {
SYSCTL_ADD_PROC(device_get_sysctl_ctx(child),
SYSCTL_CHILDREN(device_get_sysctl_tree(child)), OID_AUTO,
"wake", CTLTYPE_INT | CTLFLAG_RW, child, 0,
acpi_wake_set_sysctl, "I", "Device set to wake the system");
}
- acpi_wake_sysctl_walk(child);
}
free(devlist, M_TEMP);
@@ -2052,7 +2030,7 @@ acpi_wake_set_sysctl(SYSCTL_HANDLER_ARGS)
device_t dev;
dev = (device_t)arg1;
- enable = (device_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) ? 1 : 0;
+ enable = (acpi_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) ? 1 : 0;
error = sysctl_handle_int(oidp, &enable, 0, req);
if (error != 0 || req->newptr == NULL)
@@ -2064,13 +2042,13 @@ acpi_wake_set_sysctl(SYSCTL_HANDLER_ARGS)
}
/* Parse a device's _PRW into a structure. */
-static int
+int
acpi_parse_prw(ACPI_HANDLE h, struct acpi_prw_data *prw)
{
ACPI_STATUS status;
ACPI_BUFFER prw_buffer;
ACPI_OBJECT *res, *res2;
- int error;
+ int error, i, power_count;
if (h == NULL || prw == NULL)
return (EINVAL);
@@ -2143,8 +2121,15 @@ acpi_parse_prw(ACPI_HANDLE h, struct acpi_prw_data *prw)
goto out;
}
- /* XXX No power resource handling yet. */
- prw->power_res = NULL;
+ /* Elements 2 to N of the _PRW object are power resources. */
+ power_count = res->Package.Count - 2;
+ if (power_count > ACPI_PRW_MAX_POWERRES) {
+ printf("ACPI device %s has too many power resources\n", acpi_name(h));
+ power_count = 0;
+ }
+ prw->power_res_count = power_count;
+ for (i = 0; i < power_count; i++)
+ prw->power_res[i] = res->Package.Elements[i];
out:
if (prw_buffer.Pointer != NULL)
OpenPOWER on IntegriCloud