Skip to content

Commit

Permalink
ACPI: EC: Workaround for optimized controllers (version 3)
Browse files Browse the repository at this point in the history
Some controllers fail to send confirmation GPE after address or data write.
Detect this and don't expect such confirmation in future.
This is a generalization of previous workaround
(66c5f4e), which did only read address.

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

Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Tested-by: Romano Giannetti <romano.giannetti@gmail.com>
Tested-by: Mats Johannesson
Signed-off-by: Len Brown <len.brown@intel.com>
  • Loading branch information
Alexey Starikovskiy authored and Len Brown committed Nov 21, 2007
1 parent 3ebe08a commit e790cc8
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 14 deletions.
46 changes: 33 additions & 13 deletions drivers/acpi/ec.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ enum {
EC_FLAGS_WAIT_GPE = 0, /* Don't check status until GPE arrives */
EC_FLAGS_QUERY_PENDING, /* Query is pending */
EC_FLAGS_GPE_MODE, /* Expect GPE to be sent for status change */
EC_FLAGS_ONLY_IBF_GPE, /* Expect GPE only for IBF = 0 event */
EC_FLAGS_NO_ADDRESS_GPE, /* Expect GPE only for non-address event */
EC_FLAGS_ADDRESS, /* Address is being written */
EC_FLAGS_NO_WDATA_GPE, /* Don't expect WDATA GPE event */
EC_FLAGS_WDATA, /* Data is being written */
};

static int acpi_ec_remove(struct acpi_device *device, int type);
Expand Down Expand Up @@ -175,39 +178,54 @@ static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event)

static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
{
int ret = 0;
if (unlikely(test_bit(EC_FLAGS_ADDRESS, &ec->flags) &&
test_bit(EC_FLAGS_NO_ADDRESS_GPE, &ec->flags)))
force_poll = 1;
if (unlikely(test_bit(EC_FLAGS_WDATA, &ec->flags) &&
test_bit(EC_FLAGS_NO_WDATA_GPE, &ec->flags)))
force_poll = 1;
if (likely(test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) &&
likely(!force_poll)) {
if (wait_event_timeout(ec->wait, acpi_ec_check_status(ec, event),
msecs_to_jiffies(ACPI_EC_DELAY)))
return 0;
goto end;
clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
if (acpi_ec_check_status(ec, event)) {
if (event == ACPI_EC_EVENT_OBF_1) {
/* miss OBF = 1 GPE, don't expect it anymore */
pr_info(PREFIX "missing OBF_1 confirmation,"
"switching to degraded mode.\n");
set_bit(EC_FLAGS_ONLY_IBF_GPE, &ec->flags);
if (test_bit(EC_FLAGS_ADDRESS, &ec->flags)) {
/* miss address GPE, don't expect it anymore */
pr_info(PREFIX "missing address confirmation, "
"don't expect it any longer.\n");
set_bit(EC_FLAGS_NO_ADDRESS_GPE, &ec->flags);
} else if (test_bit(EC_FLAGS_WDATA, &ec->flags)) {
/* miss write data GPE, don't expect it */
pr_info(PREFIX "missing write data confirmation, "
"don't expect it any longer.\n");
set_bit(EC_FLAGS_NO_WDATA_GPE, &ec->flags);
} else {
/* missing GPEs, switch back to poll mode */
if (printk_ratelimit())
pr_info(PREFIX "missing IBF_1 confirmations,"
pr_info(PREFIX "missing confirmations, "
"switch off interrupt mode.\n");
clear_bit(EC_FLAGS_GPE_MODE, &ec->flags);
}
return 0;
goto end;
}
} else {
unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY);
clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
while (time_before(jiffies, delay)) {
if (acpi_ec_check_status(ec, event))
return 0;
goto end;
}
}
pr_err(PREFIX "acpi_ec_wait timeout,"
" status = %d, expect_event = %d\n",
acpi_ec_read_status(ec), event);
return -ETIME;
ret = -ETIME;
end:
clear_bit(EC_FLAGS_ADDRESS, &ec->flags);
return ret;
}

static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command,
Expand All @@ -226,11 +244,15 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command,
"write_cmd timeout, command = %d\n", command);
goto end;
}
/* mark the address byte written to EC */
if (rdata_len + wdata_len > 1)
set_bit(EC_FLAGS_ADDRESS, &ec->flags);
set_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
acpi_ec_write_data(ec, *(wdata++));
}

if (!rdata_len) {
set_bit(EC_FLAGS_WDATA, &ec->flags);
result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, force_poll);
if (result) {
pr_err(PREFIX
Expand All @@ -241,8 +263,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command,
clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);

for (; rdata_len > 0; --rdata_len) {
if (test_bit(EC_FLAGS_ONLY_IBF_GPE, &ec->flags))
force_poll = 1;
result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1, force_poll);
if (result) {
pr_err(PREFIX "read timeout, command = %d\n", command);
Expand Down
5 changes: 4 additions & 1 deletion drivers/acpi/processor_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data)
{
struct acpi_processor *pr = data;
struct acpi_device *device = NULL;

int saved;

if (!pr)
return;
Expand All @@ -694,7 +694,10 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data)

switch (event) {
case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
saved = pr->performance_platform_limit;
acpi_processor_ppc_has_changed(pr);
if (saved == pr->performance_platform_limit)
break;
acpi_bus_generate_proc_event(device, event,
pr->performance_platform_limit);
acpi_bus_generate_netlink_event(device->pnp.device_class,
Expand Down

0 comments on commit e790cc8

Please sign in to comment.