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.
This commit is contained in:
Nate Lawson 2004-05-27 18:38:45 +00:00
parent d49b8d3917
commit e8b4d56eb5
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=129783
4 changed files with 286 additions and 187 deletions

View File

@ -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
*/
@ -1976,125 +2211,6 @@ acpi_disabled(char *subsys)
return (0);
}
/*
* 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.
*

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
/*
* 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_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);
}
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);