[PATCH v2 08/10] ACPI/EC: Add command flushing support.

From: Lv Zheng
Date: Mon Jul 14 2014 - 23:15:29 EST


This patch implements command flushing support. It's better to wait all
command transactions to be completed before disabling the EC GPE when the
system is going to be suspended. By doing so, the EC hardware can be
ensured to be in the idle state when the system is resumed.

The system suspending/resuming test result is as follows:
[ 24.950829] ACPI : EC: +++++ Stopping EC +++++
[ 24.950836] ACPI : EC: +++++ EC stopped +++++

Signed-off-by: Lv Zheng <lv.zheng@xxxxxxxxx>
---
drivers/acpi/ec.c | 49 +++++++++++++++++++++++++++++++++++++++++++----
drivers/acpi/internal.h | 1 +
2 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 2340ac2..936424a 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -133,6 +133,32 @@ static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */
static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */

/* --------------------------------------------------------------------------
+ GPE Enhancement
+ -------------------------------------------------------------------------- */
+
+static bool acpi_ec_flushed(struct acpi_ec *ec)
+{
+ return !!(ec->reference_count == 1);
+}
+
+static void acpi_ec_enable_gpe(struct acpi_ec *ec)
+{
+ acpi_enable_gpe(NULL, ec->gpe);
+ ec->reference_count++;
+}
+
+static void acpi_ec_disable_gpe(struct acpi_ec *ec)
+{
+ bool flushed = false;
+
+ ec->reference_count--;
+ acpi_disable_gpe(NULL, ec->gpe);
+ flushed = acpi_ec_flushed(ec);
+ if (flushed)
+ wake_up(&ec->wait);
+}
+
+/* --------------------------------------------------------------------------
Transaction Management
-------------------------------------------------------------------------- */

@@ -325,7 +351,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
/* following two actions should be kept atomic */
ec->curr = t;
/* Enable GPE for command processing (IBF=0/OBF=1) */
- acpi_enable_gpe(NULL, ec->gpe);
+ acpi_ec_enable_gpe(ec);
pr_debug("***** Command(%s) started *****\n",
acpi_ec_cmd_string(t->command));
start_transaction(ec);
@@ -343,7 +369,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
pr_debug("***** Command(%s) stopped *****\n",
acpi_ec_cmd_string(t->command));
/* Disable GPE for command processing (IBF=0/OBF=1) */
- acpi_disable_gpe(NULL, ec->gpe);
+ acpi_ec_disable_gpe(ec);
ec->curr = NULL;
unlock:
spin_unlock_irqrestore(&ec->lock, tmp);
@@ -508,12 +534,24 @@ static void acpi_ec_start(struct acpi_ec *ec)
if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) {
pr_debug("+++++ Starting EC +++++\n");
/* Enable GPE for event processing (EVT_SCI=1) */
- acpi_enable_gpe(NULL, ec->gpe);
+ acpi_ec_enable_gpe(ec);
pr_info("+++++ EC started +++++\n");
}
spin_unlock_irqrestore(&ec->lock, flags);
}

+static bool acpi_ec_stopped(struct acpi_ec *ec)
+{
+ unsigned long flags;
+ bool flushed;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ flushed = acpi_ec_flushed(ec);
+ spin_unlock_irqrestore(&ec->lock, flags);
+
+ return flushed;
+}
+
static void acpi_ec_stop(struct acpi_ec *ec)
{
unsigned long flags;
@@ -522,8 +560,11 @@ static void acpi_ec_stop(struct acpi_ec *ec)
if (test_bit(EC_FLAGS_STARTED, &ec->flags) &&
!test_and_set_bit(EC_FLAGS_STOPPED, &ec->flags)) {
pr_debug("+++++ Stopping EC +++++\n");
+ spin_unlock_irqrestore(&ec->lock, flags);
+ wait_event(ec->wait, acpi_ec_stopped(ec));
+ spin_lock_irqsave(&ec->lock, flags);
/* Disable GPE for event processing (EVT_SCI=1) */
- acpi_disable_gpe(NULL, ec->gpe);
+ acpi_ec_disable_gpe(ec);
clear_bit(EC_FLAGS_STARTED, &ec->flags);
clear_bit(EC_FLAGS_STOPPED, &ec->flags);
pr_info("+++++ EC stopped +++++\n");
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 151f3e7..2348f9c 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -118,6 +118,7 @@ struct acpi_ec {
unsigned long data_addr;
unsigned long global_lock;
unsigned long flags;
+ unsigned long reference_count;
struct mutex mutex;
wait_queue_head_t wait;
struct list_head list;
--
1.7.10

--
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/