Commit 0b80a8ff authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'acpi-ec'

* acpi-ec:
  ACPI / EC: Free saved_ec on error exit path
  ACPI / EC: Add detailed fields debugging support of EC_SC(R).
  ACPI / EC: Update revision due to recent changes
  ACPI / EC: Fix race condition in ec_transaction_completed()
  ACPI / EC: Remove duplicated ec_wait_ibf0() waiter
  ACPI / EC: Add asynchronous command byte write support
  ACPI / EC: Avoid race condition related to advance_transaction()
parents 4488c99b ed4b197d
/* /*
* ec.c - ACPI Embedded Controller Driver (v2.1) * ec.c - ACPI Embedded Controller Driver (v2.2)
* *
* Copyright (C) 2006-2008 Alexey Starikovskiy <astarikovskiy@suse.de> * Copyright (C) 2001-2014 Intel Corporation
* Copyright (C) 2006 Denis Sadykov <denis.m.sadykov@intel.com> * Author: 2014 Lv Zheng <lv.zheng@intel.com>
* Copyright (C) 2004 Luming Yu <luming.yu@intel.com> * 2006, 2007 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> * 2006 Denis Sadykov <denis.m.sadykov@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * 2004 Luming Yu <luming.yu@intel.com>
* 2001, 2002 Andy Grover <andrew.grover@intel.com>
* 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2008 Alexey Starikovskiy <astarikovskiy@suse.de>
* *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* *
...@@ -52,6 +55,7 @@ ...@@ -52,6 +55,7 @@
/* EC status register */ /* EC status register */
#define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */ #define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */
#define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */ #define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */
#define ACPI_EC_FLAG_CMD 0x08 /* Input buffer contains a command */
#define ACPI_EC_FLAG_BURST 0x10 /* burst mode */ #define ACPI_EC_FLAG_BURST 0x10 /* burst mode */
#define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */ #define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */
...@@ -78,6 +82,9 @@ enum { ...@@ -78,6 +82,9 @@ enum {
EC_FLAGS_BLOCKED, /* Transactions are blocked */ EC_FLAGS_BLOCKED, /* Transactions are blocked */
}; };
#define ACPI_EC_COMMAND_POLL 0x01 /* Available for command byte */
#define ACPI_EC_COMMAND_COMPLETE 0x02 /* Completed last byte */
/* 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);
...@@ -109,7 +116,7 @@ struct transaction { ...@@ -109,7 +116,7 @@ struct transaction {
u8 ri; u8 ri;
u8 wlen; u8 wlen;
u8 rlen; u8 rlen;
bool done; u8 flags;
}; };
struct acpi_ec *boot_ec, *first_ec; struct acpi_ec *boot_ec, *first_ec;
...@@ -127,83 +134,104 @@ static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ ...@@ -127,83 +134,104 @@ static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */
static inline u8 acpi_ec_read_status(struct acpi_ec *ec) static inline u8 acpi_ec_read_status(struct acpi_ec *ec)
{ {
u8 x = inb(ec->command_addr); u8 x = inb(ec->command_addr);
pr_debug("---> status = 0x%2.2x\n", x); pr_debug("EC_SC(R) = 0x%2.2x "
"SCI_EVT=%d BURST=%d CMD=%d IBF=%d OBF=%d\n",
x,
!!(x & ACPI_EC_FLAG_SCI),
!!(x & ACPI_EC_FLAG_BURST),
!!(x & ACPI_EC_FLAG_CMD),
!!(x & ACPI_EC_FLAG_IBF),
!!(x & ACPI_EC_FLAG_OBF));
return x; return x;
} }
static inline u8 acpi_ec_read_data(struct acpi_ec *ec) static inline u8 acpi_ec_read_data(struct acpi_ec *ec)
{ {
u8 x = inb(ec->data_addr); u8 x = inb(ec->data_addr);
pr_debug("---> data = 0x%2.2x\n", x); pr_debug("EC_DATA(R) = 0x%2.2x\n", x);
return x; return x;
} }
static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command) static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command)
{ {
pr_debug("<--- command = 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);
} }
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("<--- data = 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);
} }
static int ec_transaction_done(struct acpi_ec *ec) static int ec_transaction_completed(struct acpi_ec *ec)
{ {
unsigned long flags; unsigned long flags;
int ret = 0; int ret = 0;
spin_lock_irqsave(&ec->lock, flags); spin_lock_irqsave(&ec->lock, flags);
if (!ec->curr || ec->curr->done) if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE))
ret = 1; ret = 1;
spin_unlock_irqrestore(&ec->lock, flags); spin_unlock_irqrestore(&ec->lock, flags);
return ret; return ret;
} }
static void start_transaction(struct acpi_ec *ec) static bool advance_transaction(struct acpi_ec *ec)
{ {
ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0;
ec->curr->done = false;
acpi_ec_write_cmd(ec, ec->curr->command);
}
static void advance_transaction(struct acpi_ec *ec, u8 status)
{
unsigned long flags;
struct transaction *t; struct transaction *t;
u8 status;
bool wakeup = false;
spin_lock_irqsave(&ec->lock, flags); pr_debug("===== %s =====\n", in_interrupt() ? "IRQ" : "TASK");
status = acpi_ec_read_status(ec);
t = ec->curr; t = ec->curr;
if (!t) if (!t)
goto unlock; goto err;
if (t->wlen > t->wi) { if (t->flags & ACPI_EC_COMMAND_POLL) {
if ((status & ACPI_EC_FLAG_IBF) == 0) if (t->wlen > t->wi) {
acpi_ec_write_data(ec, if ((status & ACPI_EC_FLAG_IBF) == 0)
t->wdata[t->wi++]); acpi_ec_write_data(ec, t->wdata[t->wi++]);
else else
goto err; goto err;
} else if (t->rlen > t->ri) { } else if (t->rlen > t->ri) {
if ((status & ACPI_EC_FLAG_OBF) == 1) { if ((status & ACPI_EC_FLAG_OBF) == 1) {
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->done = true; t->flags |= ACPI_EC_COMMAND_COMPLETE;
wakeup = true;
}
} else
goto err;
} else if (t->wlen == t->wi &&
(status & ACPI_EC_FLAG_IBF) == 0) {
t->flags |= ACPI_EC_COMMAND_COMPLETE;
wakeup = true;
}
return wakeup;
} else {
if ((status & ACPI_EC_FLAG_IBF) == 0) {
acpi_ec_write_cmd(ec, t->command);
t->flags |= ACPI_EC_COMMAND_POLL;
} else } else
goto err; goto err;
} else if (t->wlen == t->wi && return wakeup;
(status & ACPI_EC_FLAG_IBF) == 0) }
t->done = true;
goto unlock;
err: err:
/* /*
* If SCI bit is set, then don't think it's a false IRQ * If SCI bit is set, then don't think it's a false IRQ
* otherwise will take a not handled IRQ as a false one. * otherwise will take a not handled IRQ as a false one.
*/ */
if (in_interrupt() && !(status & ACPI_EC_FLAG_SCI)) if (!(status & ACPI_EC_FLAG_SCI)) {
++t->irq_count; if (in_interrupt() && t)
++t->irq_count;
}
return wakeup;
}
unlock: static void start_transaction(struct acpi_ec *ec)
spin_unlock_irqrestore(&ec->lock, flags); {
ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0;
ec->curr->flags = 0;
(void)advance_transaction(ec);
} }
static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data); static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data);
...@@ -228,15 +256,17 @@ static int ec_poll(struct acpi_ec *ec) ...@@ -228,15 +256,17 @@ static int ec_poll(struct acpi_ec *ec)
/* 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); udelay(ACPI_EC_MSI_UDELAY);
if (ec_transaction_done(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_done(ec), ec_transaction_completed(ec),
msecs_to_jiffies(1))) msecs_to_jiffies(1)))
return 0; return 0;
} }
advance_transaction(ec, acpi_ec_read_status(ec)); spin_lock_irqsave(&ec->lock, flags);
(void)advance_transaction(ec);
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");
spin_lock_irqsave(&ec->lock, flags); spin_lock_irqsave(&ec->lock, flags);
...@@ -268,23 +298,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, ...@@ -268,23 +298,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
return ret; return ret;
} }
static int ec_check_ibf0(struct acpi_ec *ec)
{
u8 status = acpi_ec_read_status(ec);
return (status & ACPI_EC_FLAG_IBF) == 0;
}
static int ec_wait_ibf0(struct acpi_ec *ec)
{
unsigned long delay = jiffies + msecs_to_jiffies(ec_delay);
/* interrupt wait manually if GPE mode is not active */
while (time_before(jiffies, delay))
if (wait_event_timeout(ec->wait, ec_check_ibf0(ec),
msecs_to_jiffies(1)))
return 0;
return -ETIME;
}
static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
{ {
int status; int status;
...@@ -305,12 +318,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) ...@@ -305,12 +318,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
goto unlock; goto unlock;
} }
} }
if (ec_wait_ibf0(ec)) {
pr_err("input buffer is not empty, "
"aborting transaction\n");
status = -ETIME;
goto end;
}
pr_debug("transaction start (cmd=0x%02x, addr=0x%02x)\n", pr_debug("transaction start (cmd=0x%02x, addr=0x%02x)\n",
t->command, t->wdata ? t->wdata[0] : 0); t->command, t->wdata ? t->wdata[0] : 0);
/* disable GPE during transaction if storm is detected */ /* disable GPE during transaction if storm is detected */
...@@ -334,7 +341,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) ...@@ -334,7 +341,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
set_bit(EC_FLAGS_GPE_STORM, &ec->flags); set_bit(EC_FLAGS_GPE_STORM, &ec->flags);
} }
pr_debug("transaction end\n"); pr_debug("transaction end\n");
end:
if (ec->global_lock) if (ec->global_lock)
acpi_release_global_lock(glk); acpi_release_global_lock(glk);
unlock: unlock:
...@@ -634,17 +640,14 @@ static int ec_check_sci(struct acpi_ec *ec, u8 state) ...@@ -634,17 +640,14 @@ static int ec_check_sci(struct acpi_ec *ec, u8 state)
static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, static u32 acpi_ec_gpe_handler(acpi_handle gpe_device,
u32 gpe_number, void *data) u32 gpe_number, void *data)
{ {
unsigned long flags;
struct acpi_ec *ec = data; struct acpi_ec *ec = data;
u8 status = acpi_ec_read_status(ec);
pr_debug("~~~> interrupt, status:0x%02x\n", status); spin_lock_irqsave(&ec->lock, flags);
if (advance_transaction(ec))
advance_transaction(ec, status);
if (ec_transaction_done(ec) &&
(acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) == 0) {
wake_up(&ec->wait); wake_up(&ec->wait);
ec_check_sci(ec, acpi_ec_read_status(ec)); spin_unlock_irqrestore(&ec->lock, flags);
} ec_check_sci(ec, acpi_ec_read_status(ec));
return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE; return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
} }
...@@ -1066,8 +1069,10 @@ int __init acpi_ec_ecdt_probe(void) ...@@ -1066,8 +1069,10 @@ int __init acpi_ec_ecdt_probe(void)
/* fall through */ /* fall through */
} }
if (EC_FLAGS_SKIP_DSDT_SCAN) if (EC_FLAGS_SKIP_DSDT_SCAN) {
kfree(saved_ec);
return -ENODEV; return -ENODEV;
}
/* This workaround is needed only on some broken machines, /* This workaround is needed only on some broken machines,
* which require early EC, but fail to provide ECDT */ * which require early EC, but fail to provide ECDT */
...@@ -1105,6 +1110,7 @@ int __init acpi_ec_ecdt_probe(void) ...@@ -1105,6 +1110,7 @@ int __init acpi_ec_ecdt_probe(void)
} }
error: error:
kfree(boot_ec); kfree(boot_ec);
kfree(saved_ec);
boot_ec = NULL; boot_ec = NULL;
return -ENODEV; return -ENODEV;
} }
......
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