From patchwork Tue Jun 29 10:48:42 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rafael Wysocki X-Patchwork-Id: 108537 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o5TDIm3i012137 for ; Tue, 29 Jun 2010 13:19:29 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755283Ab0F2Ku0 (ORCPT ); Tue, 29 Jun 2010 06:50:26 -0400 Received: from ogre.sisk.pl ([217.79.144.158]:59957 "EHLO ogre.sisk.pl" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755257Ab0F2KuZ (ORCPT ); Tue, 29 Jun 2010 06:50:25 -0400 Received: from localhost (localhost.localdomain [127.0.0.1]) by ogre.sisk.pl (Postfix) with ESMTP id EBEF4189F8E; Tue, 29 Jun 2010 12:37:30 +0200 (CEST) Received: from ogre.sisk.pl ([127.0.0.1]) by localhost (ogre.sisk.pl [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 12392-04; Tue, 29 Jun 2010 12:37:23 +0200 (CEST) Received: from ferrari.localnet (220-bem-13.acn.waw.pl [82.210.184.220]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by ogre.sisk.pl (Postfix) with ESMTP id 28D1C1884FB; Tue, 29 Jun 2010 12:37:23 +0200 (CEST) From: "Rafael J. Wysocki" To: Greg KH Subject: Stable inclusion request - ACPI GPE regression fixes Date: Tue, 29 Jun 2010 12:48:42 +0200 User-Agent: KMail/1.13.3 (Linux/2.6.35-rc3-rjw+; KDE/4.4.3; x86_64; ; ) Cc: ACPI Devel Maling List , Len Brown , stable@kernel.org, Matthew Garrett MIME-Version: 1.0 Message-Id: <201006291248.42552.rjw@sisk.pl> X-Length: 1311 X-UID: 5366 X-Virus-Scanned: amavisd-new at ogre.sisk.pl using MkS_Vir for Linux Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Tue, 29 Jun 2010 13:19:29 +0000 (UTC) From fd247447c1d94a79d5cfc647430784306b3a8323 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Tue, 8 Jun 2010 10:49:08 +0200 Subject: [PATCH] ACPI / ACPICA: Fix low-level GPE manipulation code ACPICA uses acpi_ev_enable_gpe() for enabling GPEs at the low level, which is incorrect, because this function only enables the GPE if the corresponding bit in its enable register's enable_for_run mask is set. This causes acpi_set_gpe() to work incorrectly if used for enabling GPEs that were not previously enabled with acpi_enable_gpe(). As a result, among other things, wakeup-only GPEs are never enabled by acpi_enable_wakeup_device(), so the devices that use them are unable to wake up the system. To fix this issue remove acpi_ev_enable_gpe() and its counterpart acpi_ev_disable_gpe() and replace acpi_hw_low_disable_gpe() with acpi_hw_low_set_gpe() that will be used instead to manipulate GPE enable bits at the low level. Make the users of acpi_ev_enable_gpe() and acpi_ev_disable_gpe() call acpi_hw_low_set_gpe() instead and make sure that GPE enable masks are only updated by acpi_enable_gpe() and acpi_disable_gpe() when GPE reference counters change from 0 to 1 and from 1 to 0, respectively. Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/acpi/acpica/acevents.h | 4 -- drivers/acpi/acpica/achware.h | 3 + drivers/acpi/acpica/evgpe.c | 78 +---------------------------------------- drivers/acpi/acpica/evxfevnt.c | 66 ++++++++++++++++++++++++++++++---- drivers/acpi/acpica/hwgpe.c | 26 +++++++++++-- include/acpi/acexcep.h | 2 - 6 files changed, 84 insertions(+), 95 deletions(-) Index: linux-2.6-stable/drivers/acpi/acpica/acevents.h =================================================================== --- linux-2.6-stable.orig/drivers/acpi/acpica/acevents.h +++ linux-2.6-stable/drivers/acpi/acpica/acevents.h @@ -78,10 +78,6 @@ acpi_ev_queue_notify_request(struct acpi acpi_status 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); - -acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info); - struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, u32 gpe_number); Index: linux-2.6-stable/drivers/acpi/acpica/achware.h =================================================================== --- linux-2.6-stable.orig/drivers/acpi/acpica/achware.h +++ linux-2.6-stable/drivers/acpi/acpica/achware.h @@ -93,7 +93,8 @@ acpi_status acpi_hw_write_port(acpi_io_a u32 acpi_hw_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info, struct acpi_gpe_register_info *gpe_register_info); -acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info); +acpi_status +acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 action); acpi_status acpi_hw_write_gpe_enable_reg(struct acpi_gpe_event_info *gpe_event_info); Index: linux-2.6-stable/drivers/acpi/acpica/evgpe.c =================================================================== --- linux-2.6-stable.orig/drivers/acpi/acpica/evgpe.c +++ linux-2.6-stable/drivers/acpi/acpica/evgpe.c @@ -94,76 +94,6 @@ acpi_ev_update_gpe_enable_masks(struct a /******************************************************************************* * - * FUNCTION: acpi_ev_enable_gpe - * - * PARAMETERS: gpe_event_info - GPE to enable - * - * RETURN: Status - * - * DESCRIPTION: Enable a GPE based on the GPE type - * - ******************************************************************************/ - -acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) -{ - acpi_status status; - - ACPI_FUNCTION_TRACE(ev_enable_gpe); - - /* Make sure HW enable masks are updated */ - - status = acpi_ev_update_gpe_enable_masks(gpe_event_info); - if (ACPI_FAILURE(status)) - return_ACPI_STATUS(status); - - /* 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 GPE */ - status = acpi_hw_write_gpe_enable_reg(gpe_event_info); - return_ACPI_STATUS(status); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ev_disable_gpe - * - * PARAMETERS: gpe_event_info - GPE to disable - * - * RETURN: Status - * - * DESCRIPTION: Disable a GPE based on the GPE type - * - ******************************************************************************/ - -acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) -{ - acpi_status status; - - ACPI_FUNCTION_TRACE(ev_disable_gpe); - - /* Make sure HW enable masks are updated */ - - status = acpi_ev_update_gpe_enable_masks(gpe_event_info); - if (ACPI_FAILURE(status)) - return_ACPI_STATUS(status); - - /* - * Even if we don't know the GPE type, make sure that we always - * disable it. low_disable_gpe will just clear the enable bit for this - * GPE and write it. It will not write out the current GPE enable mask, - * since this may inadvertently enable GPEs too early, if a rogue GPE has - * come in during ACPICA initialization - possibly as a result of AML or - * other code that has enabled the GPE. - */ - status = acpi_hw_low_disable_gpe(gpe_event_info); - return_ACPI_STATUS(status); -} - -/******************************************************************************* - * * FUNCTION: acpi_ev_get_gpe_event_info * * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 @@ -388,10 +318,6 @@ static void ACPI_SYSTEM_XFACE acpi_ev_as return_VOID; } - /* Set the GPE flags for return to enabled state */ - - (void)acpi_ev_update_gpe_enable_masks(gpe_event_info); - /* * Take a snapshot of the GPE info for this level - we copy the info to * prevent a race condition with remove_handler/remove_block. @@ -544,7 +470,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_eve * Disable the GPE, so it doesn't keep firing before the method has a * chance to run (it runs asynchronously with interrupts enabled). */ - status = acpi_ev_disable_gpe(gpe_event_info); + status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Unable to disable GPE[%2X]", @@ -578,7 +504,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_eve * Disable the GPE. The GPE will remain disabled until the ACPICA * Core Subsystem is restarted, or a handler is installed. */ - status = acpi_ev_disable_gpe(gpe_event_info); + status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Unable to disable GPE[%2X]", Index: linux-2.6-stable/drivers/acpi/acpica/evxfevnt.c =================================================================== --- linux-2.6-stable.orig/drivers/acpi/acpica/evxfevnt.c +++ linux-2.6-stable/drivers/acpi/acpica/evxfevnt.c @@ -201,6 +201,44 @@ ACPI_EXPORT_SYMBOL(acpi_enable_event) /******************************************************************************* * + * FUNCTION: acpi_clear_and_enable_gpe + * + * PARAMETERS: gpe_event_info - GPE to enable + * + * RETURN: Status + * + * DESCRIPTION: Clear the given GPE from stale events and enable it. + * + ******************************************************************************/ +static acpi_status +acpi_clear_and_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) +{ + acpi_status status; + + /* + * We will only allow a GPE to be enabled if it has either an + * associated method (_Lxx/_Exx) or a handler. Otherwise, the + * GPE will be immediately disabled by acpi_ev_gpe_dispatch the + * first time it fires. + */ + if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK)) { + return_ACPI_STATUS(AE_NO_HANDLER); + } + + /* Clear the GPE (of stale events) */ + status = acpi_hw_clear_gpe(gpe_event_info); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Enable the requested GPE */ + status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE); + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * * FUNCTION: acpi_set_gpe * * PARAMETERS: gpe_device - Parent GPE Device @@ -235,11 +273,11 @@ acpi_status acpi_set_gpe(acpi_handle gpe switch (action) { case ACPI_GPE_ENABLE: - status = acpi_ev_enable_gpe(gpe_event_info); + status = acpi_clear_and_enable_gpe(gpe_event_info); break; case ACPI_GPE_DISABLE: - status = acpi_ev_disable_gpe(gpe_event_info); + status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); break; default: @@ -291,9 +329,13 @@ acpi_status acpi_enable_gpe(acpi_handle if (type & ACPI_GPE_TYPE_RUNTIME) { if (++gpe_event_info->runtime_count == 1) { - status = acpi_ev_enable_gpe(gpe_event_info); - if (ACPI_FAILURE(status)) + status = acpi_ev_update_gpe_enable_masks(gpe_event_info); + if (ACPI_SUCCESS(status)) { + status = acpi_clear_and_enable_gpe(gpe_event_info); + } + if (ACPI_FAILURE(status)) { gpe_event_info->runtime_count--; + } } } @@ -308,7 +350,7 @@ acpi_status acpi_enable_gpe(acpi_handle * system into a sleep state. */ if (++gpe_event_info->wakeup_count == 1) - acpi_ev_update_gpe_enable_masks(gpe_event_info); + status = acpi_ev_update_gpe_enable_masks(gpe_event_info); } unlock_and_exit: @@ -351,8 +393,16 @@ acpi_status acpi_disable_gpe(acpi_handle } if ((type & ACPI_GPE_TYPE_RUNTIME) && gpe_event_info->runtime_count) { - if (--gpe_event_info->runtime_count == 0) - status = acpi_ev_disable_gpe(gpe_event_info); + if (--gpe_event_info->runtime_count == 0) { + status = acpi_ev_update_gpe_enable_masks(gpe_event_info); + if (ACPI_SUCCESS(status)) { + status = acpi_hw_low_set_gpe(gpe_event_info, + ACPI_GPE_DISABLE); + } + if (ACPI_FAILURE(status)) { + gpe_event_info->runtime_count++; + } + } } if ((type & ACPI_GPE_TYPE_WAKE) && gpe_event_info->wakeup_count) { @@ -361,7 +411,7 @@ acpi_status acpi_disable_gpe(acpi_handle * states, so we don't need to disable them here. */ if (--gpe_event_info->wakeup_count == 0) - acpi_ev_update_gpe_enable_masks(gpe_event_info); + status = acpi_ev_update_gpe_enable_masks(gpe_event_info); } unlock_and_exit: Index: linux-2.6-stable/drivers/acpi/acpica/hwgpe.c =================================================================== --- linux-2.6-stable.orig/drivers/acpi/acpica/hwgpe.c +++ linux-2.6-stable/drivers/acpi/acpica/hwgpe.c @@ -78,23 +78,27 @@ u32 acpi_hw_gpe_register_bit(struct acpi /****************************************************************************** * - * FUNCTION: acpi_hw_low_disable_gpe + * FUNCTION: acpi_hw_low_set_gpe * * PARAMETERS: gpe_event_info - Info block for the GPE to be disabled + * action - Enable or disable * * RETURN: Status * - * DESCRIPTION: Disable a single GPE in the enable register. + * DESCRIPTION: Enable or disable a single GPE in its enable register. * ******************************************************************************/ -acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) +acpi_status +acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 action) { struct acpi_gpe_register_info *gpe_register_info; acpi_status status; u32 enable_mask; u32 register_bit; + ACPI_FUNCTION_ENTRY(); + /* Get the info block for the entire GPE register */ gpe_register_info = gpe_event_info->register_info; @@ -109,11 +113,23 @@ acpi_status acpi_hw_low_disable_gpe(stru return (status); } - /* Clear just the bit that corresponds to this GPE */ + /* Set or clear just the bit that corresponds to this GPE */ register_bit = acpi_hw_gpe_register_bit(gpe_event_info, gpe_register_info); - ACPI_CLEAR_BIT(enable_mask, register_bit); + switch (action) { + case ACPI_GPE_ENABLE: + ACPI_SET_BIT(enable_mask, register_bit); + break; + + case ACPI_GPE_DISABLE: + ACPI_CLEAR_BIT(enable_mask, register_bit); + break; + + default: + ACPI_ERROR((AE_INFO, "Invalid action\n")); + return (AE_BAD_PARAMETER); + } /* Write the updated enable mask */ Index: linux-2.6-stable/include/acpi/acexcep.h =================================================================== --- linux-2.6-stable.orig/include/acpi/acexcep.h +++ linux-2.6-stable/include/acpi/acexcep.h @@ -87,7 +87,7 @@ #define AE_NO_GLOBAL_LOCK (acpi_status) (0x0017 | AE_CODE_ENVIRONMENTAL) #define AE_ABORT_METHOD (acpi_status) (0x0018 | AE_CODE_ENVIRONMENTAL) #define AE_SAME_HANDLER (acpi_status) (0x0019 | AE_CODE_ENVIRONMENTAL) -#define AE_WAKE_ONLY_GPE (acpi_status) (0x001A | AE_CODE_ENVIRONMENTAL) +#define AE_NO_HANDLER (acpi_status) (0x001A | AE_CODE_ENVIRONMENTAL) #define AE_OWNER_ID_LIMIT (acpi_status) (0x001B | AE_CODE_ENVIRONMENTAL) #define AE_CODE_ENV_MAX 0x001B