X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Facpi%2Fec.c;h=fdf143b405be7fb638685a7bf0bb104239348dd4;hb=f7f1b0f1e2fbadeab12d24236000e778aa9b1ead;hp=d1419145ef0f1bd3d9719a7e932a909c9ce67ba9;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index d1419145e..fdf143b40 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1,6 +1,7 @@ /* * acpi_ec.c - ACPI Embedded Controller Driver ($Revision: 38 $) * + * Copyright (C) 2004 Luming Yu * Copyright (C) 2001, 2002 Andy Grover * Copyright (C) 2001, 2002 Paul Diefenbaugh * @@ -29,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -262,6 +264,7 @@ ec_read(u8 addr, u8 *val) else return err; } +EXPORT_SYMBOL(ec_read); int ec_write(u8 addr, u8 val) @@ -278,6 +281,7 @@ ec_write(u8 addr, u8 val) return err; } +EXPORT_SYMBOL(ec_write); static int @@ -438,14 +442,23 @@ acpi_ec_space_handler ( int result = 0; struct acpi_ec *ec = NULL; u32 temp = 0; + acpi_integer f_v = 0; + int i = 0; ACPI_FUNCTION_TRACE("acpi_ec_space_handler"); - if ((address > 0xFF) || (bit_width != 8) || !value || !handler_context) + if ((address > 0xFF) || !value || !handler_context) return_VALUE(AE_BAD_PARAMETER); + if(bit_width != 8) { + printk(KERN_WARNING PREFIX "acpi_ec_space_handler: bit_width should be 8\n"); + if (acpi_strict) + return_VALUE(AE_BAD_PARAMETER); + } + ec = (struct acpi_ec *) handler_context; +next_byte: switch (function) { case ACPI_READ: result = acpi_ec_read(ec, (u8) address, &temp); @@ -456,9 +469,29 @@ acpi_ec_space_handler ( break; default: result = -EINVAL; + goto out; break; } + bit_width -= 8; + if(bit_width){ + + if(function == ACPI_READ) + f_v |= (acpi_integer) (*value) << 8*i; + if(function == ACPI_WRITE) + (*value) >>=8; + i++; + goto next_byte; + } + + + if(function == ACPI_READ){ + f_v |= (acpi_integer) (*value) << 8*i; + *value = f_v; + } + + +out: switch (result) { case -EINVAL: return_VALUE(AE_BAD_PARAMETER); @@ -472,6 +505,7 @@ acpi_ec_space_handler ( default: return_VALUE(AE_OK); } + } @@ -480,45 +514,42 @@ acpi_ec_space_handler ( FS Interface (/proc) -------------------------------------------------------------------------- */ -struct proc_dir_entry *acpi_ec_dir; +static struct proc_dir_entry *acpi_ec_dir; static int -acpi_ec_read_info ( - char *page, - char **start, - off_t off, - int count, - int *eof, - void *data) +acpi_ec_read_info (struct seq_file *seq, void *offset) { - struct acpi_ec *ec = (struct acpi_ec *) data; - char *p = page; - int len = 0; + struct acpi_ec *ec = (struct acpi_ec *) seq->private; ACPI_FUNCTION_TRACE("acpi_ec_read_info"); - if (!ec || (off != 0)) + if (!ec) goto end; - p += sprintf(p, "gpe bit: 0x%02x\n", + seq_printf(seq, "gpe bit: 0x%02x\n", (u32) ec->gpe_bit); - p += sprintf(p, "ports: 0x%02x, 0x%02x\n", + seq_printf(seq, "ports: 0x%02x, 0x%02x\n", (u32) ec->status_addr.address, (u32) ec->data_addr.address); - p += sprintf(p, "use global lock: %s\n", + seq_printf(seq, "use global lock: %s\n", ec->global_lock?"yes":"no"); end: - len = (p - page); - if (len <= off+count) *eof = 1; - *start = page + off; - len -= off; - if (len>count) len = count; - if (len<0) len = 0; - - return_VALUE(len); + return_VALUE(0); } +static int acpi_ec_info_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_ec_read_info, PDE(inode)->data); +} + +static struct file_operations acpi_ec_info_ops = { + .open = acpi_ec_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; static int acpi_ec_add_fs ( @@ -535,13 +566,17 @@ acpi_ec_add_fs ( return_VALUE(-ENODEV); } - entry = create_proc_read_entry(ACPI_EC_FILE_INFO, S_IRUGO, - acpi_device_dir(device), acpi_ec_read_info, - acpi_driver_data(device)); + entry = create_proc_entry(ACPI_EC_FILE_INFO, S_IRUGO, + acpi_device_dir(device)); if (!entry) ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to create '%s' fs entry\n", ACPI_EC_FILE_INFO)); + else { + entry->proc_fops = &acpi_ec_info_ops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } return_VALUE(0); } @@ -588,7 +623,7 @@ acpi_ec_add ( ec->handle = device->handle; ec->uid = -1; - ec->lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&ec->lock); strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_EC_CLASS); acpi_driver_data(device) = ec; @@ -776,9 +811,81 @@ acpi_ec_stop ( return_VALUE(0); } +static acpi_status __init +acpi_fake_ecdt_callback ( + acpi_handle handle, + u32 Level, + void *context, + void **retval) +{ + acpi_status status; -int __init -acpi_ec_ecdt_probe (void) + status = acpi_walk_resources(handle, METHOD_NAME__CRS, + acpi_ec_io_ports, ec_ecdt); + if (ACPI_FAILURE(status)) + return status; + ec_ecdt->status_addr = ec_ecdt->command_addr; + + ec_ecdt->uid = -1; + acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid); + + status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe_bit); + if (ACPI_FAILURE(status)) + return status; + spin_lock_init(&ec_ecdt->lock); + ec_ecdt->global_lock = TRUE; + ec_ecdt->handle = handle; + + printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n", + (u32) ec_ecdt->gpe_bit, (u32) ec_ecdt->command_addr.address, + (u32) ec_ecdt->data_addr.address); + + return AE_CTRL_TERMINATE; +} + +/* + * Some BIOS (such as some from Gateway laptops) access EC region very early + * such as in BAT0._INI or EC._INI before an EC device is found and + * do not provide an ECDT. According to ACPI spec, ECDT isn't mandatorily + * required, but if EC regison is accessed early, it is required. + * The routine tries to workaround the BIOS bug by pre-scan EC device + * It assumes that _CRS, _HID, _GPE, _UID methods of EC don't touch any + * op region (since _REG isn't invoked yet). The assumption is true for + * all systems found. + */ +static int __init +acpi_ec_fake_ecdt(void) +{ + acpi_status status; + int ret = 0; + + printk(KERN_INFO PREFIX "Try to make an fake ECDT\n"); + + ec_ecdt = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); + if (!ec_ecdt) { + ret = -ENOMEM; + goto error; + } + memset(ec_ecdt, 0, sizeof(struct acpi_ec)); + + status = acpi_get_devices (ACPI_EC_HID, + acpi_fake_ecdt_callback, + NULL, + NULL); + if (ACPI_FAILURE(status)) { + kfree(ec_ecdt); + ec_ecdt = NULL; + ret = -ENODEV; + goto error; + } + return 0; +error: + printk(KERN_ERR PREFIX "Can't make an fake ECDT\n"); + return ret; +} + +static int __init +acpi_ec_get_real_ecdt(void) { acpi_status status; struct acpi_table_ecdt *ecdt_ptr; @@ -786,11 +893,11 @@ acpi_ec_ecdt_probe (void) status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING, (struct acpi_table_header **) &ecdt_ptr); if (ACPI_FAILURE(status)) - return 0; + return -ENODEV; printk(KERN_INFO PREFIX "Found ECDT\n"); - /* + /* * Generate a temporary ec context to use until the namespace is scanned */ ec_ecdt = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); @@ -802,7 +909,7 @@ acpi_ec_ecdt_probe (void) ec_ecdt->status_addr = ecdt_ptr->ec_control; ec_ecdt->data_addr = ecdt_ptr->ec_data; ec_ecdt->gpe_bit = ecdt_ptr->gpe_bit; - ec_ecdt->lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&ec_ecdt->lock); /* use the GL just to be safe */ ec_ecdt->global_lock = TRUE; ec_ecdt->uid = ecdt_ptr->uid; @@ -812,6 +919,31 @@ acpi_ec_ecdt_probe (void) goto error; } + return 0; +error: + printk(KERN_ERR PREFIX "Could not use ECDT\n"); + kfree(ec_ecdt); + ec_ecdt = NULL; + + return -ENODEV; +} + +static int __initdata acpi_fake_ecdt_enabled; +int __init +acpi_ec_ecdt_probe (void) +{ + acpi_status status; + int ret; + + ret = acpi_ec_get_real_ecdt(); + /* Try to make a fake ECDT */ + if (ret && acpi_fake_ecdt_enabled) { + ret = acpi_ec_fake_ecdt(); + } + + if (ret) + return 0; + /* * Install GPE handler */ @@ -884,3 +1016,9 @@ acpi_ec_exit (void) } #endif /* 0 */ +static int __init acpi_fake_ecdt_setup(char *str) +{ + acpi_fake_ecdt_enabled = 1; + return 0; +} +__setup("acpi_fake_ecdt", acpi_fake_ecdt_setup);