Merge branch 'ec' into release
Conflicts: drivers/acpi/ec.c Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
commit
762caf0baa
|
@ -67,15 +67,13 @@ 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_CDELAY 10 /* Wait 10us before polling EC */
|
#define ACPI_EC_CDELAY 10 /* Wait 10us before polling EC */
|
||||||
|
#define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */
|
||||||
|
|
||||||
#define ACPI_EC_STORM_THRESHOLD 8 /* number of false interrupts
|
#define ACPI_EC_STORM_THRESHOLD 8 /* number of false interrupts
|
||||||
per one transaction */
|
per one transaction */
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
EC_FLAGS_QUERY_PENDING, /* Query is pending */
|
EC_FLAGS_QUERY_PENDING, /* Query is pending */
|
||||||
EC_FLAGS_GPE_MODE, /* Expect GPE to be sent
|
|
||||||
* for status change */
|
|
||||||
EC_FLAGS_NO_GPE, /* Don't use GPE mode */
|
|
||||||
EC_FLAGS_GPE_STORM, /* GPE storm detected */
|
EC_FLAGS_GPE_STORM, /* GPE storm detected */
|
||||||
EC_FLAGS_HANDLERS_INSTALLED /* Handlers for GPE and
|
EC_FLAGS_HANDLERS_INSTALLED /* Handlers for GPE and
|
||||||
* OpReg are installed */
|
* OpReg are installed */
|
||||||
|
@ -169,7 +167,7 @@ static void start_transaction(struct acpi_ec *ec)
|
||||||
acpi_ec_write_cmd(ec, ec->curr->command);
|
acpi_ec_write_cmd(ec, ec->curr->command);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gpe_transaction(struct acpi_ec *ec, u8 status)
|
static void advance_transaction(struct acpi_ec *ec, u8 status)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
spin_lock_irqsave(&ec->curr_lock, flags);
|
spin_lock_irqsave(&ec->curr_lock, flags);
|
||||||
|
@ -200,29 +198,6 @@ unlock:
|
||||||
spin_unlock_irqrestore(&ec->curr_lock, flags);
|
spin_unlock_irqrestore(&ec->curr_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int acpi_ec_wait(struct acpi_ec *ec)
|
|
||||||
{
|
|
||||||
if (wait_event_timeout(ec->wait, ec_transaction_done(ec),
|
|
||||||
msecs_to_jiffies(ACPI_EC_DELAY)))
|
|
||||||
return 0;
|
|
||||||
/* try restart command if we get any false interrupts */
|
|
||||||
if (ec->curr->irq_count &&
|
|
||||||
(acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) == 0) {
|
|
||||||
pr_debug(PREFIX "controller reset, restart transaction\n");
|
|
||||||
start_transaction(ec);
|
|
||||||
if (wait_event_timeout(ec->wait, ec_transaction_done(ec),
|
|
||||||
msecs_to_jiffies(ACPI_EC_DELAY)))
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* missing GPEs, switch back to poll mode */
|
|
||||||
if (printk_ratelimit())
|
|
||||||
pr_info(PREFIX "missing confirmations, "
|
|
||||||
"switch off interrupt mode.\n");
|
|
||||||
set_bit(EC_FLAGS_NO_GPE, &ec->flags);
|
|
||||||
clear_bit(EC_FLAGS_GPE_MODE, &ec->flags);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void acpi_ec_gpe_query(void *ec_cxt);
|
static void acpi_ec_gpe_query(void *ec_cxt);
|
||||||
|
|
||||||
static int ec_check_sci(struct acpi_ec *ec, u8 state)
|
static int ec_check_sci(struct acpi_ec *ec, u8 state)
|
||||||
|
@ -235,43 +210,51 @@ static int ec_check_sci(struct acpi_ec *ec, u8 state)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ec_delay(void)
|
|
||||||
{
|
|
||||||
/* EC in MSI notebooks don't tolerate delays other than 550 usec */
|
|
||||||
if (EC_FLAGS_MSI)
|
|
||||||
udelay(ACPI_EC_DELAY);
|
|
||||||
else
|
|
||||||
/* Use shortest sleep available */
|
|
||||||
msleep(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ec_poll(struct acpi_ec *ec)
|
static int ec_poll(struct acpi_ec *ec)
|
||||||
{
|
{
|
||||||
unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY);
|
unsigned long flags;
|
||||||
udelay(ACPI_EC_CDELAY);
|
int repeat = 2; /* number of command restarts */
|
||||||
while (time_before(jiffies, delay)) {
|
while (repeat--) {
|
||||||
gpe_transaction(ec, acpi_ec_read_status(ec));
|
unsigned long delay = jiffies +
|
||||||
ec_delay();
|
msecs_to_jiffies(ACPI_EC_DELAY);
|
||||||
if (ec_transaction_done(ec))
|
do {
|
||||||
return 0;
|
/* don't sleep with disabled interrupts */
|
||||||
|
if (EC_FLAGS_MSI || irqs_disabled()) {
|
||||||
|
udelay(ACPI_EC_MSI_UDELAY);
|
||||||
|
if (ec_transaction_done(ec))
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
if (wait_event_timeout(ec->wait,
|
||||||
|
ec_transaction_done(ec),
|
||||||
|
msecs_to_jiffies(1)))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
advance_transaction(ec, acpi_ec_read_status(ec));
|
||||||
|
} while (time_before(jiffies, delay));
|
||||||
|
if (!ec->curr->irq_count ||
|
||||||
|
(acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF))
|
||||||
|
break;
|
||||||
|
/* try restart command if we get any false interrupts */
|
||||||
|
pr_debug(PREFIX "controller reset, restart transaction\n");
|
||||||
|
spin_lock_irqsave(&ec->curr_lock, flags);
|
||||||
|
start_transaction(ec);
|
||||||
|
spin_unlock_irqrestore(&ec->curr_lock, flags);
|
||||||
}
|
}
|
||||||
return -ETIME;
|
return -ETIME;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
|
static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
|
||||||
struct transaction *t,
|
struct transaction *t)
|
||||||
int force_poll)
|
|
||||||
{
|
{
|
||||||
unsigned long tmp;
|
unsigned long tmp;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
pr_debug(PREFIX "transaction start\n");
|
pr_debug(PREFIX "transaction start\n");
|
||||||
/* disable GPE during transaction if storm is detected */
|
/* disable GPE during transaction if storm is detected */
|
||||||
if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) {
|
if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) {
|
||||||
clear_bit(EC_FLAGS_GPE_MODE, &ec->flags);
|
|
||||||
acpi_disable_gpe(NULL, ec->gpe);
|
acpi_disable_gpe(NULL, ec->gpe);
|
||||||
}
|
}
|
||||||
if (EC_FLAGS_MSI)
|
if (EC_FLAGS_MSI)
|
||||||
udelay(ACPI_EC_DELAY);
|
udelay(ACPI_EC_MSI_UDELAY);
|
||||||
/* start transaction */
|
/* start transaction */
|
||||||
spin_lock_irqsave(&ec->curr_lock, tmp);
|
spin_lock_irqsave(&ec->curr_lock, tmp);
|
||||||
/* following two actions should be kept atomic */
|
/* following two actions should be kept atomic */
|
||||||
|
@ -280,11 +263,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
|
||||||
if (ec->curr->command == ACPI_EC_COMMAND_QUERY)
|
if (ec->curr->command == ACPI_EC_COMMAND_QUERY)
|
||||||
clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
|
clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
|
||||||
spin_unlock_irqrestore(&ec->curr_lock, tmp);
|
spin_unlock_irqrestore(&ec->curr_lock, tmp);
|
||||||
/* if we selected poll mode or failed in GPE-mode do a poll loop */
|
ret = ec_poll(ec);
|
||||||
if (force_poll ||
|
|
||||||
!test_bit(EC_FLAGS_GPE_MODE, &ec->flags) ||
|
|
||||||
acpi_ec_wait(ec))
|
|
||||||
ret = ec_poll(ec);
|
|
||||||
pr_debug(PREFIX "transaction end\n");
|
pr_debug(PREFIX "transaction end\n");
|
||||||
spin_lock_irqsave(&ec->curr_lock, tmp);
|
spin_lock_irqsave(&ec->curr_lock, tmp);
|
||||||
ec->curr = NULL;
|
ec->curr = NULL;
|
||||||
|
@ -294,8 +273,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
|
||||||
ec_check_sci(ec, acpi_ec_read_status(ec));
|
ec_check_sci(ec, acpi_ec_read_status(ec));
|
||||||
/* it is safe to enable GPE outside of transaction */
|
/* it is safe to enable GPE outside of transaction */
|
||||||
acpi_enable_gpe(NULL, ec->gpe);
|
acpi_enable_gpe(NULL, ec->gpe);
|
||||||
} else if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags) &&
|
} else if (t->irq_count > ACPI_EC_STORM_THRESHOLD) {
|
||||||
t->irq_count > ACPI_EC_STORM_THRESHOLD) {
|
|
||||||
pr_info(PREFIX "GPE storm detected, "
|
pr_info(PREFIX "GPE storm detected, "
|
||||||
"transactions will use polling mode\n");
|
"transactions will use polling mode\n");
|
||||||
set_bit(EC_FLAGS_GPE_STORM, &ec->flags);
|
set_bit(EC_FLAGS_GPE_STORM, &ec->flags);
|
||||||
|
@ -313,16 +291,14 @@ static int ec_wait_ibf0(struct acpi_ec *ec)
|
||||||
{
|
{
|
||||||
unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY);
|
unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY);
|
||||||
/* interrupt wait manually if GPE mode is not active */
|
/* interrupt wait manually if GPE mode is not active */
|
||||||
unsigned long timeout = test_bit(EC_FLAGS_GPE_MODE, &ec->flags) ?
|
|
||||||
msecs_to_jiffies(ACPI_EC_DELAY) : msecs_to_jiffies(1);
|
|
||||||
while (time_before(jiffies, delay))
|
while (time_before(jiffies, delay))
|
||||||
if (wait_event_timeout(ec->wait, ec_check_ibf0(ec), timeout))
|
if (wait_event_timeout(ec->wait, ec_check_ibf0(ec),
|
||||||
|
msecs_to_jiffies(1)))
|
||||||
return 0;
|
return 0;
|
||||||
return -ETIME;
|
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 force_poll)
|
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
u32 glk;
|
u32 glk;
|
||||||
|
@ -344,7 +320,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t,
|
||||||
status = -ETIME;
|
status = -ETIME;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
status = acpi_ec_transaction_unlocked(ec, t, force_poll);
|
status = acpi_ec_transaction_unlocked(ec, t);
|
||||||
end:
|
end:
|
||||||
if (ec->global_lock)
|
if (ec->global_lock)
|
||||||
acpi_release_global_lock(glk);
|
acpi_release_global_lock(glk);
|
||||||
|
@ -353,10 +329,6 @@ unlock:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Note: samsung nv5000 doesn't work with ec burst mode.
|
|
||||||
* http://bugzilla.kernel.org/show_bug.cgi?id=4980
|
|
||||||
*/
|
|
||||||
static int acpi_ec_burst_enable(struct acpi_ec *ec)
|
static int acpi_ec_burst_enable(struct acpi_ec *ec)
|
||||||
{
|
{
|
||||||
u8 d;
|
u8 d;
|
||||||
|
@ -364,7 +336,7 @@ static int acpi_ec_burst_enable(struct acpi_ec *ec)
|
||||||
.wdata = NULL, .rdata = &d,
|
.wdata = NULL, .rdata = &d,
|
||||||
.wlen = 0, .rlen = 1};
|
.wlen = 0, .rlen = 1};
|
||||||
|
|
||||||
return acpi_ec_transaction(ec, &t, 0);
|
return acpi_ec_transaction(ec, &t);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int acpi_ec_burst_disable(struct acpi_ec *ec)
|
static int acpi_ec_burst_disable(struct acpi_ec *ec)
|
||||||
|
@ -374,7 +346,7 @@ static int acpi_ec_burst_disable(struct acpi_ec *ec)
|
||||||
.wlen = 0, .rlen = 0};
|
.wlen = 0, .rlen = 0};
|
||||||
|
|
||||||
return (acpi_ec_read_status(ec) & ACPI_EC_FLAG_BURST) ?
|
return (acpi_ec_read_status(ec) & ACPI_EC_FLAG_BURST) ?
|
||||||
acpi_ec_transaction(ec, &t, 0) : 0;
|
acpi_ec_transaction(ec, &t) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data)
|
static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data)
|
||||||
|
@ -385,7 +357,7 @@ static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data)
|
||||||
.wdata = &address, .rdata = &d,
|
.wdata = &address, .rdata = &d,
|
||||||
.wlen = 1, .rlen = 1};
|
.wlen = 1, .rlen = 1};
|
||||||
|
|
||||||
result = acpi_ec_transaction(ec, &t, 0);
|
result = acpi_ec_transaction(ec, &t);
|
||||||
*data = d;
|
*data = d;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -397,7 +369,7 @@ static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data)
|
||||||
.wdata = wdata, .rdata = NULL,
|
.wdata = wdata, .rdata = NULL,
|
||||||
.wlen = 2, .rlen = 0};
|
.wlen = 2, .rlen = 0};
|
||||||
|
|
||||||
return acpi_ec_transaction(ec, &t, 0);
|
return acpi_ec_transaction(ec, &t);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -465,7 +437,7 @@ int ec_transaction(u8 command,
|
||||||
if (!first_ec)
|
if (!first_ec)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
return acpi_ec_transaction(first_ec, &t, force_poll);
|
return acpi_ec_transaction(first_ec, &t);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(ec_transaction);
|
EXPORT_SYMBOL(ec_transaction);
|
||||||
|
@ -486,7 +458,7 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data)
|
||||||
* bit to be cleared (and thus clearing the interrupt source).
|
* bit to be cleared (and thus clearing the interrupt source).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
result = acpi_ec_transaction(ec, &t, 0);
|
result = acpi_ec_transaction(ec, &t);
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
@ -569,28 +541,10 @@ static u32 acpi_ec_gpe_handler(void *data)
|
||||||
pr_debug(PREFIX "~~~> interrupt\n");
|
pr_debug(PREFIX "~~~> interrupt\n");
|
||||||
status = acpi_ec_read_status(ec);
|
status = acpi_ec_read_status(ec);
|
||||||
|
|
||||||
if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) {
|
advance_transaction(ec, status);
|
||||||
gpe_transaction(ec, status);
|
if (ec_transaction_done(ec) && (status & ACPI_EC_FLAG_IBF) == 0)
|
||||||
if (ec_transaction_done(ec) &&
|
wake_up(&ec->wait);
|
||||||
(status & ACPI_EC_FLAG_IBF) == 0)
|
|
||||||
wake_up(&ec->wait);
|
|
||||||
}
|
|
||||||
|
|
||||||
ec_check_sci(ec, status);
|
ec_check_sci(ec, status);
|
||||||
if (!test_bit(EC_FLAGS_GPE_MODE, &ec->flags) &&
|
|
||||||
!test_bit(EC_FLAGS_NO_GPE, &ec->flags)) {
|
|
||||||
/* this is non-query, must be confirmation */
|
|
||||||
if (!test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) {
|
|
||||||
if (printk_ratelimit())
|
|
||||||
pr_info(PREFIX "non-query interrupt received,"
|
|
||||||
" switching to interrupt mode\n");
|
|
||||||
} else {
|
|
||||||
/* hush, STORM switches the mode every transaction */
|
|
||||||
pr_debug(PREFIX "non-query interrupt received,"
|
|
||||||
" switching to interrupt mode\n");
|
|
||||||
}
|
|
||||||
set_bit(EC_FLAGS_GPE_MODE, &ec->flags);
|
|
||||||
}
|
|
||||||
return ACPI_INTERRUPT_HANDLED;
|
return ACPI_INTERRUPT_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,7 +570,8 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
|
||||||
if (bits != 8 && acpi_strict)
|
if (bits != 8 && acpi_strict)
|
||||||
return AE_BAD_PARAMETER;
|
return AE_BAD_PARAMETER;
|
||||||
|
|
||||||
acpi_ec_burst_enable(ec);
|
if (EC_FLAGS_MSI)
|
||||||
|
acpi_ec_burst_enable(ec);
|
||||||
|
|
||||||
if (function == ACPI_READ) {
|
if (function == ACPI_READ) {
|
||||||
result = acpi_ec_read(ec, address, &temp);
|
result = acpi_ec_read(ec, address, &temp);
|
||||||
|
@ -637,7 +592,8 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
acpi_ec_burst_disable(ec);
|
if (EC_FLAGS_MSI)
|
||||||
|
acpi_ec_burst_disable(ec);
|
||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case -EINVAL:
|
case -EINVAL:
|
||||||
|
@ -871,8 +827,6 @@ static int acpi_ec_add(struct acpi_device *device)
|
||||||
acpi_ec_add_fs(device);
|
acpi_ec_add_fs(device);
|
||||||
pr_info(PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n",
|
pr_info(PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n",
|
||||||
ec->gpe, ec->command_addr, ec->data_addr);
|
ec->gpe, ec->command_addr, ec->data_addr);
|
||||||
pr_info(PREFIX "driver started in %s mode\n",
|
|
||||||
(test_bit(EC_FLAGS_GPE_MODE, &ec->flags))?"interrupt":"poll");
|
|
||||||
|
|
||||||
ret = ec_install_handlers(ec);
|
ret = ec_install_handlers(ec);
|
||||||
|
|
||||||
|
@ -1025,8 +979,6 @@ static int acpi_ec_suspend(struct acpi_device *device, pm_message_t state)
|
||||||
{
|
{
|
||||||
struct acpi_ec *ec = acpi_driver_data(device);
|
struct acpi_ec *ec = acpi_driver_data(device);
|
||||||
/* Stop using GPE */
|
/* Stop using GPE */
|
||||||
set_bit(EC_FLAGS_NO_GPE, &ec->flags);
|
|
||||||
clear_bit(EC_FLAGS_GPE_MODE, &ec->flags);
|
|
||||||
acpi_disable_gpe(NULL, ec->gpe);
|
acpi_disable_gpe(NULL, ec->gpe);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1035,8 +987,6 @@ static int acpi_ec_resume(struct acpi_device *device)
|
||||||
{
|
{
|
||||||
struct acpi_ec *ec = acpi_driver_data(device);
|
struct acpi_ec *ec = acpi_driver_data(device);
|
||||||
/* Enable use of GPE back */
|
/* Enable use of GPE back */
|
||||||
clear_bit(EC_FLAGS_NO_GPE, &ec->flags);
|
|
||||||
set_bit(EC_FLAGS_GPE_MODE, &ec->flags);
|
|
||||||
acpi_enable_gpe(NULL, ec->gpe);
|
acpi_enable_gpe(NULL, ec->gpe);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue