summaryrefslogtreecommitdiffstats
path: root/drivers/acpi/ec.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/ec.c')
-rw-r--r--drivers/acpi/ec.c59
1 files changed, 46 insertions, 13 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index d411017f8c06..7222a18a0319 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -26,6 +26,9 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
+/* Uncomment next line to get verbose print outs*/
+/* #define DEBUG */
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -47,9 +50,6 @@
#undef PREFIX
#define PREFIX "ACPI: EC: "
-/* Uncomment next line to get verbose print outs*/
-/* #define DEBUG */
-
/* EC status register */
#define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */
#define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */
@@ -82,6 +82,7 @@ enum {
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 */
+ EC_FLAGS_NO_OBF1_GPE, /* Don't expect GPE before read */
};
static int acpi_ec_remove(struct acpi_device *device, int type);
@@ -138,26 +139,26 @@ static struct acpi_ec {
static inline u8 acpi_ec_read_status(struct acpi_ec *ec)
{
u8 x = inb(ec->command_addr);
- pr_debug(PREFIX "---> status = 0x%2x\n", x);
+ pr_debug(PREFIX "---> status = 0x%2.2x\n", x);
return x;
}
static inline u8 acpi_ec_read_data(struct acpi_ec *ec)
{
u8 x = inb(ec->data_addr);
- pr_debug(PREFIX "---> data = 0x%2x\n", x);
+ pr_debug(PREFIX "---> data = 0x%2.2x\n", x);
return inb(ec->data_addr);
}
static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command)
{
- pr_debug(PREFIX "<--- command = 0x%2x\n", command);
+ pr_debug(PREFIX "<--- command = 0x%2.2x\n", command);
outb(command, ec->command_addr);
}
static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data)
{
- pr_debug(PREFIX "<--- data = 0x%2x\n", data);
+ pr_debug(PREFIX "<--- data = 0x%2.2x\n", data);
outb(data, ec->data_addr);
}
@@ -179,6 +180,10 @@ 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(event == ACPI_EC_EVENT_OBF_1 &&
+ test_bit(EC_FLAGS_NO_OBF1_GPE, &ec->flags)))
+ force_poll = 1;
if (unlikely(test_bit(EC_FLAGS_ADDRESS, &ec->flags) &&
test_bit(EC_FLAGS_NO_ADDRESS_GPE, &ec->flags)))
force_poll = 1;
@@ -192,7 +197,12 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
goto end;
clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
if (acpi_ec_check_status(ec, event)) {
- if (test_bit(EC_FLAGS_ADDRESS, &ec->flags)) {
+ if (event == ACPI_EC_EVENT_OBF_1) {
+ /* miss OBF_1 GPE, don't expect it */
+ pr_info(PREFIX "missing OBF confirmation, "
+ "don't expect it any longer.\n");
+ set_bit(EC_FLAGS_NO_OBF1_GPE, &ec->flags);
+ } else 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");
@@ -563,7 +573,7 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
void *handler_context, void *region_context)
{
struct acpi_ec *ec = handler_context;
- int result = 0, i = 0;
+ int result = 0, i;
u8 temp = 0;
if ((address > 0xFF) || !value || !handler_context)
@@ -575,7 +585,18 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
if (bits != 8 && acpi_strict)
return AE_BAD_PARAMETER;
- while (bits - i > 0) {
+ acpi_ec_burst_enable(ec);
+
+ if (function == ACPI_READ) {
+ result = acpi_ec_read(ec, address, &temp);
+ *value = temp;
+ } else {
+ temp = 0xff & (*value);
+ result = acpi_ec_write(ec, address, temp);
+ }
+
+ for (i = 8; unlikely(bits - i > 0); i += 8) {
+ ++address;
if (function == ACPI_READ) {
result = acpi_ec_read(ec, address, &temp);
(*value) |= ((acpi_integer)temp) << i;
@@ -583,10 +604,10 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
temp = 0xff & ((*value) >> i);
result = acpi_ec_write(ec, address, temp);
}
- i += 8;
- ++address;
}
+ acpi_ec_burst_disable(ec);
+
switch (result) {
case -EINVAL:
return AE_BAD_PARAMETER;
@@ -892,6 +913,17 @@ static int acpi_ec_stop(struct acpi_device *device, int type)
return 0;
}
+int __init acpi_boot_ec_enable(void)
+{
+ if (!boot_ec || boot_ec->handlers_installed)
+ return 0;
+ if (!ec_install_handlers(boot_ec)) {
+ first_ec = boot_ec;
+ return 0;
+ }
+ return -EFAULT;
+}
+
int __init acpi_ec_ecdt_probe(void)
{
int ret;
@@ -924,9 +956,10 @@ int __init acpi_ec_ecdt_probe(void)
goto error;
/* We really need to limit this workaround, the only ASUS,
* which needs it, has fake EC._INI method, so use it as flag.
+ * Keep boot_ec struct as it will be needed soon.
*/
if (ACPI_FAILURE(acpi_get_handle(boot_ec->handle, "_INI", &x)))
- goto error;
+ return -ENODEV;
}
ret = ec_install_handlers(boot_ec);