Commit 9874647b authored by Rafael J. Wysocki's avatar Rafael J. Wysocki Committed by Len Brown

ACPI / ACPICA: Do not execute _PRW methods during initialization

Currently, during initialization ACPICA walks the entire ACPI
namespace in search of any device objects with assciated _PRW
methods.  All of the _PRW methods found are executed in the process
to extract the GPE information returned by them, so that the GPEs in
question can be marked as "able to wakeup" (more precisely, the
ACPI_GPE_CAN_WAKE flag is set for them).  The only purpose of this
exercise is to avoid enabling the CAN_WAKE GPEs automatically, even
if there are _Lxx/_Exx methods associated with them.  However, it is
both costly and unnecessary, because the host OS has to execute the
_PRW methods anyway to check which devices can wake up the system
from sleep states.  Moreover, it then uses full information
returned by _PRW, including the GPE information, so it can take care
of disabling the GPEs if necessary.

Remove the code that walks the namespace and executes _PRW from
ACPICA and modify comments to reflect that change.  Make
acpi_bus_set_run_wake_flags() disable GPEs for wakeup devices
so that they don't cause spurious wakeup events to be signaled.
This not only reduces the complexity of the ACPICA initialization
code, but in some cases it should reduce the kernel boot time as
well.

Unfortunately, for this purpose we need a new ACPICA function,
acpi_gpe_can_wake(), to be called by the host OS in order to disable
the GPEs that can wake up the system and were previously enabled by
acpi_ev_initialize_gpe_block() or acpi_ev_update_gpes() (such a GPE
should be disabled only once, because the initialization code enables
it only once, but it may be pointed to by _PRW for multiple devices
and that's why the additional function is necessary).
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent e8e18c95
......@@ -99,13 +99,6 @@ u8 ACPI_INIT_GLOBAL(acpi_gbl_all_methods_serialized, FALSE);
*/
u8 ACPI_INIT_GLOBAL(acpi_gbl_create_osi_method, TRUE);
/*
* Disable wakeup GPEs during runtime? Default is TRUE because WAKE and
* RUNTIME GPEs should never be shared, and WAKE GPEs should typically only
* be enabled just before going to sleep.
*/
u8 ACPI_INIT_GLOBAL(acpi_gbl_leave_wake_gpes_disabled, TRUE);
/*
* Optionally use default values for the ACPI register widths. Set this to
* TRUE to use the defaults, if an FADT contains incorrect widths/lengths.
......
......@@ -102,9 +102,8 @@ acpi_status acpi_ev_initialize_events(void)
* RETURN: Status
*
* DESCRIPTION: Completes initialization of the FADT-defined GPE blocks
* (0 and 1). This causes the _PRW methods to be run, so the HW
* must be fully initialized at this point, including global lock
* support.
* (0 and 1). The HW must be fully initialized at this point,
* including global lock support.
*
******************************************************************************/
......
......@@ -439,8 +439,6 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
{
acpi_status status;
struct acpi_gpe_event_info *gpe_event_info;
struct acpi_gpe_walk_info walk_info;
u32 wake_gpe_count;
u32 gpe_enabled_count;
u32 gpe_index;
u32 gpe_number;
......@@ -456,37 +454,9 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
}
/*
* Runtime option: Should wake GPEs be enabled at runtime? The default
* is no, they should only be enabled just as the machine goes to sleep.
* Enable all GPEs that have a corresponding method. Any other GPEs
* within this block must be enabled via the acpi_enable_gpe interface.
*/
if (acpi_gbl_leave_wake_gpes_disabled) {
/*
* Differentiate runtime vs wake GPEs, via the _PRW control methods.
* Each GPE that has one or more _PRWs that reference it is by
* definition a wake GPE and will not be enabled while the machine
* is running.
*/
walk_info.gpe_block = gpe_block;
walk_info.gpe_device = gpe_device;
walk_info.execute_by_owner_id = FALSE;
status =
acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
acpi_ev_match_prw_and_gpe, NULL,
&walk_info, NULL);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"While executing _PRW methods"));
}
}
/*
* Enable all GPEs that have a corresponding method and are not
* 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) {
......@@ -512,13 +482,6 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
goto enabled;
}
if (gpe_event_info->flags & ACPI_GPE_CAN_WAKE) {
wake_gpe_count++;
if (acpi_gbl_leave_wake_gpes_disabled) {
continue;
}
}
/* Ignore GPEs that have no corresponding _Lxx/_Exx method */
if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD)) {
......@@ -540,10 +503,10 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
}
}
if (gpe_enabled_count || wake_gpe_count) {
if (gpe_enabled_count) {
ACPI_DEBUG_PRINT((ACPI_DB_INIT,
"Enabled %u Runtime GPEs, added %u Wake GPEs in this block\n",
gpe_enabled_count, wake_gpe_count));
"Enabled %u GPEs in this block\n",
gpe_enabled_count));
}
return_ACPI_STATUS(AE_OK);
......
......@@ -211,9 +211,7 @@ acpi_status acpi_ev_gpe_initialize(void)
* DESCRIPTION: Check for new GPE methods (_Lxx/_Exx) made available as a
* result of a Load() or load_table() operation. If new GPE
* methods have been installed, register the new methods and
* enable and runtime GPEs that are associated with them. Also,
* run any newly loaded _PRW methods in order to discover any
* new CAN_WAKE GPEs.
* enable and runtime GPEs that are associated with them.
*
******************************************************************************/
......@@ -223,49 +221,12 @@ void acpi_ev_update_gpes(acpi_owner_id table_owner_id)
struct acpi_gpe_block_info *gpe_block;
struct acpi_gpe_walk_info walk_info;
acpi_status status = AE_OK;
u32 new_wake_gpe_count = 0;
/* We will examine only _PRW/_Lxx/_Exx methods owned by this table */
walk_info.owner_id = table_owner_id;
walk_info.execute_by_owner_id = TRUE;
walk_info.count = 0;
if (acpi_gbl_leave_wake_gpes_disabled) {
/*
* 1) Run any newly-loaded _PRW methods to find any GPEs that
* can now be marked as CAN_WAKE GPEs. Note: We must run the
* _PRW methods before we process the _Lxx/_Exx methods because
* we will enable all runtime GPEs associated with the new
* _Lxx/_Exx methods at the time we process those methods.
*
* Unlock interpreter so that we can run the _PRW methods.
*/
walk_info.gpe_block = NULL;
walk_info.gpe_device = NULL;
acpi_ex_exit_interpreter();
status =
acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
ACPI_NS_WALK_NO_UNLOCK,
acpi_ev_match_prw_and_gpe, NULL,
&walk_info, NULL);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"While executing _PRW methods"));
}
acpi_ex_enter_interpreter();
new_wake_gpe_count = walk_info.count;
}
/*
* 2) Find any _Lxx/_Exx GPE methods that have just been loaded.
*
* Any GPEs that correspond to new _Lxx/_Exx methods and are not
* marked as CAN_WAKE are immediately enabled.
* Any GPEs that correspond to new _Lxx/_Exx methods are immediately
* enabled.
*
* Examine the namespace underneath each gpe_device within the
* gpe_block lists.
......@@ -275,6 +236,8 @@ void acpi_ev_update_gpes(acpi_owner_id table_owner_id)
return;
}
walk_info.owner_id = table_owner_id;
walk_info.execute_by_owner_id = TRUE;
walk_info.count = 0;
walk_info.enable_this_gpe = TRUE;
......@@ -307,10 +270,8 @@ void acpi_ev_update_gpes(acpi_owner_id table_owner_id)
gpe_xrupt_info = gpe_xrupt_info->next;
}
if (walk_info.count || new_wake_gpe_count) {
ACPI_INFO((AE_INFO,
"Enabled %u new runtime GPEs, added %u new wakeup GPEs",
walk_info.count, new_wake_gpe_count));
if (walk_info.count) {
ACPI_INFO((AE_INFO, "Enabled %u new GPEs", walk_info.count));
}
(void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
......@@ -386,9 +347,6 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
/*
* 3) Edge/Level determination is based on the 2nd character
* of the method name
*
* NOTE: Default GPE type is RUNTIME only. Later, if a _PRW object is
* found that points to this GPE, the ACPI_GPE_CAN_WAKE flag is set.
*/
switch (name[1]) {
case 'L':
......@@ -471,23 +429,18 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
*/
if (walk_info->enable_this_gpe) {
/* Ignore GPEs that can wake the system */
walk_info->count++;
gpe_device = walk_info->gpe_device;
if (!(gpe_event_info->flags & ACPI_GPE_CAN_WAKE) ||
!acpi_gbl_leave_wake_gpes_disabled) {
walk_info->count++;
gpe_device = walk_info->gpe_device;
if (gpe_device == acpi_gbl_fadt_gpe_device) {
gpe_device = NULL;
}
if (gpe_device == acpi_gbl_fadt_gpe_device) {
gpe_device = NULL;
}
status = acpi_enable_gpe(gpe_device, gpe_number);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"Could not enable GPE 0x%02X",
gpe_number));
}
status = acpi_enable_gpe(gpe_device, gpe_number);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"Could not enable GPE 0x%02X",
gpe_number));
}
}
......@@ -496,157 +449,3 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
name, gpe_number));
return_ACPI_STATUS(AE_OK);
}
/*******************************************************************************
*
* FUNCTION: acpi_ev_match_prw_and_gpe
*
* PARAMETERS: Callback from walk_namespace
*
* RETURN: Status. NOTE: We ignore errors so that the _PRW walk is
* not aborted on a single _PRW failure.
*
* DESCRIPTION: Called from acpi_walk_namespace. Expects each object to be a
* Device. Run the _PRW method. If present, extract the GPE
* number and mark the GPE as a CAN_WAKE GPE. Allows a
* per-owner_id execution if execute_by_owner_id is TRUE in the
* walk_info parameter block.
*
* If walk_info->execute_by_owner_id is TRUE, we only execute _PRWs with that
* owner.
* If walk_info->gpe_device is NULL, we execute every _PRW found. Otherwise,
* we only execute _PRWs that refer to the input gpe_device.
*
******************************************************************************/
acpi_status
acpi_ev_match_prw_and_gpe(acpi_handle obj_handle,
u32 level, void *context, void **return_value)
{
struct acpi_gpe_walk_info *walk_info =
ACPI_CAST_PTR(struct acpi_gpe_walk_info, context);
struct acpi_namespace_node *gpe_device;
struct acpi_gpe_block_info *gpe_block;
struct acpi_namespace_node *target_gpe_device;
struct acpi_namespace_node *prw_node;
struct acpi_gpe_event_info *gpe_event_info;
union acpi_operand_object *pkg_desc;
union acpi_operand_object *obj_desc;
u32 gpe_number;
acpi_status status;
ACPI_FUNCTION_TRACE(ev_match_prw_and_gpe);
/* Check for a _PRW method under this device */
status = acpi_ns_get_node(obj_handle, METHOD_NAME__PRW,
ACPI_NS_NO_UPSEARCH, &prw_node);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(AE_OK);
}
/* Check if requested owner_id matches this owner_id */
if ((walk_info->execute_by_owner_id) &&
(prw_node->owner_id != walk_info->owner_id)) {
return_ACPI_STATUS(AE_OK);
}
/* Execute the _PRW */
status = acpi_ut_evaluate_object(prw_node, NULL,
ACPI_BTYPE_PACKAGE, &pkg_desc);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(AE_OK);
}
/* The returned _PRW package must have at least two elements */
if (pkg_desc->package.count < 2) {
goto cleanup;
}
/* Extract pointers from the input context */
gpe_device = walk_info->gpe_device;
gpe_block = walk_info->gpe_block;
/*
* The _PRW object must return a package, we are only interested
* in the first element
*/
obj_desc = pkg_desc->package.elements[0];
if (obj_desc->common.type == ACPI_TYPE_INTEGER) {
/* Use FADT-defined GPE device (from definition of _PRW) */
target_gpe_device = NULL;
if (gpe_device) {
target_gpe_device = acpi_gbl_fadt_gpe_device;
}
/* Integer is the GPE number in the FADT described GPE blocks */
gpe_number = (u32)obj_desc->integer.value;
} else if (obj_desc->common.type == ACPI_TYPE_PACKAGE) {
/* Package contains a GPE reference and GPE number within a GPE block */
if ((obj_desc->package.count < 2) ||
((obj_desc->package.elements[0])->common.type !=
ACPI_TYPE_LOCAL_REFERENCE) ||
((obj_desc->package.elements[1])->common.type !=
ACPI_TYPE_INTEGER)) {
goto cleanup;
}
/* Get GPE block reference and decode */
target_gpe_device =
obj_desc->package.elements[0]->reference.node;
gpe_number = (u32)obj_desc->package.elements[1]->integer.value;
} else {
/* Unknown type, just ignore it */
goto cleanup;
}
/* Get the gpe_event_info for this GPE */
if (gpe_device) {
/*
* Is this GPE within this block?
*
* TRUE if and only if these conditions are true:
* 1) The GPE devices match.
* 2) The GPE index(number) is within the range of the Gpe Block
* associated with the GPE device.
*/
if (gpe_device != target_gpe_device) {
goto cleanup;
}
gpe_event_info =
acpi_ev_low_get_gpe_info(gpe_number, gpe_block);
} else {
/* gpe_device is NULL, just match the target_device and gpe_number */
gpe_event_info =
acpi_ev_get_gpe_event_info(target_gpe_device, gpe_number);
}
if (gpe_event_info) {
if (!(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) {
/* This GPE can wake the system */
gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
walk_info->count++;
}
}
cleanup:
acpi_ut_remove_reference(pkg_desc);
return_ACPI_STATUS(AE_OK);
}
......@@ -387,6 +387,59 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number)
}
ACPI_EXPORT_SYMBOL(acpi_disable_gpe)
/*******************************************************************************
*
* FUNCTION: acpi_gpe_can_wake
*
* PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
* gpe_number - GPE level within the GPE block
*
* RETURN: Status
*
* DESCRIPTION: Set the ACPI_GPE_CAN_WAKE flag for the given GPE. If the GPE
* has a corresponding method and is currently enabled, disable it
* (GPEs with corresponding methods are enabled unconditionally
* during initialization, but GPEs that can wake up are expected
* to be initially disabled).
*
******************************************************************************/
acpi_status acpi_gpe_can_wake(acpi_handle gpe_device, u32 gpe_number)
{
acpi_status status = AE_OK;
struct acpi_gpe_event_info *gpe_event_info;
acpi_cpu_flags flags;
u8 disable = 0;
ACPI_FUNCTION_TRACE(acpi_gpe_can_wake);
flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
/* Ensure that we have a valid GPE number */
gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
if (!gpe_event_info) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
}
if (gpe_event_info->flags & ACPI_GPE_CAN_WAKE) {
goto unlock_and_exit;
}
gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
disable = (gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD)
&& gpe_event_info->runtime_count;
unlock_and_exit:
acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
if (disable)
status = acpi_disable_gpe(gpe_device, gpe_number);
return_ACPI_STATUS(status);
}
ACPI_EXPORT_SYMBOL(acpi_gpe_can_wake)
/*******************************************************************************
*
* FUNCTION: acpi_disable_event
......@@ -703,7 +756,7 @@ acpi_install_gpe_block(acpi_handle gpe_device,
obj_desc->device.gpe_block = gpe_block;
/* Run the _PRW methods and enable the runtime GPEs in the new block */
/* Enable the runtime GPEs in the new block */
status = acpi_ev_initialize_gpe_block(node, gpe_block);
......
......@@ -120,7 +120,7 @@ acpi_ex_add_table(u32 table_index,
acpi_ns_exec_module_code_list();
acpi_ex_enter_interpreter();
/* Update GPEs for any new _PRW or _Lxx/_Exx methods. Ignore errors */
/* Update GPEs for any new _Lxx/_Exx methods. Ignore errors */
status = acpi_tb_get_owner_id(table_index, &owner_id);
if (ACPI_SUCCESS(status)) {
......
......@@ -293,12 +293,8 @@ acpi_status acpi_initialize_objects(u32 flags)
* Complete the GPE initialization for the GPE blocks defined in the FADT
* (GPE block 0 and 1).
*
* Note1: This is where the _PRW methods are executed for the GPEs. These
* methods can only be executed after the SCI and Global Lock handlers are
* installed and initialized.
*
* Note2: Currently, there seems to be no need to run the _REG methods
* before execution of the _PRW methods and enabling of the GPEs.
* NOTE: Currently, there seems to be no need to run the _REG methods
* before enabling the GPEs.
*/
if (!(flags & ACPI_NO_EVENT_INIT)) {
status = acpi_ev_install_fadt_gpes();
......
......@@ -1064,26 +1064,6 @@ static int __init acpi_serialize_setup(char *str)
__setup("acpi_serialize", acpi_serialize_setup);
/*
* Wake and Run-Time GPES are expected to be separate.
* We disable wake-GPEs at run-time to prevent spurious
* interrupts.
*
* However, if a system exists that shares Wake and
* Run-time events on the same GPE this flag is available
* to tell Linux to keep the wake-time GPEs enabled at run-time.
*/
static int __init acpi_wake_gpes_always_on_setup(char *str)
{
printk(KERN_INFO PREFIX "wake GPEs not disabled\n");
acpi_gbl_leave_wake_gpes_disabled = FALSE;
return 1;
}
__setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup);
/* Check of resource interference between native drivers and ACPI
* OperationRegions (SystemIO and System Memory only).
* IO ports and memory declared in ACPI might be used by the ACPI subsystem
......
......@@ -740,6 +740,8 @@ acpi_bus_extract_wakeup_device_power_package(struct acpi_device *device,
device->wakeup.resources.handles[i] = element->reference.handle;
}
acpi_gpe_can_wake(device->wakeup.gpe_device, device->wakeup.gpe_number);
return AE_OK;
}
......
......@@ -63,7 +63,6 @@ extern u32 acpi_dbg_layer;
extern u8 acpi_gbl_enable_interpreter_slack;
extern u8 acpi_gbl_all_methods_serialized;
extern u8 acpi_gbl_create_osi_method;
extern u8 acpi_gbl_leave_wake_gpes_disabled;
extern u8 acpi_gbl_use_default_register_widths;
extern acpi_name acpi_gbl_trace_method_name;
extern u32 acpi_gbl_trace_flags;
......@@ -286,6 +285,8 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number);
acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number);
acpi_status acpi_gpe_can_wake(acpi_handle gpe_device, u32 gpe_number);
acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number);
acpi_status acpi_gpe_wakeup(acpi_handle gpe_device, u32 gpe_number, u8 action);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment