diff options
Diffstat (limited to 'drivers/platform/x86/xo15-ebook.c')
| -rw-r--r-- | drivers/platform/x86/xo15-ebook.c | 172 | 
1 files changed, 172 insertions, 0 deletions
diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c new file mode 100644 index 00000000000..49cbccec6e2 --- /dev/null +++ b/drivers/platform/x86/xo15-ebook.c @@ -0,0 +1,172 @@ +/* + *  OLPC XO-1.5 ebook switch driver + *  (based on generic ACPI button driver) + * + *  Copyright (C) 2009 Paul Fox <pgf@laptop.org> + *  Copyright (C) 2010 One Laptop per Child + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or (at + *  your option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/input.h> +#include <linux/acpi.h> + +#define MODULE_NAME "xo15-ebook" + +#define XO15_EBOOK_CLASS		MODULE_NAME +#define XO15_EBOOK_TYPE_UNKNOWN	0x00 +#define XO15_EBOOK_NOTIFY_STATUS	0x80 + +#define XO15_EBOOK_SUBCLASS		"ebook" +#define XO15_EBOOK_HID			"XO15EBK" +#define XO15_EBOOK_DEVICE_NAME		"EBook Switch" + +ACPI_MODULE_NAME(MODULE_NAME); + +MODULE_DESCRIPTION("OLPC XO-1.5 ebook switch driver"); +MODULE_LICENSE("GPL"); + +static const struct acpi_device_id ebook_device_ids[] = { +	{ XO15_EBOOK_HID, 0 }, +	{ "", 0 }, +}; +MODULE_DEVICE_TABLE(acpi, ebook_device_ids); + +struct ebook_switch { +	struct input_dev *input; +	char phys[32];			/* for input device */ +}; + +static int ebook_send_state(struct acpi_device *device) +{ +	struct ebook_switch *button = acpi_driver_data(device); +	unsigned long long state; +	acpi_status status; + +	status = acpi_evaluate_integer(device->handle, "EBK", NULL, &state); +	if (ACPI_FAILURE(status)) +		return -EIO; + +	/* input layer checks if event is redundant */ +	input_report_switch(button->input, SW_TABLET_MODE, !state); +	input_sync(button->input); +	return 0; +} + +static void ebook_switch_notify(struct acpi_device *device, u32 event) +{ +	switch (event) { +	case ACPI_FIXED_HARDWARE_EVENT: +	case XO15_EBOOK_NOTIFY_STATUS: +		ebook_send_state(device); +		break; +	default: +		ACPI_DEBUG_PRINT((ACPI_DB_INFO, +				  "Unsupported event [0x%x]\n", event)); +		break; +	} +} + +#ifdef CONFIG_PM_SLEEP +static int ebook_switch_resume(struct device *dev) +{ +	return ebook_send_state(to_acpi_device(dev)); +} +#endif + +static SIMPLE_DEV_PM_OPS(ebook_switch_pm, NULL, ebook_switch_resume); + +static int ebook_switch_add(struct acpi_device *device) +{ +	struct ebook_switch *button; +	struct input_dev *input; +	const char *hid = acpi_device_hid(device); +	char *name, *class; +	int error; + +	button = kzalloc(sizeof(struct ebook_switch), GFP_KERNEL); +	if (!button) +		return -ENOMEM; + +	device->driver_data = button; + +	button->input = input = input_allocate_device(); +	if (!input) { +		error = -ENOMEM; +		goto err_free_button; +	} + +	name = acpi_device_name(device); +	class = acpi_device_class(device); + +	if (strcmp(hid, XO15_EBOOK_HID)) { +		pr_err("Unsupported hid [%s]\n", hid); +		error = -ENODEV; +		goto err_free_input; +	} + +	strcpy(name, XO15_EBOOK_DEVICE_NAME); +	sprintf(class, "%s/%s", XO15_EBOOK_CLASS, XO15_EBOOK_SUBCLASS); + +	snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid); + +	input->name = name; +	input->phys = button->phys; +	input->id.bustype = BUS_HOST; +	input->dev.parent = &device->dev; + +	input->evbit[0] = BIT_MASK(EV_SW); +	set_bit(SW_TABLET_MODE, input->swbit); + +	error = input_register_device(input); +	if (error) +		goto err_free_input; + +	ebook_send_state(device); + +	if (device->wakeup.flags.valid) { +		/* Button's GPE is run-wake GPE */ +		acpi_enable_gpe(device->wakeup.gpe_device, +				device->wakeup.gpe_number); +		device_set_wakeup_enable(&device->dev, true); +	} + +	return 0; + + err_free_input: +	input_free_device(input); + err_free_button: +	kfree(button); +	return error; +} + +static int ebook_switch_remove(struct acpi_device *device) +{ +	struct ebook_switch *button = acpi_driver_data(device); + +	input_unregister_device(button->input); +	kfree(button); +	return 0; +} + +static struct acpi_driver xo15_ebook_driver = { +	.name = MODULE_NAME, +	.class = XO15_EBOOK_CLASS, +	.ids = ebook_device_ids, +	.ops = { +		.add = ebook_switch_add, +		.remove = ebook_switch_remove, +		.notify = ebook_switch_notify, +	}, +	.drv.pm = &ebook_switch_pm, +}; +module_acpi_driver(xo15_ebook_driver);  | 
