Newer
Older
union acpi_ec *ec = (union acpi_ec *)context;
return AE_OK;
}
/*
* The first address region returned is the data port, and
* the second address region returned is the status/command
* port.
*/
if (ec->common.data_addr.register_bit_width == 0) {
addr = &ec->common.data_addr;
} else if (ec->common.command_addr.register_bit_width == 0) {
addr = &ec->common.command_addr;
} else {
return AE_CTRL_TERMINATE;
}
addr->address_space_id = ACPI_ADR_SPACE_SYSTEM_IO;
addr->register_bit_width = 8;
addr->register_bit_offset = 0;
static int acpi_ec_start(struct acpi_device *device)
acpi_status status = AE_OK;
union acpi_ec *ec = NULL;
ec = acpi_driver_data(device);
if (!ec)
/*
* Get I/O port addresses. Convert to GAS format.
*/
status = acpi_walk_resources(ec->common.handle, METHOD_NAME__CRS,
acpi_ec_io_ports, ec);
if (ACPI_FAILURE(status)
|| ec->common.command_addr.register_bit_width == 0) {
printk(KERN_ERR PREFIX "Error getting I/O port addresses\n");
ec->common.status_addr = ec->common.command_addr;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02x, ports=0x%2x,0x%2x\n",
(u32) ec->common.gpe_bit,
(u32) ec->common.command_addr.address,
(u32) ec->common.data_addr.address));
status = acpi_install_gpe_handler(NULL, ec->common.gpe_bit,
ACPI_GPE_EDGE_TRIGGERED,
&acpi_ec_gpe_handler, ec);
acpi_set_gpe_type(NULL, ec->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME);
acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
status = acpi_install_address_space_handler(ec->common.handle,
ACPI_ADR_SPACE_EC,
&acpi_ec_space_handler,
&acpi_ec_space_setup, ec);
acpi_remove_gpe_handler(NULL, ec->common.gpe_bit,
&acpi_ec_gpe_handler);
static int acpi_ec_stop(struct acpi_device *device, int type)
acpi_status status = AE_OK;
union acpi_ec *ec = NULL;
status = acpi_remove_address_space_handler(ec->common.handle,
ACPI_ADR_SPACE_EC,
&acpi_ec_space_handler);
status =
acpi_remove_gpe_handler(NULL, ec->common.gpe_bit,
&acpi_ec_gpe_handler);
acpi_fake_ecdt_callback(acpi_handle handle,
u32 Level, void *context, void **retval)
if (acpi_ec_poll_mode)
return acpi_fake_ecdt_poll_callback(handle,
return acpi_fake_ecdt_intr_callback(handle,
acpi_fake_ecdt_poll_callback(acpi_handle handle,
status = acpi_walk_resources(handle, METHOD_NAME__CRS,
if (ACPI_FAILURE(status))
return status;
ec_ecdt->common.status_addr = ec_ecdt->common.command_addr;
ec_ecdt->common.uid = -1;
acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid);
status =
acpi_evaluate_integer(handle, "_GPE", NULL,
&ec_ecdt->common.gpe_bit);
if (ACPI_FAILURE(status))
return status;
init_MUTEX(&ec_ecdt->poll.sem);
ec_ecdt->common.global_lock = TRUE;
ec_ecdt->common.handle = handle;
printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n",
(u32) ec_ecdt->common.gpe_bit,
(u32) ec_ecdt->common.command_addr.address,
(u32) ec_ecdt->common.data_addr.address);
return AE_CTRL_TERMINATE;
}
static acpi_status __init
acpi_fake_ecdt_intr_callback(acpi_handle handle,
init_MUTEX(&ec_ecdt->intr.sem);
init_waitqueue_head(&ec_ecdt->intr.wait);
status = acpi_walk_resources(handle, METHOD_NAME__CRS,
ec_ecdt->common.status_addr = ec_ecdt->common.command_addr;
ec_ecdt->common.uid = -1;
acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid);
status =
acpi_evaluate_integer(handle, "_GPE", NULL,
&ec_ecdt->common.gpe_bit);
ec_ecdt->common.global_lock = TRUE;
ec_ecdt->common.handle = handle;
printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n",
(u32) ec_ecdt->common.gpe_bit,
(u32) ec_ecdt->common.command_addr.address,
(u32) ec_ecdt->common.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.
*/
printk(KERN_INFO PREFIX "Try to make an fake ECDT\n");
ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
if (!ec_ecdt) {
ret = -ENOMEM;
goto error;
}
memset(ec_ecdt, 0, sizeof(union 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;
printk(KERN_ERR PREFIX "Can't make an fake ECDT\n");
return ret;
}
static int __init acpi_ec_get_real_ecdt(void)
if (acpi_ec_poll_mode)
return acpi_ec_poll_get_real_ecdt();
return acpi_ec_intr_get_real_ecdt();
static int __init acpi_ec_poll_get_real_ecdt(void)
acpi_status status;
struct acpi_table_ecdt *ecdt_ptr;
status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING,
(struct acpi_table_header **)
&ecdt_ptr);
if (ACPI_FAILURE(status))
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(union acpi_ec), GFP_KERNEL);
if (!ec_ecdt)
return -ENOMEM;
memset(ec_ecdt, 0, sizeof(union acpi_ec));
ec_ecdt->common.command_addr = ecdt_ptr->ec_control;
ec_ecdt->common.status_addr = ecdt_ptr->ec_control;
ec_ecdt->common.data_addr = ecdt_ptr->ec_data;
ec_ecdt->common.gpe_bit = ecdt_ptr->gpe_bit;
init_MUTEX(&ec_ecdt->poll.sem);
/* use the GL just to be safe */
ec_ecdt->common.global_lock = TRUE;
ec_ecdt->common.uid = ecdt_ptr->uid;
status =
acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle);
if (ACPI_FAILURE(status)) {
goto error;
}
return 0;
printk(KERN_ERR PREFIX "Could not use ECDT\n");
kfree(ec_ecdt);
ec_ecdt = NULL;
return -ENODEV;
}
static int __init acpi_ec_intr_get_real_ecdt(void)
acpi_status status;
struct acpi_table_ecdt *ecdt_ptr;
status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING,
(struct acpi_table_header **)
&ecdt_ptr);
if (ACPI_FAILURE(status))
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(union acpi_ec), GFP_KERNEL);
memset(ec_ecdt, 0, sizeof(union acpi_ec));
init_MUTEX(&ec_ecdt->intr.sem);
init_waitqueue_head(&ec_ecdt->intr.wait);
ec_ecdt->common.command_addr = ecdt_ptr->ec_control;
ec_ecdt->common.status_addr = ecdt_ptr->ec_control;
ec_ecdt->common.data_addr = ecdt_ptr->ec_data;
ec_ecdt->common.gpe_bit = ecdt_ptr->gpe_bit;
ec_ecdt->common.global_lock = TRUE;
ec_ecdt->common.uid = ecdt_ptr->uid;
status =
acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle);
if (ACPI_FAILURE(status)) {
goto error;
}
return 0;
printk(KERN_ERR PREFIX "Could not use ECDT\n");
kfree(ec_ecdt);
ec_ecdt = NULL;
return -ENODEV;
}
static int __initdata acpi_fake_ecdt_enabled;
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
*/
status = acpi_install_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
ACPI_GPE_EDGE_TRIGGERED,
&acpi_ec_gpe_handler, ec_ecdt);
acpi_set_gpe_type(NULL, ec_ecdt->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME);
acpi_enable_gpe(NULL, ec_ecdt->common.gpe_bit, ACPI_NOT_ISR);
status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_EC,
&acpi_ec_space_handler,
&acpi_ec_space_setup,
ec_ecdt);
acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
printk(KERN_ERR PREFIX "Could not use ECDT\n");
kfree(ec_ecdt);
ec_ecdt = NULL;
return -ENODEV;
}
acpi_ec_dir = proc_mkdir(ACPI_EC_CLASS, acpi_root_dir);
if (!acpi_ec_dir)
/* Now register the driver for the EC */
result = acpi_bus_register_driver(&acpi_ec_driver);
if (result < 0) {
remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);
}
subsys_initcall(acpi_ec_init);
/* EC driver currently not unloadable */
#if 0
{
acpi_bus_unregister_driver(&acpi_ec_driver);
remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);
static int __init acpi_fake_ecdt_setup(char *str)
{
acpi_fake_ecdt_enabled = 1;
static int __init acpi_ec_set_intr_mode(char *str)
if (!get_option(&str, &intr))
return 0;
if (intr) {
acpi_ec_poll_mode = EC_INTR;
acpi_ec_driver.ops.add = acpi_ec_intr_add;
} else {
acpi_ec_poll_mode = EC_POLL;
acpi_ec_driver.ops.add = acpi_ec_poll_add;
}
printk(KERN_INFO PREFIX "EC %s mode.\n", intr ? "interrupt" : "polling");
__setup("ec_intr=", acpi_ec_set_intr_mode);