Skip to content

Commit

Permalink
ACPI: EC: Accelerate query execution
Browse files Browse the repository at this point in the history
Split EC query handling into acknowledge and execution phase.
This allows much smaller pending query lattency and lowers chances
of EC going "wild" and losing events.

Reference: http://bugzilla.kernel.org/show_bug.cgi?id=14858

Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
  • Loading branch information
Alexey Starikovskiy authored and Len Brown committed Dec 24, 2009
1 parent fcb1123 commit a62e8f1
Showing 1 changed file with 77 additions and 45 deletions.
122 changes: 77 additions & 45 deletions drivers/acpi/ec.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,13 @@ static void advance_transaction(struct acpi_ec *ec, u8 status)
spin_unlock_irqrestore(&ec->curr_lock, flags);
}

static void acpi_ec_gpe_query(void *ec_cxt);
static int acpi_ec_sync_query(struct acpi_ec *ec);

static int ec_check_sci(struct acpi_ec *ec, u8 state)
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_os_execute(OSL_EC_BURST_HANDLER,
acpi_ec_gpe_query, ec);
return acpi_ec_sync_query(ec);
}
return 0;
}
Expand Down Expand Up @@ -249,11 +248,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
{
unsigned long tmp;
int ret = 0;
pr_debug(PREFIX "transaction start\n");
/* disable GPE during transaction if storm is detected */
if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) {
acpi_disable_gpe(NULL, ec->gpe);
}
if (EC_FLAGS_MSI)
udelay(ACPI_EC_MSI_UDELAY);
/* start transaction */
Expand All @@ -269,16 +263,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
spin_lock_irqsave(&ec->curr_lock, tmp);
ec->curr = NULL;
spin_unlock_irqrestore(&ec->curr_lock, tmp);
if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) {
/* check if we received SCI during transaction */
ec_check_sci(ec, acpi_ec_read_status(ec));
/* it is safe to enable GPE outside of transaction */
acpi_enable_gpe(NULL, ec->gpe);
} else if (t->irq_count > ACPI_EC_STORM_THRESHOLD) {
pr_info(PREFIX "GPE storm detected, "
"transactions will use polling mode\n");
set_bit(EC_FLAGS_GPE_STORM, &ec->flags);
}
return ret;
}

Expand Down Expand Up @@ -321,7 +305,24 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
status = -ETIME;
goto end;
}
pr_debug(PREFIX "transaction start\n");
/* disable GPE during transaction if storm is detected */
if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) {
acpi_disable_gpe(NULL, ec->gpe);
}

status = acpi_ec_transaction_unlocked(ec, t);

/* check if we received SCI during transaction */
ec_check_sci_sync(ec, acpi_ec_read_status(ec));
if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) {
/* it is safe to enable GPE outside of transaction */
acpi_enable_gpe(NULL, ec->gpe);
} else if (t->irq_count > ACPI_EC_STORM_THRESHOLD) {
pr_info(PREFIX "GPE storm detected, "
"transactions will use polling mode\n");
set_bit(EC_FLAGS_GPE_STORM, &ec->flags);
}
end:
if (ec->global_lock)
acpi_release_global_lock(glk);
Expand Down Expand Up @@ -443,7 +444,7 @@ int ec_transaction(u8 command,

EXPORT_SYMBOL(ec_transaction);

static int acpi_ec_query(struct acpi_ec *ec, u8 * data)
static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 * data)
{
int result;
u8 d;
Expand All @@ -452,20 +453,16 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data)
.wlen = 0, .rlen = 1};
if (!ec || !data)
return -EINVAL;

/*
* 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);
result = acpi_ec_transaction_unlocked(ec, &t);
if (result)
return result;

if (!d)
return -ENODATA;

*data = d;
return 0;
}
Expand Down Expand Up @@ -509,43 +506,78 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit)

EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler);

static void acpi_ec_gpe_query(void *ec_cxt)
static void acpi_ec_run(void *cxt)
{
struct acpi_ec *ec = ec_cxt;
u8 value = 0;
struct acpi_ec_query_handler *handler, copy;

if (!ec || acpi_ec_query(ec, &value))
struct acpi_ec_query_handler *handler = cxt;
if (!handler)
return;
mutex_lock(&ec->lock);
pr_debug(PREFIX "start query execution\n");
if (handler->func)
handler->func(handler->data);
else if (handler->handle)
acpi_evaluate_object(handler->handle, NULL, NULL, NULL);
pr_debug(PREFIX "stop query execution\n");
kfree(handler);
}

static int acpi_ec_sync_query(struct acpi_ec *ec)
{
u8 value = 0;
int status;
struct acpi_ec_query_handler *handler, *copy;
if ((status = acpi_ec_query_unlocked(ec, &value)))
return status;
list_for_each_entry(handler, &ec->list, node) {
if (value == handler->query_bit) {
/* have custom handler for this bit */
memcpy(&copy, handler, sizeof(copy));
mutex_unlock(&ec->lock);
if (copy.func) {
copy.func(copy.data);
} else if (copy.handle) {
acpi_evaluate_object(copy.handle, NULL, NULL, NULL);
}
return;
copy = kmalloc(sizeof(*handler), GFP_KERNEL);
if (!copy)
return -ENOMEM;
memcpy(copy, handler, sizeof(*copy));
pr_debug(PREFIX "push query execution (0x%2x) on queue\n", value);
return acpi_os_execute(OSL_GPE_HANDLER,
acpi_ec_run, copy);
}
}
return 0;
}

static void acpi_ec_gpe_query(void *ec_cxt)
{
struct acpi_ec *ec = ec_cxt;
if (!ec)
return;
mutex_lock(&ec->lock);
acpi_ec_sync_query(ec);
mutex_unlock(&ec->lock);
}

static void acpi_ec_gpe_query(void *ec_cxt);

static int ec_check_sci(struct acpi_ec *ec, u8 state)
{
if (state & ACPI_EC_FLAG_SCI) {
if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
pr_debug(PREFIX "push gpe query to the queue\n");
return acpi_os_execute(OSL_NOTIFY_HANDLER,
acpi_ec_gpe_query, ec);
}
}
return 0;
}

static u32 acpi_ec_gpe_handler(void *data)
{
struct acpi_ec *ec = data;
u8 status;

pr_debug(PREFIX "~~~> interrupt\n");
status = acpi_ec_read_status(ec);

advance_transaction(ec, status);
if (ec_transaction_done(ec) && (status & ACPI_EC_FLAG_IBF) == 0)
advance_transaction(ec, acpi_ec_read_status(ec));
if (ec_transaction_done(ec) &&
(acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) == 0) {
wake_up(&ec->wait);
ec_check_sci(ec, status);
ec_check_sci(ec, acpi_ec_read_status(ec));
}
return ACPI_INTERRUPT_HANDLED;
}

Expand Down

0 comments on commit a62e8f1

Please sign in to comment.