diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index fa68544c7c0757c52726d9533b7387dc9dc4536f..1ad1608cc88057e9788537970b6d1d2381383c62 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -44,6 +44,20 @@ #ifndef __ACEVENTS_H__ #define __ACEVENTS_H__ +/* + * Conditions to trigger post enabling GPE polling: + * It is not sufficient to trigger edge-triggered GPE with specific GPE + * chips, software need to poll once after enabling. + */ +#ifdef ACPI_USE_GPE_POLLING +#define ACPI_GPE_IS_POLLING_NEEDED(__gpe__) \ + ((__gpe__)->runtime_count == 1 && \ + (__gpe__)->flags & ACPI_GPE_INITIALIZED && \ + ((__gpe__)->flags & ACPI_GPE_XRUPT_TYPE_MASK) == ACPI_GPE_EDGE_TRIGGERED) +#else +#define ACPI_GPE_IS_POLLING_NEEDED(__gpe__) FALSE +#endif + /* * evevent */ diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index 7ce756cc28abb4189dc6cae94d01f19f11d37f50..107f3ec5dee6787da2fbda1812f825476e9ac2be 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -437,16 +437,16 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, acpi_status acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, struct acpi_gpe_block_info *gpe_block, - void *ignored) + void *context) { acpi_status status; - acpi_event_status event_status; struct acpi_gpe_event_info *gpe_event_info; u32 gpe_enabled_count; u32 gpe_index; u32 gpe_number; u32 i; u32 j; + u8 *is_polling_needed = context; ACPI_FUNCTION_TRACE(ev_initialize_gpe_block); @@ -473,6 +473,7 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, gpe_index = (i * ACPI_GPE_REGISTER_WIDTH) + j; gpe_event_info = &gpe_block->event_info[gpe_index]; gpe_number = gpe_block->block_base_number + gpe_index; + gpe_event_info->flags |= ACPI_GPE_INITIALIZED; /* * Ignore GPEs that have no corresponding _Lxx/_Exx method @@ -484,10 +485,6 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, continue; } - event_status = 0; - (void)acpi_hw_get_gpe_status(gpe_event_info, - &event_status); - status = acpi_ev_add_gpe_reference(gpe_event_info); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, @@ -498,12 +495,9 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, gpe_event_info->flags |= ACPI_GPE_AUTO_ENABLED; - if (event_status & ACPI_EVENT_FLAG_STATUS_SET) { - ACPI_INFO(("GPE 0x%02X active on init", - gpe_number)); - (void)acpi_ev_gpe_dispatch(gpe_block->node, - gpe_event_info, - gpe_number); + if (is_polling_needed && + ACPI_GPE_IS_POLLING_NEEDED(gpe_event_info)) { + *is_polling_needed = TRUE; } gpe_enabled_count++; diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 9b3c01bf1438ec44fd1ef683e72c8d05e9da7ea5..b6e462f33b0d0b1e18fc4e45ace46df1ecf1142a 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -1006,6 +1006,15 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, (ACPI_GPE_DISPATCH_TYPE(handler->original_flags) == ACPI_GPE_DISPATCH_NOTIFY)) && handler->originally_enabled) { (void)acpi_ev_add_gpe_reference(gpe_event_info); + if (ACPI_GPE_IS_POLLING_NEEDED(gpe_event_info)) { + + /* Poll edge triggered GPEs to handle existing events */ + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + (void)acpi_ev_detect_gpe(gpe_device, gpe_event_info, + gpe_number); + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + } } acpi_os_release_lock(acpi_gbl_gpe_lock, flags); diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index cbb1598df9dcfc915b58088f2d3cf71f9171c07c..f9303444f7f75b0023a8b6541c5d60f08774eee0 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -77,6 +77,7 @@ ACPI_MODULE_NAME("evxfgpe") acpi_status acpi_update_all_gpes(void) { acpi_status status; + u8 is_polling_needed = FALSE; ACPI_FUNCTION_TRACE(acpi_update_all_gpes); @@ -89,7 +90,8 @@ acpi_status acpi_update_all_gpes(void) goto unlock_and_exit; } - status = acpi_ev_walk_gpe_list(acpi_ev_initialize_gpe_block, NULL); + status = acpi_ev_walk_gpe_list(acpi_ev_initialize_gpe_block, + &is_polling_needed); if (ACPI_SUCCESS(status)) { acpi_gbl_all_gpes_initialized = TRUE; } @@ -97,6 +99,12 @@ acpi_status acpi_update_all_gpes(void) unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + if (is_polling_needed && acpi_gbl_all_gpes_initialized) { + + /* Poll GPEs to handle already triggered events */ + + acpi_ev_gpe_detect(acpi_gbl_gpe_xrupt_list_head); + } return_ACPI_STATUS(status); } @@ -135,6 +143,17 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) != ACPI_GPE_DISPATCH_NONE) { status = acpi_ev_add_gpe_reference(gpe_event_info); + if (ACPI_SUCCESS(status) && + ACPI_GPE_IS_POLLING_NEEDED(gpe_event_info)) { + + /* Poll edge-triggered GPEs to handle existing events */ + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + (void)acpi_ev_detect_gpe(gpe_device, + gpe_event_info, + gpe_number); + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + } } else { status = AE_NO_HANDLER; } diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 310b542abe238e2ec674f4bf1a32591d751892b6..a894ea13dcba00fbe31a7531e5493a55cc19afc6 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -802,6 +802,7 @@ typedef u32 acpi_event_status; #define ACPI_GPE_CAN_WAKE (u8) 0x10 #define ACPI_GPE_AUTO_ENABLED (u8) 0x20 +#define ACPI_GPE_INITIALIZED (u8) 0x40 /* * Flags for GPE and Lock interfaces diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index e6e7572541387406c0d05c797ebb1c6a32c7614e..88c50bbcc4d0a7158ad7c3dea5dd704524e6456b 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -63,6 +63,7 @@ #ifdef __KERNEL__ #define ACPI_USE_SYSTEM_INTTYPES +#define ACPI_USE_GPE_POLLING /* Kernel specific ACPICA configuration */