From patchwork Thu May 20 22:11:01 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rafael Wysocki X-Patchwork-Id: 101264 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o4KM9xeK017342 for ; Thu, 20 May 2010 22:09:59 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755166Ab0ETWJ6 (ORCPT ); Thu, 20 May 2010 18:09:58 -0400 Received: from ogre.sisk.pl ([217.79.144.158]:53212 "EHLO ogre.sisk.pl" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755163Ab0ETWJ5 (ORCPT ); Thu, 20 May 2010 18:09:57 -0400 Received: from localhost (localhost.localdomain [127.0.0.1]) by ogre.sisk.pl (Postfix) with ESMTP id 5A22617F43F; Fri, 21 May 2010 00:09:53 +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 01532-08; Fri, 21 May 2010 00:09:45 +0200 (CEST) Received: from tosh.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 A501C17DA42; Fri, 21 May 2010 00:09:45 +0200 (CEST) From: "Rafael J. Wysocki" To: ACPI Devel Maling List Subject: [PATCH] ACPI / ACPICA: Fix problems related with GPE reference counting Date: Fri, 21 May 2010 00:11:01 +0200 User-Agent: KMail/1.12.4 (Linux/2.6.34-rjw; KDE/4.3.5; x86_64; ; ) Cc: Len Brown , Matthew Garrett , Zhang Rui , Robert Moore MIME-Version: 1.0 Message-Id: <201005210011.01955.rjw@sisk.pl> 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]); Thu, 20 May 2010 22:09:59 +0000 (UTC) Index: linux-2.6/drivers/acpi/acpica/hwgpe.c =================================================================== --- linux-2.6.orig/drivers/acpi/acpica/hwgpe.c +++ linux-2.6/drivers/acpi/acpica/hwgpe.c @@ -57,21 +57,45 @@ acpi_hw_enable_wakeup_gpe_block(struct a /****************************************************************************** * - * FUNCTION: acpi_hw_low_disable_gpe + * FUNCTION: acpi_hw_gpe_register_bit + * + * PARAMETERS: gpe_event_info - Info block for the GPE + * gpe_register_info - Info block for the GPE register + * + * RETURN: Status + * + * DESCRIPTION: Compute the enable mask with one bit corresponding to the given + * GPE set. + * + ******************************************************************************/ + +u32 acpi_hw_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info, + struct acpi_gpe_register_info *gpe_register_info) +{ + return (u32)1 << (gpe_event_info->gpe_number - + gpe_register_info->base_gpe_number); +} + +/****************************************************************************** + * + * 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; /* Get the info block for the entire GPE register */ @@ -80,6 +104,9 @@ acpi_status acpi_hw_low_disable_gpe(stru return (AE_NOT_EXIST); } + register_bit = acpi_hw_gpe_register_bit(gpe_event_info, + gpe_register_info); + /* Get current value of the enable register that contains this GPE */ status = acpi_hw_read(&enable_mask, &gpe_register_info->enable_address); @@ -87,11 +114,22 @@ acpi_status acpi_hw_low_disable_gpe(stru return (status); } - /* Clear just the bit that corresponds to this GPE */ + /* Set ot clear just the bit that corresponds to this GPE */ + + 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); + } - ACPI_CLEAR_BIT(enable_mask, ((u32)1 << - (gpe_event_info->gpe_number - - gpe_register_info->base_gpe_number))); /* Write the updated enable mask */ @@ -118,6 +156,8 @@ acpi_hw_write_gpe_enable_reg(struct acpi { struct acpi_gpe_register_info *gpe_register_info; acpi_status status; + u32 enable_mask; + u32 register_bit; ACPI_FUNCTION_ENTRY(); @@ -128,10 +168,21 @@ acpi_hw_write_gpe_enable_reg(struct acpi return (AE_NOT_EXIST); } - /* Write the entire GPE (runtime) enable register */ + register_bit = acpi_hw_gpe_register_bit(gpe_event_info, + gpe_register_info); - status = acpi_hw_write(gpe_register_info->enable_for_run, - &gpe_register_info->enable_address); + /* Get current value of the enable register that contains this GPE */ + + status = acpi_hw_read(&enable_mask, &gpe_register_info->enable_address); + if (ACPI_FAILURE(status)) { + return (status); + } + + if (register_bit & gpe_register_info->enable_for_run) { + ACPI_SET_BIT(enable_mask, register_bit); + status = acpi_hw_write(enable_mask, + &gpe_register_info->enable_address); + } return (status); } @@ -150,21 +201,28 @@ acpi_hw_write_gpe_enable_reg(struct acpi acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info * gpe_event_info) { + struct acpi_gpe_register_info *gpe_register_info; acpi_status status; - u8 register_bit; + u32 register_bit; ACPI_FUNCTION_ENTRY(); - register_bit = (u8)(1 << - (gpe_event_info->gpe_number - - gpe_event_info->register_info->base_gpe_number)); + /* Get the info block for the entire GPE register */ + + gpe_register_info = gpe_event_info->register_info; + if (!gpe_register_info) { + return (AE_NOT_EXIST); + } + + register_bit = acpi_hw_gpe_register_bit(gpe_event_info, + gpe_register_info); /* * Write a one to the appropriate bit in the status register to * clear this GPE. */ status = acpi_hw_write(register_bit, - &gpe_event_info->register_info->status_address); + &gpe_register_info->status_address); return (status); } @@ -187,7 +245,7 @@ acpi_hw_get_gpe_status(struct acpi_gpe_e acpi_event_status * event_status) { u32 in_byte; - u8 register_bit; + u32 register_bit; struct acpi_gpe_register_info *gpe_register_info; acpi_status status; acpi_event_status local_event_status = 0; @@ -201,12 +259,12 @@ acpi_hw_get_gpe_status(struct acpi_gpe_e /* Get the info block for the entire GPE register */ gpe_register_info = gpe_event_info->register_info; + if (!gpe_register_info) { + return (AE_NOT_EXIST); + } - /* Get the register bitmask for this GPE */ - - register_bit = (u8)(1 << - (gpe_event_info->gpe_number - - gpe_event_info->register_info->base_gpe_number)); + register_bit = acpi_hw_gpe_register_bit(gpe_event_info, + gpe_register_info); /* GPE currently enabled? (enabled for runtime?) */ Index: linux-2.6/drivers/acpi/acpica/achware.h =================================================================== --- linux-2.6.orig/drivers/acpi/acpica/achware.h +++ linux-2.6/drivers/acpi/acpica/achware.h @@ -90,7 +90,11 @@ acpi_status acpi_hw_write_port(acpi_io_a /* * hwgpe - GPE support */ -acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info); +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_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/drivers/acpi/acpica/evgpe.c =================================================================== --- linux-2.6.orig/drivers/acpi/acpica/evgpe.c +++ linux-2.6/drivers/acpi/acpica/evgpe.c @@ -68,7 +68,7 @@ acpi_status 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; + u32 register_bit; ACPI_FUNCTION_TRACE(ev_update_gpe_enable_masks); @@ -77,9 +77,8 @@ acpi_ev_update_gpe_enable_masks(struct a return_ACPI_STATUS(AE_NOT_EXIST); } - register_bit = (u8) - (1 << - (gpe_event_info->gpe_number - gpe_register_info->base_gpe_number)); + register_bit = acpi_hw_gpe_register_bit(gpe_event_info, + gpe_register_info); ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake, register_bit); ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit); @@ -95,77 +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_gpeblk_event_info * * PARAMETERS: gpe_block - GPE block to search @@ -401,10 +329,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. @@ -557,7 +481,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]", @@ -591,7 +515,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/drivers/acpi/acpica/evxfevnt.c =================================================================== --- linux-2.6.orig/drivers/acpi/acpica/evxfevnt.c +++ linux-2.6/drivers/acpi/acpica/evxfevnt.c @@ -235,11 +235,17 @@ acpi_status acpi_set_gpe(acpi_handle gpe switch (action) { case ACPI_GPE_ENABLE: - status = acpi_ev_enable_gpe(gpe_event_info); + /* Clear the GPE (of stale events), then enable it */ + status = acpi_hw_clear_gpe(gpe_event_info); + if (ACPI_FAILURE(status)) + break; + + /* Enable the requested GPE */ + status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE); 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,7 +297,10 @@ 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); + 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_ENABLE); if (ACPI_FAILURE(status)) gpe_event_info->runtime_count--; } @@ -308,7 +317,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 +360,12 @@ 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 ((type & ACPI_GPE_TYPE_WAKE) && gpe_event_info->wakeup_count) { @@ -361,7 +374,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/drivers/acpi/acpica/evgpeblk.c =================================================================== --- linux-2.6.orig/drivers/acpi/acpica/evgpeblk.c +++ linux-2.6/drivers/acpi/acpica/evgpeblk.c @@ -1016,6 +1016,18 @@ acpi_ev_initialize_gpe_block(struct acpi /* 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_number = gpe_index + gpe_block->block_base_number; + + /* + * Ensure that the register bit corresponding to this + * GPE are clear in its register's enable masks. + */ + status = acpi_ev_update_gpe_enable_masks(gpe_event_info); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Failed to update enable " + "masks for GPE %02X\n", gpe_number)); + continue; + } if (gpe_event_info->flags & ACPI_GPE_CAN_WAKE) { wake_gpe_count++; @@ -1026,7 +1038,6 @@ acpi_ev_initialize_gpe_block(struct acpi 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))