diff options
Diffstat (limited to 'drivers/acpi/ec.c')
-rw-r--r-- | drivers/acpi/ec.c | 59 |
1 files changed, 46 insertions, 13 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index d411017f8c0..7222a18a031 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); |