diff options
Diffstat (limited to 'drivers/acpi/ec.c')
-rw-r--r-- | drivers/acpi/ec.c | 318 |
1 files changed, 138 insertions, 180 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 391f331..baef28c 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -42,12 +42,12 @@ #include <asm/io.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#include <linux/dmi.h> #define ACPI_EC_CLASS "embedded_controller" #define ACPI_EC_DEVICE_NAME "Embedded Controller" #define ACPI_EC_FILE_INFO "info" -#undef PREFIX #define PREFIX "ACPI: EC: " /* EC status register */ @@ -68,15 +68,13 @@ enum ec_command { #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_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 per one transaction */ enum { 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_HANDLERS_INSTALLED /* Handlers for GPE and * OpReg are installed */ @@ -121,6 +119,8 @@ static struct acpi_ec { } *boot_ec, *first_ec; static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ +static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ +static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */ /* -------------------------------------------------------------------------- Transaction Management @@ -170,7 +170,7 @@ static void start_transaction(struct acpi_ec *ec) 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; spin_lock_irqsave(&ec->curr_lock, flags); @@ -201,29 +201,6 @@ unlock: 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 int ec_check_sci(struct acpi_ec *ec, u8 state) @@ -236,43 +213,49 @@ static int ec_check_sci(struct acpi_ec *ec, u8 state) 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) { - unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); - udelay(ACPI_EC_CDELAY); - while (time_before(jiffies, delay)) { - gpe_transaction(ec, acpi_ec_read_status(ec)); - ec_delay(); - if (ec_transaction_done(ec)) - return 0; + unsigned long flags; + int repeat = 2; /* number of command restarts */ + while (repeat--) { + unsigned long delay = jiffies + + msecs_to_jiffies(ACPI_EC_DELAY); + do { + /* 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 (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) + break; + 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; } static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, - struct transaction *t, - int force_poll) + struct transaction *t) { unsigned long tmp; int ret = 0; pr_debug(PREFIX "transaction start\n"); /* disable GPE during transaction if storm is detected */ if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { - clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); acpi_disable_gpe(NULL, ec->gpe); } if (EC_FLAGS_MSI) - udelay(ACPI_EC_DELAY); + udelay(ACPI_EC_MSI_UDELAY); /* start transaction */ spin_lock_irqsave(&ec->curr_lock, tmp); /* following two actions should be kept atomic */ @@ -281,11 +264,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, if (ec->curr->command == ACPI_EC_COMMAND_QUERY) clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); spin_unlock_irqrestore(&ec->curr_lock, tmp); - /* if we selected poll mode or failed in GPE-mode do a poll loop */ - if (force_poll || - !test_bit(EC_FLAGS_GPE_MODE, &ec->flags) || - acpi_ec_wait(ec)) - ret = ec_poll(ec); + ret = ec_poll(ec); pr_debug(PREFIX "transaction end\n"); spin_lock_irqsave(&ec->curr_lock, tmp); ec->curr = NULL; @@ -295,8 +274,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, ec_check_sci(ec, acpi_ec_read_status(ec)); /* it is safe to enable GPE outside of transaction */ acpi_enable_gpe(NULL, ec->gpe); - } else if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags) && - t->irq_count > ACPI_EC_STORM_THRESHOLD) { + } else if (t->irq_count > ACPI_EC_STORM_THRESHOLD) { pr_info(PREFIX "GPE storm detected, " "transactions will use polling mode\n"); set_bit(EC_FLAGS_GPE_STORM, &ec->flags); @@ -314,16 +292,14 @@ static int ec_wait_ibf0(struct acpi_ec *ec) { unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); /* 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)) - 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 -ETIME; } -static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t, - int force_poll) +static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) { int status; u32 glk; @@ -345,7 +321,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t, status = -ETIME; goto end; } - status = acpi_ec_transaction_unlocked(ec, t, force_poll); + status = acpi_ec_transaction_unlocked(ec, t); end: if (ec->global_lock) acpi_release_global_lock(glk); @@ -354,10 +330,6 @@ unlock: 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) { u8 d; @@ -365,7 +337,7 @@ static int acpi_ec_burst_enable(struct acpi_ec *ec) .wdata = NULL, .rdata = &d, .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) @@ -375,7 +347,7 @@ static int acpi_ec_burst_disable(struct acpi_ec *ec) .wlen = 0, .rlen = 0}; 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) @@ -386,7 +358,7 @@ static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) .wdata = &address, .rdata = &d, .wlen = 1, .rlen = 1}; - result = acpi_ec_transaction(ec, &t, 0); + result = acpi_ec_transaction(ec, &t); *data = d; return result; } @@ -398,7 +370,7 @@ static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) .wdata = wdata, .rdata = NULL, .wlen = 2, .rlen = 0}; - return acpi_ec_transaction(ec, &t, 0); + return acpi_ec_transaction(ec, &t); } /* @@ -466,7 +438,7 @@ int ec_transaction(u8 command, if (!first_ec) return -ENODEV; - return acpi_ec_transaction(first_ec, &t, force_poll); + return acpi_ec_transaction(first_ec, &t); } EXPORT_SYMBOL(ec_transaction); @@ -487,7 +459,7 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data) * 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) return result; @@ -570,28 +542,10 @@ static u32 acpi_ec_gpe_handler(void *data) pr_debug(PREFIX "~~~> interrupt\n"); status = acpi_ec_read_status(ec); - if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) { - gpe_transaction(ec, status); - if (ec_transaction_done(ec) && - (status & ACPI_EC_FLAG_IBF) == 0) - wake_up(&ec->wait); - } - + advance_transaction(ec, status); + if (ec_transaction_done(ec) && (status & ACPI_EC_FLAG_IBF) == 0) + wake_up(&ec->wait); 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; } @@ -617,7 +571,8 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, if (bits != 8 && acpi_strict) return AE_BAD_PARAMETER; - acpi_ec_burst_enable(ec); + if (EC_FLAGS_MSI) + acpi_ec_burst_enable(ec); if (function == ACPI_READ) { result = acpi_ec_read(ec, address, &temp); @@ -638,7 +593,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) { case -EINVAL: @@ -788,6 +744,42 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) return AE_CTRL_TERMINATE; } +static int ec_install_handlers(struct acpi_ec *ec) +{ + acpi_status status; + if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) + return 0; + status = acpi_install_gpe_handler(NULL, ec->gpe, + ACPI_GPE_EDGE_TRIGGERED, + &acpi_ec_gpe_handler, ec); + if (ACPI_FAILURE(status)) + return -ENODEV; + acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); + acpi_enable_gpe(NULL, ec->gpe); + status = acpi_install_address_space_handler(ec->handle, + ACPI_ADR_SPACE_EC, + &acpi_ec_space_handler, + NULL, ec); + if (ACPI_FAILURE(status)) { + if (status == AE_NOT_FOUND) { + /* + * Maybe OS fails in evaluating the _REG object. + * The AE_NOT_FOUND error will be ignored and OS + * continue to initialize EC. + */ + printk(KERN_ERR "Fail in evaluating the _REG object" + " of EC device. Broken bios is suspected.\n"); + } else { + acpi_remove_gpe_handler(NULL, ec->gpe, + &acpi_ec_gpe_handler); + return -ENODEV; + } + } + + set_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags); + return 0; +} + static void ec_remove_handlers(struct acpi_ec *ec) { if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, @@ -802,9 +794,8 @@ static void ec_remove_handlers(struct acpi_ec *ec) static int acpi_ec_add(struct acpi_device *device) { struct acpi_ec *ec = NULL; + int ret; - if (!device) - return -EINVAL; strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_EC_CLASS); @@ -837,9 +828,12 @@ static int acpi_ec_add(struct acpi_device *device) acpi_ec_add_fs(device); pr_info(PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", 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"); - return 0; + + ret = ec_install_handlers(ec); + + /* EC is fully operational, allow queries */ + clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); + return ret; } static int acpi_ec_remove(struct acpi_device *device, int type) @@ -851,6 +845,7 @@ static int acpi_ec_remove(struct acpi_device *device, int type) return -EINVAL; ec = acpi_driver_data(device); + ec_remove_handlers(ec); mutex_lock(&ec->lock); list_for_each_entry_safe(handler, tmp, &ec->list, node) { list_del(&handler->node); @@ -888,91 +883,60 @@ ec_parse_io_ports(struct acpi_resource *resource, void *context) return AE_OK; } -static int ec_install_handlers(struct acpi_ec *ec) +int __init acpi_boot_ec_enable(void) { - acpi_status status; - if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) + if (!boot_ec || test_bit(EC_FLAGS_HANDLERS_INSTALLED, &boot_ec->flags)) + return 0; + if (!ec_install_handlers(boot_ec)) { + first_ec = boot_ec; return 0; - status = acpi_install_gpe_handler(NULL, ec->gpe, - ACPI_GPE_EDGE_TRIGGERED, - &acpi_ec_gpe_handler, ec); - if (ACPI_FAILURE(status)) - return -ENODEV; - acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe(NULL, ec->gpe); - status = acpi_install_address_space_handler(ec->handle, - ACPI_ADR_SPACE_EC, - &acpi_ec_space_handler, - NULL, ec); - if (ACPI_FAILURE(status)) { - if (status == AE_NOT_FOUND) { - /* - * Maybe OS fails in evaluating the _REG object. - * The AE_NOT_FOUND error will be ignored and OS - * continue to initialize EC. - */ - printk(KERN_ERR "Fail in evaluating the _REG object" - " of EC device. Broken bios is suspected.\n"); - } else { - acpi_remove_gpe_handler(NULL, ec->gpe, - &acpi_ec_gpe_handler); - return -ENODEV; - } } - - set_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags); - return 0; + return -EFAULT; } -static int acpi_ec_start(struct acpi_device *device) -{ - struct acpi_ec *ec; - int ret = 0; - - if (!device) - return -EINVAL; - - ec = acpi_driver_data(device); - - if (!ec) - return -EINVAL; - - ret = ec_install_handlers(ec); +static const struct acpi_device_id ec_device_ids[] = { + {"PNP0C09", 0}, + {"", 0}, +}; - /* EC is fully operational, allow queries */ - clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); - return ret; +/* Some BIOS do not survive early DSDT scan, skip it */ +static int ec_skip_dsdt_scan(const struct dmi_system_id *id) +{ + EC_FLAGS_SKIP_DSDT_SCAN = 1; + return 0; } -static int acpi_ec_stop(struct acpi_device *device, int type) +/* ASUStek often supplies us with broken ECDT, validate it */ +static int ec_validate_ecdt(const struct dmi_system_id *id) { - struct acpi_ec *ec; - if (!device) - return -EINVAL; - ec = acpi_driver_data(device); - if (!ec) - return -EINVAL; - ec_remove_handlers(ec); - + EC_FLAGS_VALIDATE_ECDT = 1; return 0; } -int __init acpi_boot_ec_enable(void) +/* MSI EC needs special treatment, enable it */ +static int ec_flag_msi(const struct dmi_system_id *id) { - if (!boot_ec || test_bit(EC_FLAGS_HANDLERS_INSTALLED, &boot_ec->flags)) - return 0; - if (!ec_install_handlers(boot_ec)) { - first_ec = boot_ec; - return 0; - } - return -EFAULT; + EC_FLAGS_MSI = 1; + EC_FLAGS_VALIDATE_ECDT = 1; + return 0; } -static const struct acpi_device_id ec_device_ids[] = { - {"PNP0C09", 0}, - {"", 0}, +static struct dmi_system_id __initdata ec_dmi_table[] = { + { + ec_skip_dsdt_scan, "Compal JFL92", { + DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"), + DMI_MATCH(DMI_BOARD_NAME, "JFL92") }, NULL}, + { + ec_flag_msi, "MSI hardware", { + DMI_MATCH(DMI_BIOS_VENDOR, "Micro-Star"), + DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-Star") }, NULL}, + { + ec_validate_ecdt, "ASUS hardware", { + DMI_MATCH(DMI_BIOS_VENDOR, "ASUS") }, NULL}, + {}, }; + int __init acpi_ec_ecdt_probe(void) { acpi_status status; @@ -985,11 +949,7 @@ int __init acpi_ec_ecdt_probe(void) /* * Generate a boot ec context */ - if (dmi_name_in_vendors("Micro-Star") || - dmi_name_in_vendors("Notebook")) { - pr_info(PREFIX "Enabling special treatment for EC from MSI.\n"); - EC_FLAGS_MSI = 1; - } + dmi_check_system(ec_dmi_table); status = acpi_get_table(ACPI_SIG_ECDT, 1, (struct acpi_table_header **)&ecdt_ptr); if (ACPI_SUCCESS(status)) { @@ -1000,7 +960,7 @@ int __init acpi_ec_ecdt_probe(void) boot_ec->handle = ACPI_ROOT_OBJECT; acpi_get_handle(ACPI_ROOT_OBJECT, ecdt_ptr->id, &boot_ec->handle); /* Don't trust ECDT, which comes from ASUSTek */ - if (!dmi_name_in_vendors("ASUS") && EC_FLAGS_MSI == 0) + if (!EC_FLAGS_VALIDATE_ECDT) goto install; saved_ec = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); if (!saved_ec) @@ -1008,6 +968,10 @@ int __init acpi_ec_ecdt_probe(void) memcpy(saved_ec, boot_ec, sizeof(struct acpi_ec)); /* fall through */ } + + if (EC_FLAGS_SKIP_DSDT_SCAN) + return -ENODEV; + /* This workaround is needed only on some broken machines, * which require early EC, but fail to provide ECDT */ printk(KERN_DEBUG PREFIX "Look up EC in DSDT\n"); @@ -1054,8 +1018,6 @@ static int acpi_ec_suspend(struct acpi_device *device, pm_message_t state) { struct acpi_ec *ec = acpi_driver_data(device); /* 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); return 0; } @@ -1064,8 +1026,6 @@ static int acpi_ec_resume(struct acpi_device *device) { struct acpi_ec *ec = acpi_driver_data(device); /* 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); return 0; } @@ -1077,8 +1037,6 @@ static struct acpi_driver acpi_ec_driver = { .ops = { .add = acpi_ec_add, .remove = acpi_ec_remove, - .start = acpi_ec_start, - .stop = acpi_ec_stop, .suspend = acpi_ec_suspend, .resume = acpi_ec_resume, }, |