Skip to content
ec.c 26.7 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
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;

	status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING,
Linus Torvalds's avatar
Linus Torvalds committed
		(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(struct acpi_ec), GFP_KERNEL);
	if (!ec_ecdt)
		return -ENOMEM;
	memset(ec_ecdt, 0, sizeof(struct acpi_ec));

 	init_MUTEX(&ec_ecdt->sem);
 	init_waitqueue_head(&ec_ecdt->wait);
Linus Torvalds's avatar
Linus Torvalds committed
	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;
	/* use the GL just to be safe */
	ec_ecdt->global_lock = TRUE;
	ec_ecdt->uid = ecdt_ptr->uid;

	status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->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 __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
	 */
	status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe_bit,
		ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler,
		ec_ecdt);
	if (ACPI_FAILURE(status)) {
		goto error;
	}
	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);
	if (ACPI_FAILURE(status)) {
		acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit,
			&acpi_ec_gpe_handler);
		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_init (void)
{
	int			result;
Linus Torvalds's avatar
Linus Torvalds committed

	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
static void __exit
acpi_ec_exit (void)
{
	ACPI_FUNCTION_TRACE("acpi_ec_exit");

	acpi_bus_unregister_driver(&acpi_ec_driver);

	remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);

	return_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);