162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Module Name: evgpe - General Purpose Event handling and dispatch 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2000 - 2023, Intel Corp. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci *****************************************************************************/ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <acpi/acpi.h> 1162306a36Sopenharmony_ci#include "accommon.h" 1262306a36Sopenharmony_ci#include "acevents.h" 1362306a36Sopenharmony_ci#include "acnamesp.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define _COMPONENT ACPI_EVENTS 1662306a36Sopenharmony_ciACPI_MODULE_NAME("evgpe") 1762306a36Sopenharmony_ci#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ 1862306a36Sopenharmony_ci/* Local prototypes */ 1962306a36Sopenharmony_cistatic void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic void ACPI_SYSTEM_XFACE acpi_ev_asynch_enable_gpe(void *context); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/******************************************************************************* 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * FUNCTION: acpi_ev_update_gpe_enable_mask 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * PARAMETERS: gpe_event_info - GPE to update 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * RETURN: Status 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * DESCRIPTION: Updates GPE register enable mask based upon whether there are 3262306a36Sopenharmony_ci * runtime references to this GPE 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci ******************************************************************************/ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ciacpi_status 3762306a36Sopenharmony_ciacpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct acpi_gpe_register_info *gpe_register_info; 4062306a36Sopenharmony_ci u32 register_bit; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci ACPI_FUNCTION_TRACE(ev_update_gpe_enable_mask); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci gpe_register_info = gpe_event_info->register_info; 4562306a36Sopenharmony_ci if (!gpe_register_info) { 4662306a36Sopenharmony_ci return_ACPI_STATUS(AE_NOT_EXIST); 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* Clear the run bit up front */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* Set the mask bit only if there are references to this GPE */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (gpe_event_info->runtime_count) { 5862306a36Sopenharmony_ci ACPI_SET_BIT(gpe_register_info->enable_for_run, 5962306a36Sopenharmony_ci (u8)register_bit); 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci gpe_register_info->enable_mask = gpe_register_info->enable_for_run; 6362306a36Sopenharmony_ci return_ACPI_STATUS(AE_OK); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/******************************************************************************* 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * FUNCTION: acpi_ev_enable_gpe 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * PARAMETERS: gpe_event_info - GPE to enable 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * RETURN: Status 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * DESCRIPTION: Enable a GPE. 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci ******************************************************************************/ 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ciacpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci acpi_status status; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci ACPI_FUNCTION_TRACE(ev_enable_gpe); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* Enable the requested GPE */ 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE); 8762306a36Sopenharmony_ci return_ACPI_STATUS(status); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/******************************************************************************* 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * FUNCTION: acpi_ev_mask_gpe 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * PARAMETERS: gpe_event_info - GPE to be blocked/unblocked 9562306a36Sopenharmony_ci * is_masked - Whether the GPE is masked or not 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * RETURN: Status 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * DESCRIPTION: Unconditionally mask/unmask a GPE during runtime. 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci ******************************************************************************/ 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ciacpi_status 10462306a36Sopenharmony_ciacpi_ev_mask_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 is_masked) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct acpi_gpe_register_info *gpe_register_info; 10762306a36Sopenharmony_ci u32 register_bit; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci ACPI_FUNCTION_TRACE(ev_mask_gpe); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci gpe_register_info = gpe_event_info->register_info; 11262306a36Sopenharmony_ci if (!gpe_register_info) { 11362306a36Sopenharmony_ci return_ACPI_STATUS(AE_NOT_EXIST); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* Perform the action */ 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (is_masked) { 12162306a36Sopenharmony_ci if (register_bit & gpe_register_info->mask_for_run) { 12262306a36Sopenharmony_ci return_ACPI_STATUS(AE_BAD_PARAMETER); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); 12662306a36Sopenharmony_ci ACPI_SET_BIT(gpe_register_info->mask_for_run, (u8)register_bit); 12762306a36Sopenharmony_ci } else { 12862306a36Sopenharmony_ci if (!(register_bit & gpe_register_info->mask_for_run)) { 12962306a36Sopenharmony_ci return_ACPI_STATUS(AE_BAD_PARAMETER); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ACPI_CLEAR_BIT(gpe_register_info->mask_for_run, 13362306a36Sopenharmony_ci (u8)register_bit); 13462306a36Sopenharmony_ci if (gpe_event_info->runtime_count 13562306a36Sopenharmony_ci && !gpe_event_info->disable_for_dispatch) { 13662306a36Sopenharmony_ci (void)acpi_hw_low_set_gpe(gpe_event_info, 13762306a36Sopenharmony_ci ACPI_GPE_ENABLE); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return_ACPI_STATUS(AE_OK); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/******************************************************************************* 14562306a36Sopenharmony_ci * 14662306a36Sopenharmony_ci * FUNCTION: acpi_ev_add_gpe_reference 14762306a36Sopenharmony_ci * 14862306a36Sopenharmony_ci * PARAMETERS: gpe_event_info - Add a reference to this GPE 14962306a36Sopenharmony_ci * clear_on_enable - Clear GPE status before enabling it 15062306a36Sopenharmony_ci * 15162306a36Sopenharmony_ci * RETURN: Status 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is 15462306a36Sopenharmony_ci * hardware-enabled. 15562306a36Sopenharmony_ci * 15662306a36Sopenharmony_ci ******************************************************************************/ 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ciacpi_status 15962306a36Sopenharmony_ciacpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info, 16062306a36Sopenharmony_ci u8 clear_on_enable) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci acpi_status status = AE_OK; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci ACPI_FUNCTION_TRACE(ev_add_gpe_reference); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (gpe_event_info->runtime_count == ACPI_UINT8_MAX) { 16762306a36Sopenharmony_ci return_ACPI_STATUS(AE_LIMIT); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci gpe_event_info->runtime_count++; 17162306a36Sopenharmony_ci if (gpe_event_info->runtime_count == 1) { 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Enable on first reference */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (clear_on_enable) { 17662306a36Sopenharmony_ci (void)acpi_hw_clear_gpe(gpe_event_info); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci status = acpi_ev_update_gpe_enable_mask(gpe_event_info); 18062306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) { 18162306a36Sopenharmony_ci status = acpi_ev_enable_gpe(gpe_event_info); 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 18562306a36Sopenharmony_ci gpe_event_info->runtime_count--; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return_ACPI_STATUS(status); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/******************************************************************************* 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * FUNCTION: acpi_ev_remove_gpe_reference 19562306a36Sopenharmony_ci * 19662306a36Sopenharmony_ci * PARAMETERS: gpe_event_info - Remove a reference to this GPE 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * RETURN: Status 19962306a36Sopenharmony_ci * 20062306a36Sopenharmony_ci * DESCRIPTION: Remove a reference to a GPE. When the last reference is 20162306a36Sopenharmony_ci * removed, the GPE is hardware-disabled. 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci ******************************************************************************/ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ciacpi_status 20662306a36Sopenharmony_ciacpi_ev_remove_gpe_reference(struct acpi_gpe_event_info *gpe_event_info) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci acpi_status status = AE_OK; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci ACPI_FUNCTION_TRACE(ev_remove_gpe_reference); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (!gpe_event_info->runtime_count) { 21362306a36Sopenharmony_ci return_ACPI_STATUS(AE_LIMIT); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci gpe_event_info->runtime_count--; 21762306a36Sopenharmony_ci if (!gpe_event_info->runtime_count) { 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Disable on last reference */ 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci status = acpi_ev_update_gpe_enable_mask(gpe_event_info); 22262306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) { 22362306a36Sopenharmony_ci status = 22462306a36Sopenharmony_ci acpi_hw_low_set_gpe(gpe_event_info, 22562306a36Sopenharmony_ci ACPI_GPE_DISABLE); 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 22962306a36Sopenharmony_ci gpe_event_info->runtime_count++; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return_ACPI_STATUS(status); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/******************************************************************************* 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci * FUNCTION: acpi_ev_low_get_gpe_info 23962306a36Sopenharmony_ci * 24062306a36Sopenharmony_ci * PARAMETERS: gpe_number - Raw GPE number 24162306a36Sopenharmony_ci * gpe_block - A GPE info block 24262306a36Sopenharmony_ci * 24362306a36Sopenharmony_ci * RETURN: A GPE event_info struct. NULL if not a valid GPE (The gpe_number 24462306a36Sopenharmony_ci * is not within the specified GPE block) 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * DESCRIPTION: Returns the event_info struct associated with this GPE. This is 24762306a36Sopenharmony_ci * the low-level implementation of ev_get_gpe_event_info. 24862306a36Sopenharmony_ci * 24962306a36Sopenharmony_ci ******************************************************************************/ 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistruct acpi_gpe_event_info *acpi_ev_low_get_gpe_info(u32 gpe_number, 25262306a36Sopenharmony_ci struct acpi_gpe_block_info 25362306a36Sopenharmony_ci *gpe_block) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci u32 gpe_index; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* 25862306a36Sopenharmony_ci * Validate that the gpe_number is within the specified gpe_block. 25962306a36Sopenharmony_ci * (Two steps) 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci if (!gpe_block || (gpe_number < gpe_block->block_base_number)) { 26262306a36Sopenharmony_ci return (NULL); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci gpe_index = gpe_number - gpe_block->block_base_number; 26662306a36Sopenharmony_ci if (gpe_index >= gpe_block->gpe_count) { 26762306a36Sopenharmony_ci return (NULL); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return (&gpe_block->event_info[gpe_index]); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/******************************************************************************* 27562306a36Sopenharmony_ci * 27662306a36Sopenharmony_ci * FUNCTION: acpi_ev_get_gpe_event_info 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 27962306a36Sopenharmony_ci * gpe_number - Raw GPE number 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * RETURN: A GPE event_info struct. NULL if not a valid GPE 28262306a36Sopenharmony_ci * 28362306a36Sopenharmony_ci * DESCRIPTION: Returns the event_info struct associated with this GPE. 28462306a36Sopenharmony_ci * Validates the gpe_block and the gpe_number 28562306a36Sopenharmony_ci * 28662306a36Sopenharmony_ci * Should be called only when the GPE lists are semaphore locked 28762306a36Sopenharmony_ci * and not subject to change. 28862306a36Sopenharmony_ci * 28962306a36Sopenharmony_ci ******************************************************************************/ 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistruct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, 29262306a36Sopenharmony_ci u32 gpe_number) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci union acpi_operand_object *obj_desc; 29562306a36Sopenharmony_ci struct acpi_gpe_event_info *gpe_info; 29662306a36Sopenharmony_ci u32 i; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci ACPI_FUNCTION_ENTRY(); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* A NULL gpe_device means use the FADT-defined GPE block(s) */ 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (!gpe_device) { 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* Examine GPE Block 0 and 1 (These blocks are permanent) */ 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci for (i = 0; i < ACPI_MAX_GPE_BLOCKS; i++) { 30762306a36Sopenharmony_ci gpe_info = acpi_ev_low_get_gpe_info(gpe_number, 30862306a36Sopenharmony_ci acpi_gbl_gpe_fadt_blocks 30962306a36Sopenharmony_ci [i]); 31062306a36Sopenharmony_ci if (gpe_info) { 31162306a36Sopenharmony_ci return (gpe_info); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* The gpe_number was not in the range of either FADT GPE block */ 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return (NULL); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* A Non-NULL gpe_device means this is a GPE Block Device */ 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci obj_desc = 32362306a36Sopenharmony_ci acpi_ns_get_attached_object((struct acpi_namespace_node *) 32462306a36Sopenharmony_ci gpe_device); 32562306a36Sopenharmony_ci if (!obj_desc || !obj_desc->device.gpe_block) { 32662306a36Sopenharmony_ci return (NULL); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return (acpi_ev_low_get_gpe_info 33062306a36Sopenharmony_ci (gpe_number, obj_desc->device.gpe_block)); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/******************************************************************************* 33462306a36Sopenharmony_ci * 33562306a36Sopenharmony_ci * FUNCTION: acpi_ev_gpe_detect 33662306a36Sopenharmony_ci * 33762306a36Sopenharmony_ci * PARAMETERS: gpe_xrupt_list - Interrupt block for this interrupt. 33862306a36Sopenharmony_ci * Can have multiple GPE blocks attached. 33962306a36Sopenharmony_ci * 34062306a36Sopenharmony_ci * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED 34162306a36Sopenharmony_ci * 34262306a36Sopenharmony_ci * DESCRIPTION: Detect if any GP events have occurred. This function is 34362306a36Sopenharmony_ci * executed at interrupt level. 34462306a36Sopenharmony_ci * 34562306a36Sopenharmony_ci ******************************************************************************/ 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ciu32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct acpi_gpe_block_info *gpe_block; 35062306a36Sopenharmony_ci struct acpi_namespace_node *gpe_device; 35162306a36Sopenharmony_ci struct acpi_gpe_register_info *gpe_register_info; 35262306a36Sopenharmony_ci struct acpi_gpe_event_info *gpe_event_info; 35362306a36Sopenharmony_ci u32 gpe_number; 35462306a36Sopenharmony_ci u32 int_status = ACPI_INTERRUPT_NOT_HANDLED; 35562306a36Sopenharmony_ci acpi_cpu_flags flags; 35662306a36Sopenharmony_ci u32 i; 35762306a36Sopenharmony_ci u32 j; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ACPI_FUNCTION_NAME(ev_gpe_detect); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Check for the case where there are no GPEs */ 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (!gpe_xrupt_list) { 36462306a36Sopenharmony_ci return (int_status); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* 36862306a36Sopenharmony_ci * We need to obtain the GPE lock for both the data structs and registers 36962306a36Sopenharmony_ci * Note: Not necessary to obtain the hardware lock, since the GPE 37062306a36Sopenharmony_ci * registers are owned by the gpe_lock. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* Examine all GPE blocks attached to this interrupt level */ 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci gpe_block = gpe_xrupt_list->gpe_block_list_head; 37762306a36Sopenharmony_ci while (gpe_block) { 37862306a36Sopenharmony_ci gpe_device = gpe_block->node; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* 38162306a36Sopenharmony_ci * Read all of the 8-bit GPE status and enable registers in this GPE 38262306a36Sopenharmony_ci * block, saving all of them. Find all currently active GP events. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci for (i = 0; i < gpe_block->register_count; i++) { 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Get the next status/enable pair */ 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci gpe_register_info = &gpe_block->register_info[i]; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* 39162306a36Sopenharmony_ci * Optimization: If there are no GPEs enabled within this 39262306a36Sopenharmony_ci * register, we can safely ignore the entire register. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_ci if (!(gpe_register_info->enable_for_run | 39562306a36Sopenharmony_ci gpe_register_info->enable_for_wake)) { 39662306a36Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS, 39762306a36Sopenharmony_ci "Ignore disabled registers for GPE %02X-%02X: " 39862306a36Sopenharmony_ci "RunEnable=%02X, WakeEnable=%02X\n", 39962306a36Sopenharmony_ci gpe_register_info-> 40062306a36Sopenharmony_ci base_gpe_number, 40162306a36Sopenharmony_ci gpe_register_info-> 40262306a36Sopenharmony_ci base_gpe_number + 40362306a36Sopenharmony_ci (ACPI_GPE_REGISTER_WIDTH - 1), 40462306a36Sopenharmony_ci gpe_register_info-> 40562306a36Sopenharmony_ci enable_for_run, 40662306a36Sopenharmony_ci gpe_register_info-> 40762306a36Sopenharmony_ci enable_for_wake)); 40862306a36Sopenharmony_ci continue; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* Now look at the individual GPEs in this byte register */ 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* Detect and dispatch one GPE bit */ 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci gpe_event_info = 41862306a36Sopenharmony_ci &gpe_block-> 41962306a36Sopenharmony_ci event_info[((acpi_size)i * 42062306a36Sopenharmony_ci ACPI_GPE_REGISTER_WIDTH) + j]; 42162306a36Sopenharmony_ci gpe_number = 42262306a36Sopenharmony_ci j + gpe_register_info->base_gpe_number; 42362306a36Sopenharmony_ci acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 42462306a36Sopenharmony_ci int_status |= 42562306a36Sopenharmony_ci acpi_ev_detect_gpe(gpe_device, 42662306a36Sopenharmony_ci gpe_event_info, 42762306a36Sopenharmony_ci gpe_number); 42862306a36Sopenharmony_ci flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci gpe_block = gpe_block->next; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 43662306a36Sopenharmony_ci return (int_status); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/******************************************************************************* 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * FUNCTION: acpi_ev_asynch_execute_gpe_method 44262306a36Sopenharmony_ci * 44362306a36Sopenharmony_ci * PARAMETERS: Context (gpe_event_info) - Info for this GPE 44462306a36Sopenharmony_ci * 44562306a36Sopenharmony_ci * RETURN: None 44662306a36Sopenharmony_ci * 44762306a36Sopenharmony_ci * DESCRIPTION: Perform the actual execution of a GPE control method. This 44862306a36Sopenharmony_ci * function is called from an invocation of acpi_os_execute and 44962306a36Sopenharmony_ci * therefore does NOT execute at interrupt level - so that 45062306a36Sopenharmony_ci * the control method itself is not executed in the context of 45162306a36Sopenharmony_ci * an interrupt handler. 45262306a36Sopenharmony_ci * 45362306a36Sopenharmony_ci ******************************************************************************/ 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct acpi_gpe_event_info *gpe_event_info = context; 45862306a36Sopenharmony_ci acpi_status status = AE_OK; 45962306a36Sopenharmony_ci struct acpi_evaluate_info *info; 46062306a36Sopenharmony_ci struct acpi_gpe_notify_info *notify; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ACPI_FUNCTION_TRACE(ev_asynch_execute_gpe_method); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* Do the correct dispatch - normal method or implicit notify */ 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci switch (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags)) { 46762306a36Sopenharmony_ci case ACPI_GPE_DISPATCH_NOTIFY: 46862306a36Sopenharmony_ci /* 46962306a36Sopenharmony_ci * Implicit notify. 47062306a36Sopenharmony_ci * Dispatch a DEVICE_WAKE notify to the appropriate handler. 47162306a36Sopenharmony_ci * NOTE: the request is queued for execution after this method 47262306a36Sopenharmony_ci * completes. The notify handlers are NOT invoked synchronously 47362306a36Sopenharmony_ci * from this thread -- because handlers may in turn run other 47462306a36Sopenharmony_ci * control methods. 47562306a36Sopenharmony_ci * 47662306a36Sopenharmony_ci * June 2012: Expand implicit notify mechanism to support 47762306a36Sopenharmony_ci * notifies on multiple device objects. 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci notify = gpe_event_info->dispatch.notify_list; 48062306a36Sopenharmony_ci while (ACPI_SUCCESS(status) && notify) { 48162306a36Sopenharmony_ci status = 48262306a36Sopenharmony_ci acpi_ev_queue_notify_request(notify->device_node, 48362306a36Sopenharmony_ci ACPI_NOTIFY_DEVICE_WAKE); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci notify = notify->next; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci break; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci case ACPI_GPE_DISPATCH_METHOD: 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* Allocate the evaluation information block */ 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); 49562306a36Sopenharmony_ci if (!info) { 49662306a36Sopenharmony_ci status = AE_NO_MEMORY; 49762306a36Sopenharmony_ci } else { 49862306a36Sopenharmony_ci /* 49962306a36Sopenharmony_ci * Invoke the GPE Method (_Lxx, _Exx) i.e., evaluate the 50062306a36Sopenharmony_ci * _Lxx/_Exx control method that corresponds to this GPE 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_ci info->prefix_node = 50362306a36Sopenharmony_ci gpe_event_info->dispatch.method_node; 50462306a36Sopenharmony_ci info->flags = ACPI_IGNORE_RETURN_VALUE; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci status = acpi_ns_evaluate(info); 50762306a36Sopenharmony_ci ACPI_FREE(info); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 51162306a36Sopenharmony_ci ACPI_EXCEPTION((AE_INFO, status, 51262306a36Sopenharmony_ci "while evaluating GPE method [%4.4s]", 51362306a36Sopenharmony_ci acpi_ut_get_node_name(gpe_event_info-> 51462306a36Sopenharmony_ci dispatch. 51562306a36Sopenharmony_ci method_node))); 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci default: 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci goto error_exit; /* Should never happen */ 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* Defer enabling of GPE until all notify handlers are done */ 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci status = acpi_os_execute(OSL_NOTIFY_HANDLER, 52762306a36Sopenharmony_ci acpi_ev_asynch_enable_gpe, gpe_event_info); 52862306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) { 52962306a36Sopenharmony_ci return_VOID; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cierror_exit: 53362306a36Sopenharmony_ci acpi_ev_asynch_enable_gpe(gpe_event_info); 53462306a36Sopenharmony_ci return_VOID; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci/******************************************************************************* 53962306a36Sopenharmony_ci * 54062306a36Sopenharmony_ci * FUNCTION: acpi_ev_asynch_enable_gpe 54162306a36Sopenharmony_ci * 54262306a36Sopenharmony_ci * PARAMETERS: Context (gpe_event_info) - Info for this GPE 54362306a36Sopenharmony_ci * Callback from acpi_os_execute 54462306a36Sopenharmony_ci * 54562306a36Sopenharmony_ci * RETURN: None 54662306a36Sopenharmony_ci * 54762306a36Sopenharmony_ci * DESCRIPTION: Asynchronous clear/enable for GPE. This allows the GPE to 54862306a36Sopenharmony_ci * complete (i.e., finish execution of Notify) 54962306a36Sopenharmony_ci * 55062306a36Sopenharmony_ci ******************************************************************************/ 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic void ACPI_SYSTEM_XFACE acpi_ev_asynch_enable_gpe(void *context) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct acpi_gpe_event_info *gpe_event_info = context; 55562306a36Sopenharmony_ci acpi_cpu_flags flags; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); 55862306a36Sopenharmony_ci (void)acpi_ev_finish_gpe(gpe_event_info); 55962306a36Sopenharmony_ci acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/******************************************************************************* 56662306a36Sopenharmony_ci * 56762306a36Sopenharmony_ci * FUNCTION: acpi_ev_finish_gpe 56862306a36Sopenharmony_ci * 56962306a36Sopenharmony_ci * PARAMETERS: gpe_event_info - Info for this GPE 57062306a36Sopenharmony_ci * 57162306a36Sopenharmony_ci * RETURN: Status 57262306a36Sopenharmony_ci * 57362306a36Sopenharmony_ci * DESCRIPTION: Clear/Enable a GPE. Common code that is used after execution 57462306a36Sopenharmony_ci * of a GPE method or a synchronous or asynchronous GPE handler. 57562306a36Sopenharmony_ci * 57662306a36Sopenharmony_ci ******************************************************************************/ 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ciacpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci acpi_status status; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == 58362306a36Sopenharmony_ci ACPI_GPE_LEVEL_TRIGGERED) { 58462306a36Sopenharmony_ci /* 58562306a36Sopenharmony_ci * GPE is level-triggered, we clear the GPE status bit after 58662306a36Sopenharmony_ci * handling the event. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ci status = acpi_hw_clear_gpe(gpe_event_info); 58962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 59062306a36Sopenharmony_ci return (status); 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* 59562306a36Sopenharmony_ci * Enable this GPE, conditionally. This means that the GPE will 59662306a36Sopenharmony_ci * only be physically enabled if the enable_mask bit is set 59762306a36Sopenharmony_ci * in the event_info. 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_ci (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE); 60062306a36Sopenharmony_ci gpe_event_info->disable_for_dispatch = FALSE; 60162306a36Sopenharmony_ci return (AE_OK); 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/******************************************************************************* 60662306a36Sopenharmony_ci * 60762306a36Sopenharmony_ci * FUNCTION: acpi_ev_detect_gpe 60862306a36Sopenharmony_ci * 60962306a36Sopenharmony_ci * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 61062306a36Sopenharmony_ci * gpe_event_info - Info for this GPE 61162306a36Sopenharmony_ci * gpe_number - Number relative to the parent GPE block 61262306a36Sopenharmony_ci * 61362306a36Sopenharmony_ci * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED 61462306a36Sopenharmony_ci * 61562306a36Sopenharmony_ci * DESCRIPTION: Detect and dispatch a General Purpose Event to either a function 61662306a36Sopenharmony_ci * (e.g. EC) or method (e.g. _Lxx/_Exx) handler. 61762306a36Sopenharmony_ci * NOTE: GPE is W1C, so it is possible to handle a single GPE from both 61862306a36Sopenharmony_ci * task and irq context in parallel as long as the process to 61962306a36Sopenharmony_ci * detect and mask the GPE is atomic. 62062306a36Sopenharmony_ci * However the atomicity of ACPI_GPE_DISPATCH_RAW_HANDLER is 62162306a36Sopenharmony_ci * dependent on the raw handler itself. 62262306a36Sopenharmony_ci * 62362306a36Sopenharmony_ci ******************************************************************************/ 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ciu32 62662306a36Sopenharmony_ciacpi_ev_detect_gpe(struct acpi_namespace_node *gpe_device, 62762306a36Sopenharmony_ci struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci u32 int_status = ACPI_INTERRUPT_NOT_HANDLED; 63062306a36Sopenharmony_ci u8 enabled_status_byte; 63162306a36Sopenharmony_ci u64 status_reg; 63262306a36Sopenharmony_ci u64 enable_reg; 63362306a36Sopenharmony_ci u32 register_bit; 63462306a36Sopenharmony_ci struct acpi_gpe_register_info *gpe_register_info; 63562306a36Sopenharmony_ci struct acpi_gpe_handler_info *gpe_handler_info; 63662306a36Sopenharmony_ci acpi_cpu_flags flags; 63762306a36Sopenharmony_ci acpi_status status; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci ACPI_FUNCTION_TRACE(ev_gpe_detect); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (!gpe_event_info) { 64462306a36Sopenharmony_ci gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); 64562306a36Sopenharmony_ci if (!gpe_event_info) 64662306a36Sopenharmony_ci goto error_exit; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* Get the info block for the entire GPE register */ 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci gpe_register_info = gpe_event_info->register_info; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Get the register bitmask for this GPE */ 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* GPE currently enabled (enable bit == 1)? */ 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci status = acpi_hw_gpe_read(&enable_reg, &gpe_register_info->enable_address); 66062306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 66162306a36Sopenharmony_ci goto error_exit; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* GPE currently active (status bit == 1)? */ 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci status = acpi_hw_gpe_read(&status_reg, &gpe_register_info->status_address); 66762306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 66862306a36Sopenharmony_ci goto error_exit; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* Check if there is anything active at all in this GPE */ 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS, 67462306a36Sopenharmony_ci "Read registers for GPE %02X: Status=%02X, Enable=%02X, " 67562306a36Sopenharmony_ci "RunEnable=%02X, WakeEnable=%02X\n", 67662306a36Sopenharmony_ci gpe_number, 67762306a36Sopenharmony_ci (u32)(status_reg & register_bit), 67862306a36Sopenharmony_ci (u32)(enable_reg & register_bit), 67962306a36Sopenharmony_ci gpe_register_info->enable_for_run, 68062306a36Sopenharmony_ci gpe_register_info->enable_for_wake)); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci enabled_status_byte = (u8)(status_reg & enable_reg); 68362306a36Sopenharmony_ci if (!(enabled_status_byte & register_bit)) { 68462306a36Sopenharmony_ci goto error_exit; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* Invoke global event handler if present */ 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci acpi_gpe_count++; 69062306a36Sopenharmony_ci if (acpi_gbl_global_event_handler) { 69162306a36Sopenharmony_ci acpi_gbl_global_event_handler(ACPI_EVENT_TYPE_GPE, 69262306a36Sopenharmony_ci gpe_device, gpe_number, 69362306a36Sopenharmony_ci acpi_gbl_global_event_handler_context); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* Found an active GPE */ 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == 69962306a36Sopenharmony_ci ACPI_GPE_DISPATCH_RAW_HANDLER) { 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* Dispatch the event to a raw handler */ 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci gpe_handler_info = gpe_event_info->dispatch.handler; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* 70662306a36Sopenharmony_ci * There is no protection around the namespace node 70762306a36Sopenharmony_ci * and the GPE handler to ensure a safe destruction 70862306a36Sopenharmony_ci * because: 70962306a36Sopenharmony_ci * 1. The namespace node is expected to always 71062306a36Sopenharmony_ci * exist after loading a table. 71162306a36Sopenharmony_ci * 2. The GPE handler is expected to be flushed by 71262306a36Sopenharmony_ci * acpi_os_wait_events_complete() before the 71362306a36Sopenharmony_ci * destruction. 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 71662306a36Sopenharmony_ci int_status |= 71762306a36Sopenharmony_ci gpe_handler_info->address(gpe_device, gpe_number, 71862306a36Sopenharmony_ci gpe_handler_info->context); 71962306a36Sopenharmony_ci flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); 72062306a36Sopenharmony_ci } else { 72162306a36Sopenharmony_ci /* Dispatch the event to a standard handler or method. */ 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci int_status |= acpi_ev_gpe_dispatch(gpe_device, 72462306a36Sopenharmony_ci gpe_event_info, gpe_number); 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cierror_exit: 72862306a36Sopenharmony_ci acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 72962306a36Sopenharmony_ci return (int_status); 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci/******************************************************************************* 73362306a36Sopenharmony_ci * 73462306a36Sopenharmony_ci * FUNCTION: acpi_ev_gpe_dispatch 73562306a36Sopenharmony_ci * 73662306a36Sopenharmony_ci * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 73762306a36Sopenharmony_ci * gpe_event_info - Info for this GPE 73862306a36Sopenharmony_ci * gpe_number - Number relative to the parent GPE block 73962306a36Sopenharmony_ci * 74062306a36Sopenharmony_ci * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED 74162306a36Sopenharmony_ci * 74262306a36Sopenharmony_ci * DESCRIPTION: Dispatch a General Purpose Event to either a function (e.g. EC) 74362306a36Sopenharmony_ci * or method (e.g. _Lxx/_Exx) handler. 74462306a36Sopenharmony_ci * 74562306a36Sopenharmony_ci ******************************************************************************/ 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ciu32 74862306a36Sopenharmony_ciacpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, 74962306a36Sopenharmony_ci struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci acpi_status status; 75262306a36Sopenharmony_ci u32 return_value; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci ACPI_FUNCTION_TRACE(ev_gpe_dispatch); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* 75762306a36Sopenharmony_ci * Always disable the GPE so that it does not keep firing before 75862306a36Sopenharmony_ci * any asynchronous activity completes (either from the execution 75962306a36Sopenharmony_ci * of a GPE method or an asynchronous GPE handler.) 76062306a36Sopenharmony_ci * 76162306a36Sopenharmony_ci * If there is no handler or method to run, just disable the 76262306a36Sopenharmony_ci * GPE and leave it disabled permanently to prevent further such 76362306a36Sopenharmony_ci * pointless events from firing. 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_ci status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); 76662306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 76762306a36Sopenharmony_ci ACPI_EXCEPTION((AE_INFO, status, 76862306a36Sopenharmony_ci "Unable to disable GPE %02X", gpe_number)); 76962306a36Sopenharmony_ci return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* 77362306a36Sopenharmony_ci * If edge-triggered, clear the GPE status bit now. Note that 77462306a36Sopenharmony_ci * level-triggered events are cleared after the GPE is serviced. 77562306a36Sopenharmony_ci */ 77662306a36Sopenharmony_ci if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == 77762306a36Sopenharmony_ci ACPI_GPE_EDGE_TRIGGERED) { 77862306a36Sopenharmony_ci status = acpi_hw_clear_gpe(gpe_event_info); 77962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 78062306a36Sopenharmony_ci ACPI_EXCEPTION((AE_INFO, status, 78162306a36Sopenharmony_ci "Unable to clear GPE %02X", 78262306a36Sopenharmony_ci gpe_number)); 78362306a36Sopenharmony_ci (void)acpi_hw_low_set_gpe(gpe_event_info, 78462306a36Sopenharmony_ci ACPI_GPE_CONDITIONAL_ENABLE); 78562306a36Sopenharmony_ci return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci gpe_event_info->disable_for_dispatch = TRUE; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* 79262306a36Sopenharmony_ci * Dispatch the GPE to either an installed handler or the control 79362306a36Sopenharmony_ci * method associated with this GPE (_Lxx or _Exx). If a handler 79462306a36Sopenharmony_ci * exists, we invoke it and do not attempt to run the method. 79562306a36Sopenharmony_ci * If there is neither a handler nor a method, leave the GPE 79662306a36Sopenharmony_ci * disabled. 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_ci switch (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags)) { 79962306a36Sopenharmony_ci case ACPI_GPE_DISPATCH_HANDLER: 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* Invoke the installed handler (at interrupt level) */ 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci return_value = 80462306a36Sopenharmony_ci gpe_event_info->dispatch.handler->address(gpe_device, 80562306a36Sopenharmony_ci gpe_number, 80662306a36Sopenharmony_ci gpe_event_info-> 80762306a36Sopenharmony_ci dispatch.handler-> 80862306a36Sopenharmony_ci context); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* If requested, clear (if level-triggered) and re-enable the GPE */ 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (return_value & ACPI_REENABLE_GPE) { 81362306a36Sopenharmony_ci (void)acpi_ev_finish_gpe(gpe_event_info); 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci break; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci case ACPI_GPE_DISPATCH_METHOD: 81862306a36Sopenharmony_ci case ACPI_GPE_DISPATCH_NOTIFY: 81962306a36Sopenharmony_ci /* 82062306a36Sopenharmony_ci * Execute the method associated with the GPE 82162306a36Sopenharmony_ci * NOTE: Level-triggered GPEs are cleared after the method completes. 82262306a36Sopenharmony_ci */ 82362306a36Sopenharmony_ci status = acpi_os_execute(OSL_GPE_HANDLER, 82462306a36Sopenharmony_ci acpi_ev_asynch_execute_gpe_method, 82562306a36Sopenharmony_ci gpe_event_info); 82662306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 82762306a36Sopenharmony_ci ACPI_EXCEPTION((AE_INFO, status, 82862306a36Sopenharmony_ci "Unable to queue handler for GPE %02X - event disabled", 82962306a36Sopenharmony_ci gpe_number)); 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci break; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci default: 83462306a36Sopenharmony_ci /* 83562306a36Sopenharmony_ci * No handler or method to run! 83662306a36Sopenharmony_ci * 03/2010: This case should no longer be possible. We will not allow 83762306a36Sopenharmony_ci * a GPE to be enabled if it has no handler or method. 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_ci ACPI_ERROR((AE_INFO, 84062306a36Sopenharmony_ci "No handler or method for GPE %02X, disabling event", 84162306a36Sopenharmony_ci gpe_number)); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci break; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return_UINT32(ACPI_INTERRUPT_HANDLED); 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci#endif /* !ACPI_REDUCED_HARDWARE */ 850