Commit 716bc413 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'acpi-ec'

* acpi-ec:
  ACPI / EC: Add GPE reference counting debugging messages
  ACPI / EC: Add query flushing support
  ACPI / EC: Refine command storm prevention support
  ACPI / EC: Add command flushing support.
  ACPI / EC: Introduce STARTED/STOPPED flags to replace BLOCKED flag
  ACPI / EC: Update revision due to raw handler mode.
  ACPI / EC: Reduce ec_poll() by referencing the last register access timestamp.
  ACPI / EC: Fix several GPE handling issues by deploying ACPI_GPE_DISPATCH_RAW_HANDLER mode.
  ACPI / EC: Cleanup QR_EC related code
  ACPI / EC: Fix issues related to the SCI_EVT handling
  ACPI / EC: Fix a code path that global lock is not held
  ACPI / EC: Fix returning values in acpi_ec_sync_query()
  ACPI / EC: Add reference counting for query handlers
  ACPI / EC: Cleanup transaction wakeup code
parents 55c39fc2 b5bca896
/* /*
* ec.c - ACPI Embedded Controller Driver (v2.2) * ec.c - ACPI Embedded Controller Driver (v3)
* *
* Copyright (C) 2001-2014 Intel Corporation * Copyright (C) 2001-2015 Intel Corporation
* Author: 2014 Lv Zheng <lv.zheng@intel.com> * Author: 2014, 2015 Lv Zheng <lv.zheng@intel.com>
* 2006, 2007 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com> * 2006, 2007 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
* 2006 Denis Sadykov <denis.m.sadykov@intel.com> * 2006 Denis Sadykov <denis.m.sadykov@intel.com>
* 2004 Luming Yu <luming.yu@intel.com> * 2004 Luming Yu <luming.yu@intel.com>
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
/* Uncomment next line to get verbose printout */ /* Uncomment next line to get verbose printout */
/* #define DEBUG */ /* #define DEBUG */
#define DEBUG_REF 0
#define pr_fmt(fmt) "ACPI : EC: " fmt #define pr_fmt(fmt) "ACPI : EC: " fmt
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -71,20 +72,32 @@ enum ec_command { ...@@ -71,20 +72,32 @@ enum ec_command {
#define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ #define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */
#define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */
#define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */ #define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */
#define ACPI_EC_UDELAY_POLL 1000 /* Wait 1ms for EC transaction polling */
#define ACPI_EC_CLEAR_MAX 100 /* Maximum number of events to query #define ACPI_EC_CLEAR_MAX 100 /* Maximum number of events to query
* when trying to clear the EC */ * when trying to clear the EC */
enum { enum {
EC_FLAGS_QUERY_PENDING, /* Query is pending */ EC_FLAGS_EVENT_ENABLED, /* Event is enabled */
EC_FLAGS_GPE_STORM, /* GPE storm detected */ EC_FLAGS_EVENT_PENDING, /* Event is pending */
EC_FLAGS_EVENT_DETECTED, /* Event is detected */
EC_FLAGS_HANDLERS_INSTALLED, /* Handlers for GPE and EC_FLAGS_HANDLERS_INSTALLED, /* Handlers for GPE and
* OpReg are installed */ * OpReg are installed */
EC_FLAGS_BLOCKED, /* Transactions are blocked */ EC_FLAGS_STARTED, /* Driver is started */
EC_FLAGS_STOPPED, /* Driver is stopped */
EC_FLAGS_COMMAND_STORM, /* GPE storms occurred to the
* current command processing */
}; };
#define ACPI_EC_COMMAND_POLL 0x01 /* Available for command byte */ #define ACPI_EC_COMMAND_POLL 0x01 /* Available for command byte */
#define ACPI_EC_COMMAND_COMPLETE 0x02 /* Completed last byte */ #define ACPI_EC_COMMAND_COMPLETE 0x02 /* Completed last byte */
#define ec_debug_ref(ec, fmt, ...) \
do { \
if (DEBUG_REF) \
pr_debug("%lu: " fmt, ec->reference_count, \
## __VA_ARGS__); \
} while (0)
/* ec.c is compiled in acpi namespace so this shows up as acpi.ec_delay param */ /* ec.c is compiled in acpi namespace so this shows up as acpi.ec_delay param */
static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY; static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY;
module_param(ec_delay, uint, 0644); module_param(ec_delay, uint, 0644);
...@@ -105,6 +118,7 @@ struct acpi_ec_query_handler { ...@@ -105,6 +118,7 @@ struct acpi_ec_query_handler {
acpi_handle handle; acpi_handle handle;
void *data; void *data;
u8 query_bit; u8 query_bit;
struct kref kref;
}; };
struct transaction { struct transaction {
...@@ -117,8 +131,12 @@ struct transaction { ...@@ -117,8 +131,12 @@ struct transaction {
u8 wlen; u8 wlen;
u8 rlen; u8 rlen;
u8 flags; u8 flags;
unsigned long timestamp;
}; };
static int acpi_ec_query(struct acpi_ec *ec, u8 *data);
static void advance_transaction(struct acpi_ec *ec);
struct acpi_ec *boot_ec, *first_ec; struct acpi_ec *boot_ec, *first_ec;
EXPORT_SYMBOL(first_ec); EXPORT_SYMBOL(first_ec);
...@@ -129,7 +147,28 @@ static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ ...@@ -129,7 +147,28 @@ static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */
static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */ static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* Transaction Management * Device Flags
* -------------------------------------------------------------------------- */
static bool acpi_ec_started(struct acpi_ec *ec)
{
return test_bit(EC_FLAGS_STARTED, &ec->flags) &&
!test_bit(EC_FLAGS_STOPPED, &ec->flags);
}
static bool acpi_ec_flushed(struct acpi_ec *ec)
{
return ec->reference_count == 1;
}
static bool acpi_ec_has_pending_event(struct acpi_ec *ec)
{
return test_bit(EC_FLAGS_EVENT_DETECTED, &ec->flags) ||
test_bit(EC_FLAGS_EVENT_PENDING, &ec->flags);
}
/* --------------------------------------------------------------------------
* EC Registers
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
static inline u8 acpi_ec_read_status(struct acpi_ec *ec) static inline u8 acpi_ec_read_status(struct acpi_ec *ec)
...@@ -151,6 +190,7 @@ static inline u8 acpi_ec_read_data(struct acpi_ec *ec) ...@@ -151,6 +190,7 @@ static inline u8 acpi_ec_read_data(struct acpi_ec *ec)
{ {
u8 x = inb(ec->data_addr); u8 x = inb(ec->data_addr);
ec->curr->timestamp = jiffies;
pr_debug("EC_DATA(R) = 0x%2.2x\n", x); pr_debug("EC_DATA(R) = 0x%2.2x\n", x);
return x; return x;
} }
...@@ -159,12 +199,14 @@ static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command) ...@@ -159,12 +199,14 @@ static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command)
{ {
pr_debug("EC_SC(W) = 0x%2.2x\n", command); pr_debug("EC_SC(W) = 0x%2.2x\n", command);
outb(command, ec->command_addr); outb(command, ec->command_addr);
ec->curr->timestamp = jiffies;
} }
static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data)
{ {
pr_debug("EC_DATA(W) = 0x%2.2x\n", data); pr_debug("EC_DATA(W) = 0x%2.2x\n", data);
outb(data, ec->data_addr); outb(data, ec->data_addr);
ec->curr->timestamp = jiffies;
} }
#ifdef DEBUG #ifdef DEBUG
...@@ -188,6 +230,203 @@ static const char *acpi_ec_cmd_string(u8 cmd) ...@@ -188,6 +230,203 @@ static const char *acpi_ec_cmd_string(u8 cmd)
#define acpi_ec_cmd_string(cmd) "UNDEF" #define acpi_ec_cmd_string(cmd) "UNDEF"
#endif #endif
/* --------------------------------------------------------------------------
* GPE Registers
* -------------------------------------------------------------------------- */
static inline bool acpi_ec_is_gpe_raised(struct acpi_ec *ec)
{
acpi_event_status gpe_status = 0;
(void)acpi_get_gpe_status(NULL, ec->gpe, &gpe_status);
return (gpe_status & ACPI_EVENT_FLAG_SET) ? true : false;
}
static inline void acpi_ec_enable_gpe(struct acpi_ec *ec, bool open)
{
if (open)
acpi_enable_gpe(NULL, ec->gpe);
else {
BUG_ON(ec->reference_count < 1);
acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE);
}
if (acpi_ec_is_gpe_raised(ec)) {
/*
* On some platforms, EN=1 writes cannot trigger GPE. So
* software need to manually trigger a pseudo GPE event on
* EN=1 writes.
*/
pr_debug("***** Polling quirk *****\n");
advance_transaction(ec);
}
}
static inline void acpi_ec_disable_gpe(struct acpi_ec *ec, bool close)
{
if (close)
acpi_disable_gpe(NULL, ec->gpe);
else {
BUG_ON(ec->reference_count < 1);
acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE);
}
}
static inline void acpi_ec_clear_gpe(struct acpi_ec *ec)
{
/*
* GPE STS is a W1C register, which means:
* 1. Software can clear it without worrying about clearing other
* GPEs' STS bits when the hardware sets them in parallel.
* 2. As long as software can ensure only clearing it when it is
* set, hardware won't set it in parallel.
* So software can clear GPE in any contexts.
* Warning: do not move the check into advance_transaction() as the
* EC commands will be sent without GPE raised.
*/
if (!acpi_ec_is_gpe_raised(ec))
return;
acpi_clear_gpe(NULL, ec->gpe);
}
/* --------------------------------------------------------------------------
* Transaction Management
* -------------------------------------------------------------------------- */
static void acpi_ec_submit_request(struct acpi_ec *ec)
{
ec->reference_count++;
if (ec->reference_count == 1)
acpi_ec_enable_gpe(ec, true);
}
static void acpi_ec_complete_request(struct acpi_ec *ec)
{
bool flushed = false;
ec->reference_count--;
if (ec->reference_count == 0)
acpi_ec_disable_gpe(ec, true);
flushed = acpi_ec_flushed(ec);
if (flushed)
wake_up(&ec->wait);
}
static void acpi_ec_set_storm(struct acpi_ec *ec, u8 flag)
{
if (!test_bit(flag, &ec->flags)) {
acpi_ec_disable_gpe(ec, false);
pr_debug("+++++ Polling enabled +++++\n");
set_bit(flag, &ec->flags);
}
}
static void acpi_ec_clear_storm(struct acpi_ec *ec, u8 flag)
{
if (test_bit(flag, &ec->flags)) {
clear_bit(flag, &ec->flags);
acpi_ec_enable_gpe(ec, false);
pr_debug("+++++ Polling disabled +++++\n");
}
}
/*
* acpi_ec_submit_flushable_request() - Increase the reference count unless
* the flush operation is not in
* progress
* @ec: the EC device
* @allow_event: whether event should be handled
*
* This function must be used before taking a new action that should hold
* the reference count. If this function returns false, then the action
* must be discarded or it will prevent the flush operation from being
* completed.
*
* During flushing, QR_EC command need to pass this check when there is a
* pending event, so that the reference count held for the pending event
* can be decreased by the completion of the QR_EC command.
*/
static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec,
bool allow_event)
{
if (!acpi_ec_started(ec)) {
if (!allow_event || !acpi_ec_has_pending_event(ec))
return false;
}
acpi_ec_submit_request(ec);
return true;
}
static void acpi_ec_submit_event(struct acpi_ec *ec)
{
if (!test_bit(EC_FLAGS_EVENT_DETECTED, &ec->flags) ||
!test_bit(EC_FLAGS_EVENT_ENABLED, &ec->flags))
return;
/* Hold reference for pending event */
if (!acpi_ec_submit_flushable_request(ec, true))
return;
ec_debug_ref(ec, "Increase event\n");
if (!test_and_set_bit(EC_FLAGS_EVENT_PENDING, &ec->flags)) {
pr_debug("***** Event query started *****\n");
schedule_work(&ec->work);
return;
}
acpi_ec_complete_request(ec);
ec_debug_ref(ec, "Decrease event\n");
}
static void acpi_ec_complete_event(struct acpi_ec *ec)
{
if (ec->curr->command == ACPI_EC_COMMAND_QUERY) {
clear_bit(EC_FLAGS_EVENT_PENDING, &ec->flags);
pr_debug("***** Event query stopped *****\n");
/* Unhold reference for pending event */
acpi_ec_complete_request(ec);
ec_debug_ref(ec, "Decrease event\n");
/* Check if there is another SCI_EVT detected */
acpi_ec_submit_event(ec);
}
}
static void acpi_ec_submit_detection(struct acpi_ec *ec)
{
/* Hold reference for query submission */
if (!acpi_ec_submit_flushable_request(ec, false))
return;
ec_debug_ref(ec, "Increase query\n");
if (!test_and_set_bit(EC_FLAGS_EVENT_DETECTED, &ec->flags)) {
pr_debug("***** Event detection blocked *****\n");
acpi_ec_submit_event(ec);
return;
}
acpi_ec_complete_request(ec);
ec_debug_ref(ec, "Decrease query\n");
}
static void acpi_ec_complete_detection(struct acpi_ec *ec)
{
if (ec->curr->command == ACPI_EC_COMMAND_QUERY) {
clear_bit(EC_FLAGS_EVENT_DETECTED, &ec->flags);
pr_debug("***** Event detetion unblocked *****\n");
/* Unhold reference for query submission */
acpi_ec_complete_request(ec);
ec_debug_ref(ec, "Decrease query\n");
}
}
static void acpi_ec_enable_event(struct acpi_ec *ec)
{
unsigned long flags;
spin_lock_irqsave(&ec->lock, flags);
set_bit(EC_FLAGS_EVENT_ENABLED, &ec->flags);
/*
* An event may be pending even with SCI_EVT=0, so QR_EC should
* always be issued right after started.
*/
acpi_ec_submit_detection(ec);
spin_unlock_irqrestore(&ec->lock, flags);
}
static int ec_transaction_completed(struct acpi_ec *ec) static int ec_transaction_completed(struct acpi_ec *ec)
{ {
unsigned long flags; unsigned long flags;
...@@ -200,7 +439,7 @@ static int ec_transaction_completed(struct acpi_ec *ec) ...@@ -200,7 +439,7 @@ static int ec_transaction_completed(struct acpi_ec *ec)
return ret; return ret;
} }
static bool advance_transaction(struct acpi_ec *ec) static void advance_transaction(struct acpi_ec *ec)
{ {
struct transaction *t; struct transaction *t;
u8 status; u8 status;
...@@ -208,6 +447,12 @@ static bool advance_transaction(struct acpi_ec *ec) ...@@ -208,6 +447,12 @@ static bool advance_transaction(struct acpi_ec *ec)
pr_debug("===== %s (%d) =====\n", pr_debug("===== %s (%d) =====\n",
in_interrupt() ? "IRQ" : "TASK", smp_processor_id()); in_interrupt() ? "IRQ" : "TASK", smp_processor_id());
/*
* By always clearing STS before handling all indications, we can
* ensure a hardware STS 0->1 change after this clearing can always
* trigger a GPE interrupt.
*/
acpi_ec_clear_gpe(ec);
status = acpi_ec_read_status(ec); status = acpi_ec_read_status(ec);
t = ec->curr; t = ec->curr;
if (!t) if (!t)
...@@ -223,6 +468,7 @@ static bool advance_transaction(struct acpi_ec *ec) ...@@ -223,6 +468,7 @@ static bool advance_transaction(struct acpi_ec *ec)
t->rdata[t->ri++] = acpi_ec_read_data(ec); t->rdata[t->ri++] = acpi_ec_read_data(ec);
if (t->rlen == t->ri) { if (t->rlen == t->ri) {
t->flags |= ACPI_EC_COMMAND_COMPLETE; t->flags |= ACPI_EC_COMMAND_COMPLETE;
acpi_ec_complete_event(ec);
if (t->command == ACPI_EC_COMMAND_QUERY) if (t->command == ACPI_EC_COMMAND_QUERY)
pr_debug("***** Command(%s) hardware completion *****\n", pr_debug("***** Command(%s) hardware completion *****\n",
acpi_ec_cmd_string(t->command)); acpi_ec_cmd_string(t->command));
...@@ -233,25 +479,29 @@ static bool advance_transaction(struct acpi_ec *ec) ...@@ -233,25 +479,29 @@ static bool advance_transaction(struct acpi_ec *ec)
} else if (t->wlen == t->wi && } else if (t->wlen == t->wi &&
(status & ACPI_EC_FLAG_IBF) == 0) { (status & ACPI_EC_FLAG_IBF) == 0) {
t->flags |= ACPI_EC_COMMAND_COMPLETE; t->flags |= ACPI_EC_COMMAND_COMPLETE;
acpi_ec_complete_event(ec);
wakeup = true; wakeup = true;
} }
return wakeup; goto out;
} else { } else {
if (EC_FLAGS_QUERY_HANDSHAKE && if (EC_FLAGS_QUERY_HANDSHAKE &&
!(status & ACPI_EC_FLAG_SCI) && !(status & ACPI_EC_FLAG_SCI) &&
(t->command == ACPI_EC_COMMAND_QUERY)) { (t->command == ACPI_EC_COMMAND_QUERY)) {
t->flags |= ACPI_EC_COMMAND_POLL; t->flags |= ACPI_EC_COMMAND_POLL;
acpi_ec_complete_detection(ec);
t->rdata[t->ri++] = 0x00; t->rdata[t->ri++] = 0x00;
t->flags |= ACPI_EC_COMMAND_COMPLETE; t->flags |= ACPI_EC_COMMAND_COMPLETE;
acpi_ec_complete_event(ec);
pr_debug("***** Command(%s) software completion *****\n", pr_debug("***** Command(%s) software completion *****\n",
acpi_ec_cmd_string(t->command)); acpi_ec_cmd_string(t->command));
wakeup = true; wakeup = true;
} else if ((status & ACPI_EC_FLAG_IBF) == 0) { } else if ((status & ACPI_EC_FLAG_IBF) == 0) {
acpi_ec_write_cmd(ec, t->command); acpi_ec_write_cmd(ec, t->command);
t->flags |= ACPI_EC_COMMAND_POLL; t->flags |= ACPI_EC_COMMAND_POLL;
acpi_ec_complete_detection(ec);
} else } else
goto err; goto err;
return wakeup; goto out;
} }
err: err:
/* /*
...@@ -259,28 +509,27 @@ static bool advance_transaction(struct acpi_ec *ec) ...@@ -259,28 +509,27 @@ static bool advance_transaction(struct acpi_ec *ec)
* otherwise will take a not handled IRQ as a false one. * otherwise will take a not handled IRQ as a false one.
*/ */
if (!(status & ACPI_EC_FLAG_SCI)) { if (!(status & ACPI_EC_FLAG_SCI)) {
if (in_interrupt() && t) if (in_interrupt() && t) {
++t->irq_count; if (t->irq_count < ec_storm_threshold)
++t->irq_count;
/* Allow triggering on 0 threshold */
if (t->irq_count == ec_storm_threshold)
acpi_ec_set_storm(ec, EC_FLAGS_COMMAND_STORM);
}
} }
return wakeup; out:
if (status & ACPI_EC_FLAG_SCI)
acpi_ec_submit_detection(ec);
if (wakeup && in_interrupt())
wake_up(&ec->wait);
} }
static void start_transaction(struct acpi_ec *ec) static void start_transaction(struct acpi_ec *ec)
{ {
ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0; ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0;
ec->curr->flags = 0; ec->curr->flags = 0;
(void)advance_transaction(ec); ec->curr->timestamp = jiffies;
} advance_transaction(ec);
static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data);
static int ec_check_sci_sync(struct acpi_ec *ec, u8 state)
{
if (state & ACPI_EC_FLAG_SCI) {
if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags))
return acpi_ec_sync_query(ec, NULL);
}
return 0;
} }
static int ec_poll(struct acpi_ec *ec) static int ec_poll(struct acpi_ec *ec)
...@@ -291,20 +540,25 @@ static int ec_poll(struct acpi_ec *ec) ...@@ -291,20 +540,25 @@ static int ec_poll(struct acpi_ec *ec)
while (repeat--) { while (repeat--) {
unsigned long delay = jiffies + unsigned long delay = jiffies +
msecs_to_jiffies(ec_delay); msecs_to_jiffies(ec_delay);
unsigned long usecs = ACPI_EC_UDELAY_POLL;
do { do {
/* don't sleep with disabled interrupts */ /* don't sleep with disabled interrupts */
if (EC_FLAGS_MSI || irqs_disabled()) { if (EC_FLAGS_MSI || irqs_disabled()) {
udelay(ACPI_EC_MSI_UDELAY); usecs = ACPI_EC_MSI_UDELAY;
udelay(usecs);
if (ec_transaction_completed(ec)) if (ec_transaction_completed(ec))
return 0; return 0;
} else { } else {
if (wait_event_timeout(ec->wait, if (wait_event_timeout(ec->wait,
ec_transaction_completed(ec), ec_transaction_completed(ec),
msecs_to_jiffies(1))) usecs_to_jiffies(usecs)))
return 0; return 0;
} }
spin_lock_irqsave(&ec->lock, flags); spin_lock_irqsave(&ec->lock, flags);
(void)advance_transaction(ec); if (time_after(jiffies,
ec->curr->timestamp +
usecs_to_jiffies(usecs)))
advance_transaction(ec);
spin_unlock_irqrestore(&ec->lock, flags); spin_unlock_irqrestore(&ec->lock, flags);
} while (time_before(jiffies, delay)); } while (time_before(jiffies, delay));
pr_debug("controller reset, restart transaction\n"); pr_debug("controller reset, restart transaction\n");
...@@ -325,21 +579,29 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, ...@@ -325,21 +579,29 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
udelay(ACPI_EC_MSI_UDELAY); udelay(ACPI_EC_MSI_UDELAY);
/* start transaction */ /* start transaction */
spin_lock_irqsave(&ec->lock, tmp); spin_lock_irqsave(&ec->lock, tmp);
/* Enable GPE for command processing (IBF=0/OBF=1) */
if (!acpi_ec_submit_flushable_request(ec, true)) {
ret = -EINVAL;
goto unlock;
}
ec_debug_ref(ec, "Increase command\n");
/* following two actions should be kept atomic */ /* following two actions should be kept atomic */
ec->curr = t; ec->curr = t;
pr_debug("***** Command(%s) started *****\n", pr_debug("***** Command(%s) started *****\n",
acpi_ec_cmd_string(t->command)); acpi_ec_cmd_string(t->command));
start_transaction(ec); start_transaction(ec);
if (ec->curr->command == ACPI_EC_COMMAND_QUERY) {
clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
pr_debug("***** Event stopped *****\n");
}
spin_unlock_irqrestore(&ec->lock, tmp); spin_unlock_irqrestore(&ec->lock, tmp);
ret = ec_poll(ec); ret = ec_poll(ec);
spin_lock_irqsave(&ec->lock, tmp); spin_lock_irqsave(&ec->lock, tmp);
if (t->irq_count == ec_storm_threshold)
acpi_ec_clear_storm(ec, EC_FLAGS_COMMAND_STORM);
pr_debug("***** Command(%s) stopped *****\n", pr_debug("***** Command(%s) stopped *****\n",
acpi_ec_cmd_string(t->command)); acpi_ec_cmd_string(t->command));
ec->curr = NULL; ec->curr = NULL;
/* Disable GPE for command processing (IBF=0/OBF=1) */
acpi_ec_complete_request(ec);
ec_debug_ref(ec, "Decrease command\n");
unlock:
spin_unlock_irqrestore(&ec->lock, tmp); spin_unlock_irqrestore(&ec->lock, tmp);
return ret; return ret;
} }
...@@ -354,10 +616,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) ...@@ -354,10 +616,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
if (t->rdata) if (t->rdata)
memset(t->rdata, 0, t->rlen); memset(t->rdata, 0, t->rlen);
mutex_lock(&ec->mutex); mutex_lock(&ec->mutex);
if (test_bit(EC_FLAGS_BLOCKED, &ec->flags)) {
status = -EINVAL;
goto unlock;
}
if (ec->global_lock) { if (ec->global_lock) {
status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
...@@ -365,26 +623,11 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) ...@@ -365,26 +623,11 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
goto unlock; goto unlock;
} }
} }
/* disable GPE during transaction if storm is detected */
if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) {
/* It has to be disabled, so that it doesn't trigger. */
acpi_disable_gpe(NULL, ec->gpe);
}
status = acpi_ec_transaction_unlocked(ec, t); status = acpi_ec_transaction_unlocked(ec, t);
/* check if we received SCI during transaction */ if (test_bit(EC_FLAGS_COMMAND_STORM, &ec->flags))
ec_check_sci_sync(ec, acpi_ec_read_status(ec));
if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) {
msleep(1); msleep(1);
/* It is safe to enable the GPE outside of the transaction. */
acpi_enable_gpe(NULL, ec->gpe);
} else if (t->irq_count > ec_storm_threshold) {
pr_info("GPE storm detected(%d GPEs), "
"transactions will use polling mode\n",
t->irq_count);
set_bit(EC_FLAGS_GPE_STORM, &ec->flags);
}
if (ec->global_lock) if (ec->global_lock)
acpi_release_global_lock(glk); acpi_release_global_lock(glk);
unlock: unlock:
...@@ -500,7 +743,7 @@ static void acpi_ec_clear(struct acpi_ec *ec) ...@@ -500,7 +743,7 @@ static void acpi_ec_clear(struct acpi_ec *ec)
u8 value = 0; u8 value = 0;
for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) { for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) {
status = acpi_ec_sync_query(ec, &value); status = acpi_ec_query(ec, &value);
if (status || !value) if (status || !value)
break; break;
} }
...@@ -511,6 +754,57 @@ static void acpi_ec_clear(struct acpi_ec *ec) ...@@ -511,6 +754,57 @@ static void acpi_ec_clear(struct acpi_ec *ec)
pr_info("%d stale EC events cleared\n", i); pr_info("%d stale EC events cleared\n", i);
} }
static void acpi_ec_start(struct acpi_ec *ec, bool resuming)
{
unsigned long flags;
spin_lock_irqsave(&ec->lock, flags);
if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) {
pr_debug("+++++ Starting EC +++++\n");
/* Enable GPE for event processing (SCI_EVT=1) */
if (!resuming) {
acpi_ec_submit_request(ec);
ec_debug_ref(ec, "Increase driver\n");
}
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, bool suspending)
{
unsigned long flags;
spin_lock_irqsave(&ec->lock, flags);
if (acpi_ec_started(ec)) {
pr_debug("+++++ Stopping EC +++++\n");
set_bit(EC_FLAGS_STOPPED, &ec->flags);
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 (SCI_EVT=1) */
if (!suspending) {
acpi_ec_complete_request(ec);
ec_debug_ref(ec, "Decrease driver\n");
}
clear_bit(EC_FLAGS_STARTED, &ec->flags);
clear_bit(EC_FLAGS_STOPPED, &ec->flags);
pr_info("+++++ EC stopped +++++\n");
}
spin_unlock_irqrestore(&ec->lock, flags);
}
void acpi_ec_block_transactions(void) void acpi_ec_block_transactions(void)
{ {
struct acpi_ec *ec = first_ec; struct acpi_ec *ec = first_ec;
...@@ -520,7 +814,7 @@ void acpi_ec_block_transactions(void) ...@@ -520,7 +814,7 @@ void acpi_ec_block_transactions(void)
mutex_lock(&ec->mutex); mutex_lock(&ec->mutex);
/* Prevent transactions from being carried out */ /* Prevent transactions from being carried out */
set_bit(EC_FLAGS_BLOCKED, &ec->flags); acpi_ec_stop(ec, true);
mutex_unlock(&ec->mutex); mutex_unlock(&ec->mutex);
} }
...@@ -531,14 +825,11 @@ void acpi_ec_unblock_transactions(void) ...@@ -531,14 +825,11 @@ void acpi_ec_unblock_transactions(void)
if (!ec) if (!ec)
return; return;
mutex_lock(&ec->mutex);
/* Allow transactions to be carried out again */ /* Allow transactions to be carried out again */
clear_bit(EC_FLAGS_BLOCKED, &ec->flags); acpi_ec_start(ec, true);
if (EC_FLAGS_CLEAR_ON_RESUME) if (EC_FLAGS_CLEAR_ON_RESUME)
acpi_ec_clear(ec); acpi_ec_clear(ec);
mutex_unlock(&ec->mutex);
} }
void acpi_ec_unblock_transactions_early(void) void acpi_ec_unblock_transactions_early(void)
...@@ -548,36 +839,33 @@ void acpi_ec_unblock_transactions_early(void) ...@@ -548,36 +839,33 @@ void acpi_ec_unblock_transactions_early(void)
* atomic context during wakeup, so we don't need to acquire the mutex). * atomic context during wakeup, so we don't need to acquire the mutex).
*/ */
if (first_ec) if (first_ec)
clear_bit(EC_FLAGS_BLOCKED, &first_ec->flags); acpi_ec_start(first_ec, true);
} }
static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 *data) /* --------------------------------------------------------------------------
Event Management
-------------------------------------------------------------------------- */
static struct acpi_ec_query_handler *
acpi_ec_get_query_handler(struct acpi_ec_query_handler *handler)
{ {
int result; if (handler)
u8 d; kref_get(&handler->kref);
struct transaction t = {.command = ACPI_EC_COMMAND_QUERY, return handler;
.wdata = NULL, .rdata = &d, }
.wlen = 0, .rlen = 1};
if (!ec || !data) static void acpi_ec_query_handler_release(struct kref *kref)
return -EINVAL; {
/* struct acpi_ec_query_handler *handler =
* Query the EC to find out which _Qxx method we need to evaluate. container_of(kref, struct acpi_ec_query_handler, kref);
* Note that successful completion of the query causes the ACPI_EC_SCI
* bit to be cleared (and thus clearing the interrupt source). kfree(handler);
*/ }
result = acpi_ec_transaction_unlocked(ec, &t);
if (result) static void acpi_ec_put_query_handler(struct acpi_ec_query_handler *handler)
return result; {
if (!d) kref_put(&handler->kref, acpi_ec_query_handler_release);
return -ENODATA;
*data = d;
return 0;
} }
/* --------------------------------------------------------------------------
Event Management
-------------------------------------------------------------------------- */
int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
acpi_handle handle, acpi_ec_query_func func, acpi_handle handle, acpi_ec_query_func func,
void *data) void *data)
...@@ -593,6 +881,7 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, ...@@ -593,6 +881,7 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
handler->func = func; handler->func = func;
handler->data = data; handler->data = data;
mutex_lock(&ec->mutex); mutex_lock(&ec->mutex);
kref_init(&handler->kref);
list_add(&handler->node, &ec->list); list_add(&handler->node, &ec->list);
mutex_unlock(&ec->mutex); mutex_unlock(&ec->mutex);
return 0; return 0;
...@@ -602,15 +891,18 @@ EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler); ...@@ -602,15 +891,18 @@ EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler);
void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit)
{ {
struct acpi_ec_query_handler *handler, *tmp; struct acpi_ec_query_handler *handler, *tmp;
LIST_HEAD(free_list);
mutex_lock(&ec->mutex); mutex_lock(&ec->mutex);
list_for_each_entry_safe(handler, tmp, &ec->list, node) { list_for_each_entry_safe(handler, tmp, &ec->list, node) {
if (query_bit == handler->query_bit) { if (query_bit == handler->query_bit) {
list_del(&handler->node); list_del_init(&handler->node);
kfree(handler); list_add(&handler->node, &free_list);
} }
} }
mutex_unlock(&ec->mutex); mutex_unlock(&ec->mutex);
list_for_each_entry(handler, &free_list, node)
acpi_ec_put_query_handler(handler);
} }
EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler);
...@@ -626,59 +918,58 @@ static void acpi_ec_run(void *cxt) ...@@ -626,59 +918,58 @@ static void acpi_ec_run(void *cxt)
else if (handler->handle) else if (handler->handle)
acpi_evaluate_object(handler->handle, NULL, NULL, NULL); acpi_evaluate_object(handler->handle, NULL, NULL, NULL);
pr_debug("##### Query(0x%02x) stopped #####\n", handler->query_bit); pr_debug("##### Query(0x%02x) stopped #####\n", handler->query_bit);
kfree(handler); acpi_ec_put_query_handler(handler);
} }
static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data) static int acpi_ec_query(struct acpi_ec *ec, u8 *data)
{ {
u8 value = 0; u8 value = 0;
int status; int result;
struct acpi_ec_query_handler *handler, *copy; acpi_status status;
struct acpi_ec_query_handler *handler;
struct transaction t = {.command = ACPI_EC_COMMAND_QUERY,
.wdata = NULL, .rdata = &value,
.wlen = 0, .rlen = 1};
status = acpi_ec_query_unlocked(ec, &value); /*
* Query the EC to find out which _Qxx method we need to evaluate.
* Note that successful completion of the query causes the ACPI_EC_SCI
* bit to be cleared (and thus clearing the interrupt source).
*/
result = acpi_ec_transaction(ec, &t);
if (result)
return result;
if (data) if (data)
*data = value; *data = value;
if (status) if (!value)
return status; return -ENODATA;
mutex_lock(&ec->mutex);
list_for_each_entry(handler, &ec->list, node) { list_for_each_entry(handler, &ec->list, node) {
if (value == handler->query_bit) { if (value == handler->query_bit) {
/* have custom handler for this bit */ /* have custom handler for this bit */
copy = kmalloc(sizeof(*handler), GFP_KERNEL); handler = acpi_ec_get_query_handler(handler);
if (!copy)
return -ENOMEM;
memcpy(copy, handler, sizeof(*copy));
pr_debug("##### Query(0x%02x) scheduled #####\n", pr_debug("##### Query(0x%02x) scheduled #####\n",
handler->query_bit); handler->query_bit);
return acpi_os_execute((copy->func) ? status = acpi_os_execute((handler->func) ?
OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER, OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER,
acpi_ec_run, copy); acpi_ec_run, handler);
if (ACPI_FAILURE(status))
result = -EBUSY;
break;
} }
} }
return 0;
}
static void acpi_ec_gpe_query(void *ec_cxt)
{
struct acpi_ec *ec = ec_cxt;
if (!ec)
return;
mutex_lock(&ec->mutex);
acpi_ec_sync_query(ec, NULL);
mutex_unlock(&ec->mutex); mutex_unlock(&ec->mutex);
return result;
} }
static int ec_check_sci(struct acpi_ec *ec, u8 state) static void acpi_ec_gpe_poller(struct work_struct *work)
{ {
if (state & ACPI_EC_FLAG_SCI) { struct acpi_ec *ec = container_of(work, struct acpi_ec, work);
if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
pr_debug("***** Event started *****\n"); pr_debug("***** Event poller started *****\n");
return acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_ec_query(ec, NULL);
acpi_ec_gpe_query, ec); pr_debug("***** Event poller stopped *****\n");
}
}
return 0;
} }
static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, static u32 acpi_ec_gpe_handler(acpi_handle gpe_device,
...@@ -688,11 +979,9 @@ static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, ...@@ -688,11 +979,9 @@ static u32 acpi_ec_gpe_handler(acpi_handle gpe_device,
struct acpi_ec *ec = data; struct acpi_ec *ec = data;
spin_lock_irqsave(&ec->lock, flags); spin_lock_irqsave(&ec->lock, flags);
if (advance_transaction(ec)) advance_transaction(ec);
wake_up(&ec->wait);
spin_unlock_irqrestore(&ec->lock, flags); spin_unlock_irqrestore(&ec->lock, flags);
ec_check_sci(ec, acpi_ec_read_status(ec)); return ACPI_INTERRUPT_HANDLED;
return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
} }
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
...@@ -750,11 +1039,11 @@ static struct acpi_ec *make_acpi_ec(void) ...@@ -750,11 +1039,11 @@ static struct acpi_ec *make_acpi_ec(void)
if (!ec) if (!ec)
return NULL; return NULL;
ec->flags = 1 << EC_FLAGS_QUERY_PENDING;
mutex_init(&ec->mutex); mutex_init(&ec->mutex);
init_waitqueue_head(&ec->wait); init_waitqueue_head(&ec->wait);
INIT_LIST_HEAD(&ec->list); INIT_LIST_HEAD(&ec->list);
spin_lock_init(&ec->lock); spin_lock_init(&ec->lock);
INIT_WORK(&ec->work, acpi_ec_gpe_poller);
return ec; return ec;
} }
...@@ -810,13 +1099,13 @@ static int ec_install_handlers(struct acpi_ec *ec) ...@@ -810,13 +1099,13 @@ static int ec_install_handlers(struct acpi_ec *ec)
if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags))
return 0; return 0;
status = acpi_install_gpe_handler(NULL, ec->gpe, status = acpi_install_gpe_raw_handler(NULL, ec->gpe,
ACPI_GPE_EDGE_TRIGGERED, ACPI_GPE_EDGE_TRIGGERED,
&acpi_ec_gpe_handler, ec); &acpi_ec_gpe_handler, ec);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return -ENODEV; return -ENODEV;
acpi_enable_gpe(NULL, ec->gpe); acpi_ec_start(ec, false);
status = acpi_install_address_space_handler(ec->handle, status = acpi_install_address_space_handler(ec->handle,
ACPI_ADR_SPACE_EC, ACPI_ADR_SPACE_EC,
&acpi_ec_space_handler, &acpi_ec_space_handler,
...@@ -831,7 +1120,7 @@ static int ec_install_handlers(struct acpi_ec *ec) ...@@ -831,7 +1120,7 @@ static int ec_install_handlers(struct acpi_ec *ec)
pr_err("Fail in evaluating the _REG object" pr_err("Fail in evaluating the _REG object"
" of EC device. Broken bios is suspected.\n"); " of EC device. Broken bios is suspected.\n");
} else { } else {
acpi_disable_gpe(NULL, ec->gpe); acpi_ec_stop(ec, false);
acpi_remove_gpe_handler(NULL, ec->gpe, acpi_remove_gpe_handler(NULL, ec->gpe,
&acpi_ec_gpe_handler); &acpi_ec_gpe_handler);
return -ENODEV; return -ENODEV;
...@@ -846,7 +1135,7 @@ static void ec_remove_handlers(struct acpi_ec *ec) ...@@ -846,7 +1135,7 @@ static void ec_remove_handlers(struct acpi_ec *ec)
{ {
if (!test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) if (!test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags))
return; return;
acpi_disable_gpe(NULL, ec->gpe); acpi_ec_stop(ec, false);
if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle,
ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) ACPI_ADR_SPACE_EC, &acpi_ec_space_handler)))
pr_err("failed to remove space handler\n"); pr_err("failed to remove space handler\n");
...@@ -900,14 +1189,11 @@ static int acpi_ec_add(struct acpi_device *device) ...@@ -900,14 +1189,11 @@ static int acpi_ec_add(struct acpi_device *device)
ret = ec_install_handlers(ec); ret = ec_install_handlers(ec);
/* EC is fully operational, allow queries */ /* EC is fully operational, allow queries */
clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); acpi_ec_enable_event(ec);
/* Clear stale _Q events if hardware might require that */ /* Clear stale _Q events if hardware might require that */
if (EC_FLAGS_CLEAR_ON_RESUME) { if (EC_FLAGS_CLEAR_ON_RESUME)
mutex_lock(&ec->mutex);
acpi_ec_clear(ec); acpi_ec_clear(ec);
mutex_unlock(&ec->mutex);
}
return ret; return ret;
} }
......
...@@ -122,11 +122,13 @@ struct acpi_ec { ...@@ -122,11 +122,13 @@ struct acpi_ec {
unsigned long data_addr; unsigned long data_addr;
unsigned long global_lock; unsigned long global_lock;
unsigned long flags; unsigned long flags;
unsigned long reference_count;
struct mutex mutex; struct mutex mutex;
wait_queue_head_t wait; wait_queue_head_t wait;
struct list_head list; struct list_head list;
struct transaction *curr; struct transaction *curr;
spinlock_t lock; spinlock_t lock;
struct work_struct work;
}; };
extern struct acpi_ec *first_ec; extern struct acpi_ec *first_ec;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment