Skip to content

Commit

Permalink
ACPI: Use GPE reference counting to support shared GPEs
Browse files Browse the repository at this point in the history
ACPI GPEs may map to multiple devices.  The current GPE interface
only provides a mechanism for enabling and disabling GPEs, making
it difficult to change the state of GPEs at runtime without extensive
cooperation between devices.

Add an API to allow devices to indicate whether or not they want
their device's GPE to be enabled for both runtime and wakeup events.

Remove the old GPE type handling entirely, which gets rid of various
quirks, like the implicit disabling with GPE type setting. This
requires a small amount of rework in order to ensure that non-wake
GPEs are enabled by default to preserve existing behaviour.

Based on patches from Matthew Garrett <mjg@redhat.com>.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
  • Loading branch information
Rafael J. Wysocki authored and Jesse Barnes committed Feb 23, 2010
1 parent c39fae1 commit 9630bdd
Show file tree
Hide file tree
Showing 13 changed files with 188 additions and 325 deletions.
6 changes: 1 addition & 5 deletions drivers/acpi/acpica/acevents.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node *node,
* evgpe - GPE handling and dispatch
*/
acpi_status
acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info,
u8 type);
acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info);

acpi_status
acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info,
Expand Down Expand Up @@ -121,9 +120,6 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info,

u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list);

acpi_status
acpi_ev_set_gpe_type(struct acpi_gpe_event_info *gpe_event_info, u8 type);

acpi_status
acpi_ev_check_for_wake_only_gpe(struct acpi_gpe_event_info *gpe_event_info);

Expand Down
2 changes: 2 additions & 0 deletions drivers/acpi/acpica/aclocal.h
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,8 @@ struct acpi_gpe_event_info {
struct acpi_gpe_register_info *register_info; /* Backpointer to register info */
u8 flags; /* Misc info about this GPE */
u8 gpe_number; /* This GPE */
u8 runtime_count;
u8 wakeup_count;
};

/* Information about a GPE register pair, one per each status/enable pair in an array */
Expand Down
153 changes: 16 additions & 137 deletions drivers/acpi/acpica/evgpe.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,56 +52,11 @@ ACPI_MODULE_NAME("evgpe")
/* Local prototypes */
static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context);

/*******************************************************************************
*
* FUNCTION: acpi_ev_set_gpe_type
*
* PARAMETERS: gpe_event_info - GPE to set
* Type - New type
*
* RETURN: Status
*
* DESCRIPTION: Sets the new type for the GPE (wake, run, or wake/run)
*
******************************************************************************/

acpi_status
acpi_ev_set_gpe_type(struct acpi_gpe_event_info *gpe_event_info, u8 type)
{
acpi_status status;

ACPI_FUNCTION_TRACE(ev_set_gpe_type);

/* Validate type and update register enable masks */

switch (type) {
case ACPI_GPE_TYPE_WAKE:
case ACPI_GPE_TYPE_RUNTIME:
case ACPI_GPE_TYPE_WAKE_RUN:
break;

default:
return_ACPI_STATUS(AE_BAD_PARAMETER);
}

/* Disable the GPE if currently enabled */

status = acpi_ev_disable_gpe(gpe_event_info);

/* Clear the type bits and insert the new Type */

gpe_event_info->flags &= ~ACPI_GPE_TYPE_MASK;
gpe_event_info->flags |= type;
return_ACPI_STATUS(status);
}

/*******************************************************************************
*
* FUNCTION: acpi_ev_update_gpe_enable_masks
*
* PARAMETERS: gpe_event_info - GPE to update
* Type - What to do: ACPI_GPE_DISABLE or
* ACPI_GPE_ENABLE
*
* RETURN: Status
*
Expand All @@ -110,8 +65,7 @@ acpi_ev_set_gpe_type(struct acpi_gpe_event_info *gpe_event_info, u8 type)
******************************************************************************/

acpi_status
acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info,
u8 type)
acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info)
{
struct acpi_gpe_register_info *gpe_register_info;
u8 register_bit;
Expand All @@ -127,37 +81,14 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info,
(1 <<
(gpe_event_info->gpe_number - gpe_register_info->base_gpe_number));

/* 1) Disable case. Simply clear all enable bits */

if (type == ACPI_GPE_DISABLE) {
ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake,
register_bit);
ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit);
return_ACPI_STATUS(AE_OK);
}

/* 2) Enable case. Set/Clear the appropriate enable bits */
ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake, register_bit);
ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit);

switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) {
case ACPI_GPE_TYPE_WAKE:
ACPI_SET_BIT(gpe_register_info->enable_for_wake, register_bit);
ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit);
break;

case ACPI_GPE_TYPE_RUNTIME:
ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake,
register_bit);
if (gpe_event_info->runtime_count)
ACPI_SET_BIT(gpe_register_info->enable_for_run, register_bit);
break;

case ACPI_GPE_TYPE_WAKE_RUN:
if (gpe_event_info->wakeup_count)
ACPI_SET_BIT(gpe_register_info->enable_for_wake, register_bit);
ACPI_SET_BIT(gpe_register_info->enable_for_run, register_bit);
break;

default:
return_ACPI_STATUS(AE_BAD_PARAMETER);
}

return_ACPI_STATUS(AE_OK);
}
Expand Down Expand Up @@ -186,47 +117,20 @@ acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info,

/* Make sure HW enable masks are updated */

status =
acpi_ev_update_gpe_enable_masks(gpe_event_info, ACPI_GPE_ENABLE);
if (ACPI_FAILURE(status)) {
status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
if (ACPI_FAILURE(status))
return_ACPI_STATUS(status);
}

/* Mark wake-enabled or HW enable, or both */

switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) {
case ACPI_GPE_TYPE_WAKE:

ACPI_SET_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED);
break;

case ACPI_GPE_TYPE_WAKE_RUN:

ACPI_SET_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED);

/*lint -fallthrough */

case ACPI_GPE_TYPE_RUNTIME:

ACPI_SET_BIT(gpe_event_info->flags, ACPI_GPE_RUN_ENABLED);

if (write_to_hardware) {

/* Clear the GPE (of stale events), then enable it */

status = acpi_hw_clear_gpe(gpe_event_info);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}

/* Enable the requested runtime GPE */

status = acpi_hw_write_gpe_enable_reg(gpe_event_info);
}
break;
if (gpe_event_info->runtime_count && write_to_hardware) {
/* Clear the GPE (of stale events), then enable it */
status = acpi_hw_clear_gpe(gpe_event_info);
if (ACPI_FAILURE(status))
return_ACPI_STATUS(status);

default:
return_ACPI_STATUS(AE_BAD_PARAMETER);
/* Enable the requested runtime GPE */
status = acpi_hw_write_gpe_enable_reg(gpe_event_info);
}

return_ACPI_STATUS(AE_OK);
Expand All @@ -252,34 +156,9 @@ acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info)

/* Make sure HW enable masks are updated */

status =
acpi_ev_update_gpe_enable_masks(gpe_event_info, ACPI_GPE_DISABLE);
if (ACPI_FAILURE(status)) {
status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
if (ACPI_FAILURE(status))
return_ACPI_STATUS(status);
}

/* Clear the appropriate enabled flags for this GPE */

switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) {
case ACPI_GPE_TYPE_WAKE:
ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED);
break;

case ACPI_GPE_TYPE_WAKE_RUN:
ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED);

/* fallthrough */

case ACPI_GPE_TYPE_RUNTIME:

/* Disable the requested runtime GPE */

ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_RUN_ENABLED);
break;

default:
break;
}

/*
* Even if we don't know the GPE type, make sure that we always
Expand Down
87 changes: 33 additions & 54 deletions drivers/acpi/acpica/evgpeblk.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@ acpi_ev_save_method_info(acpi_handle obj_handle,
u32 gpe_number;
char name[ACPI_NAME_SIZE + 1];
u8 type;
acpi_status status;

ACPI_FUNCTION_TRACE(ev_save_method_info);

Expand Down Expand Up @@ -325,26 +324,20 @@ acpi_ev_save_method_info(acpi_handle obj_handle,

/*
* Now we can add this information to the gpe_event_info block for use
* during dispatch of this GPE. Default type is RUNTIME, although this may
* change when the _PRW methods are executed later.
* during dispatch of this GPE.
*/
gpe_event_info =
&gpe_block->event_info[gpe_number - gpe_block->block_base_number];

gpe_event_info->flags = (u8)
(type | ACPI_GPE_DISPATCH_METHOD | ACPI_GPE_TYPE_RUNTIME);
gpe_event_info->flags = (u8) (type | ACPI_GPE_DISPATCH_METHOD);

gpe_event_info->dispatch.method_node =
(struct acpi_namespace_node *)obj_handle;

/* Update enable mask, but don't enable the HW GPE as of yet */

status = acpi_ev_enable_gpe(gpe_event_info, FALSE);

ACPI_DEBUG_PRINT((ACPI_DB_LOAD,
"Registered GPE method %s as GPE number 0x%.2X\n",
name, gpe_number));
return_ACPI_STATUS(status);
return_ACPI_STATUS(AE_OK);
}

/*******************************************************************************
Expand Down Expand Up @@ -454,20 +447,7 @@ acpi_ev_match_prw_and_gpe(acpi_handle obj_handle,
gpe_block->
block_base_number];

/* Mark GPE for WAKE-ONLY but WAKE_DISABLED */

gpe_event_info->flags &=
~(ACPI_GPE_WAKE_ENABLED | ACPI_GPE_RUN_ENABLED);

status =
acpi_ev_set_gpe_type(gpe_event_info, ACPI_GPE_TYPE_WAKE);
if (ACPI_FAILURE(status)) {
goto cleanup;
}

status =
acpi_ev_update_gpe_enable_masks(gpe_event_info,
ACPI_GPE_DISABLE);
gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
}

cleanup:
Expand Down Expand Up @@ -989,7 +969,6 @@ acpi_status
acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
struct acpi_gpe_block_info *gpe_block)
{
acpi_status status;
struct acpi_gpe_event_info *gpe_event_info;
struct acpi_gpe_walk_info gpe_info;
u32 wake_gpe_count;
Expand Down Expand Up @@ -1019,58 +998,58 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
gpe_info.gpe_block = gpe_block;
gpe_info.gpe_device = gpe_device;

status =
acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
acpi_ev_match_prw_and_gpe, NULL,
&gpe_info, NULL);
}

/*
* Enable all GPEs in this block that have these attributes:
* 1) are "runtime" or "run/wake" GPEs, and
* 2) have a corresponding _Lxx or _Exx method
*
* Any other GPEs within this block must be enabled via the
* acpi_enable_gpe() external interface.
* Enable all GPEs that have a corresponding method and aren't
* capable of generating wakeups. Any other GPEs within this block
* must be enabled via the acpi_enable_gpe() interface.
*/
wake_gpe_count = 0;
gpe_enabled_count = 0;
if (gpe_device == acpi_gbl_fadt_gpe_device)
gpe_device = NULL;

for (i = 0; i < gpe_block->register_count; i++) {
for (j = 0; j < 8; j++) {
for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) {
acpi_status status;
acpi_size gpe_index;
int gpe_number;

/* Get the info block for this particular GPE */
gpe_index = (acpi_size)i * ACPI_GPE_REGISTER_WIDTH + j;
gpe_event_info = &gpe_block->event_info[gpe_index];

gpe_event_info = &gpe_block->event_info[((acpi_size) i *
ACPI_GPE_REGISTER_WIDTH)
+ j];

if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
ACPI_GPE_DISPATCH_METHOD) &&
(gpe_event_info->flags & ACPI_GPE_TYPE_RUNTIME)) {
gpe_enabled_count++;
}

if (gpe_event_info->flags & ACPI_GPE_TYPE_WAKE) {
if (gpe_event_info->flags & ACPI_GPE_CAN_WAKE) {
wake_gpe_count++;
if (acpi_gbl_leave_wake_gpes_disabled)
continue;
}

if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD))
continue;

gpe_number = gpe_index + gpe_block->block_base_number;
status = acpi_enable_gpe(gpe_device, gpe_number,
ACPI_GPE_TYPE_RUNTIME);
if (ACPI_FAILURE(status))
ACPI_ERROR((AE_INFO,
"Failed to enable GPE %02X\n",
gpe_number));
else
gpe_enabled_count++;
}
}

ACPI_DEBUG_PRINT((ACPI_DB_INIT,
"Found %u Wake, Enabled %u Runtime GPEs in this block\n",
wake_gpe_count, gpe_enabled_count));

/* Enable all valid runtime GPEs found above */

status = acpi_hw_enable_runtime_gpe_block(NULL, gpe_block, NULL);
if (ACPI_FAILURE(status)) {
ACPI_ERROR((AE_INFO, "Could not enable GPEs in GpeBlock %p",
gpe_block));
}

return_ACPI_STATUS(status);
return_ACPI_STATUS(AE_OK);
}

/*******************************************************************************
Expand Down
Loading

0 comments on commit 9630bdd

Please sign in to comment.