Newer
Older
acpi_fake_ecdt_callback(acpi_handle handle,
u32 Level, void *context, void **retval)
init_MUTEX(&ec_ecdt->sem);
if (acpi_ec_mode == EC_INTR) {
init_waitqueue_head(&ec_ecdt->wait);
}
status = acpi_walk_resources(handle, METHOD_NAME__CRS,
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);
ec_ecdt->global_lock = TRUE;
ec_ecdt->handle = handle;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02x, ports=0x%2x, 0x%2x",
(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.
*/
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Try to make an fake ECDT"));
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;
ACPI_EXCEPTION((AE_INFO, status, "Can't make an fake ECDT"));
static int __init acpi_ec_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;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT"));
/*
* Generate a temporary ec context to use until the namespace is scanned
*/
ec_ecdt = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL);
memset(ec_ecdt, 0, sizeof(struct acpi_ec));
init_MUTEX(&ec_ecdt->sem);
if (acpi_ec_mode == EC_INTR) {
init_waitqueue_head(&ec_ecdt->wait);
ec_ecdt->command_addr = ecdt_ptr->ec_control;
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->global_lock = TRUE;
ec_ecdt->uid = ecdt_ptr->uid;
acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->handle);
if (ACPI_FAILURE(status)) {
goto error;
}
return 0;
error:
ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT"));
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->gpe_bit,
ACPI_GPE_EDGE_TRIGGERED,
&acpi_ec_gpe_handler, ec_ecdt);
acpi_set_gpe_type(NULL, ec_ecdt->gpe_bit, ACPI_GPE_TYPE_RUNTIME);
acpi_enable_gpe(NULL, ec_ecdt->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->gpe_bit,
ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT"));
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;
acpi_ec_mode = EC_INTR;
} else {
acpi_ec_mode = EC_POLL;
}
acpi_ec_driver.ops.add = acpi_ec_add;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "EC %s mode.\n", intr ? "interrupt" : "polling"));
__setup("ec_intr=", acpi_ec_set_intr_mode);