Newer
Older
if (!device)
return_VALUE(-EINVAL);
ec = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
if (!ec)
return_VALUE(-ENOMEM);
memset(ec, 0, sizeof(union acpi_ec));
ec->common.handle = device->handle;
ec->common.uid = -1;
init_MUTEX(&ec->poll.sem);
strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_EC_CLASS);
acpi_driver_data(device) = ec;
/* Use the global lock for all EC transactions? */
acpi_evaluate_integer(ec->common.handle, "_GLK", NULL,
&ec->common.global_lock);
/* XXX we don't test uids, because on some boxes ecdt uid = 0, see:
http://bugzilla.kernel.org/show_bug.cgi?id=6111 */
if (ec_ecdt) {
acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_EC,
&acpi_ec_space_handler);
acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
&acpi_ec_gpe_handler);
kfree(ec_ecdt);
}
/* Get GPE bit assignment (EC events). */
/* TODO: Add support for _GPE returning a package */
status =
acpi_evaluate_integer(ec->common.handle, "_GPE", NULL,
&ec->common.gpe_bit);
ACPI_EXCEPTION((AE_INFO, status, "Obtaining GPE bit"));
result = -ENODEV;
goto end;
}
result = acpi_ec_add_fs(device);
if (result)
goto end;
printk(KERN_INFO PREFIX "%s [%s] (gpe %d) polling mode.\n",
acpi_device_name(device), acpi_device_bid(device),
(u32) ec->common.gpe_bit);
if (!first_ec)
first_ec = device;
if (result)
kfree(ec);
return_VALUE(result);
}
static int acpi_ec_intr_add(struct acpi_device *device)
int result = 0;
acpi_status status = AE_OK;
union acpi_ec *ec = NULL;
ACPI_FUNCTION_TRACE("acpi_ec_add");
if (!device)
return_VALUE(-EINVAL);
ec = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
memset(ec, 0, sizeof(union acpi_ec));
ec->common.handle = device->handle;
ec->common.uid = -1;
atomic_set(&ec->intr.pending_gpe, 0);
atomic_set(&ec->intr.leaving_burst, 1);
init_MUTEX(&ec->intr.sem);
init_waitqueue_head(&ec->intr.wait);
strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_EC_CLASS);
acpi_driver_data(device) = ec;
/* Use the global lock for all EC transactions? */
acpi_evaluate_integer(ec->common.handle, "_GLK", NULL,
&ec->common.global_lock);
/* XXX we don't test uids, because on some boxes ecdt uid = 0, see:
http://bugzilla.kernel.org/show_bug.cgi?id=6111 */
if (ec_ecdt) {
ACPI_ADR_SPACE_EC,
&acpi_ec_space_handler);
acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
&acpi_ec_gpe_handler);
kfree(ec_ecdt);
}
/* Get GPE bit assignment (EC events). */
/* TODO: Add support for _GPE returning a package */
status =
acpi_evaluate_integer(ec->common.handle, "_GPE", NULL,
&ec->common.gpe_bit);
printk(KERN_ERR PREFIX "Obtaining GPE bit assignment\n");
result = -ENODEV;
goto end;
}
result = acpi_ec_add_fs(device);
if (result)
goto end;
printk(KERN_INFO PREFIX "%s [%s] (gpe %d) interrupt mode.\n",
acpi_device_name(device), acpi_device_bid(device),
(u32) ec->common.gpe_bit);
if (result)
kfree(ec);
return_VALUE(result);
}
static int acpi_ec_remove(struct acpi_device *device, int type)
ACPI_FUNCTION_TRACE("acpi_ec_remove");
if (!device)
return_VALUE(-EINVAL);
ec = acpi_driver_data(device);
acpi_ec_remove_fs(device);
kfree(ec);
return_VALUE(0);
}
static acpi_status
acpi_ec_io_ports(struct acpi_resource *resource, void *context)
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;
ACPI_FUNCTION_TRACE("acpi_ec_start");
if (!device)
return_VALUE(-EINVAL);
ec = acpi_driver_data(device);
if (!ec)
return_VALUE(-EINVAL);
/*
* 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);
if (ACPI_FAILURE(status)) {
return_VALUE(-ENODEV);
}
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);
return_VALUE(-ENODEV);
}
return_VALUE(AE_OK);
}
static int acpi_ec_stop(struct acpi_device *device, int type)
acpi_status status = AE_OK;
union acpi_ec *ec = NULL;
ACPI_FUNCTION_TRACE("acpi_ec_stop");
if (!device)
return_VALUE(-EINVAL);
ec = acpi_driver_data(device);
status = acpi_remove_address_space_handler(ec->common.handle,
ACPI_ADR_SPACE_EC,
&acpi_ec_space_handler);
if (ACPI_FAILURE(status))
return_VALUE(-ENODEV);
status =
acpi_remove_gpe_handler(NULL, ec->common.gpe_bit,
&acpi_ec_gpe_handler);
if (ACPI_FAILURE(status))
return_VALUE(-ENODEV);
return_VALUE(0);
}
static acpi_status __init
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;
}
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
ACPI_FUNCTION_TRACE("acpi_ec_init");
if (acpi_disabled)
return_VALUE(0);
acpi_ec_dir = proc_mkdir(ACPI_EC_CLASS, acpi_root_dir);
if (!acpi_ec_dir)
return_VALUE(-ENODEV);
/* 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);
return_VALUE(-ENODEV);
}
return_VALUE(result);
}
subsys_initcall(acpi_ec_init);
/* EC driver currently not unloadable */
#if 0
{
ACPI_FUNCTION_TRACE("acpi_ec_exit");
acpi_bus_unregister_driver(&acpi_ec_driver);
remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);
return_VOID;
}
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);