Skip to content
ec.c 35.3 KiB
Newer Older
	union acpi_ec *ec = (union acpi_ec *)context;
Linus Torvalds's avatar
Linus Torvalds committed
	struct acpi_generic_address *addr;

Bob Moore's avatar
Bob Moore committed
	if (resource->type != ACPI_RESOURCE_TYPE_IO) {
Linus Torvalds's avatar
Linus Torvalds committed
		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;
Linus Torvalds's avatar
Linus Torvalds committed
	} else {
		return AE_CTRL_TERMINATE;
	}

	addr->address_space_id = ACPI_ADR_SPACE_SYSTEM_IO;
	addr->register_bit_width = 8;
	addr->register_bit_offset = 0;
Bob Moore's avatar
Bob Moore committed
	addr->address = resource->data.io.minimum;
Linus Torvalds's avatar
Linus Torvalds committed

	return AE_OK;
}

static int acpi_ec_start(struct acpi_device *device)
Linus Torvalds's avatar
Linus Torvalds committed
{
	acpi_status status = AE_OK;
	union acpi_ec *ec = NULL;
Linus Torvalds's avatar
Linus Torvalds committed


	if (!device)
Linus Torvalds's avatar
Linus Torvalds committed

	ec = acpi_driver_data(device);

	if (!ec)
Linus Torvalds's avatar
Linus Torvalds committed

	/*
	 * 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;
Linus Torvalds's avatar
Linus Torvalds committed

	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));
Linus Torvalds's avatar
Linus Torvalds committed

	/*
	 * Install GPE handler
	 */
	status = acpi_install_gpe_handler(NULL, ec->common.gpe_bit,
					  ACPI_GPE_EDGE_TRIGGERED,
					  &acpi_ec_gpe_handler, ec);
Linus Torvalds's avatar
Linus Torvalds committed
	if (ACPI_FAILURE(status)) {
Linus Torvalds's avatar
Linus Torvalds committed
	}
	acpi_set_gpe_type(NULL, ec->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME);
	acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds's avatar
Linus Torvalds committed

	status = acpi_install_address_space_handler(ec->common.handle,
						    ACPI_ADR_SPACE_EC,
						    &acpi_ec_space_handler,
						    &acpi_ec_space_setup, ec);
Linus Torvalds's avatar
Linus Torvalds committed
	if (ACPI_FAILURE(status)) {
		acpi_remove_gpe_handler(NULL, ec->common.gpe_bit,
					&acpi_ec_gpe_handler);
static int acpi_ec_stop(struct acpi_device *device, int type)
Linus Torvalds's avatar
Linus Torvalds committed
{
	acpi_status status = AE_OK;
	union acpi_ec *ec = NULL;
Linus Torvalds's avatar
Linus Torvalds committed


	if (!device)
Linus Torvalds's avatar
Linus Torvalds committed

	ec = acpi_driver_data(device);

	status = acpi_remove_address_space_handler(ec->common.handle,
						   ACPI_ADR_SPACE_EC,
						   &acpi_ec_space_handler);
Linus Torvalds's avatar
Linus Torvalds committed
	if (ACPI_FAILURE(status))
Linus Torvalds's avatar
Linus Torvalds committed

	status =
	    acpi_remove_gpe_handler(NULL, ec->common.gpe_bit,
				    &acpi_ec_gpe_handler);
Linus Torvalds's avatar
Linus Torvalds committed
	if (ACPI_FAILURE(status))
Linus Torvalds's avatar
Linus Torvalds committed

Linus Torvalds's avatar
Linus Torvalds committed
}

static acpi_status __init
acpi_fake_ecdt_callback(acpi_handle handle,
			u32 Level, void *context, void **retval)
Linus Torvalds's avatar
Linus Torvalds committed
{
	if (acpi_ec_poll_mode)
		return acpi_fake_ecdt_poll_callback(handle,
						       Level, context, retval);
		return acpi_fake_ecdt_intr_callback(handle,
						     Level, context, retval);
}

static acpi_status __init
acpi_fake_ecdt_poll_callback(acpi_handle handle,
				u32 Level, void *context, void **retval)
	acpi_status status;

	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
				     acpi_ec_io_ports, ec_ecdt);
	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,
			      u32 Level, void *context, void **retval)
	acpi_status status;
Linus Torvalds's avatar
Linus Torvalds committed

	init_MUTEX(&ec_ecdt->intr.sem);
	init_waitqueue_head(&ec_ecdt->intr.wait);
Linus Torvalds's avatar
Linus Torvalds committed
	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
				     acpi_ec_io_ports, ec_ecdt);
Linus Torvalds's avatar
Linus Torvalds committed
	if (ACPI_FAILURE(status))
		return status;
	ec_ecdt->common.status_addr = ec_ecdt->common.command_addr;
Linus Torvalds's avatar
Linus Torvalds committed

	ec_ecdt->common.uid = -1;
	acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid);
Linus Torvalds's avatar
Linus Torvalds committed

	status =
	    acpi_evaluate_integer(handle, "_GPE", NULL,
				  &ec_ecdt->common.gpe_bit);
Linus Torvalds's avatar
Linus Torvalds committed
	if (ACPI_FAILURE(status))
		return status;
	ec_ecdt->common.global_lock = TRUE;
	ec_ecdt->common.handle = handle;
Linus Torvalds's avatar
Linus Torvalds committed

	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);
Linus Torvalds's avatar
Linus Torvalds committed

	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)
Linus Torvalds's avatar
Linus Torvalds committed
{
	acpi_status status;
	int ret = 0;
Linus Torvalds's avatar
Linus Torvalds committed

	printk(KERN_INFO PREFIX "Try to make an fake ECDT\n");

	ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
	if (!ec_ecdt) {
		ret = -ENOMEM;
		goto error;
	}
	memset(ec_ecdt, 0, sizeof(union acpi_ec));
Linus Torvalds's avatar
Linus Torvalds committed

	status = acpi_get_devices(ACPI_EC_HID,
				  acpi_fake_ecdt_callback, NULL, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
	if (ACPI_FAILURE(status)) {
		kfree(ec_ecdt);
		ec_ecdt = NULL;
		ret = -ENODEV;
		goto error;
	}
	return 0;
      error:
Linus Torvalds's avatar
Linus Torvalds committed
	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;
      error:
	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)
Linus Torvalds's avatar
Linus Torvalds committed
{
	acpi_status status;
	struct acpi_table_ecdt *ecdt_ptr;
Linus Torvalds's avatar
Linus Torvalds committed

	status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING,
					 (struct acpi_table_header **)
					 &ecdt_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
	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);
Linus Torvalds's avatar
Linus Torvalds committed
	if (!ec_ecdt)
		return -ENOMEM;
	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;
Linus Torvalds's avatar
Linus Torvalds committed
	/* use the GL just to be safe */
	ec_ecdt->common.global_lock = TRUE;
	ec_ecdt->common.uid = ecdt_ptr->uid;
Linus Torvalds's avatar
Linus Torvalds committed

	status =
	    acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle);
Linus Torvalds's avatar
Linus Torvalds committed
	if (ACPI_FAILURE(status)) {
		goto error;
	}

	return 0;
      error:
Linus Torvalds's avatar
Linus Torvalds committed
	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)
Linus Torvalds's avatar
Linus Torvalds committed
{
	acpi_status status;
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed

	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);
Linus Torvalds's avatar
Linus Torvalds committed
	if (ACPI_FAILURE(status)) {
		goto error;
	}
	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);
Linus Torvalds's avatar
Linus Torvalds committed
	if (ACPI_FAILURE(status)) {
		acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
					&acpi_ec_gpe_handler);
Linus Torvalds's avatar
Linus Torvalds committed
		goto error;
	}

	return 0;

      error:
Linus Torvalds's avatar
Linus Torvalds committed
	printk(KERN_ERR PREFIX "Could not use ECDT\n");
	kfree(ec_ecdt);
	ec_ecdt = NULL;

	return -ENODEV;
}

static int __init acpi_ec_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
{
	int result = 0;
Linus Torvalds's avatar
Linus Torvalds committed


	if (acpi_disabled)
Linus Torvalds's avatar
Linus Torvalds committed

	acpi_ec_dir = proc_mkdir(ACPI_EC_CLASS, acpi_root_dir);
	if (!acpi_ec_dir)
Linus Torvalds's avatar
Linus Torvalds committed

	/* 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);
Linus Torvalds's avatar
Linus Torvalds committed
}

subsys_initcall(acpi_ec_init);

/* EC driver currently not unloadable */
#if 0
static void __exit acpi_ec_exit(void)
Linus Torvalds's avatar
Linus Torvalds committed
{

	acpi_bus_unregister_driver(&acpi_ec_driver);

	remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);

Linus Torvalds's avatar
Linus Torvalds committed
}
#endif				/* 0 */
Linus Torvalds's avatar
Linus Torvalds committed

static int __init acpi_fake_ecdt_setup(char *str)
{
	acpi_fake_ecdt_enabled = 1;
Linus Torvalds's avatar
Linus Torvalds committed
}
Linus Torvalds's avatar
Linus Torvalds committed
__setup("acpi_fake_ecdt", acpi_fake_ecdt_setup);
static int __init acpi_ec_set_intr_mode(char *str)
	if (!get_option(&str, &intr))
	if (intr) {
		acpi_ec_poll_mode = EC_INTR;
		acpi_ec_driver.ops.add = acpi_ec_intr_add;
		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);