diff options
author | njl <njl@FreeBSD.org> | 2004-05-27 18:38:45 +0000 |
---|---|---|
committer | njl <njl@FreeBSD.org> | 2004-05-27 18:38:45 +0000 |
commit | fa603e929d266049001b489b9a22e7a556d4b6e0 (patch) | |
tree | 1817b677583b861f1119c6cedba1d7db92c18e9a /sys | |
parent | 7db6667914bd21b54582207e0030bc1c2231f37e (diff) | |
download | FreeBSD-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')
-rw-r--r-- | sys/dev/acpica/acpi.c | 356 | ||||
-rw-r--r-- | sys/dev/acpica/acpi_button.c | 13 | ||||
-rw-r--r-- | sys/dev/acpica/acpi_lid.c | 13 | ||||
-rw-r--r-- | sys/dev/acpica/acpivar.h | 89 |
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); |