summaryrefslogtreecommitdiffstats
path: root/sys/dev/acpica
diff options
context:
space:
mode:
authornjl <njl@FreeBSD.org>2004-05-27 18:38:45 +0000
committernjl <njl@FreeBSD.org>2004-05-27 18:38:45 +0000
commitfa603e929d266049001b489b9a22e7a556d4b6e0 (patch)
tree1817b677583b861f1119c6cedba1d7db92c18e9a /sys/dev/acpica
parent7db6667914bd21b54582207e0030bc1c2231f37e (diff)
downloadFreeBSD-src-fa603e929d266049001b489b9a22e7a556d4b6e0.zip
FreeBSD-src-fa603e929d266049001b489b9a22e7a556d4b6e0.tar.gz
Restructure the wake GPE API. Now there are three functions:
acpi_wake_init: Evaluate _PRW and set the GPE type acpi_wake_set_enable: Enable or disable a device's GPE. acpi_wake_sleep_prep: Perform any last-minute changes to the device to prepare it for entering the given sleep state. Also, walk the entire namespace when transitioning to a sleep state, disabling any GPEs which aren't appropriate for the given state. Transition acpi_lid and acpi_button to the new API. This clears the way for non-ACPI-aware devices to wake the system (i.e. modems) and fixes a problem where systems power up after shutdown when a GPE is triggered.
Diffstat (limited to 'sys/dev/acpica')
-rw-r--r--sys/dev/acpica/acpi.c356
-rw-r--r--sys/dev/acpica/acpi_button.c13
-rw-r--r--sys/dev/acpica/acpi_lid.c13
-rw-r--r--sys/dev/acpica/acpivar.h89
4 files changed, 285 insertions, 186 deletions
diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
index 53b643a..9a80c9a 100644
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -129,6 +129,10 @@ static void acpi_shutdown_pre_sync(void *arg, int howto);
static void acpi_shutdown_final(void *arg, int howto);
static void acpi_shutdown_poweroff(void *arg);
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 void acpi_system_eventhandler_sleep(void *arg, int state);
static void acpi_system_eventhandler_wakeup(void *arg, int state);
static int acpi_supported_sleep_state_sysctl(SYSCTL_HANDLER_ARGS);
@@ -667,7 +671,7 @@ acpi_add_child(device_t bus, int order, const char *name, int unit)
return (NULL);
resource_list_init(&ad->ad_rl);
-
+
child = device_add_child_ordered(bus, order, name, unit);
if (child != NULL)
device_set_ivars(child, ad);
@@ -1104,6 +1108,10 @@ acpi_probe_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status)
break;
acpi_set_handle(child, handle);
+ /* 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
@@ -1139,6 +1147,9 @@ acpi_shutdown_pre_sync(void *arg, int howto)
ACPI_ASSERTLOCK;
+ /* Disable all wake GPEs not appropriate for this state. */
+ acpi_wake_limit_walk(ACPI_STATE_S5);
+
/*
* Disable all ACPI events before soft off, otherwise the system
* will be turned on again on some laptops.
@@ -1681,6 +1692,9 @@ 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);
+
/* Inform all devices that we are going to sleep. */
if (DEVICE_SUSPEND(root_bus) != 0) {
/*
@@ -1750,6 +1764,227 @@ acpi_SetSleepState(struct acpi_softc *sc, int state)
return_ACPI_STATUS (status);
}
+/* Initialize a device's wake GPE. */
+int
+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);
+
+ /* Set the requested type for the GPE (runtime, wake, or both). */
+ if (ACPI_FAILURE(AcpiSetGpeType(prw.gpe_handle, prw.gpe_bit, type))) {
+ device_printf(dev, "set GPE type failed\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+/* Enable or disable the device's wake GPE. */
+int
+acpi_wake_set_enable(device_t dev, int enable)
+{
+ struct acpi_prw_data prw;
+ ACPI_HANDLE handle;
+ ACPI_STATUS status;
+ int flags;
+
+ /* Make sure the device supports waking the system. */
+ flags = device_get_flags(dev);
+ 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);
+
+ 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);
+ } 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);
+ }
+
+ return (0);
+}
+
+/* Configure a device's GPE appropriately for the new sleep state. */
+int
+acpi_wake_sleep_prep(device_t dev, int sstate)
+{
+ struct acpi_prw_data prw;
+ ACPI_HANDLE handle;
+
+ /* Check that this is an ACPI device and get its GPE. */
+ handle = acpi_get_handle(dev);
+ if (handle == NULL)
+ return (ENXIO);
+ if (acpi_parse_prw(handle, &prw) != 0)
+ return (ENXIO);
+
+ /*
+ * The sleeping state being entered must be less than (i.e., higher power)
+ * or equal to the value specified by _PRW. If not, disable this GPE.
+ */
+ if (sstate > prw.lowest_wake) {
+ if (bootverbose)
+ device_printf(dev, "wake_prep disabled gpe %#x for state %d\n",
+ prw.gpe_bit, sstate);
+ AcpiDisableGpe(prw.gpe_handle, prw.gpe_bit, ACPI_NOT_ISR);
+ acpi_SetInteger(handle, "_PSW", 0);
+ return (0);
+ }
+
+ /*
+ * If requested, enable the device's wake capability.
+ *
+ * TBD: All Power Resources referenced by elements 2 through N
+ * of the _PRW object are put into the ON state.
+ */
+ if ((device_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) != 0) {
+ if (bootverbose)
+ device_printf(dev, "wake_prep enabled _PSW for state %d\n", sstate);
+ acpi_SetInteger(handle, "_PSW", 1);
+ }
+
+ return (0);
+}
+
+static ACPI_STATUS
+acpi_wake_limit(ACPI_HANDLE h, 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);
+
+ return (AE_OK);
+}
+
+/* Walk all system devices, disabling them if necessary for sstate. */
+static int
+acpi_wake_limit_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, (void *)sstate, NULL);
+ return (0);
+}
+
+/* Parse a device's _PRW into a structure. */
+static 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;
+
+ if (h == NULL || prw == NULL)
+ return (EINVAL);
+
+ /*
+ * The _PRW object (7.2.9) is only required for devices that have the
+ * ability to wake the system from a sleeping state.
+ */
+ error = EINVAL;
+ prw_buffer.Pointer = NULL;
+ prw_buffer.Length = ACPI_ALLOCATE_BUFFER;
+ status = AcpiEvaluateObject(h, "_PRW", NULL, &prw_buffer);
+ if (ACPI_FAILURE(status))
+ return (ENOENT);
+ res = (ACPI_OBJECT *)prw_buffer.Pointer;
+ if (res == NULL)
+ return (ENOENT);
+ if (!ACPI_PKG_VALID(res, 2))
+ goto out;
+
+ /*
+ * Element 1 of the _PRW object:
+ * The lowest power system sleeping state that can be entered while still
+ * providing wake functionality. The sleeping state being entered must
+ * be less than (i.e., higher power) or equal to this value.
+ */
+ if (acpi_PkgInt32(res, 1, &prw->lowest_wake) != 0)
+ goto out;
+
+ /*
+ * Element 0 of the _PRW object:
+ */
+ switch (res->Package.Elements[0].Type) {
+ case ACPI_TYPE_INTEGER:
+ /*
+ * If the data type of this package element is numeric, then this
+ * _PRW package element is the bit index in the GPEx_EN, in the
+ * GPE blocks described in the FADT, of the enable bit that is
+ * enabled for the wake event.
+ */
+ prw->gpe_handle = NULL;
+ prw->gpe_bit = res->Package.Elements[0].Integer.Value;
+ error = 0;
+ break;
+ case ACPI_TYPE_PACKAGE:
+ /*
+ * If the data type of this package element is a package, then this
+ * _PRW package element is itself a package containing two
+ * elements. The first is an object reference to the GPE Block
+ * device that contains the GPE that will be triggered by the wake
+ * event. The second element is numeric and it contains the bit
+ * index in the GPEx_EN, in the GPE Block referenced by the
+ * first element in the package, of the enable bit that is enabled for
+ * the wake event.
+ *
+ * For example, if this field is a package then it is of the form:
+ * Package() {\_SB.PCI0.ISA.GPE, 2}
+ */
+ res2 = &res->Package.Elements[0];
+ if (!ACPI_PKG_VALID(res2, 2))
+ goto out;
+ prw->gpe_handle = acpi_GetReference(NULL, &res2->Package.Elements[0]);
+ if (prw->gpe_handle == NULL)
+ goto out;
+ if (acpi_PkgInt32(res2, 1, &prw->gpe_bit) != 0)
+ goto out;
+ error = 0;
+ break;
+ default:
+ goto out;
+ }
+
+ /* XXX No power resource handling yet. */
+ prw->power_res = NULL;
+
+out:
+ if (prw_buffer.Pointer != NULL)
+ AcpiOsFree(prw_buffer.Pointer);
+ return (error);
+}
+
/*
* Enable/Disable ACPI
*/
@@ -1977,125 +2212,6 @@ acpi_disabled(char *subsys)
}
/*
- * Device wake capability enable/disable.
- */
-void
-acpi_device_enable_wake_capability(ACPI_HANDLE h, int enable)
-{
- /*
- * TBD: All Power Resources referenced by elements 2 through N
- * of the _PRW object are put into the ON state.
- */
-
- (void)acpi_SetInteger(h, "_PSW", enable);
-}
-
-void
-acpi_device_enable_wake_event(ACPI_HANDLE h)
-{
- struct acpi_softc *sc;
- uint32_t gpe_bit, lowest_wake;
- ACPI_HANDLE handle;
- ACPI_STATUS status;
- ACPI_BUFFER prw_buffer;
- ACPI_OBJECT *res, *res2;
-
- ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-
- sc = devclass_get_softc(acpi_devclass, 0);
- if (sc == NULL)
- return;
-
- /*
- * The _PRW object (7.2.9) is only required for devices that have the
- * ability to wake the system from a sleeping state.
- */
- prw_buffer.Pointer = NULL;
- prw_buffer.Length = ACPI_ALLOCATE_BUFFER;
- status = AcpiEvaluateObject(h, "_PRW", NULL, &prw_buffer);
- if (ACPI_FAILURE(status))
- return;
- res = (ACPI_OBJECT *)prw_buffer.Pointer;
- if (res == NULL)
- return;
- if (!ACPI_PKG_VALID(res, 2))
- goto out;
-
- /*
- * Element 1 of the _PRW object:
- * The lowest power system sleeping state that can be entered while still
- * providing wake functionality. The sleeping state being entered must
- * be less than (i.e., higher power) or equal to this value.
- */
- if (acpi_PkgInt32(res, 1, &lowest_wake) != 0)
- goto out;
- if (sc->acpi_sstate > lowest_wake)
- goto out;
-
- /*
- * Element 0 of the _PRW object:
- */
- switch (res->Package.Elements[0].Type) {
- case ACPI_TYPE_INTEGER:
- /*
- * If the data type of this package element is numeric, then this
- * _PRW package element is the bit index in the GPEx_EN, in the
- * GPE blocks described in the FADT, of the enable bit that is
- * enabled for the wake event.
- */
- gpe_bit = res->Package.Elements[0].Integer.Value;
- status = AcpiSetGpeType(NULL, gpe_bit, ACPI_GPE_TYPE_WAKE_RUN);
- if (ACPI_FAILURE(status)) {
- printf("wake enable: AcpiSetGpeType failed for %u\n",
- gpe_bit);
- goto out;
- }
- status = AcpiEnableGpe(NULL, gpe_bit, ACPI_NOT_ISR);
- if (ACPI_FAILURE(status))
- printf("wake enable: AcpiEnableGpe failed for %u\n",
- gpe_bit);
- break;
- case ACPI_TYPE_PACKAGE:
- /*
- * If the data type of this package element is a package, then this
- * _PRW package element is itself a package containing two
- * elements. The first is an object reference to the GPE Block
- * device that contains the GPE that will be triggered by the wake
- * event. The second element is numeric and it contains the bit
- * index in the GPEx_EN, in the GPE Block referenced by the
- * first element in the package, of the enable bit that is enabled for
- * the wake event.
- *
- * For example, if this field is a package then it is of the form:
- * Package() {\_SB.PCI0.ISA.GPE, 2}
- */
- res2 = &res->Package.Elements[0];
- if (!ACPI_PKG_VALID(res2, 2))
- goto out;
- handle = acpi_GetReference(NULL, &res2->Package.Elements[0]);
- if (handle == NULL || acpi_PkgInt32(res2, 1, &gpe_bit) != 0)
- goto out;
- status = AcpiSetGpeType(handle, gpe_bit, ACPI_GPE_TYPE_WAKE_RUN);
- if (ACPI_FAILURE(status)) {
- printf("wake enable: AcpiSetGpeType failed for %u\n",
- gpe_bit);
- goto out;
- }
- status = AcpiEnableGpe(handle, gpe_bit, ACPI_NOT_ISR);
- if (ACPI_FAILURE(status))
- printf("wake enable: AcpiEnableGpe (package) failed for %u\n",
- gpe_bit);
- break;
- default:
- break;
- }
-
-out:
- if (prw_buffer.Pointer != NULL)
- AcpiOsFree(prw_buffer.Pointer);
-}
-
-/*
* Control interface.
*
* We multiplex ioctls for all participating ACPI devices here. Individual
diff --git a/sys/dev/acpica/acpi_button.c b/sys/dev/acpica/acpi_button.c
index 004a54e..b96c5fe 100644
--- a/sys/dev/acpica/acpi_button.c
+++ b/sys/dev/acpica/acpi_button.c
@@ -154,20 +154,21 @@ acpi_button_attach(device_t dev)
AcpiFormatException(status));
return_VALUE (ENXIO);
}
- acpi_device_enable_wake_capability(sc->button_handle, 1);
- acpi_device_enable_wake_event(sc->button_handle);
+ /* Enable the GPE for wake/runtime. */
+ acpi_wake_init(dev, ACPI_GPE_TYPE_WAKE_RUN);
+ acpi_wake_set_enable(dev, 1);
+
return_VALUE (0);
}
static int
acpi_button_suspend(device_t dev)
{
-#if 0
- struct acpi_button_softc *sc;
+ struct acpi_softc *acpi_sc;
- sc = device_get_softc(dev);
-#endif
+ acpi_sc = acpi_device_get_parent_softc(dev);
+ acpi_wake_sleep_prep(dev, acpi_sc->acpi_sstate);
return (0);
}
diff --git a/sys/dev/acpica/acpi_lid.c b/sys/dev/acpica/acpi_lid.c
index 82c4959..7971001 100644
--- a/sys/dev/acpica/acpi_lid.c
+++ b/sys/dev/acpica/acpi_lid.c
@@ -107,8 +107,10 @@ acpi_lid_attach(device_t dev)
*/
AcpiInstallNotifyHandler(sc->lid_handle, ACPI_DEVICE_NOTIFY,
acpi_lid_notify_handler, sc);
- acpi_device_enable_wake_capability(sc->lid_handle, 1);
- acpi_device_enable_wake_event(sc->lid_handle);
+
+ /* Enable the GPE for wake/runtime. */
+ acpi_wake_init(dev, ACPI_GPE_TYPE_WAKE_RUN);
+ acpi_wake_set_enable(dev, 1);
return_VALUE (0);
}
@@ -116,11 +118,10 @@ acpi_lid_attach(device_t dev)
static int
acpi_lid_suspend(device_t dev)
{
-#if 0
- struct acpi_lid_softc *sc;
+ struct acpi_softc *acpi_sc;
- sc = device_get_softc(dev);
-#endif
+ acpi_sc = acpi_device_get_parent_softc(dev);
+ acpi_wake_sleep_prep(dev, acpi_sc->acpi_sstate);
return (0);
}
diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h
index 4961935..1be1ed8 100644
--- a/sys/dev/acpica/acpivar.h
+++ b/sys/dev/acpica/acpivar.h
@@ -86,9 +86,19 @@ struct acpi_device {
/* Resources */
struct resource_list ad_rl;
+};
+struct acpi_prw_data {
+ ACPI_HANDLE gpe_handle;
+ int gpe_bit;
+ int lowest_wake;
+ void *power_res;
};
+/* Flags for each device defined in the AML namespace. */
+#define ACPI_FLAG_WAKE_CAPABLE 0x1
+#define ACPI_FLAG_WAKE_ENABLED 0x2
+
#if __FreeBSD_version < 500000
/*
* In 4.x, ACPI is protected by splhigh().
@@ -139,62 +149,30 @@ struct acpi_device {
#define ACPI_IVAR_MAGIC 0x101
#define ACPI_IVAR_PRIVATE 0x102
-static __inline ACPI_HANDLE
-acpi_get_handle(device_t dev)
-{
- uintptr_t up;
-
- if (BUS_READ_IVAR(device_get_parent(dev), dev, ACPI_IVAR_HANDLE, &up))
- return (NULL);
- return ((ACPI_HANDLE)up);
-}
-
-static __inline int
-acpi_set_handle(device_t dev, ACPI_HANDLE h)
-{
- uintptr_t up;
-
- up = (uintptr_t)h;
- return (BUS_WRITE_IVAR(device_get_parent(dev), dev, ACPI_IVAR_HANDLE, up));
-}
-
-static __inline int
-acpi_get_magic(device_t dev)
-{
- uintptr_t up;
-
- if (BUS_READ_IVAR(device_get_parent(dev), dev, ACPI_IVAR_MAGIC, &up))
- return(0);
- return ((int)up);
-}
-
-static __inline int
-acpi_set_magic(device_t dev, int m)
-{
- uintptr_t up;
-
- up = (uintptr_t)m;
- return (BUS_WRITE_IVAR(device_get_parent(dev), dev, ACPI_IVAR_MAGIC, up));
-}
-
-static __inline void *
-acpi_get_private(device_t dev)
-{
- uintptr_t up;
-
- if (BUS_READ_IVAR(device_get_parent(dev), dev, ACPI_IVAR_PRIVATE, &up))
- return (NULL);
- return ((void *)up);
+/*
+ * Accessor functions for our ivars. Default value for BUS_READ_IVAR is
+ * (type) 0. The <sys/bus.h> accessor functions don't check return values.
+ */
+#define __ACPI_BUS_ACCESSOR(varp, var, ivarp, ivar, type) \
+ \
+static __inline type varp ## _get_ ## var(device_t dev) \
+{ \
+ uintptr_t v = 0; \
+ BUS_READ_IVAR(device_get_parent(dev), dev, \
+ ivarp ## _IVAR_ ## ivar, &v); \
+ return ((type) v); \
+} \
+ \
+static __inline void varp ## _set_ ## var(device_t dev, type t) \
+{ \
+ uintptr_t v = (uintptr_t) t; \
+ BUS_WRITE_IVAR(device_get_parent(dev), dev, \
+ ivarp ## _IVAR_ ## ivar, v); \
}
-static __inline int
-acpi_set_private(device_t dev, void *p)
-{
- uintptr_t up;
-
- up = (uintptr_t)p;
- return (BUS_WRITE_IVAR(device_get_parent(dev), dev, ACPI_IVAR_PRIVATE, up));
-}
+__ACPI_BUS_ACCESSOR(acpi, handle, ACPI, HANDLE, ACPI_HANDLE)
+__ACPI_BUS_ACCESSOR(acpi, magic, ACPI, MAGIC, int)
+__ACPI_BUS_ACCESSOR(acpi, private, ACPI, PRIVATE, void *)
static __inline ACPI_OBJECT_TYPE
acpi_get_type(device_t dev)
@@ -249,6 +227,9 @@ extern ACPI_STATUS acpi_AppendBufferResource(ACPI_BUFFER *buf,
extern ACPI_STATUS acpi_OverrideInterruptLevel(UINT32 InterruptNumber);
extern ACPI_STATUS acpi_SetIntrModel(int model);
extern ACPI_STATUS acpi_SetSleepState(struct acpi_softc *sc, int state);
+int acpi_wake_init(device_t dev, int type);
+int acpi_wake_set_enable(device_t dev, int enable);
+int acpi_wake_sleep_prep(device_t dev, int sstate);
extern ACPI_STATUS acpi_Startup(void);
extern ACPI_STATUS acpi_Enable(struct acpi_softc *sc);
extern ACPI_STATUS acpi_Disable(struct acpi_softc *sc);
OpenPOWER on IntegriCloud