[PATCH 2/2] ACPI / ACPICA: Allow GPEs to be force disabled via sysfs

From: Rafael J. Wysocki
Date: Fri May 28 2010 - 16:46:54 EST


After the GPE reference counting patches (commits
9630bdd9b15d2f489c646d8bc04b60e53eb5ec78 and
cbbc0de700e61d0cdc854d435dbc2ef148de0e00), the sysfs interface
allowing user space to disable/enable GPEs doesn't work correctly,
because a GPE disabled this way may be re-enabled shortly by
acpi_ev_asynch_enable_gpe() if its bit is set in its register's
enable_for_run mask.

To fix this, introduce two new action values for acpi_set_gpe(),
ACPI_GPE_FORCE_ENABLE and ACPI_GPE_FORCE_DISABLE, such that if
acpi_set_gpe() is called with ACPI_GPE_FORCE_DISABLE, the GPE is
force disabled and can only be re-enabled by calling acpi_set_gpe()
with ACPI_GPE_FORCE_ENABLE. Use these new values in the sysfs
interface for disabling/enabling GPEs.

Signed-off-by: Rafael J. Wysocki <rjw@xxxxxxx>
---
drivers/acpi/acpica/evgpe.c | 5 ++++-
drivers/acpi/acpica/evxfevnt.c | 24 ++++++++++++++++++++++++
drivers/acpi/system.c | 6 ++++--
include/acpi/actypes.h | 19 ++++++++++++-------
4 files changed, 44 insertions(+), 10 deletions(-)

Index: linux-2.6/include/acpi/actypes.h
===================================================================
--- linux-2.6.orig/include/acpi/actypes.h
+++ linux-2.6/include/acpi/actypes.h
@@ -668,6 +668,8 @@ typedef u32 acpi_event_status;
#define ACPI_GPE_ENABLE 0
#define ACPI_GPE_DISABLE 1
#define ACPI_GPE_CHECK_AND_ENABLE 2
+#define ACPI_GPE_FORCE_ENABLE 3
+#define ACPI_GPE_FORCE_DISABLE 4

/* gpe_types for acpi_enable_gpe and acpi_disable_gpe */

@@ -677,13 +679,14 @@ typedef u32 acpi_event_status;

/*
* GPE info flags - Per GPE
- * +-------+---+-+-+
- * | 7:4 |3:2|1|0|
- * +-------+---+-+-+
- * | | | |
- * | | | +--- Interrupt type: edge or level triggered
- * | | +----- GPE can wake the system
- * | +-------- Type of dispatch:to method, handler, or none
+ * +-------+-+---+-+-+
+ * | 7:5 |4|3:2|1|0|
+ * +-------+-+---+-+-+
+ * | | | | |
+ * | | | | +--- Interrupt type: edge or level triggered
+ * | | | +----- GPE can wake the system
+ * | | +-------- Type of dispatch:to method, handler, or none
+ * | +----------- GPE has been forcibly disabled
* +-------------- <Reserved>
*/
#define ACPI_GPE_XRUPT_TYPE_MASK (u8) 0x01
@@ -697,6 +700,8 @@ typedef u32 acpi_event_status;
#define ACPI_GPE_DISPATCH_METHOD (u8) 0x08
#define ACPI_GPE_DISPATCH_NOT_USED (u8) 0x00

+#define ACPI_GPE_FORCE_DISABLED (u8) 0x10
+
/*
* Flags for GPE and Lock interfaces
*/
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
@@ -417,7 +417,10 @@ static void acpi_ev_asynch_enable_gpe(vo
}

/* Enable this GPE */
- (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CHECK_AND_ENABLE);
+ if (!(gpe_event_info->flags & ACPI_GPE_FORCE_DISABLED)) {
+ (void)acpi_hw_low_set_gpe(gpe_event_info,
+ ACPI_GPE_CHECK_AND_ENABLE);
+ }
return_VOID;
}

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
@@ -281,10 +281,24 @@ acpi_status acpi_set_gpe(acpi_handle gpe
status = acpi_clear_and_enable_gpe(gpe_event_info);
break;

+ case ACPI_GPE_FORCE_ENABLE:
+ status = acpi_clear_and_enable_gpe(gpe_event_info);
+ if (ACPI_SUCCESS(status)) {
+ gpe_event_info->flags &= ~(ACPI_GPE_FORCE_DISABLED);
+ }
+ break;
+
case ACPI_GPE_DISABLE:
status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
break;

+ case ACPI_GPE_FORCE_DISABLE:
+ status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
+ if (ACPI_SUCCESS(status)) {
+ gpe_event_info->flags |= ACPI_GPE_FORCE_DISABLED;
+ }
+ break;
+
default:
status = AE_BAD_PARAMETER;
break;
@@ -337,6 +351,11 @@ acpi_status acpi_enable_gpe(acpi_handle
goto unlock_and_exit;
}

+ if (gpe_event_info->flags & ACPI_GPE_FORCE_DISABLED) {
+ status = AE_ERROR;
+ goto unlock_and_exit;
+ }
+
if (gpe_type & ACPI_GPE_TYPE_RUNTIME) {
if (gpe_event_info->runtime_count == ACPI_UINT8_MAX) {
status = AE_LIMIT; /* Too many references */
@@ -426,6 +445,11 @@ acpi_status acpi_disable_gpe(acpi_handle
goto unlock_and_exit;
}

+ if (gpe_event_info->flags & ACPI_GPE_FORCE_DISABLED) {
+ status = AE_ERROR;
+ goto unlock_and_exit;
+ }
+
/* Hardware-disable a runtime GPE on removal of the last reference */

if (gpe_type & ACPI_GPE_TYPE_RUNTIME) {
Index: linux-2.6/drivers/acpi/system.c
===================================================================
--- linux-2.6.orig/drivers/acpi/system.c
+++ linux-2.6/drivers/acpi/system.c
@@ -388,10 +388,12 @@ static ssize_t counter_set(struct kobjec
if (index < num_gpes) {
if (!strcmp(buf, "disable\n") &&
(status & ACPI_EVENT_FLAG_ENABLED))
- result = acpi_set_gpe(handle, index, ACPI_GPE_DISABLE);
+ result = acpi_set_gpe(handle, index,
+ ACPI_GPE_FORCE_DISABLE);
else if (!strcmp(buf, "enable\n") &&
!(status & ACPI_EVENT_FLAG_ENABLED))
- result = acpi_set_gpe(handle, index, ACPI_GPE_ENABLE);
+ result = acpi_set_gpe(handle, index,
+ ACPI_GPE_FORCE_ENABLE);
else if (!strcmp(buf, "clear\n") &&
(status & ACPI_EVENT_FLAG_SET))
result = acpi_clear_gpe(handle, index);

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/