File-copy from v4.4.100

This is the result of 'cp' from a linux-stable tree with the 'v4.4.100'
tag checked out (commit 26d6298789e695c9f627ce49a7bbd2286405798a) on
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

Please refer to that tree for all history prior to this point.

Change-Id: I8a9ee2aea93cd29c52c847d0ce33091a73ae6afe
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
new file mode 100644
index 0000000..a35532e
--- /dev/null
+++ b/drivers/input/Kconfig
@@ -0,0 +1,215 @@
+#
+# Input device configuration
+#
+
+menu "Input device support"
+	depends on !UML
+
+config INPUT
+	tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT
+	default y
+	help
+	  Say Y here if you have any input device (mouse, keyboard, tablet,
+	  joystick, steering wheel ...) connected to your system and want
+	  it to be available to applications. This includes standard PS/2
+	  keyboard and mouse.
+
+	  Say N here if you have a headless (no monitor, no keyboard) system.
+
+	  More information is available: <file:Documentation/input/input.txt>
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called input.
+
+if INPUT
+
+config INPUT_LEDS
+	tristate "Export input device LEDs in sysfs"
+	depends on LEDS_CLASS
+	default INPUT
+	help
+	  Say Y here if you would like to export LEDs on input devices
+	  as standard LED class devices in sysfs.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called input-leds.
+
+config INPUT_FF_MEMLESS
+	tristate "Support for memoryless force-feedback devices"
+	help
+	  Say Y here if you have memoryless force-feedback input device
+	  such as Logitech WingMan Force 3D, ThrustMaster FireStorm Dual
+	  Power 2, or similar. You will also need to enable hardware-specific
+	  driver.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ff-memless.
+
+config INPUT_POLLDEV
+	tristate "Polled input device skeleton"
+	help
+	  Say Y here if you are using a driver for an input
+	  device that periodically polls hardware state. This
+	  option is only useful for out-of-tree drivers since
+	  in-tree drivers select it automatically.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called input-polldev.
+
+config INPUT_SPARSEKMAP
+	tristate "Sparse keymap support library"
+	help
+	  Say Y here if you are using a driver for an input
+	  device that uses sparse keymap. This option is only
+	  useful for out-of-tree drivers since in-tree drivers
+	  select it automatically.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sparse-keymap.
+
+config INPUT_MATRIXKMAP
+	tristate "Matrix keymap support library"
+	help
+	  Say Y here if you are using a driver for an input
+	  device that uses matrix keymap. This option is only
+	  useful for out-of-tree drivers since in-tree drivers
+	  select it automatically.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called matrix-keymap.
+
+comment "Userland interfaces"
+
+config INPUT_MOUSEDEV
+	tristate "Mouse interface"
+	default y
+	help
+	  Say Y here if you want your mouse to be accessible as char devices
+	  13:32+ - /dev/input/mouseX and 13:63 - /dev/input/mice as an
+	  emulated IntelliMouse Explorer PS/2 mouse. That way, all user space
+	  programs (including SVGAlib, GPM and X) will be able to use your
+	  mouse.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mousedev.
+
+config INPUT_MOUSEDEV_PSAUX
+	bool "Provide legacy /dev/psaux device"
+	default y
+	depends on INPUT_MOUSEDEV
+	help
+	  Say Y here if you want your mouse also be accessible as char device
+	  10:1 - /dev/psaux. The data available through /dev/psaux is exactly
+	  the same as the data from /dev/input/mice.
+
+	  If unsure, say Y.
+
+
+config INPUT_MOUSEDEV_SCREEN_X
+	int "Horizontal screen resolution"
+	depends on INPUT_MOUSEDEV
+	default "1024"
+	help
+	  If you're using a digitizer, or a graphic tablet, and want to use
+	  it as a mouse then the mousedev driver needs to know the X window
+	  screen resolution you are using to correctly scale the data. If
+	  you're not using a digitizer, this value is ignored.
+
+config INPUT_MOUSEDEV_SCREEN_Y
+	int "Vertical screen resolution"
+	depends on INPUT_MOUSEDEV
+	default "768"
+	help
+	  If you're using a digitizer, or a graphic tablet, and want to use
+	  it as a mouse then the mousedev driver needs to know the X window
+	  screen resolution you are using to correctly scale the data. If
+	  you're not using a digitizer, this value is ignored.
+
+config INPUT_JOYDEV
+	tristate "Joystick interface"
+	help
+	  Say Y here if you want your joystick or gamepad to be
+	  accessible as char device 13:0+ - /dev/input/jsX device.
+
+	  If unsure, say Y.
+
+	  More information is available: <file:Documentation/input/joystick.txt>
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called joydev.
+
+config INPUT_EVDEV
+	tristate "Event interface"
+	help
+	  Say Y here if you want your input device events be accessible
+	  under char device 13:64+ - /dev/input/eventX in a generic way.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called evdev.
+
+config INPUT_EVBUG
+	tristate "Event debugging"
+	help
+	  Say Y here if you have a problem with the input subsystem and
+	  want all events (keypresses, mouse movements), to be output to
+	  the system log. While this is useful for debugging, it's also
+	  a security threat - your keypresses include your passwords, of
+	  course.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called evbug.
+
+config INPUT_APMPOWER
+	tristate "Input Power Event -> APM Bridge" if EXPERT
+	depends on INPUT && APM_EMULATION
+	help
+	  Say Y here if you want suspend key events to trigger a user
+	  requested suspend through APM. This is useful on embedded
+	  systems where such behaviour is desired without userspace
+	  interaction. If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called apm-power.
+
+comment "Input Device Drivers"
+
+source "drivers/input/keyboard/Kconfig"
+
+source "drivers/input/mouse/Kconfig"
+
+source "drivers/input/joystick/Kconfig"
+
+source "drivers/input/tablet/Kconfig"
+
+source "drivers/input/touchscreen/Kconfig"
+
+source "drivers/input/misc/Kconfig"
+
+endif
+
+menu "Hardware I/O ports"
+
+source "drivers/input/serio/Kconfig"
+
+source "drivers/input/gameport/Kconfig"
+
+endmenu
+
+endmenu
+
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
new file mode 100644
index 0000000..0c9302c
--- /dev/null
+++ b/drivers/input/Makefile
@@ -0,0 +1,28 @@
+#
+# Makefile for the input core drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_INPUT)		+= input-core.o
+input-core-y := input.o input-compat.o input-mt.o ff-core.o
+
+obj-$(CONFIG_INPUT_FF_MEMLESS)	+= ff-memless.o
+obj-$(CONFIG_INPUT_POLLDEV)	+= input-polldev.o
+obj-$(CONFIG_INPUT_SPARSEKMAP)	+= sparse-keymap.o
+obj-$(CONFIG_INPUT_MATRIXKMAP)	+= matrix-keymap.o
+
+obj-$(CONFIG_INPUT_LEDS)	+= input-leds.o
+obj-$(CONFIG_INPUT_MOUSEDEV)	+= mousedev.o
+obj-$(CONFIG_INPUT_JOYDEV)	+= joydev.o
+obj-$(CONFIG_INPUT_EVDEV)	+= evdev.o
+obj-$(CONFIG_INPUT_EVBUG)	+= evbug.o
+
+obj-$(CONFIG_INPUT_KEYBOARD)	+= keyboard/
+obj-$(CONFIG_INPUT_MOUSE)	+= mouse/
+obj-$(CONFIG_INPUT_JOYSTICK)	+= joystick/
+obj-$(CONFIG_INPUT_TABLET)	+= tablet/
+obj-$(CONFIG_INPUT_TOUCHSCREEN)	+= touchscreen/
+obj-$(CONFIG_INPUT_MISC)	+= misc/
+
+obj-$(CONFIG_INPUT_APMPOWER)	+= apm-power.o
diff --git a/drivers/input/apm-power.c b/drivers/input/apm-power.c
new file mode 100644
index 0000000..650177a
--- /dev/null
+++ b/drivers/input/apm-power.c
@@ -0,0 +1,126 @@
+/*
+ *  Input Power Event -> APM Bridge
+ *
+ *  Copyright (c) 2007 Richard Purdie
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/apm-emulation.h>
+
+static void system_power_event(unsigned int keycode)
+{
+	switch (keycode) {
+	case KEY_SUSPEND:
+		apm_queue_event(APM_USER_SUSPEND);
+		pr_info("Requesting system suspend...\n");
+		break;
+	default:
+		break;
+	}
+}
+
+static void apmpower_event(struct input_handle *handle, unsigned int type,
+			   unsigned int code, int value)
+{
+	/* only react on key down events */
+	if (value != 1)
+		return;
+
+	switch (type) {
+	case EV_PWR:
+		system_power_event(code);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static int apmpower_connect(struct input_handler *handler,
+					  struct input_dev *dev,
+					  const struct input_device_id *id)
+{
+	struct input_handle *handle;
+	int error;
+
+	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+	if (!handle)
+		return -ENOMEM;
+
+	handle->dev = dev;
+	handle->handler = handler;
+	handle->name = "apm-power";
+
+	error = input_register_handle(handle);
+	if (error) {
+		pr_err("Failed to register input power handler, error %d\n",
+		       error);
+		kfree(handle);
+		return error;
+	}
+
+	error = input_open_device(handle);
+	if (error) {
+		pr_err("Failed to open input power device, error %d\n", error);
+		input_unregister_handle(handle);
+		kfree(handle);
+		return error;
+	}
+
+	return 0;
+}
+
+static void apmpower_disconnect(struct input_handle *handle)
+{
+	input_close_device(handle);
+	input_unregister_handle(handle);
+	kfree(handle);
+}
+
+static const struct input_device_id apmpower_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+		.evbit = { BIT_MASK(EV_PWR) },
+	},
+	{ },
+};
+
+MODULE_DEVICE_TABLE(input, apmpower_ids);
+
+static struct input_handler apmpower_handler = {
+	.event =	apmpower_event,
+	.connect =	apmpower_connect,
+	.disconnect =	apmpower_disconnect,
+	.name =		"apm-power",
+	.id_table =	apmpower_ids,
+};
+
+static int __init apmpower_init(void)
+{
+	return input_register_handler(&apmpower_handler);
+}
+
+static void __exit apmpower_exit(void)
+{
+	input_unregister_handler(&apmpower_handler);
+}
+
+module_init(apmpower_init);
+module_exit(apmpower_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("Input Power Event -> APM Bridge");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c
new file mode 100644
index 0000000..cd4e667
--- /dev/null
+++ b/drivers/input/evbug.c
@@ -0,0 +1,119 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ *  Input driver event debug module - dumps all events into syslog
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/device.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Input driver event debug module");
+MODULE_LICENSE("GPL");
+
+static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+	printk(KERN_DEBUG pr_fmt("Event. Dev: %s, Type: %d, Code: %d, Value: %d\n"),
+	       dev_name(&handle->dev->dev), type, code, value);
+}
+
+static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
+			 const struct input_device_id *id)
+{
+	struct input_handle *handle;
+	int error;
+
+	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+	if (!handle)
+		return -ENOMEM;
+
+	handle->dev = dev;
+	handle->handler = handler;
+	handle->name = "evbug";
+
+	error = input_register_handle(handle);
+	if (error)
+		goto err_free_handle;
+
+	error = input_open_device(handle);
+	if (error)
+		goto err_unregister_handle;
+
+	printk(KERN_DEBUG pr_fmt("Connected device: %s (%s at %s)\n"),
+	       dev_name(&dev->dev),
+	       dev->name ?: "unknown",
+	       dev->phys ?: "unknown");
+
+	return 0;
+
+ err_unregister_handle:
+	input_unregister_handle(handle);
+ err_free_handle:
+	kfree(handle);
+	return error;
+}
+
+static void evbug_disconnect(struct input_handle *handle)
+{
+	printk(KERN_DEBUG pr_fmt("Disconnected device: %s\n"),
+	       dev_name(&handle->dev->dev));
+
+	input_close_device(handle);
+	input_unregister_handle(handle);
+	kfree(handle);
+}
+
+static const struct input_device_id evbug_ids[] = {
+	{ .driver_info = 1 },	/* Matches all devices */
+	{ },			/* Terminating zero entry */
+};
+
+MODULE_DEVICE_TABLE(input, evbug_ids);
+
+static struct input_handler evbug_handler = {
+	.event =	evbug_event,
+	.connect =	evbug_connect,
+	.disconnect =	evbug_disconnect,
+	.name =		"evbug",
+	.id_table =	evbug_ids,
+};
+
+static int __init evbug_init(void)
+{
+	return input_register_handler(&evbug_handler);
+}
+
+static void __exit evbug_exit(void)
+{
+	input_unregister_handler(&evbug_handler);
+}
+
+module_init(evbug_init);
+module_exit(evbug_exit);
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
new file mode 100644
index 0000000..e9ae3d5
--- /dev/null
+++ b/drivers/input/evdev.c
@@ -0,0 +1,1485 @@
+/*
+ * Event char devices, giving access to raw input device events.
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define EVDEV_MINOR_BASE	64
+#define EVDEV_MINORS		32
+#define EVDEV_MIN_BUFFER_SIZE	64U
+#define EVDEV_BUF_PACKETS	8
+
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input/mt.h>
+#include <linux/major.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include "input-compat.h"
+
+enum evdev_clock_type {
+	EV_CLK_REAL = 0,
+	EV_CLK_MONO,
+	EV_CLK_BOOT,
+	EV_CLK_MAX
+};
+
+struct evdev {
+	int open;
+	struct input_handle handle;
+	wait_queue_head_t wait;
+	struct evdev_client __rcu *grab;
+	struct list_head client_list;
+	spinlock_t client_lock; /* protects client_list */
+	struct mutex mutex;
+	struct device dev;
+	struct cdev cdev;
+	bool exist;
+};
+
+struct evdev_client {
+	unsigned int head;
+	unsigned int tail;
+	unsigned int packet_head; /* [future] position of the first element of next packet */
+	spinlock_t buffer_lock; /* protects access to buffer, head and tail */
+	struct fasync_struct *fasync;
+	struct evdev *evdev;
+	struct list_head node;
+	unsigned int clk_type;
+	bool revoked;
+	unsigned long *evmasks[EV_CNT];
+	unsigned int bufsize;
+	struct input_event buffer[];
+};
+
+static size_t evdev_get_mask_cnt(unsigned int type)
+{
+	static const size_t counts[EV_CNT] = {
+		/* EV_SYN==0 is EV_CNT, _not_ SYN_CNT, see EVIOCGBIT */
+		[EV_SYN]	= EV_CNT,
+		[EV_KEY]	= KEY_CNT,
+		[EV_REL]	= REL_CNT,
+		[EV_ABS]	= ABS_CNT,
+		[EV_MSC]	= MSC_CNT,
+		[EV_SW]		= SW_CNT,
+		[EV_LED]	= LED_CNT,
+		[EV_SND]	= SND_CNT,
+		[EV_FF]		= FF_CNT,
+	};
+
+	return (type < EV_CNT) ? counts[type] : 0;
+}
+
+/* requires the buffer lock to be held */
+static bool __evdev_is_filtered(struct evdev_client *client,
+				unsigned int type,
+				unsigned int code)
+{
+	unsigned long *mask;
+	size_t cnt;
+
+	/* EV_SYN and unknown codes are never filtered */
+	if (type == EV_SYN || type >= EV_CNT)
+		return false;
+
+	/* first test whether the type is filtered */
+	mask = client->evmasks[0];
+	if (mask && !test_bit(type, mask))
+		return true;
+
+	/* unknown values are never filtered */
+	cnt = evdev_get_mask_cnt(type);
+	if (!cnt || code >= cnt)
+		return false;
+
+	mask = client->evmasks[type];
+	return mask && !test_bit(code, mask);
+}
+
+/* flush queued events of type @type, caller must hold client->buffer_lock */
+static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
+{
+	unsigned int i, head, num;
+	unsigned int mask = client->bufsize - 1;
+	bool is_report;
+	struct input_event *ev;
+
+	BUG_ON(type == EV_SYN);
+
+	head = client->tail;
+	client->packet_head = client->tail;
+
+	/* init to 1 so a leading SYN_REPORT will not be dropped */
+	num = 1;
+
+	for (i = client->tail; i != client->head; i = (i + 1) & mask) {
+		ev = &client->buffer[i];
+		is_report = ev->type == EV_SYN && ev->code == SYN_REPORT;
+
+		if (ev->type == type) {
+			/* drop matched entry */
+			continue;
+		} else if (is_report && !num) {
+			/* drop empty SYN_REPORT groups */
+			continue;
+		} else if (head != i) {
+			/* move entry to fill the gap */
+			client->buffer[head].time = ev->time;
+			client->buffer[head].type = ev->type;
+			client->buffer[head].code = ev->code;
+			client->buffer[head].value = ev->value;
+		}
+
+		num++;
+		head = (head + 1) & mask;
+
+		if (is_report) {
+			num = 0;
+			client->packet_head = head;
+		}
+	}
+
+	client->head = head;
+}
+
+static void __evdev_queue_syn_dropped(struct evdev_client *client)
+{
+	struct input_event ev;
+	ktime_t time;
+
+	time = client->clk_type == EV_CLK_REAL ?
+			ktime_get_real() :
+			client->clk_type == EV_CLK_MONO ?
+				ktime_get() :
+				ktime_get_boottime();
+
+	ev.time = ktime_to_timeval(time);
+	ev.type = EV_SYN;
+	ev.code = SYN_DROPPED;
+	ev.value = 0;
+
+	client->buffer[client->head++] = ev;
+	client->head &= client->bufsize - 1;
+
+	if (unlikely(client->head == client->tail)) {
+		/* drop queue but keep our SYN_DROPPED event */
+		client->tail = (client->head - 1) & (client->bufsize - 1);
+		client->packet_head = client->tail;
+	}
+}
+
+static void evdev_queue_syn_dropped(struct evdev_client *client)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&client->buffer_lock, flags);
+	__evdev_queue_syn_dropped(client);
+	spin_unlock_irqrestore(&client->buffer_lock, flags);
+}
+
+static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
+{
+	unsigned long flags;
+	unsigned int clk_type;
+
+	switch (clkid) {
+
+	case CLOCK_REALTIME:
+		clk_type = EV_CLK_REAL;
+		break;
+	case CLOCK_MONOTONIC:
+		clk_type = EV_CLK_MONO;
+		break;
+	case CLOCK_BOOTTIME:
+		clk_type = EV_CLK_BOOT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (client->clk_type != clk_type) {
+		client->clk_type = clk_type;
+
+		/*
+		 * Flush pending events and queue SYN_DROPPED event,
+		 * but only if the queue is not empty.
+		 */
+		spin_lock_irqsave(&client->buffer_lock, flags);
+
+		if (client->head != client->tail) {
+			client->packet_head = client->head = client->tail;
+			__evdev_queue_syn_dropped(client);
+		}
+
+		spin_unlock_irqrestore(&client->buffer_lock, flags);
+	}
+
+	return 0;
+}
+
+static void __pass_event(struct evdev_client *client,
+			 const struct input_event *event)
+{
+	client->buffer[client->head++] = *event;
+	client->head &= client->bufsize - 1;
+
+	if (unlikely(client->head == client->tail)) {
+		/*
+		 * This effectively "drops" all unconsumed events, leaving
+		 * EV_SYN/SYN_DROPPED plus the newest event in the queue.
+		 */
+		client->tail = (client->head - 2) & (client->bufsize - 1);
+
+		client->buffer[client->tail].time = event->time;
+		client->buffer[client->tail].type = EV_SYN;
+		client->buffer[client->tail].code = SYN_DROPPED;
+		client->buffer[client->tail].value = 0;
+
+		client->packet_head = client->tail;
+	}
+
+	if (event->type == EV_SYN && event->code == SYN_REPORT) {
+		client->packet_head = client->head;
+		kill_fasync(&client->fasync, SIGIO, POLL_IN);
+	}
+}
+
+static void evdev_pass_values(struct evdev_client *client,
+			const struct input_value *vals, unsigned int count,
+			ktime_t *ev_time)
+{
+	struct evdev *evdev = client->evdev;
+	const struct input_value *v;
+	struct input_event event;
+	bool wakeup = false;
+
+	if (client->revoked)
+		return;
+
+	event.time = ktime_to_timeval(ev_time[client->clk_type]);
+
+	/* Interrupts are disabled, just acquire the lock. */
+	spin_lock(&client->buffer_lock);
+
+	for (v = vals; v != vals + count; v++) {
+		if (__evdev_is_filtered(client, v->type, v->code))
+			continue;
+
+		if (v->type == EV_SYN && v->code == SYN_REPORT) {
+			/* drop empty SYN_REPORT */
+			if (client->packet_head == client->head)
+				continue;
+
+			wakeup = true;
+		}
+
+		event.type = v->type;
+		event.code = v->code;
+		event.value = v->value;
+		__pass_event(client, &event);
+	}
+
+	spin_unlock(&client->buffer_lock);
+
+	if (wakeup)
+		wake_up_interruptible(&evdev->wait);
+}
+
+/*
+ * Pass incoming events to all connected clients.
+ */
+static void evdev_events(struct input_handle *handle,
+			 const struct input_value *vals, unsigned int count)
+{
+	struct evdev *evdev = handle->private;
+	struct evdev_client *client;
+	ktime_t ev_time[EV_CLK_MAX];
+
+	ev_time[EV_CLK_MONO] = ktime_get();
+	ev_time[EV_CLK_REAL] = ktime_mono_to_real(ev_time[EV_CLK_MONO]);
+	ev_time[EV_CLK_BOOT] = ktime_mono_to_any(ev_time[EV_CLK_MONO],
+						 TK_OFFS_BOOT);
+
+	rcu_read_lock();
+
+	client = rcu_dereference(evdev->grab);
+
+	if (client)
+		evdev_pass_values(client, vals, count, ev_time);
+	else
+		list_for_each_entry_rcu(client, &evdev->client_list, node)
+			evdev_pass_values(client, vals, count, ev_time);
+
+	rcu_read_unlock();
+}
+
+/*
+ * Pass incoming event to all connected clients.
+ */
+static void evdev_event(struct input_handle *handle,
+			unsigned int type, unsigned int code, int value)
+{
+	struct input_value vals[] = { { type, code, value } };
+
+	evdev_events(handle, vals, 1);
+}
+
+static int evdev_fasync(int fd, struct file *file, int on)
+{
+	struct evdev_client *client = file->private_data;
+
+	return fasync_helper(fd, file, on, &client->fasync);
+}
+
+static int evdev_flush(struct file *file, fl_owner_t id)
+{
+	struct evdev_client *client = file->private_data;
+	struct evdev *evdev = client->evdev;
+
+	mutex_lock(&evdev->mutex);
+
+	if (evdev->exist && !client->revoked)
+		input_flush_device(&evdev->handle, file);
+
+	mutex_unlock(&evdev->mutex);
+	return 0;
+}
+
+static void evdev_free(struct device *dev)
+{
+	struct evdev *evdev = container_of(dev, struct evdev, dev);
+
+	input_put_device(evdev->handle.dev);
+	kfree(evdev);
+}
+
+/*
+ * Grabs an event device (along with underlying input device).
+ * This function is called with evdev->mutex taken.
+ */
+static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
+{
+	int error;
+
+	if (evdev->grab)
+		return -EBUSY;
+
+	error = input_grab_device(&evdev->handle);
+	if (error)
+		return error;
+
+	rcu_assign_pointer(evdev->grab, client);
+
+	return 0;
+}
+
+static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client)
+{
+	struct evdev_client *grab = rcu_dereference_protected(evdev->grab,
+					lockdep_is_held(&evdev->mutex));
+
+	if (grab != client)
+		return  -EINVAL;
+
+	rcu_assign_pointer(evdev->grab, NULL);
+	synchronize_rcu();
+	input_release_device(&evdev->handle);
+
+	return 0;
+}
+
+static void evdev_attach_client(struct evdev *evdev,
+				struct evdev_client *client)
+{
+	spin_lock(&evdev->client_lock);
+	list_add_tail_rcu(&client->node, &evdev->client_list);
+	spin_unlock(&evdev->client_lock);
+}
+
+static void evdev_detach_client(struct evdev *evdev,
+				struct evdev_client *client)
+{
+	spin_lock(&evdev->client_lock);
+	list_del_rcu(&client->node);
+	spin_unlock(&evdev->client_lock);
+	synchronize_rcu();
+}
+
+static int evdev_open_device(struct evdev *evdev)
+{
+	int retval;
+
+	retval = mutex_lock_interruptible(&evdev->mutex);
+	if (retval)
+		return retval;
+
+	if (!evdev->exist)
+		retval = -ENODEV;
+	else if (!evdev->open++) {
+		retval = input_open_device(&evdev->handle);
+		if (retval)
+			evdev->open--;
+	}
+
+	mutex_unlock(&evdev->mutex);
+	return retval;
+}
+
+static void evdev_close_device(struct evdev *evdev)
+{
+	mutex_lock(&evdev->mutex);
+
+	if (evdev->exist && !--evdev->open)
+		input_close_device(&evdev->handle);
+
+	mutex_unlock(&evdev->mutex);
+}
+
+/*
+ * Wake up users waiting for IO so they can disconnect from
+ * dead device.
+ */
+static void evdev_hangup(struct evdev *evdev)
+{
+	struct evdev_client *client;
+
+	spin_lock(&evdev->client_lock);
+	list_for_each_entry(client, &evdev->client_list, node)
+		kill_fasync(&client->fasync, SIGIO, POLL_HUP);
+	spin_unlock(&evdev->client_lock);
+
+	wake_up_interruptible(&evdev->wait);
+}
+
+static int evdev_release(struct inode *inode, struct file *file)
+{
+	struct evdev_client *client = file->private_data;
+	struct evdev *evdev = client->evdev;
+	unsigned int i;
+
+	mutex_lock(&evdev->mutex);
+	evdev_ungrab(evdev, client);
+	mutex_unlock(&evdev->mutex);
+
+	evdev_detach_client(evdev, client);
+
+	for (i = 0; i < EV_CNT; ++i)
+		kfree(client->evmasks[i]);
+
+	kvfree(client);
+
+	evdev_close_device(evdev);
+
+	return 0;
+}
+
+static unsigned int evdev_compute_buffer_size(struct input_dev *dev)
+{
+	unsigned int n_events =
+		max(dev->hint_events_per_packet * EVDEV_BUF_PACKETS,
+		    EVDEV_MIN_BUFFER_SIZE);
+
+	return roundup_pow_of_two(n_events);
+}
+
+static int evdev_open(struct inode *inode, struct file *file)
+{
+	struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
+	unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
+	unsigned int size = sizeof(struct evdev_client) +
+					bufsize * sizeof(struct input_event);
+	struct evdev_client *client;
+	int error;
+
+	client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
+	if (!client)
+		client = vzalloc(size);
+	if (!client)
+		return -ENOMEM;
+
+	client->bufsize = bufsize;
+	spin_lock_init(&client->buffer_lock);
+	client->evdev = evdev;
+	evdev_attach_client(evdev, client);
+
+	error = evdev_open_device(evdev);
+	if (error)
+		goto err_free_client;
+
+	file->private_data = client;
+	nonseekable_open(inode, file);
+
+	return 0;
+
+ err_free_client:
+	evdev_detach_client(evdev, client);
+	kvfree(client);
+	return error;
+}
+
+static ssize_t evdev_write(struct file *file, const char __user *buffer,
+			   size_t count, loff_t *ppos)
+{
+	struct evdev_client *client = file->private_data;
+	struct evdev *evdev = client->evdev;
+	struct input_event event;
+	int retval = 0;
+
+	if (count != 0 && count < input_event_size())
+		return -EINVAL;
+
+	retval = mutex_lock_interruptible(&evdev->mutex);
+	if (retval)
+		return retval;
+
+	if (!evdev->exist || client->revoked) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	while (retval + input_event_size() <= count) {
+
+		if (input_event_from_user(buffer + retval, &event)) {
+			retval = -EFAULT;
+			goto out;
+		}
+		retval += input_event_size();
+
+		input_inject_event(&evdev->handle,
+				   event.type, event.code, event.value);
+	}
+
+ out:
+	mutex_unlock(&evdev->mutex);
+	return retval;
+}
+
+static int evdev_fetch_next_event(struct evdev_client *client,
+				  struct input_event *event)
+{
+	int have_event;
+
+	spin_lock_irq(&client->buffer_lock);
+
+	have_event = client->packet_head != client->tail;
+	if (have_event) {
+		*event = client->buffer[client->tail++];
+		client->tail &= client->bufsize - 1;
+	}
+
+	spin_unlock_irq(&client->buffer_lock);
+
+	return have_event;
+}
+
+static ssize_t evdev_read(struct file *file, char __user *buffer,
+			  size_t count, loff_t *ppos)
+{
+	struct evdev_client *client = file->private_data;
+	struct evdev *evdev = client->evdev;
+	struct input_event event;
+	size_t read = 0;
+	int error;
+
+	if (count != 0 && count < input_event_size())
+		return -EINVAL;
+
+	for (;;) {
+		if (!evdev->exist || client->revoked)
+			return -ENODEV;
+
+		if (client->packet_head == client->tail &&
+		    (file->f_flags & O_NONBLOCK))
+			return -EAGAIN;
+
+		/*
+		 * count == 0 is special - no IO is done but we check
+		 * for error conditions (see above).
+		 */
+		if (count == 0)
+			break;
+
+		while (read + input_event_size() <= count &&
+		       evdev_fetch_next_event(client, &event)) {
+
+			if (input_event_to_user(buffer + read, &event))
+				return -EFAULT;
+
+			read += input_event_size();
+		}
+
+		if (read)
+			break;
+
+		if (!(file->f_flags & O_NONBLOCK)) {
+			error = wait_event_interruptible(evdev->wait,
+					client->packet_head != client->tail ||
+					!evdev->exist || client->revoked);
+			if (error)
+				return error;
+		}
+	}
+
+	return read;
+}
+
+/* No kernel lock - fine */
+static unsigned int evdev_poll(struct file *file, poll_table *wait)
+{
+	struct evdev_client *client = file->private_data;
+	struct evdev *evdev = client->evdev;
+	unsigned int mask;
+
+	poll_wait(file, &evdev->wait, wait);
+
+	if (evdev->exist && !client->revoked)
+		mask = POLLOUT | POLLWRNORM;
+	else
+		mask = POLLHUP | POLLERR;
+
+	if (client->packet_head != client->tail)
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+#ifdef CONFIG_COMPAT
+
+#define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8)
+#define BITS_TO_LONGS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1)
+
+#ifdef __BIG_ENDIAN
+static int bits_to_user(unsigned long *bits, unsigned int maxbit,
+			unsigned int maxlen, void __user *p, int compat)
+{
+	int len, i;
+
+	if (compat) {
+		len = BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t);
+		if (len > maxlen)
+			len = maxlen;
+
+		for (i = 0; i < len / sizeof(compat_long_t); i++)
+			if (copy_to_user((compat_long_t __user *) p + i,
+					 (compat_long_t *) bits +
+						i + 1 - ((i % 2) << 1),
+					 sizeof(compat_long_t)))
+				return -EFAULT;
+	} else {
+		len = BITS_TO_LONGS(maxbit) * sizeof(long);
+		if (len > maxlen)
+			len = maxlen;
+
+		if (copy_to_user(p, bits, len))
+			return -EFAULT;
+	}
+
+	return len;
+}
+
+static int bits_from_user(unsigned long *bits, unsigned int maxbit,
+			  unsigned int maxlen, const void __user *p, int compat)
+{
+	int len, i;
+
+	if (compat) {
+		if (maxlen % sizeof(compat_long_t))
+			return -EINVAL;
+
+		len = BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t);
+		if (len > maxlen)
+			len = maxlen;
+
+		for (i = 0; i < len / sizeof(compat_long_t); i++)
+			if (copy_from_user((compat_long_t *) bits +
+						i + 1 - ((i % 2) << 1),
+					   (compat_long_t __user *) p + i,
+					   sizeof(compat_long_t)))
+				return -EFAULT;
+		if (i % 2)
+			*((compat_long_t *) bits + i - 1) = 0;
+
+	} else {
+		if (maxlen % sizeof(long))
+			return -EINVAL;
+
+		len = BITS_TO_LONGS(maxbit) * sizeof(long);
+		if (len > maxlen)
+			len = maxlen;
+
+		if (copy_from_user(bits, p, len))
+			return -EFAULT;
+	}
+
+	return len;
+}
+
+#else
+
+static int bits_to_user(unsigned long *bits, unsigned int maxbit,
+			unsigned int maxlen, void __user *p, int compat)
+{
+	int len = compat ?
+			BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t) :
+			BITS_TO_LONGS(maxbit) * sizeof(long);
+
+	if (len > maxlen)
+		len = maxlen;
+
+	return copy_to_user(p, bits, len) ? -EFAULT : len;
+}
+
+static int bits_from_user(unsigned long *bits, unsigned int maxbit,
+			  unsigned int maxlen, const void __user *p, int compat)
+{
+	size_t chunk_size = compat ? sizeof(compat_long_t) : sizeof(long);
+	int len;
+
+	if (maxlen % chunk_size)
+		return -EINVAL;
+
+	len = compat ? BITS_TO_LONGS_COMPAT(maxbit) : BITS_TO_LONGS(maxbit);
+	len *= chunk_size;
+	if (len > maxlen)
+		len = maxlen;
+
+	return copy_from_user(bits, p, len) ? -EFAULT : len;
+}
+
+#endif /* __BIG_ENDIAN */
+
+#else
+
+static int bits_to_user(unsigned long *bits, unsigned int maxbit,
+			unsigned int maxlen, void __user *p, int compat)
+{
+	int len = BITS_TO_LONGS(maxbit) * sizeof(long);
+
+	if (len > maxlen)
+		len = maxlen;
+
+	return copy_to_user(p, bits, len) ? -EFAULT : len;
+}
+
+static int bits_from_user(unsigned long *bits, unsigned int maxbit,
+			  unsigned int maxlen, const void __user *p, int compat)
+{
+	int len;
+
+	if (maxlen % sizeof(long))
+		return -EINVAL;
+
+	len = BITS_TO_LONGS(maxbit) * sizeof(long);
+	if (len > maxlen)
+		len = maxlen;
+
+	return copy_from_user(bits, p, len) ? -EFAULT : len;
+}
+
+#endif /* CONFIG_COMPAT */
+
+static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
+{
+	int len;
+
+	if (!str)
+		return -ENOENT;
+
+	len = strlen(str) + 1;
+	if (len > maxlen)
+		len = maxlen;
+
+	return copy_to_user(p, str, len) ? -EFAULT : len;
+}
+
+static int handle_eviocgbit(struct input_dev *dev,
+			    unsigned int type, unsigned int size,
+			    void __user *p, int compat_mode)
+{
+	unsigned long *bits;
+	int len;
+
+	switch (type) {
+
+	case      0: bits = dev->evbit;  len = EV_MAX;  break;
+	case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
+	case EV_REL: bits = dev->relbit; len = REL_MAX; break;
+	case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
+	case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
+	case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
+	case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
+	case EV_FF:  bits = dev->ffbit;  len = FF_MAX;  break;
+	case EV_SW:  bits = dev->swbit;  len = SW_MAX;  break;
+	default: return -EINVAL;
+	}
+
+	return bits_to_user(bits, len, size, p, compat_mode);
+}
+
+static int evdev_handle_get_keycode(struct input_dev *dev, void __user *p)
+{
+	struct input_keymap_entry ke = {
+		.len	= sizeof(unsigned int),
+		.flags	= 0,
+	};
+	int __user *ip = (int __user *)p;
+	int error;
+
+	/* legacy case */
+	if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
+		return -EFAULT;
+
+	error = input_get_keycode(dev, &ke);
+	if (error)
+		return error;
+
+	if (put_user(ke.keycode, ip + 1))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int evdev_handle_get_keycode_v2(struct input_dev *dev, void __user *p)
+{
+	struct input_keymap_entry ke;
+	int error;
+
+	if (copy_from_user(&ke, p, sizeof(ke)))
+		return -EFAULT;
+
+	error = input_get_keycode(dev, &ke);
+	if (error)
+		return error;
+
+	if (copy_to_user(p, &ke, sizeof(ke)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int evdev_handle_set_keycode(struct input_dev *dev, void __user *p)
+{
+	struct input_keymap_entry ke = {
+		.len	= sizeof(unsigned int),
+		.flags	= 0,
+	};
+	int __user *ip = (int __user *)p;
+
+	if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
+		return -EFAULT;
+
+	if (get_user(ke.keycode, ip + 1))
+		return -EFAULT;
+
+	return input_set_keycode(dev, &ke);
+}
+
+static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p)
+{
+	struct input_keymap_entry ke;
+
+	if (copy_from_user(&ke, p, sizeof(ke)))
+		return -EFAULT;
+
+	if (ke.len > sizeof(ke.scancode))
+		return -EINVAL;
+
+	return input_set_keycode(dev, &ke);
+}
+
+/*
+ * If we transfer state to the user, we should flush all pending events
+ * of the same type from the client's queue. Otherwise, they might end up
+ * with duplicate events, which can screw up client's state tracking.
+ * If bits_to_user fails after flushing the queue, we queue a SYN_DROPPED
+ * event so user-space will notice missing events.
+ *
+ * LOCKING:
+ * We need to take event_lock before buffer_lock to avoid dead-locks. But we
+ * need the even_lock only to guarantee consistent state. We can safely release
+ * it while flushing the queue. This allows input-core to handle filters while
+ * we flush the queue.
+ */
+static int evdev_handle_get_val(struct evdev_client *client,
+				struct input_dev *dev, unsigned int type,
+				unsigned long *bits, unsigned int maxbit,
+				unsigned int maxlen, void __user *p,
+				int compat)
+{
+	int ret;
+	unsigned long *mem;
+	size_t len;
+
+	len = BITS_TO_LONGS(maxbit) * sizeof(unsigned long);
+	mem = kmalloc(len, GFP_KERNEL);
+	if (!mem)
+		return -ENOMEM;
+
+	spin_lock_irq(&dev->event_lock);
+	spin_lock(&client->buffer_lock);
+
+	memcpy(mem, bits, len);
+
+	spin_unlock(&dev->event_lock);
+
+	__evdev_flush_queue(client, type);
+
+	spin_unlock_irq(&client->buffer_lock);
+
+	ret = bits_to_user(mem, maxbit, maxlen, p, compat);
+	if (ret < 0)
+		evdev_queue_syn_dropped(client);
+
+	kfree(mem);
+
+	return ret;
+}
+
+static int evdev_handle_mt_request(struct input_dev *dev,
+				   unsigned int size,
+				   int __user *ip)
+{
+	const struct input_mt *mt = dev->mt;
+	unsigned int code;
+	int max_slots;
+	int i;
+
+	if (get_user(code, &ip[0]))
+		return -EFAULT;
+	if (!mt || !input_is_mt_value(code))
+		return -EINVAL;
+
+	max_slots = (size - sizeof(__u32)) / sizeof(__s32);
+	for (i = 0; i < mt->num_slots && i < max_slots; i++) {
+		int value = input_mt_get_value(&mt->slots[i], code);
+		if (put_user(value, &ip[1 + i]))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
+			struct file *file)
+{
+	client->revoked = true;
+	evdev_ungrab(evdev, client);
+	input_flush_device(&evdev->handle, file);
+	wake_up_interruptible(&evdev->wait);
+
+	return 0;
+}
+
+/* must be called with evdev-mutex held */
+static int evdev_set_mask(struct evdev_client *client,
+			  unsigned int type,
+			  const void __user *codes,
+			  u32 codes_size,
+			  int compat)
+{
+	unsigned long flags, *mask, *oldmask;
+	size_t cnt;
+	int error;
+
+	/* we allow unknown types and 'codes_size > size' for forward-compat */
+	cnt = evdev_get_mask_cnt(type);
+	if (!cnt)
+		return 0;
+
+	mask = kcalloc(sizeof(unsigned long), BITS_TO_LONGS(cnt), GFP_KERNEL);
+	if (!mask)
+		return -ENOMEM;
+
+	error = bits_from_user(mask, cnt - 1, codes_size, codes, compat);
+	if (error < 0) {
+		kfree(mask);
+		return error;
+	}
+
+	spin_lock_irqsave(&client->buffer_lock, flags);
+	oldmask = client->evmasks[type];
+	client->evmasks[type] = mask;
+	spin_unlock_irqrestore(&client->buffer_lock, flags);
+
+	kfree(oldmask);
+
+	return 0;
+}
+
+/* must be called with evdev-mutex held */
+static int evdev_get_mask(struct evdev_client *client,
+			  unsigned int type,
+			  void __user *codes,
+			  u32 codes_size,
+			  int compat)
+{
+	unsigned long *mask;
+	size_t cnt, size, xfer_size;
+	int i;
+	int error;
+
+	/* we allow unknown types and 'codes_size > size' for forward-compat */
+	cnt = evdev_get_mask_cnt(type);
+	size = sizeof(unsigned long) * BITS_TO_LONGS(cnt);
+	xfer_size = min_t(size_t, codes_size, size);
+
+	if (cnt > 0) {
+		mask = client->evmasks[type];
+		if (mask) {
+			error = bits_to_user(mask, cnt - 1,
+					     xfer_size, codes, compat);
+			if (error < 0)
+				return error;
+		} else {
+			/* fake mask with all bits set */
+			for (i = 0; i < xfer_size; i++)
+				if (put_user(0xffU, (u8 __user *)codes + i))
+					return -EFAULT;
+		}
+	}
+
+	if (xfer_size < codes_size)
+		if (clear_user(codes + xfer_size, codes_size - xfer_size))
+			return -EFAULT;
+
+	return 0;
+}
+
+static long evdev_do_ioctl(struct file *file, unsigned int cmd,
+			   void __user *p, int compat_mode)
+{
+	struct evdev_client *client = file->private_data;
+	struct evdev *evdev = client->evdev;
+	struct input_dev *dev = evdev->handle.dev;
+	struct input_absinfo abs;
+	struct input_mask mask;
+	struct ff_effect effect;
+	int __user *ip = (int __user *)p;
+	unsigned int i, t, u, v;
+	unsigned int size;
+	int error;
+
+	/* First we check for fixed-length commands */
+	switch (cmd) {
+
+	case EVIOCGVERSION:
+		return put_user(EV_VERSION, ip);
+
+	case EVIOCGID:
+		if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
+			return -EFAULT;
+		return 0;
+
+	case EVIOCGREP:
+		if (!test_bit(EV_REP, dev->evbit))
+			return -ENOSYS;
+		if (put_user(dev->rep[REP_DELAY], ip))
+			return -EFAULT;
+		if (put_user(dev->rep[REP_PERIOD], ip + 1))
+			return -EFAULT;
+		return 0;
+
+	case EVIOCSREP:
+		if (!test_bit(EV_REP, dev->evbit))
+			return -ENOSYS;
+		if (get_user(u, ip))
+			return -EFAULT;
+		if (get_user(v, ip + 1))
+			return -EFAULT;
+
+		input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
+		input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
+
+		return 0;
+
+	case EVIOCRMFF:
+		return input_ff_erase(dev, (int)(unsigned long) p, file);
+
+	case EVIOCGEFFECTS:
+		i = test_bit(EV_FF, dev->evbit) ?
+				dev->ff->max_effects : 0;
+		if (put_user(i, ip))
+			return -EFAULT;
+		return 0;
+
+	case EVIOCGRAB:
+		if (p)
+			return evdev_grab(evdev, client);
+		else
+			return evdev_ungrab(evdev, client);
+
+	case EVIOCREVOKE:
+		if (p)
+			return -EINVAL;
+		else
+			return evdev_revoke(evdev, client, file);
+
+	case EVIOCGMASK: {
+		void __user *codes_ptr;
+
+		if (copy_from_user(&mask, p, sizeof(mask)))
+			return -EFAULT;
+
+		codes_ptr = (void __user *)(unsigned long)mask.codes_ptr;
+		return evdev_get_mask(client,
+				      mask.type, codes_ptr, mask.codes_size,
+				      compat_mode);
+	}
+
+	case EVIOCSMASK: {
+		const void __user *codes_ptr;
+
+		if (copy_from_user(&mask, p, sizeof(mask)))
+			return -EFAULT;
+
+		codes_ptr = (const void __user *)(unsigned long)mask.codes_ptr;
+		return evdev_set_mask(client,
+				      mask.type, codes_ptr, mask.codes_size,
+				      compat_mode);
+	}
+
+	case EVIOCSCLOCKID:
+		if (copy_from_user(&i, p, sizeof(unsigned int)))
+			return -EFAULT;
+
+		return evdev_set_clk_type(client, i);
+
+	case EVIOCGKEYCODE:
+		return evdev_handle_get_keycode(dev, p);
+
+	case EVIOCSKEYCODE:
+		return evdev_handle_set_keycode(dev, p);
+
+	case EVIOCGKEYCODE_V2:
+		return evdev_handle_get_keycode_v2(dev, p);
+
+	case EVIOCSKEYCODE_V2:
+		return evdev_handle_set_keycode_v2(dev, p);
+	}
+
+	size = _IOC_SIZE(cmd);
+
+	/* Now check variable-length commands */
+#define EVIOC_MASK_SIZE(nr)	((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
+	switch (EVIOC_MASK_SIZE(cmd)) {
+
+	case EVIOCGPROP(0):
+		return bits_to_user(dev->propbit, INPUT_PROP_MAX,
+				    size, p, compat_mode);
+
+	case EVIOCGMTSLOTS(0):
+		return evdev_handle_mt_request(dev, size, ip);
+
+	case EVIOCGKEY(0):
+		return evdev_handle_get_val(client, dev, EV_KEY, dev->key,
+					    KEY_MAX, size, p, compat_mode);
+
+	case EVIOCGLED(0):
+		return evdev_handle_get_val(client, dev, EV_LED, dev->led,
+					    LED_MAX, size, p, compat_mode);
+
+	case EVIOCGSND(0):
+		return evdev_handle_get_val(client, dev, EV_SND, dev->snd,
+					    SND_MAX, size, p, compat_mode);
+
+	case EVIOCGSW(0):
+		return evdev_handle_get_val(client, dev, EV_SW, dev->sw,
+					    SW_MAX, size, p, compat_mode);
+
+	case EVIOCGNAME(0):
+		return str_to_user(dev->name, size, p);
+
+	case EVIOCGPHYS(0):
+		return str_to_user(dev->phys, size, p);
+
+	case EVIOCGUNIQ(0):
+		return str_to_user(dev->uniq, size, p);
+
+	case EVIOC_MASK_SIZE(EVIOCSFF):
+		if (input_ff_effect_from_user(p, size, &effect))
+			return -EFAULT;
+
+		error = input_ff_upload(dev, &effect, file);
+		if (error)
+			return error;
+
+		if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
+			return -EFAULT;
+
+		return 0;
+	}
+
+	/* Multi-number variable-length handlers */
+	if (_IOC_TYPE(cmd) != 'E')
+		return -EINVAL;
+
+	if (_IOC_DIR(cmd) == _IOC_READ) {
+
+		if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
+			return handle_eviocgbit(dev,
+						_IOC_NR(cmd) & EV_MAX, size,
+						p, compat_mode);
+
+		if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
+
+			if (!dev->absinfo)
+				return -EINVAL;
+
+			t = _IOC_NR(cmd) & ABS_MAX;
+			abs = dev->absinfo[t];
+
+			if (copy_to_user(p, &abs, min_t(size_t,
+					size, sizeof(struct input_absinfo))))
+				return -EFAULT;
+
+			return 0;
+		}
+	}
+
+	if (_IOC_DIR(cmd) == _IOC_WRITE) {
+
+		if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
+
+			if (!dev->absinfo)
+				return -EINVAL;
+
+			t = _IOC_NR(cmd) & ABS_MAX;
+
+			if (copy_from_user(&abs, p, min_t(size_t,
+					size, sizeof(struct input_absinfo))))
+				return -EFAULT;
+
+			if (size < sizeof(struct input_absinfo))
+				abs.resolution = 0;
+
+			/* We can't change number of reserved MT slots */
+			if (t == ABS_MT_SLOT)
+				return -EINVAL;
+
+			/*
+			 * Take event lock to ensure that we are not
+			 * changing device parameters in the middle
+			 * of event.
+			 */
+			spin_lock_irq(&dev->event_lock);
+			dev->absinfo[t] = abs;
+			spin_unlock_irq(&dev->event_lock);
+
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
+				void __user *p, int compat_mode)
+{
+	struct evdev_client *client = file->private_data;
+	struct evdev *evdev = client->evdev;
+	int retval;
+
+	retval = mutex_lock_interruptible(&evdev->mutex);
+	if (retval)
+		return retval;
+
+	if (!evdev->exist || client->revoked) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	retval = evdev_do_ioctl(file, cmd, p, compat_mode);
+
+ out:
+	mutex_unlock(&evdev->mutex);
+	return retval;
+}
+
+static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
+}
+
+#ifdef CONFIG_COMPAT
+static long evdev_ioctl_compat(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
+}
+#endif
+
+static const struct file_operations evdev_fops = {
+	.owner		= THIS_MODULE,
+	.read		= evdev_read,
+	.write		= evdev_write,
+	.poll		= evdev_poll,
+	.open		= evdev_open,
+	.release	= evdev_release,
+	.unlocked_ioctl	= evdev_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= evdev_ioctl_compat,
+#endif
+	.fasync		= evdev_fasync,
+	.flush		= evdev_flush,
+	.llseek		= no_llseek,
+};
+
+/*
+ * Mark device non-existent. This disables writes, ioctls and
+ * prevents new users from opening the device. Already posted
+ * blocking reads will stay, however new ones will fail.
+ */
+static void evdev_mark_dead(struct evdev *evdev)
+{
+	mutex_lock(&evdev->mutex);
+	evdev->exist = false;
+	mutex_unlock(&evdev->mutex);
+}
+
+static void evdev_cleanup(struct evdev *evdev)
+{
+	struct input_handle *handle = &evdev->handle;
+
+	evdev_mark_dead(evdev);
+	evdev_hangup(evdev);
+
+	cdev_del(&evdev->cdev);
+
+	/* evdev is marked dead so no one else accesses evdev->open */
+	if (evdev->open) {
+		input_flush_device(handle, NULL);
+		input_close_device(handle);
+	}
+}
+
+/*
+ * Create new evdev device. Note that input core serializes calls
+ * to connect and disconnect.
+ */
+static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
+			 const struct input_device_id *id)
+{
+	struct evdev *evdev;
+	int minor;
+	int dev_no;
+	int error;
+
+	minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
+	if (minor < 0) {
+		error = minor;
+		pr_err("failed to reserve new minor: %d\n", error);
+		return error;
+	}
+
+	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
+	if (!evdev) {
+		error = -ENOMEM;
+		goto err_free_minor;
+	}
+
+	INIT_LIST_HEAD(&evdev->client_list);
+	spin_lock_init(&evdev->client_lock);
+	mutex_init(&evdev->mutex);
+	init_waitqueue_head(&evdev->wait);
+	evdev->exist = true;
+
+	dev_no = minor;
+	/* Normalize device number if it falls into legacy range */
+	if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
+		dev_no -= EVDEV_MINOR_BASE;
+	dev_set_name(&evdev->dev, "event%d", dev_no);
+
+	evdev->handle.dev = input_get_device(dev);
+	evdev->handle.name = dev_name(&evdev->dev);
+	evdev->handle.handler = handler;
+	evdev->handle.private = evdev;
+
+	evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
+	evdev->dev.class = &input_class;
+	evdev->dev.parent = &dev->dev;
+	evdev->dev.release = evdev_free;
+	device_initialize(&evdev->dev);
+
+	error = input_register_handle(&evdev->handle);
+	if (error)
+		goto err_free_evdev;
+
+	cdev_init(&evdev->cdev, &evdev_fops);
+	evdev->cdev.kobj.parent = &evdev->dev.kobj;
+	error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
+	if (error)
+		goto err_unregister_handle;
+
+	error = device_add(&evdev->dev);
+	if (error)
+		goto err_cleanup_evdev;
+
+	return 0;
+
+ err_cleanup_evdev:
+	evdev_cleanup(evdev);
+ err_unregister_handle:
+	input_unregister_handle(&evdev->handle);
+ err_free_evdev:
+	put_device(&evdev->dev);
+ err_free_minor:
+	input_free_minor(minor);
+	return error;
+}
+
+static void evdev_disconnect(struct input_handle *handle)
+{
+	struct evdev *evdev = handle->private;
+
+	device_del(&evdev->dev);
+	evdev_cleanup(evdev);
+	input_free_minor(MINOR(evdev->dev.devt));
+	input_unregister_handle(handle);
+	put_device(&evdev->dev);
+}
+
+static const struct input_device_id evdev_ids[] = {
+	{ .driver_info = 1 },	/* Matches all devices */
+	{ },			/* Terminating zero entry */
+};
+
+MODULE_DEVICE_TABLE(input, evdev_ids);
+
+static struct input_handler evdev_handler = {
+	.event		= evdev_event,
+	.events		= evdev_events,
+	.connect	= evdev_connect,
+	.disconnect	= evdev_disconnect,
+	.legacy_minors	= true,
+	.minor		= EVDEV_MINOR_BASE,
+	.name		= "evdev",
+	.id_table	= evdev_ids,
+};
+
+static int __init evdev_init(void)
+{
+	return input_register_handler(&evdev_handler);
+}
+
+static void __exit evdev_exit(void)
+{
+	input_unregister_handler(&evdev_handler);
+}
+
+module_init(evdev_init);
+module_exit(evdev_exit);
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Input driver event char devices");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
new file mode 100644
index 0000000..8f20424
--- /dev/null
+++ b/drivers/input/ff-core.c
@@ -0,0 +1,384 @@
+/*
+ *  Force feedback support for Linux input subsystem
+ *
+ *  Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
+ *  Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* #define DEBUG */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+/*
+ * Check that the effect_id is a valid effect and whether the user
+ * is the owner
+ */
+static int check_effect_access(struct ff_device *ff, int effect_id,
+				struct file *file)
+{
+	if (effect_id < 0 || effect_id >= ff->max_effects ||
+	    !ff->effect_owners[effect_id])
+		return -EINVAL;
+
+	if (file && ff->effect_owners[effect_id] != file)
+		return -EACCES;
+
+	return 0;
+}
+
+/*
+ * Checks whether 2 effects can be combined together
+ */
+static inline int check_effects_compatible(struct ff_effect *e1,
+					   struct ff_effect *e2)
+{
+	return e1->type == e2->type &&
+	       (e1->type != FF_PERIODIC ||
+		e1->u.periodic.waveform == e2->u.periodic.waveform);
+}
+
+/*
+ * Convert an effect into compatible one
+ */
+static int compat_effect(struct ff_device *ff, struct ff_effect *effect)
+{
+	int magnitude;
+
+	switch (effect->type) {
+	case FF_RUMBLE:
+		if (!test_bit(FF_PERIODIC, ff->ffbit))
+			return -EINVAL;
+
+		/*
+		 * calculate magnitude of sine wave as average of rumble's
+		 * 2/3 of strong magnitude and 1/3 of weak magnitude
+		 */
+		magnitude = effect->u.rumble.strong_magnitude / 3 +
+			    effect->u.rumble.weak_magnitude / 6;
+
+		effect->type = FF_PERIODIC;
+		effect->u.periodic.waveform = FF_SINE;
+		effect->u.periodic.period = 50;
+		effect->u.periodic.magnitude = max(magnitude, 0x7fff);
+		effect->u.periodic.offset = 0;
+		effect->u.periodic.phase = 0;
+		effect->u.periodic.envelope.attack_length = 0;
+		effect->u.periodic.envelope.attack_level = 0;
+		effect->u.periodic.envelope.fade_length = 0;
+		effect->u.periodic.envelope.fade_level = 0;
+
+		return 0;
+
+	default:
+		/* Let driver handle conversion */
+		return 0;
+	}
+}
+
+/**
+ * input_ff_upload() - upload effect into force-feedback device
+ * @dev: input device
+ * @effect: effect to be uploaded
+ * @file: owner of the effect
+ */
+int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
+		    struct file *file)
+{
+	struct ff_device *ff = dev->ff;
+	struct ff_effect *old;
+	int ret = 0;
+	int id;
+
+	if (!test_bit(EV_FF, dev->evbit))
+		return -ENOSYS;
+
+	if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX ||
+	    !test_bit(effect->type, dev->ffbit)) {
+		dev_dbg(&dev->dev, "invalid or not supported effect type in upload\n");
+		return -EINVAL;
+	}
+
+	if (effect->type == FF_PERIODIC &&
+	    (effect->u.periodic.waveform < FF_WAVEFORM_MIN ||
+	     effect->u.periodic.waveform > FF_WAVEFORM_MAX ||
+	     !test_bit(effect->u.periodic.waveform, dev->ffbit))) {
+		dev_dbg(&dev->dev, "invalid or not supported wave form in upload\n");
+		return -EINVAL;
+	}
+
+	if (!test_bit(effect->type, ff->ffbit)) {
+		ret = compat_effect(ff, effect);
+		if (ret)
+			return ret;
+	}
+
+	mutex_lock(&ff->mutex);
+
+	if (effect->id == -1) {
+		for (id = 0; id < ff->max_effects; id++)
+			if (!ff->effect_owners[id])
+				break;
+
+		if (id >= ff->max_effects) {
+			ret = -ENOSPC;
+			goto out;
+		}
+
+		effect->id = id;
+		old = NULL;
+
+	} else {
+		id = effect->id;
+
+		ret = check_effect_access(ff, id, file);
+		if (ret)
+			goto out;
+
+		old = &ff->effects[id];
+
+		if (!check_effects_compatible(effect, old)) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	ret = ff->upload(dev, effect, old);
+	if (ret)
+		goto out;
+
+	spin_lock_irq(&dev->event_lock);
+	ff->effects[id] = *effect;
+	ff->effect_owners[id] = file;
+	spin_unlock_irq(&dev->event_lock);
+
+ out:
+	mutex_unlock(&ff->mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(input_ff_upload);
+
+/*
+ * Erases the effect if the requester is also the effect owner. The mutex
+ * should already be locked before calling this function.
+ */
+static int erase_effect(struct input_dev *dev, int effect_id,
+			struct file *file)
+{
+	struct ff_device *ff = dev->ff;
+	int error;
+
+	error = check_effect_access(ff, effect_id, file);
+	if (error)
+		return error;
+
+	spin_lock_irq(&dev->event_lock);
+	ff->playback(dev, effect_id, 0);
+	ff->effect_owners[effect_id] = NULL;
+	spin_unlock_irq(&dev->event_lock);
+
+	if (ff->erase) {
+		error = ff->erase(dev, effect_id);
+		if (error) {
+			spin_lock_irq(&dev->event_lock);
+			ff->effect_owners[effect_id] = file;
+			spin_unlock_irq(&dev->event_lock);
+
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * input_ff_erase - erase a force-feedback effect from device
+ * @dev: input device to erase effect from
+ * @effect_id: id of the effect to be erased
+ * @file: purported owner of the request
+ *
+ * This function erases a force-feedback effect from specified device.
+ * The effect will only be erased if it was uploaded through the same
+ * file handle that is requesting erase.
+ */
+int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file)
+{
+	struct ff_device *ff = dev->ff;
+	int ret;
+
+	if (!test_bit(EV_FF, dev->evbit))
+		return -ENOSYS;
+
+	mutex_lock(&ff->mutex);
+	ret = erase_effect(dev, effect_id, file);
+	mutex_unlock(&ff->mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(input_ff_erase);
+
+/*
+ * flush_effects - erase all effects owned by a file handle
+ */
+static int flush_effects(struct input_dev *dev, struct file *file)
+{
+	struct ff_device *ff = dev->ff;
+	int i;
+
+	dev_dbg(&dev->dev, "flushing now\n");
+
+	mutex_lock(&ff->mutex);
+
+	for (i = 0; i < ff->max_effects; i++)
+		erase_effect(dev, i, file);
+
+	mutex_unlock(&ff->mutex);
+
+	return 0;
+}
+
+/**
+ * input_ff_event() - generic handler for force-feedback events
+ * @dev: input device to send the effect to
+ * @type: event type (anything but EV_FF is ignored)
+ * @code: event code
+ * @value: event value
+ */
+int input_ff_event(struct input_dev *dev, unsigned int type,
+		   unsigned int code, int value)
+{
+	struct ff_device *ff = dev->ff;
+
+	if (type != EV_FF)
+		return 0;
+
+	switch (code) {
+	case FF_GAIN:
+		if (!test_bit(FF_GAIN, dev->ffbit) || value > 0xffffU)
+			break;
+
+		ff->set_gain(dev, value);
+		break;
+
+	case FF_AUTOCENTER:
+		if (!test_bit(FF_AUTOCENTER, dev->ffbit) || value > 0xffffU)
+			break;
+
+		ff->set_autocenter(dev, value);
+		break;
+
+	default:
+		if (check_effect_access(ff, code, NULL) == 0)
+			ff->playback(dev, code, value);
+		break;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_event);
+
+/**
+ * input_ff_create() - create force-feedback device
+ * @dev: input device supporting force-feedback
+ * @max_effects: maximum number of effects supported by the device
+ *
+ * This function allocates all necessary memory for a force feedback
+ * portion of an input device and installs all default handlers.
+ * @dev->ffbit should be already set up before calling this function.
+ * Once ff device is created you need to setup its upload, erase,
+ * playback and other handlers before registering input device
+ */
+int input_ff_create(struct input_dev *dev, unsigned int max_effects)
+{
+	struct ff_device *ff;
+	size_t ff_dev_size;
+	int i;
+
+	if (!max_effects) {
+		dev_err(&dev->dev, "cannot allocate device without any effects\n");
+		return -EINVAL;
+	}
+
+	if (max_effects > FF_MAX_EFFECTS) {
+		dev_err(&dev->dev, "cannot allocate more than FF_MAX_EFFECTS effects\n");
+		return -EINVAL;
+	}
+
+	ff_dev_size = sizeof(struct ff_device) +
+				max_effects * sizeof(struct file *);
+	if (ff_dev_size < max_effects) /* overflow */
+		return -EINVAL;
+
+	ff = kzalloc(ff_dev_size, GFP_KERNEL);
+	if (!ff)
+		return -ENOMEM;
+
+	ff->effects = kcalloc(max_effects, sizeof(struct ff_effect),
+			      GFP_KERNEL);
+	if (!ff->effects) {
+		kfree(ff);
+		return -ENOMEM;
+	}
+
+	ff->max_effects = max_effects;
+	mutex_init(&ff->mutex);
+
+	dev->ff = ff;
+	dev->flush = flush_effects;
+	dev->event = input_ff_event;
+	__set_bit(EV_FF, dev->evbit);
+
+	/* Copy "true" bits into ff device bitmap */
+	for_each_set_bit(i, dev->ffbit, FF_CNT)
+		__set_bit(i, ff->ffbit);
+
+	/* we can emulate RUMBLE with periodic effects */
+	if (test_bit(FF_PERIODIC, ff->ffbit))
+		__set_bit(FF_RUMBLE, dev->ffbit);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_create);
+
+/**
+ * input_ff_destroy() - frees force feedback portion of input device
+ * @dev: input device supporting force feedback
+ *
+ * This function is only needed in error path as input core will
+ * automatically free force feedback structures when device is
+ * destroyed.
+ */
+void input_ff_destroy(struct input_dev *dev)
+{
+	struct ff_device *ff = dev->ff;
+
+	__clear_bit(EV_FF, dev->evbit);
+	if (ff) {
+		if (ff->destroy)
+			ff->destroy(ff);
+		kfree(ff->private);
+		kfree(ff->effects);
+		kfree(ff);
+		dev->ff = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(input_ff_destroy);
diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
new file mode 100644
index 0000000..fcc6c33
--- /dev/null
+++ b/drivers/input/ff-memless.c
@@ -0,0 +1,559 @@
+/*
+ *  Force feedback support for memoryless devices
+ *
+ *  Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
+ *  Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* #define DEBUG */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/jiffies.h>
+#include <linux/fixp-arith.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@gmail.com>");
+MODULE_DESCRIPTION("Force feedback support for memoryless devices");
+
+/* Number of effects handled with memoryless devices */
+#define FF_MEMLESS_EFFECTS	16
+
+/* Envelope update interval in ms */
+#define FF_ENVELOPE_INTERVAL	50
+
+#define FF_EFFECT_STARTED	0
+#define FF_EFFECT_PLAYING	1
+#define FF_EFFECT_ABORTING	2
+
+struct ml_effect_state {
+	struct ff_effect *effect;
+	unsigned long flags;	/* effect state (STARTED, PLAYING, etc) */
+	int count;		/* loop count of the effect */
+	unsigned long play_at;	/* start time */
+	unsigned long stop_at;	/* stop time */
+	unsigned long adj_at;	/* last time the effect was sent */
+};
+
+struct ml_device {
+	void *private;
+	struct ml_effect_state states[FF_MEMLESS_EFFECTS];
+	int gain;
+	struct timer_list timer;
+	struct input_dev *dev;
+
+	int (*play_effect)(struct input_dev *dev, void *data,
+			   struct ff_effect *effect);
+};
+
+static const struct ff_envelope *get_envelope(const struct ff_effect *effect)
+{
+	static const struct ff_envelope empty_envelope;
+
+	switch (effect->type) {
+	case FF_PERIODIC:
+		return &effect->u.periodic.envelope;
+
+	case FF_CONSTANT:
+		return &effect->u.constant.envelope;
+
+	default:
+		return &empty_envelope;
+	}
+}
+
+/*
+ * Check for the next time envelope requires an update on memoryless devices
+ */
+static unsigned long calculate_next_time(struct ml_effect_state *state)
+{
+	const struct ff_envelope *envelope = get_envelope(state->effect);
+	unsigned long attack_stop, fade_start, next_fade;
+
+	if (envelope->attack_length) {
+		attack_stop = state->play_at +
+			msecs_to_jiffies(envelope->attack_length);
+		if (time_before(state->adj_at, attack_stop))
+			return state->adj_at +
+					msecs_to_jiffies(FF_ENVELOPE_INTERVAL);
+	}
+
+	if (state->effect->replay.length) {
+		if (envelope->fade_length) {
+			/* check when fading should start */
+			fade_start = state->stop_at -
+					msecs_to_jiffies(envelope->fade_length);
+
+			if (time_before(state->adj_at, fade_start))
+				return fade_start;
+
+			/* already fading, advance to next checkpoint */
+			next_fade = state->adj_at +
+					msecs_to_jiffies(FF_ENVELOPE_INTERVAL);
+			if (time_before(next_fade, state->stop_at))
+				return next_fade;
+		}
+
+		return state->stop_at;
+	}
+
+	return state->play_at;
+}
+
+static void ml_schedule_timer(struct ml_device *ml)
+{
+	struct ml_effect_state *state;
+	unsigned long now = jiffies;
+	unsigned long earliest = 0;
+	unsigned long next_at;
+	int events = 0;
+	int i;
+
+	pr_debug("calculating next timer\n");
+
+	for (i = 0; i < FF_MEMLESS_EFFECTS; i++) {
+
+		state = &ml->states[i];
+
+		if (!test_bit(FF_EFFECT_STARTED, &state->flags))
+			continue;
+
+		if (test_bit(FF_EFFECT_PLAYING, &state->flags))
+			next_at = calculate_next_time(state);
+		else
+			next_at = state->play_at;
+
+		if (time_before_eq(now, next_at) &&
+		    (++events == 1 || time_before(next_at, earliest)))
+			earliest = next_at;
+	}
+
+	if (!events) {
+		pr_debug("no actions\n");
+		del_timer(&ml->timer);
+	} else {
+		pr_debug("timer set\n");
+		mod_timer(&ml->timer, earliest);
+	}
+}
+
+/*
+ * Apply an envelope to a value
+ */
+static int apply_envelope(struct ml_effect_state *state, int value,
+			  struct ff_envelope *envelope)
+{
+	struct ff_effect *effect = state->effect;
+	unsigned long now = jiffies;
+	int time_from_level;
+	int time_of_envelope;
+	int envelope_level;
+	int difference;
+
+	if (envelope->attack_length &&
+	    time_before(now,
+			state->play_at + msecs_to_jiffies(envelope->attack_length))) {
+		pr_debug("value = 0x%x, attack_level = 0x%x\n",
+			 value, envelope->attack_level);
+		time_from_level = jiffies_to_msecs(now - state->play_at);
+		time_of_envelope = envelope->attack_length;
+		envelope_level = min_t(u16, envelope->attack_level, 0x7fff);
+
+	} else if (envelope->fade_length && effect->replay.length &&
+		   time_after(now,
+			      state->stop_at - msecs_to_jiffies(envelope->fade_length)) &&
+		   time_before(now, state->stop_at)) {
+		time_from_level = jiffies_to_msecs(state->stop_at - now);
+		time_of_envelope = envelope->fade_length;
+		envelope_level = min_t(u16, envelope->fade_level, 0x7fff);
+	} else
+		return value;
+
+	difference = abs(value) - envelope_level;
+
+	pr_debug("difference = %d\n", difference);
+	pr_debug("time_from_level = 0x%x\n", time_from_level);
+	pr_debug("time_of_envelope = 0x%x\n", time_of_envelope);
+
+	difference = difference * time_from_level / time_of_envelope;
+
+	pr_debug("difference = %d\n", difference);
+
+	return value < 0 ?
+		-(difference + envelope_level) : (difference + envelope_level);
+}
+
+/*
+ * Return the type the effect has to be converted into (memless devices)
+ */
+static int get_compatible_type(struct ff_device *ff, int effect_type)
+{
+
+	if (test_bit(effect_type, ff->ffbit))
+		return effect_type;
+
+	if (effect_type == FF_PERIODIC && test_bit(FF_RUMBLE, ff->ffbit))
+		return FF_RUMBLE;
+
+	pr_err("invalid type in get_compatible_type()\n");
+
+	return 0;
+}
+
+/*
+ * Only left/right direction should be used (under/over 0x8000) for
+ * forward/reverse motor direction (to keep calculation fast & simple).
+ */
+static u16 ml_calculate_direction(u16 direction, u16 force,
+				  u16 new_direction, u16 new_force)
+{
+	if (!force)
+		return new_direction;
+	if (!new_force)
+		return direction;
+	return (((u32)(direction >> 1) * force +
+		 (new_direction >> 1) * new_force) /
+		(force + new_force)) << 1;
+}
+
+#define FRAC_N 8
+static inline s16 fixp_new16(s16 a)
+{
+	return ((s32)a) >> (16 - FRAC_N);
+}
+
+static inline s16 fixp_mult(s16 a, s16 b)
+{
+	a = ((s32)a * 0x100) / 0x7fff;
+	return ((s32)(a * b)) >> FRAC_N;
+}
+
+/*
+ * Combine two effects and apply gain.
+ */
+static void ml_combine_effects(struct ff_effect *effect,
+			       struct ml_effect_state *state,
+			       int gain)
+{
+	struct ff_effect *new = state->effect;
+	unsigned int strong, weak, i;
+	int x, y;
+	s16 level;
+
+	switch (new->type) {
+	case FF_CONSTANT:
+		i = new->direction * 360 / 0xffff;
+		level = fixp_new16(apply_envelope(state,
+					new->u.constant.level,
+					&new->u.constant.envelope));
+		x = fixp_mult(fixp_sin16(i), level) * gain / 0xffff;
+		y = fixp_mult(-fixp_cos16(i), level) * gain / 0xffff;
+		/*
+		 * here we abuse ff_ramp to hold x and y of constant force
+		 * If in future any driver wants something else than x and y
+		 * in s8, this should be changed to something more generic
+		 */
+		effect->u.ramp.start_level =
+			clamp_val(effect->u.ramp.start_level + x, -0x80, 0x7f);
+		effect->u.ramp.end_level =
+			clamp_val(effect->u.ramp.end_level + y, -0x80, 0x7f);
+		break;
+
+	case FF_RUMBLE:
+		strong = (u32)new->u.rumble.strong_magnitude * gain / 0xffff;
+		weak = (u32)new->u.rumble.weak_magnitude * gain / 0xffff;
+
+		if (effect->u.rumble.strong_magnitude + strong)
+			effect->direction = ml_calculate_direction(
+				effect->direction,
+				effect->u.rumble.strong_magnitude,
+				new->direction, strong);
+		else if (effect->u.rumble.weak_magnitude + weak)
+			effect->direction = ml_calculate_direction(
+				effect->direction,
+				effect->u.rumble.weak_magnitude,
+				new->direction, weak);
+		else
+			effect->direction = 0;
+		effect->u.rumble.strong_magnitude =
+			min(strong + effect->u.rumble.strong_magnitude,
+			    0xffffU);
+		effect->u.rumble.weak_magnitude =
+			min(weak + effect->u.rumble.weak_magnitude, 0xffffU);
+		break;
+
+	case FF_PERIODIC:
+		i = apply_envelope(state, abs(new->u.periodic.magnitude),
+				   &new->u.periodic.envelope);
+
+		/* here we also scale it 0x7fff => 0xffff */
+		i = i * gain / 0x7fff;
+
+		if (effect->u.rumble.strong_magnitude + i)
+			effect->direction = ml_calculate_direction(
+				effect->direction,
+				effect->u.rumble.strong_magnitude,
+				new->direction, i);
+		else
+			effect->direction = 0;
+		effect->u.rumble.strong_magnitude =
+			min(i + effect->u.rumble.strong_magnitude, 0xffffU);
+		effect->u.rumble.weak_magnitude =
+			min(i + effect->u.rumble.weak_magnitude, 0xffffU);
+		break;
+
+	default:
+		pr_err("invalid type in ml_combine_effects()\n");
+		break;
+	}
+
+}
+
+
+/*
+ * Because memoryless devices have only one effect per effect type active
+ * at one time we have to combine multiple effects into one
+ */
+static int ml_get_combo_effect(struct ml_device *ml,
+			       unsigned long *effect_handled,
+			       struct ff_effect *combo_effect)
+{
+	struct ff_effect *effect;
+	struct ml_effect_state *state;
+	int effect_type;
+	int i;
+
+	memset(combo_effect, 0, sizeof(struct ff_effect));
+
+	for (i = 0; i < FF_MEMLESS_EFFECTS; i++) {
+		if (__test_and_set_bit(i, effect_handled))
+			continue;
+
+		state = &ml->states[i];
+		effect = state->effect;
+
+		if (!test_bit(FF_EFFECT_STARTED, &state->flags))
+			continue;
+
+		if (time_before(jiffies, state->play_at))
+			continue;
+
+		/*
+		 * here we have started effects that are either
+		 * currently playing (and may need be aborted)
+		 * or need to start playing.
+		 */
+		effect_type = get_compatible_type(ml->dev->ff, effect->type);
+		if (combo_effect->type != effect_type) {
+			if (combo_effect->type != 0) {
+				__clear_bit(i, effect_handled);
+				continue;
+			}
+			combo_effect->type = effect_type;
+		}
+
+		if (__test_and_clear_bit(FF_EFFECT_ABORTING, &state->flags)) {
+			__clear_bit(FF_EFFECT_PLAYING, &state->flags);
+			__clear_bit(FF_EFFECT_STARTED, &state->flags);
+		} else if (effect->replay.length &&
+			   time_after_eq(jiffies, state->stop_at)) {
+
+			__clear_bit(FF_EFFECT_PLAYING, &state->flags);
+
+			if (--state->count <= 0) {
+				__clear_bit(FF_EFFECT_STARTED, &state->flags);
+			} else {
+				state->play_at = jiffies +
+					msecs_to_jiffies(effect->replay.delay);
+				state->stop_at = state->play_at +
+					msecs_to_jiffies(effect->replay.length);
+			}
+		} else {
+			__set_bit(FF_EFFECT_PLAYING, &state->flags);
+			state->adj_at = jiffies;
+			ml_combine_effects(combo_effect, state, ml->gain);
+		}
+	}
+
+	return combo_effect->type != 0;
+}
+
+static void ml_play_effects(struct ml_device *ml)
+{
+	struct ff_effect effect;
+	DECLARE_BITMAP(handled_bm, FF_MEMLESS_EFFECTS);
+
+	memset(handled_bm, 0, sizeof(handled_bm));
+
+	while (ml_get_combo_effect(ml, handled_bm, &effect))
+		ml->play_effect(ml->dev, ml->private, &effect);
+
+	ml_schedule_timer(ml);
+}
+
+static void ml_effect_timer(unsigned long timer_data)
+{
+	struct input_dev *dev = (struct input_dev *)timer_data;
+	struct ml_device *ml = dev->ff->private;
+	unsigned long flags;
+
+	pr_debug("timer: updating effects\n");
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	ml_play_effects(ml);
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+/*
+ * Sets requested gain for FF effects. Called with dev->event_lock held.
+ */
+static void ml_ff_set_gain(struct input_dev *dev, u16 gain)
+{
+	struct ml_device *ml = dev->ff->private;
+	int i;
+
+	ml->gain = gain;
+
+	for (i = 0; i < FF_MEMLESS_EFFECTS; i++)
+		__clear_bit(FF_EFFECT_PLAYING, &ml->states[i].flags);
+
+	ml_play_effects(ml);
+}
+
+/*
+ * Start/stop specified FF effect. Called with dev->event_lock held.
+ */
+static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
+{
+	struct ml_device *ml = dev->ff->private;
+	struct ml_effect_state *state = &ml->states[effect_id];
+
+	if (value > 0) {
+		pr_debug("initiated play\n");
+
+		__set_bit(FF_EFFECT_STARTED, &state->flags);
+		state->count = value;
+		state->play_at = jiffies +
+				 msecs_to_jiffies(state->effect->replay.delay);
+		state->stop_at = state->play_at +
+				 msecs_to_jiffies(state->effect->replay.length);
+		state->adj_at = state->play_at;
+
+	} else {
+		pr_debug("initiated stop\n");
+
+		if (test_bit(FF_EFFECT_PLAYING, &state->flags))
+			__set_bit(FF_EFFECT_ABORTING, &state->flags);
+		else
+			__clear_bit(FF_EFFECT_STARTED, &state->flags);
+	}
+
+	ml_play_effects(ml);
+
+	return 0;
+}
+
+static int ml_ff_upload(struct input_dev *dev,
+			struct ff_effect *effect, struct ff_effect *old)
+{
+	struct ml_device *ml = dev->ff->private;
+	struct ml_effect_state *state = &ml->states[effect->id];
+
+	spin_lock_irq(&dev->event_lock);
+
+	if (test_bit(FF_EFFECT_STARTED, &state->flags)) {
+		__clear_bit(FF_EFFECT_PLAYING, &state->flags);
+		state->play_at = jiffies +
+				 msecs_to_jiffies(state->effect->replay.delay);
+		state->stop_at = state->play_at +
+				 msecs_to_jiffies(state->effect->replay.length);
+		state->adj_at = state->play_at;
+		ml_schedule_timer(ml);
+	}
+
+	spin_unlock_irq(&dev->event_lock);
+
+	return 0;
+}
+
+static void ml_ff_destroy(struct ff_device *ff)
+{
+	struct ml_device *ml = ff->private;
+
+	kfree(ml->private);
+}
+
+/**
+ * input_ff_create_memless() - create memoryless force-feedback device
+ * @dev: input device supporting force-feedback
+ * @data: driver-specific data to be passed into @play_effect
+ * @play_effect: driver-specific method for playing FF effect
+ */
+int input_ff_create_memless(struct input_dev *dev, void *data,
+		int (*play_effect)(struct input_dev *, void *, struct ff_effect *))
+{
+	struct ml_device *ml;
+	struct ff_device *ff;
+	int error;
+	int i;
+
+	ml = kzalloc(sizeof(struct ml_device), GFP_KERNEL);
+	if (!ml)
+		return -ENOMEM;
+
+	ml->dev = dev;
+	ml->private = data;
+	ml->play_effect = play_effect;
+	ml->gain = 0xffff;
+	setup_timer(&ml->timer, ml_effect_timer, (unsigned long)dev);
+
+	set_bit(FF_GAIN, dev->ffbit);
+
+	error = input_ff_create(dev, FF_MEMLESS_EFFECTS);
+	if (error) {
+		kfree(ml);
+		return error;
+	}
+
+	ff = dev->ff;
+	ff->private = ml;
+	ff->upload = ml_ff_upload;
+	ff->playback = ml_ff_playback;
+	ff->set_gain = ml_ff_set_gain;
+	ff->destroy = ml_ff_destroy;
+
+	/* we can emulate periodic effects with RUMBLE */
+	if (test_bit(FF_RUMBLE, ff->ffbit)) {
+		set_bit(FF_PERIODIC, dev->ffbit);
+		set_bit(FF_SINE, dev->ffbit);
+		set_bit(FF_TRIANGLE, dev->ffbit);
+		set_bit(FF_SQUARE, dev->ffbit);
+	}
+
+	for (i = 0; i < FF_MEMLESS_EFFECTS; i++)
+		ml->states[i].effect = &ff->effects[i];
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_create_memless);
diff --git a/drivers/input/gameport/Kconfig b/drivers/input/gameport/Kconfig
new file mode 100644
index 0000000..d279454
--- /dev/null
+++ b/drivers/input/gameport/Kconfig
@@ -0,0 +1,63 @@
+#
+# Gameport configuration
+#
+config GAMEPORT
+	tristate "Gameport support"
+	---help---
+	  Gameport support is for the standard 15-pin PC gameport. If you
+	  have a joystick, gamepad, gameport card, a soundcard with a gameport
+	  or anything else that uses the gameport, say Y or M here and also to
+	  at least one of the hardware specific drivers.
+
+	  For Ensoniq AudioPCI (ES1370), AudioPCI 97 (ES1371), ESS Solo1,
+	  S3 SonicVibes, Trident 4DWave, SiS7018, and ALi 5451 gameport
+	  support is provided by the sound drivers, so you won't need any
+	  from the below listed modules. You still need to say Y here.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gameport.
+
+if GAMEPORT
+
+config GAMEPORT_NS558
+	tristate "Classic ISA and PnP gameport support"
+	help
+	  Say Y here if you have an ISA or PnP gameport.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ns558.
+
+config GAMEPORT_L4
+	tristate "PDPI Lightning 4 gamecard support"
+	help
+	  Say Y here if you have a PDPI Lightning 4 gamecard.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called lightning.
+
+config GAMEPORT_EMU10K1
+	tristate "SB Live and Audigy gameport support"
+	depends on PCI
+	help
+	  Say Y here if you have a SoundBlaster Live! or SoundBlaster
+	  Audigy card and want to use its gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called emu10k1-gp.
+
+config GAMEPORT_FM801
+	tristate "ForteMedia FM801 gameport support"
+	depends on PCI
+	help
+	  Say Y here if you have ForteMedia FM801 PCI audio controller
+	  (Abit AU10, Genius Sound Maker, HP Workstation zx2000,
+	  and others), and want to use its gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called fm801-gp.
+
+endif
diff --git a/drivers/input/gameport/Makefile b/drivers/input/gameport/Makefile
new file mode 100644
index 0000000..b6f6097
--- /dev/null
+++ b/drivers/input/gameport/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the gameport drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_GAMEPORT)		+= gameport.o
+obj-$(CONFIG_GAMEPORT_EMU10K1)	+= emu10k1-gp.o
+obj-$(CONFIG_GAMEPORT_FM801)	+= fm801-gp.o
+obj-$(CONFIG_GAMEPORT_L4)	+= lightning.o
+obj-$(CONFIG_GAMEPORT_NS558)	+= ns558.o
diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c
new file mode 100644
index 0000000..2909e95
--- /dev/null
+++ b/drivers/input/gameport/emu10k1-gp.c
@@ -0,0 +1,127 @@
+/*
+ *  Copyright (c) 2001 Vojtech Pavlik
+ */
+
+/*
+ * EMU10k1 - SB Live / Audigy - gameport driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/gameport.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("EMU10k1 gameport driver");
+MODULE_LICENSE("GPL");
+
+struct emu {
+	struct pci_dev *dev;
+	struct gameport *gameport;
+	int io;
+	int size;
+};
+
+static const struct pci_device_id emu_tbl[] = {
+
+	{ 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live gameport */
+	{ 0x1102, 0x7003, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy gameport */
+	{ 0x1102, 0x7004, PCI_ANY_ID, PCI_ANY_ID }, /* Dell SB Live */
+	{ 0x1102, 0x7005, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy LS gameport */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, emu_tbl);
+
+static int emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct emu *emu;
+	struct gameport *port;
+	int error;
+
+	emu = kzalloc(sizeof(struct emu), GFP_KERNEL);
+	port = gameport_allocate_port();
+	if (!emu || !port) {
+		printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n");
+		error = -ENOMEM;
+		goto err_out_free;
+	}
+
+	error = pci_enable_device(pdev);
+	if (error)
+		goto err_out_free;
+
+	emu->io = pci_resource_start(pdev, 0);
+	emu->size = pci_resource_len(pdev, 0);
+
+	emu->dev = pdev;
+	emu->gameport = port;
+
+	gameport_set_name(port, "EMU10K1");
+	gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev));
+	port->dev.parent = &pdev->dev;
+	port->io = emu->io;
+
+	if (!request_region(emu->io, emu->size, "emu10k1-gp")) {
+		printk(KERN_ERR "emu10k1-gp: unable to grab region 0x%x-0x%x\n",
+			emu->io, emu->io + emu->size - 1);
+		error = -EBUSY;
+		goto err_out_disable_dev;
+	}
+
+	pci_set_drvdata(pdev, emu);
+
+	gameport_register_port(port);
+
+	return 0;
+
+ err_out_disable_dev:
+	pci_disable_device(pdev);
+ err_out_free:
+	gameport_free_port(port);
+	kfree(emu);
+	return error;
+}
+
+static void emu_remove(struct pci_dev *pdev)
+{
+	struct emu *emu = pci_get_drvdata(pdev);
+
+	gameport_unregister_port(emu->gameport);
+	release_region(emu->io, emu->size);
+	kfree(emu);
+
+	pci_disable_device(pdev);
+}
+
+static struct pci_driver emu_driver = {
+        .name =         "Emu10k1_gameport",
+        .id_table =     emu_tbl,
+        .probe =        emu_probe,
+	.remove =	emu_remove,
+};
+
+module_pci_driver(emu_driver);
diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c
new file mode 100644
index 0000000..7c03114
--- /dev/null
+++ b/drivers/input/gameport/fm801-gp.c
@@ -0,0 +1,159 @@
+/*
+ *  FM801 gameport driver for Linux
+ *
+ *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *   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.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+
+#define PCI_VENDOR_ID_FORTEMEDIA	0x1319
+#define PCI_DEVICE_ID_FM801_GP	0x0802
+
+#define HAVE_COOKED
+
+struct fm801_gp {
+	struct gameport *gameport;
+	struct resource *res_port;
+};
+
+#ifdef HAVE_COOKED
+static int fm801_gp_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+	unsigned short w;
+
+	w = inw(gameport->io + 2);
+	*buttons = (~w >> 14) & 0x03;
+	axes[0] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
+	w = inw(gameport->io + 4);
+	axes[1] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
+	w = inw(gameport->io + 6);
+	*buttons |= ((~w >> 14) & 0x03) << 2;
+	axes[2] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
+	w = inw(gameport->io + 8);
+	axes[3] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
+	outw(0xff, gameport->io); /* reset */
+
+        return 0;
+}
+#endif
+
+static int fm801_gp_open(struct gameport *gameport, int mode)
+{
+	switch (mode) {
+#ifdef HAVE_COOKED
+	case GAMEPORT_MODE_COOKED:
+		return 0;
+#endif
+	case GAMEPORT_MODE_RAW:
+		return 0;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+static int fm801_gp_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+	struct fm801_gp *gp;
+	struct gameport *port;
+	int error;
+
+	gp = kzalloc(sizeof(struct fm801_gp), GFP_KERNEL);
+	port = gameport_allocate_port();
+	if (!gp || !port) {
+		printk(KERN_ERR "fm801-gp: Memory allocation failed\n");
+		error = -ENOMEM;
+		goto err_out_free;
+	}
+
+	error = pci_enable_device(pci);
+	if (error)
+		goto err_out_free;
+
+	port->open = fm801_gp_open;
+#ifdef HAVE_COOKED
+	port->cooked_read = fm801_gp_cooked_read;
+#endif
+	gameport_set_name(port, "FM801");
+	gameport_set_phys(port, "pci%s/gameport0", pci_name(pci));
+	port->dev.parent = &pci->dev;
+	port->io = pci_resource_start(pci, 0);
+
+	gp->gameport = port;
+	gp->res_port = request_region(port->io, 0x10, "FM801 GP");
+	if (!gp->res_port) {
+		printk(KERN_DEBUG "fm801-gp: unable to grab region 0x%x-0x%x\n",
+			port->io, port->io + 0x0f);
+		error = -EBUSY;
+		goto err_out_disable_dev;
+	}
+
+	pci_set_drvdata(pci, gp);
+
+	outb(0x60, port->io + 0x0d); /* enable joystick 1 and 2 */
+	gameport_register_port(port);
+
+	return 0;
+
+ err_out_disable_dev:
+	pci_disable_device(pci);
+ err_out_free:
+	gameport_free_port(port);
+	kfree(gp);
+	return error;
+}
+
+static void fm801_gp_remove(struct pci_dev *pci)
+{
+	struct fm801_gp *gp = pci_get_drvdata(pci);
+
+	gameport_unregister_port(gp->gameport);
+	release_resource(gp->res_port);
+	kfree(gp);
+
+	pci_disable_device(pci);
+}
+
+static const struct pci_device_id fm801_gp_id_table[] = {
+	{ PCI_VENDOR_ID_FORTEMEDIA, PCI_DEVICE_ID_FM801_GP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0  },
+	{ 0 }
+};
+MODULE_DEVICE_TABLE(pci, fm801_gp_id_table);
+
+static struct pci_driver fm801_gp_driver = {
+	.name =		"FM801_gameport",
+	.id_table =	fm801_gp_id_table,
+	.probe =	fm801_gp_probe,
+	.remove =	fm801_gp_remove,
+};
+
+module_pci_driver(fm801_gp_driver);
+
+MODULE_DESCRIPTION("FM801 gameport driver");
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c
new file mode 100644
index 0000000..4a2a9e3
--- /dev/null
+++ b/drivers/input/gameport/gameport.c
@@ -0,0 +1,862 @@
+/*
+ * Generic gameport layer
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 2005 Dmitry Torokhov
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>	/* HZ */
+#include <linux/mutex.h>
+#include <linux/timekeeping.h>
+
+/*#include <asm/io.h>*/
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Generic gameport layer");
+MODULE_LICENSE("GPL");
+
+static bool use_ktime = true;
+module_param(use_ktime, bool, 0400);
+MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed");
+
+/*
+ * gameport_mutex protects entire gameport subsystem and is taken
+ * every time gameport port or driver registrered or unregistered.
+ */
+static DEFINE_MUTEX(gameport_mutex);
+
+static LIST_HEAD(gameport_list);
+
+static struct bus_type gameport_bus;
+
+static void gameport_add_port(struct gameport *gameport);
+static void gameport_attach_driver(struct gameport_driver *drv);
+static void gameport_reconnect_port(struct gameport *gameport);
+static void gameport_disconnect_port(struct gameport *gameport);
+
+#if defined(__i386__)
+
+#include <linux/i8253.h>
+
+#define DELTA(x,y)      ((y)-(x)+((y)<(x)?1193182/HZ:0))
+#define GET_TIME(x)     do { x = get_time_pit(); } while (0)
+
+static unsigned int get_time_pit(void)
+{
+	unsigned long flags;
+	unsigned int count;
+
+	raw_spin_lock_irqsave(&i8253_lock, flags);
+	outb_p(0x00, 0x43);
+	count = inb_p(0x40);
+	count |= inb_p(0x40) << 8;
+	raw_spin_unlock_irqrestore(&i8253_lock, flags);
+
+	return count;
+}
+
+#endif
+
+
+
+/*
+ * gameport_measure_speed() measures the gameport i/o speed.
+ */
+
+static int gameport_measure_speed(struct gameport *gameport)
+{
+	unsigned int i, t, tx;
+	u64 t1, t2, t3;
+	unsigned long flags;
+
+	if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
+		return 0;
+
+	tx = ~0;
+
+	for (i = 0; i < 50; i++) {
+		local_irq_save(flags);
+		t1 = ktime_get_ns();
+		for (t = 0; t < 50; t++)
+			gameport_read(gameport);
+		t2 = ktime_get_ns();
+		t3 = ktime_get_ns();
+		local_irq_restore(flags);
+		udelay(i * 10);
+		t = (t2 - t1) - (t3 - t2);
+		if (t < tx)
+			tx = t;
+	}
+
+	gameport_close(gameport);
+	t = 1000000 * 50;
+	if (tx)
+		t /= tx;
+	return t;
+}
+
+static int old_gameport_measure_speed(struct gameport *gameport)
+{
+#if defined(__i386__)
+
+	unsigned int i, t, t1, t2, t3, tx;
+	unsigned long flags;
+
+	if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
+		return 0;
+
+	tx = 1 << 30;
+
+	for(i = 0; i < 50; i++) {
+		local_irq_save(flags);
+		GET_TIME(t1);
+		for (t = 0; t < 50; t++) gameport_read(gameport);
+		GET_TIME(t2);
+		GET_TIME(t3);
+		local_irq_restore(flags);
+		udelay(i * 10);
+		if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t;
+	}
+
+	gameport_close(gameport);
+	return 59659 / (tx < 1 ? 1 : tx);
+
+#elif defined (__x86_64__)
+
+	unsigned int i, t;
+	unsigned long tx, t1, t2, flags;
+
+	if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
+		return 0;
+
+	tx = 1 << 30;
+
+	for(i = 0; i < 50; i++) {
+		local_irq_save(flags);
+		t1 = rdtsc();
+		for (t = 0; t < 50; t++) gameport_read(gameport);
+		t2 = rdtsc();
+		local_irq_restore(flags);
+		udelay(i * 10);
+		if (t2 - t1 < tx) tx = t2 - t1;
+	}
+
+	gameport_close(gameport);
+	return (this_cpu_read(cpu_info.loops_per_jiffy) *
+		(unsigned long)HZ / (1000 / 50)) / (tx < 1 ? 1 : tx);
+
+#else
+
+	unsigned int j, t = 0;
+
+	if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
+		return 0;
+
+	j = jiffies; while (j == jiffies);
+	j = jiffies; while (j == jiffies) { t++; gameport_read(gameport); }
+
+	gameport_close(gameport);
+	return t * HZ / 1000;
+
+#endif
+}
+
+void gameport_start_polling(struct gameport *gameport)
+{
+	spin_lock(&gameport->timer_lock);
+
+	if (!gameport->poll_cnt++) {
+		BUG_ON(!gameport->poll_handler);
+		BUG_ON(!gameport->poll_interval);
+		mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval));
+	}
+
+	spin_unlock(&gameport->timer_lock);
+}
+EXPORT_SYMBOL(gameport_start_polling);
+
+void gameport_stop_polling(struct gameport *gameport)
+{
+	spin_lock(&gameport->timer_lock);
+
+	if (!--gameport->poll_cnt)
+		del_timer(&gameport->poll_timer);
+
+	spin_unlock(&gameport->timer_lock);
+}
+EXPORT_SYMBOL(gameport_stop_polling);
+
+static void gameport_run_poll_handler(unsigned long d)
+{
+	struct gameport *gameport = (struct gameport *)d;
+
+	gameport->poll_handler(gameport);
+	if (gameport->poll_cnt)
+		mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval));
+}
+
+/*
+ * Basic gameport -> driver core mappings
+ */
+
+static int gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv)
+{
+	int error;
+
+	gameport->dev.driver = &drv->driver;
+	if (drv->connect(gameport, drv)) {
+		gameport->dev.driver = NULL;
+		return -ENODEV;
+	}
+
+	error = device_bind_driver(&gameport->dev);
+	if (error) {
+		dev_warn(&gameport->dev,
+			 "device_bind_driver() failed for %s (%s) and %s, error: %d\n",
+			gameport->phys, gameport->name,
+			drv->description, error);
+		drv->disconnect(gameport);
+		gameport->dev.driver = NULL;
+		return error;
+	}
+
+	return 0;
+}
+
+static void gameport_find_driver(struct gameport *gameport)
+{
+	int error;
+
+	error = device_attach(&gameport->dev);
+	if (error < 0)
+		dev_warn(&gameport->dev,
+			 "device_attach() failed for %s (%s), error: %d\n",
+			 gameport->phys, gameport->name, error);
+}
+
+
+/*
+ * Gameport event processing.
+ */
+
+enum gameport_event_type {
+	GAMEPORT_REGISTER_PORT,
+	GAMEPORT_ATTACH_DRIVER,
+};
+
+struct gameport_event {
+	enum gameport_event_type type;
+	void *object;
+	struct module *owner;
+	struct list_head node;
+};
+
+static DEFINE_SPINLOCK(gameport_event_lock);	/* protects gameport_event_list */
+static LIST_HEAD(gameport_event_list);
+
+static struct gameport_event *gameport_get_event(void)
+{
+	struct gameport_event *event = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gameport_event_lock, flags);
+
+	if (!list_empty(&gameport_event_list)) {
+		event = list_first_entry(&gameport_event_list,
+					 struct gameport_event, node);
+		list_del_init(&event->node);
+	}
+
+	spin_unlock_irqrestore(&gameport_event_lock, flags);
+	return event;
+}
+
+static void gameport_free_event(struct gameport_event *event)
+{
+	module_put(event->owner);
+	kfree(event);
+}
+
+static void gameport_remove_duplicate_events(struct gameport_event *event)
+{
+	struct gameport_event *e, *next;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gameport_event_lock, flags);
+
+	list_for_each_entry_safe(e, next, &gameport_event_list, node) {
+		if (event->object == e->object) {
+			/*
+			 * If this event is of different type we should not
+			 * look further - we only suppress duplicate events
+			 * that were sent back-to-back.
+			 */
+			if (event->type != e->type)
+				break;
+
+			list_del_init(&e->node);
+			gameport_free_event(e);
+		}
+	}
+
+	spin_unlock_irqrestore(&gameport_event_lock, flags);
+}
+
+
+static void gameport_handle_events(struct work_struct *work)
+{
+	struct gameport_event *event;
+
+	mutex_lock(&gameport_mutex);
+
+	/*
+	 * Note that we handle only one event here to give swsusp
+	 * a chance to freeze kgameportd thread. Gameport events
+	 * should be pretty rare so we are not concerned about
+	 * taking performance hit.
+	 */
+	if ((event = gameport_get_event())) {
+
+		switch (event->type) {
+
+		case GAMEPORT_REGISTER_PORT:
+			gameport_add_port(event->object);
+			break;
+
+		case GAMEPORT_ATTACH_DRIVER:
+			gameport_attach_driver(event->object);
+			break;
+		}
+
+		gameport_remove_duplicate_events(event);
+		gameport_free_event(event);
+	}
+
+	mutex_unlock(&gameport_mutex);
+}
+
+static DECLARE_WORK(gameport_event_work, gameport_handle_events);
+
+static int gameport_queue_event(void *object, struct module *owner,
+				enum gameport_event_type event_type)
+{
+	unsigned long flags;
+	struct gameport_event *event;
+	int retval = 0;
+
+	spin_lock_irqsave(&gameport_event_lock, flags);
+
+	/*
+	 * Scan event list for the other events for the same gameport port,
+	 * starting with the most recent one. If event is the same we
+	 * do not need add new one. If event is of different type we
+	 * need to add this event and should not look further because
+	 * we need to preserve sequence of distinct events.
+	 */
+	list_for_each_entry_reverse(event, &gameport_event_list, node) {
+		if (event->object == object) {
+			if (event->type == event_type)
+				goto out;
+			break;
+		}
+	}
+
+	event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC);
+	if (!event) {
+		pr_err("Not enough memory to queue event %d\n", event_type);
+		retval = -ENOMEM;
+		goto out;
+	}
+
+	if (!try_module_get(owner)) {
+		pr_warning("Can't get module reference, dropping event %d\n",
+			   event_type);
+		kfree(event);
+		retval = -EINVAL;
+		goto out;
+	}
+
+	event->type = event_type;
+	event->object = object;
+	event->owner = owner;
+
+	list_add_tail(&event->node, &gameport_event_list);
+	queue_work(system_long_wq, &gameport_event_work);
+
+out:
+	spin_unlock_irqrestore(&gameport_event_lock, flags);
+	return retval;
+}
+
+/*
+ * Remove all events that have been submitted for a given object,
+ * be it a gameport port or a driver.
+ */
+static void gameport_remove_pending_events(void *object)
+{
+	struct gameport_event *event, *next;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gameport_event_lock, flags);
+
+	list_for_each_entry_safe(event, next, &gameport_event_list, node) {
+		if (event->object == object) {
+			list_del_init(&event->node);
+			gameport_free_event(event);
+		}
+	}
+
+	spin_unlock_irqrestore(&gameport_event_lock, flags);
+}
+
+/*
+ * Destroy child gameport port (if any) that has not been fully registered yet.
+ *
+ * Note that we rely on the fact that port can have only one child and therefore
+ * only one child registration request can be pending. Additionally, children
+ * are registered by driver's connect() handler so there can't be a grandchild
+ * pending registration together with a child.
+ */
+static struct gameport *gameport_get_pending_child(struct gameport *parent)
+{
+	struct gameport_event *event;
+	struct gameport *gameport, *child = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gameport_event_lock, flags);
+
+	list_for_each_entry(event, &gameport_event_list, node) {
+		if (event->type == GAMEPORT_REGISTER_PORT) {
+			gameport = event->object;
+			if (gameport->parent == parent) {
+				child = gameport;
+				break;
+			}
+		}
+	}
+
+	spin_unlock_irqrestore(&gameport_event_lock, flags);
+	return child;
+}
+
+/*
+ * Gameport port operations
+ */
+
+static ssize_t gameport_description_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct gameport *gameport = to_gameport_port(dev);
+
+	return sprintf(buf, "%s\n", gameport->name);
+}
+static DEVICE_ATTR(description, S_IRUGO, gameport_description_show, NULL);
+
+static ssize_t drvctl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct gameport *gameport = to_gameport_port(dev);
+	struct device_driver *drv;
+	int error;
+
+	error = mutex_lock_interruptible(&gameport_mutex);
+	if (error)
+		return error;
+
+	if (!strncmp(buf, "none", count)) {
+		gameport_disconnect_port(gameport);
+	} else if (!strncmp(buf, "reconnect", count)) {
+		gameport_reconnect_port(gameport);
+	} else if (!strncmp(buf, "rescan", count)) {
+		gameport_disconnect_port(gameport);
+		gameport_find_driver(gameport);
+	} else if ((drv = driver_find(buf, &gameport_bus)) != NULL) {
+		gameport_disconnect_port(gameport);
+		error = gameport_bind_driver(gameport, to_gameport_driver(drv));
+	} else {
+		error = -EINVAL;
+	}
+
+	mutex_unlock(&gameport_mutex);
+
+	return error ? error : count;
+}
+static DEVICE_ATTR_WO(drvctl);
+
+static struct attribute *gameport_device_attrs[] = {
+	&dev_attr_description.attr,
+	&dev_attr_drvctl.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(gameport_device);
+
+static void gameport_release_port(struct device *dev)
+{
+	struct gameport *gameport = to_gameport_port(dev);
+
+	kfree(gameport);
+	module_put(THIS_MODULE);
+}
+
+void gameport_set_phys(struct gameport *gameport, const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	vsnprintf(gameport->phys, sizeof(gameport->phys), fmt, args);
+	va_end(args);
+}
+EXPORT_SYMBOL(gameport_set_phys);
+
+/*
+ * Prepare gameport port for registration.
+ */
+static void gameport_init_port(struct gameport *gameport)
+{
+	static atomic_t gameport_no = ATOMIC_INIT(-1);
+
+	__module_get(THIS_MODULE);
+
+	mutex_init(&gameport->drv_mutex);
+	device_initialize(&gameport->dev);
+	dev_set_name(&gameport->dev, "gameport%lu",
+			(unsigned long)atomic_inc_return(&gameport_no));
+	gameport->dev.bus = &gameport_bus;
+	gameport->dev.release = gameport_release_port;
+	if (gameport->parent)
+		gameport->dev.parent = &gameport->parent->dev;
+
+	INIT_LIST_HEAD(&gameport->node);
+	spin_lock_init(&gameport->timer_lock);
+	init_timer(&gameport->poll_timer);
+	gameport->poll_timer.function = gameport_run_poll_handler;
+	gameport->poll_timer.data = (unsigned long)gameport;
+}
+
+/*
+ * Complete gameport port registration.
+ * Driver core will attempt to find appropriate driver for the port.
+ */
+static void gameport_add_port(struct gameport *gameport)
+{
+	int error;
+
+	if (gameport->parent)
+		gameport->parent->child = gameport;
+
+	gameport->speed = use_ktime ?
+		gameport_measure_speed(gameport) :
+		old_gameport_measure_speed(gameport);
+
+	list_add_tail(&gameport->node, &gameport_list);
+
+	if (gameport->io)
+		dev_info(&gameport->dev, "%s is %s, io %#x, speed %dkHz\n",
+			 gameport->name, gameport->phys, gameport->io, gameport->speed);
+	else
+		dev_info(&gameport->dev, "%s is %s, speed %dkHz\n",
+			gameport->name, gameport->phys, gameport->speed);
+
+	error = device_add(&gameport->dev);
+	if (error)
+		dev_err(&gameport->dev,
+			"device_add() failed for %s (%s), error: %d\n",
+			gameport->phys, gameport->name, error);
+}
+
+/*
+ * gameport_destroy_port() completes deregistration process and removes
+ * port from the system
+ */
+static void gameport_destroy_port(struct gameport *gameport)
+{
+	struct gameport *child;
+
+	child = gameport_get_pending_child(gameport);
+	if (child) {
+		gameport_remove_pending_events(child);
+		put_device(&child->dev);
+	}
+
+	if (gameport->parent) {
+		gameport->parent->child = NULL;
+		gameport->parent = NULL;
+	}
+
+	if (device_is_registered(&gameport->dev))
+		device_del(&gameport->dev);
+
+	list_del_init(&gameport->node);
+
+	gameport_remove_pending_events(gameport);
+	put_device(&gameport->dev);
+}
+
+/*
+ * Reconnect gameport port and all its children (re-initialize attached devices)
+ */
+static void gameport_reconnect_port(struct gameport *gameport)
+{
+	do {
+		if (!gameport->drv || !gameport->drv->reconnect || gameport->drv->reconnect(gameport)) {
+			gameport_disconnect_port(gameport);
+			gameport_find_driver(gameport);
+			/* Ok, old children are now gone, we are done */
+			break;
+		}
+		gameport = gameport->child;
+	} while (gameport);
+}
+
+/*
+ * gameport_disconnect_port() unbinds a port from its driver. As a side effect
+ * all child ports are unbound and destroyed.
+ */
+static void gameport_disconnect_port(struct gameport *gameport)
+{
+	struct gameport *s, *parent;
+
+	if (gameport->child) {
+		/*
+		 * Children ports should be disconnected and destroyed
+		 * first, staring with the leaf one, since we don't want
+		 * to do recursion
+		 */
+		for (s = gameport; s->child; s = s->child)
+			/* empty */;
+
+		do {
+			parent = s->parent;
+
+			device_release_driver(&s->dev);
+			gameport_destroy_port(s);
+		} while ((s = parent) != gameport);
+	}
+
+	/*
+	 * Ok, no children left, now disconnect this port
+	 */
+	device_release_driver(&gameport->dev);
+}
+
+/*
+ * Submits register request to kgameportd for subsequent execution.
+ * Note that port registration is always asynchronous.
+ */
+void __gameport_register_port(struct gameport *gameport, struct module *owner)
+{
+	gameport_init_port(gameport);
+	gameport_queue_event(gameport, owner, GAMEPORT_REGISTER_PORT);
+}
+EXPORT_SYMBOL(__gameport_register_port);
+
+/*
+ * Synchronously unregisters gameport port.
+ */
+void gameport_unregister_port(struct gameport *gameport)
+{
+	mutex_lock(&gameport_mutex);
+	gameport_disconnect_port(gameport);
+	gameport_destroy_port(gameport);
+	mutex_unlock(&gameport_mutex);
+}
+EXPORT_SYMBOL(gameport_unregister_port);
+
+
+/*
+ * Gameport driver operations
+ */
+
+static ssize_t description_show(struct device_driver *drv, char *buf)
+{
+	struct gameport_driver *driver = to_gameport_driver(drv);
+	return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");
+}
+static DRIVER_ATTR_RO(description);
+
+static struct attribute *gameport_driver_attrs[] = {
+	&driver_attr_description.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(gameport_driver);
+
+static int gameport_driver_probe(struct device *dev)
+{
+	struct gameport *gameport = to_gameport_port(dev);
+	struct gameport_driver *drv = to_gameport_driver(dev->driver);
+
+	drv->connect(gameport, drv);
+	return gameport->drv ? 0 : -ENODEV;
+}
+
+static int gameport_driver_remove(struct device *dev)
+{
+	struct gameport *gameport = to_gameport_port(dev);
+	struct gameport_driver *drv = to_gameport_driver(dev->driver);
+
+	drv->disconnect(gameport);
+	return 0;
+}
+
+static void gameport_attach_driver(struct gameport_driver *drv)
+{
+	int error;
+
+	error = driver_attach(&drv->driver);
+	if (error)
+		pr_err("driver_attach() failed for %s, error: %d\n",
+			drv->driver.name, error);
+}
+
+int __gameport_register_driver(struct gameport_driver *drv, struct module *owner,
+				const char *mod_name)
+{
+	int error;
+
+	drv->driver.bus = &gameport_bus;
+	drv->driver.owner = owner;
+	drv->driver.mod_name = mod_name;
+
+	/*
+	 * Temporarily disable automatic binding because probing
+	 * takes long time and we are better off doing it in kgameportd
+	 */
+	drv->ignore = true;
+
+	error = driver_register(&drv->driver);
+	if (error) {
+		pr_err("driver_register() failed for %s, error: %d\n",
+			drv->driver.name, error);
+		return error;
+	}
+
+	/*
+	 * Reset ignore flag and let kgameportd bind the driver to free ports
+	 */
+	drv->ignore = false;
+	error = gameport_queue_event(drv, NULL, GAMEPORT_ATTACH_DRIVER);
+	if (error) {
+		driver_unregister(&drv->driver);
+		return error;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(__gameport_register_driver);
+
+void gameport_unregister_driver(struct gameport_driver *drv)
+{
+	struct gameport *gameport;
+
+	mutex_lock(&gameport_mutex);
+
+	drv->ignore = true;	/* so gameport_find_driver ignores it */
+	gameport_remove_pending_events(drv);
+
+start_over:
+	list_for_each_entry(gameport, &gameport_list, node) {
+		if (gameport->drv == drv) {
+			gameport_disconnect_port(gameport);
+			gameport_find_driver(gameport);
+			/* we could've deleted some ports, restart */
+			goto start_over;
+		}
+	}
+
+	driver_unregister(&drv->driver);
+
+	mutex_unlock(&gameport_mutex);
+}
+EXPORT_SYMBOL(gameport_unregister_driver);
+
+static int gameport_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct gameport_driver *gameport_drv = to_gameport_driver(drv);
+
+	return !gameport_drv->ignore;
+}
+
+static struct bus_type gameport_bus = {
+	.name		= "gameport",
+	.dev_groups	= gameport_device_groups,
+	.drv_groups	= gameport_driver_groups,
+	.match		= gameport_bus_match,
+	.probe		= gameport_driver_probe,
+	.remove		= gameport_driver_remove,
+};
+
+static void gameport_set_drv(struct gameport *gameport, struct gameport_driver *drv)
+{
+	mutex_lock(&gameport->drv_mutex);
+	gameport->drv = drv;
+	mutex_unlock(&gameport->drv_mutex);
+}
+
+int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mode)
+{
+	if (gameport->open) {
+		if (gameport->open(gameport, mode)) {
+			return -1;
+		}
+	} else {
+		if (mode != GAMEPORT_MODE_RAW)
+			return -1;
+	}
+
+	gameport_set_drv(gameport, drv);
+	return 0;
+}
+EXPORT_SYMBOL(gameport_open);
+
+void gameport_close(struct gameport *gameport)
+{
+	del_timer_sync(&gameport->poll_timer);
+	gameport->poll_handler = NULL;
+	gameport->poll_interval = 0;
+	gameport_set_drv(gameport, NULL);
+	if (gameport->close)
+		gameport->close(gameport);
+}
+EXPORT_SYMBOL(gameport_close);
+
+static int __init gameport_init(void)
+{
+	int error;
+
+	error = bus_register(&gameport_bus);
+	if (error) {
+		pr_err("failed to register gameport bus, error: %d\n", error);
+		return error;
+	}
+
+
+	return 0;
+}
+
+static void __exit gameport_exit(void)
+{
+	bus_unregister(&gameport_bus);
+
+	/*
+	 * There should not be any outstanding events but work may
+	 * still be scheduled so simply cancel it.
+	 */
+	cancel_work_sync(&gameport_event_work);
+}
+
+subsys_initcall(gameport_init);
+module_exit(gameport_exit);
diff --git a/drivers/input/gameport/lightning.c b/drivers/input/gameport/lightning.c
new file mode 100644
index 0000000..85d6ee0
--- /dev/null
+++ b/drivers/input/gameport/lightning.c
@@ -0,0 +1,341 @@
+/*
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * PDPI Lightning 4 gamecard driver for Linux.
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+
+#define L4_PORT			0x201
+#define L4_SELECT_ANALOG	0xa4
+#define L4_SELECT_DIGITAL	0xa5
+#define L4_SELECT_SECONDARY	0xa6
+#define L4_CMD_ID		0x80
+#define L4_CMD_GETCAL		0x92
+#define L4_CMD_SETCAL		0x93
+#define L4_ID			0x04
+#define L4_BUSY			0x01
+#define L4_TIMEOUT		80	/* 80 us */
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("PDPI Lightning 4 gamecard driver");
+MODULE_LICENSE("GPL");
+
+struct l4 {
+	struct gameport *gameport;
+	unsigned char port;
+};
+
+static struct l4 l4_ports[8];
+
+/*
+ * l4_wait_ready() waits for the L4 to become ready.
+ */
+
+static int l4_wait_ready(void)
+{
+	unsigned int t = L4_TIMEOUT;
+
+	while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--;
+	return -(t <= 0);
+}
+
+/*
+ * l4_cooked_read() reads data from the Lightning 4.
+ */
+
+static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+	struct l4 *l4 = gameport->port_data;
+	unsigned char status;
+	int i, result = -1;
+
+	outb(L4_SELECT_ANALOG, L4_PORT);
+	outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT);
+
+	if (inb(L4_PORT) & L4_BUSY) goto fail;
+	outb(l4->port & 3, L4_PORT);
+
+	if (l4_wait_ready()) goto fail;
+	status = inb(L4_PORT);
+
+	for (i = 0; i < 4; i++)
+		if (status & (1 << i)) {
+			if (l4_wait_ready()) goto fail;
+			axes[i] = inb(L4_PORT);
+			if (axes[i] > 252) axes[i] = -1;
+		}
+
+	if (status & 0x10) {
+		if (l4_wait_ready()) goto fail;
+		*buttons = inb(L4_PORT) & 0x0f;
+	}
+
+	result = 0;
+
+fail:	outb(L4_SELECT_ANALOG, L4_PORT);
+	return result;
+}
+
+static int l4_open(struct gameport *gameport, int mode)
+{
+	struct l4 *l4 = gameport->port_data;
+
+        if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED)
+		return -1;
+	outb(L4_SELECT_ANALOG, L4_PORT);
+	return 0;
+}
+
+/*
+ * l4_getcal() reads the L4 with calibration values.
+ */
+
+static int l4_getcal(int port, int *cal)
+{
+	int i, result = -1;
+
+	outb(L4_SELECT_ANALOG, L4_PORT);
+	outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
+	if (inb(L4_PORT) & L4_BUSY)
+		goto out;
+
+	outb(L4_CMD_GETCAL, L4_PORT);
+	if (l4_wait_ready())
+		goto out;
+
+	if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
+		goto out;
+
+	if (l4_wait_ready())
+		goto out;
+        outb(port & 3, L4_PORT);
+
+	for (i = 0; i < 4; i++) {
+		if (l4_wait_ready())
+			goto out;
+		cal[i] = inb(L4_PORT);
+	}
+
+	result = 0;
+
+out:	outb(L4_SELECT_ANALOG, L4_PORT);
+	return result;
+}
+
+/*
+ * l4_setcal() programs the L4 with calibration values.
+ */
+
+static int l4_setcal(int port, int *cal)
+{
+	int i, result = -1;
+
+	outb(L4_SELECT_ANALOG, L4_PORT);
+	outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
+	if (inb(L4_PORT) & L4_BUSY)
+		goto out;
+
+	outb(L4_CMD_SETCAL, L4_PORT);
+	if (l4_wait_ready())
+		goto out;
+
+	if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
+		goto out;
+
+	if (l4_wait_ready())
+		goto out;
+        outb(port & 3, L4_PORT);
+
+	for (i = 0; i < 4; i++) {
+		if (l4_wait_ready())
+			goto out;
+		outb(cal[i], L4_PORT);
+	}
+
+	result = 0;
+
+out:	outb(L4_SELECT_ANALOG, L4_PORT);
+	return result;
+}
+
+/*
+ * l4_calibrate() calibrates the L4 for the attached device, so
+ * that the device's resistance fits into the L4's 8-bit range.
+ */
+
+static int l4_calibrate(struct gameport *gameport, int *axes, int *max)
+{
+	int i, t;
+	int cal[4];
+	struct l4 *l4 = gameport->port_data;
+
+	if (l4_getcal(l4->port, cal))
+		return -1;
+
+	for (i = 0; i < 4; i++) {
+		t = (max[i] * cal[i]) / 200;
+		t = (t < 1) ? 1 : ((t > 255) ? 255 : t);
+		axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t;
+		axes[i] = (axes[i] > 252) ? 252 : axes[i];
+		cal[i] = t;
+	}
+
+	if (l4_setcal(l4->port, cal))
+		return -1;
+
+	return 0;
+}
+
+static int __init l4_create_ports(int card_no)
+{
+	struct l4 *l4;
+	struct gameport *port;
+	int i, idx;
+
+	for (i = 0; i < 4; i++) {
+
+		idx = card_no * 4 + i;
+		l4 = &l4_ports[idx];
+
+		if (!(l4->gameport = port = gameport_allocate_port())) {
+			printk(KERN_ERR "lightning: Memory allocation failed\n");
+			while (--i >= 0) {
+				gameport_free_port(l4->gameport);
+				l4->gameport = NULL;
+			}
+			return -ENOMEM;
+		}
+		l4->port = idx;
+
+		port->port_data = l4;
+		port->open = l4_open;
+		port->cooked_read = l4_cooked_read;
+		port->calibrate = l4_calibrate;
+
+		gameport_set_name(port, "PDPI Lightning 4");
+		gameport_set_phys(port, "isa%04x/gameport%d", L4_PORT, idx);
+
+		if (idx == 0)
+			port->io = L4_PORT;
+	}
+
+	return 0;
+}
+
+static int __init l4_add_card(int card_no)
+{
+	int cal[4] = { 255, 255, 255, 255 };
+	int i, rev, result;
+	struct l4 *l4;
+
+	outb(L4_SELECT_ANALOG, L4_PORT);
+	outb(L4_SELECT_DIGITAL + card_no, L4_PORT);
+
+	if (inb(L4_PORT) & L4_BUSY)
+		return -1;
+	outb(L4_CMD_ID, L4_PORT);
+
+	if (l4_wait_ready())
+		return -1;
+
+	if (inb(L4_PORT) != L4_SELECT_DIGITAL + card_no)
+		return -1;
+
+	if (l4_wait_ready())
+		return -1;
+	if (inb(L4_PORT) != L4_ID)
+		return -1;
+
+	if (l4_wait_ready())
+		return -1;
+	rev = inb(L4_PORT);
+
+	if (!rev)
+		return -1;
+
+	result = l4_create_ports(card_no);
+	if (result)
+		return result;
+
+	printk(KERN_INFO "gameport: PDPI Lightning 4 %s card v%d.%d at %#x\n",
+		card_no ? "secondary" : "primary", rev >> 4, rev, L4_PORT);
+
+	for (i = 0; i < 4; i++) {
+		l4 = &l4_ports[card_no * 4 + i];
+
+		if (rev > 0x28)		/* on 2.9+ the setcal command works correctly */
+			l4_setcal(l4->port, cal);
+		gameport_register_port(l4->gameport);
+	}
+
+	return 0;
+}
+
+static int __init l4_init(void)
+{
+	int i, cards = 0;
+
+	if (!request_region(L4_PORT, 1, "lightning"))
+		return -EBUSY;
+
+	for (i = 0; i < 2; i++)
+		if (l4_add_card(i) == 0)
+			cards++;
+
+	outb(L4_SELECT_ANALOG, L4_PORT);
+
+	if (!cards) {
+		release_region(L4_PORT, 1);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit l4_exit(void)
+{
+	int i;
+	int cal[4] = { 59, 59, 59, 59 };
+
+	for (i = 0; i < 8; i++)
+		if (l4_ports[i].gameport) {
+			l4_setcal(l4_ports[i].port, cal);
+			gameport_unregister_port(l4_ports[i].gameport);
+		}
+
+	outb(L4_SELECT_ANALOG, L4_PORT);
+	release_region(L4_PORT, 1);
+}
+
+module_init(l4_init);
+module_exit(l4_exit);
diff --git a/drivers/input/gameport/ns558.c b/drivers/input/gameport/ns558.c
new file mode 100644
index 0000000..7c21784
--- /dev/null
+++ b/drivers/input/gameport/ns558.c
@@ -0,0 +1,286 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *  Copyright (c) 1999 Brian Gerst
+ */
+
+/*
+ * NS558 based standard IBM game port driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gameport.h>
+#include <linux/slab.h>
+#include <linux/pnp.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Classic gameport (ISA/PnP) driver");
+MODULE_LICENSE("GPL");
+
+static int ns558_isa_portlist[] = { 0x201, 0x200, 0x202, 0x203, 0x204, 0x205, 0x207, 0x209,
+				    0x20b, 0x20c, 0x20e, 0x20f, 0x211, 0x219, 0x101, 0 };
+
+struct ns558 {
+	int type;
+	int io;
+	int size;
+	struct pnp_dev *dev;
+	struct gameport *gameport;
+	struct list_head node;
+};
+
+static LIST_HEAD(ns558_list);
+
+/*
+ * ns558_isa_probe() tries to find an isa gameport at the
+ * specified address, and also checks for mirrors.
+ * A joystick must be attached for this to work.
+ */
+
+static int ns558_isa_probe(int io)
+{
+	int i, j, b;
+	unsigned char c, u, v;
+	struct ns558 *ns558;
+	struct gameport *port;
+
+/*
+ * No one should be using this address.
+ */
+
+	if (!request_region(io, 1, "ns558-isa"))
+		return -EBUSY;
+
+/*
+ * We must not be able to write arbitrary values to the port.
+ * The lower two axis bits must be 1 after a write.
+ */
+
+	c = inb(io);
+	outb(~c & ~3, io);
+	if (~(u = v = inb(io)) & 3) {
+		outb(c, io);
+		release_region(io, 1);
+		return -ENODEV;
+	}
+/*
+ * After a trigger, there must be at least some bits changing.
+ */
+
+	for (i = 0; i < 1000; i++) v &= inb(io);
+
+	if (u == v) {
+		outb(c, io);
+		release_region(io, 1);
+		return -ENODEV;
+	}
+	msleep(3);
+/*
+ * After some time (4ms) the axes shouldn't change anymore.
+ */
+
+	u = inb(io);
+	for (i = 0; i < 1000; i++)
+		if ((u ^ inb(io)) & 0xf) {
+			outb(c, io);
+			release_region(io, 1);
+			return -ENODEV;
+		}
+/*
+ * And now find the number of mirrors of the port.
+ */
+
+	for (i = 1; i < 5; i++) {
+
+		release_region(io & (-1 << (i - 1)), (1 << (i - 1)));
+
+		if (!request_region(io & (-1 << i), (1 << i), "ns558-isa"))
+			break;				/* Don't disturb anyone */
+
+		outb(0xff, io & (-1 << i));
+		for (j = b = 0; j < 1000; j++)
+			if (inb(io & (-1 << i)) != inb((io & (-1 << i)) + (1 << i) - 1)) b++;
+		msleep(3);
+
+		if (b > 300) {				/* We allow 30% difference */
+			release_region(io & (-1 << i), (1 << i));
+			break;
+		}
+	}
+
+	i--;
+
+	if (i != 4) {
+		if (!request_region(io & (-1 << i), (1 << i), "ns558-isa"))
+			return -EBUSY;
+	}
+
+	ns558 = kzalloc(sizeof(struct ns558), GFP_KERNEL);
+	port = gameport_allocate_port();
+	if (!ns558 || !port) {
+		printk(KERN_ERR "ns558: Memory allocation failed.\n");
+		release_region(io & (-1 << i), (1 << i));
+		kfree(ns558);
+		gameport_free_port(port);
+		return -ENOMEM;
+	}
+
+	ns558->io = io;
+	ns558->size = 1 << i;
+	ns558->gameport = port;
+
+	port->io = io;
+	gameport_set_name(port, "NS558 ISA Gameport");
+	gameport_set_phys(port, "isa%04x/gameport0", io & (-1 << i));
+
+	gameport_register_port(port);
+
+	list_add(&ns558->node, &ns558_list);
+
+	return 0;
+}
+
+#ifdef CONFIG_PNP
+
+static const struct pnp_device_id pnp_devids[] = {
+	{ .id = "@P@0001", .driver_data = 0 }, /* ALS 100 */
+	{ .id = "@P@0020", .driver_data = 0 }, /* ALS 200 */
+	{ .id = "@P@1001", .driver_data = 0 }, /* ALS 100+ */
+	{ .id = "@P@2001", .driver_data = 0 }, /* ALS 120 */
+	{ .id = "ASB16fd", .driver_data = 0 }, /* AdLib NSC16 */
+	{ .id = "AZT3001", .driver_data = 0 }, /* AZT1008 */
+	{ .id = "CDC0001", .driver_data = 0 }, /* Opl3-SAx */
+	{ .id = "CSC0001", .driver_data = 0 }, /* CS4232 */
+	{ .id = "CSC000f", .driver_data = 0 }, /* CS4236 */
+	{ .id = "CSC0101", .driver_data = 0 }, /* CS4327 */
+	{ .id = "CTL7001", .driver_data = 0 }, /* SB16 */
+	{ .id = "CTL7002", .driver_data = 0 }, /* AWE64 */
+	{ .id = "CTL7005", .driver_data = 0 }, /* Vibra16 */
+	{ .id = "ENS2020", .driver_data = 0 }, /* SoundscapeVIVO */
+	{ .id = "ESS0001", .driver_data = 0 }, /* ES1869 */
+	{ .id = "ESS0005", .driver_data = 0 }, /* ES1878 */
+	{ .id = "ESS6880", .driver_data = 0 }, /* ES688 */
+	{ .id = "IBM0012", .driver_data = 0 }, /* CS4232 */
+	{ .id = "OPT0001", .driver_data = 0 }, /* OPTi Audio16 */
+	{ .id = "YMH0006", .driver_data = 0 }, /* Opl3-SA */
+	{ .id = "YMH0022", .driver_data = 0 }, /* Opl3-SAx */
+	{ .id = "PNPb02f", .driver_data = 0 }, /* Generic */
+	{ .id = "", },
+};
+
+MODULE_DEVICE_TABLE(pnp, pnp_devids);
+
+static int ns558_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *did)
+{
+	int ioport, iolen;
+	struct ns558 *ns558;
+	struct gameport *port;
+
+	if (!pnp_port_valid(dev, 0)) {
+		printk(KERN_WARNING "ns558: No i/o ports on a gameport? Weird\n");
+		return -ENODEV;
+	}
+
+	ioport = pnp_port_start(dev, 0);
+	iolen = pnp_port_len(dev, 0);
+
+	if (!request_region(ioport, iolen, "ns558-pnp"))
+		return -EBUSY;
+
+	ns558 = kzalloc(sizeof(struct ns558), GFP_KERNEL);
+	port = gameport_allocate_port();
+	if (!ns558 || !port) {
+		printk(KERN_ERR "ns558: Memory allocation failed\n");
+		kfree(ns558);
+		gameport_free_port(port);
+		return -ENOMEM;
+	}
+
+	ns558->io = ioport;
+	ns558->size = iolen;
+	ns558->dev = dev;
+	ns558->gameport = port;
+
+	gameport_set_name(port, "NS558 PnP Gameport");
+	gameport_set_phys(port, "pnp%s/gameport0", dev_name(&dev->dev));
+	port->dev.parent = &dev->dev;
+	port->io = ioport;
+
+	gameport_register_port(port);
+
+	list_add_tail(&ns558->node, &ns558_list);
+	return 0;
+}
+
+static struct pnp_driver ns558_pnp_driver = {
+	.name		= "ns558",
+	.id_table	= pnp_devids,
+	.probe		= ns558_pnp_probe,
+};
+
+#else
+
+static struct pnp_driver ns558_pnp_driver;
+
+#endif
+
+static int __init ns558_init(void)
+{
+	int i = 0;
+	int error;
+
+	error = pnp_register_driver(&ns558_pnp_driver);
+	if (error && error != -ENODEV)	/* should be ENOSYS really */
+		return error;
+
+/*
+ * Probe ISA ports after PnP, so that PnP ports that are already
+ * enabled get detected as PnP. This may be suboptimal in multi-device
+ * configurations, but saves hassle with simple setups.
+ */
+
+	while (ns558_isa_portlist[i])
+		ns558_isa_probe(ns558_isa_portlist[i++]);
+
+	return list_empty(&ns558_list) && error ? -ENODEV : 0;
+}
+
+static void __exit ns558_exit(void)
+{
+	struct ns558 *ns558, *safe;
+
+	list_for_each_entry_safe(ns558, safe, &ns558_list, node) {
+		gameport_unregister_port(ns558->gameport);
+		release_region(ns558->io & ~(ns558->size - 1), ns558->size);
+		kfree(ns558);
+	}
+
+	pnp_unregister_driver(&ns558_pnp_driver);
+}
+
+module_init(ns558_init);
+module_exit(ns558_exit);
diff --git a/drivers/input/input-compat.c b/drivers/input/input-compat.c
new file mode 100644
index 0000000..64ca711
--- /dev/null
+++ b/drivers/input/input-compat.c
@@ -0,0 +1,136 @@
+/*
+ * 32bit compatibility wrappers for the input subsystem.
+ *
+ * Very heavily based on evdev.c - Copyright (c) 1999-2002 Vojtech Pavlik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/export.h>
+#include <asm/uaccess.h>
+#include "input-compat.h"
+
+#ifdef CONFIG_COMPAT
+
+int input_event_from_user(const char __user *buffer,
+			  struct input_event *event)
+{
+	if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
+		struct input_event_compat compat_event;
+
+		if (copy_from_user(&compat_event, buffer,
+				   sizeof(struct input_event_compat)))
+			return -EFAULT;
+
+		event->time.tv_sec = compat_event.time.tv_sec;
+		event->time.tv_usec = compat_event.time.tv_usec;
+		event->type = compat_event.type;
+		event->code = compat_event.code;
+		event->value = compat_event.value;
+
+	} else {
+		if (copy_from_user(event, buffer, sizeof(struct input_event)))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+int input_event_to_user(char __user *buffer,
+			const struct input_event *event)
+{
+	if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
+		struct input_event_compat compat_event;
+
+		compat_event.time.tv_sec = event->time.tv_sec;
+		compat_event.time.tv_usec = event->time.tv_usec;
+		compat_event.type = event->type;
+		compat_event.code = event->code;
+		compat_event.value = event->value;
+
+		if (copy_to_user(buffer, &compat_event,
+				 sizeof(struct input_event_compat)))
+			return -EFAULT;
+
+	} else {
+		if (copy_to_user(buffer, event, sizeof(struct input_event)))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+int input_ff_effect_from_user(const char __user *buffer, size_t size,
+			      struct ff_effect *effect)
+{
+	if (INPUT_COMPAT_TEST) {
+		struct ff_effect_compat *compat_effect;
+
+		if (size != sizeof(struct ff_effect_compat))
+			return -EINVAL;
+
+		/*
+		 * It so happens that the pointer which needs to be changed
+		 * is the last field in the structure, so we can retrieve the
+		 * whole thing and replace just the pointer.
+		 */
+		compat_effect = (struct ff_effect_compat *)effect;
+
+		if (copy_from_user(compat_effect, buffer,
+				   sizeof(struct ff_effect_compat)))
+			return -EFAULT;
+
+		if (compat_effect->type == FF_PERIODIC &&
+		    compat_effect->u.periodic.waveform == FF_CUSTOM)
+			effect->u.periodic.custom_data =
+				compat_ptr(compat_effect->u.periodic.custom_data);
+	} else {
+		if (size != sizeof(struct ff_effect))
+			return -EINVAL;
+
+		if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+#else
+
+int input_event_from_user(const char __user *buffer,
+			 struct input_event *event)
+{
+	if (copy_from_user(event, buffer, sizeof(struct input_event)))
+		return -EFAULT;
+
+	return 0;
+}
+
+int input_event_to_user(char __user *buffer,
+			const struct input_event *event)
+{
+	if (copy_to_user(buffer, event, sizeof(struct input_event)))
+		return -EFAULT;
+
+	return 0;
+}
+
+int input_ff_effect_from_user(const char __user *buffer, size_t size,
+			      struct ff_effect *effect)
+{
+	if (size != sizeof(struct ff_effect))
+		return -EINVAL;
+
+	if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
+		return -EFAULT;
+
+	return 0;
+}
+
+#endif /* CONFIG_COMPAT */
+
+EXPORT_SYMBOL_GPL(input_event_from_user);
+EXPORT_SYMBOL_GPL(input_event_to_user);
+EXPORT_SYMBOL_GPL(input_ff_effect_from_user);
diff --git a/drivers/input/input-compat.h b/drivers/input/input-compat.h
new file mode 100644
index 0000000..148f66f
--- /dev/null
+++ b/drivers/input/input-compat.h
@@ -0,0 +1,92 @@
+#ifndef _INPUT_COMPAT_H
+#define _INPUT_COMPAT_H
+
+/*
+ * 32bit compatibility wrappers for the input subsystem.
+ *
+ * Very heavily based on evdev.c - Copyright (c) 1999-2002 Vojtech Pavlik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/compiler.h>
+#include <linux/compat.h>
+#include <linux/input.h>
+
+#ifdef CONFIG_COMPAT
+
+/* Note to the author of this code: did it ever occur to
+   you why the ifdefs are needed? Think about it again. -AK */
+#if defined(CONFIG_X86_64) || defined(CONFIG_TILE)
+#  define INPUT_COMPAT_TEST is_compat_task()
+#elif defined(CONFIG_S390)
+#  define INPUT_COMPAT_TEST test_thread_flag(TIF_31BIT)
+#elif defined(CONFIG_MIPS)
+#  define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT_ADDR)
+#else
+#  define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT)
+#endif
+
+struct input_event_compat {
+	struct compat_timeval time;
+	__u16 type;
+	__u16 code;
+	__s32 value;
+};
+
+struct ff_periodic_effect_compat {
+	__u16 waveform;
+	__u16 period;
+	__s16 magnitude;
+	__s16 offset;
+	__u16 phase;
+
+	struct ff_envelope envelope;
+
+	__u32 custom_len;
+	compat_uptr_t custom_data;
+};
+
+struct ff_effect_compat {
+	__u16 type;
+	__s16 id;
+	__u16 direction;
+	struct ff_trigger trigger;
+	struct ff_replay replay;
+
+	union {
+		struct ff_constant_effect constant;
+		struct ff_ramp_effect ramp;
+		struct ff_periodic_effect_compat periodic;
+		struct ff_condition_effect condition[2]; /* One for each axis */
+		struct ff_rumble_effect rumble;
+	} u;
+};
+
+static inline size_t input_event_size(void)
+{
+	return (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) ?
+		sizeof(struct input_event_compat) : sizeof(struct input_event);
+}
+
+#else
+
+static inline size_t input_event_size(void)
+{
+	return sizeof(struct input_event);
+}
+
+#endif /* CONFIG_COMPAT */
+
+int input_event_from_user(const char __user *buffer,
+			 struct input_event *event);
+
+int input_event_to_user(char __user *buffer,
+			const struct input_event *event);
+
+int input_ff_effect_from_user(const char __user *buffer, size_t size,
+			      struct ff_effect *effect);
+
+#endif /* _INPUT_COMPAT_H */
diff --git a/drivers/input/input-leds.c b/drivers/input/input-leds.c
new file mode 100644
index 0000000..766bf26
--- /dev/null
+++ b/drivers/input/input-leds.c
@@ -0,0 +1,224 @@
+/*
+ * LED support for the input layer
+ *
+ * Copyright 2010-2015 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/input.h>
+
+#if IS_ENABLED(CONFIG_VT)
+#define VT_TRIGGER(_name)	.trigger = _name
+#else
+#define VT_TRIGGER(_name)	.trigger = NULL
+#endif
+
+static const struct {
+	const char *name;
+	const char *trigger;
+} input_led_info[LED_CNT] = {
+	[LED_NUML]	= { "numlock", VT_TRIGGER("kbd-numlock") },
+	[LED_CAPSL]	= { "capslock", VT_TRIGGER("kbd-capslock") },
+	[LED_SCROLLL]	= { "scrolllock", VT_TRIGGER("kbd-scrolllock") },
+	[LED_COMPOSE]	= { "compose" },
+	[LED_KANA]	= { "kana", VT_TRIGGER("kbd-kanalock") },
+	[LED_SLEEP]	= { "sleep" } ,
+	[LED_SUSPEND]	= { "suspend" },
+	[LED_MUTE]	= { "mute" },
+	[LED_MISC]	= { "misc" },
+	[LED_MAIL]	= { "mail" },
+	[LED_CHARGING]	= { "charging" },
+};
+
+struct input_led {
+	struct led_classdev cdev;
+	struct input_handle *handle;
+	unsigned int code; /* One of LED_* constants */
+};
+
+struct input_leds {
+	struct input_handle handle;
+	unsigned int num_leds;
+	struct input_led leds[];
+};
+
+static enum led_brightness input_leds_brightness_get(struct led_classdev *cdev)
+{
+	struct input_led *led = container_of(cdev, struct input_led, cdev);
+	struct input_dev *input = led->handle->dev;
+
+	return test_bit(led->code, input->led) ? cdev->max_brightness : 0;
+}
+
+static void input_leds_brightness_set(struct led_classdev *cdev,
+				      enum led_brightness brightness)
+{
+	struct input_led *led = container_of(cdev, struct input_led, cdev);
+
+	input_inject_event(led->handle, EV_LED, led->code, !!brightness);
+}
+
+static void input_leds_event(struct input_handle *handle, unsigned int type,
+			     unsigned int code, int value)
+{
+}
+
+static int input_leds_get_count(struct input_dev *dev)
+{
+	unsigned int led_code;
+	int count = 0;
+
+	for_each_set_bit(led_code, dev->ledbit, LED_CNT)
+		if (input_led_info[led_code].name)
+			count++;
+
+	return count;
+}
+
+static int input_leds_connect(struct input_handler *handler,
+			      struct input_dev *dev,
+			      const struct input_device_id *id)
+{
+	struct input_leds *leds;
+	unsigned int num_leds;
+	unsigned int led_code;
+	int led_no;
+	int error;
+
+	num_leds = input_leds_get_count(dev);
+	if (!num_leds)
+		return -ENXIO;
+
+	leds = kzalloc(sizeof(*leds) + num_leds * sizeof(*leds->leds),
+		       GFP_KERNEL);
+	if (!leds)
+		return -ENOMEM;
+
+	leds->num_leds = num_leds;
+
+	leds->handle.dev = dev;
+	leds->handle.handler = handler;
+	leds->handle.name = "leds";
+	leds->handle.private = leds;
+
+	error = input_register_handle(&leds->handle);
+	if (error)
+		goto err_free_mem;
+
+	error = input_open_device(&leds->handle);
+	if (error)
+		goto err_unregister_handle;
+
+	led_no = 0;
+	for_each_set_bit(led_code, dev->ledbit, LED_CNT) {
+		struct input_led *led = &leds->leds[led_no];
+
+		led->handle = &leds->handle;
+		led->code = led_code;
+
+		if (!input_led_info[led_code].name)
+			continue;
+
+		led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s",
+					   dev_name(&dev->dev),
+					   input_led_info[led_code].name);
+		if (!led->cdev.name) {
+			error = -ENOMEM;
+			goto err_unregister_leds;
+		}
+
+		led->cdev.max_brightness = 1;
+		led->cdev.brightness_get = input_leds_brightness_get;
+		led->cdev.brightness_set = input_leds_brightness_set;
+		led->cdev.default_trigger = input_led_info[led_code].trigger;
+
+		error = led_classdev_register(&dev->dev, &led->cdev);
+		if (error) {
+			dev_err(&dev->dev, "failed to register LED %s: %d\n",
+				led->cdev.name, error);
+			kfree(led->cdev.name);
+			goto err_unregister_leds;
+		}
+
+		led_no++;
+	}
+
+	return 0;
+
+err_unregister_leds:
+	while (--led_no >= 0) {
+		struct input_led *led = &leds->leds[led_no];
+
+		led_classdev_unregister(&led->cdev);
+		kfree(led->cdev.name);
+	}
+
+	input_close_device(&leds->handle);
+
+err_unregister_handle:
+	input_unregister_handle(&leds->handle);
+
+err_free_mem:
+	kfree(leds);
+	return error;
+}
+
+static void input_leds_disconnect(struct input_handle *handle)
+{
+	struct input_leds *leds = handle->private;
+	int i;
+
+	for (i = 0; i < leds->num_leds; i++) {
+		struct input_led *led = &leds->leds[i];
+
+		led_classdev_unregister(&led->cdev);
+		kfree(led->cdev.name);
+	}
+
+	input_close_device(handle);
+	input_unregister_handle(handle);
+
+	kfree(leds);
+}
+
+static const struct input_device_id input_leds_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+		.evbit = { BIT_MASK(EV_LED) },
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(input, input_leds_ids);
+
+static struct input_handler input_leds_handler = {
+	.event =	input_leds_event,
+	.connect =	input_leds_connect,
+	.disconnect =	input_leds_disconnect,
+	.name =		"leds",
+	.id_table =	input_leds_ids,
+};
+
+static int __init input_leds_init(void)
+{
+	return input_register_handler(&input_leds_handler);
+}
+module_init(input_leds_init);
+
+static void __exit input_leds_exit(void)
+{
+	input_unregister_handler(&input_leds_handler);
+}
+module_exit(input_leds_exit);
+
+MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>");
+MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>");
+MODULE_DESCRIPTION("Input -> LEDs Bridge");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
new file mode 100644
index 0000000..54fce56
--- /dev/null
+++ b/drivers/input/input-mt.c
@@ -0,0 +1,476 @@
+/*
+ * Input Multitouch Library
+ *
+ * Copyright (c) 2008-2010 Henrik Rydberg
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/input/mt.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+
+#define TRKID_SGN	((TRKID_MAX + 1) >> 1)
+
+static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src)
+{
+	if (dev->absinfo && test_bit(src, dev->absbit)) {
+		dev->absinfo[dst] = dev->absinfo[src];
+		dev->absinfo[dst].fuzz = 0;
+		dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
+	}
+}
+
+/**
+ * input_mt_init_slots() - initialize MT input slots
+ * @dev: input device supporting MT events and finger tracking
+ * @num_slots: number of slots used by the device
+ * @flags: mt tasks to handle in core
+ *
+ * This function allocates all necessary memory for MT slot handling
+ * in the input device, prepares the ABS_MT_SLOT and
+ * ABS_MT_TRACKING_ID events for use and sets up appropriate buffers.
+ * Depending on the flags set, it also performs pointer emulation and
+ * frame synchronization.
+ *
+ * May be called repeatedly. Returns -EINVAL if attempting to
+ * reinitialize with a different number of slots.
+ */
+int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
+			unsigned int flags)
+{
+	struct input_mt *mt = dev->mt;
+	int i;
+
+	if (!num_slots)
+		return 0;
+	if (mt)
+		return mt->num_slots != num_slots ? -EINVAL : 0;
+
+	mt = kzalloc(sizeof(*mt) + num_slots * sizeof(*mt->slots), GFP_KERNEL);
+	if (!mt)
+		goto err_mem;
+
+	mt->num_slots = num_slots;
+	mt->flags = flags;
+	input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0);
+	input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0);
+
+	if (flags & (INPUT_MT_POINTER | INPUT_MT_DIRECT)) {
+		__set_bit(EV_KEY, dev->evbit);
+		__set_bit(BTN_TOUCH, dev->keybit);
+
+		copy_abs(dev, ABS_X, ABS_MT_POSITION_X);
+		copy_abs(dev, ABS_Y, ABS_MT_POSITION_Y);
+		copy_abs(dev, ABS_PRESSURE, ABS_MT_PRESSURE);
+	}
+	if (flags & INPUT_MT_POINTER) {
+		__set_bit(BTN_TOOL_FINGER, dev->keybit);
+		__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+		if (num_slots >= 3)
+			__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+		if (num_slots >= 4)
+			__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
+		if (num_slots >= 5)
+			__set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
+		__set_bit(INPUT_PROP_POINTER, dev->propbit);
+	}
+	if (flags & INPUT_MT_DIRECT)
+		__set_bit(INPUT_PROP_DIRECT, dev->propbit);
+	if (flags & INPUT_MT_SEMI_MT)
+		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
+	if (flags & INPUT_MT_TRACK) {
+		unsigned int n2 = num_slots * num_slots;
+		mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL);
+		if (!mt->red)
+			goto err_mem;
+	}
+
+	/* Mark slots as 'inactive' */
+	for (i = 0; i < num_slots; i++)
+		input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1);
+
+	/* Mark slots as 'unused' */
+	mt->frame = 1;
+
+	dev->mt = mt;
+	return 0;
+err_mem:
+	kfree(mt);
+	return -ENOMEM;
+}
+EXPORT_SYMBOL(input_mt_init_slots);
+
+/**
+ * input_mt_destroy_slots() - frees the MT slots of the input device
+ * @dev: input device with allocated MT slots
+ *
+ * This function is only needed in error path as the input core will
+ * automatically free the MT slots when the device is destroyed.
+ */
+void input_mt_destroy_slots(struct input_dev *dev)
+{
+	if (dev->mt) {
+		kfree(dev->mt->red);
+		kfree(dev->mt);
+	}
+	dev->mt = NULL;
+}
+EXPORT_SYMBOL(input_mt_destroy_slots);
+
+/**
+ * input_mt_report_slot_state() - report contact state
+ * @dev: input device with allocated MT slots
+ * @tool_type: the tool type to use in this slot
+ * @active: true if contact is active, false otherwise
+ *
+ * Reports a contact via ABS_MT_TRACKING_ID, and optionally
+ * ABS_MT_TOOL_TYPE. If active is true and the slot is currently
+ * inactive, or if the tool type is changed, a new tracking id is
+ * assigned to the slot. The tool type is only reported if the
+ * corresponding absbit field is set.
+ */
+void input_mt_report_slot_state(struct input_dev *dev,
+				unsigned int tool_type, bool active)
+{
+	struct input_mt *mt = dev->mt;
+	struct input_mt_slot *slot;
+	int id;
+
+	if (!mt)
+		return;
+
+	slot = &mt->slots[mt->slot];
+	slot->frame = mt->frame;
+
+	if (!active) {
+		input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+		return;
+	}
+
+	id = input_mt_get_value(slot, ABS_MT_TRACKING_ID);
+	if (id < 0 || input_mt_get_value(slot, ABS_MT_TOOL_TYPE) != tool_type)
+		id = input_mt_new_trkid(mt);
+
+	input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id);
+	input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type);
+}
+EXPORT_SYMBOL(input_mt_report_slot_state);
+
+/**
+ * input_mt_report_finger_count() - report contact count
+ * @dev: input device with allocated MT slots
+ * @count: the number of contacts
+ *
+ * Reports the contact count via BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP,
+ * BTN_TOOL_TRIPLETAP and BTN_TOOL_QUADTAP.
+ *
+ * The input core ensures only the KEY events already setup for
+ * this device will produce output.
+ */
+void input_mt_report_finger_count(struct input_dev *dev, int count)
+{
+	input_event(dev, EV_KEY, BTN_TOOL_FINGER, count == 1);
+	input_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, count == 2);
+	input_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, count == 3);
+	input_event(dev, EV_KEY, BTN_TOOL_QUADTAP, count == 4);
+	input_event(dev, EV_KEY, BTN_TOOL_QUINTTAP, count == 5);
+}
+EXPORT_SYMBOL(input_mt_report_finger_count);
+
+/**
+ * input_mt_report_pointer_emulation() - common pointer emulation
+ * @dev: input device with allocated MT slots
+ * @use_count: report number of active contacts as finger count
+ *
+ * Performs legacy pointer emulation via BTN_TOUCH, ABS_X, ABS_Y and
+ * ABS_PRESSURE. Touchpad finger count is emulated if use_count is true.
+ *
+ * The input core ensures only the KEY and ABS axes already setup for
+ * this device will produce output.
+ */
+void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
+{
+	struct input_mt *mt = dev->mt;
+	struct input_mt_slot *oldest;
+	int oldid, count, i;
+
+	if (!mt)
+		return;
+
+	oldest = NULL;
+	oldid = mt->trkid;
+	count = 0;
+
+	for (i = 0; i < mt->num_slots; ++i) {
+		struct input_mt_slot *ps = &mt->slots[i];
+		int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
+
+		if (id < 0)
+			continue;
+		if ((id - oldid) & TRKID_SGN) {
+			oldest = ps;
+			oldid = id;
+		}
+		count++;
+	}
+
+	input_event(dev, EV_KEY, BTN_TOUCH, count > 0);
+	if (use_count)
+		input_mt_report_finger_count(dev, count);
+
+	if (oldest) {
+		int x = input_mt_get_value(oldest, ABS_MT_POSITION_X);
+		int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y);
+
+		input_event(dev, EV_ABS, ABS_X, x);
+		input_event(dev, EV_ABS, ABS_Y, y);
+
+		if (test_bit(ABS_MT_PRESSURE, dev->absbit)) {
+			int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
+			input_event(dev, EV_ABS, ABS_PRESSURE, p);
+		}
+	} else {
+		if (test_bit(ABS_MT_PRESSURE, dev->absbit))
+			input_event(dev, EV_ABS, ABS_PRESSURE, 0);
+	}
+}
+EXPORT_SYMBOL(input_mt_report_pointer_emulation);
+
+static void __input_mt_drop_unused(struct input_dev *dev, struct input_mt *mt)
+{
+	int i;
+
+	for (i = 0; i < mt->num_slots; i++) {
+		if (!input_mt_is_used(mt, &mt->slots[i])) {
+			input_mt_slot(dev, i);
+			input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+		}
+	}
+}
+
+/**
+ * input_mt_drop_unused() - Inactivate slots not seen in this frame
+ * @dev: input device with allocated MT slots
+ *
+ * Lift all slots not seen since the last call to this function.
+ */
+void input_mt_drop_unused(struct input_dev *dev)
+{
+	struct input_mt *mt = dev->mt;
+
+	if (mt) {
+		__input_mt_drop_unused(dev, mt);
+		mt->frame++;
+	}
+}
+EXPORT_SYMBOL(input_mt_drop_unused);
+
+/**
+ * input_mt_sync_frame() - synchronize mt frame
+ * @dev: input device with allocated MT slots
+ *
+ * Close the frame and prepare the internal state for a new one.
+ * Depending on the flags, marks unused slots as inactive and performs
+ * pointer emulation.
+ */
+void input_mt_sync_frame(struct input_dev *dev)
+{
+	struct input_mt *mt = dev->mt;
+	bool use_count = false;
+
+	if (!mt)
+		return;
+
+	if (mt->flags & INPUT_MT_DROP_UNUSED)
+		__input_mt_drop_unused(dev, mt);
+
+	if ((mt->flags & INPUT_MT_POINTER) && !(mt->flags & INPUT_MT_SEMI_MT))
+		use_count = true;
+
+	input_mt_report_pointer_emulation(dev, use_count);
+
+	mt->frame++;
+}
+EXPORT_SYMBOL(input_mt_sync_frame);
+
+static int adjust_dual(int *begin, int step, int *end, int eq, int mu)
+{
+	int f, *p, s, c;
+
+	if (begin == end)
+		return 0;
+
+	f = *begin;
+	p = begin + step;
+	s = p == end ? f + 1 : *p;
+
+	for (; p != end; p += step)
+		if (*p < f)
+			s = f, f = *p;
+		else if (*p < s)
+			s = *p;
+
+	c = (f + s + 1) / 2;
+	if (c == 0 || (c > mu && (!eq || mu > 0)))
+		return 0;
+	/* Improve convergence for positive matrices by penalizing overcovers */
+	if (s < 0 && mu <= 0)
+		c *= 2;
+
+	for (p = begin; p != end; p += step)
+		*p -= c;
+
+	return (c < s && s <= 0) || (f >= 0 && f < c);
+}
+
+static void find_reduced_matrix(int *w, int nr, int nc, int nrc, int mu)
+{
+	int i, k, sum;
+
+	for (k = 0; k < nrc; k++) {
+		for (i = 0; i < nr; i++)
+			adjust_dual(w + i, nr, w + i + nrc, nr <= nc, mu);
+		sum = 0;
+		for (i = 0; i < nrc; i += nr)
+			sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr, mu);
+		if (!sum)
+			break;
+	}
+}
+
+static int input_mt_set_matrix(struct input_mt *mt,
+			       const struct input_mt_pos *pos, int num_pos,
+			       int mu)
+{
+	const struct input_mt_pos *p;
+	struct input_mt_slot *s;
+	int *w = mt->red;
+	int x, y;
+
+	for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+		if (!input_mt_is_active(s))
+			continue;
+		x = input_mt_get_value(s, ABS_MT_POSITION_X);
+		y = input_mt_get_value(s, ABS_MT_POSITION_Y);
+		for (p = pos; p != pos + num_pos; p++) {
+			int dx = x - p->x, dy = y - p->y;
+			*w++ = dx * dx + dy * dy - mu;
+		}
+	}
+
+	return w - mt->red;
+}
+
+static void input_mt_set_slots(struct input_mt *mt,
+			       int *slots, int num_pos)
+{
+	struct input_mt_slot *s;
+	int *w = mt->red, j;
+
+	for (j = 0; j != num_pos; j++)
+		slots[j] = -1;
+
+	for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+		if (!input_mt_is_active(s))
+			continue;
+
+		for (j = 0; j != num_pos; j++) {
+			if (w[j] < 0) {
+				slots[j] = s - mt->slots;
+				break;
+			}
+		}
+
+		w += num_pos;
+	}
+
+	for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+		if (input_mt_is_active(s))
+			continue;
+
+		for (j = 0; j != num_pos; j++) {
+			if (slots[j] < 0) {
+				slots[j] = s - mt->slots;
+				break;
+			}
+		}
+	}
+}
+
+/**
+ * input_mt_assign_slots() - perform a best-match assignment
+ * @dev: input device with allocated MT slots
+ * @slots: the slot assignment to be filled
+ * @pos: the position array to match
+ * @num_pos: number of positions
+ * @dmax: maximum ABS_MT_POSITION displacement (zero for infinite)
+ *
+ * Performs a best match against the current contacts and returns
+ * the slot assignment list. New contacts are assigned to unused
+ * slots.
+ *
+ * The assignments are balanced so that all coordinate displacements are
+ * below the euclidian distance dmax. If no such assignment can be found,
+ * some contacts are assigned to unused slots.
+ *
+ * Returns zero on success, or negative error in case of failure.
+ */
+int input_mt_assign_slots(struct input_dev *dev, int *slots,
+			  const struct input_mt_pos *pos, int num_pos,
+			  int dmax)
+{
+	struct input_mt *mt = dev->mt;
+	int mu = 2 * dmax * dmax;
+	int nrc;
+
+	if (!mt || !mt->red)
+		return -ENXIO;
+	if (num_pos > mt->num_slots)
+		return -EINVAL;
+	if (num_pos < 1)
+		return 0;
+
+	nrc = input_mt_set_matrix(mt, pos, num_pos, mu);
+	find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc, mu);
+	input_mt_set_slots(mt, slots, num_pos);
+
+	return 0;
+}
+EXPORT_SYMBOL(input_mt_assign_slots);
+
+/**
+ * input_mt_get_slot_by_key() - return slot matching key
+ * @dev: input device with allocated MT slots
+ * @key: the key of the sought slot
+ *
+ * Returns the slot of the given key, if it exists, otherwise
+ * set the key on the first unused slot and return.
+ *
+ * If no available slot can be found, -1 is returned.
+ * Note that for this function to work properly, input_mt_sync_frame() has
+ * to be called at each frame.
+ */
+int input_mt_get_slot_by_key(struct input_dev *dev, int key)
+{
+	struct input_mt *mt = dev->mt;
+	struct input_mt_slot *s;
+
+	if (!mt)
+		return -1;
+
+	for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
+		if (input_mt_is_active(s) && s->key == key)
+			return s - mt->slots;
+
+	for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
+		if (!input_mt_is_active(s) && !input_mt_is_used(mt, s)) {
+			s->key = key;
+			return s - mt->slots;
+		}
+
+	return -1;
+}
+EXPORT_SYMBOL(input_mt_get_slot_by_key);
diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c
new file mode 100644
index 0000000..3664f81
--- /dev/null
+++ b/drivers/input/input-polldev.c
@@ -0,0 +1,366 @@
+/*
+ * Generic implementation of a polled input device
+
+ * Copyright (c) 2007 Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/module.h>
+#include <linux/input-polldev.h>
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION("Generic implementation of a polled input device");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
+
+static void input_polldev_queue_work(struct input_polled_dev *dev)
+{
+	unsigned long delay;
+
+	delay = msecs_to_jiffies(dev->poll_interval);
+	if (delay >= HZ)
+		delay = round_jiffies_relative(delay);
+
+	queue_delayed_work(system_freezable_wq, &dev->work, delay);
+}
+
+static void input_polled_device_work(struct work_struct *work)
+{
+	struct input_polled_dev *dev =
+		container_of(work, struct input_polled_dev, work.work);
+
+	dev->poll(dev);
+	input_polldev_queue_work(dev);
+}
+
+static int input_open_polled_device(struct input_dev *input)
+{
+	struct input_polled_dev *dev = input_get_drvdata(input);
+
+	if (dev->open)
+		dev->open(dev);
+
+	/* Only start polling if polling is enabled */
+	if (dev->poll_interval > 0) {
+		dev->poll(dev);
+		input_polldev_queue_work(dev);
+	}
+
+	return 0;
+}
+
+static void input_close_polled_device(struct input_dev *input)
+{
+	struct input_polled_dev *dev = input_get_drvdata(input);
+
+	cancel_delayed_work_sync(&dev->work);
+
+	if (dev->close)
+		dev->close(dev);
+}
+
+/* SYSFS interface */
+
+static ssize_t input_polldev_get_poll(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct input_polled_dev *polldev = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", polldev->poll_interval);
+}
+
+static ssize_t input_polldev_set_poll(struct device *dev,
+				struct device_attribute *attr, const char *buf,
+				size_t count)
+{
+	struct input_polled_dev *polldev = dev_get_drvdata(dev);
+	struct input_dev *input = polldev->input;
+	unsigned int interval;
+	int err;
+
+	err = kstrtouint(buf, 0, &interval);
+	if (err)
+		return err;
+
+	if (interval < polldev->poll_interval_min)
+		return -EINVAL;
+
+	if (interval > polldev->poll_interval_max)
+		return -EINVAL;
+
+	mutex_lock(&input->mutex);
+
+	polldev->poll_interval = interval;
+
+	if (input->users) {
+		cancel_delayed_work_sync(&polldev->work);
+		if (polldev->poll_interval > 0)
+			input_polldev_queue_work(polldev);
+	}
+
+	mutex_unlock(&input->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(poll, S_IRUGO | S_IWUSR, input_polldev_get_poll,
+					    input_polldev_set_poll);
+
+
+static ssize_t input_polldev_get_max(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct input_polled_dev *polldev = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", polldev->poll_interval_max);
+}
+
+static DEVICE_ATTR(max, S_IRUGO, input_polldev_get_max, NULL);
+
+static ssize_t input_polldev_get_min(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct input_polled_dev *polldev = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", polldev->poll_interval_min);
+}
+
+static DEVICE_ATTR(min, S_IRUGO, input_polldev_get_min, NULL);
+
+static struct attribute *sysfs_attrs[] = {
+	&dev_attr_poll.attr,
+	&dev_attr_max.attr,
+	&dev_attr_min.attr,
+	NULL
+};
+
+static struct attribute_group input_polldev_attribute_group = {
+	.attrs = sysfs_attrs
+};
+
+static const struct attribute_group *input_polldev_attribute_groups[] = {
+	&input_polldev_attribute_group,
+	NULL
+};
+
+/**
+ * input_allocate_polled_device - allocate memory for polled device
+ *
+ * The function allocates memory for a polled device and also
+ * for an input device associated with this polled device.
+ */
+struct input_polled_dev *input_allocate_polled_device(void)
+{
+	struct input_polled_dev *dev;
+
+	dev = kzalloc(sizeof(struct input_polled_dev), GFP_KERNEL);
+	if (!dev)
+		return NULL;
+
+	dev->input = input_allocate_device();
+	if (!dev->input) {
+		kfree(dev);
+		return NULL;
+	}
+
+	return dev;
+}
+EXPORT_SYMBOL(input_allocate_polled_device);
+
+struct input_polled_devres {
+	struct input_polled_dev *polldev;
+};
+
+static int devm_input_polldev_match(struct device *dev, void *res, void *data)
+{
+	struct input_polled_devres *devres = res;
+
+	return devres->polldev == data;
+}
+
+static void devm_input_polldev_release(struct device *dev, void *res)
+{
+	struct input_polled_devres *devres = res;
+	struct input_polled_dev *polldev = devres->polldev;
+
+	dev_dbg(dev, "%s: dropping reference/freeing %s\n",
+		__func__, dev_name(&polldev->input->dev));
+
+	input_put_device(polldev->input);
+	kfree(polldev);
+}
+
+static void devm_input_polldev_unregister(struct device *dev, void *res)
+{
+	struct input_polled_devres *devres = res;
+	struct input_polled_dev *polldev = devres->polldev;
+
+	dev_dbg(dev, "%s: unregistering device %s\n",
+		__func__, dev_name(&polldev->input->dev));
+	input_unregister_device(polldev->input);
+
+	/*
+	 * Note that we are still holding extra reference to the input
+	 * device so it will stick around until devm_input_polldev_release()
+	 * is called.
+	 */
+}
+
+/**
+ * devm_input_allocate_polled_device - allocate managed polled device
+ * @dev: device owning the polled device being created
+ *
+ * Returns prepared &struct input_polled_dev or %NULL.
+ *
+ * Managed polled input devices do not need to be explicitly unregistered
+ * or freed as it will be done automatically when owner device unbinds
+ * from * its driver (or binding fails). Once such managed polled device
+ * is allocated, it is ready to be set up and registered in the same
+ * fashion as regular polled input devices (using
+ * input_register_polled_device() function).
+ *
+ * If you want to manually unregister and free such managed polled devices,
+ * it can be still done by calling input_unregister_polled_device() and
+ * input_free_polled_device(), although it is rarely needed.
+ *
+ * NOTE: the owner device is set up as parent of input device and users
+ * should not override it.
+ */
+struct input_polled_dev *devm_input_allocate_polled_device(struct device *dev)
+{
+	struct input_polled_dev *polldev;
+	struct input_polled_devres *devres;
+
+	devres = devres_alloc(devm_input_polldev_release, sizeof(*devres),
+			      GFP_KERNEL);
+	if (!devres)
+		return NULL;
+
+	polldev = input_allocate_polled_device();
+	if (!polldev) {
+		devres_free(devres);
+		return NULL;
+	}
+
+	polldev->input->dev.parent = dev;
+	polldev->devres_managed = true;
+
+	devres->polldev = polldev;
+	devres_add(dev, devres);
+
+	return polldev;
+}
+EXPORT_SYMBOL(devm_input_allocate_polled_device);
+
+/**
+ * input_free_polled_device - free memory allocated for polled device
+ * @dev: device to free
+ *
+ * The function frees memory allocated for polling device and drops
+ * reference to the associated input device.
+ */
+void input_free_polled_device(struct input_polled_dev *dev)
+{
+	if (dev) {
+		if (dev->devres_managed)
+			WARN_ON(devres_destroy(dev->input->dev.parent,
+						devm_input_polldev_release,
+						devm_input_polldev_match,
+						dev));
+		input_put_device(dev->input);
+		kfree(dev);
+	}
+}
+EXPORT_SYMBOL(input_free_polled_device);
+
+/**
+ * input_register_polled_device - register polled device
+ * @dev: device to register
+ *
+ * The function registers previously initialized polled input device
+ * with input layer. The device should be allocated with call to
+ * input_allocate_polled_device(). Callers should also set up poll()
+ * method and set up capabilities (id, name, phys, bits) of the
+ * corresponding input_dev structure.
+ */
+int input_register_polled_device(struct input_polled_dev *dev)
+{
+	struct input_polled_devres *devres = NULL;
+	struct input_dev *input = dev->input;
+	int error;
+
+	if (dev->devres_managed) {
+		devres = devres_alloc(devm_input_polldev_unregister,
+				      sizeof(*devres), GFP_KERNEL);
+		if (!devres)
+			return -ENOMEM;
+
+		devres->polldev = dev;
+	}
+
+	input_set_drvdata(input, dev);
+	INIT_DELAYED_WORK(&dev->work, input_polled_device_work);
+
+	if (!dev->poll_interval)
+		dev->poll_interval = 500;
+	if (!dev->poll_interval_max)
+		dev->poll_interval_max = dev->poll_interval;
+
+	input->open = input_open_polled_device;
+	input->close = input_close_polled_device;
+
+	input->dev.groups = input_polldev_attribute_groups;
+
+	error = input_register_device(input);
+	if (error) {
+		devres_free(devres);
+		return error;
+	}
+
+	/*
+	 * Take extra reference to the underlying input device so
+	 * that it survives call to input_unregister_polled_device()
+	 * and is deleted only after input_free_polled_device()
+	 * has been invoked. This is needed to ease task of freeing
+	 * sparse keymaps.
+	 */
+	input_get_device(input);
+
+	if (dev->devres_managed) {
+		dev_dbg(input->dev.parent, "%s: registering %s with devres.\n",
+			__func__, dev_name(&input->dev));
+		devres_add(input->dev.parent, devres);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(input_register_polled_device);
+
+/**
+ * input_unregister_polled_device - unregister polled device
+ * @dev: device to unregister
+ *
+ * The function unregisters previously registered polled input
+ * device from input layer. Polling is stopped and device is
+ * ready to be freed with call to input_free_polled_device().
+ */
+void input_unregister_polled_device(struct input_polled_dev *dev)
+{
+	if (dev->devres_managed)
+		WARN_ON(devres_destroy(dev->input->dev.parent,
+					devm_input_polldev_unregister,
+					devm_input_polldev_match,
+					dev));
+
+	input_unregister_device(dev->input);
+}
+EXPORT_SYMBOL(input_unregister_polled_device);
diff --git a/drivers/input/input.c b/drivers/input/input.c
new file mode 100644
index 0000000..8806059
--- /dev/null
+++ b/drivers/input/input.c
@@ -0,0 +1,2454 @@
+/*
+ * The input core
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/idr.h>
+#include <linux/input/mt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/major.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/poll.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/rcupdate.h>
+#include "input-compat.h"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Input core");
+MODULE_LICENSE("GPL");
+
+#define INPUT_MAX_CHAR_DEVICES		1024
+#define INPUT_FIRST_DYNAMIC_DEV		256
+static DEFINE_IDA(input_ida);
+
+static LIST_HEAD(input_dev_list);
+static LIST_HEAD(input_handler_list);
+
+/*
+ * input_mutex protects access to both input_dev_list and input_handler_list.
+ * This also causes input_[un]register_device and input_[un]register_handler
+ * be mutually exclusive which simplifies locking in drivers implementing
+ * input handlers.
+ */
+static DEFINE_MUTEX(input_mutex);
+
+static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1 };
+
+static inline int is_event_supported(unsigned int code,
+				     unsigned long *bm, unsigned int max)
+{
+	return code <= max && test_bit(code, bm);
+}
+
+static int input_defuzz_abs_event(int value, int old_val, int fuzz)
+{
+	if (fuzz) {
+		if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2)
+			return old_val;
+
+		if (value > old_val - fuzz && value < old_val + fuzz)
+			return (old_val * 3 + value) / 4;
+
+		if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2)
+			return (old_val + value) / 2;
+	}
+
+	return value;
+}
+
+static void input_start_autorepeat(struct input_dev *dev, int code)
+{
+	if (test_bit(EV_REP, dev->evbit) &&
+	    dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
+	    dev->timer.data) {
+		dev->repeat_key = code;
+		mod_timer(&dev->timer,
+			  jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
+	}
+}
+
+static void input_stop_autorepeat(struct input_dev *dev)
+{
+	del_timer(&dev->timer);
+}
+
+/*
+ * Pass event first through all filters and then, if event has not been
+ * filtered out, through all open handles. This function is called with
+ * dev->event_lock held and interrupts disabled.
+ */
+static unsigned int input_to_handler(struct input_handle *handle,
+			struct input_value *vals, unsigned int count)
+{
+	struct input_handler *handler = handle->handler;
+	struct input_value *end = vals;
+	struct input_value *v;
+
+	if (handler->filter) {
+		for (v = vals; v != vals + count; v++) {
+			if (handler->filter(handle, v->type, v->code, v->value))
+				continue;
+			if (end != v)
+				*end = *v;
+			end++;
+		}
+		count = end - vals;
+	}
+
+	if (!count)
+		return 0;
+
+	if (handler->events)
+		handler->events(handle, vals, count);
+	else if (handler->event)
+		for (v = vals; v != vals + count; v++)
+			handler->event(handle, v->type, v->code, v->value);
+
+	return count;
+}
+
+/*
+ * Pass values first through all filters and then, if event has not been
+ * filtered out, through all open handles. This function is called with
+ * dev->event_lock held and interrupts disabled.
+ */
+static void input_pass_values(struct input_dev *dev,
+			      struct input_value *vals, unsigned int count)
+{
+	struct input_handle *handle;
+	struct input_value *v;
+
+	if (!count)
+		return;
+
+	rcu_read_lock();
+
+	handle = rcu_dereference(dev->grab);
+	if (handle) {
+		count = input_to_handler(handle, vals, count);
+	} else {
+		list_for_each_entry_rcu(handle, &dev->h_list, d_node)
+			if (handle->open) {
+				count = input_to_handler(handle, vals, count);
+				if (!count)
+					break;
+			}
+	}
+
+	rcu_read_unlock();
+
+	add_input_randomness(vals->type, vals->code, vals->value);
+
+	/* trigger auto repeat for key events */
+	if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {
+		for (v = vals; v != vals + count; v++) {
+			if (v->type == EV_KEY && v->value != 2) {
+				if (v->value)
+					input_start_autorepeat(dev, v->code);
+				else
+					input_stop_autorepeat(dev);
+			}
+		}
+	}
+}
+
+static void input_pass_event(struct input_dev *dev,
+			     unsigned int type, unsigned int code, int value)
+{
+	struct input_value vals[] = { { type, code, value } };
+
+	input_pass_values(dev, vals, ARRAY_SIZE(vals));
+}
+
+/*
+ * Generate software autorepeat event. Note that we take
+ * dev->event_lock here to avoid racing with input_event
+ * which may cause keys get "stuck".
+ */
+static void input_repeat_key(unsigned long data)
+{
+	struct input_dev *dev = (void *) data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	if (test_bit(dev->repeat_key, dev->key) &&
+	    is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
+		struct input_value vals[] =  {
+			{ EV_KEY, dev->repeat_key, 2 },
+			input_value_sync
+		};
+
+		input_pass_values(dev, vals, ARRAY_SIZE(vals));
+
+		if (dev->rep[REP_PERIOD])
+			mod_timer(&dev->timer, jiffies +
+					msecs_to_jiffies(dev->rep[REP_PERIOD]));
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+#define INPUT_IGNORE_EVENT	0
+#define INPUT_PASS_TO_HANDLERS	1
+#define INPUT_PASS_TO_DEVICE	2
+#define INPUT_SLOT		4
+#define INPUT_FLUSH		8
+#define INPUT_PASS_TO_ALL	(INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
+
+static int input_handle_abs_event(struct input_dev *dev,
+				  unsigned int code, int *pval)
+{
+	struct input_mt *mt = dev->mt;
+	bool is_mt_event;
+	int *pold;
+
+	if (code == ABS_MT_SLOT) {
+		/*
+		 * "Stage" the event; we'll flush it later, when we
+		 * get actual touch data.
+		 */
+		if (mt && *pval >= 0 && *pval < mt->num_slots)
+			mt->slot = *pval;
+
+		return INPUT_IGNORE_EVENT;
+	}
+
+	is_mt_event = input_is_mt_value(code);
+
+	if (!is_mt_event) {
+		pold = &dev->absinfo[code].value;
+	} else if (mt) {
+		pold = &mt->slots[mt->slot].abs[code - ABS_MT_FIRST];
+	} else {
+		/*
+		 * Bypass filtering for multi-touch events when
+		 * not employing slots.
+		 */
+		pold = NULL;
+	}
+
+	if (pold) {
+		*pval = input_defuzz_abs_event(*pval, *pold,
+						dev->absinfo[code].fuzz);
+		if (*pold == *pval)
+			return INPUT_IGNORE_EVENT;
+
+		*pold = *pval;
+	}
+
+	/* Flush pending "slot" event */
+	if (is_mt_event && mt && mt->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
+		input_abs_set_val(dev, ABS_MT_SLOT, mt->slot);
+		return INPUT_PASS_TO_HANDLERS | INPUT_SLOT;
+	}
+
+	return INPUT_PASS_TO_HANDLERS;
+}
+
+static int input_get_disposition(struct input_dev *dev,
+			  unsigned int type, unsigned int code, int *pval)
+{
+	int disposition = INPUT_IGNORE_EVENT;
+	int value = *pval;
+
+	switch (type) {
+
+	case EV_SYN:
+		switch (code) {
+		case SYN_CONFIG:
+			disposition = INPUT_PASS_TO_ALL;
+			break;
+
+		case SYN_REPORT:
+			disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
+			break;
+		case SYN_MT_REPORT:
+			disposition = INPUT_PASS_TO_HANDLERS;
+			break;
+		}
+		break;
+
+	case EV_KEY:
+		if (is_event_supported(code, dev->keybit, KEY_MAX)) {
+
+			/* auto-repeat bypasses state updates */
+			if (value == 2) {
+				disposition = INPUT_PASS_TO_HANDLERS;
+				break;
+			}
+
+			if (!!test_bit(code, dev->key) != !!value) {
+
+				__change_bit(code, dev->key);
+				disposition = INPUT_PASS_TO_HANDLERS;
+			}
+		}
+		break;
+
+	case EV_SW:
+		if (is_event_supported(code, dev->swbit, SW_MAX) &&
+		    !!test_bit(code, dev->sw) != !!value) {
+
+			__change_bit(code, dev->sw);
+			disposition = INPUT_PASS_TO_HANDLERS;
+		}
+		break;
+
+	case EV_ABS:
+		if (is_event_supported(code, dev->absbit, ABS_MAX))
+			disposition = input_handle_abs_event(dev, code, &value);
+
+		break;
+
+	case EV_REL:
+		if (is_event_supported(code, dev->relbit, REL_MAX) && value)
+			disposition = INPUT_PASS_TO_HANDLERS;
+
+		break;
+
+	case EV_MSC:
+		if (is_event_supported(code, dev->mscbit, MSC_MAX))
+			disposition = INPUT_PASS_TO_ALL;
+
+		break;
+
+	case EV_LED:
+		if (is_event_supported(code, dev->ledbit, LED_MAX) &&
+		    !!test_bit(code, dev->led) != !!value) {
+
+			__change_bit(code, dev->led);
+			disposition = INPUT_PASS_TO_ALL;
+		}
+		break;
+
+	case EV_SND:
+		if (is_event_supported(code, dev->sndbit, SND_MAX)) {
+
+			if (!!test_bit(code, dev->snd) != !!value)
+				__change_bit(code, dev->snd);
+			disposition = INPUT_PASS_TO_ALL;
+		}
+		break;
+
+	case EV_REP:
+		if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
+			dev->rep[code] = value;
+			disposition = INPUT_PASS_TO_ALL;
+		}
+		break;
+
+	case EV_FF:
+		if (value >= 0)
+			disposition = INPUT_PASS_TO_ALL;
+		break;
+
+	case EV_PWR:
+		disposition = INPUT_PASS_TO_ALL;
+		break;
+	}
+
+	*pval = value;
+	return disposition;
+}
+
+static void input_handle_event(struct input_dev *dev,
+			       unsigned int type, unsigned int code, int value)
+{
+	int disposition;
+
+	disposition = input_get_disposition(dev, type, code, &value);
+
+	if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
+		dev->event(dev, type, code, value);
+
+	if (!dev->vals)
+		return;
+
+	if (disposition & INPUT_PASS_TO_HANDLERS) {
+		struct input_value *v;
+
+		if (disposition & INPUT_SLOT) {
+			v = &dev->vals[dev->num_vals++];
+			v->type = EV_ABS;
+			v->code = ABS_MT_SLOT;
+			v->value = dev->mt->slot;
+		}
+
+		v = &dev->vals[dev->num_vals++];
+		v->type = type;
+		v->code = code;
+		v->value = value;
+	}
+
+	if (disposition & INPUT_FLUSH) {
+		if (dev->num_vals >= 2)
+			input_pass_values(dev, dev->vals, dev->num_vals);
+		dev->num_vals = 0;
+	} else if (dev->num_vals >= dev->max_vals - 2) {
+		dev->vals[dev->num_vals++] = input_value_sync;
+		input_pass_values(dev, dev->vals, dev->num_vals);
+		dev->num_vals = 0;
+	}
+
+}
+
+/**
+ * input_event() - report new input event
+ * @dev: device that generated the event
+ * @type: type of the event
+ * @code: event code
+ * @value: value of the event
+ *
+ * This function should be used by drivers implementing various input
+ * devices to report input events. See also input_inject_event().
+ *
+ * NOTE: input_event() may be safely used right after input device was
+ * allocated with input_allocate_device(), even before it is registered
+ * with input_register_device(), but the event will not reach any of the
+ * input handlers. Such early invocation of input_event() may be used
+ * to 'seed' initial state of a switch or initial position of absolute
+ * axis, etc.
+ */
+void input_event(struct input_dev *dev,
+		 unsigned int type, unsigned int code, int value)
+{
+	unsigned long flags;
+
+	if (is_event_supported(type, dev->evbit, EV_MAX)) {
+
+		spin_lock_irqsave(&dev->event_lock, flags);
+		input_handle_event(dev, type, code, value);
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+	}
+}
+EXPORT_SYMBOL(input_event);
+
+/**
+ * input_inject_event() - send input event from input handler
+ * @handle: input handle to send event through
+ * @type: type of the event
+ * @code: event code
+ * @value: value of the event
+ *
+ * Similar to input_event() but will ignore event if device is
+ * "grabbed" and handle injecting event is not the one that owns
+ * the device.
+ */
+void input_inject_event(struct input_handle *handle,
+			unsigned int type, unsigned int code, int value)
+{
+	struct input_dev *dev = handle->dev;
+	struct input_handle *grab;
+	unsigned long flags;
+
+	if (is_event_supported(type, dev->evbit, EV_MAX)) {
+		spin_lock_irqsave(&dev->event_lock, flags);
+
+		rcu_read_lock();
+		grab = rcu_dereference(dev->grab);
+		if (!grab || grab == handle)
+			input_handle_event(dev, type, code, value);
+		rcu_read_unlock();
+
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+	}
+}
+EXPORT_SYMBOL(input_inject_event);
+
+/**
+ * input_alloc_absinfo - allocates array of input_absinfo structs
+ * @dev: the input device emitting absolute events
+ *
+ * If the absinfo struct the caller asked for is already allocated, this
+ * functions will not do anything.
+ */
+void input_alloc_absinfo(struct input_dev *dev)
+{
+	if (!dev->absinfo)
+		dev->absinfo = kcalloc(ABS_CNT, sizeof(struct input_absinfo),
+					GFP_KERNEL);
+
+	WARN(!dev->absinfo, "%s(): kcalloc() failed?\n", __func__);
+}
+EXPORT_SYMBOL(input_alloc_absinfo);
+
+void input_set_abs_params(struct input_dev *dev, unsigned int axis,
+			  int min, int max, int fuzz, int flat)
+{
+	struct input_absinfo *absinfo;
+
+	input_alloc_absinfo(dev);
+	if (!dev->absinfo)
+		return;
+
+	absinfo = &dev->absinfo[axis];
+	absinfo->minimum = min;
+	absinfo->maximum = max;
+	absinfo->fuzz = fuzz;
+	absinfo->flat = flat;
+
+	__set_bit(EV_ABS, dev->evbit);
+	__set_bit(axis, dev->absbit);
+}
+EXPORT_SYMBOL(input_set_abs_params);
+
+
+/**
+ * input_grab_device - grabs device for exclusive use
+ * @handle: input handle that wants to own the device
+ *
+ * When a device is grabbed by an input handle all events generated by
+ * the device are delivered only to this handle. Also events injected
+ * by other input handles are ignored while device is grabbed.
+ */
+int input_grab_device(struct input_handle *handle)
+{
+	struct input_dev *dev = handle->dev;
+	int retval;
+
+	retval = mutex_lock_interruptible(&dev->mutex);
+	if (retval)
+		return retval;
+
+	if (dev->grab) {
+		retval = -EBUSY;
+		goto out;
+	}
+
+	rcu_assign_pointer(dev->grab, handle);
+
+ out:
+	mutex_unlock(&dev->mutex);
+	return retval;
+}
+EXPORT_SYMBOL(input_grab_device);
+
+static void __input_release_device(struct input_handle *handle)
+{
+	struct input_dev *dev = handle->dev;
+	struct input_handle *grabber;
+
+	grabber = rcu_dereference_protected(dev->grab,
+					    lockdep_is_held(&dev->mutex));
+	if (grabber == handle) {
+		rcu_assign_pointer(dev->grab, NULL);
+		/* Make sure input_pass_event() notices that grab is gone */
+		synchronize_rcu();
+
+		list_for_each_entry(handle, &dev->h_list, d_node)
+			if (handle->open && handle->handler->start)
+				handle->handler->start(handle);
+	}
+}
+
+/**
+ * input_release_device - release previously grabbed device
+ * @handle: input handle that owns the device
+ *
+ * Releases previously grabbed device so that other input handles can
+ * start receiving input events. Upon release all handlers attached
+ * to the device have their start() method called so they have a change
+ * to synchronize device state with the rest of the system.
+ */
+void input_release_device(struct input_handle *handle)
+{
+	struct input_dev *dev = handle->dev;
+
+	mutex_lock(&dev->mutex);
+	__input_release_device(handle);
+	mutex_unlock(&dev->mutex);
+}
+EXPORT_SYMBOL(input_release_device);
+
+/**
+ * input_open_device - open input device
+ * @handle: handle through which device is being accessed
+ *
+ * This function should be called by input handlers when they
+ * want to start receive events from given input device.
+ */
+int input_open_device(struct input_handle *handle)
+{
+	struct input_dev *dev = handle->dev;
+	int retval;
+
+	retval = mutex_lock_interruptible(&dev->mutex);
+	if (retval)
+		return retval;
+
+	if (dev->going_away) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	handle->open++;
+
+	if (!dev->users++ && dev->open)
+		retval = dev->open(dev);
+
+	if (retval) {
+		dev->users--;
+		if (!--handle->open) {
+			/*
+			 * Make sure we are not delivering any more events
+			 * through this handle
+			 */
+			synchronize_rcu();
+		}
+	}
+
+ out:
+	mutex_unlock(&dev->mutex);
+	return retval;
+}
+EXPORT_SYMBOL(input_open_device);
+
+int input_flush_device(struct input_handle *handle, struct file *file)
+{
+	struct input_dev *dev = handle->dev;
+	int retval;
+
+	retval = mutex_lock_interruptible(&dev->mutex);
+	if (retval)
+		return retval;
+
+	if (dev->flush)
+		retval = dev->flush(dev, file);
+
+	mutex_unlock(&dev->mutex);
+	return retval;
+}
+EXPORT_SYMBOL(input_flush_device);
+
+/**
+ * input_close_device - close input device
+ * @handle: handle through which device is being accessed
+ *
+ * This function should be called by input handlers when they
+ * want to stop receive events from given input device.
+ */
+void input_close_device(struct input_handle *handle)
+{
+	struct input_dev *dev = handle->dev;
+
+	mutex_lock(&dev->mutex);
+
+	__input_release_device(handle);
+
+	if (!--dev->users && dev->close)
+		dev->close(dev);
+
+	if (!--handle->open) {
+		/*
+		 * synchronize_rcu() makes sure that input_pass_event()
+		 * completed and that no more input events are delivered
+		 * through this handle
+		 */
+		synchronize_rcu();
+	}
+
+	mutex_unlock(&dev->mutex);
+}
+EXPORT_SYMBOL(input_close_device);
+
+/*
+ * Simulate keyup events for all keys that are marked as pressed.
+ * The function must be called with dev->event_lock held.
+ */
+static void input_dev_release_keys(struct input_dev *dev)
+{
+	bool need_sync = false;
+	int code;
+
+	if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) {
+		for_each_set_bit(code, dev->key, KEY_CNT) {
+			input_pass_event(dev, EV_KEY, code, 0);
+			need_sync = true;
+		}
+
+		if (need_sync)
+			input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
+
+		memset(dev->key, 0, sizeof(dev->key));
+	}
+}
+
+/*
+ * Prepare device for unregistering
+ */
+static void input_disconnect_device(struct input_dev *dev)
+{
+	struct input_handle *handle;
+
+	/*
+	 * Mark device as going away. Note that we take dev->mutex here
+	 * not to protect access to dev->going_away but rather to ensure
+	 * that there are no threads in the middle of input_open_device()
+	 */
+	mutex_lock(&dev->mutex);
+	dev->going_away = true;
+	mutex_unlock(&dev->mutex);
+
+	spin_lock_irq(&dev->event_lock);
+
+	/*
+	 * Simulate keyup events for all pressed keys so that handlers
+	 * are not left with "stuck" keys. The driver may continue
+	 * generate events even after we done here but they will not
+	 * reach any handlers.
+	 */
+	input_dev_release_keys(dev);
+
+	list_for_each_entry(handle, &dev->h_list, d_node)
+		handle->open = 0;
+
+	spin_unlock_irq(&dev->event_lock);
+}
+
+/**
+ * input_scancode_to_scalar() - converts scancode in &struct input_keymap_entry
+ * @ke: keymap entry containing scancode to be converted.
+ * @scancode: pointer to the location where converted scancode should
+ *	be stored.
+ *
+ * This function is used to convert scancode stored in &struct keymap_entry
+ * into scalar form understood by legacy keymap handling methods. These
+ * methods expect scancodes to be represented as 'unsigned int'.
+ */
+int input_scancode_to_scalar(const struct input_keymap_entry *ke,
+			     unsigned int *scancode)
+{
+	switch (ke->len) {
+	case 1:
+		*scancode = *((u8 *)ke->scancode);
+		break;
+
+	case 2:
+		*scancode = *((u16 *)ke->scancode);
+		break;
+
+	case 4:
+		*scancode = *((u32 *)ke->scancode);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(input_scancode_to_scalar);
+
+/*
+ * Those routines handle the default case where no [gs]etkeycode() is
+ * defined. In this case, an array indexed by the scancode is used.
+ */
+
+static unsigned int input_fetch_keycode(struct input_dev *dev,
+					unsigned int index)
+{
+	switch (dev->keycodesize) {
+	case 1:
+		return ((u8 *)dev->keycode)[index];
+
+	case 2:
+		return ((u16 *)dev->keycode)[index];
+
+	default:
+		return ((u32 *)dev->keycode)[index];
+	}
+}
+
+static int input_default_getkeycode(struct input_dev *dev,
+				    struct input_keymap_entry *ke)
+{
+	unsigned int index;
+	int error;
+
+	if (!dev->keycodesize)
+		return -EINVAL;
+
+	if (ke->flags & INPUT_KEYMAP_BY_INDEX)
+		index = ke->index;
+	else {
+		error = input_scancode_to_scalar(ke, &index);
+		if (error)
+			return error;
+	}
+
+	if (index >= dev->keycodemax)
+		return -EINVAL;
+
+	ke->keycode = input_fetch_keycode(dev, index);
+	ke->index = index;
+	ke->len = sizeof(index);
+	memcpy(ke->scancode, &index, sizeof(index));
+
+	return 0;
+}
+
+static int input_default_setkeycode(struct input_dev *dev,
+				    const struct input_keymap_entry *ke,
+				    unsigned int *old_keycode)
+{
+	unsigned int index;
+	int error;
+	int i;
+
+	if (!dev->keycodesize)
+		return -EINVAL;
+
+	if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+		index = ke->index;
+	} else {
+		error = input_scancode_to_scalar(ke, &index);
+		if (error)
+			return error;
+	}
+
+	if (index >= dev->keycodemax)
+		return -EINVAL;
+
+	if (dev->keycodesize < sizeof(ke->keycode) &&
+			(ke->keycode >> (dev->keycodesize * 8)))
+		return -EINVAL;
+
+	switch (dev->keycodesize) {
+		case 1: {
+			u8 *k = (u8 *)dev->keycode;
+			*old_keycode = k[index];
+			k[index] = ke->keycode;
+			break;
+		}
+		case 2: {
+			u16 *k = (u16 *)dev->keycode;
+			*old_keycode = k[index];
+			k[index] = ke->keycode;
+			break;
+		}
+		default: {
+			u32 *k = (u32 *)dev->keycode;
+			*old_keycode = k[index];
+			k[index] = ke->keycode;
+			break;
+		}
+	}
+
+	__clear_bit(*old_keycode, dev->keybit);
+	__set_bit(ke->keycode, dev->keybit);
+
+	for (i = 0; i < dev->keycodemax; i++) {
+		if (input_fetch_keycode(dev, i) == *old_keycode) {
+			__set_bit(*old_keycode, dev->keybit);
+			break; /* Setting the bit twice is useless, so break */
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * input_get_keycode - retrieve keycode currently mapped to a given scancode
+ * @dev: input device which keymap is being queried
+ * @ke: keymap entry
+ *
+ * This function should be called by anyone interested in retrieving current
+ * keymap. Presently evdev handlers use it.
+ */
+int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke)
+{
+	unsigned long flags;
+	int retval;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	retval = dev->getkeycode(dev, ke);
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	return retval;
+}
+EXPORT_SYMBOL(input_get_keycode);
+
+/**
+ * input_set_keycode - attribute a keycode to a given scancode
+ * @dev: input device which keymap is being updated
+ * @ke: new keymap entry
+ *
+ * This function should be called by anyone needing to update current
+ * keymap. Presently keyboard and evdev handlers use it.
+ */
+int input_set_keycode(struct input_dev *dev,
+		      const struct input_keymap_entry *ke)
+{
+	unsigned long flags;
+	unsigned int old_keycode;
+	int retval;
+
+	if (ke->keycode > KEY_MAX)
+		return -EINVAL;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	retval = dev->setkeycode(dev, ke, &old_keycode);
+	if (retval)
+		goto out;
+
+	/* Make sure KEY_RESERVED did not get enabled. */
+	__clear_bit(KEY_RESERVED, dev->keybit);
+
+	/*
+	 * Simulate keyup event if keycode is not present
+	 * in the keymap anymore
+	 */
+	if (test_bit(EV_KEY, dev->evbit) &&
+	    !is_event_supported(old_keycode, dev->keybit, KEY_MAX) &&
+	    __test_and_clear_bit(old_keycode, dev->key)) {
+		struct input_value vals[] =  {
+			{ EV_KEY, old_keycode, 0 },
+			input_value_sync
+		};
+
+		input_pass_values(dev, vals, ARRAY_SIZE(vals));
+	}
+
+ out:
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	return retval;
+}
+EXPORT_SYMBOL(input_set_keycode);
+
+static const struct input_device_id *input_match_device(struct input_handler *handler,
+							struct input_dev *dev)
+{
+	const struct input_device_id *id;
+
+	for (id = handler->id_table; id->flags || id->driver_info; id++) {
+
+		if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
+			if (id->bustype != dev->id.bustype)
+				continue;
+
+		if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
+			if (id->vendor != dev->id.vendor)
+				continue;
+
+		if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
+			if (id->product != dev->id.product)
+				continue;
+
+		if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
+			if (id->version != dev->id.version)
+				continue;
+
+		if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))
+			continue;
+
+		if (!bitmap_subset(id->keybit, dev->keybit, KEY_MAX))
+			continue;
+
+		if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))
+			continue;
+
+		if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))
+			continue;
+
+		if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))
+			continue;
+
+		if (!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX))
+			continue;
+
+		if (!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX))
+			continue;
+
+		if (!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX))
+			continue;
+
+		if (!bitmap_subset(id->swbit, dev->swbit, SW_MAX))
+			continue;
+
+		if (!handler->match || handler->match(handler, dev))
+			return id;
+	}
+
+	return NULL;
+}
+
+static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
+{
+	const struct input_device_id *id;
+	int error;
+
+	id = input_match_device(handler, dev);
+	if (!id)
+		return -ENODEV;
+
+	error = handler->connect(handler, dev, id);
+	if (error && error != -ENODEV)
+		pr_err("failed to attach handler %s to device %s, error: %d\n",
+		       handler->name, kobject_name(&dev->dev.kobj), error);
+
+	return error;
+}
+
+#ifdef CONFIG_COMPAT
+
+static int input_bits_to_string(char *buf, int buf_size,
+				unsigned long bits, bool skip_empty)
+{
+	int len = 0;
+
+	if (INPUT_COMPAT_TEST) {
+		u32 dword = bits >> 32;
+		if (dword || !skip_empty)
+			len += snprintf(buf, buf_size, "%x ", dword);
+
+		dword = bits & 0xffffffffUL;
+		if (dword || !skip_empty || len)
+			len += snprintf(buf + len, max(buf_size - len, 0),
+					"%x", dword);
+	} else {
+		if (bits || !skip_empty)
+			len += snprintf(buf, buf_size, "%lx", bits);
+	}
+
+	return len;
+}
+
+#else /* !CONFIG_COMPAT */
+
+static int input_bits_to_string(char *buf, int buf_size,
+				unsigned long bits, bool skip_empty)
+{
+	return bits || !skip_empty ?
+		snprintf(buf, buf_size, "%lx", bits) : 0;
+}
+
+#endif
+
+#ifdef CONFIG_PROC_FS
+
+static struct proc_dir_entry *proc_bus_input_dir;
+static DECLARE_WAIT_QUEUE_HEAD(input_devices_poll_wait);
+static int input_devices_state;
+
+static inline void input_wakeup_procfs_readers(void)
+{
+	input_devices_state++;
+	wake_up(&input_devices_poll_wait);
+}
+
+static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait)
+{
+	poll_wait(file, &input_devices_poll_wait, wait);
+	if (file->f_version != input_devices_state) {
+		file->f_version = input_devices_state;
+		return POLLIN | POLLRDNORM;
+	}
+
+	return 0;
+}
+
+union input_seq_state {
+	struct {
+		unsigned short pos;
+		bool mutex_acquired;
+	};
+	void *p;
+};
+
+static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	union input_seq_state *state = (union input_seq_state *)&seq->private;
+	int error;
+
+	/* We need to fit into seq->private pointer */
+	BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private));
+
+	error = mutex_lock_interruptible(&input_mutex);
+	if (error) {
+		state->mutex_acquired = false;
+		return ERR_PTR(error);
+	}
+
+	state->mutex_acquired = true;
+
+	return seq_list_start(&input_dev_list, *pos);
+}
+
+static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	return seq_list_next(v, &input_dev_list, pos);
+}
+
+static void input_seq_stop(struct seq_file *seq, void *v)
+{
+	union input_seq_state *state = (union input_seq_state *)&seq->private;
+
+	if (state->mutex_acquired)
+		mutex_unlock(&input_mutex);
+}
+
+static void input_seq_print_bitmap(struct seq_file *seq, const char *name,
+				   unsigned long *bitmap, int max)
+{
+	int i;
+	bool skip_empty = true;
+	char buf[18];
+
+	seq_printf(seq, "B: %s=", name);
+
+	for (i = BITS_TO_LONGS(max) - 1; i >= 0; i--) {
+		if (input_bits_to_string(buf, sizeof(buf),
+					 bitmap[i], skip_empty)) {
+			skip_empty = false;
+			seq_printf(seq, "%s%s", buf, i > 0 ? " " : "");
+		}
+	}
+
+	/*
+	 * If no output was produced print a single 0.
+	 */
+	if (skip_empty)
+		seq_puts(seq, "0");
+
+	seq_putc(seq, '\n');
+}
+
+static int input_devices_seq_show(struct seq_file *seq, void *v)
+{
+	struct input_dev *dev = container_of(v, struct input_dev, node);
+	const char *path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
+	struct input_handle *handle;
+
+	seq_printf(seq, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n",
+		   dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version);
+
+	seq_printf(seq, "N: Name=\"%s\"\n", dev->name ? dev->name : "");
+	seq_printf(seq, "P: Phys=%s\n", dev->phys ? dev->phys : "");
+	seq_printf(seq, "S: Sysfs=%s\n", path ? path : "");
+	seq_printf(seq, "U: Uniq=%s\n", dev->uniq ? dev->uniq : "");
+	seq_printf(seq, "H: Handlers=");
+
+	list_for_each_entry(handle, &dev->h_list, d_node)
+		seq_printf(seq, "%s ", handle->name);
+	seq_putc(seq, '\n');
+
+	input_seq_print_bitmap(seq, "PROP", dev->propbit, INPUT_PROP_MAX);
+
+	input_seq_print_bitmap(seq, "EV", dev->evbit, EV_MAX);
+	if (test_bit(EV_KEY, dev->evbit))
+		input_seq_print_bitmap(seq, "KEY", dev->keybit, KEY_MAX);
+	if (test_bit(EV_REL, dev->evbit))
+		input_seq_print_bitmap(seq, "REL", dev->relbit, REL_MAX);
+	if (test_bit(EV_ABS, dev->evbit))
+		input_seq_print_bitmap(seq, "ABS", dev->absbit, ABS_MAX);
+	if (test_bit(EV_MSC, dev->evbit))
+		input_seq_print_bitmap(seq, "MSC", dev->mscbit, MSC_MAX);
+	if (test_bit(EV_LED, dev->evbit))
+		input_seq_print_bitmap(seq, "LED", dev->ledbit, LED_MAX);
+	if (test_bit(EV_SND, dev->evbit))
+		input_seq_print_bitmap(seq, "SND", dev->sndbit, SND_MAX);
+	if (test_bit(EV_FF, dev->evbit))
+		input_seq_print_bitmap(seq, "FF", dev->ffbit, FF_MAX);
+	if (test_bit(EV_SW, dev->evbit))
+		input_seq_print_bitmap(seq, "SW", dev->swbit, SW_MAX);
+
+	seq_putc(seq, '\n');
+
+	kfree(path);
+	return 0;
+}
+
+static const struct seq_operations input_devices_seq_ops = {
+	.start	= input_devices_seq_start,
+	.next	= input_devices_seq_next,
+	.stop	= input_seq_stop,
+	.show	= input_devices_seq_show,
+};
+
+static int input_proc_devices_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &input_devices_seq_ops);
+}
+
+static const struct file_operations input_devices_fileops = {
+	.owner		= THIS_MODULE,
+	.open		= input_proc_devices_open,
+	.poll		= input_proc_devices_poll,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	union input_seq_state *state = (union input_seq_state *)&seq->private;
+	int error;
+
+	/* We need to fit into seq->private pointer */
+	BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private));
+
+	error = mutex_lock_interruptible(&input_mutex);
+	if (error) {
+		state->mutex_acquired = false;
+		return ERR_PTR(error);
+	}
+
+	state->mutex_acquired = true;
+	state->pos = *pos;
+
+	return seq_list_start(&input_handler_list, *pos);
+}
+
+static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	union input_seq_state *state = (union input_seq_state *)&seq->private;
+
+	state->pos = *pos + 1;
+	return seq_list_next(v, &input_handler_list, pos);
+}
+
+static int input_handlers_seq_show(struct seq_file *seq, void *v)
+{
+	struct input_handler *handler = container_of(v, struct input_handler, node);
+	union input_seq_state *state = (union input_seq_state *)&seq->private;
+
+	seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name);
+	if (handler->filter)
+		seq_puts(seq, " (filter)");
+	if (handler->legacy_minors)
+		seq_printf(seq, " Minor=%d", handler->minor);
+	seq_putc(seq, '\n');
+
+	return 0;
+}
+
+static const struct seq_operations input_handlers_seq_ops = {
+	.start	= input_handlers_seq_start,
+	.next	= input_handlers_seq_next,
+	.stop	= input_seq_stop,
+	.show	= input_handlers_seq_show,
+};
+
+static int input_proc_handlers_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &input_handlers_seq_ops);
+}
+
+static const struct file_operations input_handlers_fileops = {
+	.owner		= THIS_MODULE,
+	.open		= input_proc_handlers_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static int __init input_proc_init(void)
+{
+	struct proc_dir_entry *entry;
+
+	proc_bus_input_dir = proc_mkdir("bus/input", NULL);
+	if (!proc_bus_input_dir)
+		return -ENOMEM;
+
+	entry = proc_create("devices", 0, proc_bus_input_dir,
+			    &input_devices_fileops);
+	if (!entry)
+		goto fail1;
+
+	entry = proc_create("handlers", 0, proc_bus_input_dir,
+			    &input_handlers_fileops);
+	if (!entry)
+		goto fail2;
+
+	return 0;
+
+ fail2:	remove_proc_entry("devices", proc_bus_input_dir);
+ fail1: remove_proc_entry("bus/input", NULL);
+	return -ENOMEM;
+}
+
+static void input_proc_exit(void)
+{
+	remove_proc_entry("devices", proc_bus_input_dir);
+	remove_proc_entry("handlers", proc_bus_input_dir);
+	remove_proc_entry("bus/input", NULL);
+}
+
+#else /* !CONFIG_PROC_FS */
+static inline void input_wakeup_procfs_readers(void) { }
+static inline int input_proc_init(void) { return 0; }
+static inline void input_proc_exit(void) { }
+#endif
+
+#define INPUT_DEV_STRING_ATTR_SHOW(name)				\
+static ssize_t input_dev_show_##name(struct device *dev,		\
+				     struct device_attribute *attr,	\
+				     char *buf)				\
+{									\
+	struct input_dev *input_dev = to_input_dev(dev);		\
+									\
+	return scnprintf(buf, PAGE_SIZE, "%s\n",			\
+			 input_dev->name ? input_dev->name : "");	\
+}									\
+static DEVICE_ATTR(name, S_IRUGO, input_dev_show_##name, NULL)
+
+INPUT_DEV_STRING_ATTR_SHOW(name);
+INPUT_DEV_STRING_ATTR_SHOW(phys);
+INPUT_DEV_STRING_ATTR_SHOW(uniq);
+
+static int input_print_modalias_bits(char *buf, int size,
+				     char name, unsigned long *bm,
+				     unsigned int min_bit, unsigned int max_bit)
+{
+	int len = 0, i;
+
+	len += snprintf(buf, max(size, 0), "%c", name);
+	for (i = min_bit; i < max_bit; i++)
+		if (bm[BIT_WORD(i)] & BIT_MASK(i))
+			len += snprintf(buf + len, max(size - len, 0), "%X,", i);
+	return len;
+}
+
+static int input_print_modalias(char *buf, int size, struct input_dev *id,
+				int add_cr)
+{
+	int len;
+
+	len = snprintf(buf, max(size, 0),
+		       "input:b%04Xv%04Xp%04Xe%04X-",
+		       id->id.bustype, id->id.vendor,
+		       id->id.product, id->id.version);
+
+	len += input_print_modalias_bits(buf + len, size - len,
+				'e', id->evbit, 0, EV_MAX);
+	len += input_print_modalias_bits(buf + len, size - len,
+				'k', id->keybit, KEY_MIN_INTERESTING, KEY_MAX);
+	len += input_print_modalias_bits(buf + len, size - len,
+				'r', id->relbit, 0, REL_MAX);
+	len += input_print_modalias_bits(buf + len, size - len,
+				'a', id->absbit, 0, ABS_MAX);
+	len += input_print_modalias_bits(buf + len, size - len,
+				'm', id->mscbit, 0, MSC_MAX);
+	len += input_print_modalias_bits(buf + len, size - len,
+				'l', id->ledbit, 0, LED_MAX);
+	len += input_print_modalias_bits(buf + len, size - len,
+				's', id->sndbit, 0, SND_MAX);
+	len += input_print_modalias_bits(buf + len, size - len,
+				'f', id->ffbit, 0, FF_MAX);
+	len += input_print_modalias_bits(buf + len, size - len,
+				'w', id->swbit, 0, SW_MAX);
+
+	if (add_cr)
+		len += snprintf(buf + len, max(size - len, 0), "\n");
+
+	return len;
+}
+
+static ssize_t input_dev_show_modalias(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct input_dev *id = to_input_dev(dev);
+	ssize_t len;
+
+	len = input_print_modalias(buf, PAGE_SIZE, id, 1);
+
+	return min_t(int, len, PAGE_SIZE);
+}
+static DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL);
+
+static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap,
+			      int max, int add_cr);
+
+static ssize_t input_dev_show_properties(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct input_dev *input_dev = to_input_dev(dev);
+	int len = input_print_bitmap(buf, PAGE_SIZE, input_dev->propbit,
+				     INPUT_PROP_MAX, true);
+	return min_t(int, len, PAGE_SIZE);
+}
+static DEVICE_ATTR(properties, S_IRUGO, input_dev_show_properties, NULL);
+
+static struct attribute *input_dev_attrs[] = {
+	&dev_attr_name.attr,
+	&dev_attr_phys.attr,
+	&dev_attr_uniq.attr,
+	&dev_attr_modalias.attr,
+	&dev_attr_properties.attr,
+	NULL
+};
+
+static struct attribute_group input_dev_attr_group = {
+	.attrs	= input_dev_attrs,
+};
+
+#define INPUT_DEV_ID_ATTR(name)						\
+static ssize_t input_dev_show_id_##name(struct device *dev,		\
+					struct device_attribute *attr,	\
+					char *buf)			\
+{									\
+	struct input_dev *input_dev = to_input_dev(dev);		\
+	return scnprintf(buf, PAGE_SIZE, "%04x\n", input_dev->id.name);	\
+}									\
+static DEVICE_ATTR(name, S_IRUGO, input_dev_show_id_##name, NULL)
+
+INPUT_DEV_ID_ATTR(bustype);
+INPUT_DEV_ID_ATTR(vendor);
+INPUT_DEV_ID_ATTR(product);
+INPUT_DEV_ID_ATTR(version);
+
+static struct attribute *input_dev_id_attrs[] = {
+	&dev_attr_bustype.attr,
+	&dev_attr_vendor.attr,
+	&dev_attr_product.attr,
+	&dev_attr_version.attr,
+	NULL
+};
+
+static struct attribute_group input_dev_id_attr_group = {
+	.name	= "id",
+	.attrs	= input_dev_id_attrs,
+};
+
+static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap,
+			      int max, int add_cr)
+{
+	int i;
+	int len = 0;
+	bool skip_empty = true;
+
+	for (i = BITS_TO_LONGS(max) - 1; i >= 0; i--) {
+		len += input_bits_to_string(buf + len, max(buf_size - len, 0),
+					    bitmap[i], skip_empty);
+		if (len) {
+			skip_empty = false;
+			if (i > 0)
+				len += snprintf(buf + len, max(buf_size - len, 0), " ");
+		}
+	}
+
+	/*
+	 * If no output was produced print a single 0.
+	 */
+	if (len == 0)
+		len = snprintf(buf, buf_size, "%d", 0);
+
+	if (add_cr)
+		len += snprintf(buf + len, max(buf_size - len, 0), "\n");
+
+	return len;
+}
+
+#define INPUT_DEV_CAP_ATTR(ev, bm)					\
+static ssize_t input_dev_show_cap_##bm(struct device *dev,		\
+				       struct device_attribute *attr,	\
+				       char *buf)			\
+{									\
+	struct input_dev *input_dev = to_input_dev(dev);		\
+	int len = input_print_bitmap(buf, PAGE_SIZE,			\
+				     input_dev->bm##bit, ev##_MAX,	\
+				     true);				\
+	return min_t(int, len, PAGE_SIZE);				\
+}									\
+static DEVICE_ATTR(bm, S_IRUGO, input_dev_show_cap_##bm, NULL)
+
+INPUT_DEV_CAP_ATTR(EV, ev);
+INPUT_DEV_CAP_ATTR(KEY, key);
+INPUT_DEV_CAP_ATTR(REL, rel);
+INPUT_DEV_CAP_ATTR(ABS, abs);
+INPUT_DEV_CAP_ATTR(MSC, msc);
+INPUT_DEV_CAP_ATTR(LED, led);
+INPUT_DEV_CAP_ATTR(SND, snd);
+INPUT_DEV_CAP_ATTR(FF, ff);
+INPUT_DEV_CAP_ATTR(SW, sw);
+
+static struct attribute *input_dev_caps_attrs[] = {
+	&dev_attr_ev.attr,
+	&dev_attr_key.attr,
+	&dev_attr_rel.attr,
+	&dev_attr_abs.attr,
+	&dev_attr_msc.attr,
+	&dev_attr_led.attr,
+	&dev_attr_snd.attr,
+	&dev_attr_ff.attr,
+	&dev_attr_sw.attr,
+	NULL
+};
+
+static struct attribute_group input_dev_caps_attr_group = {
+	.name	= "capabilities",
+	.attrs	= input_dev_caps_attrs,
+};
+
+static const struct attribute_group *input_dev_attr_groups[] = {
+	&input_dev_attr_group,
+	&input_dev_id_attr_group,
+	&input_dev_caps_attr_group,
+	NULL
+};
+
+static void input_dev_release(struct device *device)
+{
+	struct input_dev *dev = to_input_dev(device);
+
+	input_ff_destroy(dev);
+	input_mt_destroy_slots(dev);
+	kfree(dev->absinfo);
+	kfree(dev->vals);
+	kfree(dev);
+
+	module_put(THIS_MODULE);
+}
+
+/*
+ * Input uevent interface - loading event handlers based on
+ * device bitfields.
+ */
+static int input_add_uevent_bm_var(struct kobj_uevent_env *env,
+				   const char *name, unsigned long *bitmap, int max)
+{
+	int len;
+
+	if (add_uevent_var(env, "%s", name))
+		return -ENOMEM;
+
+	len = input_print_bitmap(&env->buf[env->buflen - 1],
+				 sizeof(env->buf) - env->buflen,
+				 bitmap, max, false);
+	if (len >= (sizeof(env->buf) - env->buflen))
+		return -ENOMEM;
+
+	env->buflen += len;
+	return 0;
+}
+
+static int input_add_uevent_modalias_var(struct kobj_uevent_env *env,
+					 struct input_dev *dev)
+{
+	int len;
+
+	if (add_uevent_var(env, "MODALIAS="))
+		return -ENOMEM;
+
+	len = input_print_modalias(&env->buf[env->buflen - 1],
+				   sizeof(env->buf) - env->buflen,
+				   dev, 0);
+	if (len >= (sizeof(env->buf) - env->buflen))
+		return -ENOMEM;
+
+	env->buflen += len;
+	return 0;
+}
+
+#define INPUT_ADD_HOTPLUG_VAR(fmt, val...)				\
+	do {								\
+		int err = add_uevent_var(env, fmt, val);		\
+		if (err)						\
+			return err;					\
+	} while (0)
+
+#define INPUT_ADD_HOTPLUG_BM_VAR(name, bm, max)				\
+	do {								\
+		int err = input_add_uevent_bm_var(env, name, bm, max);	\
+		if (err)						\
+			return err;					\
+	} while (0)
+
+#define INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev)				\
+	do {								\
+		int err = input_add_uevent_modalias_var(env, dev);	\
+		if (err)						\
+			return err;					\
+	} while (0)
+
+static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env)
+{
+	struct input_dev *dev = to_input_dev(device);
+
+	INPUT_ADD_HOTPLUG_VAR("PRODUCT=%x/%x/%x/%x",
+				dev->id.bustype, dev->id.vendor,
+				dev->id.product, dev->id.version);
+	if (dev->name)
+		INPUT_ADD_HOTPLUG_VAR("NAME=\"%s\"", dev->name);
+	if (dev->phys)
+		INPUT_ADD_HOTPLUG_VAR("PHYS=\"%s\"", dev->phys);
+	if (dev->uniq)
+		INPUT_ADD_HOTPLUG_VAR("UNIQ=\"%s\"", dev->uniq);
+
+	INPUT_ADD_HOTPLUG_BM_VAR("PROP=", dev->propbit, INPUT_PROP_MAX);
+
+	INPUT_ADD_HOTPLUG_BM_VAR("EV=", dev->evbit, EV_MAX);
+	if (test_bit(EV_KEY, dev->evbit))
+		INPUT_ADD_HOTPLUG_BM_VAR("KEY=", dev->keybit, KEY_MAX);
+	if (test_bit(EV_REL, dev->evbit))
+		INPUT_ADD_HOTPLUG_BM_VAR("REL=", dev->relbit, REL_MAX);
+	if (test_bit(EV_ABS, dev->evbit))
+		INPUT_ADD_HOTPLUG_BM_VAR("ABS=", dev->absbit, ABS_MAX);
+	if (test_bit(EV_MSC, dev->evbit))
+		INPUT_ADD_HOTPLUG_BM_VAR("MSC=", dev->mscbit, MSC_MAX);
+	if (test_bit(EV_LED, dev->evbit))
+		INPUT_ADD_HOTPLUG_BM_VAR("LED=", dev->ledbit, LED_MAX);
+	if (test_bit(EV_SND, dev->evbit))
+		INPUT_ADD_HOTPLUG_BM_VAR("SND=", dev->sndbit, SND_MAX);
+	if (test_bit(EV_FF, dev->evbit))
+		INPUT_ADD_HOTPLUG_BM_VAR("FF=", dev->ffbit, FF_MAX);
+	if (test_bit(EV_SW, dev->evbit))
+		INPUT_ADD_HOTPLUG_BM_VAR("SW=", dev->swbit, SW_MAX);
+
+	INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev);
+
+	return 0;
+}
+
+#define INPUT_DO_TOGGLE(dev, type, bits, on)				\
+	do {								\
+		int i;							\
+		bool active;						\
+									\
+		if (!test_bit(EV_##type, dev->evbit))			\
+			break;						\
+									\
+		for_each_set_bit(i, dev->bits##bit, type##_CNT) {	\
+			active = test_bit(i, dev->bits);		\
+			if (!active && !on)				\
+				continue;				\
+									\
+			dev->event(dev, EV_##type, i, on ? active : 0);	\
+		}							\
+	} while (0)
+
+static void input_dev_toggle(struct input_dev *dev, bool activate)
+{
+	if (!dev->event)
+		return;
+
+	INPUT_DO_TOGGLE(dev, LED, led, activate);
+	INPUT_DO_TOGGLE(dev, SND, snd, activate);
+
+	if (activate && test_bit(EV_REP, dev->evbit)) {
+		dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]);
+		dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]);
+	}
+}
+
+/**
+ * input_reset_device() - reset/restore the state of input device
+ * @dev: input device whose state needs to be reset
+ *
+ * This function tries to reset the state of an opened input device and
+ * bring internal state and state if the hardware in sync with each other.
+ * We mark all keys as released, restore LED state, repeat rate, etc.
+ */
+void input_reset_device(struct input_dev *dev)
+{
+	unsigned long flags;
+
+	mutex_lock(&dev->mutex);
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	input_dev_toggle(dev, true);
+	input_dev_release_keys(dev);
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+	mutex_unlock(&dev->mutex);
+}
+EXPORT_SYMBOL(input_reset_device);
+
+#ifdef CONFIG_PM_SLEEP
+static int input_dev_suspend(struct device *dev)
+{
+	struct input_dev *input_dev = to_input_dev(dev);
+
+	spin_lock_irq(&input_dev->event_lock);
+
+	/*
+	 * Keys that are pressed now are unlikely to be
+	 * still pressed when we resume.
+	 */
+	input_dev_release_keys(input_dev);
+
+	/* Turn off LEDs and sounds, if any are active. */
+	input_dev_toggle(input_dev, false);
+
+	spin_unlock_irq(&input_dev->event_lock);
+
+	return 0;
+}
+
+static int input_dev_resume(struct device *dev)
+{
+	struct input_dev *input_dev = to_input_dev(dev);
+
+	spin_lock_irq(&input_dev->event_lock);
+
+	/* Restore state of LEDs and sounds, if any were active. */
+	input_dev_toggle(input_dev, true);
+
+	spin_unlock_irq(&input_dev->event_lock);
+
+	return 0;
+}
+
+static int input_dev_freeze(struct device *dev)
+{
+	struct input_dev *input_dev = to_input_dev(dev);
+
+	spin_lock_irq(&input_dev->event_lock);
+
+	/*
+	 * Keys that are pressed now are unlikely to be
+	 * still pressed when we resume.
+	 */
+	input_dev_release_keys(input_dev);
+
+	spin_unlock_irq(&input_dev->event_lock);
+
+	return 0;
+}
+
+static int input_dev_poweroff(struct device *dev)
+{
+	struct input_dev *input_dev = to_input_dev(dev);
+
+	spin_lock_irq(&input_dev->event_lock);
+
+	/* Turn off LEDs and sounds, if any are active. */
+	input_dev_toggle(input_dev, false);
+
+	spin_unlock_irq(&input_dev->event_lock);
+
+	return 0;
+}
+
+static const struct dev_pm_ops input_dev_pm_ops = {
+	.suspend	= input_dev_suspend,
+	.resume		= input_dev_resume,
+	.freeze		= input_dev_freeze,
+	.poweroff	= input_dev_poweroff,
+	.restore	= input_dev_resume,
+};
+#endif /* CONFIG_PM */
+
+static struct device_type input_dev_type = {
+	.groups		= input_dev_attr_groups,
+	.release	= input_dev_release,
+	.uevent		= input_dev_uevent,
+#ifdef CONFIG_PM_SLEEP
+	.pm		= &input_dev_pm_ops,
+#endif
+};
+
+static char *input_devnode(struct device *dev, umode_t *mode)
+{
+	return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev));
+}
+
+struct class input_class = {
+	.name		= "input",
+	.devnode	= input_devnode,
+};
+EXPORT_SYMBOL_GPL(input_class);
+
+/**
+ * input_allocate_device - allocate memory for new input device
+ *
+ * Returns prepared struct input_dev or %NULL.
+ *
+ * NOTE: Use input_free_device() to free devices that have not been
+ * registered; input_unregister_device() should be used for already
+ * registered devices.
+ */
+struct input_dev *input_allocate_device(void)
+{
+	static atomic_t input_no = ATOMIC_INIT(-1);
+	struct input_dev *dev;
+
+	dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
+	if (dev) {
+		dev->dev.type = &input_dev_type;
+		dev->dev.class = &input_class;
+		device_initialize(&dev->dev);
+		mutex_init(&dev->mutex);
+		spin_lock_init(&dev->event_lock);
+		init_timer(&dev->timer);
+		INIT_LIST_HEAD(&dev->h_list);
+		INIT_LIST_HEAD(&dev->node);
+
+		dev_set_name(&dev->dev, "input%lu",
+			     (unsigned long)atomic_inc_return(&input_no));
+
+		__module_get(THIS_MODULE);
+	}
+
+	return dev;
+}
+EXPORT_SYMBOL(input_allocate_device);
+
+struct input_devres {
+	struct input_dev *input;
+};
+
+static int devm_input_device_match(struct device *dev, void *res, void *data)
+{
+	struct input_devres *devres = res;
+
+	return devres->input == data;
+}
+
+static void devm_input_device_release(struct device *dev, void *res)
+{
+	struct input_devres *devres = res;
+	struct input_dev *input = devres->input;
+
+	dev_dbg(dev, "%s: dropping reference to %s\n",
+		__func__, dev_name(&input->dev));
+	input_put_device(input);
+}
+
+/**
+ * devm_input_allocate_device - allocate managed input device
+ * @dev: device owning the input device being created
+ *
+ * Returns prepared struct input_dev or %NULL.
+ *
+ * Managed input devices do not need to be explicitly unregistered or
+ * freed as it will be done automatically when owner device unbinds from
+ * its driver (or binding fails). Once managed input device is allocated,
+ * it is ready to be set up and registered in the same fashion as regular
+ * input device. There are no special devm_input_device_[un]register()
+ * variants, regular ones work with both managed and unmanaged devices,
+ * should you need them. In most cases however, managed input device need
+ * not be explicitly unregistered or freed.
+ *
+ * NOTE: the owner device is set up as parent of input device and users
+ * should not override it.
+ */
+struct input_dev *devm_input_allocate_device(struct device *dev)
+{
+	struct input_dev *input;
+	struct input_devres *devres;
+
+	devres = devres_alloc(devm_input_device_release,
+			      sizeof(struct input_devres), GFP_KERNEL);
+	if (!devres)
+		return NULL;
+
+	input = input_allocate_device();
+	if (!input) {
+		devres_free(devres);
+		return NULL;
+	}
+
+	input->dev.parent = dev;
+	input->devres_managed = true;
+
+	devres->input = input;
+	devres_add(dev, devres);
+
+	return input;
+}
+EXPORT_SYMBOL(devm_input_allocate_device);
+
+/**
+ * input_free_device - free memory occupied by input_dev structure
+ * @dev: input device to free
+ *
+ * This function should only be used if input_register_device()
+ * was not called yet or if it failed. Once device was registered
+ * use input_unregister_device() and memory will be freed once last
+ * reference to the device is dropped.
+ *
+ * Device should be allocated by input_allocate_device().
+ *
+ * NOTE: If there are references to the input device then memory
+ * will not be freed until last reference is dropped.
+ */
+void input_free_device(struct input_dev *dev)
+{
+	if (dev) {
+		if (dev->devres_managed)
+			WARN_ON(devres_destroy(dev->dev.parent,
+						devm_input_device_release,
+						devm_input_device_match,
+						dev));
+		input_put_device(dev);
+	}
+}
+EXPORT_SYMBOL(input_free_device);
+
+/**
+ * input_set_capability - mark device as capable of a certain event
+ * @dev: device that is capable of emitting or accepting event
+ * @type: type of the event (EV_KEY, EV_REL, etc...)
+ * @code: event code
+ *
+ * In addition to setting up corresponding bit in appropriate capability
+ * bitmap the function also adjusts dev->evbit.
+ */
+void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
+{
+	switch (type) {
+	case EV_KEY:
+		__set_bit(code, dev->keybit);
+		break;
+
+	case EV_REL:
+		__set_bit(code, dev->relbit);
+		break;
+
+	case EV_ABS:
+		input_alloc_absinfo(dev);
+		if (!dev->absinfo)
+			return;
+
+		__set_bit(code, dev->absbit);
+		break;
+
+	case EV_MSC:
+		__set_bit(code, dev->mscbit);
+		break;
+
+	case EV_SW:
+		__set_bit(code, dev->swbit);
+		break;
+
+	case EV_LED:
+		__set_bit(code, dev->ledbit);
+		break;
+
+	case EV_SND:
+		__set_bit(code, dev->sndbit);
+		break;
+
+	case EV_FF:
+		__set_bit(code, dev->ffbit);
+		break;
+
+	case EV_PWR:
+		/* do nothing */
+		break;
+
+	default:
+		pr_err("input_set_capability: unknown type %u (code %u)\n",
+		       type, code);
+		dump_stack();
+		return;
+	}
+
+	__set_bit(type, dev->evbit);
+}
+EXPORT_SYMBOL(input_set_capability);
+
+static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
+{
+	int mt_slots;
+	int i;
+	unsigned int events;
+
+	if (dev->mt) {
+		mt_slots = dev->mt->num_slots;
+	} else if (test_bit(ABS_MT_TRACKING_ID, dev->absbit)) {
+		mt_slots = dev->absinfo[ABS_MT_TRACKING_ID].maximum -
+			   dev->absinfo[ABS_MT_TRACKING_ID].minimum + 1,
+		mt_slots = clamp(mt_slots, 2, 32);
+	} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
+		mt_slots = 2;
+	} else {
+		mt_slots = 0;
+	}
+
+	events = mt_slots + 1; /* count SYN_MT_REPORT and SYN_REPORT */
+
+	if (test_bit(EV_ABS, dev->evbit))
+		for_each_set_bit(i, dev->absbit, ABS_CNT)
+			events += input_is_mt_axis(i) ? mt_slots : 1;
+
+	if (test_bit(EV_REL, dev->evbit))
+		events += bitmap_weight(dev->relbit, REL_CNT);
+
+	/* Make room for KEY and MSC events */
+	events += 7;
+
+	return events;
+}
+
+#define INPUT_CLEANSE_BITMASK(dev, type, bits)				\
+	do {								\
+		if (!test_bit(EV_##type, dev->evbit))			\
+			memset(dev->bits##bit, 0,			\
+				sizeof(dev->bits##bit));		\
+	} while (0)
+
+static void input_cleanse_bitmasks(struct input_dev *dev)
+{
+	INPUT_CLEANSE_BITMASK(dev, KEY, key);
+	INPUT_CLEANSE_BITMASK(dev, REL, rel);
+	INPUT_CLEANSE_BITMASK(dev, ABS, abs);
+	INPUT_CLEANSE_BITMASK(dev, MSC, msc);
+	INPUT_CLEANSE_BITMASK(dev, LED, led);
+	INPUT_CLEANSE_BITMASK(dev, SND, snd);
+	INPUT_CLEANSE_BITMASK(dev, FF, ff);
+	INPUT_CLEANSE_BITMASK(dev, SW, sw);
+}
+
+static void __input_unregister_device(struct input_dev *dev)
+{
+	struct input_handle *handle, *next;
+
+	input_disconnect_device(dev);
+
+	mutex_lock(&input_mutex);
+
+	list_for_each_entry_safe(handle, next, &dev->h_list, d_node)
+		handle->handler->disconnect(handle);
+	WARN_ON(!list_empty(&dev->h_list));
+
+	del_timer_sync(&dev->timer);
+	list_del_init(&dev->node);
+
+	input_wakeup_procfs_readers();
+
+	mutex_unlock(&input_mutex);
+
+	device_del(&dev->dev);
+}
+
+static void devm_input_device_unregister(struct device *dev, void *res)
+{
+	struct input_devres *devres = res;
+	struct input_dev *input = devres->input;
+
+	dev_dbg(dev, "%s: unregistering device %s\n",
+		__func__, dev_name(&input->dev));
+	__input_unregister_device(input);
+}
+
+/**
+ * input_enable_softrepeat - enable software autorepeat
+ * @dev: input device
+ * @delay: repeat delay
+ * @period: repeat period
+ *
+ * Enable software autorepeat on the input device.
+ */
+void input_enable_softrepeat(struct input_dev *dev, int delay, int period)
+{
+	dev->timer.data = (unsigned long) dev;
+	dev->timer.function = input_repeat_key;
+	dev->rep[REP_DELAY] = delay;
+	dev->rep[REP_PERIOD] = period;
+}
+EXPORT_SYMBOL(input_enable_softrepeat);
+
+/**
+ * input_register_device - register device with input core
+ * @dev: device to be registered
+ *
+ * This function registers device with input core. The device must be
+ * allocated with input_allocate_device() and all it's capabilities
+ * set up before registering.
+ * If function fails the device must be freed with input_free_device().
+ * Once device has been successfully registered it can be unregistered
+ * with input_unregister_device(); input_free_device() should not be
+ * called in this case.
+ *
+ * Note that this function is also used to register managed input devices
+ * (ones allocated with devm_input_allocate_device()). Such managed input
+ * devices need not be explicitly unregistered or freed, their tear down
+ * is controlled by the devres infrastructure. It is also worth noting
+ * that tear down of managed input devices is internally a 2-step process:
+ * registered managed input device is first unregistered, but stays in
+ * memory and can still handle input_event() calls (although events will
+ * not be delivered anywhere). The freeing of managed input device will
+ * happen later, when devres stack is unwound to the point where device
+ * allocation was made.
+ */
+int input_register_device(struct input_dev *dev)
+{
+	struct input_devres *devres = NULL;
+	struct input_handler *handler;
+	unsigned int packet_size;
+	const char *path;
+	int error;
+
+	if (dev->devres_managed) {
+		devres = devres_alloc(devm_input_device_unregister,
+				      sizeof(struct input_devres), GFP_KERNEL);
+		if (!devres)
+			return -ENOMEM;
+
+		devres->input = dev;
+	}
+
+	/* Every input device generates EV_SYN/SYN_REPORT events. */
+	__set_bit(EV_SYN, dev->evbit);
+
+	/* KEY_RESERVED is not supposed to be transmitted to userspace. */
+	__clear_bit(KEY_RESERVED, dev->keybit);
+
+	/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
+	input_cleanse_bitmasks(dev);
+
+	packet_size = input_estimate_events_per_packet(dev);
+	if (dev->hint_events_per_packet < packet_size)
+		dev->hint_events_per_packet = packet_size;
+
+	dev->max_vals = dev->hint_events_per_packet + 2;
+	dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
+	if (!dev->vals) {
+		error = -ENOMEM;
+		goto err_devres_free;
+	}
+
+	/*
+	 * If delay and period are pre-set by the driver, then autorepeating
+	 * is handled by the driver itself and we don't do it in input.c.
+	 */
+	if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
+		input_enable_softrepeat(dev, 250, 33);
+
+	if (!dev->getkeycode)
+		dev->getkeycode = input_default_getkeycode;
+
+	if (!dev->setkeycode)
+		dev->setkeycode = input_default_setkeycode;
+
+	error = device_add(&dev->dev);
+	if (error)
+		goto err_free_vals;
+
+	path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
+	pr_info("%s as %s\n",
+		dev->name ? dev->name : "Unspecified device",
+		path ? path : "N/A");
+	kfree(path);
+
+	error = mutex_lock_interruptible(&input_mutex);
+	if (error)
+		goto err_device_del;
+
+	list_add_tail(&dev->node, &input_dev_list);
+
+	list_for_each_entry(handler, &input_handler_list, node)
+		input_attach_handler(dev, handler);
+
+	input_wakeup_procfs_readers();
+
+	mutex_unlock(&input_mutex);
+
+	if (dev->devres_managed) {
+		dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
+			__func__, dev_name(&dev->dev));
+		devres_add(dev->dev.parent, devres);
+	}
+	return 0;
+
+err_device_del:
+	device_del(&dev->dev);
+err_free_vals:
+	kfree(dev->vals);
+	dev->vals = NULL;
+err_devres_free:
+	devres_free(devres);
+	return error;
+}
+EXPORT_SYMBOL(input_register_device);
+
+/**
+ * input_unregister_device - unregister previously registered device
+ * @dev: device to be unregistered
+ *
+ * This function unregisters an input device. Once device is unregistered
+ * the caller should not try to access it as it may get freed at any moment.
+ */
+void input_unregister_device(struct input_dev *dev)
+{
+	if (dev->devres_managed) {
+		WARN_ON(devres_destroy(dev->dev.parent,
+					devm_input_device_unregister,
+					devm_input_device_match,
+					dev));
+		__input_unregister_device(dev);
+		/*
+		 * We do not do input_put_device() here because it will be done
+		 * when 2nd devres fires up.
+		 */
+	} else {
+		__input_unregister_device(dev);
+		input_put_device(dev);
+	}
+}
+EXPORT_SYMBOL(input_unregister_device);
+
+/**
+ * input_register_handler - register a new input handler
+ * @handler: handler to be registered
+ *
+ * This function registers a new input handler (interface) for input
+ * devices in the system and attaches it to all input devices that
+ * are compatible with the handler.
+ */
+int input_register_handler(struct input_handler *handler)
+{
+	struct input_dev *dev;
+	int error;
+
+	error = mutex_lock_interruptible(&input_mutex);
+	if (error)
+		return error;
+
+	INIT_LIST_HEAD(&handler->h_list);
+
+	list_add_tail(&handler->node, &input_handler_list);
+
+	list_for_each_entry(dev, &input_dev_list, node)
+		input_attach_handler(dev, handler);
+
+	input_wakeup_procfs_readers();
+
+	mutex_unlock(&input_mutex);
+	return 0;
+}
+EXPORT_SYMBOL(input_register_handler);
+
+/**
+ * input_unregister_handler - unregisters an input handler
+ * @handler: handler to be unregistered
+ *
+ * This function disconnects a handler from its input devices and
+ * removes it from lists of known handlers.
+ */
+void input_unregister_handler(struct input_handler *handler)
+{
+	struct input_handle *handle, *next;
+
+	mutex_lock(&input_mutex);
+
+	list_for_each_entry_safe(handle, next, &handler->h_list, h_node)
+		handler->disconnect(handle);
+	WARN_ON(!list_empty(&handler->h_list));
+
+	list_del_init(&handler->node);
+
+	input_wakeup_procfs_readers();
+
+	mutex_unlock(&input_mutex);
+}
+EXPORT_SYMBOL(input_unregister_handler);
+
+/**
+ * input_handler_for_each_handle - handle iterator
+ * @handler: input handler to iterate
+ * @data: data for the callback
+ * @fn: function to be called for each handle
+ *
+ * Iterate over @bus's list of devices, and call @fn for each, passing
+ * it @data and stop when @fn returns a non-zero value. The function is
+ * using RCU to traverse the list and therefore may be using in atomic
+ * contexts. The @fn callback is invoked from RCU critical section and
+ * thus must not sleep.
+ */
+int input_handler_for_each_handle(struct input_handler *handler, void *data,
+				  int (*fn)(struct input_handle *, void *))
+{
+	struct input_handle *handle;
+	int retval = 0;
+
+	rcu_read_lock();
+
+	list_for_each_entry_rcu(handle, &handler->h_list, h_node) {
+		retval = fn(handle, data);
+		if (retval)
+			break;
+	}
+
+	rcu_read_unlock();
+
+	return retval;
+}
+EXPORT_SYMBOL(input_handler_for_each_handle);
+
+/**
+ * input_register_handle - register a new input handle
+ * @handle: handle to register
+ *
+ * This function puts a new input handle onto device's
+ * and handler's lists so that events can flow through
+ * it once it is opened using input_open_device().
+ *
+ * This function is supposed to be called from handler's
+ * connect() method.
+ */
+int input_register_handle(struct input_handle *handle)
+{
+	struct input_handler *handler = handle->handler;
+	struct input_dev *dev = handle->dev;
+	int error;
+
+	/*
+	 * We take dev->mutex here to prevent race with
+	 * input_release_device().
+	 */
+	error = mutex_lock_interruptible(&dev->mutex);
+	if (error)
+		return error;
+
+	/*
+	 * Filters go to the head of the list, normal handlers
+	 * to the tail.
+	 */
+	if (handler->filter)
+		list_add_rcu(&handle->d_node, &dev->h_list);
+	else
+		list_add_tail_rcu(&handle->d_node, &dev->h_list);
+
+	mutex_unlock(&dev->mutex);
+
+	/*
+	 * Since we are supposed to be called from ->connect()
+	 * which is mutually exclusive with ->disconnect()
+	 * we can't be racing with input_unregister_handle()
+	 * and so separate lock is not needed here.
+	 */
+	list_add_tail_rcu(&handle->h_node, &handler->h_list);
+
+	if (handler->start)
+		handler->start(handle);
+
+	return 0;
+}
+EXPORT_SYMBOL(input_register_handle);
+
+/**
+ * input_unregister_handle - unregister an input handle
+ * @handle: handle to unregister
+ *
+ * This function removes input handle from device's
+ * and handler's lists.
+ *
+ * This function is supposed to be called from handler's
+ * disconnect() method.
+ */
+void input_unregister_handle(struct input_handle *handle)
+{
+	struct input_dev *dev = handle->dev;
+
+	list_del_rcu(&handle->h_node);
+
+	/*
+	 * Take dev->mutex to prevent race with input_release_device().
+	 */
+	mutex_lock(&dev->mutex);
+	list_del_rcu(&handle->d_node);
+	mutex_unlock(&dev->mutex);
+
+	synchronize_rcu();
+}
+EXPORT_SYMBOL(input_unregister_handle);
+
+/**
+ * input_get_new_minor - allocates a new input minor number
+ * @legacy_base: beginning or the legacy range to be searched
+ * @legacy_num: size of legacy range
+ * @allow_dynamic: whether we can also take ID from the dynamic range
+ *
+ * This function allocates a new device minor for from input major namespace.
+ * Caller can request legacy minor by specifying @legacy_base and @legacy_num
+ * parameters and whether ID can be allocated from dynamic range if there are
+ * no free IDs in legacy range.
+ */
+int input_get_new_minor(int legacy_base, unsigned int legacy_num,
+			bool allow_dynamic)
+{
+	/*
+	 * This function should be called from input handler's ->connect()
+	 * methods, which are serialized with input_mutex, so no additional
+	 * locking is needed here.
+	 */
+	if (legacy_base >= 0) {
+		int minor = ida_simple_get(&input_ida,
+					   legacy_base,
+					   legacy_base + legacy_num,
+					   GFP_KERNEL);
+		if (minor >= 0 || !allow_dynamic)
+			return minor;
+	}
+
+	return ida_simple_get(&input_ida,
+			      INPUT_FIRST_DYNAMIC_DEV, INPUT_MAX_CHAR_DEVICES,
+			      GFP_KERNEL);
+}
+EXPORT_SYMBOL(input_get_new_minor);
+
+/**
+ * input_free_minor - release previously allocated minor
+ * @minor: minor to be released
+ *
+ * This function releases previously allocated input minor so that it can be
+ * reused later.
+ */
+void input_free_minor(unsigned int minor)
+{
+	ida_simple_remove(&input_ida, minor);
+}
+EXPORT_SYMBOL(input_free_minor);
+
+static int __init input_init(void)
+{
+	int err;
+
+	err = class_register(&input_class);
+	if (err) {
+		pr_err("unable to register input_dev class\n");
+		return err;
+	}
+
+	err = input_proc_init();
+	if (err)
+		goto fail1;
+
+	err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
+				     INPUT_MAX_CHAR_DEVICES, "input");
+	if (err) {
+		pr_err("unable to register char major %d", INPUT_MAJOR);
+		goto fail2;
+	}
+
+	return 0;
+
+ fail2:	input_proc_exit();
+ fail1:	class_unregister(&input_class);
+	return err;
+}
+
+static void __exit input_exit(void)
+{
+	input_proc_exit();
+	unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0),
+				 INPUT_MAX_CHAR_DEVICES);
+	class_unregister(&input_class);
+}
+
+subsys_initcall(input_init);
+module_exit(input_exit);
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
new file mode 100644
index 0000000..5d11fea
--- /dev/null
+++ b/drivers/input/joydev.c
@@ -0,0 +1,1006 @@
+/*
+ * Joystick device driver for the input driver suite.
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 1999 Colin Van Dyke
+ *
+ * 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 <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/joystick.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Joystick device interfaces");
+MODULE_SUPPORTED_DEVICE("input/js");
+MODULE_LICENSE("GPL");
+
+#define JOYDEV_MINOR_BASE	0
+#define JOYDEV_MINORS		16
+#define JOYDEV_BUFFER_SIZE	64
+
+struct joydev {
+	int open;
+	struct input_handle handle;
+	wait_queue_head_t wait;
+	struct list_head client_list;
+	spinlock_t client_lock; /* protects client_list */
+	struct mutex mutex;
+	struct device dev;
+	struct cdev cdev;
+	bool exist;
+
+	struct js_corr corr[ABS_CNT];
+	struct JS_DATA_SAVE_TYPE glue;
+	int nabs;
+	int nkey;
+	__u16 keymap[KEY_MAX - BTN_MISC + 1];
+	__u16 keypam[KEY_MAX - BTN_MISC + 1];
+	__u8 absmap[ABS_CNT];
+	__u8 abspam[ABS_CNT];
+	__s16 abs[ABS_CNT];
+};
+
+struct joydev_client {
+	struct js_event buffer[JOYDEV_BUFFER_SIZE];
+	int head;
+	int tail;
+	int startup;
+	spinlock_t buffer_lock; /* protects access to buffer, head and tail */
+	struct fasync_struct *fasync;
+	struct joydev *joydev;
+	struct list_head node;
+};
+
+static int joydev_correct(int value, struct js_corr *corr)
+{
+	switch (corr->type) {
+
+	case JS_CORR_NONE:
+		break;
+
+	case JS_CORR_BROKEN:
+		value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
+			((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
+			((corr->coef[2] * (value - corr->coef[0])) >> 14);
+		break;
+
+	default:
+		return 0;
+	}
+
+	return value < -32767 ? -32767 : (value > 32767 ? 32767 : value);
+}
+
+static void joydev_pass_event(struct joydev_client *client,
+			      struct js_event *event)
+{
+	struct joydev *joydev = client->joydev;
+
+	/*
+	 * IRQs already disabled, just acquire the lock
+	 */
+	spin_lock(&client->buffer_lock);
+
+	client->buffer[client->head] = *event;
+
+	if (client->startup == joydev->nabs + joydev->nkey) {
+		client->head++;
+		client->head &= JOYDEV_BUFFER_SIZE - 1;
+		if (client->tail == client->head)
+			client->startup = 0;
+	}
+
+	spin_unlock(&client->buffer_lock);
+
+	kill_fasync(&client->fasync, SIGIO, POLL_IN);
+}
+
+static void joydev_event(struct input_handle *handle,
+			 unsigned int type, unsigned int code, int value)
+{
+	struct joydev *joydev = handle->private;
+	struct joydev_client *client;
+	struct js_event event;
+
+	switch (type) {
+
+	case EV_KEY:
+		if (code < BTN_MISC || value == 2)
+			return;
+		event.type = JS_EVENT_BUTTON;
+		event.number = joydev->keymap[code - BTN_MISC];
+		event.value = value;
+		break;
+
+	case EV_ABS:
+		event.type = JS_EVENT_AXIS;
+		event.number = joydev->absmap[code];
+		event.value = joydev_correct(value,
+					&joydev->corr[event.number]);
+		if (event.value == joydev->abs[event.number])
+			return;
+		joydev->abs[event.number] = event.value;
+		break;
+
+	default:
+		return;
+	}
+
+	event.time = jiffies_to_msecs(jiffies);
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(client, &joydev->client_list, node)
+		joydev_pass_event(client, &event);
+	rcu_read_unlock();
+
+	wake_up_interruptible(&joydev->wait);
+}
+
+static int joydev_fasync(int fd, struct file *file, int on)
+{
+	struct joydev_client *client = file->private_data;
+
+	return fasync_helper(fd, file, on, &client->fasync);
+}
+
+static void joydev_free(struct device *dev)
+{
+	struct joydev *joydev = container_of(dev, struct joydev, dev);
+
+	input_put_device(joydev->handle.dev);
+	kfree(joydev);
+}
+
+static void joydev_attach_client(struct joydev *joydev,
+				 struct joydev_client *client)
+{
+	spin_lock(&joydev->client_lock);
+	list_add_tail_rcu(&client->node, &joydev->client_list);
+	spin_unlock(&joydev->client_lock);
+}
+
+static void joydev_detach_client(struct joydev *joydev,
+				 struct joydev_client *client)
+{
+	spin_lock(&joydev->client_lock);
+	list_del_rcu(&client->node);
+	spin_unlock(&joydev->client_lock);
+	synchronize_rcu();
+}
+
+static int joydev_open_device(struct joydev *joydev)
+{
+	int retval;
+
+	retval = mutex_lock_interruptible(&joydev->mutex);
+	if (retval)
+		return retval;
+
+	if (!joydev->exist)
+		retval = -ENODEV;
+	else if (!joydev->open++) {
+		retval = input_open_device(&joydev->handle);
+		if (retval)
+			joydev->open--;
+	}
+
+	mutex_unlock(&joydev->mutex);
+	return retval;
+}
+
+static void joydev_close_device(struct joydev *joydev)
+{
+	mutex_lock(&joydev->mutex);
+
+	if (joydev->exist && !--joydev->open)
+		input_close_device(&joydev->handle);
+
+	mutex_unlock(&joydev->mutex);
+}
+
+/*
+ * Wake up users waiting for IO so they can disconnect from
+ * dead device.
+ */
+static void joydev_hangup(struct joydev *joydev)
+{
+	struct joydev_client *client;
+
+	spin_lock(&joydev->client_lock);
+	list_for_each_entry(client, &joydev->client_list, node)
+		kill_fasync(&client->fasync, SIGIO, POLL_HUP);
+	spin_unlock(&joydev->client_lock);
+
+	wake_up_interruptible(&joydev->wait);
+}
+
+static int joydev_release(struct inode *inode, struct file *file)
+{
+	struct joydev_client *client = file->private_data;
+	struct joydev *joydev = client->joydev;
+
+	joydev_detach_client(joydev, client);
+	kfree(client);
+
+	joydev_close_device(joydev);
+
+	return 0;
+}
+
+static int joydev_open(struct inode *inode, struct file *file)
+{
+	struct joydev *joydev =
+			container_of(inode->i_cdev, struct joydev, cdev);
+	struct joydev_client *client;
+	int error;
+
+	client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL);
+	if (!client)
+		return -ENOMEM;
+
+	spin_lock_init(&client->buffer_lock);
+	client->joydev = joydev;
+	joydev_attach_client(joydev, client);
+
+	error = joydev_open_device(joydev);
+	if (error)
+		goto err_free_client;
+
+	file->private_data = client;
+	nonseekable_open(inode, file);
+
+	return 0;
+
+ err_free_client:
+	joydev_detach_client(joydev, client);
+	kfree(client);
+	return error;
+}
+
+static int joydev_generate_startup_event(struct joydev_client *client,
+					 struct input_dev *input,
+					 struct js_event *event)
+{
+	struct joydev *joydev = client->joydev;
+	int have_event;
+
+	spin_lock_irq(&client->buffer_lock);
+
+	have_event = client->startup < joydev->nabs + joydev->nkey;
+
+	if (have_event) {
+
+		event->time = jiffies_to_msecs(jiffies);
+		if (client->startup < joydev->nkey) {
+			event->type = JS_EVENT_BUTTON | JS_EVENT_INIT;
+			event->number = client->startup;
+			event->value = !!test_bit(joydev->keypam[event->number],
+						  input->key);
+		} else {
+			event->type = JS_EVENT_AXIS | JS_EVENT_INIT;
+			event->number = client->startup - joydev->nkey;
+			event->value = joydev->abs[event->number];
+		}
+		client->startup++;
+	}
+
+	spin_unlock_irq(&client->buffer_lock);
+
+	return have_event;
+}
+
+static int joydev_fetch_next_event(struct joydev_client *client,
+				   struct js_event *event)
+{
+	int have_event;
+
+	spin_lock_irq(&client->buffer_lock);
+
+	have_event = client->head != client->tail;
+	if (have_event) {
+		*event = client->buffer[client->tail++];
+		client->tail &= JOYDEV_BUFFER_SIZE - 1;
+	}
+
+	spin_unlock_irq(&client->buffer_lock);
+
+	return have_event;
+}
+
+/*
+ * Old joystick interface
+ */
+static ssize_t joydev_0x_read(struct joydev_client *client,
+			      struct input_dev *input,
+			      char __user *buf)
+{
+	struct joydev *joydev = client->joydev;
+	struct JS_DATA_TYPE data;
+	int i;
+
+	spin_lock_irq(&input->event_lock);
+
+	/*
+	 * Get device state
+	 */
+	for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++)
+		data.buttons |=
+			test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0;
+	data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x;
+	data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y;
+
+	/*
+	 * Reset reader's event queue
+	 */
+	spin_lock(&client->buffer_lock);
+	client->startup = 0;
+	client->tail = client->head;
+	spin_unlock(&client->buffer_lock);
+
+	spin_unlock_irq(&input->event_lock);
+
+	if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))
+		return -EFAULT;
+
+	return sizeof(struct JS_DATA_TYPE);
+}
+
+static inline int joydev_data_pending(struct joydev_client *client)
+{
+	struct joydev *joydev = client->joydev;
+
+	return client->startup < joydev->nabs + joydev->nkey ||
+		client->head != client->tail;
+}
+
+static ssize_t joydev_read(struct file *file, char __user *buf,
+			   size_t count, loff_t *ppos)
+{
+	struct joydev_client *client = file->private_data;
+	struct joydev *joydev = client->joydev;
+	struct input_dev *input = joydev->handle.dev;
+	struct js_event event;
+	int retval;
+
+	if (!joydev->exist)
+		return -ENODEV;
+
+	if (count < sizeof(struct js_event))
+		return -EINVAL;
+
+	if (count == sizeof(struct JS_DATA_TYPE))
+		return joydev_0x_read(client, input, buf);
+
+	if (!joydev_data_pending(client) && (file->f_flags & O_NONBLOCK))
+		return -EAGAIN;
+
+	retval = wait_event_interruptible(joydev->wait,
+			!joydev->exist || joydev_data_pending(client));
+	if (retval)
+		return retval;
+
+	if (!joydev->exist)
+		return -ENODEV;
+
+	while (retval + sizeof(struct js_event) <= count &&
+	       joydev_generate_startup_event(client, input, &event)) {
+
+		if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
+			return -EFAULT;
+
+		retval += sizeof(struct js_event);
+	}
+
+	while (retval + sizeof(struct js_event) <= count &&
+	       joydev_fetch_next_event(client, &event)) {
+
+		if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
+			return -EFAULT;
+
+		retval += sizeof(struct js_event);
+	}
+
+	return retval;
+}
+
+/* No kernel lock - fine */
+static unsigned int joydev_poll(struct file *file, poll_table *wait)
+{
+	struct joydev_client *client = file->private_data;
+	struct joydev *joydev = client->joydev;
+
+	poll_wait(file, &joydev->wait, wait);
+	return (joydev_data_pending(client) ? (POLLIN | POLLRDNORM) : 0) |
+		(joydev->exist ?  0 : (POLLHUP | POLLERR));
+}
+
+static int joydev_handle_JSIOCSAXMAP(struct joydev *joydev,
+				     void __user *argp, size_t len)
+{
+	__u8 *abspam;
+	int i;
+	int retval = 0;
+
+	len = min(len, sizeof(joydev->abspam));
+
+	/* Validate the map. */
+	abspam = memdup_user(argp, len);
+	if (IS_ERR(abspam))
+		return PTR_ERR(abspam);
+
+	for (i = 0; i < joydev->nabs; i++) {
+		if (abspam[i] > ABS_MAX) {
+			retval = -EINVAL;
+			goto out;
+		}
+	}
+
+	memcpy(joydev->abspam, abspam, len);
+
+	for (i = 0; i < joydev->nabs; i++)
+		joydev->absmap[joydev->abspam[i]] = i;
+
+ out:
+	kfree(abspam);
+	return retval;
+}
+
+static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev,
+				      void __user *argp, size_t len)
+{
+	__u16 *keypam;
+	int i;
+	int retval = 0;
+
+	len = min(len, sizeof(joydev->keypam));
+
+	/* Validate the map. */
+	keypam = memdup_user(argp, len);
+	if (IS_ERR(keypam))
+		return PTR_ERR(keypam);
+
+	for (i = 0; i < joydev->nkey; i++) {
+		if (keypam[i] > KEY_MAX || keypam[i] < BTN_MISC) {
+			retval = -EINVAL;
+			goto out;
+		}
+	}
+
+	memcpy(joydev->keypam, keypam, len);
+
+	for (i = 0; i < joydev->nkey; i++)
+		joydev->keymap[keypam[i] - BTN_MISC] = i;
+
+ out:
+	kfree(keypam);
+	return retval;
+}
+
+
+static int joydev_ioctl_common(struct joydev *joydev,
+				unsigned int cmd, void __user *argp)
+{
+	struct input_dev *dev = joydev->handle.dev;
+	size_t len;
+	int i;
+	const char *name;
+
+	/* Process fixed-sized commands. */
+	switch (cmd) {
+
+	case JS_SET_CAL:
+		return copy_from_user(&joydev->glue.JS_CORR, argp,
+				sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0;
+
+	case JS_GET_CAL:
+		return copy_to_user(argp, &joydev->glue.JS_CORR,
+				sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0;
+
+	case JS_SET_TIMEOUT:
+		return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
+
+	case JS_GET_TIMEOUT:
+		return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
+
+	case JSIOCGVERSION:
+		return put_user(JS_VERSION, (__u32 __user *) argp);
+
+	case JSIOCGAXES:
+		return put_user(joydev->nabs, (__u8 __user *) argp);
+
+	case JSIOCGBUTTONS:
+		return put_user(joydev->nkey, (__u8 __user *) argp);
+
+	case JSIOCSCORR:
+		if (copy_from_user(joydev->corr, argp,
+			      sizeof(joydev->corr[0]) * joydev->nabs))
+			return -EFAULT;
+
+		for (i = 0; i < joydev->nabs; i++) {
+			int val = input_abs_get_val(dev, joydev->abspam[i]);
+			joydev->abs[i] = joydev_correct(val, &joydev->corr[i]);
+		}
+		return 0;
+
+	case JSIOCGCORR:
+		return copy_to_user(argp, joydev->corr,
+			sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0;
+
+	}
+
+	/*
+	 * Process variable-sized commands (the axis and button map commands
+	 * are considered variable-sized to decouple them from the values of
+	 * ABS_MAX and KEY_MAX).
+	 */
+	switch (cmd & ~IOCSIZE_MASK) {
+
+	case (JSIOCSAXMAP & ~IOCSIZE_MASK):
+		return joydev_handle_JSIOCSAXMAP(joydev, argp, _IOC_SIZE(cmd));
+
+	case (JSIOCGAXMAP & ~IOCSIZE_MASK):
+		len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->abspam));
+		return copy_to_user(argp, joydev->abspam, len) ? -EFAULT : len;
+
+	case (JSIOCSBTNMAP & ~IOCSIZE_MASK):
+		return joydev_handle_JSIOCSBTNMAP(joydev, argp, _IOC_SIZE(cmd));
+
+	case (JSIOCGBTNMAP & ~IOCSIZE_MASK):
+		len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->keypam));
+		return copy_to_user(argp, joydev->keypam, len) ? -EFAULT : len;
+
+	case JSIOCGNAME(0):
+		name = dev->name;
+		if (!name)
+			return 0;
+
+		len = min_t(size_t, _IOC_SIZE(cmd), strlen(name) + 1);
+		return copy_to_user(argp, name, len) ? -EFAULT : len;
+	}
+
+	return -EINVAL;
+}
+
+#ifdef CONFIG_COMPAT
+static long joydev_compat_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct joydev_client *client = file->private_data;
+	struct joydev *joydev = client->joydev;
+	void __user *argp = (void __user *)arg;
+	s32 tmp32;
+	struct JS_DATA_SAVE_TYPE_32 ds32;
+	int retval;
+
+	retval = mutex_lock_interruptible(&joydev->mutex);
+	if (retval)
+		return retval;
+
+	if (!joydev->exist) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	switch (cmd) {
+
+	case JS_SET_TIMELIMIT:
+		retval = get_user(tmp32, (s32 __user *) arg);
+		if (retval == 0)
+			joydev->glue.JS_TIMELIMIT = tmp32;
+		break;
+
+	case JS_GET_TIMELIMIT:
+		tmp32 = joydev->glue.JS_TIMELIMIT;
+		retval = put_user(tmp32, (s32 __user *) arg);
+		break;
+
+	case JS_SET_ALL:
+		retval = copy_from_user(&ds32, argp,
+					sizeof(ds32)) ? -EFAULT : 0;
+		if (retval == 0) {
+			joydev->glue.JS_TIMEOUT    = ds32.JS_TIMEOUT;
+			joydev->glue.BUSY          = ds32.BUSY;
+			joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME;
+			joydev->glue.JS_TIMELIMIT  = ds32.JS_TIMELIMIT;
+			joydev->glue.JS_SAVE       = ds32.JS_SAVE;
+			joydev->glue.JS_CORR       = ds32.JS_CORR;
+		}
+		break;
+
+	case JS_GET_ALL:
+		ds32.JS_TIMEOUT    = joydev->glue.JS_TIMEOUT;
+		ds32.BUSY          = joydev->glue.BUSY;
+		ds32.JS_EXPIRETIME = joydev->glue.JS_EXPIRETIME;
+		ds32.JS_TIMELIMIT  = joydev->glue.JS_TIMELIMIT;
+		ds32.JS_SAVE       = joydev->glue.JS_SAVE;
+		ds32.JS_CORR       = joydev->glue.JS_CORR;
+
+		retval = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0;
+		break;
+
+	default:
+		retval = joydev_ioctl_common(joydev, cmd, argp);
+		break;
+	}
+
+ out:
+	mutex_unlock(&joydev->mutex);
+	return retval;
+}
+#endif /* CONFIG_COMPAT */
+
+static long joydev_ioctl(struct file *file,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct joydev_client *client = file->private_data;
+	struct joydev *joydev = client->joydev;
+	void __user *argp = (void __user *)arg;
+	int retval;
+
+	retval = mutex_lock_interruptible(&joydev->mutex);
+	if (retval)
+		return retval;
+
+	if (!joydev->exist) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	switch (cmd) {
+
+	case JS_SET_TIMELIMIT:
+		retval = get_user(joydev->glue.JS_TIMELIMIT,
+				  (long __user *) arg);
+		break;
+
+	case JS_GET_TIMELIMIT:
+		retval = put_user(joydev->glue.JS_TIMELIMIT,
+				  (long __user *) arg);
+		break;
+
+	case JS_SET_ALL:
+		retval = copy_from_user(&joydev->glue, argp,
+					sizeof(joydev->glue)) ? -EFAULT : 0;
+		break;
+
+	case JS_GET_ALL:
+		retval = copy_to_user(argp, &joydev->glue,
+				      sizeof(joydev->glue)) ? -EFAULT : 0;
+		break;
+
+	default:
+		retval = joydev_ioctl_common(joydev, cmd, argp);
+		break;
+	}
+ out:
+	mutex_unlock(&joydev->mutex);
+	return retval;
+}
+
+static const struct file_operations joydev_fops = {
+	.owner		= THIS_MODULE,
+	.read		= joydev_read,
+	.poll		= joydev_poll,
+	.open		= joydev_open,
+	.release	= joydev_release,
+	.unlocked_ioctl	= joydev_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= joydev_compat_ioctl,
+#endif
+	.fasync		= joydev_fasync,
+	.llseek		= no_llseek,
+};
+
+/*
+ * Mark device non-existent. This disables writes, ioctls and
+ * prevents new users from opening the device. Already posted
+ * blocking reads will stay, however new ones will fail.
+ */
+static void joydev_mark_dead(struct joydev *joydev)
+{
+	mutex_lock(&joydev->mutex);
+	joydev->exist = false;
+	mutex_unlock(&joydev->mutex);
+}
+
+static void joydev_cleanup(struct joydev *joydev)
+{
+	struct input_handle *handle = &joydev->handle;
+
+	joydev_mark_dead(joydev);
+	joydev_hangup(joydev);
+
+	cdev_del(&joydev->cdev);
+
+	/* joydev is marked dead so no one else accesses joydev->open */
+	if (joydev->open)
+		input_close_device(handle);
+}
+
+static bool joydev_dev_is_absolute_mouse(struct input_dev *dev)
+{
+	DECLARE_BITMAP(jd_scratch, KEY_CNT);
+
+	BUILD_BUG_ON(ABS_CNT > KEY_CNT || EV_CNT > KEY_CNT);
+
+	/*
+	 * Virtualization (VMware, etc) and remote management (HP
+	 * ILO2) solutions use absolute coordinates for their virtual
+	 * pointing devices so that there is one-to-one relationship
+	 * between pointer position on the host screen and virtual
+	 * guest screen, and so their mice use ABS_X, ABS_Y and 3
+	 * primary button events. This clashes with what joydev
+	 * considers to be joysticks (a device with at minimum ABS_X
+	 * axis).
+	 *
+	 * Here we are trying to separate absolute mice from
+	 * joysticks. A device is, for joystick detection purposes,
+	 * considered to be an absolute mouse if the following is
+	 * true:
+	 *
+	 * 1) Event types are exactly EV_ABS, EV_KEY and EV_SYN.
+	 * 2) Absolute events are exactly ABS_X and ABS_Y.
+	 * 3) Keys are exactly BTN_LEFT, BTN_RIGHT and BTN_MIDDLE.
+	 * 4) Device is not on "Amiga" bus.
+	 */
+
+	bitmap_zero(jd_scratch, EV_CNT);
+	__set_bit(EV_ABS, jd_scratch);
+	__set_bit(EV_KEY, jd_scratch);
+	__set_bit(EV_SYN, jd_scratch);
+	if (!bitmap_equal(jd_scratch, dev->evbit, EV_CNT))
+		return false;
+
+	bitmap_zero(jd_scratch, ABS_CNT);
+	__set_bit(ABS_X, jd_scratch);
+	__set_bit(ABS_Y, jd_scratch);
+	if (!bitmap_equal(dev->absbit, jd_scratch, ABS_CNT))
+		return false;
+
+	bitmap_zero(jd_scratch, KEY_CNT);
+	__set_bit(BTN_LEFT, jd_scratch);
+	__set_bit(BTN_RIGHT, jd_scratch);
+	__set_bit(BTN_MIDDLE, jd_scratch);
+
+	if (!bitmap_equal(dev->keybit, jd_scratch, KEY_CNT))
+		return false;
+
+	/*
+	 * Amiga joystick (amijoy) historically uses left/middle/right
+	 * button events.
+	 */
+	if (dev->id.bustype == BUS_AMIGA)
+		return false;
+
+	return true;
+}
+
+static bool joydev_match(struct input_handler *handler, struct input_dev *dev)
+{
+	/* Avoid touchpads and touchscreens */
+	if (test_bit(EV_KEY, dev->evbit) && test_bit(BTN_TOUCH, dev->keybit))
+		return false;
+
+	/* Avoid tablets, digitisers and similar devices */
+	if (test_bit(EV_KEY, dev->evbit) && test_bit(BTN_DIGI, dev->keybit))
+		return false;
+
+	/* Avoid absolute mice */
+	if (joydev_dev_is_absolute_mouse(dev))
+		return false;
+
+	return true;
+}
+
+static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
+			  const struct input_device_id *id)
+{
+	struct joydev *joydev;
+	int i, j, t, minor, dev_no;
+	int error;
+
+	minor = input_get_new_minor(JOYDEV_MINOR_BASE, JOYDEV_MINORS, true);
+	if (minor < 0) {
+		error = minor;
+		pr_err("failed to reserve new minor: %d\n", error);
+		return error;
+	}
+
+	joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL);
+	if (!joydev) {
+		error = -ENOMEM;
+		goto err_free_minor;
+	}
+
+	INIT_LIST_HEAD(&joydev->client_list);
+	spin_lock_init(&joydev->client_lock);
+	mutex_init(&joydev->mutex);
+	init_waitqueue_head(&joydev->wait);
+	joydev->exist = true;
+
+	dev_no = minor;
+	/* Normalize device number if it falls into legacy range */
+	if (dev_no < JOYDEV_MINOR_BASE + JOYDEV_MINORS)
+		dev_no -= JOYDEV_MINOR_BASE;
+	dev_set_name(&joydev->dev, "js%d", dev_no);
+
+	joydev->handle.dev = input_get_device(dev);
+	joydev->handle.name = dev_name(&joydev->dev);
+	joydev->handle.handler = handler;
+	joydev->handle.private = joydev;
+
+	for_each_set_bit(i, dev->absbit, ABS_CNT) {
+		joydev->absmap[i] = joydev->nabs;
+		joydev->abspam[joydev->nabs] = i;
+		joydev->nabs++;
+	}
+
+	for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC + 1; i++)
+		if (test_bit(i + BTN_MISC, dev->keybit)) {
+			joydev->keymap[i] = joydev->nkey;
+			joydev->keypam[joydev->nkey] = i + BTN_MISC;
+			joydev->nkey++;
+		}
+
+	for (i = 0; i < BTN_JOYSTICK - BTN_MISC; i++)
+		if (test_bit(i + BTN_MISC, dev->keybit)) {
+			joydev->keymap[i] = joydev->nkey;
+			joydev->keypam[joydev->nkey] = i + BTN_MISC;
+			joydev->nkey++;
+		}
+
+	for (i = 0; i < joydev->nabs; i++) {
+		j = joydev->abspam[i];
+		if (input_abs_get_max(dev, j) == input_abs_get_min(dev, j)) {
+			joydev->corr[i].type = JS_CORR_NONE;
+			joydev->abs[i] = input_abs_get_val(dev, j);
+			continue;
+		}
+		joydev->corr[i].type = JS_CORR_BROKEN;
+		joydev->corr[i].prec = input_abs_get_fuzz(dev, j);
+
+		t = (input_abs_get_max(dev, j) + input_abs_get_min(dev, j)) / 2;
+		joydev->corr[i].coef[0] = t - input_abs_get_flat(dev, j);
+		joydev->corr[i].coef[1] = t + input_abs_get_flat(dev, j);
+
+		t = (input_abs_get_max(dev, j) - input_abs_get_min(dev, j)) / 2
+			- 2 * input_abs_get_flat(dev, j);
+		if (t) {
+			joydev->corr[i].coef[2] = (1 << 29) / t;
+			joydev->corr[i].coef[3] = (1 << 29) / t;
+
+			joydev->abs[i] =
+				joydev_correct(input_abs_get_val(dev, j),
+					       joydev->corr + i);
+		}
+	}
+
+	joydev->dev.devt = MKDEV(INPUT_MAJOR, minor);
+	joydev->dev.class = &input_class;
+	joydev->dev.parent = &dev->dev;
+	joydev->dev.release = joydev_free;
+	device_initialize(&joydev->dev);
+
+	error = input_register_handle(&joydev->handle);
+	if (error)
+		goto err_free_joydev;
+
+	cdev_init(&joydev->cdev, &joydev_fops);
+	joydev->cdev.kobj.parent = &joydev->dev.kobj;
+	error = cdev_add(&joydev->cdev, joydev->dev.devt, 1);
+	if (error)
+		goto err_unregister_handle;
+
+	error = device_add(&joydev->dev);
+	if (error)
+		goto err_cleanup_joydev;
+
+	return 0;
+
+ err_cleanup_joydev:
+	joydev_cleanup(joydev);
+ err_unregister_handle:
+	input_unregister_handle(&joydev->handle);
+ err_free_joydev:
+	put_device(&joydev->dev);
+ err_free_minor:
+	input_free_minor(minor);
+	return error;
+}
+
+static void joydev_disconnect(struct input_handle *handle)
+{
+	struct joydev *joydev = handle->private;
+
+	device_del(&joydev->dev);
+	joydev_cleanup(joydev);
+	input_free_minor(MINOR(joydev->dev.devt));
+	input_unregister_handle(handle);
+	put_device(&joydev->dev);
+}
+
+static const struct input_device_id joydev_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+				INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.evbit = { BIT_MASK(EV_ABS) },
+		.absbit = { BIT_MASK(ABS_X) },
+	},
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+				INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.evbit = { BIT_MASK(EV_ABS) },
+		.absbit = { BIT_MASK(ABS_WHEEL) },
+	},
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+				INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.evbit = { BIT_MASK(EV_ABS) },
+		.absbit = { BIT_MASK(ABS_THROTTLE) },
+	},
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+				INPUT_DEVICE_ID_MATCH_KEYBIT,
+		.evbit = { BIT_MASK(EV_KEY) },
+		.keybit = {[BIT_WORD(BTN_JOYSTICK)] = BIT_MASK(BTN_JOYSTICK) },
+	},
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+				INPUT_DEVICE_ID_MATCH_KEYBIT,
+		.evbit = { BIT_MASK(EV_KEY) },
+		.keybit = { [BIT_WORD(BTN_GAMEPAD)] = BIT_MASK(BTN_GAMEPAD) },
+	},
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+				INPUT_DEVICE_ID_MATCH_KEYBIT,
+		.evbit = { BIT_MASK(EV_KEY) },
+		.keybit = { [BIT_WORD(BTN_TRIGGER_HAPPY)] = BIT_MASK(BTN_TRIGGER_HAPPY) },
+	},
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, joydev_ids);
+
+static struct input_handler joydev_handler = {
+	.event		= joydev_event,
+	.match		= joydev_match,
+	.connect	= joydev_connect,
+	.disconnect	= joydev_disconnect,
+	.legacy_minors	= true,
+	.minor		= JOYDEV_MINOR_BASE,
+	.name		= "joydev",
+	.id_table	= joydev_ids,
+};
+
+static int __init joydev_init(void)
+{
+	return input_register_handler(&joydev_handler);
+}
+
+static void __exit joydev_exit(void)
+{
+	input_unregister_handler(&joydev_handler);
+}
+
+module_init(joydev_init);
+module_exit(joydev_exit);
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
new file mode 100644
index 0000000..4215b53
--- /dev/null
+++ b/drivers/input/joystick/Kconfig
@@ -0,0 +1,333 @@
+#
+# Joystick driver configuration
+#
+menuconfig INPUT_JOYSTICK
+	bool "Joysticks/Gamepads"
+	help
+	  If you have a joystick, 6dof controller, gamepad, steering wheel,
+	  weapon control system or something like that you can say Y here
+	  and the list of supported devices will be displayed. This option
+	  doesn't affect the kernel.
+
+	  Please read the file <file:Documentation/input/joystick.txt> which
+	  contains more information.
+
+if INPUT_JOYSTICK
+
+config JOYSTICK_ANALOG
+	tristate "Classic PC analog joysticks and gamepads"
+	select GAMEPORT
+	---help---
+	  Say Y here if you have a joystick that connects to the PC
+	  gameport. In addition to the usual PC analog joystick, this driver
+	  supports many extensions, including joysticks with throttle control,
+	  with rudders, additional hats and buttons compatible with CH
+	  Flightstick Pro, ThrustMaster FCS, 6 and 8 button gamepads, or
+	  Saitek Cyborg joysticks.
+
+	  Please read the file <file:Documentation/input/joystick.txt> which
+	  contains more information.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called analog.
+
+config JOYSTICK_A3D
+	tristate "Assassin 3D and MadCatz Panther devices"
+	select GAMEPORT
+	help
+	  Say Y here if you have an FPGaming or MadCatz controller using the
+	  A3D protocol over the PC gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called a3d.
+
+config JOYSTICK_ADI
+	tristate "Logitech ADI digital joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have a Logitech controller using the ADI
+	  protocol over the PC gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adi.
+
+config JOYSTICK_COBRA
+	tristate "Creative Labs Blaster Cobra gamepad"
+	select GAMEPORT
+	help
+	  Say Y here if you have a Creative Labs Blaster Cobra gamepad.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cobra.
+
+config JOYSTICK_GF2K
+	tristate "Genius Flight2000 Digital joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have a Genius Flight2000 or MaxFighter digitally
+	  communicating joystick or gamepad.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gf2k.
+
+config JOYSTICK_GRIP
+	tristate "Gravis GrIP joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have a Gravis controller using the GrIP protocol
+	  over the PC gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called grip.
+
+config JOYSTICK_GRIP_MP
+	tristate "Gravis GrIP MultiPort"
+	select GAMEPORT
+	help
+	  Say Y here if you have the original Gravis GrIP MultiPort, a hub
+	  that connects to the gameport and you connect gamepads to it.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called grip_mp.
+
+config JOYSTICK_GUILLEMOT
+	tristate "Guillemot joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have a Guillemot joystick using a digital
+	  protocol over the PC gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called guillemot.
+
+config JOYSTICK_INTERACT
+	tristate "InterAct digital joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have an InterAct gameport or joystick
+	  communicating digitally over the gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called interact.
+
+config JOYSTICK_SIDEWINDER
+	tristate "Microsoft SideWinder digital joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have a Microsoft controller using the Digital
+	  Overdrive protocol over PC gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sidewinder.
+
+config JOYSTICK_TMDC
+	tristate "ThrustMaster DirectConnect joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have a ThrustMaster controller using the
+	  DirectConnect (BSP) protocol over the PC gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tmdc.
+
+source "drivers/input/joystick/iforce/Kconfig"
+
+config JOYSTICK_WARRIOR
+	tristate "Logitech WingMan Warrior joystick"
+	select SERIO
+	help
+	  Say Y here if you have a Logitech WingMan Warrior joystick connected
+	  to your computer's serial port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called warrior.
+
+config JOYSTICK_MAGELLAN
+	tristate "LogiCad3d Magellan/SpaceMouse 6dof controllers"
+	select SERIO
+	help
+	  Say Y here if you have a Magellan or Space Mouse 6DOF controller
+	  connected to your computer's serial port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called magellan.
+
+config JOYSTICK_SPACEORB
+	tristate "SpaceTec SpaceOrb/Avenger 6dof controllers"
+	select SERIO
+	help
+	  Say Y here if you have a SpaceOrb 360 or SpaceBall Avenger 6DOF
+	  controller connected to your computer's serial port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called spaceorb.
+
+config JOYSTICK_SPACEBALL
+	tristate "SpaceTec SpaceBall 6dof controllers"
+	select SERIO
+	help
+	  Say Y here if you have a SpaceTec SpaceBall 2003/3003/4000 FLX
+	  controller connected to your computer's serial port. For the
+	  SpaceBall 4000 USB model, use the USB HID driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called spaceball.
+
+config JOYSTICK_STINGER
+	tristate "Gravis Stinger gamepad"
+	select SERIO
+	help
+	  Say Y here if you have a Gravis Stinger connected to one of your
+	  serial ports.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called stinger.
+
+config JOYSTICK_TWIDJOY
+	tristate "Twiddler as a joystick"
+	select SERIO
+	help
+	  Say Y here if you have a Handykey Twiddler connected to your
+	  computer's serial port and want to use it as a joystick.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called twidjoy.
+
+config JOYSTICK_ZHENHUA
+	tristate "5-byte Zhenhua RC transmitter"
+	select SERIO
+	select BITREVERSE
+	help
+	  Say Y here if you have a Zhen Hua PPM-4CH transmitter which is
+	  supplied with a ready to fly micro electric indoor helicopters
+	  such as EasyCopter, Lama, MiniCopter, DragonFly or Jabo and want
+	  to use it via serial cable as a joystick.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called zhenhua.
+
+config JOYSTICK_DB9
+	tristate "Multisystem, Sega Genesis, Saturn joysticks and gamepads"
+	depends on PARPORT
+	help
+	  Say Y here if you have a Sega Master System gamepad, Sega Genesis
+	  gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga,
+	  Commodore, Amstrad CPC joystick connected to your parallel port.
+	  For more information on how to use the driver please read
+	  <file:Documentation/input/joystick-parport.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called db9.
+
+config JOYSTICK_GAMECON
+	tristate "Multisystem, NES, SNES, N64, PSX joysticks and gamepads"
+	depends on PARPORT
+	select INPUT_FF_MEMLESS
+	---help---
+	  Say Y here if you have a Nintendo Entertainment System gamepad,
+	  Super Nintendo Entertainment System gamepad, Nintendo 64 gamepad,
+	  Sony PlayStation gamepad or a Multisystem -- Atari, Amiga,
+	  Commodore, Amstrad CPC joystick connected to your parallel port.
+	  For more information on how to use the driver please read
+	  <file:Documentation/input/joystick-parport.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gamecon.
+
+config JOYSTICK_TURBOGRAFX
+	tristate "Multisystem joysticks via TurboGraFX device"
+	depends on PARPORT
+	help
+	  Say Y here if you have the TurboGraFX interface by Steffen Schwenke,
+	  and want to use it with Multisystem -- Atari, Amiga, Commodore,
+	  Amstrad CPC joystick. For more information on how to use the driver
+	  please read <file:Documentation/input/joystick-parport.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called turbografx.
+
+config JOYSTICK_AMIGA
+	tristate "Amiga joysticks"
+	depends on AMIGA
+	help
+	  Say Y here if you have an Amiga with a digital joystick connected
+	  to it.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called amijoy.
+
+config JOYSTICK_AS5011
+	tristate "Austria Microsystem AS5011 joystick"
+	depends on I2C
+	help
+	  Say Y here if you have an AS5011 digital joystick connected to your
+	  system.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called as5011.
+
+config JOYSTICK_JOYDUMP
+	tristate "Gameport data dumper"
+	select GAMEPORT
+	help
+	  Say Y here if you want to dump data from your joystick into the system
+	  log for debugging purposes. Say N if you are making a production
+	  configuration or aren't sure.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called joydump.
+
+config JOYSTICK_XPAD
+	tristate "X-Box gamepad support"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you want to use the X-Box pad with your computer.
+	  Make sure to say Y to "Joystick support" (CONFIG_INPUT_JOYDEV)
+	  and/or "Event interface support" (CONFIG_INPUT_EVDEV) as well.
+
+	  For information about how to connect the X-Box pad to USB, see
+	  <file:Documentation/input/xpad.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called xpad.
+
+config JOYSTICK_XPAD_FF
+	bool "X-Box gamepad rumble support"
+	depends on JOYSTICK_XPAD && INPUT
+	select INPUT_FF_MEMLESS
+	---help---
+	  Say Y here if you want to take advantage of xbox 360 rumble features.
+
+config JOYSTICK_XPAD_LEDS
+	bool "LED Support for Xbox360 controller 'BigX' LED"
+	depends on JOYSTICK_XPAD && (LEDS_CLASS=y || LEDS_CLASS=JOYSTICK_XPAD)
+	---help---
+	  This option enables support for the LED which surrounds the Big X on
+	  XBox 360 controller.
+
+config JOYSTICK_WALKERA0701
+	tristate "Walkera WK-0701 RC transmitter"
+	depends on HIGH_RES_TIMERS && PARPORT
+	help
+	  Say Y or M here if you have a Walkera WK-0701 transmitter which is
+	  supplied with a ready to fly Walkera helicopters such as HM36,
+	  HM37, HM60 and want to use it via parport as a joystick. More
+	  information is available: <file:Documentation/input/walkera0701.txt>
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called walkera0701.
+
+config JOYSTICK_MAPLE
+	tristate "Dreamcast control pad"
+	depends on MAPLE
+	help
+	  Say Y here if you have a SEGA Dreamcast and want to use your
+	  controller as a joystick.
+
+	  Most Dreamcast users will say Y.
+
+	  To compile this as a module choose M here: the module will be called
+	  maplecontrol.
+
+endif
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
new file mode 100644
index 0000000..92dc0de
--- /dev/null
+++ b/drivers/input/joystick/Makefile
@@ -0,0 +1,35 @@
+#
+# Makefile for the input core drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_JOYSTICK_A3D)		+= a3d.o
+obj-$(CONFIG_JOYSTICK_ADI)		+= adi.o
+obj-$(CONFIG_JOYSTICK_AMIGA)		+= amijoy.o
+obj-$(CONFIG_JOYSTICK_AS5011)		+= as5011.o
+obj-$(CONFIG_JOYSTICK_ANALOG)		+= analog.o
+obj-$(CONFIG_JOYSTICK_COBRA)		+= cobra.o
+obj-$(CONFIG_JOYSTICK_DB9)		+= db9.o
+obj-$(CONFIG_JOYSTICK_GAMECON)		+= gamecon.o
+obj-$(CONFIG_JOYSTICK_GF2K)		+= gf2k.o
+obj-$(CONFIG_JOYSTICK_GRIP)		+= grip.o
+obj-$(CONFIG_JOYSTICK_GRIP_MP)		+= grip_mp.o
+obj-$(CONFIG_JOYSTICK_GUILLEMOT)	+= guillemot.o
+obj-$(CONFIG_JOYSTICK_IFORCE)		+= iforce/
+obj-$(CONFIG_JOYSTICK_INTERACT)		+= interact.o
+obj-$(CONFIG_JOYSTICK_JOYDUMP)		+= joydump.o
+obj-$(CONFIG_JOYSTICK_MAGELLAN)		+= magellan.o
+obj-$(CONFIG_JOYSTICK_MAPLE)		+= maplecontrol.o
+obj-$(CONFIG_JOYSTICK_SIDEWINDER)	+= sidewinder.o
+obj-$(CONFIG_JOYSTICK_SPACEBALL)	+= spaceball.o
+obj-$(CONFIG_JOYSTICK_SPACEORB)		+= spaceorb.o
+obj-$(CONFIG_JOYSTICK_STINGER)		+= stinger.o
+obj-$(CONFIG_JOYSTICK_TMDC)		+= tmdc.o
+obj-$(CONFIG_JOYSTICK_TURBOGRAFX)	+= turbografx.o
+obj-$(CONFIG_JOYSTICK_TWIDJOY)		+= twidjoy.o
+obj-$(CONFIG_JOYSTICK_WARRIOR)		+= warrior.o
+obj-$(CONFIG_JOYSTICK_XPAD)		+= xpad.o
+obj-$(CONFIG_JOYSTICK_ZHENHUA)		+= zhenhua.o
+obj-$(CONFIG_JOYSTICK_WALKERA0701)	+= walkera0701.o
+
diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c
new file mode 100644
index 0000000..55efdfc
--- /dev/null
+++ b/drivers/input/joystick/a3d.c
@@ -0,0 +1,415 @@
+/*
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * FP-Gaming Assassin 3D joystick driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_DESC	"FP-Gaming Assassin 3D joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define A3D_MAX_START		600	/* 600 us */
+#define A3D_MAX_STROBE		80	/* 80 us */
+#define A3D_MAX_LENGTH		40	/* 40*3 bits */
+
+#define A3D_MODE_A3D		1	/* Assassin 3D */
+#define A3D_MODE_PAN		2	/* Panther */
+#define A3D_MODE_OEM		3	/* Panther OEM version */
+#define A3D_MODE_PXL		4	/* Panther XL */
+
+static char *a3d_names[] = { NULL, "FP-Gaming Assassin 3D", "MadCatz Panther", "OEM Panther",
+			"MadCatz Panther XL", "MadCatz Panther XL w/ rudder" };
+
+struct a3d {
+	struct gameport *gameport;
+	struct gameport *adc;
+	struct input_dev *dev;
+	int axes[4];
+	int buttons;
+	int mode;
+	int length;
+	int reads;
+	int bads;
+	char phys[32];
+};
+
+/*
+ * a3d_read_packet() reads an Assassin 3D packet.
+ */
+
+static int a3d_read_packet(struct gameport *gameport, int length, char *data)
+{
+	unsigned long flags;
+	unsigned char u, v;
+	unsigned int t, s;
+	int i;
+
+	i = 0;
+	t = gameport_time(gameport, A3D_MAX_START);
+	s = gameport_time(gameport, A3D_MAX_STROBE);
+
+	local_irq_save(flags);
+	gameport_trigger(gameport);
+	v = gameport_read(gameport);
+
+	while (t > 0 && i < length) {
+		t--;
+		u = v; v = gameport_read(gameport);
+		if (~v & u & 0x10) {
+			data[i++] = v >> 5;
+			t = s;
+		}
+	}
+
+	local_irq_restore(flags);
+
+	return i;
+}
+
+/*
+ * a3d_csum() computes checksum of triplet packet
+ */
+
+static int a3d_csum(char *data, int count)
+{
+	int i, csum = 0;
+
+	for (i = 0; i < count - 2; i++)
+		csum += data[i];
+	return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]);
+}
+
+static void a3d_read(struct a3d *a3d, unsigned char *data)
+{
+	struct input_dev *dev = a3d->dev;
+
+	switch (a3d->mode) {
+
+		case A3D_MODE_A3D:
+		case A3D_MODE_OEM:
+		case A3D_MODE_PAN:
+
+			input_report_rel(dev, REL_X, ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7));
+			input_report_rel(dev, REL_Y, ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7));
+
+			input_report_key(dev, BTN_RIGHT,  data[2] & 1);
+			input_report_key(dev, BTN_LEFT,   data[3] & 2);
+			input_report_key(dev, BTN_MIDDLE, data[3] & 4);
+
+			input_sync(dev);
+
+			a3d->axes[0] = ((signed char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128;
+			a3d->axes[1] = ((signed char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128;
+			a3d->axes[2] = ((signed char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128;
+			a3d->axes[3] = ((signed char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128;
+
+			a3d->buttons = ((data[3] << 3) | data[4]) & 0xf;
+
+			break;
+
+		case A3D_MODE_PXL:
+
+			input_report_rel(dev, REL_X, ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7));
+			input_report_rel(dev, REL_Y, ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7));
+
+			input_report_key(dev, BTN_RIGHT,  data[2] & 1);
+			input_report_key(dev, BTN_LEFT,   data[3] & 2);
+			input_report_key(dev, BTN_MIDDLE, data[3] & 4);
+			input_report_key(dev, BTN_SIDE,   data[7] & 2);
+			input_report_key(dev, BTN_EXTRA,  data[7] & 4);
+
+			input_report_abs(dev, ABS_X,        ((signed char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128);
+			input_report_abs(dev, ABS_Y,        ((signed char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128);
+			input_report_abs(dev, ABS_RUDDER,   ((signed char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128);
+			input_report_abs(dev, ABS_THROTTLE, ((signed char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128);
+
+			input_report_abs(dev, ABS_HAT0X, ( data[5]       & 1) - ((data[5] >> 2) & 1));
+			input_report_abs(dev, ABS_HAT0Y, ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1));
+			input_report_abs(dev, ABS_HAT1X, ((data[4] >> 1) & 1) - ( data[3]       & 1));
+			input_report_abs(dev, ABS_HAT1Y, ((data[4] >> 2) & 1) - ( data[4]       & 1));
+
+			input_report_key(dev, BTN_TRIGGER, data[8] & 1);
+			input_report_key(dev, BTN_THUMB,   data[8] & 2);
+			input_report_key(dev, BTN_TOP,     data[8] & 4);
+			input_report_key(dev, BTN_PINKIE,  data[7] & 1);
+
+			input_sync(dev);
+
+			break;
+	}
+}
+
+
+/*
+ * a3d_poll() reads and analyzes A3D joystick data.
+ */
+
+static void a3d_poll(struct gameport *gameport)
+{
+	struct a3d *a3d = gameport_get_drvdata(gameport);
+	unsigned char data[A3D_MAX_LENGTH];
+
+	a3d->reads++;
+	if (a3d_read_packet(a3d->gameport, a3d->length, data) != a3d->length ||
+	    data[0] != a3d->mode || a3d_csum(data, a3d->length))
+		a3d->bads++;
+	else
+		a3d_read(a3d, data);
+}
+
+/*
+ * a3d_adc_cooked_read() copies the acis and button data to the
+ * callers arrays. It could do the read itself, but the caller could
+ * call this more than 50 times a second, which would use too much CPU.
+ */
+
+static int a3d_adc_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+	struct a3d *a3d = gameport->port_data;
+	int i;
+
+	for (i = 0; i < 4; i++)
+		axes[i] = (a3d->axes[i] < 254) ? a3d->axes[i] : -1;
+	*buttons = a3d->buttons;
+	return 0;
+}
+
+/*
+ * a3d_adc_open() is the gameport open routine. It refuses to serve
+ * any but cooked data.
+ */
+
+static int a3d_adc_open(struct gameport *gameport, int mode)
+{
+	struct a3d *a3d = gameport->port_data;
+
+	if (mode != GAMEPORT_MODE_COOKED)
+		return -1;
+
+	gameport_start_polling(a3d->gameport);
+	return 0;
+}
+
+/*
+ * a3d_adc_close() is a callback from the input close routine.
+ */
+
+static void a3d_adc_close(struct gameport *gameport)
+{
+	struct a3d *a3d = gameport->port_data;
+
+	gameport_stop_polling(a3d->gameport);
+}
+
+/*
+ * a3d_open() is a callback from the input open routine.
+ */
+
+static int a3d_open(struct input_dev *dev)
+{
+	struct a3d *a3d = input_get_drvdata(dev);
+
+	gameport_start_polling(a3d->gameport);
+	return 0;
+}
+
+/*
+ * a3d_close() is a callback from the input close routine.
+ */
+
+static void a3d_close(struct input_dev *dev)
+{
+	struct a3d *a3d = input_get_drvdata(dev);
+
+	gameport_stop_polling(a3d->gameport);
+}
+
+/*
+ * a3d_connect() probes for A3D joysticks.
+ */
+
+static int a3d_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct a3d *a3d;
+	struct input_dev *input_dev;
+	struct gameport *adc;
+	unsigned char data[A3D_MAX_LENGTH];
+	int i;
+	int err;
+
+	a3d = kzalloc(sizeof(struct a3d), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!a3d || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	a3d->dev = input_dev;
+	a3d->gameport = gameport;
+
+	gameport_set_drvdata(gameport, a3d);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	i = a3d_read_packet(gameport, A3D_MAX_LENGTH, data);
+
+	if (!i || a3d_csum(data, i)) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	a3d->mode = data[0];
+
+	if (!a3d->mode || a3d->mode > 5) {
+		printk(KERN_WARNING "a3d.c: Unknown A3D device detected "
+			"(%s, id=%d), contact <vojtech@ucw.cz>\n", gameport->phys, a3d->mode);
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, a3d_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	snprintf(a3d->phys, sizeof(a3d->phys), "%s/input0", gameport->phys);
+
+	input_dev->name = a3d_names[a3d->mode];
+	input_dev->phys = a3d->phys;
+	input_dev->id.bustype = BUS_GAMEPORT;
+	input_dev->id.vendor = GAMEPORT_ID_VENDOR_MADCATZ;
+	input_dev->id.product = a3d->mode;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &gameport->dev;
+	input_dev->open = a3d_open;
+	input_dev->close = a3d_close;
+
+	input_set_drvdata(input_dev, a3d);
+
+	if (a3d->mode == A3D_MODE_PXL) {
+
+		int axes[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER };
+
+		a3d->length = 33;
+
+		input_dev->evbit[0] |= BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) |
+			BIT_MASK(EV_REL);
+		input_dev->relbit[0] |= BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+		input_dev->absbit[0] |= BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
+			BIT_MASK(ABS_THROTTLE) | BIT_MASK(ABS_RUDDER) |
+			BIT_MASK(ABS_HAT0X) | BIT_MASK(ABS_HAT0Y) |
+			BIT_MASK(ABS_HAT1X) | BIT_MASK(ABS_HAT1Y);
+		input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_RIGHT) |
+			BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) |
+			BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
+		input_dev->keybit[BIT_WORD(BTN_JOYSTICK)] |=
+			BIT_MASK(BTN_TRIGGER) | BIT_MASK(BTN_THUMB) |
+			BIT_MASK(BTN_TOP) | BIT_MASK(BTN_PINKIE);
+
+		a3d_read(a3d, data);
+
+		for (i = 0; i < 4; i++) {
+			if (i < 2)
+				input_set_abs_params(input_dev, axes[i],
+					48, input_abs_get_val(input_dev, axes[i]) * 2 - 48, 0, 8);
+			else
+				input_set_abs_params(input_dev, axes[i], 2, 253, 0, 0);
+			input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0);
+		}
+
+	} else {
+		a3d->length = 29;
+
+		input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+		input_dev->relbit[0] |= BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+		input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_RIGHT) |
+			BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE);
+
+		a3d_read(a3d, data);
+
+		if (!(a3d->adc = adc = gameport_allocate_port()))
+			printk(KERN_ERR "a3d: Not enough memory for ADC port\n");
+		else {
+			adc->port_data = a3d;
+			adc->open = a3d_adc_open;
+			adc->close = a3d_adc_close;
+			adc->cooked_read = a3d_adc_cooked_read;
+			adc->fuzz = 1;
+
+			gameport_set_name(adc, a3d_names[a3d->mode]);
+			gameport_set_phys(adc, "%s/gameport0", gameport->phys);
+			adc->dev.parent = &gameport->dev;
+
+			gameport_register_port(adc);
+		}
+	}
+
+	err = input_register_device(a3d->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	if (a3d->adc)
+		gameport_unregister_port(a3d->adc);
+ fail2:	gameport_close(gameport);
+ fail1:	gameport_set_drvdata(gameport, NULL);
+	input_free_device(input_dev);
+	kfree(a3d);
+	return err;
+}
+
+static void a3d_disconnect(struct gameport *gameport)
+{
+	struct a3d *a3d = gameport_get_drvdata(gameport);
+
+	input_unregister_device(a3d->dev);
+	if (a3d->adc)
+		gameport_unregister_port(a3d->adc);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(a3d);
+}
+
+static struct gameport_driver a3d_drv = {
+	.driver		= {
+		.name	= "adc",
+		.owner	= THIS_MODULE,
+	},
+	.description	= DRIVER_DESC,
+	.connect	= a3d_connect,
+	.disconnect	= a3d_disconnect,
+};
+
+module_gameport_driver(a3d_drv);
diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c
new file mode 100644
index 0000000..d09cefa
--- /dev/null
+++ b/drivers/input/joystick/adi.c
@@ -0,0 +1,567 @@
+/*
+ *  Copyright (c) 1998-2005 Vojtech Pavlik
+ */
+
+/*
+ * Logitech ADI joystick family driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_DESC	"Logitech ADI joystick family driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Times, array sizes, flags, ids.
+ */
+
+#define ADI_MAX_START		200	/* Trigger to packet timeout [200us] */
+#define ADI_MAX_STROBE		40	/* Single bit timeout [40us] */
+#define ADI_INIT_DELAY		10	/* Delay after init packet [10ms] */
+#define ADI_DATA_DELAY		4	/* Delay after data packet [4ms] */
+
+#define ADI_MAX_LENGTH		256
+#define ADI_MIN_LENGTH		8
+#define ADI_MIN_LEN_LENGTH	10
+#define ADI_MIN_ID_LENGTH	66
+#define ADI_MAX_NAME_LENGTH	64
+#define ADI_MAX_CNAME_LENGTH	16
+#define ADI_MAX_PHYS_LENGTH	64
+
+#define ADI_FLAG_HAT		0x04
+#define ADI_FLAG_10BIT		0x08
+
+#define ADI_ID_TPD		0x01
+#define ADI_ID_WGP		0x06
+#define ADI_ID_WGPE		0x08
+#define ADI_ID_MAX		0x0a
+
+/*
+ * Names, buttons, axes ...
+ */
+
+static char *adi_names[] = {	"WingMan Extreme Digital", "ThunderPad Digital", "SideCar", "CyberMan 2",
+				"WingMan Interceptor", "WingMan Formula", "WingMan GamePad",
+				"WingMan Extreme Digital 3D", "WingMan GamePad Extreme",
+				"WingMan GamePad USB", "Unknown Device %#x" };
+
+static char adi_wmgpe_abs[] =	{ ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y };
+static char adi_wmi_abs[] =	{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y };
+static char adi_wmed3d_abs[] =	{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RZ, ABS_HAT0X, ABS_HAT0Y };
+static char adi_cm2_abs[] =	{ ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ };
+static char adi_wmf_abs[] =	{ ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y };
+
+static short adi_wmgpe_key[] =	{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z,  BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT };
+static short adi_wmi_key[] =	{ BTN_TRIGGER,  BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_EXTRA };
+static short adi_wmed3d_key[] =	{ BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2 };
+static short adi_cm2_key[] =	{ BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 };
+
+static char* adi_abs[] = { adi_wmi_abs, adi_wmgpe_abs, adi_wmf_abs, adi_cm2_abs, adi_wmi_abs, adi_wmf_abs,
+			   adi_wmgpe_abs, adi_wmed3d_abs, adi_wmgpe_abs, adi_wmgpe_abs, adi_wmi_abs };
+
+static short* adi_key[] = { adi_wmi_key, adi_wmgpe_key, adi_cm2_key, adi_cm2_key, adi_wmi_key, adi_cm2_key,
+			    adi_wmgpe_key, adi_wmed3d_key, adi_wmgpe_key, adi_wmgpe_key, adi_wmi_key };
+
+/*
+ * Hat to axis conversion arrays.
+ */
+
+static struct {
+	int x;
+	int y;
+} adi_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+/*
+ * Per-port information.
+ */
+
+struct adi {
+	struct input_dev *dev;
+	int length;
+	int ret;
+	int idx;
+	unsigned char id;
+	char buttons;
+	char axes10;
+	char axes8;
+	signed char pad;
+	char hats;
+	char *abs;
+	short *key;
+	char name[ADI_MAX_NAME_LENGTH];
+	char cname[ADI_MAX_CNAME_LENGTH];
+	char phys[ADI_MAX_PHYS_LENGTH];
+	unsigned char data[ADI_MAX_LENGTH];
+};
+
+struct adi_port {
+	struct gameport *gameport;
+	struct adi adi[2];
+	int bad;
+	int reads;
+};
+
+/*
+ * adi_read_packet() reads a Logitech ADI packet.
+ */
+
+static void adi_read_packet(struct adi_port *port)
+{
+	struct adi *adi = port->adi;
+	struct gameport *gameport = port->gameport;
+	unsigned char u, v, w, x, z;
+	int t[2], s[2], i;
+	unsigned long flags;
+
+	for (i = 0; i < 2; i++) {
+		adi[i].ret = -1;
+		t[i] = gameport_time(gameport, ADI_MAX_START);
+		s[i] = 0;
+	}
+
+	local_irq_save(flags);
+
+	gameport_trigger(gameport);
+	v = z = gameport_read(gameport);
+
+	do {
+		u = v;
+		w = u ^ (v = x = gameport_read(gameport));
+		for (i = 0; i < 2; i++, w >>= 2, x >>= 2) {
+			t[i]--;
+			if ((w & 0x30) && s[i]) {
+				if ((w & 0x30) < 0x30 && adi[i].ret < ADI_MAX_LENGTH && t[i] > 0) {
+					adi[i].data[++adi[i].ret] = w;
+					t[i] = gameport_time(gameport, ADI_MAX_STROBE);
+				} else t[i] = 0;
+			} else if (!(x & 0x30)) s[i] = 1;
+		}
+	} while (t[0] > 0 || t[1] > 0);
+
+	local_irq_restore(flags);
+
+	return;
+}
+
+/*
+ * adi_move_bits() detects a possible 2-stream mode, and moves
+ * the bits accordingly.
+ */
+
+static void adi_move_bits(struct adi_port *port, int length)
+{
+	int i;
+	struct adi *adi = port->adi;
+
+	adi[0].idx = adi[1].idx = 0;
+
+	if (adi[0].ret <= 0 || adi[1].ret <= 0) return;
+	if (adi[0].data[0] & 0x20 || ~adi[1].data[0] & 0x20) return;
+
+	for (i = 1; i <= adi[1].ret; i++)
+		adi[0].data[((length - 1) >> 1) + i + 1] = adi[1].data[i];
+
+	adi[0].ret += adi[1].ret;
+	adi[1].ret = -1;
+}
+
+/*
+ * adi_get_bits() gathers bits from the data packet.
+ */
+
+static inline int adi_get_bits(struct adi *adi, int count)
+{
+	int bits = 0;
+	int i;
+	if ((adi->idx += count) > adi->ret) return 0;
+	for (i = 0; i < count; i++)
+		bits |= ((adi->data[adi->idx - i] >> 5) & 1) << i;
+	return bits;
+}
+
+/*
+ * adi_decode() decodes Logitech joystick data into input events.
+ */
+
+static int adi_decode(struct adi *adi)
+{
+	struct input_dev *dev = adi->dev;
+	char *abs = adi->abs;
+	short *key = adi->key;
+	int i, t;
+
+	if (adi->ret < adi->length || adi->id != (adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4)))
+		return -1;
+
+	for (i = 0; i < adi->axes10; i++)
+		input_report_abs(dev, *abs++, adi_get_bits(adi, 10));
+
+	for (i = 0; i < adi->axes8; i++)
+		input_report_abs(dev, *abs++, adi_get_bits(adi, 8));
+
+	for (i = 0; i < adi->buttons && i < 63; i++) {
+		if (i == adi->pad) {
+			t = adi_get_bits(adi, 4);
+			input_report_abs(dev, *abs++, ((t >> 2) & 1) - ( t       & 1));
+			input_report_abs(dev, *abs++, ((t >> 1) & 1) - ((t >> 3) & 1));
+		}
+		input_report_key(dev, *key++, adi_get_bits(adi, 1));
+	}
+
+	for (i = 0; i < adi->hats; i++) {
+		if ((t = adi_get_bits(adi, 4)) > 8) t = 0;
+		input_report_abs(dev, *abs++, adi_hat_to_axis[t].x);
+		input_report_abs(dev, *abs++, adi_hat_to_axis[t].y);
+	}
+
+	for (i = 63; i < adi->buttons; i++)
+		input_report_key(dev, *key++, adi_get_bits(adi, 1));
+
+	input_sync(dev);
+
+	return 0;
+}
+
+/*
+ * adi_read() reads the data packet and decodes it.
+ */
+
+static int adi_read(struct adi_port *port)
+{
+	int i;
+	int result = 0;
+
+	adi_read_packet(port);
+	adi_move_bits(port, port->adi[0].length);
+
+	for (i = 0; i < 2; i++)
+		if (port->adi[i].length)
+			 result |= adi_decode(port->adi + i);
+
+	return result;
+}
+
+/*
+ * adi_poll() repeatedly polls the Logitech joysticks.
+ */
+
+static void adi_poll(struct gameport *gameport)
+{
+	struct adi_port *port = gameport_get_drvdata(gameport);
+
+	port->bad -= adi_read(port);
+	port->reads++;
+}
+
+/*
+ * adi_open() is a callback from the input open routine.
+ */
+
+static int adi_open(struct input_dev *dev)
+{
+	struct adi_port *port = input_get_drvdata(dev);
+
+	gameport_start_polling(port->gameport);
+	return 0;
+}
+
+/*
+ * adi_close() is a callback from the input close routine.
+ */
+
+static void adi_close(struct input_dev *dev)
+{
+	struct adi_port *port = input_get_drvdata(dev);
+
+	gameport_stop_polling(port->gameport);
+}
+
+/*
+ * adi_init_digital() sends a trigger & delay sequence
+ * to reset and initialize a Logitech joystick into digital mode.
+ */
+
+static void adi_init_digital(struct gameport *gameport)
+{
+	int seq[] = { 4, -2, -3, 10, -6, -11, -7, -9, 11, 0 };
+	int i;
+
+	for (i = 0; seq[i]; i++) {
+		gameport_trigger(gameport);
+		if (seq[i] > 0)
+			msleep(seq[i]);
+		if (seq[i] < 0) {
+			mdelay(-seq[i]);
+			udelay(-seq[i]*14);	/* It looks like mdelay() is off by approx 1.4% */
+		}
+	}
+}
+
+static void adi_id_decode(struct adi *adi, struct adi_port *port)
+{
+	int i, t;
+
+	if (adi->ret < ADI_MIN_ID_LENGTH) /* Minimum ID packet length */
+		return;
+
+	if (adi->ret < (t = adi_get_bits(adi, 10))) {
+		printk(KERN_WARNING "adi: Short ID packet: reported: %d != read: %d\n", t, adi->ret);
+		return;
+	}
+
+	adi->id = adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4);
+
+	if ((t = adi_get_bits(adi, 4)) & ADI_FLAG_HAT) adi->hats++;
+
+	adi->length = adi_get_bits(adi, 10);
+
+	if (adi->length >= ADI_MAX_LENGTH || adi->length < ADI_MIN_LENGTH) {
+		printk(KERN_WARNING "adi: Bad data packet length (%d).\n", adi->length);
+		adi->length = 0;
+		return;
+	}
+
+	adi->axes8 = adi_get_bits(adi, 4);
+	adi->buttons = adi_get_bits(adi, 6);
+
+	if (adi_get_bits(adi, 6) != 8 && adi->hats) {
+		printk(KERN_WARNING "adi: Other than 8-dir POVs not supported yet.\n");
+		adi->length = 0;
+		return;
+	}
+
+	adi->buttons += adi_get_bits(adi, 6);
+	adi->hats += adi_get_bits(adi, 4);
+
+	i = adi_get_bits(adi, 4);
+
+	if (t & ADI_FLAG_10BIT) {
+		adi->axes10 = adi->axes8 - i;
+		adi->axes8 = i;
+	}
+
+	t = adi_get_bits(adi, 4);
+
+	for (i = 0; i < t; i++)
+		adi->cname[i] = adi_get_bits(adi, 8);
+	adi->cname[i] = 0;
+
+	t = 8 + adi->buttons + adi->axes10 * 10 + adi->axes8 * 8 + adi->hats * 4;
+	if (adi->length != t && adi->length != t + (t & 1)) {
+		printk(KERN_WARNING "adi: Expected length %d != data length %d\n", t, adi->length);
+		adi->length = 0;
+		return;
+	}
+
+	switch (adi->id) {
+		case ADI_ID_TPD:
+			adi->pad = 4;
+			adi->buttons -= 4;
+			break;
+		case ADI_ID_WGP:
+			adi->pad = 0;
+			adi->buttons -= 4;
+			break;
+		default:
+			adi->pad = -1;
+			break;
+	}
+}
+
+static int adi_init_input(struct adi *adi, struct adi_port *port, int half)
+{
+	struct input_dev *input_dev;
+	char buf[ADI_MAX_NAME_LENGTH];
+	int i, t;
+
+	adi->dev = input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	t = adi->id < ADI_ID_MAX ? adi->id : ADI_ID_MAX;
+
+	snprintf(buf, ADI_MAX_PHYS_LENGTH, adi_names[t], adi->id);
+	snprintf(adi->name, ADI_MAX_NAME_LENGTH, "Logitech %s [%s]", buf, adi->cname);
+	snprintf(adi->phys, ADI_MAX_PHYS_LENGTH, "%s/input%d", port->gameport->phys, half);
+
+	adi->abs = adi_abs[t];
+	adi->key = adi_key[t];
+
+	input_dev->name = adi->name;
+	input_dev->phys = adi->phys;
+	input_dev->id.bustype = BUS_GAMEPORT;
+	input_dev->id.vendor = GAMEPORT_ID_VENDOR_LOGITECH;
+	input_dev->id.product = adi->id;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &port->gameport->dev;
+
+	input_set_drvdata(input_dev, port);
+
+	input_dev->open = adi_open;
+	input_dev->close = adi_close;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++)
+		set_bit(adi->abs[i], input_dev->absbit);
+
+	for (i = 0; i < adi->buttons; i++)
+		set_bit(adi->key[i], input_dev->keybit);
+
+	return 0;
+}
+
+static void adi_init_center(struct adi *adi)
+{
+	int i, t, x;
+
+	if (!adi->length)
+		return;
+
+	for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++) {
+
+		t = adi->abs[i];
+		x = input_abs_get_val(adi->dev, t);
+
+		if (t == ABS_THROTTLE || t == ABS_RUDDER || adi->id == ADI_ID_WGPE)
+			x = i < adi->axes10 ? 512 : 128;
+
+		if (i < adi->axes10)
+			input_set_abs_params(adi->dev, t, 64, x * 2 - 64, 2, 16);
+		else if (i < adi->axes10 + adi->axes8)
+			input_set_abs_params(adi->dev, t, 48, x * 2 - 48, 1, 16);
+		else
+			input_set_abs_params(adi->dev, t, -1, 1, 0, 0);
+	}
+}
+
+/*
+ * adi_connect() probes for Logitech ADI joysticks.
+ */
+
+static int adi_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct adi_port *port;
+	int i;
+	int err;
+
+	port = kzalloc(sizeof(struct adi_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->gameport = gameport;
+
+	gameport_set_drvdata(gameport, port);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	adi_init_digital(gameport);
+	adi_read_packet(port);
+
+	if (port->adi[0].ret >= ADI_MIN_LEN_LENGTH)
+		adi_move_bits(port, adi_get_bits(port->adi, 10));
+
+	for (i = 0; i < 2; i++) {
+		adi_id_decode(port->adi + i, port);
+
+		if (!port->adi[i].length)
+			continue;
+
+		err = adi_init_input(port->adi + i, port, i);
+		if (err)
+			goto fail2;
+	}
+
+	if (!port->adi[0].length && !port->adi[1].length) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, adi_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	msleep(ADI_INIT_DELAY);
+	if (adi_read(port)) {
+		msleep(ADI_DATA_DELAY);
+		adi_read(port);
+	}
+
+	for (i = 0; i < 2; i++)
+		if (port->adi[i].length > 0) {
+			adi_init_center(port->adi + i);
+			err = input_register_device(port->adi[i].dev);
+			if (err)
+				goto fail3;
+		}
+
+	return 0;
+
+ fail3: while (--i >= 0) {
+		if (port->adi[i].length > 0) {
+			input_unregister_device(port->adi[i].dev);
+			port->adi[i].dev = NULL;
+		}
+	}
+ fail2:	for (i = 0; i < 2; i++)
+		input_free_device(port->adi[i].dev);
+	gameport_close(gameport);
+ fail1:	gameport_set_drvdata(gameport, NULL);
+	kfree(port);
+	return err;
+}
+
+static void adi_disconnect(struct gameport *gameport)
+{
+	int i;
+	struct adi_port *port = gameport_get_drvdata(gameport);
+
+	for (i = 0; i < 2; i++)
+		if (port->adi[i].length > 0)
+			input_unregister_device(port->adi[i].dev);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(port);
+}
+
+static struct gameport_driver adi_drv = {
+	.driver		= {
+		.name	= "adi",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= adi_connect,
+	.disconnect	= adi_disconnect,
+};
+
+module_gameport_driver(adi_drv);
diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c
new file mode 100644
index 0000000..c65b5fa
--- /dev/null
+++ b/drivers/input/joystick/amijoy.c
@@ -0,0 +1,176 @@
+/*
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * Driver for Amiga joysticks for Linux/m68k
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Driver for Amiga joysticks");
+MODULE_LICENSE("GPL");
+
+static int amijoy[2] = { 0, 1 };
+module_param_array_named(map, amijoy, uint, NULL, 0);
+MODULE_PARM_DESC(map, "Map of attached joysticks in form of <a>,<b> (default is 0,1)");
+
+static int amijoy_used;
+static DEFINE_MUTEX(amijoy_mutex);
+static struct input_dev *amijoy_dev[2];
+static char *amijoy_phys[2] = { "amijoy/input0", "amijoy/input1" };
+
+static irqreturn_t amijoy_interrupt(int irq, void *dummy)
+{
+	int i, data = 0, button = 0;
+
+	for (i = 0; i < 2; i++)
+		if (amijoy[i]) {
+
+			switch (i) {
+				case 0: data = ~amiga_custom.joy0dat; button = (~ciaa.pra >> 6) & 1; break;
+				case 1: data = ~amiga_custom.joy1dat; button = (~ciaa.pra >> 7) & 1; break;
+			}
+
+			input_report_key(amijoy_dev[i], BTN_TRIGGER, button);
+
+			input_report_abs(amijoy_dev[i], ABS_X, ((data >> 1) & 1) - ((data >> 9) & 1));
+			data = ~(data ^ (data << 1));
+			input_report_abs(amijoy_dev[i], ABS_Y, ((data >> 1) & 1) - ((data >> 9) & 1));
+
+			input_sync(amijoy_dev[i]);
+		}
+	return IRQ_HANDLED;
+}
+
+static int amijoy_open(struct input_dev *dev)
+{
+	int err;
+
+	err = mutex_lock_interruptible(&amijoy_mutex);
+	if (err)
+		return err;
+
+	if (!amijoy_used && request_irq(IRQ_AMIGA_VERTB, amijoy_interrupt, 0, "amijoy", amijoy_interrupt)) {
+		printk(KERN_ERR "amijoy.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB);
+		err = -EBUSY;
+		goto out;
+	}
+
+	amijoy_used++;
+out:
+	mutex_unlock(&amijoy_mutex);
+	return err;
+}
+
+static void amijoy_close(struct input_dev *dev)
+{
+	mutex_lock(&amijoy_mutex);
+	if (!--amijoy_used)
+		free_irq(IRQ_AMIGA_VERTB, amijoy_interrupt);
+	mutex_unlock(&amijoy_mutex);
+}
+
+static int __init amijoy_init(void)
+{
+	int i, j;
+	int err;
+
+	if (!MACH_IS_AMIGA)
+		return -ENODEV;
+
+	for (i = 0; i < 2; i++) {
+		if (!amijoy[i])
+			continue;
+
+		amijoy_dev[i] = input_allocate_device();
+		if (!amijoy_dev[i]) {
+			err = -ENOMEM;
+			goto fail;
+		}
+
+		if (!request_mem_region(CUSTOM_PHYSADDR + 10 + i * 2, 2, "amijoy [Denise]")) {
+			input_free_device(amijoy_dev[i]);
+			err = -EBUSY;
+			goto fail;
+		}
+
+		amijoy_dev[i]->name = "Amiga joystick";
+		amijoy_dev[i]->phys = amijoy_phys[i];
+		amijoy_dev[i]->id.bustype = BUS_AMIGA;
+		amijoy_dev[i]->id.vendor = 0x0001;
+		amijoy_dev[i]->id.product = 0x0003;
+		amijoy_dev[i]->id.version = 0x0100;
+
+		amijoy_dev[i]->open = amijoy_open;
+		amijoy_dev[i]->close = amijoy_close;
+
+		amijoy_dev[i]->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+		amijoy_dev[i]->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y);
+		amijoy_dev[i]->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+			BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+		for (j = 0; j < 2; j++) {
+			input_set_abs_params(amijoy_dev[i], ABS_X + j,
+					     -1, 1, 0, 0);
+		}
+
+		err = input_register_device(amijoy_dev[i]);
+		if (err) {
+			input_free_device(amijoy_dev[i]);
+			goto fail;
+		}
+	}
+	return 0;
+
+ fail:	while (--i >= 0)
+		if (amijoy[i]) {
+			input_unregister_device(amijoy_dev[i]);
+			release_mem_region(CUSTOM_PHYSADDR + 10 + i * 2, 2);
+		}
+	return err;
+}
+
+static void __exit amijoy_exit(void)
+{
+	int i;
+
+	for (i = 0; i < 2; i++)
+		if (amijoy[i]) {
+			input_unregister_device(amijoy_dev[i]);
+			release_mem_region(CUSTOM_PHYSADDR + 10 + i * 2, 2);
+		}
+}
+
+module_init(amijoy_init);
+module_exit(amijoy_exit);
diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
new file mode 100644
index 0000000..6f8b084
--- /dev/null
+++ b/drivers/input/joystick/analog.c
@@ -0,0 +1,802 @@
+/*
+ *  Copyright (c) 1996-2001 Vojtech Pavlik
+ */
+
+/*
+ * Analog joystick and gamepad driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+#include <linux/jiffies.h>
+#include <linux/timex.h>
+#include <linux/timekeeping.h>
+
+#define DRIVER_DESC	"Analog joystick and gamepad driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static bool use_ktime = true;
+module_param(use_ktime, bool, 0400);
+MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed");
+
+/*
+ * Option parsing.
+ */
+
+#define ANALOG_PORTS		16
+
+static char *js[ANALOG_PORTS];
+static unsigned int js_nargs;
+static int analog_options[ANALOG_PORTS];
+module_param_array_named(map, js, charp, &js_nargs, 0);
+MODULE_PARM_DESC(map, "Describes analog joysticks type/capabilities");
+
+/*
+ * Times, feature definitions.
+ */
+
+#define ANALOG_RUDDER		0x00004
+#define ANALOG_THROTTLE		0x00008
+#define ANALOG_AXES_STD		0x0000f
+#define ANALOG_BTNS_STD		0x000f0
+
+#define ANALOG_BTNS_CHF		0x00100
+#define ANALOG_HAT1_CHF		0x00200
+#define ANALOG_HAT2_CHF		0x00400
+#define ANALOG_HAT_FCS		0x00800
+#define ANALOG_HATS_ALL		0x00e00
+#define ANALOG_BTN_TL		0x01000
+#define ANALOG_BTN_TR		0x02000
+#define ANALOG_BTN_TL2		0x04000
+#define ANALOG_BTN_TR2		0x08000
+#define ANALOG_BTNS_TLR		0x03000
+#define ANALOG_BTNS_TLR2	0x0c000
+#define ANALOG_BTNS_GAMEPAD	0x0f000
+
+#define ANALOG_HBTN_CHF		0x10000
+#define ANALOG_ANY_CHF		0x10700
+#define ANALOG_SAITEK		0x20000
+#define ANALOG_EXTENSIONS	0x7ff00
+#define ANALOG_GAMEPAD		0x80000
+
+#define ANALOG_MAX_TIME		3	/* 3 ms */
+#define ANALOG_LOOP_TIME	2000	/* 2 * loop */
+#define ANALOG_SAITEK_DELAY	200	/* 200 us */
+#define ANALOG_SAITEK_TIME	2000	/* 2000 us */
+#define ANALOG_AXIS_TIME	2	/* 2 * refresh */
+#define ANALOG_INIT_RETRIES	8	/* 8 times */
+#define ANALOG_FUZZ_BITS	2	/* 2 bit more */
+#define ANALOG_FUZZ_MAGIC	36	/* 36 u*ms/loop */
+
+#define ANALOG_MAX_NAME_LENGTH  128
+#define ANALOG_MAX_PHYS_LENGTH	32
+
+static short analog_axes[] = { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE };
+static short analog_hats[] = { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y };
+static short analog_pads[] = { BTN_Y, BTN_Z, BTN_TL, BTN_TR };
+static short analog_exts[] = { ANALOG_HAT1_CHF, ANALOG_HAT2_CHF, ANALOG_HAT_FCS };
+static short analog_pad_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_TL2, BTN_TR2, BTN_SELECT, BTN_START, BTN_MODE, BTN_BASE };
+static short analog_joy_btn[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2,
+				  BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_BASE6 };
+
+static unsigned char analog_chf[] = { 0xf, 0x0, 0x1, 0x9, 0x2, 0x4, 0xc, 0x8, 0x3, 0x5, 0xb, 0x7, 0xd, 0xe, 0xa, 0x6 };
+
+struct analog {
+	struct input_dev *dev;
+	int mask;
+	short *buttons;
+	char name[ANALOG_MAX_NAME_LENGTH];
+	char phys[ANALOG_MAX_PHYS_LENGTH];
+};
+
+struct analog_port {
+	struct gameport *gameport;
+	struct analog analog[2];
+	unsigned char mask;
+	char saitek;
+	char cooked;
+	int bads;
+	int reads;
+	int speed;
+	int loop;
+	int fuzz;
+	int axes[4];
+	int buttons;
+	int initial[4];
+	int axtime;
+};
+
+/*
+ * Time macros.
+ */
+
+#ifdef __i386__
+
+#include <linux/i8253.h>
+
+#define GET_TIME(x)	do { if (cpu_has_tsc) x = (unsigned int)rdtsc(); else x = get_time_pit(); } while (0)
+#define DELTA(x,y)	(cpu_has_tsc ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? PIT_TICK_RATE / HZ : 0)))
+#define TIME_NAME	(cpu_has_tsc?"TSC":"PIT")
+static unsigned int get_time_pit(void)
+{
+        unsigned long flags;
+        unsigned int count;
+
+        raw_spin_lock_irqsave(&i8253_lock, flags);
+        outb_p(0x00, 0x43);
+        count = inb_p(0x40);
+        count |= inb_p(0x40) << 8;
+        raw_spin_unlock_irqrestore(&i8253_lock, flags);
+
+        return count;
+}
+#elif defined(__x86_64__)
+#define GET_TIME(x)	do { x = (unsigned int)rdtsc(); } while (0)
+#define DELTA(x,y)	((y)-(x))
+#define TIME_NAME	"TSC"
+#elif defined(__alpha__) || defined(CONFIG_MN10300) || defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_TILE)
+#define GET_TIME(x)	do { x = get_cycles(); } while (0)
+#define DELTA(x,y)	((y)-(x))
+#define TIME_NAME	"get_cycles"
+#else
+#define FAKE_TIME
+static unsigned long analog_faketime = 0;
+#define GET_TIME(x)     do { x = analog_faketime++; } while(0)
+#define DELTA(x,y)	((y)-(x))
+#define TIME_NAME	"Unreliable"
+#warning Precise timer not defined for this architecture.
+#endif
+
+static inline u64 get_time(void)
+{
+	if (use_ktime) {
+		return ktime_get_ns();
+	} else {
+		unsigned int x;
+		GET_TIME(x);
+		return x;
+	}
+}
+
+static inline unsigned int delta(u64 x, u64 y)
+{
+	if (use_ktime)
+		return y - x;
+	else
+		return DELTA((unsigned int)x, (unsigned int)y);
+}
+
+/*
+ * analog_decode() decodes analog joystick data and reports input events.
+ */
+
+static void analog_decode(struct analog *analog, int *axes, int *initial, int buttons)
+{
+	struct input_dev *dev = analog->dev;
+	int i, j;
+
+	if (analog->mask & ANALOG_HAT_FCS)
+		for (i = 0; i < 4; i++)
+			if (axes[3] < ((initial[3] * ((i << 1) + 1)) >> 3)) {
+				buttons |= 1 << (i + 14);
+				break;
+			}
+
+	for (i = j = 0; i < 6; i++)
+		if (analog->mask & (0x10 << i))
+			input_report_key(dev, analog->buttons[j++], (buttons >> i) & 1);
+
+	if (analog->mask & ANALOG_HBTN_CHF)
+		for (i = 0; i < 4; i++)
+			input_report_key(dev, analog->buttons[j++], (buttons >> (i + 10)) & 1);
+
+	if (analog->mask & ANALOG_BTN_TL)
+		input_report_key(dev, analog_pads[0], axes[2] < (initial[2] >> 1));
+	if (analog->mask & ANALOG_BTN_TR)
+		input_report_key(dev, analog_pads[1], axes[3] < (initial[3] >> 1));
+	if (analog->mask & ANALOG_BTN_TL2)
+		input_report_key(dev, analog_pads[2], axes[2] > (initial[2] + (initial[2] >> 1)));
+	if (analog->mask & ANALOG_BTN_TR2)
+		input_report_key(dev, analog_pads[3], axes[3] > (initial[3] + (initial[3] >> 1)));
+
+	for (i = j = 0; i < 4; i++)
+		if (analog->mask & (1 << i))
+			input_report_abs(dev, analog_axes[j++], axes[i]);
+
+	for (i = j = 0; i < 3; i++)
+		if (analog->mask & analog_exts[i]) {
+			input_report_abs(dev, analog_hats[j++],
+				((buttons >> ((i << 2) + 7)) & 1) - ((buttons >> ((i << 2) + 9)) & 1));
+			input_report_abs(dev, analog_hats[j++],
+				((buttons >> ((i << 2) + 8)) & 1) - ((buttons >> ((i << 2) + 6)) & 1));
+		}
+
+	input_sync(dev);
+}
+
+/*
+ * analog_cooked_read() reads analog joystick data.
+ */
+
+static int analog_cooked_read(struct analog_port *port)
+{
+	struct gameport *gameport = port->gameport;
+	u64 time[4], start, loop, now;
+	unsigned int loopout, timeout;
+	unsigned char data[4], this, last;
+	unsigned long flags;
+	int i, j;
+
+	loopout = (ANALOG_LOOP_TIME * port->loop) / 1000;
+	timeout = ANALOG_MAX_TIME * port->speed;
+
+	local_irq_save(flags);
+	gameport_trigger(gameport);
+	now = get_time();
+	local_irq_restore(flags);
+
+	start = now;
+	this = port->mask;
+	i = 0;
+
+	do {
+		loop = now;
+		last = this;
+
+		local_irq_disable();
+		this = gameport_read(gameport) & port->mask;
+		now = get_time();
+		local_irq_restore(flags);
+
+		if ((last ^ this) && (delta(loop, now) < loopout)) {
+			data[i] = last ^ this;
+			time[i] = now;
+			i++;
+		}
+
+	} while (this && (i < 4) && (delta(start, now) < timeout));
+
+	this <<= 4;
+
+	for (--i; i >= 0; i--) {
+		this |= data[i];
+		for (j = 0; j < 4; j++)
+			if (data[i] & (1 << j))
+				port->axes[j] = (delta(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop;
+	}
+
+	return -(this != port->mask);
+}
+
+static int analog_button_read(struct analog_port *port, char saitek, char chf)
+{
+	unsigned char u;
+	int t = 1, i = 0;
+	int strobe = gameport_time(port->gameport, ANALOG_SAITEK_TIME);
+
+	u = gameport_read(port->gameport);
+
+	if (!chf) {
+		port->buttons = (~u >> 4) & 0xf;
+		return 0;
+	}
+
+	port->buttons = 0;
+
+	while ((~u & 0xf0) && (i < 16) && t) {
+		port->buttons |= 1 << analog_chf[(~u >> 4) & 0xf];
+		if (!saitek) return 0;
+		udelay(ANALOG_SAITEK_DELAY);
+		t = strobe;
+		gameport_trigger(port->gameport);
+		while (((u = gameport_read(port->gameport)) & port->mask) && t) t--;
+		i++;
+	}
+
+	return -(!t || (i == 16));
+}
+
+/*
+ * analog_poll() repeatedly polls the Analog joysticks.
+ */
+
+static void analog_poll(struct gameport *gameport)
+{
+	struct analog_port *port = gameport_get_drvdata(gameport);
+	int i;
+
+	char saitek = !!(port->analog[0].mask & ANALOG_SAITEK);
+	char chf = !!(port->analog[0].mask & ANALOG_ANY_CHF);
+
+	if (port->cooked) {
+		port->bads -= gameport_cooked_read(port->gameport, port->axes, &port->buttons);
+		if (chf)
+			port->buttons = port->buttons ? (1 << analog_chf[port->buttons]) : 0;
+		port->reads++;
+	} else {
+		if (!port->axtime--) {
+			port->bads -= analog_cooked_read(port);
+			port->bads -= analog_button_read(port, saitek, chf);
+			port->reads++;
+			port->axtime = ANALOG_AXIS_TIME - 1;
+		} else {
+			if (!saitek)
+				analog_button_read(port, saitek, chf);
+		}
+	}
+
+	for (i = 0; i < 2; i++)
+		if (port->analog[i].mask)
+			analog_decode(port->analog + i, port->axes, port->initial, port->buttons);
+}
+
+/*
+ * analog_open() is a callback from the input open routine.
+ */
+
+static int analog_open(struct input_dev *dev)
+{
+	struct analog_port *port = input_get_drvdata(dev);
+
+	gameport_start_polling(port->gameport);
+	return 0;
+}
+
+/*
+ * analog_close() is a callback from the input close routine.
+ */
+
+static void analog_close(struct input_dev *dev)
+{
+	struct analog_port *port = input_get_drvdata(dev);
+
+	gameport_stop_polling(port->gameport);
+}
+
+/*
+ * analog_calibrate_timer() calibrates the timer and computes loop
+ * and timeout values for a joystick port.
+ */
+
+static void analog_calibrate_timer(struct analog_port *port)
+{
+	struct gameport *gameport = port->gameport;
+	unsigned int i, t, tx;
+	u64 t1, t2, t3;
+	unsigned long flags;
+
+	if (use_ktime) {
+		port->speed = 1000000;
+	} else {
+		local_irq_save(flags);
+		t1 = get_time();
+#ifdef FAKE_TIME
+		analog_faketime += 830;
+#endif
+		mdelay(1);
+		t2 = get_time();
+		t3 = get_time();
+		local_irq_restore(flags);
+
+		port->speed = delta(t1, t2) - delta(t2, t3);
+	}
+
+	tx = ~0;
+
+	for (i = 0; i < 50; i++) {
+		local_irq_save(flags);
+		t1 = get_time();
+		for (t = 0; t < 50; t++) {
+			gameport_read(gameport);
+			t2 = get_time();
+		}
+		t3 = get_time();
+		local_irq_restore(flags);
+		udelay(i);
+		t = delta(t1, t2) - delta(t2, t3);
+		if (t < tx) tx = t;
+	}
+
+        port->loop = tx / 50;
+}
+
+/*
+ * analog_name() constructs a name for an analog joystick.
+ */
+
+static void analog_name(struct analog *analog)
+{
+	snprintf(analog->name, sizeof(analog->name), "Analog %d-axis %d-button",
+		 hweight8(analog->mask & ANALOG_AXES_STD),
+		 hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 +
+		 hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4);
+
+	if (analog->mask & ANALOG_HATS_ALL)
+		snprintf(analog->name, sizeof(analog->name), "%s %d-hat",
+			 analog->name, hweight16(analog->mask & ANALOG_HATS_ALL));
+
+	if (analog->mask & ANALOG_HAT_FCS)
+		strlcat(analog->name, " FCS", sizeof(analog->name));
+	if (analog->mask & ANALOG_ANY_CHF)
+		strlcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF",
+			sizeof(analog->name));
+
+	strlcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick",
+		sizeof(analog->name));
+}
+
+/*
+ * analog_init_device()
+ */
+
+static int analog_init_device(struct analog_port *port, struct analog *analog, int index)
+{
+	struct input_dev *input_dev;
+	int i, j, t, v, w, x, y, z;
+	int error;
+
+	analog_name(analog);
+	snprintf(analog->phys, sizeof(analog->phys),
+		 "%s/input%d", port->gameport->phys, index);
+	analog->buttons = (analog->mask & ANALOG_GAMEPAD) ? analog_pad_btn : analog_joy_btn;
+
+	analog->dev = input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	input_dev->name = analog->name;
+	input_dev->phys = analog->phys;
+	input_dev->id.bustype = BUS_GAMEPORT;
+	input_dev->id.vendor = GAMEPORT_ID_VENDOR_ANALOG;
+	input_dev->id.product = analog->mask >> 4;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &port->gameport->dev;
+
+	input_set_drvdata(input_dev, port);
+
+	input_dev->open = analog_open;
+	input_dev->close = analog_close;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	for (i = j = 0; i < 4; i++)
+		if (analog->mask & (1 << i)) {
+
+			t = analog_axes[j];
+			x = port->axes[i];
+			y = (port->axes[0] + port->axes[1]) >> 1;
+			z = y - port->axes[i];
+			z = z > 0 ? z : -z;
+			v = (x >> 3);
+			w = (x >> 3);
+
+			if ((i == 2 || i == 3) && (j == 2 || j == 3) && (z > (y >> 3)))
+				x = y;
+
+			if (analog->mask & ANALOG_SAITEK) {
+				if (i == 2) x = port->axes[i];
+				v = x - (x >> 2);
+				w = (x >> 4);
+			}
+
+			input_set_abs_params(input_dev, t, v, (x << 1) - v, port->fuzz, w);
+			j++;
+		}
+
+	for (i = j = 0; i < 3; i++)
+		if (analog->mask & analog_exts[i])
+			for (x = 0; x < 2; x++) {
+				t = analog_hats[j++];
+				input_set_abs_params(input_dev, t, -1, 1, 0, 0);
+			}
+
+	for (i = j = 0; i < 4; i++)
+		if (analog->mask & (0x10 << i))
+			set_bit(analog->buttons[j++], input_dev->keybit);
+
+	if (analog->mask & ANALOG_BTNS_CHF)
+		for (i = 0; i < 2; i++)
+			set_bit(analog->buttons[j++], input_dev->keybit);
+
+	if (analog->mask & ANALOG_HBTN_CHF)
+		for (i = 0; i < 4; i++)
+			set_bit(analog->buttons[j++], input_dev->keybit);
+
+	for (i = 0; i < 4; i++)
+		if (analog->mask & (ANALOG_BTN_TL << i))
+			set_bit(analog_pads[i], input_dev->keybit);
+
+	analog_decode(analog, port->axes, port->initial, port->buttons);
+
+	error = input_register_device(analog->dev);
+	if (error) {
+		input_free_device(analog->dev);
+		return error;
+	}
+
+	return 0;
+}
+
+/*
+ * analog_init_devices() sets up device-specific values and registers the input devices.
+ */
+
+static int analog_init_masks(struct analog_port *port)
+{
+	int i;
+	struct analog *analog = port->analog;
+	int max[4];
+
+	if (!port->mask)
+		return -1;
+
+	if ((port->mask & 3) != 3 && port->mask != 0xc) {
+		printk(KERN_WARNING "analog.c: Unknown joystick device found  "
+			"(data=%#x, %s), probably not analog joystick.\n",
+			port->mask, port->gameport->phys);
+		return -1;
+	}
+
+
+	i = analog_options[0]; /* FIXME !!! - need to specify options for different ports */
+
+	analog[0].mask = i & 0xfffff;
+
+	analog[0].mask &= ~(ANALOG_AXES_STD | ANALOG_HAT_FCS | ANALOG_BTNS_GAMEPAD)
+			| port->mask | ((port->mask << 8) & ANALOG_HAT_FCS)
+			| ((port->mask << 10) & ANALOG_BTNS_TLR) | ((port->mask << 12) & ANALOG_BTNS_TLR2);
+
+	analog[0].mask &= ~(ANALOG_HAT2_CHF)
+			| ((analog[0].mask & ANALOG_HBTN_CHF) ? 0 : ANALOG_HAT2_CHF);
+
+	analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_BTN_TR | ANALOG_BTN_TR2)
+			| ((~analog[0].mask & ANALOG_HAT_FCS) >> 8)
+			| ((~analog[0].mask & ANALOG_HAT_FCS) << 2)
+			| ((~analog[0].mask & ANALOG_HAT_FCS) << 4);
+
+	analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_RUDDER)
+			| (((~analog[0].mask & ANALOG_BTNS_TLR ) >> 10)
+			&  ((~analog[0].mask & ANALOG_BTNS_TLR2) >> 12));
+
+	analog[1].mask = ((i >> 20) & 0xff) | ((i >> 12) & 0xf0000);
+
+	analog[1].mask &= (analog[0].mask & ANALOG_EXTENSIONS) ? ANALOG_GAMEPAD
+			: (((ANALOG_BTNS_STD | port->mask) & ~analog[0].mask) | ANALOG_GAMEPAD);
+
+	if (port->cooked) {
+
+		for (i = 0; i < 4; i++) max[i] = port->axes[i] << 1;
+
+		if ((analog[0].mask & 0x7) == 0x7) max[2] = (max[0] + max[1]) >> 1;
+		if ((analog[0].mask & 0xb) == 0xb) max[3] = (max[0] + max[1]) >> 1;
+		if ((analog[0].mask & ANALOG_BTN_TL) && !(analog[0].mask & ANALOG_BTN_TL2)) max[2] >>= 1;
+		if ((analog[0].mask & ANALOG_BTN_TR) && !(analog[0].mask & ANALOG_BTN_TR2)) max[3] >>= 1;
+		if ((analog[0].mask & ANALOG_HAT_FCS)) max[3] >>= 1;
+
+		gameport_calibrate(port->gameport, port->axes, max);
+	}
+
+	for (i = 0; i < 4; i++)
+		port->initial[i] = port->axes[i];
+
+	return -!(analog[0].mask || analog[1].mask);
+}
+
+static int analog_init_port(struct gameport *gameport, struct gameport_driver *drv, struct analog_port *port)
+{
+	int i, t, u, v;
+
+	port->gameport = gameport;
+
+	gameport_set_drvdata(gameport, port);
+
+	if (!gameport_open(gameport, drv, GAMEPORT_MODE_RAW)) {
+
+		analog_calibrate_timer(port);
+
+		gameport_trigger(gameport);
+		t = gameport_read(gameport);
+		msleep(ANALOG_MAX_TIME);
+		port->mask = (gameport_read(gameport) ^ t) & t & 0xf;
+		port->fuzz = (port->speed * ANALOG_FUZZ_MAGIC) / port->loop / 1000 + ANALOG_FUZZ_BITS;
+
+		for (i = 0; i < ANALOG_INIT_RETRIES; i++) {
+			if (!analog_cooked_read(port))
+				break;
+			msleep(ANALOG_MAX_TIME);
+		}
+
+		u = v = 0;
+
+		msleep(ANALOG_MAX_TIME);
+		t = gameport_time(gameport, ANALOG_MAX_TIME * 1000);
+		gameport_trigger(gameport);
+		while ((gameport_read(port->gameport) & port->mask) && (u < t))
+			u++;
+		udelay(ANALOG_SAITEK_DELAY);
+		t = gameport_time(gameport, ANALOG_SAITEK_TIME);
+		gameport_trigger(gameport);
+		while ((gameport_read(port->gameport) & port->mask) && (v < t))
+			v++;
+
+		if (v < (u >> 1)) { /* FIXME - more than one port */
+			analog_options[0] |= /* FIXME - more than one port */
+				ANALOG_SAITEK | ANALOG_BTNS_CHF | ANALOG_HBTN_CHF | ANALOG_HAT1_CHF;
+			return 0;
+		}
+
+		gameport_close(gameport);
+	}
+
+	if (!gameport_open(gameport, drv, GAMEPORT_MODE_COOKED)) {
+
+		for (i = 0; i < ANALOG_INIT_RETRIES; i++)
+			if (!gameport_cooked_read(gameport, port->axes, &port->buttons))
+				break;
+		for (i = 0; i < 4; i++)
+			if (port->axes[i] != -1)
+				port->mask |= 1 << i;
+
+		port->fuzz = gameport->fuzz;
+		port->cooked = 1;
+		return 0;
+	}
+
+	return gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+}
+
+static int analog_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct analog_port *port;
+	int i;
+	int err;
+
+	if (!(port = kzalloc(sizeof(struct analog_port), GFP_KERNEL)))
+		return - ENOMEM;
+
+	err = analog_init_port(gameport, drv, port);
+	if (err)
+		goto fail1;
+
+	err = analog_init_masks(port);
+	if (err)
+		goto fail2;
+
+	gameport_set_poll_handler(gameport, analog_poll);
+	gameport_set_poll_interval(gameport, 10);
+
+	for (i = 0; i < 2; i++)
+		if (port->analog[i].mask) {
+			err = analog_init_device(port, port->analog + i, i);
+			if (err)
+				goto fail3;
+		}
+
+	return 0;
+
+ fail3: while (--i >= 0)
+		if (port->analog[i].mask)
+			input_unregister_device(port->analog[i].dev);
+ fail2:	gameport_close(gameport);
+ fail1:	gameport_set_drvdata(gameport, NULL);
+	kfree(port);
+	return err;
+}
+
+static void analog_disconnect(struct gameport *gameport)
+{
+	struct analog_port *port = gameport_get_drvdata(gameport);
+	int i;
+
+	for (i = 0; i < 2; i++)
+		if (port->analog[i].mask)
+			input_unregister_device(port->analog[i].dev);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	printk(KERN_INFO "analog.c: %d out of %d reads (%d%%) on %s failed\n",
+		port->bads, port->reads, port->reads ? (port->bads * 100 / port->reads) : 0,
+		port->gameport->phys);
+	kfree(port);
+}
+
+struct analog_types {
+	char *name;
+	int value;
+};
+
+static struct analog_types analog_types[] = {
+	{ "none",	0x00000000 },
+	{ "auto",	0x000000ff },
+	{ "2btn",	0x0000003f },
+	{ "y-joy",	0x0cc00033 },
+	{ "y-pad",	0x8cc80033 },
+	{ "fcs",	0x000008f7 },
+	{ "chf",	0x000002ff },
+	{ "fullchf",	0x000007ff },
+	{ "gamepad",	0x000830f3 },
+	{ "gamepad8",	0x0008f0f3 },
+	{ NULL, 0 }
+};
+
+static void analog_parse_options(void)
+{
+	int i, j;
+	char *end;
+
+	for (i = 0; i < js_nargs; i++) {
+
+		for (j = 0; analog_types[j].name; j++)
+			if (!strcmp(analog_types[j].name, js[i])) {
+				analog_options[i] = analog_types[j].value;
+				break;
+			}
+		if (analog_types[j].name) continue;
+
+		analog_options[i] = simple_strtoul(js[i], &end, 0);
+		if (end != js[i]) continue;
+
+		analog_options[i] = 0xff;
+		if (!strlen(js[i])) continue;
+
+		printk(KERN_WARNING "analog.c: Bad config for port %d - \"%s\"\n", i, js[i]);
+	}
+
+	for (; i < ANALOG_PORTS; i++)
+		analog_options[i] = 0xff;
+}
+
+/*
+ * The gameport device structure.
+ */
+
+static struct gameport_driver analog_drv = {
+	.driver		= {
+		.name	= "analog",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= analog_connect,
+	.disconnect	= analog_disconnect,
+};
+
+static int __init analog_init(void)
+{
+	analog_parse_options();
+	return gameport_register_driver(&analog_drv);
+}
+
+static void __exit analog_exit(void)
+{
+	gameport_unregister_driver(&analog_drv);
+}
+
+module_init(analog_init);
+module_exit(analog_exit);
diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c
new file mode 100644
index 0000000..005d852
--- /dev/null
+++ b/drivers/input/joystick/as5011.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2010, 2011 Fabien Marteau <fabien.marteau@armadeus.com>
+ * Sponsored by ARMadeus Systems
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Driver for Austria Microsystems joysticks AS5011
+ *
+ * TODO:
+ *	- Power on the chip when open() and power down when close()
+ *	- Manage power mode
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/input/as5011.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define DRIVER_DESC "Driver for Austria Microsystems AS5011 joystick"
+#define MODULE_DEVICE_ALIAS "as5011"
+
+MODULE_AUTHOR("Fabien Marteau <fabien.marteau@armadeus.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/* registers */
+#define AS5011_CTRL1		0x76
+#define AS5011_CTRL2		0x75
+#define AS5011_XP		0x43
+#define AS5011_XN		0x44
+#define AS5011_YP		0x53
+#define AS5011_YN		0x54
+#define AS5011_X_REG		0x41
+#define AS5011_Y_REG		0x42
+#define AS5011_X_RES_INT	0x51
+#define AS5011_Y_RES_INT	0x52
+
+/* CTRL1 bits */
+#define AS5011_CTRL1_LP_PULSED		0x80
+#define AS5011_CTRL1_LP_ACTIVE		0x40
+#define AS5011_CTRL1_LP_CONTINUE	0x20
+#define AS5011_CTRL1_INT_WUP_EN		0x10
+#define AS5011_CTRL1_INT_ACT_EN		0x08
+#define AS5011_CTRL1_EXT_CLK_EN		0x04
+#define AS5011_CTRL1_SOFT_RST		0x02
+#define AS5011_CTRL1_DATA_VALID		0x01
+
+/* CTRL2 bits */
+#define AS5011_CTRL2_EXT_SAMPLE_EN	0x08
+#define AS5011_CTRL2_RC_BIAS_ON		0x04
+#define AS5011_CTRL2_INV_SPINNING	0x02
+
+#define AS5011_MAX_AXIS	80
+#define AS5011_MIN_AXIS	(-80)
+#define AS5011_FUZZ	8
+#define AS5011_FLAT	40
+
+struct as5011_device {
+	struct input_dev *input_dev;
+	struct i2c_client *i2c_client;
+	unsigned int button_gpio;
+	unsigned int button_irq;
+	unsigned int axis_irq;
+};
+
+static int as5011_i2c_write(struct i2c_client *client,
+			    uint8_t aregaddr,
+			    uint8_t avalue)
+{
+	uint8_t data[2] = { aregaddr, avalue };
+	struct i2c_msg msg = {
+		.addr = client->addr,
+		.flags = I2C_M_IGNORE_NAK,
+		.len = 2,
+		.buf = (uint8_t *)data
+	};
+	int error;
+
+	error = i2c_transfer(client->adapter, &msg, 1);
+	return error < 0 ? error : 0;
+}
+
+static int as5011_i2c_read(struct i2c_client *client,
+			   uint8_t aregaddr, signed char *value)
+{
+	uint8_t data[2] = { aregaddr };
+	struct i2c_msg msg_set[2] = {
+		{
+			.addr = client->addr,
+			.flags = I2C_M_REV_DIR_ADDR,
+			.len = 1,
+			.buf = (uint8_t *)data
+		},
+		{
+			.addr = client->addr,
+			.flags = I2C_M_RD | I2C_M_NOSTART,
+			.len = 1,
+			.buf = (uint8_t *)data
+		}
+	};
+	int error;
+
+	error = i2c_transfer(client->adapter, msg_set, 2);
+	if (error < 0)
+		return error;
+
+	*value = data[0] & 0x80 ? -1 * (1 + ~data[0]) : data[0];
+	return 0;
+}
+
+static irqreturn_t as5011_button_interrupt(int irq, void *dev_id)
+{
+	struct as5011_device *as5011 = dev_id;
+	int val = gpio_get_value_cansleep(as5011->button_gpio);
+
+	input_report_key(as5011->input_dev, BTN_JOYSTICK, !val);
+	input_sync(as5011->input_dev);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t as5011_axis_interrupt(int irq, void *dev_id)
+{
+	struct as5011_device *as5011 = dev_id;
+	int error;
+	signed char x, y;
+
+	error = as5011_i2c_read(as5011->i2c_client, AS5011_X_RES_INT, &x);
+	if (error < 0)
+		goto out;
+
+	error = as5011_i2c_read(as5011->i2c_client, AS5011_Y_RES_INT, &y);
+	if (error < 0)
+		goto out;
+
+	input_report_abs(as5011->input_dev, ABS_X, x);
+	input_report_abs(as5011->input_dev, ABS_Y, y);
+	input_sync(as5011->input_dev);
+
+out:
+	return IRQ_HANDLED;
+}
+
+static int as5011_configure_chip(struct as5011_device *as5011,
+				const struct as5011_platform_data *plat_dat)
+{
+	struct i2c_client *client = as5011->i2c_client;
+	int error;
+	signed char value;
+
+	/* chip soft reset */
+	error = as5011_i2c_write(client, AS5011_CTRL1,
+				 AS5011_CTRL1_SOFT_RST);
+	if (error < 0) {
+		dev_err(&client->dev, "Soft reset failed\n");
+		return error;
+	}
+
+	mdelay(10);
+
+	error = as5011_i2c_write(client, AS5011_CTRL1,
+				 AS5011_CTRL1_LP_PULSED |
+				 AS5011_CTRL1_LP_ACTIVE |
+				 AS5011_CTRL1_INT_ACT_EN);
+	if (error < 0) {
+		dev_err(&client->dev, "Power config failed\n");
+		return error;
+	}
+
+	error = as5011_i2c_write(client, AS5011_CTRL2,
+				 AS5011_CTRL2_INV_SPINNING);
+	if (error < 0) {
+		dev_err(&client->dev, "Can't invert spinning\n");
+		return error;
+	}
+
+	/* write threshold */
+	error = as5011_i2c_write(client, AS5011_XP, plat_dat->xp);
+	if (error < 0) {
+		dev_err(&client->dev, "Can't write threshold\n");
+		return error;
+	}
+
+	error = as5011_i2c_write(client, AS5011_XN, plat_dat->xn);
+	if (error < 0) {
+		dev_err(&client->dev, "Can't write threshold\n");
+		return error;
+	}
+
+	error = as5011_i2c_write(client, AS5011_YP, plat_dat->yp);
+	if (error < 0) {
+		dev_err(&client->dev, "Can't write threshold\n");
+		return error;
+	}
+
+	error = as5011_i2c_write(client, AS5011_YN, plat_dat->yn);
+	if (error < 0) {
+		dev_err(&client->dev, "Can't write threshold\n");
+		return error;
+	}
+
+	/* to free irq gpio in chip */
+	error = as5011_i2c_read(client, AS5011_X_RES_INT, &value);
+	if (error < 0) {
+		dev_err(&client->dev, "Can't read i2c X resolution value\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static int as5011_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	const struct as5011_platform_data *plat_data;
+	struct as5011_device *as5011;
+	struct input_dev *input_dev;
+	int irq;
+	int error;
+
+	plat_data = dev_get_platdata(&client->dev);
+	if (!plat_data)
+		return -EINVAL;
+
+	if (!plat_data->axis_irq) {
+		dev_err(&client->dev, "No axis IRQ?\n");
+		return -EINVAL;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_NOSTART |
+				     I2C_FUNC_PROTOCOL_MANGLING)) {
+		dev_err(&client->dev,
+			"need i2c bus that supports protocol mangling\n");
+		return -ENODEV;
+	}
+
+	as5011 = kmalloc(sizeof(struct as5011_device), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!as5011 || !input_dev) {
+		dev_err(&client->dev,
+			"Can't allocate memory for device structure\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	as5011->i2c_client = client;
+	as5011->input_dev = input_dev;
+	as5011->button_gpio = plat_data->button_gpio;
+	as5011->axis_irq = plat_data->axis_irq;
+
+	input_dev->name = "Austria Microsystem as5011 joystick";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &client->dev;
+
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(BTN_JOYSTICK, input_dev->keybit);
+
+	input_set_abs_params(input_dev, ABS_X,
+		AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT);
+	input_set_abs_params(as5011->input_dev, ABS_Y,
+		AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT);
+
+	error = gpio_request(as5011->button_gpio, "AS5011 button");
+	if (error < 0) {
+		dev_err(&client->dev, "Failed to request button gpio\n");
+		goto err_free_mem;
+	}
+
+	irq = gpio_to_irq(as5011->button_gpio);
+	if (irq < 0) {
+		dev_err(&client->dev,
+			"Failed to get irq number for button gpio\n");
+		error = irq;
+		goto err_free_button_gpio;
+	}
+
+	as5011->button_irq = irq;
+
+	error = request_threaded_irq(as5011->button_irq,
+				     NULL, as5011_button_interrupt,
+				     IRQF_TRIGGER_RISING |
+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				     "as5011_button", as5011);
+	if (error < 0) {
+		dev_err(&client->dev,
+			"Can't allocate button irq %d\n", as5011->button_irq);
+		goto err_free_button_gpio;
+	}
+
+	error = as5011_configure_chip(as5011, plat_data);
+	if (error)
+		goto err_free_button_irq;
+
+	error = request_threaded_irq(as5011->axis_irq, NULL,
+				     as5011_axis_interrupt,
+				     plat_data->axis_irqflags | IRQF_ONESHOT,
+				     "as5011_joystick", as5011);
+	if (error) {
+		dev_err(&client->dev,
+			"Can't allocate axis irq %d\n", plat_data->axis_irq);
+		goto err_free_button_irq;
+	}
+
+	error = input_register_device(as5011->input_dev);
+	if (error) {
+		dev_err(&client->dev, "Failed to register input device\n");
+		goto err_free_axis_irq;
+	}
+
+	i2c_set_clientdata(client, as5011);
+
+	return 0;
+
+err_free_axis_irq:
+	free_irq(as5011->axis_irq, as5011);
+err_free_button_irq:
+	free_irq(as5011->button_irq, as5011);
+err_free_button_gpio:
+	gpio_free(as5011->button_gpio);
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(as5011);
+
+	return error;
+}
+
+static int as5011_remove(struct i2c_client *client)
+{
+	struct as5011_device *as5011 = i2c_get_clientdata(client);
+
+	free_irq(as5011->axis_irq, as5011);
+	free_irq(as5011->button_irq, as5011);
+	gpio_free(as5011->button_gpio);
+
+	input_unregister_device(as5011->input_dev);
+	kfree(as5011);
+
+	return 0;
+}
+
+static const struct i2c_device_id as5011_id[] = {
+	{ MODULE_DEVICE_ALIAS, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, as5011_id);
+
+static struct i2c_driver as5011_driver = {
+	.driver = {
+		.name = "as5011",
+	},
+	.probe		= as5011_probe,
+	.remove		= as5011_remove,
+	.id_table	= as5011_id,
+};
+
+module_i2c_driver(as5011_driver);
diff --git a/drivers/input/joystick/cobra.c b/drivers/input/joystick/cobra.c
new file mode 100644
index 0000000..ae3ee24
--- /dev/null
+++ b/drivers/input/joystick/cobra.c
@@ -0,0 +1,263 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Creative Labs Blaster GamePad Cobra driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_DESC	"Creative Labs Blaster GamePad Cobra driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define COBRA_MAX_STROBE	45	/* 45 us max wait for first strobe */
+#define COBRA_LENGTH		36
+
+static int cobra_btn[] = { BTN_START, BTN_SELECT, BTN_TL, BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL2, BTN_TR2, 0 };
+
+struct cobra {
+	struct gameport *gameport;
+	struct input_dev *dev[2];
+	int reads;
+	int bads;
+	unsigned char exists;
+	char phys[2][32];
+};
+
+static unsigned char cobra_read_packet(struct gameport *gameport, unsigned int *data)
+{
+	unsigned long flags;
+	unsigned char u, v, w;
+	__u64 buf[2];
+	int r[2], t[2];
+	int i, j, ret;
+
+	int strobe = gameport_time(gameport, COBRA_MAX_STROBE);
+
+	for (i = 0; i < 2; i++) {
+		r[i] = buf[i] = 0;
+		t[i] = COBRA_MAX_STROBE;
+	}
+
+	local_irq_save(flags);
+
+	u = gameport_read(gameport);
+
+	do {
+		t[0]--; t[1]--;
+		v = gameport_read(gameport);
+		for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2)
+			if (w & 0x30) {
+				if ((w & 0x30) < 0x30 && r[i] < COBRA_LENGTH && t[i] > 0) {
+					buf[i] |= (__u64)((w >> 5) & 1) << r[i]++;
+					t[i] = strobe;
+					u = v;
+				} else t[i] = 0;
+			}
+	} while (t[0] > 0 || t[1] > 0);
+
+	local_irq_restore(flags);
+
+	ret = 0;
+
+	for (i = 0; i < 2; i++) {
+
+		if (r[i] != COBRA_LENGTH) continue;
+
+		for (j = 0; j < COBRA_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++)
+			buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (COBRA_LENGTH - 1));
+
+		if (j < COBRA_LENGTH) ret |= (1 << i);
+
+		data[i] = ((buf[i] >>  7) & 0x000001f) | ((buf[i] >>  8) & 0x00003e0)
+			| ((buf[i] >>  9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000)
+			| ((buf[i] >> 11) & 0x1f00000);
+
+	}
+
+	return ret;
+}
+
+static void cobra_poll(struct gameport *gameport)
+{
+	struct cobra *cobra = gameport_get_drvdata(gameport);
+	struct input_dev *dev;
+	unsigned int data[2];
+	int i, j, r;
+
+	cobra->reads++;
+
+	if ((r = cobra_read_packet(gameport, data)) != cobra->exists) {
+		cobra->bads++;
+		return;
+	}
+
+	for (i = 0; i < 2; i++)
+		if (cobra->exists & r & (1 << i)) {
+
+			dev = cobra->dev[i];
+
+			input_report_abs(dev, ABS_X, ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1));
+			input_report_abs(dev, ABS_Y, ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1));
+
+			for (j = 0; cobra_btn[j]; j++)
+				input_report_key(dev, cobra_btn[j], data[i] & (0x20 << j));
+
+			input_sync(dev);
+
+		}
+}
+
+static int cobra_open(struct input_dev *dev)
+{
+	struct cobra *cobra = input_get_drvdata(dev);
+
+	gameport_start_polling(cobra->gameport);
+	return 0;
+}
+
+static void cobra_close(struct input_dev *dev)
+{
+	struct cobra *cobra = input_get_drvdata(dev);
+
+	gameport_stop_polling(cobra->gameport);
+}
+
+static int cobra_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct cobra *cobra;
+	struct input_dev *input_dev;
+	unsigned int data[2];
+	int i, j;
+	int err;
+
+	cobra = kzalloc(sizeof(struct cobra), GFP_KERNEL);
+	if (!cobra)
+		return -ENOMEM;
+
+	cobra->gameport = gameport;
+
+	gameport_set_drvdata(gameport, cobra);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	cobra->exists = cobra_read_packet(gameport, data);
+
+	for (i = 0; i < 2; i++)
+		if ((cobra->exists >> i) & data[i] & 1) {
+			printk(KERN_WARNING "cobra.c: Device %d on %s has the Ext bit set. ID is: %d"
+				" Contact vojtech@ucw.cz\n", i, gameport->phys, (data[i] >> 2) & 7);
+			cobra->exists &= ~(1 << i);
+		}
+
+	if (!cobra->exists) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, cobra_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	for (i = 0; i < 2; i++) {
+		if (~(cobra->exists >> i) & 1)
+			continue;
+
+		cobra->dev[i] = input_dev = input_allocate_device();
+		if (!input_dev) {
+			err = -ENOMEM;
+			goto fail3;
+		}
+
+		snprintf(cobra->phys[i], sizeof(cobra->phys[i]),
+			 "%s/input%d", gameport->phys, i);
+
+		input_dev->name = "Creative Labs Blaster GamePad Cobra";
+		input_dev->phys = cobra->phys[i];
+		input_dev->id.bustype = BUS_GAMEPORT;
+		input_dev->id.vendor = GAMEPORT_ID_VENDOR_CREATIVE;
+		input_dev->id.product = 0x0008;
+		input_dev->id.version = 0x0100;
+		input_dev->dev.parent = &gameport->dev;
+
+		input_set_drvdata(input_dev, cobra);
+
+		input_dev->open = cobra_open;
+		input_dev->close = cobra_close;
+
+		input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+		input_set_abs_params(input_dev, ABS_X, -1, 1, 0, 0);
+		input_set_abs_params(input_dev, ABS_Y, -1, 1, 0, 0);
+		for (j = 0; cobra_btn[j]; j++)
+			set_bit(cobra_btn[j], input_dev->keybit);
+
+		err = input_register_device(cobra->dev[i]);
+		if (err)
+			goto fail4;
+	}
+
+	return 0;
+
+ fail4:	input_free_device(cobra->dev[i]);
+ fail3:	while (--i >= 0)
+		if (cobra->dev[i])
+			input_unregister_device(cobra->dev[i]);
+ fail2:	gameport_close(gameport);
+ fail1:	gameport_set_drvdata(gameport, NULL);
+	kfree(cobra);
+	return err;
+}
+
+static void cobra_disconnect(struct gameport *gameport)
+{
+	struct cobra *cobra = gameport_get_drvdata(gameport);
+	int i;
+
+	for (i = 0; i < 2; i++)
+		if ((cobra->exists >> i) & 1)
+			input_unregister_device(cobra->dev[i]);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(cobra);
+}
+
+static struct gameport_driver cobra_drv = {
+	.driver		= {
+		.name	= "cobra",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= cobra_connect,
+	.disconnect	= cobra_disconnect,
+};
+
+module_gameport_driver(cobra_drv);
diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c
new file mode 100644
index 0000000..da32609
--- /dev/null
+++ b/drivers/input/joystick/db9.c
@@ -0,0 +1,724 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Andree Borrmann		Mats Sjövall
+ */
+
+/*
+ * Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver");
+MODULE_LICENSE("GPL");
+
+struct db9_config {
+	int args[2];
+	unsigned int nargs;
+};
+
+#define DB9_MAX_PORTS		3
+static struct db9_config db9_cfg[DB9_MAX_PORTS];
+
+module_param_array_named(dev, db9_cfg[0].args, int, &db9_cfg[0].nargs, 0);
+MODULE_PARM_DESC(dev, "Describes first attached device (<parport#>,<type>)");
+module_param_array_named(dev2, db9_cfg[1].args, int, &db9_cfg[1].nargs, 0);
+MODULE_PARM_DESC(dev2, "Describes second attached device (<parport#>,<type>)");
+module_param_array_named(dev3, db9_cfg[2].args, int, &db9_cfg[2].nargs, 0);
+MODULE_PARM_DESC(dev3, "Describes third attached device (<parport#>,<type>)");
+
+#define DB9_ARG_PARPORT		0
+#define DB9_ARG_MODE		1
+
+#define DB9_MULTI_STICK		0x01
+#define DB9_MULTI2_STICK	0x02
+#define DB9_GENESIS_PAD		0x03
+#define DB9_GENESIS5_PAD	0x05
+#define DB9_GENESIS6_PAD	0x06
+#define DB9_SATURN_PAD		0x07
+#define DB9_MULTI_0802		0x08
+#define DB9_MULTI_0802_2	0x09
+#define DB9_CD32_PAD		0x0A
+#define DB9_SATURN_DPP		0x0B
+#define DB9_SATURN_DPP_2	0x0C
+#define DB9_MAX_PAD		0x0D
+
+#define DB9_UP			0x01
+#define DB9_DOWN		0x02
+#define DB9_LEFT		0x04
+#define DB9_RIGHT		0x08
+#define DB9_FIRE1		0x10
+#define DB9_FIRE2		0x20
+#define DB9_FIRE3		0x40
+#define DB9_FIRE4		0x80
+
+#define DB9_NORMAL		0x0a
+#define DB9_NOSELECT		0x08
+
+#define DB9_GENESIS6_DELAY	14
+#define DB9_REFRESH_TIME	HZ/100
+
+#define DB9_MAX_DEVICES		2
+
+struct db9_mode_data {
+	const char *name;
+	const short *buttons;
+	int n_buttons;
+	int n_pads;
+	int n_axis;
+	int bidirectional;
+	int reverse;
+};
+
+struct db9 {
+	struct input_dev *dev[DB9_MAX_DEVICES];
+	struct timer_list timer;
+	struct pardevice *pd;
+	int mode;
+	int used;
+	int parportno;
+	struct mutex mutex;
+	char phys[DB9_MAX_DEVICES][32];
+};
+
+static struct db9 *db9_base[3];
+
+static const short db9_multi_btn[] = { BTN_TRIGGER, BTN_THUMB };
+static const short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE };
+static const short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START };
+static const short db9_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_RZ, ABS_Z, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
+
+static const struct db9_mode_data db9_modes[] = {
+	{ NULL,					 NULL,		  0,  0,  0,  0,  0 },
+	{ "Multisystem joystick",		 db9_multi_btn,	  1,  1,  2,  1,  1 },
+	{ "Multisystem joystick (2 fire)",	 db9_multi_btn,	  2,  1,  2,  1,  1 },
+	{ "Genesis pad",			 db9_genesis_btn, 4,  1,  2,  1,  1 },
+	{ NULL,					 NULL,		  0,  0,  0,  0,  0 },
+	{ "Genesis 5 pad",			 db9_genesis_btn, 6,  1,  2,  1,  1 },
+	{ "Genesis 6 pad",			 db9_genesis_btn, 8,  1,  2,  1,  1 },
+	{ "Saturn pad",				 db9_cd32_btn,	  9,  6,  7,  0,  1 },
+	{ "Multisystem (0.8.0.2) joystick",	 db9_multi_btn,	  1,  1,  2,  1,  1 },
+	{ "Multisystem (0.8.0.2-dual) joystick", db9_multi_btn,	  1,  2,  2,  1,  1 },
+	{ "Amiga CD-32 pad",			 db9_cd32_btn,	  7,  1,  2,  1,  1 },
+	{ "Saturn dpp",				 db9_cd32_btn,	  9,  6,  7,  0,  0 },
+	{ "Saturn dpp dual",			 db9_cd32_btn,	  9,  12, 7,  0,  0 },
+};
+
+/*
+ * Saturn controllers
+ */
+#define DB9_SATURN_DELAY 300
+static const int db9_saturn_byte[] = { 1, 1, 1, 2, 2, 2, 2, 2, 1 };
+static const unsigned char db9_saturn_mask[] = { 0x04, 0x01, 0x02, 0x40, 0x20, 0x10, 0x08, 0x80, 0x08 };
+
+/*
+ * db9_saturn_write_sub() writes 2 bit data.
+ */
+static void db9_saturn_write_sub(struct parport *port, int type, unsigned char data, int powered, int pwr_sub)
+{
+	unsigned char c;
+
+	switch (type) {
+	case 1: /* DPP1 */
+		c = 0x80 | 0x30 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | data;
+		parport_write_data(port, c);
+		break;
+	case 2: /* DPP2 */
+		c = 0x40 | data << 4 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | 0x03;
+		parport_write_data(port, c);
+		break;
+	case 0:	/* DB9 */
+		c = ((((data & 2) ? 2 : 0) | ((data & 1) ? 4 : 0)) ^ 0x02) | !powered;
+		parport_write_control(port, c);
+		break;
+	}
+}
+
+/*
+ * gc_saturn_read_sub() reads 4 bit data.
+ */
+static unsigned char db9_saturn_read_sub(struct parport *port, int type)
+{
+	unsigned char data;
+
+	if (type) {
+		/* DPP */
+		data = parport_read_status(port) ^ 0x80;
+		return (data & 0x80 ? 1 : 0) | (data & 0x40 ? 2 : 0)
+		     | (data & 0x20 ? 4 : 0) | (data & 0x10 ? 8 : 0);
+	} else {
+		/* DB9 */
+		data = parport_read_data(port) & 0x0f;
+		return (data & 0x8 ? 1 : 0) | (data & 0x4 ? 2 : 0)
+		     | (data & 0x2 ? 4 : 0) | (data & 0x1 ? 8 : 0);
+	}
+}
+
+/*
+ * db9_saturn_read_analog() sends clock and reads 8 bit data.
+ */
+static unsigned char db9_saturn_read_analog(struct parport *port, int type, int powered)
+{
+	unsigned char data;
+
+	db9_saturn_write_sub(port, type, 0, powered, 0);
+	udelay(DB9_SATURN_DELAY);
+	data = db9_saturn_read_sub(port, type) << 4;
+	db9_saturn_write_sub(port, type, 2, powered, 0);
+	udelay(DB9_SATURN_DELAY);
+	data |= db9_saturn_read_sub(port, type);
+	return data;
+}
+
+/*
+ * db9_saturn_read_packet() reads whole saturn packet at connector
+ * and returns device identifier code.
+ */
+static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char *data, int type, int powered)
+{
+	int i, j;
+	unsigned char tmp;
+
+	db9_saturn_write_sub(port, type, 3, powered, 0);
+	data[0] = db9_saturn_read_sub(port, type);
+	switch (data[0] & 0x0f) {
+	case 0xf:
+		/* 1111  no pad */
+		return data[0] = 0xff;
+	case 0x4: case 0x4 | 0x8:
+		/* ?100 : digital controller */
+		db9_saturn_write_sub(port, type, 0, powered, 1);
+		data[2] = db9_saturn_read_sub(port, type) << 4;
+		db9_saturn_write_sub(port, type, 2, powered, 1);
+		data[1] = db9_saturn_read_sub(port, type) << 4;
+		db9_saturn_write_sub(port, type, 1, powered, 1);
+		data[1] |= db9_saturn_read_sub(port, type);
+		db9_saturn_write_sub(port, type, 3, powered, 1);
+		/* data[2] |= db9_saturn_read_sub(port, type); */
+		data[2] |= data[0];
+		return data[0] = 0x02;
+	case 0x1:
+		/* 0001 : analog controller or multitap */
+		db9_saturn_write_sub(port, type, 2, powered, 0);
+		udelay(DB9_SATURN_DELAY);
+		data[0] = db9_saturn_read_analog(port, type, powered);
+		if (data[0] != 0x41) {
+			/* read analog controller */
+			for (i = 0; i < (data[0] & 0x0f); i++)
+				data[i + 1] = db9_saturn_read_analog(port, type, powered);
+			db9_saturn_write_sub(port, type, 3, powered, 0);
+			return data[0];
+		} else {
+			/* read multitap */
+			if (db9_saturn_read_analog(port, type, powered) != 0x60)
+				return data[0] = 0xff;
+			for (i = 0; i < 60; i += 10) {
+				data[i] = db9_saturn_read_analog(port, type, powered);
+				if (data[i] != 0xff)
+					/* read each pad */
+					for (j = 0; j < (data[i] & 0x0f); j++)
+						data[i + j + 1] = db9_saturn_read_analog(port, type, powered);
+			}
+			db9_saturn_write_sub(port, type, 3, powered, 0);
+			return 0x41;
+		}
+	case 0x0:
+		/* 0000 : mouse */
+		db9_saturn_write_sub(port, type, 2, powered, 0);
+		udelay(DB9_SATURN_DELAY);
+		tmp = db9_saturn_read_analog(port, type, powered);
+		if (tmp == 0xff) {
+			for (i = 0; i < 3; i++)
+				data[i + 1] = db9_saturn_read_analog(port, type, powered);
+			db9_saturn_write_sub(port, type, 3, powered, 0);
+			return data[0] = 0xe3;
+		}
+	default:
+		return data[0];
+	}
+}
+
+/*
+ * db9_saturn_report() analyzes packet and reports.
+ */
+static int db9_saturn_report(unsigned char id, unsigned char data[60], struct input_dev *devs[], int n, int max_pads)
+{
+	struct input_dev *dev;
+	int tmp, i, j;
+
+	tmp = (id == 0x41) ? 60 : 10;
+	for (j = 0; j < tmp && n < max_pads; j += 10, n++) {
+		dev = devs[n];
+		switch (data[j]) {
+		case 0x16: /* multi controller (analog 4 axis) */
+			input_report_abs(dev, db9_abs[5], data[j + 6]);
+		case 0x15: /* mission stick (analog 3 axis) */
+			input_report_abs(dev, db9_abs[3], data[j + 4]);
+			input_report_abs(dev, db9_abs[4], data[j + 5]);
+		case 0x13: /* racing controller (analog 1 axis) */
+			input_report_abs(dev, db9_abs[2], data[j + 3]);
+		case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */
+		case 0x02: /* digital pad (digital 2 axis + buttons) */
+			input_report_abs(dev, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
+			input_report_abs(dev, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
+			for (i = 0; i < 9; i++)
+				input_report_key(dev, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
+			break;
+		case 0x19: /* mission stick x2 (analog 6 axis + buttons) */
+			input_report_abs(dev, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
+			input_report_abs(dev, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
+			for (i = 0; i < 9; i++)
+				input_report_key(dev, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
+			input_report_abs(dev, db9_abs[2], data[j + 3]);
+			input_report_abs(dev, db9_abs[3], data[j + 4]);
+			input_report_abs(dev, db9_abs[4], data[j + 5]);
+			/*
+			input_report_abs(dev, db9_abs[8], (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1));
+			input_report_abs(dev, db9_abs[9], (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1));
+			*/
+			input_report_abs(dev, db9_abs[6], data[j + 7]);
+			input_report_abs(dev, db9_abs[7], data[j + 8]);
+			input_report_abs(dev, db9_abs[5], data[j + 9]);
+			break;
+		case 0xd3: /* sankyo ff (analog 1 axis + stop btn) */
+			input_report_key(dev, BTN_A, data[j + 3] & 0x80);
+			input_report_abs(dev, db9_abs[2], data[j + 3] & 0x7f);
+			break;
+		case 0xe3: /* shuttle mouse (analog 2 axis + buttons. signed value) */
+			input_report_key(dev, BTN_START, data[j + 1] & 0x08);
+			input_report_key(dev, BTN_A, data[j + 1] & 0x04);
+			input_report_key(dev, BTN_C, data[j + 1] & 0x02);
+			input_report_key(dev, BTN_B, data[j + 1] & 0x01);
+			input_report_abs(dev, db9_abs[2], data[j + 2] ^ 0x80);
+			input_report_abs(dev, db9_abs[3], (0xff-(data[j + 3] ^ 0x80))+1); /* */
+			break;
+		case 0xff:
+		default: /* no pad */
+			input_report_abs(dev, db9_abs[0], 0);
+			input_report_abs(dev, db9_abs[1], 0);
+			for (i = 0; i < 9; i++)
+				input_report_key(dev, db9_cd32_btn[i], 0);
+			break;
+		}
+	}
+	return n;
+}
+
+static int db9_saturn(int mode, struct parport *port, struct input_dev *devs[])
+{
+	unsigned char id, data[60];
+	int type, n, max_pads;
+	int tmp, i;
+
+	switch (mode) {
+	case DB9_SATURN_PAD:
+		type = 0;
+		n = 1;
+		break;
+	case DB9_SATURN_DPP:
+		type = 1;
+		n = 1;
+		break;
+	case DB9_SATURN_DPP_2:
+		type = 1;
+		n = 2;
+		break;
+	default:
+		return -1;
+	}
+	max_pads = min(db9_modes[mode].n_pads, DB9_MAX_DEVICES);
+	for (tmp = 0, i = 0; i < n; i++) {
+		id = db9_saturn_read_packet(port, data, type + i, 1);
+		tmp = db9_saturn_report(id, data, devs, tmp, max_pads);
+	}
+	return 0;
+}
+
+static void db9_timer(unsigned long private)
+{
+	struct db9 *db9 = (void *) private;
+	struct parport *port = db9->pd->port;
+	struct input_dev *dev = db9->dev[0];
+	struct input_dev *dev2 = db9->dev[1];
+	int data, i;
+
+	switch (db9->mode) {
+		case DB9_MULTI_0802_2:
+
+			data = parport_read_data(port) >> 3;
+
+			input_report_abs(dev2, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev2, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev2, BTN_TRIGGER, ~data & DB9_FIRE1);
+
+		case DB9_MULTI_0802:
+
+			data = parport_read_status(port) >> 3;
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev, BTN_TRIGGER, data & DB9_FIRE1);
+			break;
+
+		case DB9_MULTI_STICK:
+
+			data = parport_read_data(port);
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1);
+			break;
+
+		case DB9_MULTI2_STICK:
+
+			data = parport_read_data(port);
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_THUMB,   ~data & DB9_FIRE2);
+			break;
+
+		case DB9_GENESIS_PAD:
+
+			parport_write_control(port, DB9_NOSELECT);
+			data = parport_read_data(port);
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
+
+			parport_write_control(port, DB9_NORMAL);
+			data = parport_read_data(port);
+
+			input_report_key(dev, BTN_A,     ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_START, ~data & DB9_FIRE2);
+			break;
+
+		case DB9_GENESIS5_PAD:
+
+			parport_write_control(port, DB9_NOSELECT);
+			data = parport_read_data(port);
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
+
+			parport_write_control(port, DB9_NORMAL);
+			data = parport_read_data(port);
+
+			input_report_key(dev, BTN_A,     ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_X,     ~data & DB9_FIRE2);
+			input_report_key(dev, BTN_Y,     ~data & DB9_LEFT);
+			input_report_key(dev, BTN_START, ~data & DB9_RIGHT);
+			break;
+
+		case DB9_GENESIS6_PAD:
+
+			parport_write_control(port, DB9_NOSELECT); /* 1 */
+			udelay(DB9_GENESIS6_DELAY);
+			data = parport_read_data(port);
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
+
+			parport_write_control(port, DB9_NORMAL);
+			udelay(DB9_GENESIS6_DELAY);
+			data = parport_read_data(port);
+
+			input_report_key(dev, BTN_A, ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_START, ~data & DB9_FIRE2);
+
+			parport_write_control(port, DB9_NOSELECT); /* 2 */
+			udelay(DB9_GENESIS6_DELAY);
+			parport_write_control(port, DB9_NORMAL);
+			udelay(DB9_GENESIS6_DELAY);
+			parport_write_control(port, DB9_NOSELECT); /* 3 */
+			udelay(DB9_GENESIS6_DELAY);
+			data=parport_read_data(port);
+
+			input_report_key(dev, BTN_X,    ~data & DB9_LEFT);
+			input_report_key(dev, BTN_Y,    ~data & DB9_DOWN);
+			input_report_key(dev, BTN_Z,    ~data & DB9_UP);
+			input_report_key(dev, BTN_MODE, ~data & DB9_RIGHT);
+
+			parport_write_control(port, DB9_NORMAL);
+			udelay(DB9_GENESIS6_DELAY);
+			parport_write_control(port, DB9_NOSELECT); /* 4 */
+			udelay(DB9_GENESIS6_DELAY);
+			parport_write_control(port, DB9_NORMAL);
+			break;
+
+		case DB9_SATURN_PAD:
+		case DB9_SATURN_DPP:
+		case DB9_SATURN_DPP_2:
+
+			db9_saturn(db9->mode, port, db9->dev);
+			break;
+
+		case DB9_CD32_PAD:
+
+			data = parport_read_data(port);
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+
+			parport_write_control(port, 0x0a);
+
+			for (i = 0; i < 7; i++) {
+				data = parport_read_data(port);
+				parport_write_control(port, 0x02);
+				parport_write_control(port, 0x0a);
+				input_report_key(dev, db9_cd32_btn[i], ~data & DB9_FIRE2);
+			}
+
+			parport_write_control(port, 0x00);
+			break;
+		}
+
+	input_sync(dev);
+
+	mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
+}
+
+static int db9_open(struct input_dev *dev)
+{
+	struct db9 *db9 = input_get_drvdata(dev);
+	struct parport *port = db9->pd->port;
+	int err;
+
+	err = mutex_lock_interruptible(&db9->mutex);
+	if (err)
+		return err;
+
+	if (!db9->used++) {
+		parport_claim(db9->pd);
+		parport_write_data(port, 0xff);
+		if (db9_modes[db9->mode].reverse) {
+			parport_data_reverse(port);
+			parport_write_control(port, DB9_NORMAL);
+		}
+		mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
+	}
+
+	mutex_unlock(&db9->mutex);
+	return 0;
+}
+
+static void db9_close(struct input_dev *dev)
+{
+	struct db9 *db9 = input_get_drvdata(dev);
+	struct parport *port = db9->pd->port;
+
+	mutex_lock(&db9->mutex);
+	if (!--db9->used) {
+		del_timer_sync(&db9->timer);
+		parport_write_control(port, 0x00);
+		parport_data_forward(port);
+		parport_release(db9->pd);
+	}
+	mutex_unlock(&db9->mutex);
+}
+
+static void db9_attach(struct parport *pp)
+{
+	struct db9 *db9;
+	const struct db9_mode_data *db9_mode;
+	struct pardevice *pd;
+	struct input_dev *input_dev;
+	int i, j, port_idx;
+	int mode;
+	struct pardev_cb db9_parport_cb;
+
+	for (port_idx = 0; port_idx < DB9_MAX_PORTS; port_idx++) {
+		if (db9_cfg[port_idx].nargs == 0 ||
+		    db9_cfg[port_idx].args[DB9_ARG_PARPORT] < 0)
+			continue;
+
+		if (db9_cfg[port_idx].args[DB9_ARG_PARPORT] == pp->number)
+			break;
+	}
+
+	if (port_idx == DB9_MAX_PORTS) {
+		pr_debug("Not using parport%d.\n", pp->number);
+		return;
+	}
+
+	mode = db9_cfg[port_idx].args[DB9_ARG_MODE];
+
+	if (mode < 1 || mode >= DB9_MAX_PAD || !db9_modes[mode].n_buttons) {
+		printk(KERN_ERR "db9.c: Bad device type %d\n", mode);
+		return;
+	}
+
+	db9_mode = &db9_modes[mode];
+
+	if (db9_mode->bidirectional && !(pp->modes & PARPORT_MODE_TRISTATE)) {
+		printk(KERN_ERR "db9.c: specified parport is not bidirectional\n");
+		return;
+	}
+
+	memset(&db9_parport_cb, 0, sizeof(db9_parport_cb));
+	db9_parport_cb.flags = PARPORT_FLAG_EXCL;
+
+	pd = parport_register_dev_model(pp, "db9", &db9_parport_cb, port_idx);
+	if (!pd) {
+		printk(KERN_ERR "db9.c: parport busy already - lp.o loaded?\n");
+		return;
+	}
+
+	db9 = kzalloc(sizeof(struct db9), GFP_KERNEL);
+	if (!db9)
+		goto err_unreg_pardev;
+
+	mutex_init(&db9->mutex);
+	db9->pd = pd;
+	db9->mode = mode;
+	db9->parportno = pp->number;
+	init_timer(&db9->timer);
+	db9->timer.data = (long) db9;
+	db9->timer.function = db9_timer;
+
+	for (i = 0; i < (min(db9_mode->n_pads, DB9_MAX_DEVICES)); i++) {
+
+		db9->dev[i] = input_dev = input_allocate_device();
+		if (!input_dev) {
+			printk(KERN_ERR "db9.c: Not enough memory for input device\n");
+			goto err_unreg_devs;
+		}
+
+		snprintf(db9->phys[i], sizeof(db9->phys[i]),
+			 "%s/input%d", db9->pd->port->name, i);
+
+		input_dev->name = db9_mode->name;
+		input_dev->phys = db9->phys[i];
+		input_dev->id.bustype = BUS_PARPORT;
+		input_dev->id.vendor = 0x0002;
+		input_dev->id.product = mode;
+		input_dev->id.version = 0x0100;
+
+		input_set_drvdata(input_dev, db9);
+
+		input_dev->open = db9_open;
+		input_dev->close = db9_close;
+
+		input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+		for (j = 0; j < db9_mode->n_buttons; j++)
+			set_bit(db9_mode->buttons[j], input_dev->keybit);
+		for (j = 0; j < db9_mode->n_axis; j++) {
+			if (j < 2)
+				input_set_abs_params(input_dev, db9_abs[j], -1, 1, 0, 0);
+			else
+				input_set_abs_params(input_dev, db9_abs[j], 1, 255, 0, 0);
+		}
+
+		if (input_register_device(input_dev))
+			goto err_free_dev;
+	}
+
+	db9_base[port_idx] = db9;
+	return;
+
+ err_free_dev:
+	input_free_device(db9->dev[i]);
+ err_unreg_devs:
+	while (--i >= 0)
+		input_unregister_device(db9->dev[i]);
+	kfree(db9);
+ err_unreg_pardev:
+	parport_unregister_device(pd);
+}
+
+static void db9_detach(struct parport *port)
+{
+	int i;
+	struct db9 *db9;
+
+	for (i = 0; i < DB9_MAX_PORTS; i++) {
+		if (db9_base[i] && db9_base[i]->parportno == port->number)
+			break;
+	}
+
+	if (i == DB9_MAX_PORTS)
+		return;
+
+	db9 = db9_base[i];
+	db9_base[i] = NULL;
+
+	for (i = 0; i < min(db9_modes[db9->mode].n_pads, DB9_MAX_DEVICES); i++)
+		input_unregister_device(db9->dev[i]);
+	parport_unregister_device(db9->pd);
+	kfree(db9);
+}
+
+static struct parport_driver db9_parport_driver = {
+	.name = "db9",
+	.match_port = db9_attach,
+	.detach = db9_detach,
+	.devmodel = true,
+};
+
+static int __init db9_init(void)
+{
+	int i;
+	int have_dev = 0;
+
+	for (i = 0; i < DB9_MAX_PORTS; i++) {
+		if (db9_cfg[i].nargs == 0 || db9_cfg[i].args[DB9_ARG_PARPORT] < 0)
+			continue;
+
+		if (db9_cfg[i].nargs < 2) {
+			printk(KERN_ERR "db9.c: Device type must be specified.\n");
+			return -EINVAL;
+		}
+
+		have_dev = 1;
+	}
+
+	if (!have_dev)
+		return -ENODEV;
+
+	return parport_register_driver(&db9_parport_driver);
+}
+
+static void __exit db9_exit(void)
+{
+	parport_unregister_driver(&db9_parport_driver);
+}
+
+module_init(db9_init);
+module_exit(db9_exit);
diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c
new file mode 100644
index 0000000..eae14d5
--- /dev/null
+++ b/drivers/input/joystick/gamecon.c
@@ -0,0 +1,1062 @@
+/*
+ * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux
+ *
+ *  Copyright (c) 1999-2004	Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2004		Peter Nelson <rufus-kernel@hackish.org>
+ *
+ *  Based on the work of:
+ *	Andree Borrmann		John Dahlstrom
+ *	David Kuder		Nathan Hand
+ *	Raphael Assenat
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("NES, SNES, N64, MultiSystem, PSX gamepad driver");
+MODULE_LICENSE("GPL");
+
+#define GC_MAX_PORTS		3
+#define GC_MAX_DEVICES		5
+
+struct gc_config {
+	int args[GC_MAX_DEVICES + 1];
+	unsigned int nargs;
+};
+
+static struct gc_config gc_cfg[GC_MAX_PORTS];
+
+module_param_array_named(map, gc_cfg[0].args, int, &gc_cfg[0].nargs, 0);
+MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<pad1>,<pad2>,..<pad5>)");
+module_param_array_named(map2, gc_cfg[1].args, int, &gc_cfg[1].nargs, 0);
+MODULE_PARM_DESC(map2, "Describes second set of devices");
+module_param_array_named(map3, gc_cfg[2].args, int, &gc_cfg[2].nargs, 0);
+MODULE_PARM_DESC(map3, "Describes third set of devices");
+
+/* see also gs_psx_delay parameter in PSX support section */
+
+enum gc_type {
+	GC_NONE = 0,
+	GC_SNES,
+	GC_NES,
+	GC_NES4,
+	GC_MULTI,
+	GC_MULTI2,
+	GC_N64,
+	GC_PSX,
+	GC_DDR,
+	GC_SNESMOUSE,
+	GC_MAX
+};
+
+#define GC_REFRESH_TIME	HZ/100
+
+struct gc_pad {
+	struct input_dev *dev;
+	enum gc_type type;
+	char phys[32];
+};
+
+struct gc {
+	struct pardevice *pd;
+	struct gc_pad pads[GC_MAX_DEVICES];
+	struct timer_list timer;
+	int pad_count[GC_MAX];
+	int used;
+	int parportno;
+	struct mutex mutex;
+};
+
+struct gc_subdev {
+	unsigned int idx;
+};
+
+static struct gc *gc_base[3];
+
+static const int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 };
+
+static const char *gc_names[] = {
+	NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick",
+	"Multisystem 2-button joystick", "N64 controller", "PSX controller",
+	"PSX DDR controller", "SNES mouse"
+};
+
+/*
+ * N64 support.
+ */
+
+static const unsigned char gc_n64_bytes[] = { 0, 1, 13, 15, 14, 12, 10, 11, 2, 3 };
+static const short gc_n64_btn[] = {
+	BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z,
+	BTN_TL, BTN_TR, BTN_TRIGGER, BTN_START
+};
+
+#define GC_N64_LENGTH		32		/* N64 bit length, not including stop bit */
+#define GC_N64_STOP_LENGTH	5		/* Length of encoded stop bit */
+#define GC_N64_CMD_00		0x11111111UL
+#define GC_N64_CMD_01		0xd1111111UL
+#define GC_N64_CMD_03		0xdd111111UL
+#define GC_N64_CMD_1b		0xdd1dd111UL
+#define GC_N64_CMD_c0		0x111111ddUL
+#define GC_N64_CMD_80		0x1111111dUL
+#define GC_N64_STOP_BIT		0x1d		/* Encoded stop bit */
+#define GC_N64_REQUEST_DATA	GC_N64_CMD_01	/* the request data command */
+#define GC_N64_DELAY		133		/* delay between transmit request, and response ready (us) */
+#define GC_N64_DWS		3		/* delay between write segments (required for sound playback because of ISA DMA) */
+						/* GC_N64_DWS > 24 is known to fail */
+#define GC_N64_POWER_W		0xe2		/* power during write (transmit request) */
+#define GC_N64_POWER_R		0xfd		/* power during read */
+#define GC_N64_OUT		0x1d		/* output bits to the 4 pads */
+						/* Reading the main axes of any N64 pad is known to fail if the corresponding bit */
+						/* in GC_N64_OUT is pulled low on the output port (by any routine) for more */
+						/* than 123 us */
+#define GC_N64_CLOCK		0x02		/* clock bits for read */
+
+/*
+ * Used for rumble code.
+ */
+
+/* Send encoded command */
+static void gc_n64_send_command(struct gc *gc, unsigned long cmd,
+				unsigned char target)
+{
+	struct parport *port = gc->pd->port;
+	int i;
+
+	for (i = 0; i < GC_N64_LENGTH; i++) {
+		unsigned char data = (cmd >> i) & 1 ? target : 0;
+		parport_write_data(port, GC_N64_POWER_W | data);
+		udelay(GC_N64_DWS);
+	}
+}
+
+/* Send stop bit */
+static void gc_n64_send_stop_bit(struct gc *gc, unsigned char target)
+{
+	struct parport *port = gc->pd->port;
+	int i;
+
+	for (i = 0; i < GC_N64_STOP_LENGTH; i++) {
+		unsigned char data = (GC_N64_STOP_BIT >> i) & 1 ? target : 0;
+		parport_write_data(port, GC_N64_POWER_W | data);
+		udelay(GC_N64_DWS);
+	}
+}
+
+/*
+ * gc_n64_read_packet() reads an N64 packet.
+ * Each pad uses one bit per byte. So all pads connected to this port
+ * are read in parallel.
+ */
+
+static void gc_n64_read_packet(struct gc *gc, unsigned char *data)
+{
+	int i;
+	unsigned long flags;
+
+/*
+ * Request the pad to transmit data
+ */
+
+	local_irq_save(flags);
+	gc_n64_send_command(gc, GC_N64_REQUEST_DATA, GC_N64_OUT);
+	gc_n64_send_stop_bit(gc, GC_N64_OUT);
+	local_irq_restore(flags);
+
+/*
+ * Wait for the pad response to be loaded into the 33-bit register
+ * of the adapter.
+ */
+
+	udelay(GC_N64_DELAY);
+
+/*
+ * Grab data (ignoring the last bit, which is a stop bit)
+ */
+
+	for (i = 0; i < GC_N64_LENGTH; i++) {
+		parport_write_data(gc->pd->port, GC_N64_POWER_R);
+		udelay(2);
+		data[i] = parport_read_status(gc->pd->port);
+		parport_write_data(gc->pd->port, GC_N64_POWER_R | GC_N64_CLOCK);
+	 }
+
+/*
+ * We must wait 200 ms here for the controller to reinitialize before
+ * the next read request. No worries as long as gc_read is polled less
+ * frequently than this.
+ */
+
+}
+
+static void gc_n64_process_packet(struct gc *gc)
+{
+	unsigned char data[GC_N64_LENGTH];
+	struct input_dev *dev;
+	int i, j, s;
+	signed char x, y;
+
+	gc_n64_read_packet(gc, data);
+
+	for (i = 0; i < GC_MAX_DEVICES; i++) {
+
+		if (gc->pads[i].type != GC_N64)
+			continue;
+
+		dev = gc->pads[i].dev;
+		s = gc_status_bit[i];
+
+		if (s & ~(data[8] | data[9])) {
+
+			x = y = 0;
+
+			for (j = 0; j < 8; j++) {
+				if (data[23 - j] & s)
+					x |= 1 << j;
+				if (data[31 - j] & s)
+					y |= 1 << j;
+			}
+
+			input_report_abs(dev, ABS_X,  x);
+			input_report_abs(dev, ABS_Y, -y);
+
+			input_report_abs(dev, ABS_HAT0X,
+					 !(s & data[6]) - !(s & data[7]));
+			input_report_abs(dev, ABS_HAT0Y,
+					 !(s & data[4]) - !(s & data[5]));
+
+			for (j = 0; j < 10; j++)
+				input_report_key(dev, gc_n64_btn[j],
+						 s & data[gc_n64_bytes[j]]);
+
+			input_sync(dev);
+		}
+	}
+}
+
+static int gc_n64_play_effect(struct input_dev *dev, void *data,
+			      struct ff_effect *effect)
+{
+	int i;
+	unsigned long flags;
+	struct gc *gc = input_get_drvdata(dev);
+	struct gc_subdev *sdev = data;
+	unsigned char target = 1 << sdev->idx; /* select desired pin */
+
+	if (effect->type == FF_RUMBLE) {
+		struct ff_rumble_effect *rumble = &effect->u.rumble;
+		unsigned int cmd =
+			rumble->strong_magnitude || rumble->weak_magnitude ?
+			GC_N64_CMD_01 : GC_N64_CMD_00;
+
+		local_irq_save(flags);
+
+		/* Init Rumble - 0x03, 0x80, 0x01, (34)0x80 */
+		gc_n64_send_command(gc, GC_N64_CMD_03, target);
+		gc_n64_send_command(gc, GC_N64_CMD_80, target);
+		gc_n64_send_command(gc, GC_N64_CMD_01, target);
+		for (i = 0; i < 32; i++)
+			gc_n64_send_command(gc, GC_N64_CMD_80, target);
+		gc_n64_send_stop_bit(gc, target);
+
+		udelay(GC_N64_DELAY);
+
+		/* Now start or stop it - 0x03, 0xc0, 0zx1b, (32)0x01/0x00 */
+		gc_n64_send_command(gc, GC_N64_CMD_03, target);
+		gc_n64_send_command(gc, GC_N64_CMD_c0, target);
+		gc_n64_send_command(gc, GC_N64_CMD_1b, target);
+		for (i = 0; i < 32; i++)
+			gc_n64_send_command(gc, cmd, target);
+		gc_n64_send_stop_bit(gc, target);
+
+		local_irq_restore(flags);
+
+	}
+
+	return 0;
+}
+
+static int gc_n64_init_ff(struct input_dev *dev, int i)
+{
+	struct gc_subdev *sdev;
+	int err;
+
+	sdev = kmalloc(sizeof(*sdev), GFP_KERNEL);
+	if (!sdev)
+		return -ENOMEM;
+
+	sdev->idx = i;
+
+	input_set_capability(dev, EV_FF, FF_RUMBLE);
+
+	err = input_ff_create_memless(dev, sdev, gc_n64_play_effect);
+	if (err) {
+		kfree(sdev);
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * NES/SNES support.
+ */
+
+#define GC_NES_DELAY		6	/* Delay between bits - 6us */
+#define GC_NES_LENGTH		8	/* The NES pads use 8 bits of data */
+#define GC_SNES_LENGTH		12	/* The SNES true length is 16, but the
+					   last 4 bits are unused */
+#define GC_SNESMOUSE_LENGTH	32	/* The SNES mouse uses 32 bits, the first
+					   16 bits are equivalent to a gamepad */
+
+#define GC_NES_POWER	0xfc
+#define GC_NES_CLOCK	0x01
+#define GC_NES_LATCH	0x02
+
+static const unsigned char gc_nes_bytes[] = { 0, 1, 2, 3 };
+static const unsigned char gc_snes_bytes[] = { 8, 0, 2, 3, 9, 1, 10, 11 };
+static const short gc_snes_btn[] = {
+	BTN_A, BTN_B, BTN_SELECT, BTN_START, BTN_X, BTN_Y, BTN_TL, BTN_TR
+};
+
+/*
+ * gc_nes_read_packet() reads a NES/SNES packet.
+ * Each pad uses one bit per byte. So all pads connected to
+ * this port are read in parallel.
+ */
+
+static void gc_nes_read_packet(struct gc *gc, int length, unsigned char *data)
+{
+	int i;
+
+	parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK | GC_NES_LATCH);
+	udelay(GC_NES_DELAY * 2);
+	parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK);
+
+	for (i = 0; i < length; i++) {
+		udelay(GC_NES_DELAY);
+		parport_write_data(gc->pd->port, GC_NES_POWER);
+		data[i] = parport_read_status(gc->pd->port) ^ 0x7f;
+		udelay(GC_NES_DELAY);
+		parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK);
+	}
+}
+
+static void gc_nes_process_packet(struct gc *gc)
+{
+	unsigned char data[GC_SNESMOUSE_LENGTH];
+	struct gc_pad *pad;
+	struct input_dev *dev;
+	int i, j, s, len;
+	char x_rel, y_rel;
+
+	len = gc->pad_count[GC_SNESMOUSE] ? GC_SNESMOUSE_LENGTH :
+			(gc->pad_count[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH);
+
+	gc_nes_read_packet(gc, len, data);
+
+	for (i = 0; i < GC_MAX_DEVICES; i++) {
+
+		pad = &gc->pads[i];
+		dev = pad->dev;
+		s = gc_status_bit[i];
+
+		switch (pad->type) {
+
+		case GC_NES:
+
+			input_report_abs(dev, ABS_X, !(s & data[6]) - !(s & data[7]));
+			input_report_abs(dev, ABS_Y, !(s & data[4]) - !(s & data[5]));
+
+			for (j = 0; j < 4; j++)
+				input_report_key(dev, gc_snes_btn[j],
+						 s & data[gc_nes_bytes[j]]);
+			input_sync(dev);
+			break;
+
+		case GC_SNES:
+
+			input_report_abs(dev, ABS_X, !(s & data[6]) - !(s & data[7]));
+			input_report_abs(dev, ABS_Y, !(s & data[4]) - !(s & data[5]));
+
+			for (j = 0; j < 8; j++)
+				input_report_key(dev, gc_snes_btn[j],
+						 s & data[gc_snes_bytes[j]]);
+			input_sync(dev);
+			break;
+
+		case GC_SNESMOUSE:
+			/*
+			 * The 4 unused bits from SNES controllers appear
+			 * to be ID bits so use them to make sure we are
+			 * dealing with a mouse.
+			 * gamepad is connected. This is important since
+			 * my SNES gamepad sends 1's for bits 16-31, which
+			 * cause the mouse pointer to quickly move to the
+			 * upper left corner of the screen.
+			 */
+			if (!(s & data[12]) && !(s & data[13]) &&
+			    !(s & data[14]) && (s & data[15])) {
+				input_report_key(dev, BTN_LEFT, s & data[9]);
+				input_report_key(dev, BTN_RIGHT, s & data[8]);
+
+				x_rel = y_rel = 0;
+				for (j = 0; j < 7; j++) {
+					x_rel <<= 1;
+					if (data[25 + j] & s)
+						x_rel |= 1;
+
+					y_rel <<= 1;
+					if (data[17 + j] & s)
+						y_rel |= 1;
+				}
+
+				if (x_rel) {
+					if (data[24] & s)
+						x_rel = -x_rel;
+					input_report_rel(dev, REL_X, x_rel);
+				}
+
+				if (y_rel) {
+					if (data[16] & s)
+						y_rel = -y_rel;
+					input_report_rel(dev, REL_Y, y_rel);
+				}
+
+				input_sync(dev);
+			}
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+/*
+ * Multisystem joystick support
+ */
+
+#define GC_MULTI_LENGTH		5	/* Multi system joystick packet length is 5 */
+#define GC_MULTI2_LENGTH	6	/* One more bit for one more button */
+
+/*
+ * gc_multi_read_packet() reads a Multisystem joystick packet.
+ */
+
+static void gc_multi_read_packet(struct gc *gc, int length, unsigned char *data)
+{
+	int i;
+
+	for (i = 0; i < length; i++) {
+		parport_write_data(gc->pd->port, ~(1 << i));
+		data[i] = parport_read_status(gc->pd->port) ^ 0x7f;
+	}
+}
+
+static void gc_multi_process_packet(struct gc *gc)
+{
+	unsigned char data[GC_MULTI2_LENGTH];
+	int data_len = gc->pad_count[GC_MULTI2] ? GC_MULTI2_LENGTH : GC_MULTI_LENGTH;
+	struct gc_pad *pad;
+	struct input_dev *dev;
+	int i, s;
+
+	gc_multi_read_packet(gc, data_len, data);
+
+	for (i = 0; i < GC_MAX_DEVICES; i++) {
+		pad = &gc->pads[i];
+		dev = pad->dev;
+		s = gc_status_bit[i];
+
+		switch (pad->type) {
+		case GC_MULTI2:
+			input_report_key(dev, BTN_THUMB, s & data[5]);
+			/* fall through */
+
+		case GC_MULTI:
+			input_report_abs(dev, ABS_X,
+					 !(s & data[2]) - !(s & data[3]));
+			input_report_abs(dev, ABS_Y,
+					 !(s & data[0]) - !(s & data[1]));
+			input_report_key(dev, BTN_TRIGGER, s & data[4]);
+			input_sync(dev);
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+/*
+ * PSX support
+ *
+ * See documentation at:
+ *	http://www.geocities.co.jp/Playtown/2004/psx/ps_eng.txt	
+ *	http://www.gamesx.com/controldata/psxcont/psxcont.htm
+ *
+ */
+
+#define GC_PSX_DELAY	25		/* 25 usec */
+#define GC_PSX_LENGTH	8		/* talk to the controller in bits */
+#define GC_PSX_BYTES	6		/* the maximum number of bytes to read off the controller */
+
+#define GC_PSX_MOUSE	1		/* Mouse */
+#define GC_PSX_NEGCON	2		/* NegCon */
+#define GC_PSX_NORMAL	4		/* Digital / Analog or Rumble in Digital mode  */
+#define GC_PSX_ANALOG	5		/* Analog in Analog mode / Rumble in Green mode */
+#define GC_PSX_RUMBLE	7		/* Rumble in Red mode */
+
+#define GC_PSX_CLOCK	0x04		/* Pin 4 */
+#define GC_PSX_COMMAND	0x01		/* Pin 2 */
+#define GC_PSX_POWER	0xf8		/* Pins 5-9 */
+#define GC_PSX_SELECT	0x02		/* Pin 3 */
+
+#define GC_PSX_ID(x)	((x) >> 4)	/* High nibble is device type */
+#define GC_PSX_LEN(x)	(((x) & 0xf) << 1)	/* Low nibble is length in bytes/2 */
+
+static int gc_psx_delay = GC_PSX_DELAY;
+module_param_named(psx_delay, gc_psx_delay, uint, 0);
+MODULE_PARM_DESC(psx_delay, "Delay when accessing Sony PSX controller (usecs)");
+
+static const short gc_psx_abs[] = {
+	ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_HAT0X, ABS_HAT0Y
+};
+static const short gc_psx_btn[] = {
+	BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_A, BTN_B, BTN_X, BTN_Y,
+	BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR
+};
+static const short gc_psx_ddr_btn[] = { BTN_0, BTN_1, BTN_2, BTN_3 };
+
+/*
+ * gc_psx_command() writes 8bit command and reads 8bit data from
+ * the psx pad.
+ */
+
+static void gc_psx_command(struct gc *gc, int b, unsigned char *data)
+{
+	struct parport *port = gc->pd->port;
+	int i, j, cmd, read;
+
+	memset(data, 0, GC_MAX_DEVICES);
+
+	for (i = 0; i < GC_PSX_LENGTH; i++, b >>= 1) {
+		cmd = (b & 1) ? GC_PSX_COMMAND : 0;
+		parport_write_data(port, cmd | GC_PSX_POWER);
+		udelay(gc_psx_delay);
+
+		read = parport_read_status(port) ^ 0x80;
+
+		for (j = 0; j < GC_MAX_DEVICES; j++) {
+			struct gc_pad *pad = &gc->pads[j];
+
+			if (pad->type == GC_PSX || pad->type == GC_DDR)
+				data[j] |= (read & gc_status_bit[j]) ? (1 << i) : 0;
+		}
+
+		parport_write_data(gc->pd->port, cmd | GC_PSX_CLOCK | GC_PSX_POWER);
+		udelay(gc_psx_delay);
+	}
+}
+
+/*
+ * gc_psx_read_packet() reads a whole psx packet and returns
+ * device identifier code.
+ */
+
+static void gc_psx_read_packet(struct gc *gc,
+			       unsigned char data[GC_MAX_DEVICES][GC_PSX_BYTES],
+			       unsigned char id[GC_MAX_DEVICES])
+{
+	int i, j, max_len = 0;
+	unsigned long flags;
+	unsigned char data2[GC_MAX_DEVICES];
+
+	/* Select pad */
+	parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER);
+	udelay(gc_psx_delay);
+	/* Deselect, begin command */
+	parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_POWER);
+	udelay(gc_psx_delay);
+
+	local_irq_save(flags);
+
+	gc_psx_command(gc, 0x01, data2);	/* Access pad */
+	gc_psx_command(gc, 0x42, id);		/* Get device ids */
+	gc_psx_command(gc, 0, data2);		/* Dump status */
+
+	/* Find the longest pad */
+	for (i = 0; i < GC_MAX_DEVICES; i++) {
+		struct gc_pad *pad = &gc->pads[i];
+
+		if ((pad->type == GC_PSX || pad->type == GC_DDR) &&
+		    GC_PSX_LEN(id[i]) > max_len &&
+		    GC_PSX_LEN(id[i]) <= GC_PSX_BYTES) {
+			max_len = GC_PSX_LEN(id[i]);
+		}
+	}
+
+	/* Read in all the data */
+	for (i = 0; i < max_len; i++) {
+		gc_psx_command(gc, 0, data2);
+		for (j = 0; j < GC_MAX_DEVICES; j++)
+			data[j][i] = data2[j];
+	}
+
+	local_irq_restore(flags);
+
+	parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER);
+
+	/* Set id's to the real value */
+	for (i = 0; i < GC_MAX_DEVICES; i++)
+		id[i] = GC_PSX_ID(id[i]);
+}
+
+static void gc_psx_report_one(struct gc_pad *pad, unsigned char psx_type,
+			      unsigned char *data)
+{
+	struct input_dev *dev = pad->dev;
+	int i;
+
+	switch (psx_type) {
+
+	case GC_PSX_RUMBLE:
+
+		input_report_key(dev, BTN_THUMBL, ~data[0] & 0x04);
+		input_report_key(dev, BTN_THUMBR, ~data[0] & 0x02);
+
+	case GC_PSX_NEGCON:
+	case GC_PSX_ANALOG:
+
+		if (pad->type == GC_DDR) {
+			for (i = 0; i < 4; i++)
+				input_report_key(dev, gc_psx_ddr_btn[i],
+						 ~data[0] & (0x10 << i));
+		} else {
+			for (i = 0; i < 4; i++)
+				input_report_abs(dev, gc_psx_abs[i + 2],
+						 data[i + 2]);
+
+			input_report_abs(dev, ABS_X,
+				!!(data[0] & 0x80) * 128 + !(data[0] & 0x20) * 127);
+			input_report_abs(dev, ABS_Y,
+				!!(data[0] & 0x10) * 128 + !(data[0] & 0x40) * 127);
+		}
+
+		for (i = 0; i < 8; i++)
+			input_report_key(dev, gc_psx_btn[i], ~data[1] & (1 << i));
+
+		input_report_key(dev, BTN_START,  ~data[0] & 0x08);
+		input_report_key(dev, BTN_SELECT, ~data[0] & 0x01);
+
+		input_sync(dev);
+
+		break;
+
+	case GC_PSX_NORMAL:
+
+		if (pad->type == GC_DDR) {
+			for (i = 0; i < 4; i++)
+				input_report_key(dev, gc_psx_ddr_btn[i],
+						 ~data[0] & (0x10 << i));
+		} else {
+			input_report_abs(dev, ABS_X,
+				!!(data[0] & 0x80) * 128 + !(data[0] & 0x20) * 127);
+			input_report_abs(dev, ABS_Y,
+				!!(data[0] & 0x10) * 128 + !(data[0] & 0x40) * 127);
+
+			/*
+			 * For some reason if the extra axes are left unset
+			 * they drift.
+			 * for (i = 0; i < 4; i++)
+				input_report_abs(dev, gc_psx_abs[i + 2], 128);
+			 * This needs to be debugged properly,
+			 * maybe fuzz processing needs to be done
+			 * in input_sync()
+			 *				 --vojtech
+			 */
+		}
+
+		for (i = 0; i < 8; i++)
+			input_report_key(dev, gc_psx_btn[i], ~data[1] & (1 << i));
+
+		input_report_key(dev, BTN_START,  ~data[0] & 0x08);
+		input_report_key(dev, BTN_SELECT, ~data[0] & 0x01);
+
+		input_sync(dev);
+
+		break;
+
+	default: /* not a pad, ignore */
+		break;
+	}
+}
+
+static void gc_psx_process_packet(struct gc *gc)
+{
+	unsigned char data[GC_MAX_DEVICES][GC_PSX_BYTES];
+	unsigned char id[GC_MAX_DEVICES];
+	struct gc_pad *pad;
+	int i;
+
+	gc_psx_read_packet(gc, data, id);
+
+	for (i = 0; i < GC_MAX_DEVICES; i++) {
+		pad = &gc->pads[i];
+		if (pad->type == GC_PSX || pad->type == GC_DDR)
+			gc_psx_report_one(pad, id[i], data[i]);
+	}
+}
+
+/*
+ * gc_timer() initiates reads of console pads data.
+ */
+
+static void gc_timer(unsigned long private)
+{
+	struct gc *gc = (void *) private;
+
+/*
+ * N64 pads - must be read first, any read confuses them for 200 us
+ */
+
+	if (gc->pad_count[GC_N64])
+		gc_n64_process_packet(gc);
+
+/*
+ * NES and SNES pads or mouse
+ */
+
+	if (gc->pad_count[GC_NES] ||
+	    gc->pad_count[GC_SNES] ||
+	    gc->pad_count[GC_SNESMOUSE]) {
+		gc_nes_process_packet(gc);
+	}
+
+/*
+ * Multi and Multi2 joysticks
+ */
+
+	if (gc->pad_count[GC_MULTI] || gc->pad_count[GC_MULTI2])
+		gc_multi_process_packet(gc);
+
+/*
+ * PSX controllers
+ */
+
+	if (gc->pad_count[GC_PSX] || gc->pad_count[GC_DDR])
+		gc_psx_process_packet(gc);
+
+	mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME);
+}
+
+static int gc_open(struct input_dev *dev)
+{
+	struct gc *gc = input_get_drvdata(dev);
+	int err;
+
+	err = mutex_lock_interruptible(&gc->mutex);
+	if (err)
+		return err;
+
+	if (!gc->used++) {
+		parport_claim(gc->pd);
+		parport_write_control(gc->pd->port, 0x04);
+		mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME);
+	}
+
+	mutex_unlock(&gc->mutex);
+	return 0;
+}
+
+static void gc_close(struct input_dev *dev)
+{
+	struct gc *gc = input_get_drvdata(dev);
+
+	mutex_lock(&gc->mutex);
+	if (!--gc->used) {
+		del_timer_sync(&gc->timer);
+		parport_write_control(gc->pd->port, 0x00);
+		parport_release(gc->pd);
+	}
+	mutex_unlock(&gc->mutex);
+}
+
+static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
+{
+	struct gc_pad *pad = &gc->pads[idx];
+	struct input_dev *input_dev;
+	int i;
+	int err;
+
+	if (pad_type < 1 || pad_type >= GC_MAX) {
+		pr_err("Pad type %d unknown\n", pad_type);
+		return -EINVAL;
+	}
+
+	pad->dev = input_dev = input_allocate_device();
+	if (!input_dev) {
+		pr_err("Not enough memory for input device\n");
+		return -ENOMEM;
+	}
+
+	pad->type = pad_type;
+
+	snprintf(pad->phys, sizeof(pad->phys),
+		 "%s/input%d", gc->pd->port->name, idx);
+
+	input_dev->name = gc_names[pad_type];
+	input_dev->phys = pad->phys;
+	input_dev->id.bustype = BUS_PARPORT;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = pad_type;
+	input_dev->id.version = 0x0100;
+
+	input_set_drvdata(input_dev, gc);
+
+	input_dev->open = gc_open;
+	input_dev->close = gc_close;
+
+	if (pad_type != GC_SNESMOUSE) {
+		input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+		for (i = 0; i < 2; i++)
+			input_set_abs_params(input_dev, ABS_X + i, -1, 1, 0, 0);
+	} else
+		input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+
+	gc->pad_count[pad_type]++;
+
+	switch (pad_type) {
+
+	case GC_N64:
+		for (i = 0; i < 10; i++)
+			__set_bit(gc_n64_btn[i], input_dev->keybit);
+
+		for (i = 0; i < 2; i++) {
+			input_set_abs_params(input_dev, ABS_X + i, -127, 126, 0, 2);
+			input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0);
+		}
+
+		err = gc_n64_init_ff(input_dev, idx);
+		if (err) {
+			pr_warning("Failed to initiate rumble for N64 device %d\n", idx);
+			goto err_free_dev;
+		}
+
+		break;
+
+	case GC_SNESMOUSE:
+		__set_bit(BTN_LEFT, input_dev->keybit);
+		__set_bit(BTN_RIGHT, input_dev->keybit);
+		__set_bit(REL_X, input_dev->relbit);
+		__set_bit(REL_Y, input_dev->relbit);
+		break;
+
+	case GC_SNES:
+		for (i = 4; i < 8; i++)
+			__set_bit(gc_snes_btn[i], input_dev->keybit);
+	case GC_NES:
+		for (i = 0; i < 4; i++)
+			__set_bit(gc_snes_btn[i], input_dev->keybit);
+		break;
+
+	case GC_MULTI2:
+		__set_bit(BTN_THUMB, input_dev->keybit);
+	case GC_MULTI:
+		__set_bit(BTN_TRIGGER, input_dev->keybit);
+		break;
+
+	case GC_PSX:
+		for (i = 0; i < 6; i++)
+			input_set_abs_params(input_dev,
+					     gc_psx_abs[i], 4, 252, 0, 2);
+		for (i = 0; i < 12; i++)
+			__set_bit(gc_psx_btn[i], input_dev->keybit);
+
+		break;
+
+	case GC_DDR:
+		for (i = 0; i < 4; i++)
+			__set_bit(gc_psx_ddr_btn[i], input_dev->keybit);
+		for (i = 0; i < 12; i++)
+			__set_bit(gc_psx_btn[i], input_dev->keybit);
+
+		break;
+	}
+
+	err = input_register_device(pad->dev);
+	if (err)
+		goto err_free_dev;
+
+	return 0;
+
+err_free_dev:
+	input_free_device(pad->dev);
+	pad->dev = NULL;
+	return err;
+}
+
+static void gc_attach(struct parport *pp)
+{
+	struct gc *gc;
+	struct pardevice *pd;
+	int i, port_idx;
+	int count = 0;
+	int *pads, n_pads;
+	struct pardev_cb gc_parport_cb;
+
+	for (port_idx = 0; port_idx < GC_MAX_PORTS; port_idx++) {
+		if (gc_cfg[port_idx].nargs == 0 || gc_cfg[port_idx].args[0] < 0)
+			continue;
+
+		if (gc_cfg[port_idx].args[0] == pp->number)
+			break;
+	}
+
+	if (port_idx == GC_MAX_PORTS) {
+		pr_debug("Not using parport%d.\n", pp->number);
+		return;
+	}
+	pads = gc_cfg[port_idx].args + 1;
+	n_pads = gc_cfg[port_idx].nargs - 1;
+
+	memset(&gc_parport_cb, 0, sizeof(gc_parport_cb));
+	gc_parport_cb.flags = PARPORT_FLAG_EXCL;
+
+	pd = parport_register_dev_model(pp, "gamecon", &gc_parport_cb,
+					port_idx);
+	if (!pd) {
+		pr_err("parport busy already - lp.o loaded?\n");
+		return;
+	}
+
+	gc = kzalloc(sizeof(struct gc), GFP_KERNEL);
+	if (!gc) {
+		pr_err("Not enough memory\n");
+		goto err_unreg_pardev;
+	}
+
+	mutex_init(&gc->mutex);
+	gc->pd = pd;
+	gc->parportno = pp->number;
+	setup_timer(&gc->timer, gc_timer, (long) gc);
+
+	for (i = 0; i < n_pads && i < GC_MAX_DEVICES; i++) {
+		if (!pads[i])
+			continue;
+
+		if (gc_setup_pad(gc, i, pads[i]))
+			goto err_unreg_devs;
+
+		count++;
+	}
+
+	if (count == 0) {
+		pr_err("No valid devices specified\n");
+		goto err_free_gc;
+	}
+
+	gc_base[port_idx] = gc;
+	return;
+
+ err_unreg_devs:
+	while (--i >= 0)
+		if (gc->pads[i].dev)
+			input_unregister_device(gc->pads[i].dev);
+ err_free_gc:
+	kfree(gc);
+ err_unreg_pardev:
+	parport_unregister_device(pd);
+}
+
+static void gc_detach(struct parport *port)
+{
+	int i;
+	struct gc *gc;
+
+	for (i = 0; i < GC_MAX_PORTS; i++) {
+		if (gc_base[i] && gc_base[i]->parportno == port->number)
+			break;
+	}
+
+	if (i == GC_MAX_PORTS)
+		return;
+
+	gc = gc_base[i];
+	gc_base[i] = NULL;
+
+	for (i = 0; i < GC_MAX_DEVICES; i++)
+		if (gc->pads[i].dev)
+			input_unregister_device(gc->pads[i].dev);
+	parport_unregister_device(gc->pd);
+	kfree(gc);
+}
+
+static struct parport_driver gc_parport_driver = {
+	.name = "gamecon",
+	.match_port = gc_attach,
+	.detach = gc_detach,
+	.devmodel = true,
+};
+
+static int __init gc_init(void)
+{
+	int i;
+	int have_dev = 0;
+
+	for (i = 0; i < GC_MAX_PORTS; i++) {
+		if (gc_cfg[i].nargs == 0 || gc_cfg[i].args[0] < 0)
+			continue;
+
+		if (gc_cfg[i].nargs < 2) {
+			pr_err("at least one device must be specified\n");
+			return -EINVAL;
+		}
+
+		have_dev = 1;
+	}
+
+	if (!have_dev)
+		return -ENODEV;
+
+	return parport_register_driver(&gc_parport_driver);
+}
+
+static void __exit gc_exit(void)
+{
+	parport_unregister_driver(&gc_parport_driver);
+}
+
+module_init(gc_init);
+module_exit(gc_exit);
diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c
new file mode 100644
index 0000000..0f519db
--- /dev/null
+++ b/drivers/input/joystick/gf2k.c
@@ -0,0 +1,375 @@
+/*
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * Genius Flight 2000 joystick driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_DESC	"Genius Flight 2000 joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define GF2K_START		400	/* The time we wait for the first bit [400 us] */
+#define GF2K_STROBE		40	/* The time we wait for the first bit [40 us] */
+#define GF2K_TIMEOUT		4	/* Wait for everything to settle [4 ms] */
+#define GF2K_LENGTH		80	/* Max number of triplets in a packet */
+
+/*
+ * Genius joystick ids ...
+ */
+
+#define GF2K_ID_G09		1
+#define GF2K_ID_F30D		2
+#define GF2K_ID_F30		3
+#define GF2K_ID_F31D		4
+#define GF2K_ID_F305		5
+#define GF2K_ID_F23P		6
+#define GF2K_ID_F31		7
+#define GF2K_ID_MAX		7
+
+static char gf2k_length[] = { 40, 40, 40, 40, 40, 40, 40, 40 };
+static char gf2k_hat_to_axis[][2] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+static char *gf2k_names[] = {"", "Genius G-09D", "Genius F-30D", "Genius F-30", "Genius MaxFighter F-31D",
+				"Genius F-30-5", "Genius Flight2000 F-23", "Genius F-31"};
+static unsigned char gf2k_hats[] = { 0, 2, 0, 0, 2, 0, 2, 0 };
+static unsigned char gf2k_axes[] = { 0, 2, 0, 0, 4, 0, 4, 0 };
+static unsigned char gf2k_joys[] = { 0, 0, 0, 0,10, 0, 8, 0 };
+static unsigned char gf2k_pads[] = { 0, 6, 0, 0, 0, 0, 0, 0 };
+static unsigned char gf2k_lens[] = { 0,18, 0, 0,18, 0,18, 0 };
+
+static unsigned char gf2k_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_GAS, ABS_BRAKE };
+static short gf2k_btn_joy[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 };
+static short gf2k_btn_pad[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_START, BTN_SELECT };
+
+
+static short gf2k_seq_reset[] = { 240, 340, 0 };
+static short gf2k_seq_digital[] = { 590, 320, 860, 0 };
+
+struct gf2k {
+	struct gameport *gameport;
+	struct input_dev *dev;
+	int reads;
+	int bads;
+	unsigned char id;
+	unsigned char length;
+	char phys[32];
+};
+
+/*
+ * gf2k_read_packet() reads a Genius Flight2000 packet.
+ */
+
+static int gf2k_read_packet(struct gameport *gameport, int length, char *data)
+{
+	unsigned char u, v;
+	int i;
+	unsigned int t, p;
+	unsigned long flags;
+
+	t = gameport_time(gameport, GF2K_START);
+	p = gameport_time(gameport, GF2K_STROBE);
+
+	i = 0;
+
+	local_irq_save(flags);
+
+	gameport_trigger(gameport);
+	v = gameport_read(gameport);
+
+	while (t > 0 && i < length) {
+		t--; u = v;
+		v = gameport_read(gameport);
+		if (v & ~u & 0x10) {
+			data[i++] = v >> 5;
+			t = p;
+		}
+	}
+
+	local_irq_restore(flags);
+
+	return i;
+}
+
+/*
+ * gf2k_trigger_seq() initializes a Genius Flight2000 joystick
+ * into digital mode.
+ */
+
+static void gf2k_trigger_seq(struct gameport *gameport, short *seq)
+{
+
+	unsigned long flags;
+	int i, t;
+
+        local_irq_save(flags);
+
+	i = 0;
+        do {
+		gameport_trigger(gameport);
+		t = gameport_time(gameport, GF2K_TIMEOUT * 1000);
+		while ((gameport_read(gameport) & 1) && t) t--;
+                udelay(seq[i]);
+        } while (seq[++i]);
+
+	gameport_trigger(gameport);
+
+	local_irq_restore(flags);
+}
+
+/*
+ * js_sw_get_bits() composes bits from the triplet buffer into a __u64.
+ * Parameter 'pos' is bit number inside packet where to start at, 'num' is number
+ * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits
+ * is number of bits per triplet.
+ */
+
+#define GB(p,n,s)	gf2k_get_bits(data, p, n, s)
+
+static int gf2k_get_bits(unsigned char *buf, int pos, int num, int shift)
+{
+	__u64 data = 0;
+	int i;
+
+	for (i = 0; i < num / 3 + 2; i++)
+		data |= buf[pos / 3 + i] << (i * 3);
+	data >>= pos % 3;
+	data &= (1 << num) - 1;
+	data <<= shift;
+
+	return data;
+}
+
+static void gf2k_read(struct gf2k *gf2k, unsigned char *data)
+{
+	struct input_dev *dev = gf2k->dev;
+	int i, t;
+
+	for (i = 0; i < 4 && i < gf2k_axes[gf2k->id]; i++)
+		input_report_abs(dev, gf2k_abs[i], GB(i<<3,8,0) | GB(i+46,1,8) | GB(i+50,1,9));
+
+	for (i = 0; i < 2 && i < gf2k_axes[gf2k->id] - 4; i++)
+		input_report_abs(dev, gf2k_abs[i], GB(i*9+60,8,0) | GB(i+54,1,9));
+
+	t = GB(40,4,0);
+
+	for (i = 0; i < gf2k_hats[gf2k->id]; i++)
+		input_report_abs(dev, ABS_HAT0X + i, gf2k_hat_to_axis[t][i]);
+
+	t = GB(44,2,0) | GB(32,8,2) | GB(78,2,10);
+
+	for (i = 0; i < gf2k_joys[gf2k->id]; i++)
+		input_report_key(dev, gf2k_btn_joy[i], (t >> i) & 1);
+
+	for (i = 0; i < gf2k_pads[gf2k->id]; i++)
+		input_report_key(dev, gf2k_btn_pad[i], (t >> i) & 1);
+
+	input_sync(dev);
+}
+
+/*
+ * gf2k_poll() reads and analyzes Genius joystick data.
+ */
+
+static void gf2k_poll(struct gameport *gameport)
+{
+	struct gf2k *gf2k = gameport_get_drvdata(gameport);
+	unsigned char data[GF2K_LENGTH];
+
+	gf2k->reads++;
+
+	if (gf2k_read_packet(gf2k->gameport, gf2k_length[gf2k->id], data) < gf2k_length[gf2k->id])
+		gf2k->bads++;
+	else
+		gf2k_read(gf2k, data);
+}
+
+static int gf2k_open(struct input_dev *dev)
+{
+	struct gf2k *gf2k = input_get_drvdata(dev);
+
+	gameport_start_polling(gf2k->gameport);
+	return 0;
+}
+
+static void gf2k_close(struct input_dev *dev)
+{
+	struct gf2k *gf2k = input_get_drvdata(dev);
+
+	gameport_stop_polling(gf2k->gameport);
+}
+
+/*
+ * gf2k_connect() probes for Genius id joysticks.
+ */
+
+static int gf2k_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct gf2k *gf2k;
+	struct input_dev *input_dev;
+	unsigned char data[GF2K_LENGTH];
+	int i, err;
+
+	gf2k = kzalloc(sizeof(struct gf2k), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!gf2k || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	gf2k->gameport = gameport;
+	gf2k->dev = input_dev;
+
+	gameport_set_drvdata(gameport, gf2k);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	gf2k_trigger_seq(gameport, gf2k_seq_reset);
+
+	msleep(GF2K_TIMEOUT);
+
+	gf2k_trigger_seq(gameport, gf2k_seq_digital);
+
+	msleep(GF2K_TIMEOUT);
+
+	if (gf2k_read_packet(gameport, GF2K_LENGTH, data) < 12) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	if (!(gf2k->id = GB(7,2,0) | GB(3,3,2) | GB(0,3,5))) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+#ifdef RESET_WORKS
+	if ((gf2k->id != (GB(19,2,0) | GB(15,3,2) | GB(12,3,5))) &&
+	    (gf2k->id != (GB(31,2,0) | GB(27,3,2) | GB(24,3,5)))) {
+		err = -ENODEV;
+		goto fail2;
+	}
+#else
+	gf2k->id = 6;
+#endif
+
+	if (gf2k->id > GF2K_ID_MAX || !gf2k_axes[gf2k->id]) {
+		printk(KERN_WARNING "gf2k.c: Not yet supported joystick on %s. [id: %d type:%s]\n",
+			gameport->phys, gf2k->id, gf2k->id > GF2K_ID_MAX ? "Unknown" : gf2k_names[gf2k->id]);
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, gf2k_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	snprintf(gf2k->phys, sizeof(gf2k->phys), "%s/input0", gameport->phys);
+
+	gf2k->length = gf2k_lens[gf2k->id];
+
+	input_dev->name = gf2k_names[gf2k->id];
+	input_dev->phys = gf2k->phys;
+	input_dev->id.bustype = BUS_GAMEPORT;
+	input_dev->id.vendor = GAMEPORT_ID_VENDOR_GENIUS;
+	input_dev->id.product = gf2k->id;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &gameport->dev;
+
+	input_set_drvdata(input_dev, gf2k);
+
+	input_dev->open = gf2k_open;
+	input_dev->close = gf2k_close;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	for (i = 0; i < gf2k_axes[gf2k->id]; i++)
+		set_bit(gf2k_abs[i], input_dev->absbit);
+
+	for (i = 0; i < gf2k_hats[gf2k->id]; i++)
+		input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0);
+
+	for (i = 0; i < gf2k_joys[gf2k->id]; i++)
+		set_bit(gf2k_btn_joy[i], input_dev->keybit);
+
+	for (i = 0; i < gf2k_pads[gf2k->id]; i++)
+		set_bit(gf2k_btn_pad[i], input_dev->keybit);
+
+	gf2k_read_packet(gameport, gf2k->length, data);
+	gf2k_read(gf2k, data);
+
+	for (i = 0; i < gf2k_axes[gf2k->id]; i++) {
+		int max = i < 2 ?
+			input_abs_get_val(input_dev, gf2k_abs[i]) * 2 :
+			input_abs_get_val(input_dev, gf2k_abs[0]) +
+				input_abs_get_val(input_dev, gf2k_abs[1]);
+		int flat = i < 2 ? 24 : 0;
+
+		input_set_abs_params(input_dev, gf2k_abs[i],
+				     32, max - 32, 8, flat);
+	}
+
+	err = input_register_device(gf2k->dev);
+	if (err)
+		goto fail2;
+
+	return 0;
+
+ fail2:	gameport_close(gameport);
+ fail1:	gameport_set_drvdata(gameport, NULL);
+	input_free_device(input_dev);
+	kfree(gf2k);
+	return err;
+}
+
+static void gf2k_disconnect(struct gameport *gameport)
+{
+	struct gf2k *gf2k = gameport_get_drvdata(gameport);
+
+	input_unregister_device(gf2k->dev);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(gf2k);
+}
+
+static struct gameport_driver gf2k_drv = {
+	.driver		= {
+		.name	= "gf2k",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= gf2k_connect,
+	.disconnect	= gf2k_disconnect,
+};
+
+module_gameport_driver(gf2k_drv);
diff --git a/drivers/input/joystick/grip.c b/drivers/input/joystick/grip.c
new file mode 100644
index 0000000..eac9c5b
--- /dev/null
+++ b/drivers/input/joystick/grip.c
@@ -0,0 +1,426 @@
+/*
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * Gravis/Kensington GrIP protocol joystick and gamepad driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_DESC	"Gravis GrIP protocol joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define GRIP_MODE_GPP		1
+#define GRIP_MODE_BD		2
+#define GRIP_MODE_XT		3
+#define GRIP_MODE_DC		4
+
+#define GRIP_LENGTH_GPP		24
+#define GRIP_STROBE_GPP		200	/* 200 us */
+#define GRIP_LENGTH_XT		4
+#define GRIP_STROBE_XT		64	/* 64 us */
+#define GRIP_MAX_CHUNKS_XT	10
+#define GRIP_MAX_BITS_XT	30
+
+struct grip {
+	struct gameport *gameport;
+	struct input_dev *dev[2];
+	unsigned char mode[2];
+	int reads;
+	int bads;
+	char phys[2][32];
+};
+
+static int grip_btn_gpp[] = { BTN_START, BTN_SELECT, BTN_TR2, BTN_Y, 0, BTN_TL2, BTN_A, BTN_B, BTN_X, 0, BTN_TL, BTN_TR, -1 };
+static int grip_btn_bd[] = { BTN_THUMB, BTN_THUMB2, BTN_TRIGGER, BTN_TOP, BTN_BASE, -1 };
+static int grip_btn_xt[] = { BTN_TRIGGER, BTN_THUMB, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_SELECT, BTN_START, BTN_MODE, -1 };
+static int grip_btn_dc[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, -1 };
+
+static int grip_abs_gpp[] = { ABS_X, ABS_Y, -1 };
+static int grip_abs_bd[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
+static int grip_abs_xt[] = { ABS_X, ABS_Y, ABS_BRAKE, ABS_GAS, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, -1 };
+static int grip_abs_dc[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
+
+static char *grip_name[] = { NULL, "Gravis GamePad Pro", "Gravis Blackhawk Digital",
+				"Gravis Xterminator Digital", "Gravis Xterminator DualControl" };
+static int *grip_abs[] = { NULL, grip_abs_gpp, grip_abs_bd, grip_abs_xt, grip_abs_dc };
+static int *grip_btn[] = { NULL, grip_btn_gpp, grip_btn_bd, grip_btn_xt, grip_btn_dc };
+static char grip_anx[] = { 0, 0, 3, 5, 5 };
+static char grip_cen[] = { 0, 0, 2, 2, 4 };
+
+/*
+ * grip_gpp_read_packet() reads a Gravis GamePad Pro packet.
+ */
+
+static int grip_gpp_read_packet(struct gameport *gameport, int shift, unsigned int *data)
+{
+	unsigned long flags;
+	unsigned char u, v;
+	unsigned int t;
+	int i;
+
+	int strobe = gameport_time(gameport, GRIP_STROBE_GPP);
+
+	data[0] = 0;
+	t = strobe;
+	i = 0;
+
+	local_irq_save(flags);
+
+	v = gameport_read(gameport) >> shift;
+
+	do {
+		t--;
+		u = v; v = (gameport_read(gameport) >> shift) & 3;
+		if (~v & u & 1) {
+			data[0] |= (v >> 1) << i++;
+			t = strobe;
+		}
+	} while (i < GRIP_LENGTH_GPP && t > 0);
+
+	local_irq_restore(flags);
+
+	if (i < GRIP_LENGTH_GPP) return -1;
+
+	for (i = 0; i < GRIP_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++)
+		data[0] = data[0] >> 1 | (data[0] & 1) << (GRIP_LENGTH_GPP - 1);
+
+	return -(i == GRIP_LENGTH_GPP);
+}
+
+/*
+ * grip_xt_read_packet() reads a Gravis Xterminator packet.
+ */
+
+static int grip_xt_read_packet(struct gameport *gameport, int shift, unsigned int *data)
+{
+	unsigned int i, j, buf, crc;
+	unsigned char u, v, w;
+	unsigned long flags;
+	unsigned int t;
+	char status;
+
+	int strobe = gameport_time(gameport, GRIP_STROBE_XT);
+
+	data[0] = data[1] = data[2] = data[3] = 0;
+	status = buf = i = j = 0;
+	t = strobe;
+
+	local_irq_save(flags);
+
+	v = w = (gameport_read(gameport) >> shift) & 3;
+
+	do {
+		t--;
+		u = (gameport_read(gameport) >> shift) & 3;
+
+		if (u ^ v) {
+
+			if ((u ^ v) & 1) {
+				buf = (buf << 1) | (u >> 1);
+				t = strobe;
+				i++;
+			} else
+
+			if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) {
+				if (i == 20) {
+					crc = buf ^ (buf >> 7) ^ (buf >> 14);
+					if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) {
+						data[buf >> 18] = buf >> 4;
+						status |= 1 << (buf >> 18);
+					}
+					j++;
+				}
+				t = strobe;
+				buf = 0;
+				i = 0;
+			}
+			w = v;
+			v = u;
+		}
+
+	} while (status != 0xf && i < GRIP_MAX_BITS_XT && j < GRIP_MAX_CHUNKS_XT && t > 0);
+
+	local_irq_restore(flags);
+
+	return -(status != 0xf);
+}
+
+/*
+ * grip_timer() repeatedly polls the joysticks and generates events.
+ */
+
+static void grip_poll(struct gameport *gameport)
+{
+	struct grip *grip = gameport_get_drvdata(gameport);
+	unsigned int data[GRIP_LENGTH_XT];
+	struct input_dev *dev;
+	int i, j;
+
+	for (i = 0; i < 2; i++) {
+
+		dev = grip->dev[i];
+		if (!dev)
+			continue;
+
+		grip->reads++;
+
+		switch (grip->mode[i]) {
+
+			case GRIP_MODE_GPP:
+
+				if (grip_gpp_read_packet(grip->gameport, (i << 1) + 4, data)) {
+					grip->bads++;
+					break;
+				}
+
+				input_report_abs(dev, ABS_X, ((*data >> 15) & 1) - ((*data >> 16) & 1));
+				input_report_abs(dev, ABS_Y, ((*data >> 13) & 1) - ((*data >> 12) & 1));
+
+				for (j = 0; j < 12; j++)
+					if (grip_btn_gpp[j])
+						input_report_key(dev, grip_btn_gpp[j], (*data >> j) & 1);
+
+				break;
+
+			case GRIP_MODE_BD:
+
+				if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) {
+					grip->bads++;
+					break;
+				}
+
+				input_report_abs(dev, ABS_X,        (data[0] >> 2) & 0x3f);
+				input_report_abs(dev, ABS_Y,  63 - ((data[0] >> 8) & 0x3f));
+				input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f);
+
+				input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2]       & 1));
+				input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1));
+
+				for (j = 0; j < 5; j++)
+					input_report_key(dev, grip_btn_bd[j], (data[3] >> (j + 4)) & 1);
+
+				break;
+
+			case GRIP_MODE_XT:
+
+				if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) {
+					grip->bads++;
+					break;
+				}
+
+				input_report_abs(dev, ABS_X,        (data[0] >> 2) & 0x3f);
+				input_report_abs(dev, ABS_Y,  63 - ((data[0] >> 8) & 0x3f));
+				input_report_abs(dev, ABS_BRAKE,    (data[1] >> 2) & 0x3f);
+				input_report_abs(dev, ABS_GAS,	    (data[1] >> 8) & 0x3f);
+				input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f);
+
+				input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2]       & 1));
+				input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1));
+				input_report_abs(dev, ABS_HAT1X, ((data[2] >> 5) & 1) - ((data[2] >> 4) & 1));
+				input_report_abs(dev, ABS_HAT1Y, ((data[2] >> 6) & 1) - ((data[2] >> 7) & 1));
+
+				for (j = 0; j < 11; j++)
+					input_report_key(dev, grip_btn_xt[j], (data[3] >> (j + 3)) & 1);
+				break;
+
+			case GRIP_MODE_DC:
+
+				if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) {
+					grip->bads++;
+					break;
+				}
+
+				input_report_abs(dev, ABS_X,        (data[0] >> 2) & 0x3f);
+				input_report_abs(dev, ABS_Y,        (data[0] >> 8) & 0x3f);
+				input_report_abs(dev, ABS_RX,       (data[1] >> 2) & 0x3f);
+				input_report_abs(dev, ABS_RY,	    (data[1] >> 8) & 0x3f);
+				input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f);
+
+				input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2]       & 1));
+				input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1));
+
+				for (j = 0; j < 9; j++)
+					input_report_key(dev, grip_btn_dc[j], (data[3] >> (j + 3)) & 1);
+				break;
+
+
+		}
+
+		input_sync(dev);
+	}
+}
+
+static int grip_open(struct input_dev *dev)
+{
+	struct grip *grip = input_get_drvdata(dev);
+
+	gameport_start_polling(grip->gameport);
+	return 0;
+}
+
+static void grip_close(struct input_dev *dev)
+{
+	struct grip *grip = input_get_drvdata(dev);
+
+	gameport_stop_polling(grip->gameport);
+}
+
+static int grip_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct grip *grip;
+	struct input_dev *input_dev;
+	unsigned int data[GRIP_LENGTH_XT];
+	int i, j, t;
+	int err;
+
+	if (!(grip = kzalloc(sizeof(struct grip), GFP_KERNEL)))
+		return -ENOMEM;
+
+	grip->gameport = gameport;
+
+	gameport_set_drvdata(gameport, grip);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	for (i = 0; i < 2; i++) {
+		if (!grip_gpp_read_packet(gameport, (i << 1) + 4, data)) {
+			grip->mode[i] = GRIP_MODE_GPP;
+			continue;
+		}
+		if (!grip_xt_read_packet(gameport, (i << 1) + 4, data)) {
+			if (!(data[3] & 7)) {
+				grip->mode[i] = GRIP_MODE_BD;
+				continue;
+			}
+			if (!(data[2] & 0xf0)) {
+				grip->mode[i] = GRIP_MODE_XT;
+				continue;
+			}
+			grip->mode[i] = GRIP_MODE_DC;
+			continue;
+		}
+	}
+
+	if (!grip->mode[0] && !grip->mode[1]) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, grip_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	for (i = 0; i < 2; i++) {
+		if (!grip->mode[i])
+			continue;
+
+		grip->dev[i] = input_dev = input_allocate_device();
+		if (!input_dev) {
+			err = -ENOMEM;
+			goto fail3;
+		}
+
+		snprintf(grip->phys[i], sizeof(grip->phys[i]),
+			 "%s/input%d", gameport->phys, i);
+
+		input_dev->name = grip_name[grip->mode[i]];
+		input_dev->phys = grip->phys[i];
+		input_dev->id.bustype = BUS_GAMEPORT;
+		input_dev->id.vendor = GAMEPORT_ID_VENDOR_GRAVIS;
+		input_dev->id.product = grip->mode[i];
+		input_dev->id.version = 0x0100;
+		input_dev->dev.parent = &gameport->dev;
+
+		input_set_drvdata(input_dev, grip);
+
+		input_dev->open = grip_open;
+		input_dev->close = grip_close;
+
+		input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+		for (j = 0; (t = grip_abs[grip->mode[i]][j]) >= 0; j++) {
+
+			if (j < grip_cen[grip->mode[i]])
+				input_set_abs_params(input_dev, t, 14, 52, 1, 2);
+			else if (j < grip_anx[grip->mode[i]])
+				input_set_abs_params(input_dev, t, 3, 57, 1, 0);
+			else
+				input_set_abs_params(input_dev, t, -1, 1, 0, 0);
+		}
+
+		for (j = 0; (t = grip_btn[grip->mode[i]][j]) >= 0; j++)
+			if (t > 0)
+				set_bit(t, input_dev->keybit);
+
+		err = input_register_device(grip->dev[i]);
+		if (err)
+			goto fail4;
+	}
+
+	return 0;
+
+ fail4:	input_free_device(grip->dev[i]);
+ fail3:	while (--i >= 0)
+		if (grip->dev[i])
+			input_unregister_device(grip->dev[i]);
+ fail2:	gameport_close(gameport);
+ fail1:	gameport_set_drvdata(gameport, NULL);
+	kfree(grip);
+	return err;
+}
+
+static void grip_disconnect(struct gameport *gameport)
+{
+	struct grip *grip = gameport_get_drvdata(gameport);
+	int i;
+
+	for (i = 0; i < 2; i++)
+		if (grip->dev[i])
+			input_unregister_device(grip->dev[i]);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(grip);
+}
+
+static struct gameport_driver grip_drv = {
+	.driver		= {
+		.name	= "grip",
+		.owner	= THIS_MODULE,
+	},
+	.description	= DRIVER_DESC,
+	.connect	= grip_connect,
+	.disconnect	= grip_disconnect,
+};
+
+module_gameport_driver(grip_drv);
diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c
new file mode 100644
index 0000000..573191d
--- /dev/null
+++ b/drivers/input/joystick/grip_mp.c
@@ -0,0 +1,689 @@
+/*
+ *  Driver for the Gravis Grip Multiport, a gamepad "hub" that
+ *  connects up to four 9-pin digital gamepads/joysticks.
+ *  Driver tested on SMP and UP kernel versions 2.4.18-4 and 2.4.18-5.
+ *
+ *  Thanks to Chris Gassib for helpful advice.
+ *
+ *  Copyright (c)      2002 Brian Bonnlander, Bill Soudan
+ *  Copyright (c) 1998-2000 Vojtech Pavlik
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_DESC	"Gravis Grip Multiport driver"
+
+MODULE_AUTHOR("Brian Bonnlander");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#ifdef GRIP_DEBUG
+#define dbg(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+#define GRIP_MAX_PORTS	4
+/*
+ * Grip multiport state
+ */
+
+struct grip_port {
+	struct input_dev *dev;
+	int mode;
+	int registered;
+
+	/* individual gamepad states */
+	int buttons;
+	int xaxes;
+	int yaxes;
+	int dirty;     /* has the state been updated? */
+};
+
+struct grip_mp {
+	struct gameport *gameport;
+	struct grip_port *port[GRIP_MAX_PORTS];
+	int reads;
+	int bads;
+};
+
+/*
+ * Multiport packet interpretation
+ */
+
+#define PACKET_FULL          0x80000000       /* packet is full                        */
+#define PACKET_IO_FAST       0x40000000       /* 3 bits per gameport read              */
+#define PACKET_IO_SLOW       0x20000000       /* 1 bit per gameport read               */
+#define PACKET_MP_MORE       0x04000000       /* multiport wants to send more          */
+#define PACKET_MP_DONE       0x02000000       /* multiport done sending                */
+
+/*
+ * Packet status code interpretation
+ */
+
+#define IO_GOT_PACKET        0x0100           /* Got a packet                           */
+#define IO_MODE_FAST         0x0200           /* Used 3 data bits per gameport read     */
+#define IO_SLOT_CHANGE       0x0800           /* Multiport physical slot status changed */
+#define IO_DONE              0x1000           /* Multiport is done sending packets      */
+#define IO_RETRY             0x4000           /* Try again later to get packet          */
+#define IO_RESET             0x8000           /* Force multiport to resend all packets  */
+
+/*
+ * Gamepad configuration data.  Other 9-pin digital joystick devices
+ * may work with the multiport, so this may not be an exhaustive list!
+ * Commodore 64 joystick remains untested.
+ */
+
+#define GRIP_INIT_DELAY         2000          /*  2 ms */
+
+#define GRIP_MODE_NONE		0
+#define GRIP_MODE_RESET         1
+#define GRIP_MODE_GP		2
+#define GRIP_MODE_C64		3
+
+static const int grip_btn_gp[]  = { BTN_TR, BTN_TL, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, -1 };
+static const int grip_btn_c64[] = { BTN_JOYSTICK, -1 };
+
+static const int grip_abs_gp[]  = { ABS_X, ABS_Y, -1 };
+static const int grip_abs_c64[] = { ABS_X, ABS_Y, -1 };
+
+static const int *grip_abs[] = { NULL, NULL, grip_abs_gp, grip_abs_c64 };
+static const int *grip_btn[] = { NULL, NULL, grip_btn_gp, grip_btn_c64 };
+
+static const char *grip_name[] = { NULL, NULL, "Gravis Grip Pad", "Commodore 64 Joystick" };
+
+static const int init_seq[] = {
+	1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+	1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1,
+	1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+	0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1 };
+
+/* Maps multiport directional values to X,Y axis values (each axis encoded in 3 bits) */
+
+static const int axis_map[] = { 5, 9, 1, 5, 6, 10, 2, 6, 4, 8, 0, 4, 5, 9, 1, 5 };
+
+static int register_slot(int i, struct grip_mp *grip);
+
+/*
+ * Returns whether an odd or even number of bits are on in pkt.
+ */
+
+static int bit_parity(u32 pkt)
+{
+	int x = pkt ^ (pkt >> 16);
+	x ^= x >> 8;
+	x ^= x >> 4;
+	x ^= x >> 2;
+	x ^= x >> 1;
+	return x & 1;
+}
+
+/*
+ * Poll gameport; return true if all bits set in 'onbits' are on and
+ * all bits set in 'offbits' are off.
+ */
+
+static inline int poll_until(u8 onbits, u8 offbits, int u_sec, struct gameport* gp, u8 *data)
+{
+	int i, nloops;
+
+	nloops = gameport_time(gp, u_sec);
+	for (i = 0; i < nloops; i++) {
+		*data = gameport_read(gp);
+		if ((*data & onbits) == onbits &&
+		    (~(*data) & offbits) == offbits)
+			return 1;
+	}
+	dbg("gameport timed out after %d microseconds.\n", u_sec);
+	return 0;
+}
+
+/*
+ * Gets a 28-bit packet from the multiport.
+ *
+ * After getting a packet successfully, commands encoded by sendcode may
+ * be sent to the multiport.
+ *
+ * The multiport clock value is reflected in gameport bit B4.
+ *
+ * Returns a packet status code indicating whether packet is valid, the transfer
+ * mode, and any error conditions.
+ *
+ * sendflags:      current I/O status
+ * sendcode:   data to send to the multiport if sendflags is nonzero
+ */
+
+static int mp_io(struct gameport* gameport, int sendflags, int sendcode, u32 *packet)
+{
+	u8  raw_data;            /* raw data from gameport */
+	u8  data_mask;           /* packet data bits from raw_data */
+	u32 pkt;                 /* packet temporary storage */
+	int bits_per_read;       /* num packet bits per gameport read */
+	int portvals = 0;        /* used for port value sanity check */
+	int i;
+
+	/* Gameport bits B0, B4, B5 should first be off, then B4 should come on. */
+
+	*packet = 0;
+	raw_data = gameport_read(gameport);
+	if (raw_data & 1)
+		return IO_RETRY;
+
+	for (i = 0; i < 64; i++) {
+		raw_data = gameport_read(gameport);
+		portvals |= 1 << ((raw_data >> 4) & 3); /* Demux B4, B5 */
+	}
+
+	if (portvals == 1) {                            /* B4, B5 off */
+		raw_data = gameport_read(gameport);
+		portvals = raw_data & 0xf0;
+
+		if (raw_data & 0x31)
+			return IO_RESET;
+		gameport_trigger(gameport);
+
+		if (!poll_until(0x10, 0, 308, gameport, &raw_data))
+			return IO_RESET;
+	} else
+		return IO_RETRY;
+
+	/* Determine packet transfer mode and prepare for packet construction. */
+
+	if (raw_data & 0x20) {                 /* 3 data bits/read */
+		portvals |= raw_data >> 4;     /* Compare B4-B7 before & after trigger */
+
+		if (portvals != 0xb)
+			return 0;
+		data_mask = 7;
+		bits_per_read = 3;
+		pkt = (PACKET_FULL | PACKET_IO_FAST) >> 28;
+	} else {                                 /* 1 data bit/read */
+		data_mask = 1;
+		bits_per_read = 1;
+		pkt = (PACKET_FULL | PACKET_IO_SLOW) >> 28;
+	}
+
+	/* Construct a packet.  Final data bits must be zero. */
+
+	while (1) {
+		if (!poll_until(0, 0x10, 77, gameport, &raw_data))
+			return IO_RESET;
+		raw_data = (raw_data >> 5) & data_mask;
+
+		if (pkt & PACKET_FULL)
+			break;
+		pkt = (pkt << bits_per_read) | raw_data;
+
+		if (!poll_until(0x10, 0, 77, gameport, &raw_data))
+			return IO_RESET;
+	}
+
+	if (raw_data)
+		return IO_RESET;
+
+	/* If 3 bits/read used, drop from 30 bits to 28. */
+
+	if (bits_per_read == 3) {
+		pkt = (pkt & 0xffff0000) | ((pkt << 1) & 0xffff);
+		pkt = (pkt >> 2) | 0xf0000000;
+	}
+
+	if (bit_parity(pkt) == 1)
+		return IO_RESET;
+
+	/* Acknowledge packet receipt */
+
+	if (!poll_until(0x30, 0, 77, gameport, &raw_data))
+		return IO_RESET;
+
+	raw_data = gameport_read(gameport);
+
+	if (raw_data & 1)
+		return IO_RESET;
+
+	gameport_trigger(gameport);
+
+	if (!poll_until(0, 0x20, 77, gameport, &raw_data))
+		return IO_RESET;
+
+        /* Return if we just wanted the packet or multiport wants to send more */
+
+	*packet = pkt;
+	if ((sendflags == 0) || ((sendflags & IO_RETRY) && !(pkt & PACKET_MP_DONE)))
+		return IO_GOT_PACKET;
+
+	if (pkt & PACKET_MP_MORE)
+		return IO_GOT_PACKET | IO_RETRY;
+
+	/* Multiport is done sending packets and is ready to receive data */
+
+	if (!poll_until(0x20, 0, 77, gameport, &raw_data))
+		return IO_GOT_PACKET | IO_RESET;
+
+	raw_data = gameport_read(gameport);
+	if (raw_data & 1)
+		return IO_GOT_PACKET | IO_RESET;
+
+	/* Trigger gameport based on bits in sendcode */
+
+	gameport_trigger(gameport);
+	do {
+		if (!poll_until(0x20, 0x10, 116, gameport, &raw_data))
+			return IO_GOT_PACKET | IO_RESET;
+
+		if (!poll_until(0x30, 0, 193, gameport, &raw_data))
+			return IO_GOT_PACKET | IO_RESET;
+
+		if (raw_data & 1)
+			return IO_GOT_PACKET | IO_RESET;
+
+		if (sendcode & 1)
+			gameport_trigger(gameport);
+
+		sendcode >>= 1;
+	} while (sendcode);
+
+	return IO_GOT_PACKET | IO_MODE_FAST;
+}
+
+/*
+ * Disables and restores interrupts for mp_io(), which does the actual I/O.
+ */
+
+static int multiport_io(struct gameport* gameport, int sendflags, int sendcode, u32 *packet)
+{
+	int status;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	status = mp_io(gameport, sendflags, sendcode, packet);
+	local_irq_restore(flags);
+
+	return status;
+}
+
+/*
+ * Puts multiport into digital mode.  Multiport LED turns green.
+ *
+ * Returns true if a valid digital packet was received, false otherwise.
+ */
+
+static int dig_mode_start(struct gameport *gameport, u32 *packet)
+{
+	int i;
+	int flags, tries = 0, bads = 0;
+
+	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {     /* Send magic sequence */
+		if (init_seq[i])
+			gameport_trigger(gameport);
+		udelay(GRIP_INIT_DELAY);
+	}
+
+	for (i = 0; i < 16; i++)            /* Wait for multiport to settle */
+		udelay(GRIP_INIT_DELAY);
+
+	while (tries < 64 && bads < 8) {    /* Reset multiport and try getting a packet */
+
+		flags = multiport_io(gameport, IO_RESET, 0x27, packet);
+
+		if (flags & IO_MODE_FAST)
+			return 1;
+
+		if (flags & IO_RETRY)
+			tries++;
+		else
+			bads++;
+	}
+	return 0;
+}
+
+/*
+ * Packet structure: B0-B15   => gamepad state
+ *                   B16-B20  => gamepad device type
+ *                   B21-B24  => multiport slot index (1-4)
+ *
+ * Known device types: 0x1f (grip pad), 0x0 (no device).  Others may exist.
+ *
+ * Returns the packet status.
+ */
+
+static int get_and_decode_packet(struct grip_mp *grip, int flags)
+{
+	struct grip_port *port;
+	u32 packet;
+	int joytype = 0;
+	int slot;
+
+	/* Get a packet and check for validity */
+
+	flags &= IO_RESET | IO_RETRY;
+	flags = multiport_io(grip->gameport, flags, 0, &packet);
+	grip->reads++;
+
+	if (packet & PACKET_MP_DONE)
+		flags |= IO_DONE;
+
+	if (flags && !(flags & IO_GOT_PACKET)) {
+		grip->bads++;
+		return flags;
+	}
+
+	/* Ignore non-gamepad packets, e.g. multiport hardware version */
+
+	slot = ((packet >> 21) & 0xf) - 1;
+	if ((slot < 0) || (slot > 3))
+		return flags;
+
+	port = grip->port[slot];
+
+	/*
+	 * Handle "reset" packets, which occur at startup, and when gamepads
+	 * are removed or plugged in.  May contain configuration of a new gamepad.
+	 */
+
+	joytype = (packet >> 16) & 0x1f;
+	if (!joytype) {
+
+		if (port->registered) {
+			printk(KERN_INFO "grip_mp: removing %s, slot %d\n",
+			       grip_name[port->mode], slot);
+			input_unregister_device(port->dev);
+			port->registered = 0;
+		}
+		dbg("Reset: grip multiport slot %d\n", slot);
+		port->mode = GRIP_MODE_RESET;
+		flags |= IO_SLOT_CHANGE;
+		return flags;
+	}
+
+	/* Interpret a grip pad packet */
+
+	if (joytype == 0x1f) {
+
+		int dir = (packet >> 8) & 0xf;          /* eight way directional value */
+		port->buttons = (~packet) & 0xff;
+		port->yaxes = ((axis_map[dir] >> 2) & 3) - 1;
+		port->xaxes = (axis_map[dir] & 3) - 1;
+		port->dirty = 1;
+
+		if (port->mode == GRIP_MODE_RESET)
+			flags |= IO_SLOT_CHANGE;
+
+		port->mode = GRIP_MODE_GP;
+
+		if (!port->registered) {
+			dbg("New Grip pad in multiport slot %d.\n", slot);
+			if (register_slot(slot, grip)) {
+				port->mode = GRIP_MODE_RESET;
+				port->dirty = 0;
+			}
+		}
+		return flags;
+	}
+
+	/* Handle non-grip device codes.  For now, just print diagnostics. */
+
+	{
+		static int strange_code = 0;
+		if (strange_code != joytype) {
+			printk(KERN_INFO "Possible non-grip pad/joystick detected.\n");
+			printk(KERN_INFO "Got joy type 0x%x and packet 0x%x.\n", joytype, packet);
+			strange_code = joytype;
+		}
+	}
+	return flags;
+}
+
+/*
+ * Returns true if all multiport slot states appear valid.
+ */
+
+static int slots_valid(struct grip_mp *grip)
+{
+	int flags, slot, invalid = 0, active = 0;
+
+	flags = get_and_decode_packet(grip, 0);
+	if (!(flags & IO_GOT_PACKET))
+		return 0;
+
+	for (slot = 0; slot < 4; slot++) {
+		if (grip->port[slot]->mode == GRIP_MODE_RESET)
+			invalid = 1;
+		if (grip->port[slot]->mode != GRIP_MODE_NONE)
+			active = 1;
+	}
+
+	/* Return true if no active slot but multiport sent all its data */
+	if (!active)
+		return (flags & IO_DONE) ? 1 : 0;
+
+	/* Return false if invalid device code received */
+	return invalid ? 0 : 1;
+}
+
+/*
+ * Returns whether the multiport was placed into digital mode and
+ * able to communicate its state successfully.
+ */
+
+static int multiport_init(struct grip_mp *grip)
+{
+	int dig_mode, initialized = 0, tries = 0;
+	u32 packet;
+
+	dig_mode = dig_mode_start(grip->gameport, &packet);
+	while (!dig_mode && tries < 4) {
+		dig_mode = dig_mode_start(grip->gameport, &packet);
+		tries++;
+	}
+
+	if (dig_mode)
+		dbg("multiport_init(): digital mode activated.\n");
+	else {
+		dbg("multiport_init(): unable to activate digital mode.\n");
+		return 0;
+	}
+
+	/* Get packets, store multiport state, and check state's validity */
+	for (tries = 0; tries < 4096; tries++) {
+		if (slots_valid(grip)) {
+			initialized = 1;
+			break;
+		}
+	}
+	dbg("multiport_init(): initialized == %d\n", initialized);
+	return initialized;
+}
+
+/*
+ * Reports joystick state to the linux input layer.
+ */
+
+static void report_slot(struct grip_mp *grip, int slot)
+{
+	struct grip_port *port = grip->port[slot];
+	int i;
+
+	/* Store button states with linux input driver */
+
+	for (i = 0; i < 8; i++)
+		input_report_key(port->dev, grip_btn_gp[i], (port->buttons >> i) & 1);
+
+	/* Store axis states with linux driver */
+
+	input_report_abs(port->dev, ABS_X, port->xaxes);
+	input_report_abs(port->dev, ABS_Y, port->yaxes);
+
+	/* Tell the receiver of the events to process them */
+
+	input_sync(port->dev);
+
+	port->dirty = 0;
+}
+
+/*
+ * Get the multiport state.
+ */
+
+static void grip_poll(struct gameport *gameport)
+{
+	struct grip_mp *grip = gameport_get_drvdata(gameport);
+	int i, npkts, flags;
+
+	for (npkts = 0; npkts < 4; npkts++) {
+		flags = IO_RETRY;
+		for (i = 0; i < 32; i++) {
+			flags = get_and_decode_packet(grip, flags);
+			if ((flags & IO_GOT_PACKET) || !(flags & IO_RETRY))
+				break;
+		}
+		if (flags & IO_DONE)
+			break;
+	}
+
+	for (i = 0; i < 4; i++)
+		if (grip->port[i]->dirty)
+			report_slot(grip, i);
+}
+
+/*
+ * Called when a joystick device file is opened
+ */
+
+static int grip_open(struct input_dev *dev)
+{
+	struct grip_mp *grip = input_get_drvdata(dev);
+
+	gameport_start_polling(grip->gameport);
+	return 0;
+}
+
+/*
+ * Called when a joystick device file is closed
+ */
+
+static void grip_close(struct input_dev *dev)
+{
+	struct grip_mp *grip = input_get_drvdata(dev);
+
+	gameport_stop_polling(grip->gameport);
+}
+
+/*
+ * Tell the linux input layer about a newly plugged-in gamepad.
+ */
+
+static int register_slot(int slot, struct grip_mp *grip)
+{
+	struct grip_port *port = grip->port[slot];
+	struct input_dev *input_dev;
+	int j, t;
+	int err;
+
+	port->dev = input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	input_dev->name = grip_name[port->mode];
+	input_dev->id.bustype = BUS_GAMEPORT;
+	input_dev->id.vendor = GAMEPORT_ID_VENDOR_GRAVIS;
+	input_dev->id.product = 0x0100 + port->mode;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &grip->gameport->dev;
+
+	input_set_drvdata(input_dev, grip);
+
+	input_dev->open = grip_open;
+	input_dev->close = grip_close;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	for (j = 0; (t = grip_abs[port->mode][j]) >= 0; j++)
+		input_set_abs_params(input_dev, t, -1, 1, 0, 0);
+
+	for (j = 0; (t = grip_btn[port->mode][j]) >= 0; j++)
+		if (t > 0)
+			set_bit(t, input_dev->keybit);
+
+	err = input_register_device(port->dev);
+	if (err) {
+		input_free_device(port->dev);
+		return err;
+	}
+
+	port->registered = 1;
+
+	if (port->dirty)	            /* report initial state, if any */
+		report_slot(grip, slot);
+
+	return 0;
+}
+
+static int grip_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct grip_mp *grip;
+	int err;
+
+	if (!(grip = kzalloc(sizeof(struct grip_mp), GFP_KERNEL)))
+		return -ENOMEM;
+
+	grip->gameport = gameport;
+
+	gameport_set_drvdata(gameport, grip);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	gameport_set_poll_handler(gameport, grip_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	if (!multiport_init(grip)) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	if (!grip->port[0]->mode && !grip->port[1]->mode && !grip->port[2]->mode && !grip->port[3]->mode) {
+		/* nothing plugged in */
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	return 0;
+
+fail2:	gameport_close(gameport);
+fail1:	gameport_set_drvdata(gameport, NULL);
+	kfree(grip);
+	return err;
+}
+
+static void grip_disconnect(struct gameport *gameport)
+{
+	struct grip_mp *grip = gameport_get_drvdata(gameport);
+	int i;
+
+	for (i = 0; i < 4; i++)
+		if (grip->port[i]->registered)
+			input_unregister_device(grip->port[i]->dev);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(grip);
+}
+
+static struct gameport_driver grip_drv = {
+	.driver		= {
+		.name	= "grip_mp",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= grip_connect,
+	.disconnect	= grip_disconnect,
+};
+
+module_gameport_driver(grip_drv);
diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c
new file mode 100644
index 0000000..a9ac2f9
--- /dev/null
+++ b/drivers/input/joystick/guillemot.c
@@ -0,0 +1,283 @@
+/*
+ *  Copyright (c) 2001 Vojtech Pavlik
+ */
+
+/*
+ * Guillemot Digital Interface Protocol driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_DESC	"Guillemot Digital joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define GUILLEMOT_MAX_START	600	/* 600 us */
+#define GUILLEMOT_MAX_STROBE	60	/* 60 us */
+#define GUILLEMOT_MAX_LENGTH	17	/* 17 bytes */
+
+static short guillemot_abs_pad[] =
+	{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, -1 };
+
+static short guillemot_btn_pad[] =
+	{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_MODE, BTN_SELECT, -1 };
+
+static struct {
+        int x;
+        int y;
+} guillemot_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+struct guillemot_type {
+	unsigned char id;
+	short *abs;
+	short *btn;
+	int hat;
+	char *name;
+};
+
+struct guillemot {
+	struct gameport *gameport;
+	struct input_dev *dev;
+	int bads;
+	int reads;
+	struct guillemot_type *type;
+	unsigned char length;
+	char phys[32];
+};
+
+static struct guillemot_type guillemot_type[] = {
+	{ 0x00, guillemot_abs_pad, guillemot_btn_pad, 1, "Guillemot Pad" },
+	{ 0 }};
+
+/*
+ * guillemot_read_packet() reads Guillemot joystick data.
+ */
+
+static int guillemot_read_packet(struct gameport *gameport, u8 *data)
+{
+	unsigned long flags;
+	unsigned char u, v;
+	unsigned int t, s;
+	int i;
+
+	for (i = 0; i < GUILLEMOT_MAX_LENGTH; i++)
+		data[i] = 0;
+
+	i = 0;
+	t = gameport_time(gameport, GUILLEMOT_MAX_START);
+	s = gameport_time(gameport, GUILLEMOT_MAX_STROBE);
+
+	local_irq_save(flags);
+	gameport_trigger(gameport);
+	v = gameport_read(gameport);
+
+	while (t > 0 && i < GUILLEMOT_MAX_LENGTH * 8) {
+		t--;
+		u = v; v = gameport_read(gameport);
+		if (v & ~u & 0x10) {
+			data[i >> 3] |= ((v >> 5) & 1) << (i & 7);
+			i++;
+			t = s;
+		}
+	}
+
+	local_irq_restore(flags);
+
+	return i;
+}
+
+/*
+ * guillemot_poll() reads and analyzes Guillemot joystick data.
+ */
+
+static void guillemot_poll(struct gameport *gameport)
+{
+	struct guillemot *guillemot = gameport_get_drvdata(gameport);
+	struct input_dev *dev = guillemot->dev;
+	u8 data[GUILLEMOT_MAX_LENGTH];
+	int i;
+
+	guillemot->reads++;
+
+	if (guillemot_read_packet(guillemot->gameport, data) != GUILLEMOT_MAX_LENGTH * 8 ||
+		data[0] != 0x55 || data[16] != 0xaa) {
+		guillemot->bads++;
+	} else {
+
+		for (i = 0; i < 6 && guillemot->type->abs[i] >= 0; i++)
+			input_report_abs(dev, guillemot->type->abs[i], data[i + 5]);
+
+		if (guillemot->type->hat) {
+			input_report_abs(dev, ABS_HAT0X, guillemot_hat_to_axis[data[4] >> 4].x);
+			input_report_abs(dev, ABS_HAT0Y, guillemot_hat_to_axis[data[4] >> 4].y);
+		}
+
+		for (i = 0; i < 16 && guillemot->type->btn[i] >= 0; i++)
+			input_report_key(dev, guillemot->type->btn[i], (data[2 + (i >> 3)] >> (i & 7)) & 1);
+	}
+
+	input_sync(dev);
+}
+
+/*
+ * guillemot_open() is a callback from the input open routine.
+ */
+
+static int guillemot_open(struct input_dev *dev)
+{
+	struct guillemot *guillemot = input_get_drvdata(dev);
+
+	gameport_start_polling(guillemot->gameport);
+	return 0;
+}
+
+/*
+ * guillemot_close() is a callback from the input close routine.
+ */
+
+static void guillemot_close(struct input_dev *dev)
+{
+	struct guillemot *guillemot = input_get_drvdata(dev);
+
+	gameport_stop_polling(guillemot->gameport);
+}
+
+/*
+ * guillemot_connect() probes for Guillemot joysticks.
+ */
+
+static int guillemot_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct guillemot *guillemot;
+	struct input_dev *input_dev;
+	u8 data[GUILLEMOT_MAX_LENGTH];
+	int i, t;
+	int err;
+
+	guillemot = kzalloc(sizeof(struct guillemot), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!guillemot || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	guillemot->gameport = gameport;
+	guillemot->dev = input_dev;
+
+	gameport_set_drvdata(gameport, guillemot);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	i = guillemot_read_packet(gameport, data);
+
+	if (i != GUILLEMOT_MAX_LENGTH * 8 || data[0] != 0x55 || data[16] != 0xaa) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	for (i = 0; guillemot_type[i].name; i++)
+		if (guillemot_type[i].id == data[11])
+			break;
+
+	if (!guillemot_type[i].name) {
+		printk(KERN_WARNING "guillemot.c: Unknown joystick on %s. [ %02x%02x:%04x, ver %d.%02d ]\n",
+			gameport->phys, data[12], data[13], data[11], data[14], data[15]);
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, guillemot_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	snprintf(guillemot->phys, sizeof(guillemot->phys), "%s/input0", gameport->phys);
+	guillemot->type = guillemot_type + i;
+
+	input_dev->name = guillemot_type[i].name;
+	input_dev->phys = guillemot->phys;
+	input_dev->id.bustype = BUS_GAMEPORT;
+	input_dev->id.vendor = GAMEPORT_ID_VENDOR_GUILLEMOT;
+	input_dev->id.product = guillemot_type[i].id;
+	input_dev->id.version = (int)data[14] << 8 | data[15];
+	input_dev->dev.parent = &gameport->dev;
+
+	input_set_drvdata(input_dev, guillemot);
+
+	input_dev->open = guillemot_open;
+	input_dev->close = guillemot_close;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	for (i = 0; (t = guillemot->type->abs[i]) >= 0; i++)
+		input_set_abs_params(input_dev, t, 0, 255, 0, 0);
+
+	if (guillemot->type->hat) {
+		input_set_abs_params(input_dev, ABS_HAT0X, -1, 1, 0, 0);
+		input_set_abs_params(input_dev, ABS_HAT0Y, -1, 1, 0, 0);
+	}
+
+	for (i = 0; (t = guillemot->type->btn[i]) >= 0; i++)
+		set_bit(t, input_dev->keybit);
+
+	err = input_register_device(guillemot->dev);
+	if (err)
+		goto fail2;
+
+	return 0;
+
+fail2:	gameport_close(gameport);
+fail1:  gameport_set_drvdata(gameport, NULL);
+	input_free_device(input_dev);
+	kfree(guillemot);
+	return err;
+}
+
+static void guillemot_disconnect(struct gameport *gameport)
+{
+	struct guillemot *guillemot = gameport_get_drvdata(gameport);
+
+	printk(KERN_INFO "guillemot.c: Failed %d reads out of %d on %s\n", guillemot->reads, guillemot->bads, guillemot->phys);
+	input_unregister_device(guillemot->dev);
+	gameport_close(gameport);
+	kfree(guillemot);
+}
+
+static struct gameport_driver guillemot_drv = {
+	.driver		= {
+		.name	= "guillemot",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= guillemot_connect,
+	.disconnect	= guillemot_disconnect,
+};
+
+module_gameport_driver(guillemot_drv);
diff --git a/drivers/input/joystick/iforce/Kconfig b/drivers/input/joystick/iforce/Kconfig
new file mode 100644
index 0000000..8fde22a
--- /dev/null
+++ b/drivers/input/joystick/iforce/Kconfig
@@ -0,0 +1,32 @@
+#
+# I-Force driver configuration
+#
+config JOYSTICK_IFORCE
+	tristate "I-Force devices"
+	depends on INPUT && INPUT_JOYSTICK
+	help
+	  Say Y here if you have an I-Force joystick or steering wheel
+
+	  You also must choose at least one of the two options below.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called iforce.
+
+config JOYSTICK_IFORCE_USB
+	bool "I-Force USB joysticks and wheels"
+	depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || USB=y) && USB
+	help
+	  Say Y here if you have an I-Force joystick or steering wheel
+	  connected to your USB port.
+
+config JOYSTICK_IFORCE_232
+	bool "I-Force Serial joysticks and wheels"
+	depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || SERIO=y) && SERIO
+	help
+	  Say Y here if you have an I-Force joystick or steering wheel
+	  connected to your serial (COM) port.
+
+	  You will need an additional utility called inputattach, see
+	  <file:Documentation/input/joystick.txt>
+	  and <file:Documentation/input/ff.txt>.
+
diff --git a/drivers/input/joystick/iforce/Makefile b/drivers/input/joystick/iforce/Makefile
new file mode 100644
index 0000000..bc5bda2
--- /dev/null
+++ b/drivers/input/joystick/iforce/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the I-Force driver
+#
+# By Johann Deneux <johann.deneux@gmail.com>
+#
+
+obj-$(CONFIG_JOYSTICK_IFORCE)	+= iforce.o
+
+iforce-y := iforce-ff.o iforce-main.o iforce-packets.o
+iforce-$(CONFIG_JOYSTICK_IFORCE_232)	+= iforce-serio.o
+iforce-$(CONFIG_JOYSTICK_IFORCE_USB)	+= iforce-usb.o
diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c
new file mode 100644
index 0000000..0de9a09
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-ff.c
@@ -0,0 +1,543 @@
+/*
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+/*
+ * Set the magnitude of a constant force effect
+ * Return error code
+ *
+ * Note: caller must ensure exclusive access to device
+ */
+
+static int make_magnitude_modifier(struct iforce* iforce,
+	struct resource* mod_chunk, int no_alloc, __s16 level)
+{
+	unsigned char data[3];
+
+	if (!no_alloc) {
+		mutex_lock(&iforce->mem_mutex);
+		if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,
+			iforce->device_memory.start, iforce->device_memory.end, 2L,
+			NULL, NULL)) {
+			mutex_unlock(&iforce->mem_mutex);
+			return -ENOSPC;
+		}
+		mutex_unlock(&iforce->mem_mutex);
+	}
+
+	data[0] = LO(mod_chunk->start);
+	data[1] = HI(mod_chunk->start);
+	data[2] = HIFIX80(level);
+
+	iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data);
+
+	iforce_dump_packet("magnitude: ", FF_CMD_MAGNITUDE, data);
+	return 0;
+}
+
+/*
+ * Upload the component of an effect dealing with the period, phase and magnitude
+ */
+
+static int make_period_modifier(struct iforce* iforce,
+	struct resource* mod_chunk, int no_alloc,
+	__s16 magnitude, __s16 offset, u16 period, u16 phase)
+{
+	unsigned char data[7];
+
+	period = TIME_SCALE(period);
+
+	if (!no_alloc) {
+		mutex_lock(&iforce->mem_mutex);
+		if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,
+			iforce->device_memory.start, iforce->device_memory.end, 2L,
+			NULL, NULL)) {
+			mutex_unlock(&iforce->mem_mutex);
+			return -ENOSPC;
+		}
+		mutex_unlock(&iforce->mem_mutex);
+	}
+
+	data[0] = LO(mod_chunk->start);
+	data[1] = HI(mod_chunk->start);
+
+	data[2] = HIFIX80(magnitude);
+	data[3] = HIFIX80(offset);
+	data[4] = HI(phase);
+
+	data[5] = LO(period);
+	data[6] = HI(period);
+
+	iforce_send_packet(iforce, FF_CMD_PERIOD, data);
+
+	return 0;
+}
+
+/*
+ * Uploads the part of an effect setting the envelope of the force
+ */
+
+static int make_envelope_modifier(struct iforce* iforce,
+	struct resource* mod_chunk, int no_alloc,
+	u16 attack_duration, __s16 initial_level,
+	u16 fade_duration, __s16 final_level)
+{
+	unsigned char data[8];
+
+	attack_duration = TIME_SCALE(attack_duration);
+	fade_duration = TIME_SCALE(fade_duration);
+
+	if (!no_alloc) {
+		mutex_lock(&iforce->mem_mutex);
+		if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,
+			iforce->device_memory.start, iforce->device_memory.end, 2L,
+			NULL, NULL)) {
+			mutex_unlock(&iforce->mem_mutex);
+			return -ENOSPC;
+		}
+		mutex_unlock(&iforce->mem_mutex);
+	}
+
+	data[0] = LO(mod_chunk->start);
+	data[1] = HI(mod_chunk->start);
+
+	data[2] = LO(attack_duration);
+	data[3] = HI(attack_duration);
+	data[4] = HI(initial_level);
+
+	data[5] = LO(fade_duration);
+	data[6] = HI(fade_duration);
+	data[7] = HI(final_level);
+
+	iforce_send_packet(iforce, FF_CMD_ENVELOPE, data);
+
+	return 0;
+}
+
+/*
+ * Component of spring, friction, inertia... effects
+ */
+
+static int make_condition_modifier(struct iforce* iforce,
+	struct resource* mod_chunk, int no_alloc,
+	__u16 rsat, __u16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center)
+{
+	unsigned char data[10];
+
+	if (!no_alloc) {
+		mutex_lock(&iforce->mem_mutex);
+		if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,
+			iforce->device_memory.start, iforce->device_memory.end, 2L,
+			NULL, NULL)) {
+			mutex_unlock(&iforce->mem_mutex);
+			return -ENOSPC;
+		}
+		mutex_unlock(&iforce->mem_mutex);
+	}
+
+	data[0] = LO(mod_chunk->start);
+	data[1] = HI(mod_chunk->start);
+
+	data[2] = (100 * rk) >> 15;	/* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */
+	data[3] = (100 * lk) >> 15; /* This code is incorrect on cpus lacking arith shift */
+
+	center = (500 * center) >> 15;
+	data[4] = LO(center);
+	data[5] = HI(center);
+
+	db = (1000 * db) >> 16;
+	data[6] = LO(db);
+	data[7] = HI(db);
+
+	data[8] = (100 * rsat) >> 16;
+	data[9] = (100 * lsat) >> 16;
+
+	iforce_send_packet(iforce, FF_CMD_CONDITION, data);
+	iforce_dump_packet("condition", FF_CMD_CONDITION, data);
+
+	return 0;
+}
+
+static unsigned char find_button(struct iforce *iforce, signed short button)
+{
+	int i;
+
+	for (i = 1; iforce->type->btn[i] >= 0; i++)
+		if (iforce->type->btn[i] == button)
+			return i + 1;
+	return 0;
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send an condition
+ * parameter packet
+ */
+static int need_condition_modifier(struct iforce *iforce,
+				   struct ff_effect *old,
+				   struct ff_effect *new)
+{
+	int ret = 0;
+	int i;
+
+	if (new->type != FF_SPRING && new->type != FF_FRICTION) {
+		dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
+			 __func__);
+		return 0;
+	}
+
+	for (i = 0; i < 2; i++) {
+		ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation
+			|| old->u.condition[i].left_saturation != new->u.condition[i].left_saturation
+			|| old->u.condition[i].right_coeff != new->u.condition[i].right_coeff
+			|| old->u.condition[i].left_coeff != new->u.condition[i].left_coeff
+			|| old->u.condition[i].deadband != new->u.condition[i].deadband
+			|| old->u.condition[i].center != new->u.condition[i].center;
+	}
+	return ret;
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send a magnitude
+ * parameter packet
+ */
+static int need_magnitude_modifier(struct iforce *iforce,
+				   struct ff_effect *old,
+				   struct ff_effect *effect)
+{
+	if (effect->type != FF_CONSTANT) {
+		dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
+			 __func__);
+		return 0;
+	}
+
+	return old->u.constant.level != effect->u.constant.level;
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send an envelope
+ * parameter packet
+ */
+static int need_envelope_modifier(struct iforce *iforce, struct ff_effect *old,
+				  struct ff_effect *effect)
+{
+	switch (effect->type) {
+	case FF_CONSTANT:
+		if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length
+		|| old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level
+		|| old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length
+		|| old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level)
+			return 1;
+		break;
+
+	case FF_PERIODIC:
+		if (old->u.periodic.envelope.attack_length != effect->u.periodic.envelope.attack_length
+		|| old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level
+		|| old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length
+		|| old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level)
+			return 1;
+		break;
+
+	default:
+		dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
+			 __func__);
+	}
+
+	return 0;
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send a periodic
+ * parameter effect
+ */
+static int need_period_modifier(struct iforce *iforce, struct ff_effect *old,
+				struct ff_effect *new)
+{
+	if (new->type != FF_PERIODIC) {
+		dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
+			 __func__);
+		return 0;
+	}
+	return (old->u.periodic.period != new->u.periodic.period
+		|| old->u.periodic.magnitude != new->u.periodic.magnitude
+		|| old->u.periodic.offset != new->u.periodic.offset
+		|| old->u.periodic.phase != new->u.periodic.phase);
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send an effect
+ * packet
+ */
+static int need_core(struct ff_effect *old, struct ff_effect *new)
+{
+	if (old->direction != new->direction
+		|| old->trigger.button != new->trigger.button
+		|| old->trigger.interval != new->trigger.interval
+		|| old->replay.length != new->replay.length
+		|| old->replay.delay != new->replay.delay)
+		return 1;
+
+	return 0;
+}
+/*
+ * Send the part common to all effects to the device
+ */
+static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2,
+	u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button,
+	u16 interval, u16 direction)
+{
+	unsigned char data[14];
+
+	duration = TIME_SCALE(duration);
+	delay    = TIME_SCALE(delay);
+	interval = TIME_SCALE(interval);
+
+	data[0]  = LO(id);
+	data[1]  = effect_type;
+	data[2]  = LO(axes) | find_button(iforce, button);
+
+	data[3]  = LO(duration);
+	data[4]  = HI(duration);
+
+	data[5]  = HI(direction);
+
+	data[6]  = LO(interval);
+	data[7]  = HI(interval);
+
+	data[8]  = LO(mod_id1);
+	data[9]  = HI(mod_id1);
+	data[10] = LO(mod_id2);
+	data[11] = HI(mod_id2);
+
+	data[12] = LO(delay);
+	data[13] = HI(delay);
+
+	/* Stop effect */
+/*	iforce_control_playback(iforce, id, 0);*/
+
+	iforce_send_packet(iforce, FF_CMD_EFFECT, data);
+
+	/* If needed, restart effect */
+	if (test_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[id].flags)) {
+		/* BUG: perhaps we should replay n times, instead of 1. But we do not know n */
+		iforce_control_playback(iforce, id, 1);
+	}
+
+	return 0;
+}
+
+/*
+ * Upload a periodic effect to the device
+ * See also iforce_upload_constant.
+ */
+int iforce_upload_periodic(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
+{
+	u8 wave_code;
+	int core_id = effect->id;
+	struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+	struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
+	struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
+	int param1_err = 1;
+	int param2_err = 1;
+	int core_err = 0;
+
+	if (!old || need_period_modifier(iforce, old, effect)) {
+		param1_err = make_period_modifier(iforce, mod1_chunk,
+			old != NULL,
+			effect->u.periodic.magnitude, effect->u.periodic.offset,
+			effect->u.periodic.period, effect->u.periodic.phase);
+		if (param1_err)
+			return param1_err;
+		set_bit(FF_MOD1_IS_USED, core_effect->flags);
+	}
+
+	if (!old || need_envelope_modifier(iforce, old, effect)) {
+		param2_err = make_envelope_modifier(iforce, mod2_chunk,
+			old !=NULL,
+			effect->u.periodic.envelope.attack_length,
+			effect->u.periodic.envelope.attack_level,
+			effect->u.periodic.envelope.fade_length,
+			effect->u.periodic.envelope.fade_level);
+		if (param2_err)
+			return param2_err;
+		set_bit(FF_MOD2_IS_USED, core_effect->flags);
+	}
+
+	switch (effect->u.periodic.waveform) {
+		case FF_SQUARE:		wave_code = 0x20; break;
+		case FF_TRIANGLE:	wave_code = 0x21; break;
+		case FF_SINE:		wave_code = 0x22; break;
+		case FF_SAW_UP:		wave_code = 0x23; break;
+		case FF_SAW_DOWN:	wave_code = 0x24; break;
+		default:		wave_code = 0x20; break;
+	}
+
+	if (!old || need_core(old, effect)) {
+		core_err = make_core(iforce, effect->id,
+			mod1_chunk->start,
+			mod2_chunk->start,
+			wave_code,
+			0x20,
+			effect->replay.length,
+			effect->replay.delay,
+			effect->trigger.button,
+			effect->trigger.interval,
+			effect->direction);
+	}
+
+	/* If one of the parameter creation failed, we already returned an
+	 * error code.
+	 * If the core creation failed, we return its error code.
+	 * Else: if one parameter at least was created, we return 0
+	 *       else we return 1;
+	 */
+	return core_err < 0 ? core_err : (param1_err && param2_err);
+}
+
+/*
+ * Upload a constant force effect
+ * Return value:
+ *  <0 Error code
+ *  0 Ok, effect created or updated
+ *  1 effect did not change since last upload, and no packet was therefore sent
+ */
+int iforce_upload_constant(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
+{
+	int core_id = effect->id;
+	struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+	struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
+	struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
+	int param1_err = 1;
+	int param2_err = 1;
+	int core_err = 0;
+
+	if (!old || need_magnitude_modifier(iforce, old, effect)) {
+		param1_err = make_magnitude_modifier(iforce, mod1_chunk,
+			old != NULL,
+			effect->u.constant.level);
+		if (param1_err)
+			return param1_err;
+		set_bit(FF_MOD1_IS_USED, core_effect->flags);
+	}
+
+	if (!old || need_envelope_modifier(iforce, old, effect)) {
+		param2_err = make_envelope_modifier(iforce, mod2_chunk,
+			old != NULL,
+			effect->u.constant.envelope.attack_length,
+			effect->u.constant.envelope.attack_level,
+			effect->u.constant.envelope.fade_length,
+			effect->u.constant.envelope.fade_level);
+		if (param2_err)
+			return param2_err;
+		set_bit(FF_MOD2_IS_USED, core_effect->flags);
+	}
+
+	if (!old || need_core(old, effect)) {
+		core_err = make_core(iforce, effect->id,
+			mod1_chunk->start,
+			mod2_chunk->start,
+			0x00,
+			0x20,
+			effect->replay.length,
+			effect->replay.delay,
+			effect->trigger.button,
+			effect->trigger.interval,
+			effect->direction);
+	}
+
+	/* If one of the parameter creation failed, we already returned an
+	 * error code.
+	 * If the core creation failed, we return its error code.
+	 * Else: if one parameter at least was created, we return 0
+	 *       else we return 1;
+	 */
+	return core_err < 0 ? core_err : (param1_err && param2_err);
+}
+
+/*
+ * Upload an condition effect. Those are for example friction, inertia, springs...
+ */
+int iforce_upload_condition(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
+{
+	int core_id = effect->id;
+	struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+	struct resource* mod1_chunk = &(core_effect->mod1_chunk);
+	struct resource* mod2_chunk = &(core_effect->mod2_chunk);
+	u8 type;
+	int param_err = 1;
+	int core_err = 0;
+
+	switch (effect->type) {
+		case FF_SPRING:	type = 0x40; break;
+		case FF_DAMPER:	type = 0x41; break;
+		default: return -1;
+	}
+
+	if (!old || need_condition_modifier(iforce, old, effect)) {
+		param_err = make_condition_modifier(iforce, mod1_chunk,
+			old != NULL,
+			effect->u.condition[0].right_saturation,
+			effect->u.condition[0].left_saturation,
+			effect->u.condition[0].right_coeff,
+			effect->u.condition[0].left_coeff,
+			effect->u.condition[0].deadband,
+			effect->u.condition[0].center);
+		if (param_err)
+			return param_err;
+		set_bit(FF_MOD1_IS_USED, core_effect->flags);
+
+		param_err = make_condition_modifier(iforce, mod2_chunk,
+			old != NULL,
+			effect->u.condition[1].right_saturation,
+			effect->u.condition[1].left_saturation,
+			effect->u.condition[1].right_coeff,
+			effect->u.condition[1].left_coeff,
+			effect->u.condition[1].deadband,
+			effect->u.condition[1].center);
+		if (param_err)
+			return param_err;
+		set_bit(FF_MOD2_IS_USED, core_effect->flags);
+
+	}
+
+	if (!old || need_core(old, effect)) {
+		core_err = make_core(iforce, effect->id,
+			mod1_chunk->start, mod2_chunk->start,
+			type, 0xc0,
+			effect->replay.length, effect->replay.delay,
+			effect->trigger.button, effect->trigger.interval,
+			effect->direction);
+	}
+
+	/* If the parameter creation failed, we already returned an
+	 * error code.
+	 * If the core creation failed, we return its error code.
+	 * Else: if a parameter  was created, we return 0
+	 *       else we return 1;
+	 */
+	return core_err < 0 ? core_err : param_err;
+}
diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c
new file mode 100644
index 0000000..daeeb4c
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-main.c
@@ -0,0 +1,489 @@
+/*
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>");
+MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver");
+MODULE_LICENSE("GPL");
+
+static signed short btn_joystick[] =
+{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+  BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
+
+static signed short btn_avb_pegasus[] =
+{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+  BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 };
+
+static signed short btn_wheel[] =
+{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+  BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
+
+static signed short btn_avb_tw[] =
+{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE,
+  BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 };
+
+static signed short btn_avb_wheel[] =
+{ BTN_GEAR_DOWN, BTN_GEAR_UP, BTN_BASE, BTN_BASE2, BTN_BASE3,
+  BTN_BASE4, BTN_BASE5, BTN_BASE6, -1 };
+
+static signed short abs_joystick[] =
+{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
+
+static signed short abs_joystick_rudder[] =
+{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_HAT0X, ABS_HAT0Y, -1 };
+
+static signed short abs_avb_pegasus[] =
+{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_HAT0X, ABS_HAT0Y,
+  ABS_HAT1X, ABS_HAT1Y, -1 };
+
+static signed short abs_wheel[] =
+{ ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, -1 };
+
+static signed short ff_iforce[] =
+{ FF_PERIODIC, FF_CONSTANT, FF_SPRING, FF_DAMPER,
+  FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, FF_SAW_DOWN, FF_GAIN,
+  FF_AUTOCENTER, -1 };
+
+static struct iforce_device iforce_device[] = {
+	{ 0x044f, 0xa01c, "Thrustmaster Motor Sport GT",		btn_wheel, abs_wheel, ff_iforce },
+	{ 0x046d, 0xc281, "Logitech WingMan Force",			btn_joystick, abs_joystick, ff_iforce },
+	{ 0x046d, 0xc291, "Logitech WingMan Formula Force",		btn_wheel, abs_wheel, ff_iforce },
+	{ 0x05ef, 0x020a, "AVB Top Shot Pegasus",			btn_avb_pegasus, abs_avb_pegasus, ff_iforce },
+	{ 0x05ef, 0x8884, "AVB Mag Turbo Force",			btn_avb_wheel, abs_wheel, ff_iforce },
+	{ 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel",	btn_avb_tw, abs_wheel, ff_iforce }, //?
+	{ 0x061c, 0xc0a4, "ACT LABS Force RS",                          btn_wheel, abs_wheel, ff_iforce }, //?
+	{ 0x061c, 0xc084, "ACT LABS Force RS",				btn_wheel, abs_wheel, ff_iforce },
+	{ 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback",	btn_wheel, abs_wheel, ff_iforce }, //?
+	{ 0x06f8, 0x0001, "Guillemot Jet Leader Force Feedback",	btn_joystick, abs_joystick_rudder, ff_iforce },
+	{ 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel",	btn_wheel, abs_wheel, ff_iforce }, //?
+	{ 0x06f8, 0xa302, "Guillemot Jet Leader 3D",			btn_joystick, abs_joystick, ff_iforce }, //?
+	{ 0x06d6, 0x29bc, "Trust Force Feedback Race Master",		btn_wheel, abs_wheel, ff_iforce },
+	{ 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]",		btn_joystick, abs_joystick, ff_iforce }
+};
+
+static int iforce_playback(struct input_dev *dev, int effect_id, int value)
+{
+	struct iforce *iforce = input_get_drvdata(dev);
+	struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id];
+
+	if (value > 0)
+		set_bit(FF_CORE_SHOULD_PLAY, core_effect->flags);
+	else
+		clear_bit(FF_CORE_SHOULD_PLAY, core_effect->flags);
+
+	iforce_control_playback(iforce, effect_id, value);
+	return 0;
+}
+
+static void iforce_set_gain(struct input_dev *dev, u16 gain)
+{
+	struct iforce *iforce = input_get_drvdata(dev);
+	unsigned char data[3];
+
+	data[0] = gain >> 9;
+	iforce_send_packet(iforce, FF_CMD_GAIN, data);
+}
+
+static void iforce_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+	struct iforce *iforce = input_get_drvdata(dev);
+	unsigned char data[3];
+
+	data[0] = 0x03;
+	data[1] = magnitude >> 9;
+	iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+
+	data[0] = 0x04;
+	data[1] = 0x01;
+	iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+}
+
+/*
+ * Function called when an ioctl is performed on the event dev entry.
+ * It uploads an effect to the device
+ */
+static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
+{
+	struct iforce *iforce = input_get_drvdata(dev);
+	struct iforce_core_effect *core_effect = &iforce->core_effects[effect->id];
+	int ret;
+
+	if (__test_and_set_bit(FF_CORE_IS_USED, core_effect->flags)) {
+		/* Check the effect is not already being updated */
+		if (test_bit(FF_CORE_UPDATE, core_effect->flags))
+			return -EAGAIN;
+	}
+
+/*
+ * Upload the effect
+ */
+	switch (effect->type) {
+
+		case FF_PERIODIC:
+			ret = iforce_upload_periodic(iforce, effect, old);
+			break;
+
+		case FF_CONSTANT:
+			ret = iforce_upload_constant(iforce, effect, old);
+			break;
+
+		case FF_SPRING:
+		case FF_DAMPER:
+			ret = iforce_upload_condition(iforce, effect, old);
+			break;
+
+		default:
+			return -EINVAL;
+	}
+
+	if (ret == 0) {
+		/* A packet was sent, forbid new updates until we are notified
+		 * that the packet was updated
+		 */
+		set_bit(FF_CORE_UPDATE, core_effect->flags);
+	}
+	return ret;
+}
+
+/*
+ * Erases an effect: it frees the effect id and mark as unused the memory
+ * allocated for the parameters
+ */
+static int iforce_erase_effect(struct input_dev *dev, int effect_id)
+{
+	struct iforce *iforce = input_get_drvdata(dev);
+	struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id];
+	int err = 0;
+
+	if (test_bit(FF_MOD1_IS_USED, core_effect->flags))
+		err = release_resource(&core_effect->mod1_chunk);
+
+	if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags))
+		err = release_resource(&core_effect->mod2_chunk);
+
+	/* TODO: remember to change that if more FF_MOD* bits are added */
+	core_effect->flags[0] = 0;
+
+	return err;
+}
+
+static int iforce_open(struct input_dev *dev)
+{
+	struct iforce *iforce = input_get_drvdata(dev);
+
+	switch (iforce->bus) {
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+		case IFORCE_USB:
+			iforce->irq->dev = iforce->usbdev;
+			if (usb_submit_urb(iforce->irq, GFP_KERNEL))
+				return -EIO;
+			break;
+#endif
+	}
+
+	if (test_bit(EV_FF, dev->evbit)) {
+		/* Enable force feedback */
+		iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
+	}
+
+	return 0;
+}
+
+static void iforce_close(struct input_dev *dev)
+{
+	struct iforce *iforce = input_get_drvdata(dev);
+	int i;
+
+	if (test_bit(EV_FF, dev->evbit)) {
+		/* Check: no effects should be present in memory */
+		for (i = 0; i < dev->ff->max_effects; i++) {
+			if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) {
+				dev_warn(&dev->dev,
+					"%s: Device still owns effects\n",
+					__func__);
+				break;
+			}
+		}
+
+		/* Disable force feedback playback */
+		iforce_send_packet(iforce, FF_CMD_ENABLE, "\001");
+		/* Wait for the command to complete */
+		wait_event_interruptible(iforce->wait,
+			!test_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags));
+	}
+
+	switch (iforce->bus) {
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+	case IFORCE_USB:
+		usb_kill_urb(iforce->irq);
+		usb_kill_urb(iforce->out);
+		usb_kill_urb(iforce->ctrl);
+		break;
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+	case IFORCE_232:
+		//TODO: Wait for the last packets to be sent
+		break;
+#endif
+	}
+}
+
+int iforce_init_device(struct iforce *iforce)
+{
+	struct input_dev *input_dev;
+	struct ff_device *ff;
+	unsigned char c[] = "CEOV";
+	int i, error;
+	int ff_effects = 0;
+
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	init_waitqueue_head(&iforce->wait);
+	spin_lock_init(&iforce->xmit_lock);
+	mutex_init(&iforce->mem_mutex);
+	iforce->xmit.buf = iforce->xmit_data;
+	iforce->dev = input_dev;
+
+/*
+ * Input device fields.
+ */
+
+	switch (iforce->bus) {
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+	case IFORCE_USB:
+		input_dev->id.bustype = BUS_USB;
+		input_dev->dev.parent = &iforce->usbdev->dev;
+		break;
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+	case IFORCE_232:
+		input_dev->id.bustype = BUS_RS232;
+		input_dev->dev.parent = &iforce->serio->dev;
+		break;
+#endif
+	}
+
+	input_set_drvdata(input_dev, iforce);
+
+	input_dev->name = "Unknown I-Force device";
+	input_dev->open = iforce_open;
+	input_dev->close = iforce_close;
+
+/*
+ * On-device memory allocation.
+ */
+
+	iforce->device_memory.name = "I-Force device effect memory";
+	iforce->device_memory.start = 0;
+	iforce->device_memory.end = 200;
+	iforce->device_memory.flags = IORESOURCE_MEM;
+	iforce->device_memory.parent = NULL;
+	iforce->device_memory.child = NULL;
+	iforce->device_memory.sibling = NULL;
+
+/*
+ * Wait until device ready - until it sends its first response.
+ */
+
+	for (i = 0; i < 20; i++)
+		if (!iforce_get_id_packet(iforce, "O"))
+			break;
+
+	if (i == 20) { /* 5 seconds */
+		dev_err(&input_dev->dev,
+			"Timeout waiting for response from device.\n");
+		error = -ENODEV;
+		goto fail;
+	}
+
+/*
+ * Get device info.
+ */
+
+	if (!iforce_get_id_packet(iforce, "M"))
+		input_dev->id.vendor = (iforce->edata[2] << 8) | iforce->edata[1];
+	else
+		dev_warn(&iforce->dev->dev, "Device does not respond to id packet M\n");
+
+	if (!iforce_get_id_packet(iforce, "P"))
+		input_dev->id.product = (iforce->edata[2] << 8) | iforce->edata[1];
+	else
+		dev_warn(&iforce->dev->dev, "Device does not respond to id packet P\n");
+
+	if (!iforce_get_id_packet(iforce, "B"))
+		iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1];
+	else
+		dev_warn(&iforce->dev->dev, "Device does not respond to id packet B\n");
+
+	if (!iforce_get_id_packet(iforce, "N"))
+		ff_effects = iforce->edata[1];
+	else
+		dev_warn(&iforce->dev->dev, "Device does not respond to id packet N\n");
+
+	/* Check if the device can store more effects than the driver can really handle */
+	if (ff_effects > IFORCE_EFFECTS_MAX) {
+		dev_warn(&iforce->dev->dev, "Limiting number of effects to %d (device reports %d)\n",
+		       IFORCE_EFFECTS_MAX, ff_effects);
+		ff_effects = IFORCE_EFFECTS_MAX;
+	}
+
+/*
+ * Display additional info.
+ */
+
+	for (i = 0; c[i]; i++)
+		if (!iforce_get_id_packet(iforce, c + i))
+			iforce_dump_packet("info", iforce->ecmd, iforce->edata);
+
+/*
+ * Disable spring, enable force feedback.
+ */
+	iforce_set_autocenter(input_dev, 0);
+
+/*
+ * Find appropriate device entry
+ */
+
+	for (i = 0; iforce_device[i].idvendor; i++)
+		if (iforce_device[i].idvendor == input_dev->id.vendor &&
+		    iforce_device[i].idproduct == input_dev->id.product)
+			break;
+
+	iforce->type = iforce_device + i;
+	input_dev->name = iforce->type->name;
+
+/*
+ * Set input device bitfields and ranges.
+ */
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) |
+		BIT_MASK(EV_FF_STATUS);
+
+	for (i = 0; iforce->type->btn[i] >= 0; i++)
+		set_bit(iforce->type->btn[i], input_dev->keybit);
+	set_bit(BTN_DEAD, input_dev->keybit);
+
+	for (i = 0; iforce->type->abs[i] >= 0; i++) {
+
+		signed short t = iforce->type->abs[i];
+
+		switch (t) {
+
+			case ABS_X:
+			case ABS_Y:
+			case ABS_WHEEL:
+
+				input_set_abs_params(input_dev, t, -1920, 1920, 16, 128);
+				set_bit(t, input_dev->ffbit);
+				break;
+
+			case ABS_THROTTLE:
+			case ABS_GAS:
+			case ABS_BRAKE:
+
+				input_set_abs_params(input_dev, t, 0, 255, 0, 0);
+				break;
+
+			case ABS_RUDDER:
+
+				input_set_abs_params(input_dev, t, -128, 127, 0, 0);
+				break;
+
+			case ABS_HAT0X:
+			case ABS_HAT0Y:
+		        case ABS_HAT1X:
+		        case ABS_HAT1Y:
+
+				input_set_abs_params(input_dev, t, -1, 1, 0, 0);
+				break;
+		}
+	}
+
+	if (ff_effects) {
+
+		for (i = 0; iforce->type->ff[i] >= 0; i++)
+			set_bit(iforce->type->ff[i], input_dev->ffbit);
+
+		error = input_ff_create(input_dev, ff_effects);
+		if (error)
+			goto fail;
+
+		ff = input_dev->ff;
+		ff->upload = iforce_upload_effect;
+		ff->erase = iforce_erase_effect;
+		ff->set_gain = iforce_set_gain;
+		ff->set_autocenter = iforce_set_autocenter;
+		ff->playback = iforce_playback;
+	}
+/*
+ * Register input device.
+ */
+
+	error = input_register_device(iforce->dev);
+	if (error)
+		goto fail;
+
+	return 0;
+
+ fail:	input_free_device(input_dev);
+	return error;
+}
+
+static int __init iforce_init(void)
+{
+	int err = 0;
+
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+	err = usb_register(&iforce_usb_driver);
+	if (err)
+		return err;
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+	err = serio_register_driver(&iforce_serio_drv);
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+	if (err)
+		usb_deregister(&iforce_usb_driver);
+#endif
+#endif
+	return err;
+}
+
+static void __exit iforce_exit(void)
+{
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+	usb_deregister(&iforce_usb_driver);
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+	serio_unregister_driver(&iforce_serio_drv);
+#endif
+}
+
+module_init(iforce_init);
+module_exit(iforce_exit);
diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c
new file mode 100644
index 0000000..08f98f2
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-packets.c
@@ -0,0 +1,309 @@
+/*
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+static struct {
+	__s32 x;
+	__s32 y;
+} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+
+void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data)
+{
+	int i;
+
+	printk(KERN_DEBUG __FILE__ ": %s cmd = %04x, data = ", msg, cmd);
+	for (i = 0; i < LO(cmd); i++)
+		printk("%02x ", data[i]);
+	printk("\n");
+}
+
+/*
+ * Send a packet of bytes to the device
+ */
+int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data)
+{
+	/* Copy data to buffer */
+	int n = LO(cmd);
+	int c;
+	int empty;
+	int head, tail;
+	unsigned long flags;
+
+/*
+ * Update head and tail of xmit buffer
+ */
+	spin_lock_irqsave(&iforce->xmit_lock, flags);
+
+	head = iforce->xmit.head;
+	tail = iforce->xmit.tail;
+
+
+	if (CIRC_SPACE(head, tail, XMIT_SIZE) < n+2) {
+		dev_warn(&iforce->dev->dev,
+			 "not enough space in xmit buffer to send new packet\n");
+		spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+		return -1;
+	}
+
+	empty = head == tail;
+	XMIT_INC(iforce->xmit.head, n+2);
+
+/*
+ * Store packet in xmit buffer
+ */
+	iforce->xmit.buf[head] = HI(cmd);
+	XMIT_INC(head, 1);
+	iforce->xmit.buf[head] = LO(cmd);
+	XMIT_INC(head, 1);
+
+	c = CIRC_SPACE_TO_END(head, tail, XMIT_SIZE);
+	if (n < c) c=n;
+
+	memcpy(&iforce->xmit.buf[head],
+	       data,
+	       c);
+	if (n != c) {
+		memcpy(&iforce->xmit.buf[0],
+		       data + c,
+		       n - c);
+	}
+	XMIT_INC(head, n);
+
+	spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+/*
+ * If necessary, start the transmission
+ */
+	switch (iforce->bus) {
+
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+		case IFORCE_232:
+		if (empty)
+			iforce_serial_xmit(iforce);
+		break;
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+		case IFORCE_USB:
+
+		if (iforce->usbdev && empty &&
+			!test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
+
+			iforce_usb_xmit(iforce);
+		}
+		break;
+#endif
+	}
+	return 0;
+}
+
+/* Start or stop an effect */
+int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value)
+{
+	unsigned char data[3];
+
+	data[0] = LO(id);
+	data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0;
+	data[2] = LO(value);
+	return iforce_send_packet(iforce, FF_CMD_PLAY, data);
+}
+
+/* Mark an effect that was being updated as ready. That means it can be updated
+ * again */
+static int mark_core_as_ready(struct iforce *iforce, unsigned short addr)
+{
+	int i;
+
+	if (!iforce->dev->ff)
+		return 0;
+
+	for (i = 0; i < iforce->dev->ff->max_effects; ++i) {
+		if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
+		    (iforce->core_effects[i].mod1_chunk.start == addr ||
+		     iforce->core_effects[i].mod2_chunk.start == addr)) {
+			clear_bit(FF_CORE_UPDATE, iforce->core_effects[i].flags);
+			return 0;
+		}
+	}
+	dev_warn(&iforce->dev->dev, "unused effect %04x updated !!!\n", addr);
+	return -1;
+}
+
+void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data)
+{
+	struct input_dev *dev = iforce->dev;
+	int i;
+	static int being_used = 0;
+
+	if (being_used)
+		dev_warn(&iforce->dev->dev,
+			 "re-entrant call to iforce_process %d\n", being_used);
+	being_used++;
+
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+	if (HI(iforce->expect_packet) == HI(cmd)) {
+		iforce->expect_packet = 0;
+		iforce->ecmd = cmd;
+		memcpy(iforce->edata, data, IFORCE_MAX_LENGTH);
+	}
+#endif
+	wake_up(&iforce->wait);
+
+	if (!iforce->type) {
+		being_used--;
+		return;
+	}
+
+	switch (HI(cmd)) {
+
+		case 0x01:	/* joystick position data */
+		case 0x03:	/* wheel position data */
+			if (HI(cmd) == 1) {
+				input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0]));
+				input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2]));
+				input_report_abs(dev, ABS_THROTTLE, 255 - data[4]);
+				if (LO(cmd) >= 8 && test_bit(ABS_RUDDER ,dev->absbit))
+					input_report_abs(dev, ABS_RUDDER, (__s8)data[7]);
+			} else {
+				input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0]));
+				input_report_abs(dev, ABS_GAS,   255 - data[2]);
+				input_report_abs(dev, ABS_BRAKE, 255 - data[3]);
+			}
+
+			input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x);
+			input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y);
+
+			for (i = 0; iforce->type->btn[i] >= 0; i++)
+				input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7)));
+
+			/* If there are untouched bits left, interpret them as the second hat */
+			if (i <= 8) {
+				int btns = data[6];
+				if (test_bit(ABS_HAT1X, dev->absbit)) {
+					if (btns & 8) input_report_abs(dev, ABS_HAT1X, -1);
+					else if (btns & 2) input_report_abs(dev, ABS_HAT1X, 1);
+					else input_report_abs(dev, ABS_HAT1X, 0);
+				}
+				if (test_bit(ABS_HAT1Y, dev->absbit)) {
+					if (btns & 1) input_report_abs(dev, ABS_HAT1Y, -1);
+					else if (btns & 4) input_report_abs(dev, ABS_HAT1Y, 1);
+					else input_report_abs(dev, ABS_HAT1Y, 0);
+				}
+			}
+
+			input_sync(dev);
+
+			break;
+
+		case 0x02:	/* status report */
+			input_report_key(dev, BTN_DEAD, data[0] & 0x02);
+			input_sync(dev);
+
+			/* Check if an effect was just started or stopped */
+			i = data[1] & 0x7f;
+			if (data[1] & 0x80) {
+				if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
+					/* Report play event */
+					input_report_ff_status(dev, i, FF_STATUS_PLAYING);
+				}
+			} else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
+				/* Report stop event */
+				input_report_ff_status(dev, i, FF_STATUS_STOPPED);
+			}
+			if (LO(cmd) > 3) {
+				int j;
+				for (j = 3; j < LO(cmd); j += 2)
+					mark_core_as_ready(iforce, data[j] | (data[j+1]<<8));
+			}
+			break;
+	}
+	being_used--;
+}
+
+int iforce_get_id_packet(struct iforce *iforce, char *packet)
+{
+	switch (iforce->bus) {
+
+	case IFORCE_USB: {
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+		int status;
+
+		iforce->cr.bRequest = packet[0];
+		iforce->ctrl->dev = iforce->usbdev;
+
+		status = usb_submit_urb(iforce->ctrl, GFP_ATOMIC);
+		if (status) {
+			dev_err(&iforce->intf->dev,
+				"usb_submit_urb failed %d\n", status);
+			return -1;
+		}
+
+		wait_event_interruptible_timeout(iforce->wait,
+			iforce->ctrl->status != -EINPROGRESS, HZ);
+
+		if (iforce->ctrl->status) {
+			dev_dbg(&iforce->intf->dev,
+				"iforce->ctrl->status = %d\n",
+				iforce->ctrl->status);
+			usb_unlink_urb(iforce->ctrl);
+			return -1;
+		}
+#else
+		printk(KERN_DEBUG "iforce_get_id_packet: iforce->bus = USB!\n");
+#endif
+		}
+		break;
+
+	case IFORCE_232:
+
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+		iforce->expect_packet = FF_CMD_QUERY;
+		iforce_send_packet(iforce, FF_CMD_QUERY, packet);
+
+		wait_event_interruptible_timeout(iforce->wait,
+			!iforce->expect_packet, HZ);
+
+		if (iforce->expect_packet) {
+			iforce->expect_packet = 0;
+			return -1;
+		}
+#else
+		dev_err(&iforce->dev->dev,
+			"iforce_get_id_packet: iforce->bus = SERIO!\n");
+#endif
+		break;
+
+	default:
+		dev_err(&iforce->dev->dev,
+			"iforce_get_id_packet: iforce->bus = %d\n",
+			iforce->bus);
+		break;
+	}
+
+	return -(iforce->edata[0] != packet[0]);
+}
+
diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c
new file mode 100644
index 0000000..46d5041
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-serio.c
@@ -0,0 +1,189 @@
+/*
+ *  Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001, 2007 Johann Deneux <johann.deneux@gmail.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+void iforce_serial_xmit(struct iforce *iforce)
+{
+	unsigned char cs;
+	int i;
+	unsigned long flags;
+
+	if (test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
+		set_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags);
+		return;
+	}
+
+	spin_lock_irqsave(&iforce->xmit_lock, flags);
+
+again:
+	if (iforce->xmit.head == iforce->xmit.tail) {
+		clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+		spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+		return;
+	}
+
+	cs = 0x2b;
+
+	serio_write(iforce->serio, 0x2b);
+
+	serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
+	cs ^= iforce->xmit.buf[iforce->xmit.tail];
+	XMIT_INC(iforce->xmit.tail, 1);
+
+	for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) {
+		serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
+		cs ^= iforce->xmit.buf[iforce->xmit.tail];
+		XMIT_INC(iforce->xmit.tail, 1);
+	}
+
+	serio_write(iforce->serio, cs);
+
+	if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags))
+		goto again;
+
+	clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+
+	spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+}
+
+static void iforce_serio_write_wakeup(struct serio *serio)
+{
+	struct iforce *iforce = serio_get_drvdata(serio);
+
+	iforce_serial_xmit(iforce);
+}
+
+static irqreturn_t iforce_serio_irq(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct iforce *iforce = serio_get_drvdata(serio);
+
+	if (!iforce->pkt) {
+		if (data == 0x2b)
+			iforce->pkt = 1;
+		goto out;
+	}
+
+	if (!iforce->id) {
+		if (data > 3 && data != 0xff)
+			iforce->pkt = 0;
+		else
+			iforce->id = data;
+		goto out;
+	}
+
+	if (!iforce->len) {
+		if (data > IFORCE_MAX_LENGTH) {
+			iforce->pkt = 0;
+			iforce->id = 0;
+		} else {
+			iforce->len = data;
+		}
+		goto out;
+	}
+
+	if (iforce->idx < iforce->len) {
+		iforce->csum += iforce->data[iforce->idx++] = data;
+		goto out;
+	}
+
+	if (iforce->idx == iforce->len) {
+		iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data);
+		iforce->pkt = 0;
+		iforce->id  = 0;
+		iforce->len = 0;
+		iforce->idx = 0;
+		iforce->csum = 0;
+	}
+out:
+	return IRQ_HANDLED;
+}
+
+static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct iforce *iforce;
+	int err;
+
+	iforce = kzalloc(sizeof(struct iforce), GFP_KERNEL);
+	if (!iforce)
+		return -ENOMEM;
+
+	iforce->bus = IFORCE_232;
+	iforce->serio = serio;
+
+	serio_set_drvdata(serio, iforce);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail1;
+
+	err = iforce_init_device(iforce);
+	if (err)
+		goto fail2;
+
+	return 0;
+
+ fail2:	serio_close(serio);
+ fail1:	serio_set_drvdata(serio, NULL);
+	kfree(iforce);
+	return err;
+}
+
+static void iforce_serio_disconnect(struct serio *serio)
+{
+	struct iforce *iforce = serio_get_drvdata(serio);
+
+	input_unregister_device(iforce->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(iforce);
+}
+
+static struct serio_device_id iforce_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_IFORCE,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, iforce_serio_ids);
+
+struct serio_driver iforce_serio_drv = {
+	.driver		= {
+		.name	= "iforce",
+	},
+	.description	= "RS232 I-Force joysticks and wheels driver",
+	.id_table	= iforce_serio_ids,
+	.write_wakeup	= iforce_serio_write_wakeup,
+	.interrupt	= iforce_serio_irq,
+	.connect	= iforce_serio_connect,
+	.disconnect	= iforce_serio_disconnect,
+};
diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c
new file mode 100644
index 0000000..db64adf
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-usb.c
@@ -0,0 +1,235 @@
+ /*
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+void iforce_usb_xmit(struct iforce *iforce)
+{
+	int n, c;
+	unsigned long flags;
+
+	spin_lock_irqsave(&iforce->xmit_lock, flags);
+
+	if (iforce->xmit.head == iforce->xmit.tail) {
+		clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+		spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+		return;
+	}
+
+	((char *)iforce->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];
+	XMIT_INC(iforce->xmit.tail, 1);
+	n = iforce->xmit.buf[iforce->xmit.tail];
+	XMIT_INC(iforce->xmit.tail, 1);
+
+	iforce->out->transfer_buffer_length = n + 1;
+	iforce->out->dev = iforce->usbdev;
+
+	/* Copy rest of data then */
+	c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE);
+	if (n < c) c=n;
+
+	memcpy(iforce->out->transfer_buffer + 1,
+	       &iforce->xmit.buf[iforce->xmit.tail],
+	       c);
+	if (n != c) {
+		memcpy(iforce->out->transfer_buffer + 1 + c,
+		       &iforce->xmit.buf[0],
+		       n-c);
+	}
+	XMIT_INC(iforce->xmit.tail, n);
+
+	if ( (n=usb_submit_urb(iforce->out, GFP_ATOMIC)) ) {
+		clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+		dev_warn(&iforce->intf->dev, "usb_submit_urb failed %d\n", n);
+	}
+
+	/* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended.
+	 * As long as the urb completion handler is not called, the transmiting
+	 * is considered to be running */
+	spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+}
+
+static void iforce_usb_irq(struct urb *urb)
+{
+	struct iforce *iforce = urb->context;
+	struct device *dev = &iforce->intf->dev;
+	int status;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
+			__func__, urb->status);
+		return;
+	default:
+		dev_dbg(dev, "%s - urb has status of: %d\n",
+			__func__, urb->status);
+		goto exit;
+	}
+
+	iforce_process_packet(iforce,
+		(iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1);
+
+exit:
+	status = usb_submit_urb (urb, GFP_ATOMIC);
+	if (status)
+		dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
+			__func__, status);
+}
+
+static void iforce_usb_out(struct urb *urb)
+{
+	struct iforce *iforce = urb->context;
+
+	if (urb->status) {
+		clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+		dev_dbg(&iforce->intf->dev, "urb->status %d, exiting\n",
+			urb->status);
+		return;
+	}
+
+	iforce_usb_xmit(iforce);
+
+	wake_up(&iforce->wait);
+}
+
+static void iforce_usb_ctrl(struct urb *urb)
+{
+	struct iforce *iforce = urb->context;
+	if (urb->status) return;
+	iforce->ecmd = 0xff00 | urb->actual_length;
+	wake_up(&iforce->wait);
+}
+
+static int iforce_usb_probe(struct usb_interface *intf,
+				const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *epirq, *epout;
+	struct iforce *iforce;
+	int err = -ENOMEM;
+
+	interface = intf->cur_altsetting;
+
+	if (interface->desc.bNumEndpoints < 2)
+		return -ENODEV;
+
+	epirq = &interface->endpoint[0].desc;
+	epout = &interface->endpoint[1].desc;
+
+	if (!(iforce = kzalloc(sizeof(struct iforce) + 32, GFP_KERNEL)))
+		goto fail;
+
+	if (!(iforce->irq = usb_alloc_urb(0, GFP_KERNEL)))
+		goto fail;
+
+	if (!(iforce->out = usb_alloc_urb(0, GFP_KERNEL)))
+		goto fail;
+
+	if (!(iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL)))
+		goto fail;
+
+	iforce->bus = IFORCE_USB;
+	iforce->usbdev = dev;
+	iforce->intf = intf;
+
+	iforce->cr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
+	iforce->cr.wIndex = 0;
+	iforce->cr.wLength = cpu_to_le16(16);
+
+	usb_fill_int_urb(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),
+			iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);
+
+	usb_fill_int_urb(iforce->out, dev, usb_sndintpipe(dev, epout->bEndpointAddress),
+			iforce + 1, 32, iforce_usb_out, iforce, epout->bInterval);
+
+	usb_fill_control_urb(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),
+			(void*) &iforce->cr, iforce->edata, 16, iforce_usb_ctrl, iforce);
+
+	err = iforce_init_device(iforce);
+	if (err)
+		goto fail;
+
+	usb_set_intfdata(intf, iforce);
+	return 0;
+
+fail:
+	if (iforce) {
+		usb_free_urb(iforce->irq);
+		usb_free_urb(iforce->out);
+		usb_free_urb(iforce->ctrl);
+		kfree(iforce);
+	}
+
+	return err;
+}
+
+static void iforce_usb_disconnect(struct usb_interface *intf)
+{
+	struct iforce *iforce = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+
+	input_unregister_device(iforce->dev);
+
+	usb_free_urb(iforce->irq);
+	usb_free_urb(iforce->out);
+	usb_free_urb(iforce->ctrl);
+
+	kfree(iforce);
+}
+
+static struct usb_device_id iforce_usb_ids [] = {
+	{ USB_DEVICE(0x044f, 0xa01c) },		/* Thrustmaster Motor Sport GT */
+	{ USB_DEVICE(0x046d, 0xc281) },		/* Logitech WingMan Force */
+	{ USB_DEVICE(0x046d, 0xc291) },		/* Logitech WingMan Formula Force */
+	{ USB_DEVICE(0x05ef, 0x020a) },		/* AVB Top Shot Pegasus */
+	{ USB_DEVICE(0x05ef, 0x8884) },		/* AVB Mag Turbo Force */
+	{ USB_DEVICE(0x05ef, 0x8888) },		/* AVB Top Shot FFB Racing Wheel */
+	{ USB_DEVICE(0x061c, 0xc0a4) },         /* ACT LABS Force RS */
+	{ USB_DEVICE(0x061c, 0xc084) },         /* ACT LABS Force RS */
+	{ USB_DEVICE(0x06f8, 0x0001) },		/* Guillemot Race Leader Force Feedback */
+	{ USB_DEVICE(0x06f8, 0x0003) },		/* Guillemot Jet Leader Force Feedback */
+	{ USB_DEVICE(0x06f8, 0x0004) },		/* Guillemot Force Feedback Racing Wheel */
+	{ USB_DEVICE(0x06f8, 0xa302) },		/* Guillemot Jet Leader 3D */
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, iforce_usb_ids);
+
+struct usb_driver iforce_usb_driver = {
+	.name =		"iforce",
+	.probe =	iforce_usb_probe,
+	.disconnect =	iforce_usb_disconnect,
+	.id_table =	iforce_usb_ids,
+};
diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h
new file mode 100644
index 0000000..96ae4f5
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce.h
@@ -0,0 +1,171 @@
+/*
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/serio.h>
+#include <linux/circ_buf.h>
+#include <linux/mutex.h>
+
+/* This module provides arbitrary resource management routines.
+ * I use it to manage the device's memory.
+ * Despite the name of this module, I am *not* going to access the ioports.
+ */
+#include <linux/ioport.h>
+
+
+#define IFORCE_MAX_LENGTH	16
+
+/* iforce::bus */
+#define IFORCE_232	1
+#define IFORCE_USB	2
+
+#define IFORCE_EFFECTS_MAX	32
+
+/* Each force feedback effect is made of one core effect, which can be
+ * associated to at most to effect modifiers
+ */
+#define FF_MOD1_IS_USED		0
+#define FF_MOD2_IS_USED		1
+#define FF_CORE_IS_USED		2
+#define FF_CORE_IS_PLAYED	3	/* Effect is currently being played */
+#define FF_CORE_SHOULD_PLAY	4	/* User wants the effect to be played */
+#define FF_CORE_UPDATE		5	/* Effect is being updated */
+#define FF_MODCORE_CNT		6
+
+struct iforce_core_effect {
+	/* Information about where modifiers are stored in the device's memory */
+	struct resource mod1_chunk;
+	struct resource mod2_chunk;
+	unsigned long flags[BITS_TO_LONGS(FF_MODCORE_CNT)];
+};
+
+#define FF_CMD_EFFECT		0x010e
+#define FF_CMD_ENVELOPE		0x0208
+#define FF_CMD_MAGNITUDE	0x0303
+#define FF_CMD_PERIOD		0x0407
+#define FF_CMD_CONDITION	0x050a
+
+#define FF_CMD_AUTOCENTER	0x4002
+#define FF_CMD_PLAY		0x4103
+#define FF_CMD_ENABLE		0x4201
+#define FF_CMD_GAIN		0x4301
+
+#define FF_CMD_QUERY		0xff01
+
+/* Buffer for async write */
+#define XMIT_SIZE		256
+#define XMIT_INC(var, n)	(var)+=n; (var)&= XMIT_SIZE -1
+/* iforce::xmit_flags */
+#define IFORCE_XMIT_RUNNING	0
+#define IFORCE_XMIT_AGAIN	1
+
+struct iforce_device {
+	u16 idvendor;
+	u16 idproduct;
+	char *name;
+	signed short *btn;
+	signed short *abs;
+	signed short *ff;
+};
+
+struct iforce {
+	struct input_dev *dev;		/* Input device interface */
+	struct iforce_device *type;
+	int bus;
+
+	unsigned char data[IFORCE_MAX_LENGTH];
+	unsigned char edata[IFORCE_MAX_LENGTH];
+	u16 ecmd;
+	u16 expect_packet;
+
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+	struct serio *serio;		/* RS232 transfer */
+	int idx, pkt, len, id;
+	unsigned char csum;
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+	struct usb_device *usbdev;	/* USB transfer */
+	struct usb_interface *intf;
+	struct urb *irq, *out, *ctrl;
+	struct usb_ctrlrequest cr;
+#endif
+	spinlock_t xmit_lock;
+	/* Buffer used for asynchronous sending of bytes to the device */
+	struct circ_buf xmit;
+	unsigned char xmit_data[XMIT_SIZE];
+	unsigned long xmit_flags[1];
+
+					/* Force Feedback */
+	wait_queue_head_t wait;
+	struct resource device_memory;
+	struct iforce_core_effect core_effects[IFORCE_EFFECTS_MAX];
+	struct mutex mem_mutex;
+};
+
+/* Get hi and low bytes of a 16-bits int */
+#define HI(a)	((unsigned char)((a) >> 8))
+#define LO(a)	((unsigned char)((a) & 0xff))
+
+/* For many parameters, it seems that 0x80 is a special value that should
+ * be avoided. Instead, we replace this value by 0x7f
+ */
+#define HIFIX80(a) ((unsigned char)(((a)<0? (a)+255 : (a))>>8))
+
+/* Encode a time value */
+#define TIME_SCALE(a)	(a)
+
+
+/* Public functions */
+/* iforce-serio.c */
+void iforce_serial_xmit(struct iforce *iforce);
+
+/* iforce-usb.c */
+void iforce_usb_xmit(struct iforce *iforce);
+
+/* iforce-main.c */
+int iforce_init_device(struct iforce *iforce);
+
+/* iforce-packets.c */
+int iforce_control_playback(struct iforce*, u16 id, unsigned int);
+void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data);
+int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data);
+void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) ;
+int iforce_get_id_packet(struct iforce *iforce, char *packet);
+
+/* iforce-ff.c */
+int iforce_upload_periodic(struct iforce *, struct ff_effect *, struct ff_effect *);
+int iforce_upload_constant(struct iforce *, struct ff_effect *, struct ff_effect *);
+int iforce_upload_condition(struct iforce *, struct ff_effect *, struct ff_effect *);
+
+/* Public variables */
+extern struct serio_driver iforce_serio_drv;
+extern struct usb_driver iforce_usb_driver;
diff --git a/drivers/input/joystick/interact.c b/drivers/input/joystick/interact.c
new file mode 100644
index 0000000..17c2c80
--- /dev/null
+++ b/drivers/input/joystick/interact.c
@@ -0,0 +1,313 @@
+/*
+ *  Copyright (c) 2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Toby Deshane
+ */
+
+/*
+ * InterAct digital gamepad/joystick driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_DESC	"InterAct digital joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define INTERACT_MAX_START	600	/* 400 us */
+#define INTERACT_MAX_STROBE	60	/* 40 us */
+#define INTERACT_MAX_LENGTH	32	/* 32 bits */
+
+#define INTERACT_TYPE_HHFX	0	/* HammerHead/FX */
+#define INTERACT_TYPE_PP8D	1	/* ProPad 8 */
+
+struct interact {
+	struct gameport *gameport;
+	struct input_dev *dev;
+	int bads;
+	int reads;
+	unsigned char type;
+	unsigned char length;
+	char phys[32];
+};
+
+static short interact_abs_hhfx[] =
+	{ ABS_RX, ABS_RY, ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y, -1 };
+static short interact_abs_pp8d[] =
+	{ ABS_X, ABS_Y, -1 };
+
+static short interact_btn_hhfx[] =
+	{ BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL, BTN_TL2, BTN_TR2, BTN_MODE, BTN_SELECT, -1 };
+static short interact_btn_pp8d[] =
+	{ BTN_C, BTN_TL, BTN_TR, BTN_A, BTN_B, BTN_Y, BTN_Z, BTN_X, -1 };
+
+struct interact_type {
+	int id;
+	short *abs;
+	short *btn;
+	char *name;
+	unsigned char length;
+	unsigned char b8;
+};
+
+static struct interact_type interact_type[] = {
+	{ 0x6202, interact_abs_hhfx, interact_btn_hhfx, "InterAct HammerHead/FX",    32, 4 },
+	{ 0x53f8, interact_abs_pp8d, interact_btn_pp8d, "InterAct ProPad 8 Digital", 16, 0 },
+	{ 0 }};
+
+/*
+ * interact_read_packet() reads and InterAct joystick data.
+ */
+
+static int interact_read_packet(struct gameport *gameport, int length, u32 *data)
+{
+	unsigned long flags;
+	unsigned char u, v;
+	unsigned int t, s;
+	int i;
+
+	i = 0;
+	data[0] = data[1] = data[2] = 0;
+	t = gameport_time(gameport, INTERACT_MAX_START);
+	s = gameport_time(gameport, INTERACT_MAX_STROBE);
+
+	local_irq_save(flags);
+	gameport_trigger(gameport);
+	v = gameport_read(gameport);
+
+	while (t > 0 && i < length) {
+		t--;
+		u = v; v = gameport_read(gameport);
+		if (v & ~u & 0x40) {
+			data[0] = (data[0] << 1) | ((v >> 4) & 1);
+			data[1] = (data[1] << 1) | ((v >> 5) & 1);
+			data[2] = (data[2] << 1) | ((v >> 7) & 1);
+			i++;
+			t = s;
+		}
+	}
+
+	local_irq_restore(flags);
+
+	return i;
+}
+
+/*
+ * interact_poll() reads and analyzes InterAct joystick data.
+ */
+
+static void interact_poll(struct gameport *gameport)
+{
+	struct interact *interact = gameport_get_drvdata(gameport);
+	struct input_dev *dev = interact->dev;
+	u32 data[3];
+	int i;
+
+	interact->reads++;
+
+	if (interact_read_packet(interact->gameport, interact->length, data) < interact->length) {
+		interact->bads++;
+	} else {
+
+		for (i = 0; i < 3; i++)
+			data[i] <<= INTERACT_MAX_LENGTH - interact->length;
+
+		switch (interact->type) {
+
+			case INTERACT_TYPE_HHFX:
+
+				for (i = 0; i < 4; i++)
+					input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >> 1) << 3)) & 0xff);
+
+				for (i = 0; i < 2; i++)
+					input_report_abs(dev, ABS_HAT0Y - i,
+						((data[1] >> ((i << 1) + 17)) & 1)  - ((data[1] >> ((i << 1) + 16)) & 1));
+
+				for (i = 0; i < 8; i++)
+					input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1);
+
+				for (i = 0; i < 4; i++)
+					input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1);
+
+				break;
+
+			case INTERACT_TYPE_PP8D:
+
+				for (i = 0; i < 2; i++)
+					input_report_abs(dev, interact_abs_pp8d[i],
+						((data[0] >> ((i << 1) + 20)) & 1)  - ((data[0] >> ((i << 1) + 21)) & 1));
+
+				for (i = 0; i < 8; i++)
+					input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1);
+
+				break;
+		}
+	}
+
+	input_sync(dev);
+}
+
+/*
+ * interact_open() is a callback from the input open routine.
+ */
+
+static int interact_open(struct input_dev *dev)
+{
+	struct interact *interact = input_get_drvdata(dev);
+
+	gameport_start_polling(interact->gameport);
+	return 0;
+}
+
+/*
+ * interact_close() is a callback from the input close routine.
+ */
+
+static void interact_close(struct input_dev *dev)
+{
+	struct interact *interact = input_get_drvdata(dev);
+
+	gameport_stop_polling(interact->gameport);
+}
+
+/*
+ * interact_connect() probes for InterAct joysticks.
+ */
+
+static int interact_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct interact *interact;
+	struct input_dev *input_dev;
+	__u32 data[3];
+	int i, t;
+	int err;
+
+	interact = kzalloc(sizeof(struct interact), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!interact || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	interact->gameport = gameport;
+	interact->dev = input_dev;
+
+	gameport_set_drvdata(gameport, interact);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	i = interact_read_packet(gameport, INTERACT_MAX_LENGTH * 2, data);
+
+	if (i != 32 || (data[0] >> 24) != 0x0c || (data[1] >> 24) != 0x02) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	for (i = 0; interact_type[i].length; i++)
+		if (interact_type[i].id == (data[2] >> 16))
+			break;
+
+	if (!interact_type[i].length) {
+		printk(KERN_WARNING "interact.c: Unknown joystick on %s. [len %d d0 %08x d1 %08x i2 %08x]\n",
+			gameport->phys, i, data[0], data[1], data[2]);
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, interact_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	snprintf(interact->phys, sizeof(interact->phys), "%s/input0", gameport->phys);
+
+	interact->type = i;
+	interact->length = interact_type[i].length;
+
+	input_dev->name = interact_type[i].name;
+	input_dev->phys = interact->phys;
+	input_dev->id.bustype = BUS_GAMEPORT;
+	input_dev->id.vendor = GAMEPORT_ID_VENDOR_INTERACT;
+	input_dev->id.product = interact_type[i].id;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &gameport->dev;
+
+	input_set_drvdata(input_dev, interact);
+
+	input_dev->open = interact_open;
+	input_dev->close = interact_close;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) {
+		if (i < interact_type[interact->type].b8)
+			input_set_abs_params(input_dev, t, 0, 255, 0, 0);
+		else
+			input_set_abs_params(input_dev, t, -1, 1, 0, 0);
+	}
+
+	for (i = 0; (t = interact_type[interact->type].btn[i]) >= 0; i++)
+		__set_bit(t, input_dev->keybit);
+
+	err = input_register_device(interact->dev);
+	if (err)
+		goto fail2;
+
+	return 0;
+
+fail2:	gameport_close(gameport);
+fail1:  gameport_set_drvdata(gameport, NULL);
+	input_free_device(input_dev);
+	kfree(interact);
+	return err;
+}
+
+static void interact_disconnect(struct gameport *gameport)
+{
+	struct interact *interact = gameport_get_drvdata(gameport);
+
+	input_unregister_device(interact->dev);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(interact);
+}
+
+static struct gameport_driver interact_drv = {
+	.driver		= {
+		.name	= "interact",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= interact_connect,
+	.disconnect	= interact_disconnect,
+};
+
+module_gameport_driver(interact_drv);
diff --git a/drivers/input/joystick/joydump.c b/drivers/input/joystick/joydump.c
new file mode 100644
index 0000000..d1c6e48
--- /dev/null
+++ b/drivers/input/joystick/joydump.c
@@ -0,0 +1,161 @@
+/*
+ *  Copyright (c) 1996-2001 Vojtech Pavlik
+ */
+
+/*
+ * This is just a very simple driver that can dump the data
+ * out of the joystick port into the syslog ...
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/gameport.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#define DRIVER_DESC	"Gameport data dumper module"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define BUF_SIZE 256
+
+struct joydump {
+	unsigned int time;
+	unsigned char data;
+};
+
+static int joydump_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct joydump *buf;	/* all entries */
+	struct joydump *dump, *prev;	/* one entry each */
+	int axes[4], buttons;
+	int i, j, t, timeout;
+	unsigned long flags;
+	unsigned char u;
+
+	printk(KERN_INFO "joydump: ,------------------ START ----------------.\n");
+	printk(KERN_INFO "joydump: | Dumping: %30s |\n", gameport->phys);
+	printk(KERN_INFO "joydump: | Speed: %28d kHz |\n", gameport->speed);
+
+	if (gameport_open(gameport, drv, GAMEPORT_MODE_RAW)) {
+
+		printk(KERN_INFO "joydump: | Raw mode not available - trying cooked.    |\n");
+
+		if (gameport_open(gameport, drv, GAMEPORT_MODE_COOKED)) {
+
+			printk(KERN_INFO "joydump: | Cooked not available either. Failing.   |\n");
+			printk(KERN_INFO "joydump: `------------------- END -----------------'\n");
+			return -ENODEV;
+		}
+
+		gameport_cooked_read(gameport, axes, &buttons);
+
+		for (i = 0; i < 4; i++)
+			printk(KERN_INFO "joydump: | Axis %d: %4d.                           |\n", i, axes[i]);
+		printk(KERN_INFO "joydump: | Buttons %02x.                             |\n", buttons);
+		printk(KERN_INFO "joydump: `------------------- END -----------------'\n");
+	}
+
+	timeout = gameport_time(gameport, 10000); /* 10 ms */
+
+	buf = kmalloc(BUF_SIZE * sizeof(struct joydump), GFP_KERNEL);
+	if (!buf) {
+		printk(KERN_INFO "joydump: no memory for testing\n");
+		goto jd_end;
+	}
+	dump = buf;
+	t = 0;
+	i = 1;
+
+	local_irq_save(flags);
+
+	u = gameport_read(gameport);
+
+	dump->data = u;
+	dump->time = t;
+	dump++;
+
+	gameport_trigger(gameport);
+
+	while (i < BUF_SIZE && t < timeout) {
+
+		dump->data = gameport_read(gameport);
+
+		if (dump->data ^ u) {
+			u = dump->data;
+			dump->time = t;
+			i++;
+			dump++;
+		}
+		t++;
+	}
+
+	local_irq_restore(flags);
+
+/*
+ * Dump data.
+ */
+
+	t = i;
+	dump = buf;
+	prev = dump;
+
+	printk(KERN_INFO "joydump: >------------------ DATA -----------------<\n");
+	printk(KERN_INFO "joydump: | index: %3d delta: %3d us data: ", 0, 0);
+	for (j = 7; j >= 0; j--)
+		printk("%d", (dump->data >> j) & 1);
+	printk(" |\n");
+	dump++;
+
+	for (i = 1; i < t; i++, dump++, prev++) {
+		printk(KERN_INFO "joydump: | index: %3d delta: %3d us data: ",
+			i, dump->time - prev->time);
+		for (j = 7; j >= 0; j--)
+			printk("%d", (dump->data >> j) & 1);
+		printk(" |\n");
+	}
+	kfree(buf);
+
+jd_end:
+	printk(KERN_INFO "joydump: `------------------- END -----------------'\n");
+
+	return 0;
+}
+
+static void joydump_disconnect(struct gameport *gameport)
+{
+	gameport_close(gameport);
+}
+
+static struct gameport_driver joydump_drv = {
+	.driver		= {
+		.name	= "joydump",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= joydump_connect,
+	.disconnect	= joydump_disconnect,
+};
+
+module_gameport_driver(joydump_drv);
diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c
new file mode 100644
index 0000000..c5358ba
--- /dev/null
+++ b/drivers/input/joystick/magellan.c
@@ -0,0 +1,224 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Magellan and Space Mouse 6dof controller driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *  Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Magellan and SpaceMouse 6dof controller driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define	MAGELLAN_MAX_LENGTH	32
+
+static int magellan_buttons[] = { BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 };
+static int magellan_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ };
+
+/*
+ * Per-Magellan data.
+ */
+
+struct magellan {
+	struct input_dev *dev;
+	int idx;
+	unsigned char data[MAGELLAN_MAX_LENGTH];
+	char phys[32];
+};
+
+/*
+ * magellan_crunch_nibbles() verifies that the bytes sent from the Magellan
+ * have correct upper nibbles for the lower ones, if not, the packet will
+ * be thrown away. It also strips these upper halves to simplify further
+ * processing.
+ */
+
+static int magellan_crunch_nibbles(unsigned char *data, int count)
+{
+	static unsigned char nibbles[16] = "0AB3D56GH9:K<MN?";
+
+	do {
+		if (data[count] == nibbles[data[count] & 0xf])
+			data[count] = data[count] & 0xf;
+		else
+			return -1;
+	} while (--count);
+
+	return 0;
+}
+
+static void magellan_process_packet(struct magellan* magellan)
+{
+	struct input_dev *dev = magellan->dev;
+	unsigned char *data = magellan->data;
+	int i, t;
+
+	if (!magellan->idx) return;
+
+	switch (magellan->data[0]) {
+
+		case 'd':				/* Axis data */
+			if (magellan->idx != 25) return;
+			if (magellan_crunch_nibbles(data, 24)) return;
+			for (i = 0; i < 6; i++)
+				input_report_abs(dev, magellan_axes[i],
+					(data[(i << 2) + 1] << 12 | data[(i << 2) + 2] << 8 |
+					 data[(i << 2) + 3] <<  4 | data[(i << 2) + 4]) - 32768);
+			break;
+
+		case 'k':				/* Button data */
+			if (magellan->idx != 4) return;
+			if (magellan_crunch_nibbles(data, 3)) return;
+			t = (data[1] << 1) | (data[2] << 5) | data[3];
+			for (i = 0; i < 9; i++) input_report_key(dev, magellan_buttons[i], (t >> i) & 1);
+			break;
+	}
+
+	input_sync(dev);
+}
+
+static irqreturn_t magellan_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct magellan* magellan = serio_get_drvdata(serio);
+
+	if (data == '\r') {
+		magellan_process_packet(magellan);
+		magellan->idx = 0;
+	} else {
+		if (magellan->idx < MAGELLAN_MAX_LENGTH)
+			magellan->data[magellan->idx++] = data;
+	}
+	return IRQ_HANDLED;
+}
+
+/*
+ * magellan_disconnect() is the opposite of magellan_connect()
+ */
+
+static void magellan_disconnect(struct serio *serio)
+{
+	struct magellan* magellan = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(magellan->dev);
+	kfree(magellan);
+}
+
+/*
+ * magellan_connect() is the routine that is called when someone adds a
+ * new serio device that supports Magellan protocol and registers it as
+ * an input device.
+ */
+
+static int magellan_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct magellan *magellan;
+	struct input_dev *input_dev;
+	int err = -ENOMEM;
+	int i;
+
+	magellan = kzalloc(sizeof(struct magellan), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!magellan || !input_dev)
+		goto fail1;
+
+	magellan->dev = input_dev;
+	snprintf(magellan->phys, sizeof(magellan->phys), "%s/input0", serio->phys);
+
+	input_dev->name = "LogiCad3D Magellan / SpaceMouse";
+	input_dev->phys = magellan->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_MAGELLAN;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	for (i = 0; i < 9; i++)
+		set_bit(magellan_buttons[i], input_dev->keybit);
+
+	for (i = 0; i < 6; i++)
+		input_set_abs_params(input_dev, magellan_axes[i], -360, 360, 0, 0);
+
+	serio_set_drvdata(serio, magellan);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(magellan->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(magellan);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id magellan_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MAGELLAN,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, magellan_serio_ids);
+
+static struct serio_driver magellan_drv = {
+	.driver		= {
+		.name	= "magellan",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= magellan_serio_ids,
+	.interrupt	= magellan_interrupt,
+	.connect	= magellan_connect,
+	.disconnect	= magellan_disconnect,
+};
+
+module_serio_driver(magellan_drv);
diff --git a/drivers/input/joystick/maplecontrol.c b/drivers/input/joystick/maplecontrol.c
new file mode 100644
index 0000000..8aa6e4c
--- /dev/null
+++ b/drivers/input/joystick/maplecontrol.c
@@ -0,0 +1,193 @@
+/*
+ *	SEGA Dreamcast controller driver
+ *	Based on drivers/usb/iforce.c
+ *
+ *	Copyright Yaegashi Takeshi, 2001
+ *	Adrian McMenamin, 2008 - 2009
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/maple.h>
+
+MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
+MODULE_DESCRIPTION("SEGA Dreamcast controller driver");
+MODULE_LICENSE("GPL");
+
+struct dc_pad {
+	struct input_dev *dev;
+	struct maple_device *mdev;
+};
+
+static void dc_pad_callback(struct mapleq *mq)
+{
+	unsigned short buttons;
+	struct maple_device *mapledev = mq->dev;
+	struct dc_pad *pad = maple_get_drvdata(mapledev);
+	struct input_dev *dev = pad->dev;
+	unsigned char *res = mq->recvbuf->buf;
+
+	buttons = ~le16_to_cpup((__le16 *)(res + 8));
+
+	input_report_abs(dev, ABS_HAT0Y,
+		(buttons & 0x0010 ? -1 : 0) + (buttons & 0x0020 ? 1 : 0));
+	input_report_abs(dev, ABS_HAT0X,
+		(buttons & 0x0040 ? -1 : 0) + (buttons & 0x0080 ? 1 : 0));
+	input_report_abs(dev, ABS_HAT1Y,
+		(buttons & 0x1000 ? -1 : 0) + (buttons & 0x2000 ? 1 : 0));
+	input_report_abs(dev, ABS_HAT1X,
+		(buttons & 0x4000 ? -1 : 0) + (buttons & 0x8000 ? 1 : 0));
+
+	input_report_key(dev, BTN_C,      buttons & 0x0001);
+	input_report_key(dev, BTN_B,      buttons & 0x0002);
+	input_report_key(dev, BTN_A,      buttons & 0x0004);
+	input_report_key(dev, BTN_START,  buttons & 0x0008);
+	input_report_key(dev, BTN_Z,      buttons & 0x0100);
+	input_report_key(dev, BTN_Y,      buttons & 0x0200);
+	input_report_key(dev, BTN_X,      buttons & 0x0400);
+	input_report_key(dev, BTN_SELECT, buttons & 0x0800);
+
+	input_report_abs(dev, ABS_GAS,    res[10]);
+	input_report_abs(dev, ABS_BRAKE,  res[11]);
+	input_report_abs(dev, ABS_X,      res[12]);
+	input_report_abs(dev, ABS_Y,      res[13]);
+	input_report_abs(dev, ABS_RX,     res[14]);
+	input_report_abs(dev, ABS_RY,     res[15]);
+}
+
+static int dc_pad_open(struct input_dev *dev)
+{
+	struct dc_pad *pad = dev_get_platdata(&dev->dev);
+
+	maple_getcond_callback(pad->mdev, dc_pad_callback, HZ/20,
+		MAPLE_FUNC_CONTROLLER);
+
+	return 0;
+}
+
+static void dc_pad_close(struct input_dev *dev)
+{
+	struct dc_pad *pad = dev_get_platdata(&dev->dev);
+
+	maple_getcond_callback(pad->mdev, dc_pad_callback, 0,
+		MAPLE_FUNC_CONTROLLER);
+}
+
+/* allow the controller to be used */
+static int probe_maple_controller(struct device *dev)
+{
+	static const short btn_bit[32] = {
+		BTN_C, BTN_B, BTN_A, BTN_START, -1, -1, -1, -1,
+		BTN_Z, BTN_Y, BTN_X, BTN_SELECT, -1, -1, -1, -1,
+		-1, -1, -1, -1, -1, -1, -1, -1,
+		-1, -1, -1, -1, -1, -1, -1, -1,
+	};
+
+	static const short abs_bit[32] = {
+		-1, -1, -1, -1, ABS_HAT0Y, ABS_HAT0Y, ABS_HAT0X, ABS_HAT0X,
+		-1, -1, -1, -1, ABS_HAT1Y, ABS_HAT1Y, ABS_HAT1X, ABS_HAT1X,
+		ABS_GAS, ABS_BRAKE, ABS_X, ABS_Y, ABS_RX, ABS_RY, -1, -1,
+		-1, -1, -1, -1, -1, -1, -1, -1,
+	};
+
+	struct maple_device *mdev = to_maple_dev(dev);
+	struct maple_driver *mdrv = to_maple_driver(dev->driver);
+	int i, error;
+	struct dc_pad *pad;
+	struct input_dev *idev;
+	unsigned long data = be32_to_cpu(mdev->devinfo.function_data[0]);
+
+	pad = kzalloc(sizeof(struct dc_pad), GFP_KERNEL);
+	idev = input_allocate_device();
+	if (!pad || !idev) {
+		error = -ENOMEM;
+		goto fail;
+	}
+
+	pad->dev = idev;
+	pad->mdev = mdev;
+
+	idev->open = dc_pad_open;
+	idev->close = dc_pad_close;
+
+	for (i = 0; i < 32; i++) {
+		if (data & (1 << i)) {
+			if (btn_bit[i] >= 0)
+				__set_bit(btn_bit[i], idev->keybit);
+			else if (abs_bit[i] >= 0)
+				__set_bit(abs_bit[i], idev->absbit);
+		}
+	}
+
+	if (idev->keybit[BIT_WORD(BTN_JOYSTICK)])
+		idev->evbit[0] |= BIT_MASK(EV_KEY);
+
+	if (idev->absbit[0])
+		idev->evbit[0] |= BIT_MASK(EV_ABS);
+
+	for (i = ABS_X; i <= ABS_BRAKE; i++)
+		input_set_abs_params(idev, i, 0, 255, 0, 0);
+
+	for (i = ABS_HAT0X; i <= ABS_HAT3Y; i++)
+		input_set_abs_params(idev, i, 1, -1, 0, 0);
+
+	idev->dev.platform_data = pad;
+	idev->dev.parent = &mdev->dev;
+	idev->name = mdev->product_name;
+	idev->id.bustype = BUS_HOST;
+	input_set_drvdata(idev, pad);
+
+	error = input_register_device(idev);
+	if (error)
+		goto fail;
+
+	mdev->driver = mdrv;
+	maple_set_drvdata(mdev, pad);
+
+	return 0;
+
+fail:
+	input_free_device(idev);
+	kfree(pad);
+	maple_set_drvdata(mdev, NULL);
+	return error;
+}
+
+static int remove_maple_controller(struct device *dev)
+{
+	struct maple_device *mdev = to_maple_dev(dev);
+	struct dc_pad *pad = maple_get_drvdata(mdev);
+
+	mdev->callback = NULL;
+	input_unregister_device(pad->dev);
+	maple_set_drvdata(mdev, NULL);
+	kfree(pad);
+
+	return 0;
+}
+
+static struct maple_driver dc_pad_driver = {
+	.function =	MAPLE_FUNC_CONTROLLER,
+	.drv = {
+		.name	= "Dreamcast_controller",
+		.probe	= probe_maple_controller,
+		.remove	= remove_maple_controller,
+	},
+};
+
+static int __init dc_pad_init(void)
+{
+	return maple_driver_register(&dc_pad_driver);
+}
+
+static void __exit dc_pad_exit(void)
+{
+	maple_driver_unregister(&dc_pad_driver);
+}
+
+module_init(dc_pad_init);
+module_exit(dc_pad_exit);
diff --git a/drivers/input/joystick/sidewinder.c b/drivers/input/joystick/sidewinder.c
new file mode 100644
index 0000000..4a95b22
--- /dev/null
+++ b/drivers/input/joystick/sidewinder.c
@@ -0,0 +1,822 @@
+/*
+ *  Copyright (c) 1998-2005 Vojtech Pavlik
+ */
+
+/*
+ * Microsoft SideWinder joystick family driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_DESC	"Microsoft SideWinder joystick family driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * These are really magic values. Changing them can make a problem go away,
+ * as well as break everything.
+ */
+
+#undef SW_DEBUG
+#undef SW_DEBUG_DATA
+
+#define SW_START	600	/* The time we wait for the first bit [600 us] */
+#define SW_STROBE	60	/* Max time per bit [60 us] */
+#define SW_TIMEOUT	6	/* Wait for everything to settle [6 ms] */
+#define SW_KICK		45	/* Wait after A0 fall till kick [45 us] */
+#define SW_END		8	/* Number of bits before end of packet to kick */
+#define SW_FAIL		16	/* Number of packet read errors to fail and reinitialize */
+#define SW_BAD		2	/* Number of packet read errors to switch off 3d Pro optimization */
+#define SW_OK		64	/* Number of packet read successes to switch optimization back on */
+#define SW_LENGTH	512	/* Max number of bits in a packet */
+
+#ifdef SW_DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+/*
+ * SideWinder joystick types ...
+ */
+
+#define SW_ID_3DP	0
+#define SW_ID_GP	1
+#define SW_ID_PP	2
+#define SW_ID_FFP	3
+#define SW_ID_FSP	4
+#define SW_ID_FFW	5
+
+/*
+ * Names, buttons, axes ...
+ */
+
+static char *sw_name[] = {	"3D Pro", "GamePad", "Precision Pro", "Force Feedback Pro", "FreeStyle Pro",
+				"Force Feedback Wheel" };
+
+static char sw_abs[][7] = {
+	{ ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
+	{ ABS_X, ABS_Y },
+	{ ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
+	{ ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
+	{ ABS_X, ABS_Y,         ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
+	{ ABS_RX, ABS_RUDDER,   ABS_THROTTLE }};
+
+static char sw_bit[][7] = {
+	{ 10, 10,  9, 10,  1,  1 },
+	{  1,  1                 },
+	{ 10, 10,  6,  7,  1,  1 },
+	{ 10, 10,  6,  7,  1,  1 },
+	{ 10, 10,  6,  1,  1     },
+	{ 10,  7,  7,  1,  1     }};
+
+static short sw_btn[][12] = {
+	{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_MODE },
+	{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE },
+	{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT },
+	{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT },
+	{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT },
+	{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 }};
+
+static struct {
+	int x;
+	int y;
+} sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+struct sw {
+	struct gameport *gameport;
+	struct input_dev *dev[4];
+	char name[64];
+	char phys[4][32];
+	int length;
+	int type;
+	int bits;
+	int number;
+	int fail;
+	int ok;
+	int reads;
+	int bads;
+};
+
+/*
+ * sw_read_packet() is a function which reads either a data packet, or an
+ * identification packet from a SideWinder joystick. The protocol is very,
+ * very, very braindamaged. Microsoft patented it in US patent #5628686.
+ */
+
+static int sw_read_packet(struct gameport *gameport, unsigned char *buf, int length, int id)
+{
+	unsigned long flags;
+	int timeout, bitout, sched, i, kick, start, strobe;
+	unsigned char pending, u, v;
+
+	i = -id;						/* Don't care about data, only want ID */
+	timeout = id ? gameport_time(gameport, SW_TIMEOUT * 1000) : 0; /* Set up global timeout for ID packet */
+	kick = id ? gameport_time(gameport, SW_KICK) : 0;	/* Set up kick timeout for ID packet */
+	start = gameport_time(gameport, SW_START);
+	strobe = gameport_time(gameport, SW_STROBE);
+	bitout = start;
+	pending = 0;
+	sched = 0;
+
+        local_irq_save(flags);					/* Quiet, please */
+
+	gameport_trigger(gameport);				/* Trigger */
+	v = gameport_read(gameport);
+
+	do {
+		bitout--;
+		u = v;
+		v = gameport_read(gameport);
+	} while (!(~v & u & 0x10) && (bitout > 0));		/* Wait for first falling edge on clock */
+
+	if (bitout > 0)
+		bitout = strobe;				/* Extend time if not timed out */
+
+	while ((timeout > 0 || bitout > 0) && (i < length)) {
+
+		timeout--;
+		bitout--;					/* Decrement timers */
+		sched--;
+
+		u = v;
+		v = gameport_read(gameport);
+
+		if ((~u & v & 0x10) && (bitout > 0)) {		/* Rising edge on clock - data bit */
+			if (i >= 0)				/* Want this data */
+				buf[i] = v >> 5;		/* Store it */
+			i++;					/* Advance index */
+			bitout = strobe;			/* Extend timeout for next bit */
+		}
+
+		if (kick && (~v & u & 0x01)) {			/* Falling edge on axis 0 */
+			sched = kick;				/* Schedule second trigger */
+			kick = 0;				/* Don't schedule next time on falling edge */
+			pending = 1;				/* Mark schedule */
+		}
+
+		if (pending && sched < 0 && (i > -SW_END)) {	/* Second trigger time */
+			gameport_trigger(gameport);		/* Trigger */
+			bitout = start;				/* Long bit timeout */
+			pending = 0;				/* Unmark schedule */
+			timeout = 0;				/* Switch from global to bit timeouts */
+		}
+	}
+
+	local_irq_restore(flags);					/* Done - relax */
+
+#ifdef SW_DEBUG_DATA
+	{
+		int j;
+		printk(KERN_DEBUG "sidewinder.c: Read %d triplets. [", i);
+		for (j = 0; j < i; j++) printk("%d", buf[j]);
+		printk("]\n");
+	}
+#endif
+
+	return i;
+}
+
+/*
+ * sw_get_bits() and GB() compose bits from the triplet buffer into a __u64.
+ * Parameter 'pos' is bit number inside packet where to start at, 'num' is number
+ * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits
+ * is number of bits per triplet.
+ */
+
+#define GB(pos,num) sw_get_bits(buf, pos, num, sw->bits)
+
+static __u64 sw_get_bits(unsigned char *buf, int pos, int num, char bits)
+{
+	__u64 data = 0;
+	int tri = pos % bits;						/* Start position */
+	int i   = pos / bits;
+	int bit = 0;
+
+	while (num--) {
+		data |= (__u64)((buf[i] >> tri++) & 1) << bit++;	/* Transfer bit */
+		if (tri == bits) {
+			i++;						/* Next triplet */
+			tri = 0;
+		}
+	}
+
+	return data;
+}
+
+/*
+ * sw_init_digital() initializes a SideWinder 3D Pro joystick
+ * into digital mode.
+ */
+
+static void sw_init_digital(struct gameport *gameport)
+{
+	int seq[] = { 140, 140+725, 140+300, 0 };
+	unsigned long flags;
+	int i, t;
+
+        local_irq_save(flags);
+
+	i = 0;
+        do {
+                gameport_trigger(gameport);			/* Trigger */
+		t = gameport_time(gameport, SW_TIMEOUT * 1000);
+		while ((gameport_read(gameport) & 1) && t) t--;	/* Wait for axis to fall back to 0 */
+                udelay(seq[i]);					/* Delay magic time */
+        } while (seq[++i]);
+
+	gameport_trigger(gameport);				/* Last trigger */
+
+	local_irq_restore(flags);
+}
+
+/*
+ * sw_parity() computes parity of __u64
+ */
+
+static int sw_parity(__u64 t)
+{
+	int x = t ^ (t >> 32);
+
+	x ^= x >> 16;
+	x ^= x >> 8;
+	x ^= x >> 4;
+	x ^= x >> 2;
+	x ^= x >> 1;
+	return x & 1;
+}
+
+/*
+ * sw_ccheck() checks synchronization bits and computes checksum of nibbles.
+ */
+
+static int sw_check(__u64 t)
+{
+	unsigned char sum = 0;
+
+	if ((t & 0x8080808080808080ULL) ^ 0x80)			/* Sync */
+		return -1;
+
+	while (t) {						/* Sum */
+		sum += t & 0xf;
+		t >>= 4;
+	}
+
+	return sum & 0xf;
+}
+
+/*
+ * sw_parse() analyzes SideWinder joystick data, and writes the results into
+ * the axes and buttons arrays.
+ */
+
+static int sw_parse(unsigned char *buf, struct sw *sw)
+{
+	int hat, i, j;
+	struct input_dev *dev;
+
+	switch (sw->type) {
+
+		case SW_ID_3DP:
+
+			if (sw_check(GB(0,64)) || (hat = (GB(6,1) << 3) | GB(60,3)) > 8)
+				return -1;
+
+			dev = sw->dev[0];
+
+			input_report_abs(dev, ABS_X,        (GB( 3,3) << 7) | GB(16,7));
+			input_report_abs(dev, ABS_Y,        (GB( 0,3) << 7) | GB(24,7));
+			input_report_abs(dev, ABS_RZ,       (GB(35,2) << 7) | GB(40,7));
+			input_report_abs(dev, ABS_THROTTLE, (GB(32,3) << 7) | GB(48,7));
+
+			input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);
+			input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);
+
+			for (j = 0; j < 7; j++)
+				input_report_key(dev, sw_btn[SW_ID_3DP][j], !GB(j+8,1));
+
+			input_report_key(dev, BTN_BASE4, !GB(38,1));
+			input_report_key(dev, BTN_BASE5, !GB(37,1));
+
+			input_sync(dev);
+
+			return 0;
+
+		case SW_ID_GP:
+
+			for (i = 0; i < sw->number; i ++) {
+
+				if (sw_parity(GB(i*15,15)))
+					return -1;
+
+				input_report_abs(sw->dev[i], ABS_X, GB(i*15+3,1) - GB(i*15+2,1));
+				input_report_abs(sw->dev[i], ABS_Y, GB(i*15+0,1) - GB(i*15+1,1));
+
+				for (j = 0; j < 10; j++)
+					input_report_key(sw->dev[i], sw_btn[SW_ID_GP][j], !GB(i*15+j+4,1));
+
+				input_sync(sw->dev[i]);
+			}
+
+			return 0;
+
+		case SW_ID_PP:
+		case SW_ID_FFP:
+
+			if (!sw_parity(GB(0,48)) || (hat = GB(42,4)) > 8)
+				return -1;
+
+			dev = sw->dev[0];
+			input_report_abs(dev, ABS_X,        GB( 9,10));
+			input_report_abs(dev, ABS_Y,        GB(19,10));
+			input_report_abs(dev, ABS_RZ,       GB(36, 6));
+			input_report_abs(dev, ABS_THROTTLE, GB(29, 7));
+
+			input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);
+			input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);
+
+			for (j = 0; j < 9; j++)
+				input_report_key(dev, sw_btn[SW_ID_PP][j], !GB(j,1));
+
+			input_sync(dev);
+
+			return 0;
+
+		case SW_ID_FSP:
+
+			if (!sw_parity(GB(0,43)) || (hat = GB(28,4)) > 8)
+				return -1;
+
+			dev = sw->dev[0];
+			input_report_abs(dev, ABS_X,        GB( 0,10));
+			input_report_abs(dev, ABS_Y,        GB(16,10));
+			input_report_abs(dev, ABS_THROTTLE, GB(32, 6));
+
+			input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);
+			input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);
+
+			for (j = 0; j < 6; j++)
+				input_report_key(dev, sw_btn[SW_ID_FSP][j], !GB(j+10,1));
+
+			input_report_key(dev, BTN_TR,     !GB(26,1));
+			input_report_key(dev, BTN_START,  !GB(27,1));
+			input_report_key(dev, BTN_MODE,   !GB(38,1));
+			input_report_key(dev, BTN_SELECT, !GB(39,1));
+
+			input_sync(dev);
+
+			return 0;
+
+		case SW_ID_FFW:
+
+			if (!sw_parity(GB(0,33)))
+				return -1;
+
+			dev = sw->dev[0];
+			input_report_abs(dev, ABS_RX,       GB( 0,10));
+			input_report_abs(dev, ABS_RUDDER,   GB(10, 6));
+			input_report_abs(dev, ABS_THROTTLE, GB(16, 6));
+
+			for (j = 0; j < 8; j++)
+				input_report_key(dev, sw_btn[SW_ID_FFW][j], !GB(j+22,1));
+
+			input_sync(dev);
+
+			return 0;
+	}
+
+	return -1;
+}
+
+/*
+ * sw_read() reads SideWinder joystick data, and reinitializes
+ * the joystick in case of persistent problems. This is the function that is
+ * called from the generic code to poll the joystick.
+ */
+
+static int sw_read(struct sw *sw)
+{
+	unsigned char buf[SW_LENGTH];
+	int i;
+
+	i = sw_read_packet(sw->gameport, buf, sw->length, 0);
+
+	if (sw->type == SW_ID_3DP && sw->length == 66 && i != 66) {		/* Broken packet, try to fix */
+
+		if (i == 64 && !sw_check(sw_get_bits(buf,0,64,1))) {		/* Last init failed, 1 bit mode */
+			printk(KERN_WARNING "sidewinder.c: Joystick in wrong mode on %s"
+				" - going to reinitialize.\n", sw->gameport->phys);
+			sw->fail = SW_FAIL;					/* Reinitialize */
+			i = 128;						/* Bogus value */
+		}
+
+		if (i < 66 && GB(0,64) == GB(i*3-66,64))			/* 1 == 3 */
+			i = 66;							/* Everything is fine */
+
+		if (i < 66 && GB(0,64) == GB(66,64))				/* 1 == 2 */
+			i = 66;							/* Everything is fine */
+
+		if (i < 66 && GB(i*3-132,64) == GB(i*3-66,64)) {		/* 2 == 3 */
+			memmove(buf, buf + i - 22, 22);				/* Move data */
+			i = 66;							/* Carry on */
+		}
+	}
+
+	if (i == sw->length && !sw_parse(buf, sw)) {				/* Parse data */
+
+		sw->fail = 0;
+		sw->ok++;
+
+		if (sw->type == SW_ID_3DP && sw->length == 66			/* Many packets OK */
+			&& sw->ok > SW_OK) {
+
+			printk(KERN_INFO "sidewinder.c: No more trouble on %s"
+				" - enabling optimization again.\n", sw->gameport->phys);
+			sw->length = 22;
+		}
+
+		return 0;
+	}
+
+	sw->ok = 0;
+	sw->fail++;
+
+	if (sw->type == SW_ID_3DP && sw->length == 22 && sw->fail > SW_BAD) {	/* Consecutive bad packets */
+
+		printk(KERN_INFO "sidewinder.c: Many bit errors on %s"
+			" - disabling optimization.\n", sw->gameport->phys);
+		sw->length = 66;
+	}
+
+	if (sw->fail < SW_FAIL)
+		return -1;							/* Not enough, don't reinitialize yet */
+
+	printk(KERN_WARNING "sidewinder.c: Too many bit errors on %s"
+		" - reinitializing joystick.\n", sw->gameport->phys);
+
+	if (!i && sw->type == SW_ID_3DP) {					/* 3D Pro can be in analog mode */
+		mdelay(3 * SW_TIMEOUT);
+		sw_init_digital(sw->gameport);
+	}
+
+	mdelay(SW_TIMEOUT);
+	i = sw_read_packet(sw->gameport, buf, SW_LENGTH, 0);			/* Read normal data packet */
+	mdelay(SW_TIMEOUT);
+	sw_read_packet(sw->gameport, buf, SW_LENGTH, i);			/* Read ID packet, this initializes the stick */
+
+	sw->fail = SW_FAIL;
+
+	return -1;
+}
+
+static void sw_poll(struct gameport *gameport)
+{
+	struct sw *sw = gameport_get_drvdata(gameport);
+
+	sw->reads++;
+	if (sw_read(sw))
+		sw->bads++;
+}
+
+static int sw_open(struct input_dev *dev)
+{
+	struct sw *sw = input_get_drvdata(dev);
+
+	gameport_start_polling(sw->gameport);
+	return 0;
+}
+
+static void sw_close(struct input_dev *dev)
+{
+	struct sw *sw = input_get_drvdata(dev);
+
+	gameport_stop_polling(sw->gameport);
+}
+
+/*
+ * sw_print_packet() prints the contents of a SideWinder packet.
+ */
+
+static void sw_print_packet(char *name, int length, unsigned char *buf, char bits)
+{
+	int i;
+
+	printk(KERN_INFO "sidewinder.c: %s packet, %d bits. [", name, length);
+	for (i = (((length + 3) >> 2) - 1); i >= 0; i--)
+		printk("%x", (int)sw_get_bits(buf, i << 2, 4, bits));
+	printk("]\n");
+}
+
+/*
+ * sw_3dp_id() translates the 3DP id into a human legible string.
+ * Unfortunately I don't know how to do this for the other SW types.
+ */
+
+static void sw_3dp_id(unsigned char *buf, char *comment, size_t size)
+{
+	int i;
+	char pnp[8], rev[9];
+
+	for (i = 0; i < 7; i++)						/* ASCII PnP ID */
+		pnp[i] = sw_get_bits(buf, 24+8*i, 8, 1);
+
+	for (i = 0; i < 8; i++)						/* ASCII firmware revision */
+		rev[i] = sw_get_bits(buf, 88+8*i, 8, 1);
+
+	pnp[7] = rev[8] = 0;
+
+	snprintf(comment, size, " [PnP %d.%02d id %s rev %s]",
+		(int) ((sw_get_bits(buf, 8, 6, 1) << 6) |		/* Two 6-bit values */
+			sw_get_bits(buf, 16, 6, 1)) / 100,
+		(int) ((sw_get_bits(buf, 8, 6, 1) << 6) |
+			sw_get_bits(buf, 16, 6, 1)) % 100,
+		 pnp, rev);
+}
+
+/*
+ * sw_guess_mode() checks the upper two button bits for toggling -
+ * indication of that the joystick is in 3-bit mode. This is documented
+ * behavior for 3DP ID packet, and for example the FSP does this in
+ * normal packets instead. Fun ...
+ */
+
+static int sw_guess_mode(unsigned char *buf, int len)
+{
+	int i;
+	unsigned char xor = 0;
+
+	for (i = 1; i < len; i++)
+		xor |= (buf[i - 1] ^ buf[i]) & 6;
+
+	return !!xor * 2 + 1;
+}
+
+/*
+ * sw_connect() probes for SideWinder type joysticks.
+ */
+
+static int sw_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct sw *sw;
+	struct input_dev *input_dev;
+	int i, j, k, l;
+	int err = 0;
+	unsigned char *buf = NULL;	/* [SW_LENGTH] */
+	unsigned char *idbuf = NULL;	/* [SW_LENGTH] */
+	unsigned char m = 1;
+	char comment[40];
+
+	comment[0] = 0;
+
+	sw = kzalloc(sizeof(struct sw), GFP_KERNEL);
+	buf = kmalloc(SW_LENGTH, GFP_KERNEL);
+	idbuf = kmalloc(SW_LENGTH, GFP_KERNEL);
+	if (!sw || !buf || !idbuf) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	sw->gameport = gameport;
+
+	gameport_set_drvdata(gameport, sw);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	dbg("Init 0: Opened %s, io %#x, speed %d",
+		gameport->phys, gameport->io, gameport->speed);
+
+	i = sw_read_packet(gameport, buf, SW_LENGTH, 0);		/* Read normal packet */
+	msleep(SW_TIMEOUT);
+	dbg("Init 1: Mode %d. Length %d.", m , i);
+
+	if (!i) {							/* No data. 3d Pro analog mode? */
+		sw_init_digital(gameport);				/* Switch to digital */
+		msleep(SW_TIMEOUT);
+		i = sw_read_packet(gameport, buf, SW_LENGTH, 0);	/* Retry reading packet */
+		msleep(SW_TIMEOUT);
+		dbg("Init 1b: Length %d.", i);
+		if (!i) {						/* No data -> FAIL */
+			err = -ENODEV;
+			goto fail2;
+		}
+	}
+
+	j = sw_read_packet(gameport, idbuf, SW_LENGTH, i);		/* Read ID. This initializes the stick */
+	m |= sw_guess_mode(idbuf, j);					/* ID packet should carry mode info [3DP] */
+	dbg("Init 2: Mode %d. ID Length %d.", m, j);
+
+	if (j <= 0) {							/* Read ID failed. Happens in 1-bit mode on PP */
+		msleep(SW_TIMEOUT);
+		i = sw_read_packet(gameport, buf, SW_LENGTH, 0);	/* Retry reading packet */
+		m |= sw_guess_mode(buf, i);
+		dbg("Init 2b: Mode %d. Length %d.", m, i);
+		if (!i) {
+			err = -ENODEV;
+			goto fail2;
+		}
+		msleep(SW_TIMEOUT);
+		j = sw_read_packet(gameport, idbuf, SW_LENGTH, i);	/* Retry reading ID */
+		dbg("Init 2c: ID Length %d.", j);
+	}
+
+	sw->type = -1;
+	k = SW_FAIL;							/* Try SW_FAIL times */
+	l = 0;
+
+	do {
+		k--;
+		msleep(SW_TIMEOUT);
+		i = sw_read_packet(gameport, buf, SW_LENGTH, 0);	/* Read data packet */
+		dbg("Init 3: Mode %d. Length %d. Last %d. Tries %d.", m, i, l, k);
+
+		if (i > l) {						/* Longer? As we can only lose bits, it makes */
+									/* no sense to try detection for a packet shorter */
+			l = i;						/* than the previous one */
+
+			sw->number = 1;
+			sw->gameport = gameport;
+			sw->length = i;
+			sw->bits = m;
+
+			dbg("Init 3a: Case %d.\n", i * m);
+
+			switch (i * m) {
+				case 60:
+					sw->number++;
+				case 45:				/* Ambiguous packet length */
+					if (j <= 40) {			/* ID length less or eq 40 -> FSP */
+				case 43:
+						sw->type = SW_ID_FSP;
+						break;
+					}
+					sw->number++;
+				case 30:
+					sw->number++;
+				case 15:
+					sw->type = SW_ID_GP;
+					break;
+				case 33:
+				case 31:
+					sw->type = SW_ID_FFW;
+					break;
+				case 48:				/* Ambiguous */
+					if (j == 14) {			/* ID length 14*3 -> FFP */
+						sw->type = SW_ID_FFP;
+						sprintf(comment, " [AC %s]", sw_get_bits(idbuf,38,1,3) ? "off" : "on");
+					} else
+						sw->type = SW_ID_PP;
+					break;
+				case 66:
+					sw->bits = 3;
+				case 198:
+					sw->length = 22;
+				case 64:
+					sw->type = SW_ID_3DP;
+					if (j == 160)
+						sw_3dp_id(idbuf, comment, sizeof(comment));
+					break;
+			}
+		}
+
+	} while (k && sw->type == -1);
+
+	if (sw->type == -1) {
+		printk(KERN_WARNING "sidewinder.c: unknown joystick device detected "
+			"on %s, contact <vojtech@ucw.cz>\n", gameport->phys);
+		sw_print_packet("ID", j * 3, idbuf, 3);
+		sw_print_packet("Data", i * m, buf, m);
+		err = -ENODEV;
+		goto fail2;
+	}
+
+#ifdef SW_DEBUG
+	sw_print_packet("ID", j * 3, idbuf, 3);
+	sw_print_packet("Data", i * m, buf, m);
+#endif
+
+	gameport_set_poll_handler(gameport, sw_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	k = i;
+	l = j;
+
+	for (i = 0; i < sw->number; i++) {
+		int bits, code;
+
+		snprintf(sw->name, sizeof(sw->name),
+			 "Microsoft SideWinder %s", sw_name[sw->type]);
+		snprintf(sw->phys[i], sizeof(sw->phys[i]),
+			 "%s/input%d", gameport->phys, i);
+
+		sw->dev[i] = input_dev = input_allocate_device();
+		if (!input_dev) {
+			err = -ENOMEM;
+			goto fail3;
+		}
+
+		input_dev->name = sw->name;
+		input_dev->phys = sw->phys[i];
+		input_dev->id.bustype = BUS_GAMEPORT;
+		input_dev->id.vendor = GAMEPORT_ID_VENDOR_MICROSOFT;
+		input_dev->id.product = sw->type;
+		input_dev->id.version = 0x0100;
+		input_dev->dev.parent = &gameport->dev;
+
+		input_set_drvdata(input_dev, sw);
+
+		input_dev->open = sw_open;
+		input_dev->close = sw_close;
+
+		input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+		for (j = 0; (bits = sw_bit[sw->type][j]); j++) {
+			int min, max, fuzz, flat;
+
+			code = sw_abs[sw->type][j];
+			min = bits == 1 ? -1 : 0;
+			max = (1 << bits) - 1;
+			fuzz = (bits >> 1) >= 2 ? 1 << ((bits >> 1) - 2) : 0;
+			flat = code == ABS_THROTTLE || bits < 5 ?
+				0 : 1 << (bits - 5);
+
+			input_set_abs_params(input_dev, code,
+					     min, max, fuzz, flat);
+		}
+
+		for (j = 0; (code = sw_btn[sw->type][j]); j++)
+			__set_bit(code, input_dev->keybit);
+
+		dbg("%s%s [%d-bit id %d data %d]\n", sw->name, comment, m, l, k);
+
+		err = input_register_device(sw->dev[i]);
+		if (err)
+			goto fail4;
+	}
+
+ out:	kfree(buf);
+	kfree(idbuf);
+
+	return err;
+
+ fail4:	input_free_device(sw->dev[i]);
+ fail3:	while (--i >= 0)
+		input_unregister_device(sw->dev[i]);
+ fail2:	gameport_close(gameport);
+ fail1:	gameport_set_drvdata(gameport, NULL);
+	kfree(sw);
+	goto out;
+}
+
+static void sw_disconnect(struct gameport *gameport)
+{
+	struct sw *sw = gameport_get_drvdata(gameport);
+	int i;
+
+	for (i = 0; i < sw->number; i++)
+		input_unregister_device(sw->dev[i]);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(sw);
+}
+
+static struct gameport_driver sw_drv = {
+	.driver		= {
+		.name	= "sidewinder",
+		.owner	= THIS_MODULE,
+	},
+	.description	= DRIVER_DESC,
+	.connect	= sw_connect,
+	.disconnect	= sw_disconnect,
+};
+
+module_gameport_driver(sw_drv);
diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c
new file mode 100644
index 0000000..f4445a4
--- /dev/null
+++ b/drivers/input/joystick/spaceball.c
@@ -0,0 +1,298 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	David Thompson
+ *	Joseph Krahn
+ */
+
+/*
+ * SpaceTec SpaceBall 2003/3003/4000 FLX driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *  Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"SpaceTec SpaceBall 2003/3003/4000 FLX driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define SPACEBALL_MAX_LENGTH	128
+#define SPACEBALL_MAX_ID	9
+
+#define SPACEBALL_1003      1
+#define SPACEBALL_2003B     3
+#define SPACEBALL_2003C     4
+#define SPACEBALL_3003C     7
+#define SPACEBALL_4000FLX   8
+#define SPACEBALL_4000FLX_L 9
+
+static int spaceball_axes[] = { ABS_X, ABS_Z, ABS_Y, ABS_RX, ABS_RZ, ABS_RY };
+static char *spaceball_names[] = {
+	"?", "SpaceTec SpaceBall 1003", "SpaceTec SpaceBall 2003", "SpaceTec SpaceBall 2003B",
+	"SpaceTec SpaceBall 2003C", "SpaceTec SpaceBall 3003", "SpaceTec SpaceBall SpaceController",
+	"SpaceTec SpaceBall 3003C", "SpaceTec SpaceBall 4000FLX", "SpaceTec SpaceBall 4000FLX Lefty" };
+
+/*
+ * Per-Ball data.
+ */
+
+struct spaceball {
+	struct input_dev *dev;
+	int idx;
+	int escape;
+	unsigned char data[SPACEBALL_MAX_LENGTH];
+	char phys[32];
+};
+
+/*
+ * spaceball_process_packet() decodes packets the driver receives from the
+ * SpaceBall.
+ */
+
+static void spaceball_process_packet(struct spaceball* spaceball)
+{
+	struct input_dev *dev = spaceball->dev;
+	unsigned char *data = spaceball->data;
+	int i;
+
+	if (spaceball->idx < 2) return;
+
+	switch (spaceball->data[0]) {
+
+		case 'D':					/* Ball data */
+			if (spaceball->idx != 15) return;
+			for (i = 0; i < 6; i++)
+				input_report_abs(dev, spaceball_axes[i],
+					(__s16)((data[2 * i + 3] << 8) | data[2 * i + 2]));
+			break;
+
+		case 'K':					/* Button data */
+			if (spaceball->idx != 3) return;
+			input_report_key(dev, BTN_1, (data[2] & 0x01) || (data[2] & 0x20));
+			input_report_key(dev, BTN_2, data[2] & 0x02);
+			input_report_key(dev, BTN_3, data[2] & 0x04);
+			input_report_key(dev, BTN_4, data[2] & 0x08);
+			input_report_key(dev, BTN_5, data[1] & 0x01);
+			input_report_key(dev, BTN_6, data[1] & 0x02);
+			input_report_key(dev, BTN_7, data[1] & 0x04);
+			input_report_key(dev, BTN_8, data[1] & 0x10);
+			break;
+
+		case '.':					/* Advanced button data */
+			if (spaceball->idx != 3) return;
+			input_report_key(dev, BTN_1, data[2] & 0x01);
+			input_report_key(dev, BTN_2, data[2] & 0x02);
+			input_report_key(dev, BTN_3, data[2] & 0x04);
+			input_report_key(dev, BTN_4, data[2] & 0x08);
+			input_report_key(dev, BTN_5, data[2] & 0x10);
+			input_report_key(dev, BTN_6, data[2] & 0x20);
+			input_report_key(dev, BTN_7, data[2] & 0x80);
+			input_report_key(dev, BTN_8, data[1] & 0x01);
+			input_report_key(dev, BTN_9, data[1] & 0x02);
+			input_report_key(dev, BTN_A, data[1] & 0x04);
+			input_report_key(dev, BTN_B, data[1] & 0x08);
+			input_report_key(dev, BTN_C, data[1] & 0x10);
+			input_report_key(dev, BTN_MODE, data[1] & 0x20);
+			break;
+
+		case 'E':					/* Device error */
+			spaceball->data[spaceball->idx - 1] = 0;
+			printk(KERN_ERR "spaceball: Device error. [%s]\n", spaceball->data + 1);
+			break;
+
+		case '?':					/* Bad command packet */
+			spaceball->data[spaceball->idx - 1] = 0;
+			printk(KERN_ERR "spaceball: Bad command. [%s]\n", spaceball->data + 1);
+			break;
+	}
+
+	input_sync(dev);
+}
+
+/*
+ * Spaceball 4000 FLX packets all start with a one letter packet-type decriptor,
+ * and end in 0x0d. It uses '^' as an escape for CR, XOFF and XON characters which
+ * can occur in the axis values.
+ */
+
+static irqreturn_t spaceball_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct spaceball *spaceball = serio_get_drvdata(serio);
+
+	switch (data) {
+		case 0xd:
+			spaceball_process_packet(spaceball);
+			spaceball->idx = 0;
+			spaceball->escape = 0;
+			break;
+		case '^':
+			if (!spaceball->escape) {
+				spaceball->escape = 1;
+				break;
+			}
+			spaceball->escape = 0;
+		case 'M':
+		case 'Q':
+		case 'S':
+			if (spaceball->escape) {
+				spaceball->escape = 0;
+				data &= 0x1f;
+			}
+		default:
+			if (spaceball->escape)
+				spaceball->escape = 0;
+			if (spaceball->idx < SPACEBALL_MAX_LENGTH)
+				spaceball->data[spaceball->idx++] = data;
+			break;
+	}
+	return IRQ_HANDLED;
+}
+
+/*
+ * spaceball_disconnect() is the opposite of spaceball_connect()
+ */
+
+static void spaceball_disconnect(struct serio *serio)
+{
+	struct spaceball* spaceball = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(spaceball->dev);
+	kfree(spaceball);
+}
+
+/*
+ * spaceball_connect() is the routine that is called when someone adds a
+ * new serio device that supports Spaceball protocol and registers it as
+ * an input device.
+ */
+
+static int spaceball_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct spaceball *spaceball;
+	struct input_dev *input_dev;
+	int err = -ENOMEM;
+	int i, id;
+
+	if ((id = serio->id.id) > SPACEBALL_MAX_ID)
+		return -ENODEV;
+
+	spaceball = kmalloc(sizeof(struct spaceball), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!spaceball || !input_dev)
+		goto fail1;
+
+	spaceball->dev = input_dev;
+	snprintf(spaceball->phys, sizeof(spaceball->phys), "%s/input0", serio->phys);
+
+	input_dev->name = spaceball_names[id];
+	input_dev->phys = spaceball->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_SPACEBALL;
+	input_dev->id.product = id;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	switch (id) {
+		case SPACEBALL_4000FLX:
+		case SPACEBALL_4000FLX_L:
+			input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_9);
+			input_dev->keybit[BIT_WORD(BTN_A)] |= BIT_MASK(BTN_A) |
+				BIT_MASK(BTN_B) | BIT_MASK(BTN_C) |
+				BIT_MASK(BTN_MODE);
+		default:
+			input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_2) |
+				BIT_MASK(BTN_3) | BIT_MASK(BTN_4) |
+				BIT_MASK(BTN_5) | BIT_MASK(BTN_6) |
+				BIT_MASK(BTN_7) | BIT_MASK(BTN_8);
+		case SPACEBALL_3003C:
+			input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_1) |
+				BIT_MASK(BTN_8);
+	}
+
+	for (i = 0; i < 3; i++) {
+		input_set_abs_params(input_dev, ABS_X + i, -8000, 8000, 8, 40);
+		input_set_abs_params(input_dev, ABS_RX + i, -1600, 1600, 2, 8);
+	}
+
+	serio_set_drvdata(serio, spaceball);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(spaceball->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(spaceball);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id spaceball_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_SPACEBALL,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, spaceball_serio_ids);
+
+static struct serio_driver spaceball_drv = {
+	.driver		= {
+		.name	= "spaceball",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= spaceball_serio_ids,
+	.interrupt	= spaceball_interrupt,
+	.connect	= spaceball_connect,
+	.disconnect	= spaceball_disconnect,
+};
+
+module_serio_driver(spaceball_drv);
diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c
new file mode 100644
index 0000000..f266782
--- /dev/null
+++ b/drivers/input/joystick/spaceorb.c
@@ -0,0 +1,239 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	David Thompson
+ */
+
+/*
+ * SpaceTec SpaceOrb 360 and Avenger 6dof controller driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *  Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"SpaceTec SpaceOrb 360 and Avenger 6dof controller driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define SPACEORB_MAX_LENGTH	64
+
+static int spaceorb_buttons[] = { BTN_TL, BTN_TR, BTN_Y, BTN_X, BTN_B, BTN_A };
+static int spaceorb_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ };
+
+/*
+ * Per-Orb data.
+ */
+
+struct spaceorb {
+	struct input_dev *dev;
+	int idx;
+	unsigned char data[SPACEORB_MAX_LENGTH];
+	char phys[32];
+};
+
+static unsigned char spaceorb_xor[] = "SpaceWare";
+
+static unsigned char *spaceorb_errors[] = { "EEPROM storing 0 failed", "Receive queue overflow", "Transmit queue timeout",
+		"Bad packet", "Power brown-out", "EEPROM checksum error", "Hardware fault" };
+
+/*
+ * spaceorb_process_packet() decodes packets the driver receives from the
+ * SpaceOrb.
+ */
+
+static void spaceorb_process_packet(struct spaceorb *spaceorb)
+{
+	struct input_dev *dev = spaceorb->dev;
+	unsigned char *data = spaceorb->data;
+	unsigned char c = 0;
+	int axes[6];
+	int i;
+
+	if (spaceorb->idx < 2) return;
+	for (i = 0; i < spaceorb->idx; i++) c ^= data[i];
+	if (c) return;
+
+	switch (data[0]) {
+
+		case 'R':				/* Reset packet */
+			spaceorb->data[spaceorb->idx - 1] = 0;
+			for (i = 1; i < spaceorb->idx && spaceorb->data[i] == ' '; i++);
+			printk(KERN_INFO "input: %s [%s] is %s\n",
+				 dev->name, spaceorb->data + i, spaceorb->phys);
+			break;
+
+		case 'D':				/* Ball + button data */
+			if (spaceorb->idx != 12) return;
+			for (i = 0; i < 9; i++) spaceorb->data[i+2] ^= spaceorb_xor[i];
+			axes[0] = ( data[2]	 << 3) | (data[ 3] >> 4);
+			axes[1] = ((data[3] & 0x0f) << 6) | (data[ 4] >> 1);
+			axes[2] = ((data[4] & 0x01) << 9) | (data[ 5] << 2) | (data[4] >> 5);
+			axes[3] = ((data[6] & 0x1f) << 5) | (data[ 7] >> 2);
+			axes[4] = ((data[7] & 0x03) << 8) | (data[ 8] << 1) | (data[7] >> 6);
+			axes[5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3);
+			for (i = 0; i < 6; i++)
+				input_report_abs(dev, spaceorb_axes[i], axes[i] - ((axes[i] & 0x200) ? 1024 : 0));
+			for (i = 0; i < 6; i++)
+				input_report_key(dev, spaceorb_buttons[i], (data[1] >> i) & 1);
+			break;
+
+		case 'K':				/* Button data */
+			if (spaceorb->idx != 5) return;
+			for (i = 0; i < 6; i++)
+				input_report_key(dev, spaceorb_buttons[i], (data[2] >> i) & 1);
+
+			break;
+
+		case 'E':				/* Error packet */
+			if (spaceorb->idx != 4) return;
+			printk(KERN_ERR "spaceorb: Device error. [ ");
+			for (i = 0; i < 7; i++) if (data[1] & (1 << i)) printk("%s ", spaceorb_errors[i]);
+			printk("]\n");
+			break;
+	}
+
+	input_sync(dev);
+}
+
+static irqreturn_t spaceorb_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct spaceorb* spaceorb = serio_get_drvdata(serio);
+
+	if (~data & 0x80) {
+		if (spaceorb->idx) spaceorb_process_packet(spaceorb);
+		spaceorb->idx = 0;
+	}
+	if (spaceorb->idx < SPACEORB_MAX_LENGTH)
+		spaceorb->data[spaceorb->idx++] = data & 0x7f;
+	return IRQ_HANDLED;
+}
+
+/*
+ * spaceorb_disconnect() is the opposite of spaceorb_connect()
+ */
+
+static void spaceorb_disconnect(struct serio *serio)
+{
+	struct spaceorb* spaceorb = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(spaceorb->dev);
+	kfree(spaceorb);
+}
+
+/*
+ * spaceorb_connect() is the routine that is called when someone adds a
+ * new serio device that supports SpaceOrb/Avenger protocol and registers
+ * it as an input device.
+ */
+
+static int spaceorb_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct spaceorb *spaceorb;
+	struct input_dev *input_dev;
+	int err = -ENOMEM;
+	int i;
+
+	spaceorb = kzalloc(sizeof(struct spaceorb), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!spaceorb || !input_dev)
+		goto fail1;
+
+	spaceorb->dev = input_dev;
+	snprintf(spaceorb->phys, sizeof(spaceorb->phys), "%s/input0", serio->phys);
+
+	input_dev->name = "SpaceTec SpaceOrb 360 / Avenger";
+	input_dev->phys = spaceorb->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_SPACEORB;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	for (i = 0; i < 6; i++)
+		set_bit(spaceorb_buttons[i], input_dev->keybit);
+
+	for (i = 0; i < 6; i++)
+		input_set_abs_params(input_dev, spaceorb_axes[i], -508, 508, 0, 0);
+
+	serio_set_drvdata(serio, spaceorb);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(spaceorb->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(spaceorb);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id spaceorb_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_SPACEORB,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, spaceorb_serio_ids);
+
+static struct serio_driver spaceorb_drv = {
+	.driver		= {
+		.name	= "spaceorb",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= spaceorb_serio_ids,
+	.interrupt	= spaceorb_interrupt,
+	.connect	= spaceorb_connect,
+	.disconnect	= spaceorb_disconnect,
+};
+
+module_serio_driver(spaceorb_drv);
diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c
new file mode 100644
index 0000000..099c6d7
--- /dev/null
+++ b/drivers/input/joystick/stinger.c
@@ -0,0 +1,210 @@
+/*
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *  Copyright (c) 2000 Mark Fletcher
+ */
+
+/*
+ * Gravis Stinger gamepad driver for Linux
+ */
+
+/*
+ * This program is free warftware; 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *  Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Gravis Stinger gamepad driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define STINGER_MAX_LENGTH 8
+
+/*
+ * Per-Stinger data.
+ */
+
+struct stinger {
+	struct input_dev *dev;
+	int idx;
+	unsigned char data[STINGER_MAX_LENGTH];
+	char phys[32];
+};
+
+/*
+ * stinger_process_packet() decodes packets the driver receives from the
+ * Stinger. It updates the data accordingly.
+ */
+
+static void stinger_process_packet(struct stinger *stinger)
+{
+	struct input_dev *dev = stinger->dev;
+	unsigned char *data = stinger->data;
+
+	if (!stinger->idx) return;
+
+	input_report_key(dev, BTN_A,	  ((data[0] & 0x20) >> 5));
+	input_report_key(dev, BTN_B,	  ((data[0] & 0x10) >> 4));
+	input_report_key(dev, BTN_C,	  ((data[0] & 0x08) >> 3));
+	input_report_key(dev, BTN_X,	  ((data[0] & 0x04) >> 2));
+	input_report_key(dev, BTN_Y,	  ((data[3] & 0x20) >> 5));
+	input_report_key(dev, BTN_Z,	  ((data[3] & 0x10) >> 4));
+	input_report_key(dev, BTN_TL,     ((data[3] & 0x08) >> 3));
+	input_report_key(dev, BTN_TR,     ((data[3] & 0x04) >> 2));
+	input_report_key(dev, BTN_SELECT, ((data[3] & 0x02) >> 1));
+	input_report_key(dev, BTN_START,   (data[3] & 0x01));
+
+	input_report_abs(dev, ABS_X, (data[1] & 0x3F) - ((data[0] & 0x01) << 6));
+	input_report_abs(dev, ABS_Y, ((data[0] & 0x02) << 5) - (data[2] & 0x3F));
+
+	input_sync(dev);
+
+	return;
+}
+
+/*
+ * stinger_interrupt() is called by the low level driver when characters
+ * are ready for us. We then buffer them for further processing, or call the
+ * packet processing routine.
+ */
+
+static irqreturn_t stinger_interrupt(struct serio *serio,
+	unsigned char data, unsigned int flags)
+{
+	struct stinger *stinger = serio_get_drvdata(serio);
+
+	/* All Stinger packets are 4 bytes */
+
+	if (stinger->idx < STINGER_MAX_LENGTH)
+		stinger->data[stinger->idx++] = data;
+
+	if (stinger->idx == 4) {
+		stinger_process_packet(stinger);
+		stinger->idx = 0;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * stinger_disconnect() is the opposite of stinger_connect()
+ */
+
+static void stinger_disconnect(struct serio *serio)
+{
+	struct stinger *stinger = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(stinger->dev);
+	kfree(stinger);
+}
+
+/*
+ * stinger_connect() is the routine that is called when someone adds a
+ * new serio device that supports Stinger protocol and registers it as
+ * an input device.
+ */
+
+static int stinger_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct stinger *stinger;
+	struct input_dev *input_dev;
+	int err = -ENOMEM;
+
+	stinger = kmalloc(sizeof(struct stinger), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!stinger || !input_dev)
+		goto fail1;
+
+	stinger->dev = input_dev;
+	snprintf(stinger->phys, sizeof(stinger->phys), "%s/serio0", serio->phys);
+
+	input_dev->name = "Gravis Stinger";
+	input_dev->phys = stinger->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_STINGER;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_A)] = BIT_MASK(BTN_A) | BIT_MASK(BTN_B) |
+		BIT_MASK(BTN_C) | BIT_MASK(BTN_X) | BIT_MASK(BTN_Y) |
+		BIT_MASK(BTN_Z) | BIT_MASK(BTN_TL) | BIT_MASK(BTN_TR) |
+		BIT_MASK(BTN_START) | BIT_MASK(BTN_SELECT);
+	input_set_abs_params(input_dev, ABS_X, -64, 64, 0, 4);
+	input_set_abs_params(input_dev, ABS_Y, -64, 64, 0, 4);
+
+	serio_set_drvdata(serio, stinger);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(stinger->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(stinger);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id stinger_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_STINGER,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, stinger_serio_ids);
+
+static struct serio_driver stinger_drv = {
+	.driver		= {
+		.name	= "stinger",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= stinger_serio_ids,
+	.interrupt	= stinger_interrupt,
+	.connect	= stinger_connect,
+	.disconnect	= stinger_disconnect,
+};
+
+module_serio_driver(stinger_drv);
diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c
new file mode 100644
index 0000000..7e17cde
--- /dev/null
+++ b/drivers/input/joystick/tmdc.c
@@ -0,0 +1,438 @@
+/*
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ *
+ *   Based on the work of:
+ *	Trystan Larey-Williams
+ */
+
+/*
+ * ThrustMaster DirectConnect (BSP) joystick family driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_DESC	"ThrustMaster DirectConnect joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define TMDC_MAX_START		600	/* 600 us */
+#define TMDC_MAX_STROBE		60	/* 60 us */
+#define TMDC_MAX_LENGTH		13
+
+#define TMDC_MODE_M3DI		1
+#define TMDC_MODE_3DRP		3
+#define TMDC_MODE_AT		4
+#define TMDC_MODE_FM		8
+#define TMDC_MODE_FGP		163
+
+#define TMDC_BYTE_ID		10
+#define TMDC_BYTE_REV		11
+#define TMDC_BYTE_DEF		12
+
+#define TMDC_ABS		7
+#define TMDC_ABS_HAT		4
+#define TMDC_BTN		16
+
+static const unsigned char tmdc_byte_a[16] = { 0, 1, 3, 4, 6, 7 };
+static const unsigned char tmdc_byte_d[16] = { 2, 5, 8, 9 };
+
+static const signed char tmdc_abs[TMDC_ABS] =
+	{ ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE, ABS_RX, ABS_RY, ABS_RZ };
+static const signed char tmdc_abs_hat[TMDC_ABS_HAT] =
+	{ ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
+static const signed char tmdc_abs_at[TMDC_ABS] =
+	{ ABS_X, ABS_Y, ABS_RUDDER, -1, ABS_THROTTLE };
+static const signed char tmdc_abs_fm[TMDC_ABS] =
+	{ ABS_RX, ABS_RY, ABS_X, ABS_Y };
+
+static const short tmdc_btn_pad[TMDC_BTN] =
+	{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_START, BTN_SELECT, BTN_TL, BTN_TR };
+static const short tmdc_btn_joy[TMDC_BTN] =
+	{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_THUMB2, BTN_PINKIE,
+	  BTN_BASE3, BTN_BASE4, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z };
+static const short tmdc_btn_fm[TMDC_BTN] =
+        { BTN_TRIGGER, BTN_C, BTN_B, BTN_A, BTN_THUMB, BTN_X, BTN_Y, BTN_Z, BTN_TOP, BTN_TOP2 };
+static const short tmdc_btn_at[TMDC_BTN] =
+        { BTN_TRIGGER, BTN_THUMB2, BTN_PINKIE, BTN_THUMB, BTN_BASE6, BTN_BASE5, BTN_BASE4,
+          BTN_BASE3, BTN_BASE2, BTN_BASE };
+
+static const struct {
+        int x;
+        int y;
+} tmdc_hat_to_axis[] = {{ 0, 0}, { 1, 0}, { 0,-1}, {-1, 0}, { 0, 1}};
+
+static const struct tmdc_model {
+	unsigned char id;
+	const char *name;
+	char abs;
+	char hats;
+	char btnc[4];
+	char btno[4];
+	const signed char *axes;
+	const short *buttons;
+} tmdc_models[] = {
+	{   1, "ThrustMaster Millenium 3D Inceptor",	  6, 2, { 4, 2 }, { 4, 6 }, tmdc_abs, tmdc_btn_joy },
+	{   3, "ThrustMaster Rage 3D Gamepad",		  2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad },
+	{   4, "ThrustMaster Attack Throttle",		  5, 2, { 4, 6 }, { 4, 2 }, tmdc_abs_at, tmdc_btn_at },
+	{   8, "ThrustMaster FragMaster",		  4, 0, { 8, 2 }, { 0, 0 }, tmdc_abs_fm, tmdc_btn_fm },
+	{ 163, "Thrustmaster Fusion GamePad",		  2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad },
+	{   0, "Unknown %d-axis, %d-button TM device %d", 0, 0, { 0, 0 }, { 0, 0 }, tmdc_abs, tmdc_btn_joy }
+};
+
+
+struct tmdc_port {
+	struct input_dev *dev;
+	char name[64];
+	char phys[32];
+	int mode;
+	const signed char *abs;
+	const short *btn;
+	unsigned char absc;
+	unsigned char btnc[4];
+	unsigned char btno[4];
+};
+
+struct tmdc {
+	struct gameport *gameport;
+	struct tmdc_port *port[2];
+#if 0
+	struct input_dev *dev[2];
+	char name[2][64];
+	char phys[2][32];
+	int mode[2];
+	signed char *abs[2];
+	short *btn[2];
+	unsigned char absc[2];
+	unsigned char btnc[2][4];
+	unsigned char btno[2][4];
+#endif
+	int reads;
+	int bads;
+	unsigned char exists;
+};
+
+/*
+ * tmdc_read_packet() reads a ThrustMaster packet.
+ */
+
+static int tmdc_read_packet(struct gameport *gameport, unsigned char data[2][TMDC_MAX_LENGTH])
+{
+	unsigned char u, v, w, x;
+	unsigned long flags;
+	int i[2], j[2], t[2], p, k;
+
+	p = gameport_time(gameport, TMDC_MAX_STROBE);
+
+	for (k = 0; k < 2; k++) {
+		t[k] = gameport_time(gameport, TMDC_MAX_START);
+		i[k] = j[k] = 0;
+	}
+
+	local_irq_save(flags);
+	gameport_trigger(gameport);
+
+	w = gameport_read(gameport) >> 4;
+
+	do {
+		x = w;
+		w = gameport_read(gameport) >> 4;
+
+		for (k = 0, v = w, u = x; k < 2; k++, v >>= 2, u >>= 2) {
+			if (~v & u & 2) {
+				if (t[k] <= 0 || i[k] >= TMDC_MAX_LENGTH) continue;
+				t[k] = p;
+				if (j[k] == 0) {				 /* Start bit */
+					if (~v & 1) t[k] = 0;
+					data[k][i[k]] = 0; j[k]++; continue;
+				}
+				if (j[k] == 9) {				/* Stop bit */
+					if (v & 1) t[k] = 0;
+					j[k] = 0; i[k]++; continue;
+				}
+				data[k][i[k]] |= (~v & 1) << (j[k]++ - 1);	/* Data bit */
+			}
+			t[k]--;
+		}
+	} while (t[0] > 0 || t[1] > 0);
+
+	local_irq_restore(flags);
+
+	return (i[0] == TMDC_MAX_LENGTH) | ((i[1] == TMDC_MAX_LENGTH) << 1);
+}
+
+static int tmdc_parse_packet(struct tmdc_port *port, unsigned char *data)
+{
+	int i, k, l;
+
+	if (data[TMDC_BYTE_ID] != port->mode)
+		return -1;
+
+	for (i = 0; i < port->absc; i++) {
+		if (port->abs[i] < 0)
+			return 0;
+
+		input_report_abs(port->dev, port->abs[i], data[tmdc_byte_a[i]]);
+	}
+
+	switch (port->mode) {
+
+		case TMDC_MODE_M3DI:
+
+			i = tmdc_byte_d[0];
+			input_report_abs(port->dev, ABS_HAT0X, ((data[i] >> 3) & 1) - ((data[i] >> 1) & 1));
+			input_report_abs(port->dev, ABS_HAT0Y, ((data[i] >> 2) & 1) - ( data[i]       & 1));
+			break;
+
+		case TMDC_MODE_AT:
+
+			i = tmdc_byte_a[3];
+			input_report_abs(port->dev, ABS_HAT0X, tmdc_hat_to_axis[(data[i] - 141) / 25].x);
+			input_report_abs(port->dev, ABS_HAT0Y, tmdc_hat_to_axis[(data[i] - 141) / 25].y);
+			break;
+
+	}
+
+	for (k = l = 0; k < 4; k++) {
+		for (i = 0; i < port->btnc[k]; i++)
+			input_report_key(port->dev, port->btn[i + l],
+				((data[tmdc_byte_d[k]] >> (i + port->btno[k])) & 1));
+		l += port->btnc[k];
+	}
+
+	input_sync(port->dev);
+
+	return 0;
+}
+
+/*
+ * tmdc_poll() reads and analyzes ThrustMaster joystick data.
+ */
+
+static void tmdc_poll(struct gameport *gameport)
+{
+	unsigned char data[2][TMDC_MAX_LENGTH];
+	struct tmdc *tmdc = gameport_get_drvdata(gameport);
+	unsigned char r, bad = 0;
+	int i;
+
+	tmdc->reads++;
+
+	if ((r = tmdc_read_packet(tmdc->gameport, data)) != tmdc->exists)
+		bad = 1;
+	else {
+		for (i = 0; i < 2; i++) {
+			if (r & (1 << i) & tmdc->exists) {
+
+				if (tmdc_parse_packet(tmdc->port[i], data[i]))
+					bad = 1;
+			}
+		}
+	}
+
+	tmdc->bads += bad;
+}
+
+static int tmdc_open(struct input_dev *dev)
+{
+	struct tmdc *tmdc = input_get_drvdata(dev);
+
+	gameport_start_polling(tmdc->gameport);
+	return 0;
+}
+
+static void tmdc_close(struct input_dev *dev)
+{
+	struct tmdc *tmdc = input_get_drvdata(dev);
+
+	gameport_stop_polling(tmdc->gameport);
+}
+
+static int tmdc_setup_port(struct tmdc *tmdc, int idx, unsigned char *data)
+{
+	const struct tmdc_model *model;
+	struct tmdc_port *port;
+	struct input_dev *input_dev;
+	int i, j, b = 0;
+	int err;
+
+	tmdc->port[idx] = port = kzalloc(sizeof (struct tmdc_port), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!port || !input_dev) {
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	port->mode = data[TMDC_BYTE_ID];
+
+	for (model = tmdc_models; model->id && model->id != port->mode; model++)
+		/* empty */;
+
+	port->abs = model->axes;
+	port->btn = model->buttons;
+
+	if (!model->id) {
+		port->absc = data[TMDC_BYTE_DEF] >> 4;
+		for (i = 0; i < 4; i++)
+			port->btnc[i] = i < (data[TMDC_BYTE_DEF] & 0xf) ? 8 : 0;
+	} else {
+		port->absc = model->abs;
+		for (i = 0; i < 4; i++)
+			port->btnc[i] = model->btnc[i];
+	}
+
+	for (i = 0; i < 4; i++)
+		port->btno[i] = model->btno[i];
+
+	snprintf(port->name, sizeof(port->name), model->name,
+		 port->absc, (data[TMDC_BYTE_DEF] & 0xf) << 3, port->mode);
+	snprintf(port->phys, sizeof(port->phys), "%s/input%d", tmdc->gameport->phys, i);
+
+	port->dev = input_dev;
+
+	input_dev->name = port->name;
+	input_dev->phys = port->phys;
+	input_dev->id.bustype = BUS_GAMEPORT;
+	input_dev->id.vendor = GAMEPORT_ID_VENDOR_THRUSTMASTER;
+	input_dev->id.product = model->id;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &tmdc->gameport->dev;
+
+	input_set_drvdata(input_dev, tmdc);
+
+	input_dev->open = tmdc_open;
+	input_dev->close = tmdc_close;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	for (i = 0; i < port->absc && i < TMDC_ABS; i++)
+		if (port->abs[i] >= 0)
+			input_set_abs_params(input_dev, port->abs[i], 8, 248, 2, 4);
+
+	for (i = 0; i < model->hats && i < TMDC_ABS_HAT; i++)
+		input_set_abs_params(input_dev, tmdc_abs_hat[i], -1, 1, 0, 0);
+
+	for (i = 0; i < 4; i++) {
+		for (j = 0; j < port->btnc[i] && j < TMDC_BTN; j++)
+			set_bit(port->btn[j + b], input_dev->keybit);
+		b += port->btnc[i];
+	}
+
+	err = input_register_device(port->dev);
+	if (err)
+		goto fail;
+
+	return 0;
+
+ fail:	input_free_device(input_dev);
+	kfree(port);
+	return err;
+}
+
+/*
+ * tmdc_probe() probes for ThrustMaster type joysticks.
+ */
+
+static int tmdc_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	unsigned char data[2][TMDC_MAX_LENGTH];
+	struct tmdc *tmdc;
+	int i;
+	int err;
+
+	if (!(tmdc = kzalloc(sizeof(struct tmdc), GFP_KERNEL)))
+		return -ENOMEM;
+
+	tmdc->gameport = gameport;
+
+	gameport_set_drvdata(gameport, tmdc);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	if (!(tmdc->exists = tmdc_read_packet(gameport, data))) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, tmdc_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	for (i = 0; i < 2; i++) {
+		if (tmdc->exists & (1 << i)) {
+
+			err = tmdc_setup_port(tmdc, i, data[i]);
+			if (err)
+				goto fail3;
+		}
+	}
+
+	return 0;
+
+ fail3: while (--i >= 0) {
+		if (tmdc->port[i]) {
+			input_unregister_device(tmdc->port[i]->dev);
+			kfree(tmdc->port[i]);
+		}
+	}
+ fail2:	gameport_close(gameport);
+ fail1:	gameport_set_drvdata(gameport, NULL);
+	kfree(tmdc);
+	return err;
+}
+
+static void tmdc_disconnect(struct gameport *gameport)
+{
+	struct tmdc *tmdc = gameport_get_drvdata(gameport);
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		if (tmdc->port[i]) {
+			input_unregister_device(tmdc->port[i]->dev);
+			kfree(tmdc->port[i]);
+		}
+	}
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(tmdc);
+}
+
+static struct gameport_driver tmdc_drv = {
+	.driver		= {
+		.name	= "tmdc",
+		.owner	= THIS_MODULE,
+	},
+	.description	= DRIVER_DESC,
+	.connect	= tmdc_connect,
+	.disconnect	= tmdc_disconnect,
+};
+
+module_gameport_driver(tmdc_drv);
diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c
new file mode 100644
index 0000000..77f575d
--- /dev/null
+++ b/drivers/input/joystick/turbografx.c
@@ -0,0 +1,330 @@
+/*
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Steffen Schwenke
+ */
+
+/*
+ * TurboGraFX parallel port interface driver for Linux.
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("TurboGraFX parallel port interface driver");
+MODULE_LICENSE("GPL");
+
+#define TGFX_MAX_PORTS		3
+#define TGFX_MAX_DEVICES	7
+
+struct tgfx_config {
+	int args[TGFX_MAX_DEVICES + 1];
+	unsigned int nargs;
+};
+
+static struct tgfx_config tgfx_cfg[TGFX_MAX_PORTS];
+
+module_param_array_named(map, tgfx_cfg[0].args, int, &tgfx_cfg[0].nargs, 0);
+MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<js1>,<js2>,..<js7>");
+module_param_array_named(map2, tgfx_cfg[1].args, int, &tgfx_cfg[1].nargs, 0);
+MODULE_PARM_DESC(map2, "Describes second set of devices");
+module_param_array_named(map3, tgfx_cfg[2].args, int, &tgfx_cfg[2].nargs, 0);
+MODULE_PARM_DESC(map3, "Describes third set of devices");
+
+#define TGFX_REFRESH_TIME	HZ/100	/* 10 ms */
+
+#define TGFX_TRIGGER		0x08
+#define TGFX_UP			0x10
+#define TGFX_DOWN		0x20
+#define TGFX_LEFT		0x40
+#define TGFX_RIGHT		0x80
+
+#define TGFX_THUMB		0x02
+#define TGFX_THUMB2		0x04
+#define TGFX_TOP		0x01
+#define TGFX_TOP2		0x08
+
+static int tgfx_buttons[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2 };
+
+static struct tgfx {
+	struct pardevice *pd;
+	struct timer_list timer;
+	struct input_dev *dev[TGFX_MAX_DEVICES];
+	char name[TGFX_MAX_DEVICES][64];
+	char phys[TGFX_MAX_DEVICES][32];
+	int sticks;
+	int used;
+	int parportno;
+	struct mutex sem;
+} *tgfx_base[TGFX_MAX_PORTS];
+
+/*
+ * tgfx_timer() reads and analyzes TurboGraFX joystick data.
+ */
+
+static void tgfx_timer(unsigned long private)
+{
+	struct tgfx *tgfx = (void *) private;
+	struct input_dev *dev;
+	int data1, data2, i;
+
+	for (i = 0; i < 7; i++)
+		if (tgfx->sticks & (1 << i)) {
+
+			dev = tgfx->dev[i];
+
+			parport_write_data(tgfx->pd->port, ~(1 << i));
+			data1 = parport_read_status(tgfx->pd->port) ^ 0x7f;
+			data2 = parport_read_control(tgfx->pd->port) ^ 0x04;	/* CAVEAT parport */
+
+			input_report_abs(dev, ABS_X, !!(data1 & TGFX_RIGHT) - !!(data1 & TGFX_LEFT));
+			input_report_abs(dev, ABS_Y, !!(data1 & TGFX_DOWN ) - !!(data1 & TGFX_UP  ));
+
+			input_report_key(dev, BTN_TRIGGER, (data1 & TGFX_TRIGGER));
+			input_report_key(dev, BTN_THUMB,   (data2 & TGFX_THUMB  ));
+			input_report_key(dev, BTN_THUMB2,  (data2 & TGFX_THUMB2 ));
+			input_report_key(dev, BTN_TOP,     (data2 & TGFX_TOP    ));
+			input_report_key(dev, BTN_TOP2,    (data2 & TGFX_TOP2   ));
+
+			input_sync(dev);
+		}
+
+	mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME);
+}
+
+static int tgfx_open(struct input_dev *dev)
+{
+	struct tgfx *tgfx = input_get_drvdata(dev);
+	int err;
+
+	err = mutex_lock_interruptible(&tgfx->sem);
+	if (err)
+		return err;
+
+	if (!tgfx->used++) {
+		parport_claim(tgfx->pd);
+		parport_write_control(tgfx->pd->port, 0x04);
+		mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME);
+	}
+
+	mutex_unlock(&tgfx->sem);
+	return 0;
+}
+
+static void tgfx_close(struct input_dev *dev)
+{
+	struct tgfx *tgfx = input_get_drvdata(dev);
+
+	mutex_lock(&tgfx->sem);
+	if (!--tgfx->used) {
+		del_timer_sync(&tgfx->timer);
+		parport_write_control(tgfx->pd->port, 0x00);
+		parport_release(tgfx->pd);
+	}
+	mutex_unlock(&tgfx->sem);
+}
+
+
+
+/*
+ * tgfx_probe() probes for tg gamepads.
+ */
+
+static void tgfx_attach(struct parport *pp)
+{
+	struct tgfx *tgfx;
+	struct input_dev *input_dev;
+	struct pardevice *pd;
+	int i, j, port_idx;
+	int *n_buttons, n_devs;
+	struct pardev_cb tgfx_parport_cb;
+
+	for (port_idx = 0; port_idx < TGFX_MAX_PORTS; port_idx++) {
+		if (tgfx_cfg[port_idx].nargs == 0 ||
+		    tgfx_cfg[port_idx].args[0] < 0)
+			continue;
+		if (tgfx_cfg[port_idx].args[0] == pp->number)
+			break;
+	}
+
+	if (port_idx == TGFX_MAX_PORTS) {
+		pr_debug("Not using parport%d.\n", pp->number);
+		return;
+	}
+	n_buttons = tgfx_cfg[port_idx].args + 1;
+	n_devs = tgfx_cfg[port_idx].nargs - 1;
+
+	memset(&tgfx_parport_cb, 0, sizeof(tgfx_parport_cb));
+	tgfx_parport_cb.flags = PARPORT_FLAG_EXCL;
+
+	pd = parport_register_dev_model(pp, "turbografx", &tgfx_parport_cb,
+					port_idx);
+	if (!pd) {
+		pr_err("parport busy already - lp.o loaded?\n");
+		return;
+	}
+
+	tgfx = kzalloc(sizeof(struct tgfx), GFP_KERNEL);
+	if (!tgfx) {
+		printk(KERN_ERR "turbografx.c: Not enough memory\n");
+		goto err_unreg_pardev;
+	}
+
+	mutex_init(&tgfx->sem);
+	tgfx->pd = pd;
+	tgfx->parportno = pp->number;
+	init_timer(&tgfx->timer);
+	tgfx->timer.data = (long) tgfx;
+	tgfx->timer.function = tgfx_timer;
+
+	for (i = 0; i < n_devs; i++) {
+		if (n_buttons[i] < 1)
+			continue;
+
+		if (n_buttons[i] > ARRAY_SIZE(tgfx_buttons)) {
+			printk(KERN_ERR "turbografx.c: Invalid number of buttons %d\n", n_buttons[i]);
+			goto err_unreg_devs;
+		}
+
+		tgfx->dev[i] = input_dev = input_allocate_device();
+		if (!input_dev) {
+			printk(KERN_ERR "turbografx.c: Not enough memory for input device\n");
+			goto err_unreg_devs;
+		}
+
+		tgfx->sticks |= (1 << i);
+		snprintf(tgfx->name[i], sizeof(tgfx->name[i]),
+			 "TurboGraFX %d-button Multisystem joystick", n_buttons[i]);
+		snprintf(tgfx->phys[i], sizeof(tgfx->phys[i]),
+			 "%s/input%d", tgfx->pd->port->name, i);
+
+		input_dev->name = tgfx->name[i];
+		input_dev->phys = tgfx->phys[i];
+		input_dev->id.bustype = BUS_PARPORT;
+		input_dev->id.vendor = 0x0003;
+		input_dev->id.product = n_buttons[i];
+		input_dev->id.version = 0x0100;
+
+		input_set_drvdata(input_dev, tgfx);
+
+		input_dev->open = tgfx_open;
+		input_dev->close = tgfx_close;
+
+		input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+		input_set_abs_params(input_dev, ABS_X, -1, 1, 0, 0);
+		input_set_abs_params(input_dev, ABS_Y, -1, 1, 0, 0);
+
+		for (j = 0; j < n_buttons[i]; j++)
+			set_bit(tgfx_buttons[j], input_dev->keybit);
+
+		if (input_register_device(tgfx->dev[i]))
+			goto err_free_dev;
+	}
+
+        if (!tgfx->sticks) {
+		printk(KERN_ERR "turbografx.c: No valid devices specified\n");
+		goto err_free_tgfx;
+        }
+
+	tgfx_base[port_idx] = tgfx;
+	return;
+
+ err_free_dev:
+	input_free_device(tgfx->dev[i]);
+ err_unreg_devs:
+	while (--i >= 0)
+		if (tgfx->dev[i])
+			input_unregister_device(tgfx->dev[i]);
+ err_free_tgfx:
+	kfree(tgfx);
+ err_unreg_pardev:
+	parport_unregister_device(pd);
+}
+
+static void tgfx_detach(struct parport *port)
+{
+	int i;
+	struct tgfx *tgfx;
+
+	for (i = 0; i < TGFX_MAX_PORTS; i++) {
+		if (tgfx_base[i] && tgfx_base[i]->parportno == port->number)
+			break;
+	}
+
+	if (i == TGFX_MAX_PORTS)
+		return;
+
+	tgfx = tgfx_base[i];
+	tgfx_base[i] = NULL;
+
+	for (i = 0; i < TGFX_MAX_DEVICES; i++)
+		if (tgfx->dev[i])
+			input_unregister_device(tgfx->dev[i]);
+	parport_unregister_device(tgfx->pd);
+	kfree(tgfx);
+}
+
+static struct parport_driver tgfx_parport_driver = {
+	.name = "turbografx",
+	.match_port = tgfx_attach,
+	.detach = tgfx_detach,
+	.devmodel = true,
+};
+
+static int __init tgfx_init(void)
+{
+	int i;
+	int have_dev = 0;
+
+	for (i = 0; i < TGFX_MAX_PORTS; i++) {
+		if (tgfx_cfg[i].nargs == 0 || tgfx_cfg[i].args[0] < 0)
+			continue;
+
+		if (tgfx_cfg[i].nargs < 2) {
+			printk(KERN_ERR "turbografx.c: at least one joystick must be specified\n");
+			return -EINVAL;
+		}
+
+		have_dev = 1;
+	}
+
+	if (!have_dev)
+		return -ENODEV;
+
+	return parport_register_driver(&tgfx_parport_driver);
+}
+
+static void __exit tgfx_exit(void)
+{
+	parport_unregister_driver(&tgfx_parport_driver);
+}
+
+module_init(tgfx_init);
+module_exit(tgfx_exit);
diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c
new file mode 100644
index 0000000..7f7e5ab
--- /dev/null
+++ b/drivers/input/joystick/twidjoy.c
@@ -0,0 +1,259 @@
+/*
+ *  Copyright (c) 2001 Arndt Schoenewald
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *  Copyright (c) 2000 Mark Fletcher
+ *
+ *  Sponsored by Quelltext AG (http://www.quelltext-ag.de), Dortmund, Germany
+ */
+
+/*
+ * Driver to use Handykey's Twiddler (the first edition, i.e. the one with
+ * the RS232 interface) as a joystick under Linux
+ *
+ * The Twiddler is a one-handed chording keyboard featuring twelve buttons on
+ * the front, six buttons on the top, and a built-in tilt sensor. The buttons
+ * on the front, which are grouped as four rows of three buttons, are pressed
+ * by the four fingers (this implies only one button per row can be held down
+ * at the same time) and the buttons on the top are for the thumb. The tilt
+ * sensor delivers X and Y axis data depending on how the Twiddler is held.
+ * Additional information can be found at http://www.handykey.com.
+ *
+ * This driver does not use the Twiddler for its intended purpose, i.e. as
+ * a chording keyboard, but as a joystick: pressing and releasing a button
+ * immediately sends a corresponding button event, and tilting it generates
+ * corresponding ABS_X and ABS_Y events. This turns the Twiddler into a game
+ * controller with amazing 18 buttons :-)
+ *
+ * Note: The Twiddler2 (the successor of the Twiddler that connects directly
+ * to the PS/2 keyboard and mouse ports) is NOT supported by this driver!
+ *
+ * For questions or feedback regarding this driver module please contact:
+ * Arndt Schoenewald <arndt@quelltext.com>
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Handykey Twiddler keyboard as a joystick driver"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define TWIDJOY_MAX_LENGTH 5
+
+static struct twidjoy_button_spec {
+	int bitshift;
+	int bitmask;
+	int buttons[3];
+}
+twidjoy_buttons[] = {
+	{  0, 3, { BTN_A,      BTN_B,     BTN_C    } },
+	{  2, 3, { BTN_X,      BTN_Y,     BTN_Z    } },
+	{  4, 3, { BTN_TL,     BTN_TR,    BTN_TR2  } },
+	{  6, 3, { BTN_SELECT, BTN_START, BTN_MODE } },
+	{  8, 1, { BTN_BASE5                       } },
+	{  9, 1, { BTN_BASE                        } },
+	{ 10, 1, { BTN_BASE3                       } },
+	{ 11, 1, { BTN_BASE4                       } },
+	{ 12, 1, { BTN_BASE2                       } },
+	{ 13, 1, { BTN_BASE6                       } },
+	{ 0,  0, { 0                               } }
+};
+
+/*
+ * Per-Twiddler data.
+ */
+
+struct twidjoy {
+	struct input_dev *dev;
+	int idx;
+	unsigned char data[TWIDJOY_MAX_LENGTH];
+	char phys[32];
+};
+
+/*
+ * twidjoy_process_packet() decodes packets the driver receives from the
+ * Twiddler. It updates the data accordingly.
+ */
+
+static void twidjoy_process_packet(struct twidjoy *twidjoy)
+{
+	struct input_dev *dev = twidjoy->dev;
+	unsigned char *data = twidjoy->data;
+	struct twidjoy_button_spec *bp;
+	int button_bits, abs_x, abs_y;
+
+	button_bits = ((data[1] & 0x7f) << 7) | (data[0] & 0x7f);
+
+	for (bp = twidjoy_buttons; bp->bitmask; bp++) {
+		int value = (button_bits & (bp->bitmask << bp->bitshift)) >> bp->bitshift;
+		int i;
+
+		for (i = 0; i < bp->bitmask; i++)
+			input_report_key(dev, bp->buttons[i], i+1 == value);
+	}
+
+	abs_x = ((data[4] & 0x07) << 5) | ((data[3] & 0x7C) >> 2);
+	if (data[4] & 0x08) abs_x -= 256;
+
+	abs_y = ((data[3] & 0x01) << 7) | ((data[2] & 0x7F) >> 0);
+	if (data[3] & 0x02) abs_y -= 256;
+
+	input_report_abs(dev, ABS_X, -abs_x);
+	input_report_abs(dev, ABS_Y, +abs_y);
+
+	input_sync(dev);
+}
+
+/*
+ * twidjoy_interrupt() is called by the low level driver when characters
+ * are ready for us. We then buffer them for further processing, or call the
+ * packet processing routine.
+ */
+
+static irqreturn_t twidjoy_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+	struct twidjoy *twidjoy = serio_get_drvdata(serio);
+
+	/* All Twiddler packets are 5 bytes. The fact that the first byte
+	 * has a MSB of 0 and all other bytes have a MSB of 1 can be used
+	 * to check and regain sync. */
+
+	if ((data & 0x80) == 0)
+		twidjoy->idx = 0;	/* this byte starts a new packet */
+	else if (twidjoy->idx == 0)
+		return IRQ_HANDLED;	/* wrong MSB -- ignore this byte */
+
+	if (twidjoy->idx < TWIDJOY_MAX_LENGTH)
+		twidjoy->data[twidjoy->idx++] = data;
+
+	if (twidjoy->idx == TWIDJOY_MAX_LENGTH) {
+		twidjoy_process_packet(twidjoy);
+		twidjoy->idx = 0;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * twidjoy_disconnect() is the opposite of twidjoy_connect()
+ */
+
+static void twidjoy_disconnect(struct serio *serio)
+{
+	struct twidjoy *twidjoy = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(twidjoy->dev);
+	kfree(twidjoy);
+}
+
+/*
+ * twidjoy_connect() is the routine that is called when someone adds a
+ * new serio device. It looks for the Twiddler, and if found, registers
+ * it as an input device.
+ */
+
+static int twidjoy_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct twidjoy_button_spec *bp;
+	struct twidjoy *twidjoy;
+	struct input_dev *input_dev;
+	int err = -ENOMEM;
+	int i;
+
+	twidjoy = kzalloc(sizeof(struct twidjoy), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!twidjoy || !input_dev)
+		goto fail1;
+
+	twidjoy->dev = input_dev;
+	snprintf(twidjoy->phys, sizeof(twidjoy->phys), "%s/input0", serio->phys);
+
+	input_dev->name = "Handykey Twiddler";
+	input_dev->phys = twidjoy->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_TWIDJOY;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_set_abs_params(input_dev, ABS_X, -50, 50, 4, 4);
+	input_set_abs_params(input_dev, ABS_Y, -50, 50, 4, 4);
+
+	for (bp = twidjoy_buttons; bp->bitmask; bp++)
+		for (i = 0; i < bp->bitmask; i++)
+			set_bit(bp->buttons[i], input_dev->keybit);
+
+	serio_set_drvdata(serio, twidjoy);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(twidjoy->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(twidjoy);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id twidjoy_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_TWIDJOY,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, twidjoy_serio_ids);
+
+static struct serio_driver twidjoy_drv = {
+	.driver		= {
+		.name	= "twidjoy",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= twidjoy_serio_ids,
+	.interrupt	= twidjoy_interrupt,
+	.connect	= twidjoy_connect,
+	.disconnect	= twidjoy_disconnect,
+};
+
+module_serio_driver(twidjoy_drv);
diff --git a/drivers/input/joystick/walkera0701.c b/drivers/input/joystick/walkera0701.c
new file mode 100644
index 0000000..70a893a
--- /dev/null
+++ b/drivers/input/joystick/walkera0701.c
@@ -0,0 +1,314 @@
+/*
+ *  Parallel port to Walkera WK-0701 TX joystick
+ *
+ *  Copyright (c) 2008 Peter Popovec
+ *
+ *  More about driver:  <file:Documentation/input/walkera0701.txt>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define RESERVE 20000
+#define SYNC_PULSE 1306000
+#define BIN0_PULSE 288000
+#define BIN1_PULSE 438000
+
+#define ANALOG_MIN_PULSE 318000
+#define ANALOG_MAX_PULSE 878000
+#define ANALOG_DELTA 80000
+
+#define BIN_SAMPLE ((BIN0_PULSE + BIN1_PULSE) / 2)
+
+#define NO_SYNC 25
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+#include <linux/hrtimer.h>
+
+MODULE_AUTHOR("Peter Popovec <popovec@fei.tuke.sk>");
+MODULE_DESCRIPTION("Walkera WK-0701 TX as joystick");
+MODULE_LICENSE("GPL");
+
+static unsigned int walkera0701_pp_no;
+module_param_named(port, walkera0701_pp_no, int, 0);
+MODULE_PARM_DESC(port,
+		 "Parallel port adapter for Walkera WK-0701 TX (default is 0)");
+
+/*
+ * For now, only one device is supported, if somebody need more devices, code
+ * can be expanded, one struct walkera_dev per device must be allocated and
+ * set up by walkera0701_connect (release of device by walkera0701_disconnect)
+ */
+
+struct walkera_dev {
+	unsigned char buf[25];
+	u64 irq_time, irq_lasttime;
+	int counter;
+	int ack;
+
+	struct input_dev *input_dev;
+	struct hrtimer timer;
+
+	struct parport *parport;
+	struct pardevice *pardevice;
+};
+
+static struct walkera_dev w_dev;
+
+static inline void walkera0701_parse_frame(struct walkera_dev *w)
+{
+	int i;
+	int val1, val2, val3, val4, val5, val6, val7, val8;
+	int magic, magic_bit;
+	int crc1, crc2;
+
+	for (crc1 = crc2 = i = 0; i < 10; i++) {
+		crc1 += w->buf[i] & 7;
+		crc2 += (w->buf[i] & 8) >> 3;
+	}
+	if ((w->buf[10] & 7) != (crc1 & 7))
+		return;
+	if (((w->buf[10] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1))
+		return;
+	for (crc1 = crc2 = 0, i = 11; i < 23; i++) {
+		crc1 += w->buf[i] & 7;
+		crc2 += (w->buf[i] & 8) >> 3;
+	}
+	if ((w->buf[23] & 7) != (crc1 & 7))
+		return;
+	if (((w->buf[23] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1))
+		return;
+	val1 = ((w->buf[0] & 7) * 256 + w->buf[1] * 16 + w->buf[2]) >> 2;
+	val1 *= ((w->buf[0] >> 2) & 2) - 1;	/* sign */
+	val2 = (w->buf[2] & 1) << 8 | (w->buf[3] << 4) | w->buf[4];
+	val2 *= (w->buf[2] & 2) - 1;	/* sign */
+	val3 = ((w->buf[5] & 7) * 256 + w->buf[6] * 16 + w->buf[7]) >> 2;
+	val3 *= ((w->buf[5] >> 2) & 2) - 1;	/* sign */
+	val4 = (w->buf[7] & 1) << 8 | (w->buf[8] << 4) | w->buf[9];
+	val4 *= (w->buf[7] & 2) - 1;	/* sign */
+	val5 = ((w->buf[11] & 7) * 256 + w->buf[12] * 16 + w->buf[13]) >> 2;
+	val5 *= ((w->buf[11] >> 2) & 2) - 1;	/* sign */
+	val6 = (w->buf[13] & 1) << 8 | (w->buf[14] << 4) | w->buf[15];
+	val6 *= (w->buf[13] & 2) - 1;	/* sign */
+	val7 = ((w->buf[16] & 7) * 256 + w->buf[17] * 16 + w->buf[18]) >> 2;
+	val7 *= ((w->buf[16] >> 2) & 2) - 1;	/*sign */
+	val8 = (w->buf[18] & 1) << 8 | (w->buf[19] << 4) | w->buf[20];
+	val8 *= (w->buf[18] & 2) - 1;	/*sign */
+
+	magic = (w->buf[21] << 4) | w->buf[22];
+	magic_bit = (w->buf[24] & 8) >> 3;
+	pr_debug("%4d %4d %4d %4d  %4d %4d %4d %4d (magic %2x %d)\n",
+		 val1, val2, val3, val4, val5, val6, val7, val8,
+		 magic, magic_bit);
+
+	input_report_abs(w->input_dev, ABS_X, val2);
+	input_report_abs(w->input_dev, ABS_Y, val1);
+	input_report_abs(w->input_dev, ABS_Z, val6);
+	input_report_abs(w->input_dev, ABS_THROTTLE, val3);
+	input_report_abs(w->input_dev, ABS_RUDDER, val4);
+	input_report_abs(w->input_dev, ABS_MISC, val7);
+	input_report_key(w->input_dev, BTN_GEAR_DOWN, val5 > 0);
+}
+
+static inline int read_ack(struct pardevice *p)
+{
+	return parport_read_status(p->port) & 0x40;
+}
+
+/* falling edge, prepare to BIN value calculation */
+static void walkera0701_irq_handler(void *handler_data)
+{
+	u64 pulse_time;
+	struct walkera_dev *w = handler_data;
+
+	w->irq_time = ktime_to_ns(ktime_get());
+	pulse_time = w->irq_time - w->irq_lasttime;
+	w->irq_lasttime = w->irq_time;
+
+	/* cancel timer, if in handler or active do resync */
+	if (unlikely(0 != hrtimer_try_to_cancel(&w->timer))) {
+		w->counter = NO_SYNC;
+		return;
+	}
+
+	if (w->counter < NO_SYNC) {
+		if (w->ack) {
+			pulse_time -= BIN1_PULSE;
+			w->buf[w->counter] = 8;
+		} else {
+			pulse_time -= BIN0_PULSE;
+			w->buf[w->counter] = 0;
+		}
+		if (w->counter == 24) {	/* full frame */
+			walkera0701_parse_frame(w);
+			w->counter = NO_SYNC;
+			if (abs(pulse_time - SYNC_PULSE) < RESERVE)	/* new frame sync */
+				w->counter = 0;
+		} else {
+			if ((pulse_time > (ANALOG_MIN_PULSE - RESERVE)
+			     && (pulse_time < (ANALOG_MAX_PULSE + RESERVE)))) {
+				pulse_time -= (ANALOG_MIN_PULSE - RESERVE);
+				pulse_time = (u32) pulse_time / ANALOG_DELTA;	/* overtiping is safe, pulsetime < s32.. */
+				w->buf[w->counter++] |= (pulse_time & 7);
+			} else
+				w->counter = NO_SYNC;
+		}
+	} else if (abs(pulse_time - SYNC_PULSE - BIN0_PULSE) <
+				RESERVE + BIN1_PULSE - BIN0_PULSE)	/* frame sync .. */
+		w->counter = 0;
+
+	hrtimer_start(&w->timer, ktime_set(0, BIN_SAMPLE), HRTIMER_MODE_REL);
+}
+
+static enum hrtimer_restart timer_handler(struct hrtimer
+					  *handle)
+{
+	struct walkera_dev *w;
+
+	w = container_of(handle, struct walkera_dev, timer);
+	w->ack = read_ack(w->pardevice);
+
+	return HRTIMER_NORESTART;
+}
+
+static int walkera0701_open(struct input_dev *dev)
+{
+	struct walkera_dev *w = input_get_drvdata(dev);
+
+	if (parport_claim(w->pardevice))
+		return -EBUSY;
+
+	parport_enable_irq(w->parport);
+	return 0;
+}
+
+static void walkera0701_close(struct input_dev *dev)
+{
+	struct walkera_dev *w = input_get_drvdata(dev);
+
+	parport_disable_irq(w->parport);
+	hrtimer_cancel(&w->timer);
+
+	parport_release(w->pardevice);
+}
+
+static void walkera0701_attach(struct parport *pp)
+{
+	struct pardev_cb walkera0701_parport_cb;
+	struct walkera_dev *w = &w_dev;
+
+	if (pp->number != walkera0701_pp_no) {
+		pr_debug("Not using parport%d.\n", pp->number);
+		return;
+	}
+
+	if (pp->irq == -1) {
+		pr_err("parport %d does not have interrupt assigned\n",
+			pp->number);
+		return;
+	}
+
+	w->parport = pp;
+
+	memset(&walkera0701_parport_cb, 0, sizeof(walkera0701_parport_cb));
+	walkera0701_parport_cb.flags = PARPORT_FLAG_EXCL;
+	walkera0701_parport_cb.irq_func = walkera0701_irq_handler;
+	walkera0701_parport_cb.private = w;
+
+	w->pardevice = parport_register_dev_model(pp, "walkera0701",
+						  &walkera0701_parport_cb, 0);
+
+	if (!w->pardevice) {
+		pr_err("failed to register parport device\n");
+		return;
+	}
+
+	if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT)) {
+		pr_err("failed to negotiate parport mode\n");
+		goto err_unregister_device;
+	}
+
+	hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	w->timer.function = timer_handler;
+
+	w->input_dev = input_allocate_device();
+	if (!w->input_dev) {
+		pr_err("failed to allocate input device\n");
+		goto err_unregister_device;
+	}
+
+	input_set_drvdata(w->input_dev, w);
+	w->input_dev->name = "Walkera WK-0701 TX";
+	w->input_dev->phys = w->parport->name;
+	w->input_dev->id.bustype = BUS_PARPORT;
+
+	/* TODO what id vendor/product/version ? */
+	w->input_dev->id.vendor = 0x0001;
+	w->input_dev->id.product = 0x0001;
+	w->input_dev->id.version = 0x0100;
+	w->input_dev->dev.parent = w->parport->dev;
+	w->input_dev->open = walkera0701_open;
+	w->input_dev->close = walkera0701_close;
+
+	w->input_dev->evbit[0] = BIT(EV_ABS) | BIT_MASK(EV_KEY);
+	w->input_dev->keybit[BIT_WORD(BTN_GEAR_DOWN)] = BIT_MASK(BTN_GEAR_DOWN);
+
+	input_set_abs_params(w->input_dev, ABS_X, -512, 512, 0, 0);
+	input_set_abs_params(w->input_dev, ABS_Y, -512, 512, 0, 0);
+	input_set_abs_params(w->input_dev, ABS_Z, -512, 512, 0, 0);
+	input_set_abs_params(w->input_dev, ABS_THROTTLE, -512, 512, 0, 0);
+	input_set_abs_params(w->input_dev, ABS_RUDDER, -512, 512, 0, 0);
+	input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0);
+
+	if (input_register_device(w->input_dev)) {
+		pr_err("failed to register input device\n");
+		goto err_free_input_dev;
+	}
+
+	return;
+
+err_free_input_dev:
+	input_free_device(w->input_dev);
+err_unregister_device:
+	parport_unregister_device(w->pardevice);
+}
+
+static void walkera0701_detach(struct parport *port)
+{
+	struct walkera_dev *w = &w_dev;
+
+	if (!w->pardevice || w->parport->number != port->number)
+		return;
+
+	input_unregister_device(w->input_dev);
+	parport_unregister_device(w->pardevice);
+	w->parport = NULL;
+}
+
+static struct parport_driver walkera0701_parport_driver = {
+	.name = "walkera0701",
+	.match_port = walkera0701_attach,
+	.detach = walkera0701_detach,
+	.devmodel = true,
+};
+
+static int __init walkera0701_init(void)
+{
+	return parport_register_driver(&walkera0701_parport_driver);
+}
+
+static void __exit walkera0701_exit(void)
+{
+	parport_unregister_driver(&walkera0701_parport_driver);
+}
+
+module_init(walkera0701_init);
+module_exit(walkera0701_exit);
diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c
new file mode 100644
index 0000000..e13a914
--- /dev/null
+++ b/drivers/input/joystick/warrior.c
@@ -0,0 +1,219 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Logitech WingMan Warrior joystick driver for Linux
+ */
+
+/*
+ * This program is free warftware; 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *  Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Logitech WingMan Warrior joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define WARRIOR_MAX_LENGTH	16
+static char warrior_lengths[] = { 0, 4, 12, 3, 4, 4, 0, 0 };
+
+/*
+ * Per-Warrior data.
+ */
+
+struct warrior {
+	struct input_dev *dev;
+	int idx, len;
+	unsigned char data[WARRIOR_MAX_LENGTH];
+	char phys[32];
+};
+
+/*
+ * warrior_process_packet() decodes packets the driver receives from the
+ * Warrior. It updates the data accordingly.
+ */
+
+static void warrior_process_packet(struct warrior *warrior)
+{
+	struct input_dev *dev = warrior->dev;
+	unsigned char *data = warrior->data;
+
+	if (!warrior->idx) return;
+
+	switch ((data[0] >> 4) & 7) {
+		case 1:					/* Button data */
+			input_report_key(dev, BTN_TRIGGER,  data[3]       & 1);
+			input_report_key(dev, BTN_THUMB,   (data[3] >> 1) & 1);
+			input_report_key(dev, BTN_TOP,     (data[3] >> 2) & 1);
+			input_report_key(dev, BTN_TOP2,    (data[3] >> 3) & 1);
+			break;
+		case 3:					/* XY-axis info->data */
+			input_report_abs(dev, ABS_X, ((data[0] & 8) << 5) - (data[2] | ((data[0] & 4) << 5)));
+			input_report_abs(dev, ABS_Y, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7));
+			break;
+		case 5:					/* Throttle, spinner, hat info->data */
+			input_report_abs(dev, ABS_THROTTLE, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7));
+			input_report_abs(dev, ABS_HAT0X, (data[3] & 2 ? 1 : 0) - (data[3] & 1 ? 1 : 0));
+			input_report_abs(dev, ABS_HAT0Y, (data[3] & 8 ? 1 : 0) - (data[3] & 4 ? 1 : 0));
+			input_report_rel(dev, REL_DIAL,  (data[2] | ((data[0] & 4) << 5)) - ((data[0] & 8) << 5));
+			break;
+	}
+	input_sync(dev);
+}
+
+/*
+ * warrior_interrupt() is called by the low level driver when characters
+ * are ready for us. We then buffer them for further processing, or call the
+ * packet processing routine.
+ */
+
+static irqreturn_t warrior_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct warrior *warrior = serio_get_drvdata(serio);
+
+	if (data & 0x80) {
+		if (warrior->idx) warrior_process_packet(warrior);
+		warrior->idx = 0;
+		warrior->len = warrior_lengths[(data >> 4) & 7];
+	}
+
+	if (warrior->idx < warrior->len)
+		warrior->data[warrior->idx++] = data;
+
+	if (warrior->idx == warrior->len) {
+		if (warrior->idx) warrior_process_packet(warrior);
+		warrior->idx = 0;
+		warrior->len = 0;
+	}
+	return IRQ_HANDLED;
+}
+
+/*
+ * warrior_disconnect() is the opposite of warrior_connect()
+ */
+
+static void warrior_disconnect(struct serio *serio)
+{
+	struct warrior *warrior = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(warrior->dev);
+	kfree(warrior);
+}
+
+/*
+ * warrior_connect() is the routine that is called when someone adds a
+ * new serio device. It looks for the Warrior, and if found, registers
+ * it as an input device.
+ */
+
+static int warrior_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct warrior *warrior;
+	struct input_dev *input_dev;
+	int err = -ENOMEM;
+
+	warrior = kzalloc(sizeof(struct warrior), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!warrior || !input_dev)
+		goto fail1;
+
+	warrior->dev = input_dev;
+	snprintf(warrior->phys, sizeof(warrior->phys), "%s/input0", serio->phys);
+
+	input_dev->name = "Logitech WingMan Warrior";
+	input_dev->phys = warrior->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_WARRIOR;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) |
+		BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TRIGGER)] = BIT_MASK(BTN_TRIGGER) |
+		BIT_MASK(BTN_THUMB) | BIT_MASK(BTN_TOP) | BIT_MASK(BTN_TOP2);
+	input_dev->relbit[0] = BIT_MASK(REL_DIAL);
+	input_set_abs_params(input_dev, ABS_X, -64, 64, 0, 8);
+	input_set_abs_params(input_dev, ABS_Y, -64, 64, 0, 8);
+	input_set_abs_params(input_dev, ABS_THROTTLE, -112, 112, 0, 0);
+	input_set_abs_params(input_dev, ABS_HAT0X, -1, 1, 0, 0);
+	input_set_abs_params(input_dev, ABS_HAT0Y, -1, 1, 0, 0);
+
+	serio_set_drvdata(serio, warrior);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(warrior->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(warrior);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id warrior_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_WARRIOR,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, warrior_serio_ids);
+
+static struct serio_driver warrior_drv = {
+	.driver		= {
+		.name	= "warrior",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= warrior_serio_ids,
+	.interrupt	= warrior_interrupt,
+	.connect	= warrior_connect,
+	.disconnect	= warrior_disconnect,
+};
+
+module_serio_driver(warrior_drv);
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
new file mode 100644
index 0000000..3258baf
--- /dev/null
+++ b/drivers/input/joystick/xpad.c
@@ -0,0 +1,1495 @@
+/*
+ * X-Box gamepad driver
+ *
+ * Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de>
+ *               2004 Oliver Schwartz <Oliver.Schwartz@gmx.de>,
+ *                    Steven Toth <steve@toth.demon.co.uk>,
+ *                    Franz Lehner <franz@caos.at>,
+ *                    Ivan Hawkes <blackhawk@ivanhawkes.com>
+ *               2005 Dominic Cerquetti <binary1230@yahoo.com>
+ *               2006 Adam Buchbinder <adam.buchbinder@gmail.com>
+ *               2007 Jan Kratochvil <honza@jikos.cz>
+ *               2010 Christoph Fritz <chf.fritz@googlemail.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * This driver is based on:
+ *  - information from     http://euc.jp/periphs/xbox-controller.ja.html
+ *  - the iForce driver    drivers/char/joystick/iforce.c
+ *  - the skeleton-driver  drivers/usb/usb-skeleton.c
+ *  - Xbox 360 information http://www.free60.org/wiki/Gamepad
+ *  - Xbox One information https://github.com/quantus/xbox-one-controller-protocol
+ *
+ * Thanks to:
+ *  - ITO Takayuki for providing essential xpad information on his website
+ *  - Vojtech Pavlik     - iforce driver / input subsystem
+ *  - Greg Kroah-Hartman - usb-skeleton driver
+ *  - XBOX Linux project - extra USB id's
+ *  - Pekka Pöyry (quantus) - Xbox One controller reverse engineering
+ *
+ * TODO:
+ *  - fine tune axes (especially trigger axes)
+ *  - fix "analog" buttons (reported as digital now)
+ *  - get rumble working
+ *  - need USB IDs for other dance pads
+ *
+ * History:
+ *
+ * 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller"
+ *
+ * 2002-07-02 - 0.0.2 : basic working version
+ *  - all axes and 9 of the 10 buttons work (german InterAct device)
+ *  - the black button does not work
+ *
+ * 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik
+ *  - indentation fixes
+ *  - usb + input init sequence fixes
+ *
+ * 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3
+ *  - verified the lack of HID and report descriptors
+ *  - verified that ALL buttons WORK
+ *  - fixed d-pad to axes mapping
+ *
+ * 2002-07-17 - 0.0.5 : simplified d-pad handling
+ *
+ * 2004-10-02 - 0.0.6 : DDR pad support
+ *  - borrowed from the XBOX linux kernel
+ *  - USB id's for commonly used dance pads are present
+ *  - dance pads will map D-PAD to buttons, not axes
+ *  - pass the module paramater 'dpad_to_buttons' to force
+ *    the D-PAD to map to buttons if your pad is not detected
+ *
+ * Later changes can be tracked in SCM.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+
+#define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
+#define DRIVER_DESC "X-Box pad driver"
+
+#define XPAD_PKT_LEN 32
+
+/* xbox d-pads should map to buttons, as is required for DDR pads
+   but we map them to axes when possible to simplify things */
+#define MAP_DPAD_TO_BUTTONS		(1 << 0)
+#define MAP_TRIGGERS_TO_BUTTONS		(1 << 1)
+#define MAP_STICKS_TO_NULL		(1 << 2)
+#define DANCEPAD_MAP_CONFIG	(MAP_DPAD_TO_BUTTONS |			\
+				MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL)
+
+#define XTYPE_XBOX        0
+#define XTYPE_XBOX360     1
+#define XTYPE_XBOX360W    2
+#define XTYPE_XBOXONE     3
+#define XTYPE_UNKNOWN     4
+
+static bool dpad_to_buttons;
+module_param(dpad_to_buttons, bool, S_IRUGO);
+MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads");
+
+static bool triggers_to_buttons;
+module_param(triggers_to_buttons, bool, S_IRUGO);
+MODULE_PARM_DESC(triggers_to_buttons, "Map triggers to buttons rather than axes for unknown pads");
+
+static bool sticks_to_null;
+module_param(sticks_to_null, bool, S_IRUGO);
+MODULE_PARM_DESC(sticks_to_null, "Do not map sticks at all for unknown pads");
+
+static const struct xpad_device {
+	u16 idVendor;
+	u16 idProduct;
+	char *name;
+	u8 mapping;
+	u8 xtype;
+} xpad_device[] = {
+	{ 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", 0, XTYPE_XBOX },
+	{ 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", 0, XTYPE_XBOX },
+	{ 0x045e, 0x0287, "Microsoft Xbox Controller S", 0, XTYPE_XBOX },
+	{ 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", 0, XTYPE_XBOX },
+	{ 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 },
+	{ 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE },
+	{ 0x045e, 0x02dd, "Microsoft X-Box One pad (Covert Forces)", 0, XTYPE_XBOXONE },
+	{ 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
+	{ 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
+	{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },
+	{ 0x044f, 0xb326, "Thrustmaster Gamepad GP XID", 0, XTYPE_XBOX360 },
+	{ 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 },
+	{ 0x046d, 0xc21e, "Logitech Gamepad F510", 0, XTYPE_XBOX360 },
+	{ 0x046d, 0xc21f, "Logitech Gamepad F710", 0, XTYPE_XBOX360 },
+	{ 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 },
+	{ 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX },
+	{ 0x046d, 0xca88, "Logitech Compact Controller for Xbox", 0, XTYPE_XBOX },
+	{ 0x05fd, 0x1007, "Mad Catz Controller (unverified)", 0, XTYPE_XBOX },
+	{ 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", 0, XTYPE_XBOX },
+	{ 0x0738, 0x4516, "Mad Catz Control Pad", 0, XTYPE_XBOX },
+	{ 0x0738, 0x4522, "Mad Catz LumiCON", 0, XTYPE_XBOX },
+	{ 0x0738, 0x4526, "Mad Catz Control Pad Pro", 0, XTYPE_XBOX },
+	{ 0x0738, 0x4536, "Mad Catz MicroCON", 0, XTYPE_XBOX },
+	{ 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+	{ 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", 0, XTYPE_XBOX },
+	{ 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", 0, XTYPE_XBOX360 },
+	{ 0x0738, 0x4718, "Mad Catz Street Fighter IV FightStick SE", 0, XTYPE_XBOX360 },
+	{ 0x0738, 0x4726, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 },
+	{ 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+	{ 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+	{ 0x0738, 0x4740, "Mad Catz Beat Pad", 0, XTYPE_XBOX360 },
+	{ 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+	{ 0x0738, 0xb726, "Mad Catz Xbox controller - MW2", 0, XTYPE_XBOX360 },
+	{ 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad", XTYPE_XBOX360 },
+	{ 0x0738, 0xcb02, "Saitek Cyborg Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 },
+	{ 0x0738, 0xcb03, "Saitek P3200 Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 },
+	{ 0x0738, 0xf738, "Super SFIV FightStick TE S", 0, XTYPE_XBOX360 },
+	{ 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX },
+	{ 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX },
+	{ 0x0c12, 0x880a, "Pelican Eclipse PL-2023", 0, XTYPE_XBOX },
+	{ 0x0c12, 0x8810, "Zeroplus Xbox Controller", 0, XTYPE_XBOX },
+	{ 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", 0, XTYPE_XBOX },
+	{ 0x0d2f, 0x0002, "Andamiro Pump It Up pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+	{ 0x0e4c, 0x1097, "Radica Gamester Controller", 0, XTYPE_XBOX },
+	{ 0x0e4c, 0x2390, "Radica Games Jtech Controller", 0, XTYPE_XBOX },
+	{ 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", 0, XTYPE_XBOX },
+	{ 0x0e6f, 0x0005, "Eclipse wireless Controller", 0, XTYPE_XBOX },
+	{ 0x0e6f, 0x0006, "Edge wireless Controller", 0, XTYPE_XBOX },
+	{ 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+	{ 0x0e6f, 0x0113, "Afterglow AX.1 Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
+	{ 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 },
+	{ 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
+	{ 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
+	{ 0x0e6f, 0x0301, "Logic3 Controller", 0, XTYPE_XBOX360 },
+	{ 0x0e6f, 0x0401, "Logic3 Controller", 0, XTYPE_XBOX360 },
+	{ 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", 0, XTYPE_XBOX },
+	{ 0x0e8f, 0x3008, "Generic xbox control (dealextreme)", 0, XTYPE_XBOX },
+	{ 0x0f0d, 0x000a, "Hori Co. DOA4 FightStick", 0, XTYPE_XBOX360 },
+	{ 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+	{ 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+	{ 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX },
+	{ 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX },
+	{ 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", 0, XTYPE_XBOX },
+	{ 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+	{ 0x12ab, 0x0301, "PDP AFTERGLOW AX.1", 0, XTYPE_XBOX360 },
+	{ 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+	{ 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 },
+	{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+	{ 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 },
+	{ 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 },
+	{ 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE },
+	{ 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 },
+	{ 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
+	{ 0x15e4, 0x3f10, "Batarang Xbox 360 controller", 0, XTYPE_XBOX360 },
+	{ 0x162e, 0xbeef, "Joytech Neo-Se Take2", 0, XTYPE_XBOX360 },
+	{ 0x1689, 0xfd00, "Razer Onza Tournament Edition", 0, XTYPE_XBOX360 },
+	{ 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 },
+	{ 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 },
+	{ 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 },
+	{ 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+	{ 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 },
+	{ 0x1bad, 0xf023, "MLG Pro Circuit Controller (Xbox)", 0, XTYPE_XBOX360 },
+	{ 0x1bad, 0xf028, "Street Fighter IV FightPad", 0, XTYPE_XBOX360 },
+	{ 0x1bad, 0xf038, "Street Fighter IV FightStick TE", 0, XTYPE_XBOX360 },
+	{ 0x1bad, 0xf900, "Harmonix Xbox 360 Controller", 0, XTYPE_XBOX360 },
+	{ 0x1bad, 0xf901, "Gamestop Xbox 360 Controller", 0, XTYPE_XBOX360 },
+	{ 0x1bad, 0xf903, "Tron Xbox 360 controller", 0, XTYPE_XBOX360 },
+	{ 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+	{ 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 },
+	{ 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
+	{ 0x24c6, 0x5500, "Hori XBOX 360 EX 2 with Turbo", 0, XTYPE_XBOX360 },
+	{ 0x24c6, 0x5501, "Hori Real Arcade Pro VX-SA", 0, XTYPE_XBOX360 },
+	{ 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 },
+	{ 0x24c6, 0x5b02, "Thrustmaster, Inc. GPX Controller", 0, XTYPE_XBOX360 },
+	{ 0x24c6, 0x5b03, "Thrustmaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 },
+	{ 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX },
+	{ 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN }
+};
+
+/* buttons shared with xbox and xbox360 */
+static const signed short xpad_common_btn[] = {
+	BTN_A, BTN_B, BTN_X, BTN_Y,			/* "analog" buttons */
+	BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR,	/* start/back/sticks */
+	-1						/* terminating entry */
+};
+
+/* original xbox controllers only */
+static const signed short xpad_btn[] = {
+	BTN_C, BTN_Z,		/* "analog" buttons */
+	-1			/* terminating entry */
+};
+
+/* used when dpad is mapped to buttons */
+static const signed short xpad_btn_pad[] = {
+	BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY2,		/* d-pad left, right */
+	BTN_TRIGGER_HAPPY3, BTN_TRIGGER_HAPPY4,		/* d-pad up, down */
+	-1				/* terminating entry */
+};
+
+/* used when triggers are mapped to buttons */
+static const signed short xpad_btn_triggers[] = {
+	BTN_TL2, BTN_TR2,		/* triggers left/right */
+	-1
+};
+
+static const signed short xpad360_btn[] = {  /* buttons for x360 controller */
+	BTN_TL, BTN_TR,		/* Button LB/RB */
+	BTN_MODE,		/* The big X button */
+	-1
+};
+
+static const signed short xpad_abs[] = {
+	ABS_X, ABS_Y,		/* left stick */
+	ABS_RX, ABS_RY,		/* right stick */
+	-1			/* terminating entry */
+};
+
+/* used when dpad is mapped to axes */
+static const signed short xpad_abs_pad[] = {
+	ABS_HAT0X, ABS_HAT0Y,	/* d-pad axes */
+	-1			/* terminating entry */
+};
+
+/* used when triggers are mapped to axes */
+static const signed short xpad_abs_triggers[] = {
+	ABS_Z, ABS_RZ,		/* triggers left/right */
+	-1
+};
+
+/*
+ * Xbox 360 has a vendor-specific class, so we cannot match it with only
+ * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we
+ * match against vendor id as well. Wired Xbox 360 devices have protocol 1,
+ * wireless controllers have protocol 129.
+ */
+#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \
+	.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \
+	.idVendor = (vend), \
+	.bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
+	.bInterfaceSubClass = 93, \
+	.bInterfaceProtocol = (pr)
+#define XPAD_XBOX360_VENDOR(vend) \
+	{ XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \
+	{ XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) }
+
+/* The Xbox One controller uses subclass 71 and protocol 208. */
+#define XPAD_XBOXONE_VENDOR_PROTOCOL(vend, pr) \
+	.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \
+	.idVendor = (vend), \
+	.bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
+	.bInterfaceSubClass = 71, \
+	.bInterfaceProtocol = (pr)
+#define XPAD_XBOXONE_VENDOR(vend) \
+	{ XPAD_XBOXONE_VENDOR_PROTOCOL(vend, 208) }
+
+static struct usb_device_id xpad_table[] = {
+	{ USB_INTERFACE_INFO('X', 'B', 0) },	/* X-Box USB-IF not approved class */
+	XPAD_XBOX360_VENDOR(0x044f),		/* Thrustmaster X-Box 360 controllers */
+	XPAD_XBOX360_VENDOR(0x045e),		/* Microsoft X-Box 360 controllers */
+	XPAD_XBOXONE_VENDOR(0x045e),		/* Microsoft X-Box One controllers */
+	XPAD_XBOX360_VENDOR(0x046d),		/* Logitech X-Box 360 style controllers */
+	XPAD_XBOX360_VENDOR(0x0738),		/* Mad Catz X-Box 360 controllers */
+	{ USB_DEVICE(0x0738, 0x4540) },		/* Mad Catz Beat Pad */
+	XPAD_XBOX360_VENDOR(0x0e6f),		/* 0x0e6f X-Box 360 controllers */
+	XPAD_XBOX360_VENDOR(0x12ab),		/* X-Box 360 dance pads */
+	XPAD_XBOX360_VENDOR(0x1430),		/* RedOctane X-Box 360 controllers */
+	XPAD_XBOX360_VENDOR(0x146b),		/* BigBen Interactive Controllers */
+	XPAD_XBOX360_VENDOR(0x1bad),		/* Harminix Rock Band Guitar and Drums */
+	XPAD_XBOX360_VENDOR(0x0f0d),		/* Hori Controllers */
+	XPAD_XBOX360_VENDOR(0x1689),		/* Razer Onza */
+	XPAD_XBOX360_VENDOR(0x24c6),		/* PowerA Controllers */
+	XPAD_XBOX360_VENDOR(0x1532),		/* Razer Sabertooth */
+	XPAD_XBOXONE_VENDOR(0x1532),		/* Razer Wildcat */
+	XPAD_XBOX360_VENDOR(0x15e4),		/* Numark X-Box 360 controllers */
+	XPAD_XBOX360_VENDOR(0x162e),		/* Joytech X-Box 360 controllers */
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, xpad_table);
+
+struct xpad_output_packet {
+	u8 data[XPAD_PKT_LEN];
+	u8 len;
+	bool pending;
+};
+
+#define XPAD_OUT_CMD_IDX	0
+#define XPAD_OUT_FF_IDX		1
+#define XPAD_OUT_LED_IDX	(1 + IS_ENABLED(CONFIG_JOYSTICK_XPAD_FF))
+#define XPAD_NUM_OUT_PACKETS	(1 + \
+				 IS_ENABLED(CONFIG_JOYSTICK_XPAD_FF) + \
+				 IS_ENABLED(CONFIG_JOYSTICK_XPAD_LEDS))
+
+struct usb_xpad {
+	struct input_dev *dev;		/* input device interface */
+	struct usb_device *udev;	/* usb device */
+	struct usb_interface *intf;	/* usb interface */
+
+	int pad_present;
+
+	struct urb *irq_in;		/* urb for interrupt in report */
+	unsigned char *idata;		/* input data */
+	dma_addr_t idata_dma;
+
+	struct urb *irq_out;		/* urb for interrupt out report */
+	bool irq_out_active;		/* we must not use an active URB */
+	unsigned char *odata;		/* output data */
+	dma_addr_t odata_dma;
+	spinlock_t odata_lock;
+
+	struct xpad_output_packet out_packets[XPAD_NUM_OUT_PACKETS];
+	int last_out_packet;
+
+#if defined(CONFIG_JOYSTICK_XPAD_LEDS)
+	struct xpad_led *led;
+#endif
+
+	char phys[64];			/* physical device path */
+
+	int mapping;			/* map d-pad to buttons or to axes */
+	int xtype;			/* type of xbox device */
+	int pad_nr;			/* the order x360 pads were attached */
+	const char *name;		/* name of the device */
+};
+
+/*
+ *	xpad_process_packet
+ *
+ *	Completes a request by converting the data into events for the
+ *	input subsystem.
+ *
+ *	The used report descriptor was taken from ITO Takayukis website:
+ *	 http://euc.jp/periphs/xbox-controller.ja.html
+ */
+static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+{
+	struct input_dev *dev = xpad->dev;
+
+	if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
+		/* left stick */
+		input_report_abs(dev, ABS_X,
+				 (__s16) le16_to_cpup((__le16 *)(data + 12)));
+		input_report_abs(dev, ABS_Y,
+				 ~(__s16) le16_to_cpup((__le16 *)(data + 14)));
+
+		/* right stick */
+		input_report_abs(dev, ABS_RX,
+				 (__s16) le16_to_cpup((__le16 *)(data + 16)));
+		input_report_abs(dev, ABS_RY,
+				 ~(__s16) le16_to_cpup((__le16 *)(data + 18)));
+	}
+
+	/* triggers left/right */
+	if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
+		input_report_key(dev, BTN_TL2, data[10]);
+		input_report_key(dev, BTN_TR2, data[11]);
+	} else {
+		input_report_abs(dev, ABS_Z, data[10]);
+		input_report_abs(dev, ABS_RZ, data[11]);
+	}
+
+	/* digital pad */
+	if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
+		/* dpad as buttons (left, right, up, down) */
+		input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04);
+		input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08);
+		input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01);
+		input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);
+	} else {
+		input_report_abs(dev, ABS_HAT0X,
+				 !!(data[2] & 0x08) - !!(data[2] & 0x04));
+		input_report_abs(dev, ABS_HAT0Y,
+				 !!(data[2] & 0x02) - !!(data[2] & 0x01));
+	}
+
+	/* start/back buttons and stick press left/right */
+	input_report_key(dev, BTN_START,  data[2] & 0x10);
+	input_report_key(dev, BTN_SELECT, data[2] & 0x20);
+	input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
+	input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
+
+	/* "analog" buttons A, B, X, Y */
+	input_report_key(dev, BTN_A, data[4]);
+	input_report_key(dev, BTN_B, data[5]);
+	input_report_key(dev, BTN_X, data[6]);
+	input_report_key(dev, BTN_Y, data[7]);
+
+	/* "analog" buttons black, white */
+	input_report_key(dev, BTN_C, data[8]);
+	input_report_key(dev, BTN_Z, data[9]);
+
+	input_sync(dev);
+}
+
+/*
+ *	xpad360_process_packet
+ *
+ *	Completes a request by converting the data into events for the
+ *	input subsystem. It is version for xbox 360 controller
+ *
+ *	The used report descriptor was taken from:
+ *		http://www.free60.org/wiki/Gamepad
+ */
+
+static void xpad360_process_packet(struct usb_xpad *xpad,
+				   u16 cmd, unsigned char *data)
+{
+	struct input_dev *dev = xpad->dev;
+
+	/* digital pad */
+	if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
+		/* dpad as buttons (left, right, up, down) */
+		input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04);
+		input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08);
+		input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01);
+		input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);
+	}
+
+	/*
+	 * This should be a simple else block. However historically
+	 * xbox360w has mapped DPAD to buttons while xbox360 did not. This
+	 * made no sense, but now we can not just switch back and have to
+	 * support both behaviors.
+	 */
+	if (!(xpad->mapping & MAP_DPAD_TO_BUTTONS) ||
+	    xpad->xtype == XTYPE_XBOX360W) {
+		input_report_abs(dev, ABS_HAT0X,
+				 !!(data[2] & 0x08) - !!(data[2] & 0x04));
+		input_report_abs(dev, ABS_HAT0Y,
+				 !!(data[2] & 0x02) - !!(data[2] & 0x01));
+	}
+
+	/* start/back buttons */
+	input_report_key(dev, BTN_START,  data[2] & 0x10);
+	input_report_key(dev, BTN_SELECT, data[2] & 0x20);
+
+	/* stick press left/right */
+	input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
+	input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
+
+	/* buttons A,B,X,Y,TL,TR and MODE */
+	input_report_key(dev, BTN_A,	data[3] & 0x10);
+	input_report_key(dev, BTN_B,	data[3] & 0x20);
+	input_report_key(dev, BTN_X,	data[3] & 0x40);
+	input_report_key(dev, BTN_Y,	data[3] & 0x80);
+	input_report_key(dev, BTN_TL,	data[3] & 0x01);
+	input_report_key(dev, BTN_TR,	data[3] & 0x02);
+	input_report_key(dev, BTN_MODE,	data[3] & 0x04);
+
+	if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
+		/* left stick */
+		input_report_abs(dev, ABS_X,
+				 (__s16) le16_to_cpup((__le16 *)(data + 6)));
+		input_report_abs(dev, ABS_Y,
+				 ~(__s16) le16_to_cpup((__le16 *)(data + 8)));
+
+		/* right stick */
+		input_report_abs(dev, ABS_RX,
+				 (__s16) le16_to_cpup((__le16 *)(data + 10)));
+		input_report_abs(dev, ABS_RY,
+				 ~(__s16) le16_to_cpup((__le16 *)(data + 12)));
+	}
+
+	/* triggers left/right */
+	if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
+		input_report_key(dev, BTN_TL2, data[4]);
+		input_report_key(dev, BTN_TR2, data[5]);
+	} else {
+		input_report_abs(dev, ABS_Z, data[4]);
+		input_report_abs(dev, ABS_RZ, data[5]);
+	}
+
+	input_sync(dev);
+}
+
+static void xpad_identify_controller(struct usb_xpad *xpad);
+
+/*
+ * xpad360w_process_packet
+ *
+ * Completes a request by converting the data into events for the
+ * input subsystem. It is version for xbox 360 wireless controller.
+ *
+ * Byte.Bit
+ * 00.1 - Status change: The controller or headset has connected/disconnected
+ *                       Bits 01.7 and 01.6 are valid
+ * 01.7 - Controller present
+ * 01.6 - Headset present
+ * 01.1 - Pad state (Bytes 4+) valid
+ *
+ */
+static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+{
+	/* Presence change */
+	if (data[0] & 0x08) {
+		if (data[1] & 0x80) {
+			xpad->pad_present = 1;
+			/*
+			 * Light up the segment corresponding to
+			 * controller number.
+			 */
+			xpad_identify_controller(xpad);
+		} else
+			xpad->pad_present = 0;
+	}
+
+	/* Valid pad data */
+	if (!(data[1] & 0x1))
+		return;
+
+	xpad360_process_packet(xpad, cmd, &data[4]);
+}
+
+/*
+ *	xpadone_process_buttons
+ *
+ *	Process a button update packet from an Xbox one controller.
+ */
+static void xpadone_process_buttons(struct usb_xpad *xpad,
+				struct input_dev *dev,
+				unsigned char *data)
+{
+	/* menu/view buttons */
+	input_report_key(dev, BTN_START,  data[4] & 0x04);
+	input_report_key(dev, BTN_SELECT, data[4] & 0x08);
+
+	/* buttons A,B,X,Y */
+	input_report_key(dev, BTN_A,	data[4] & 0x10);
+	input_report_key(dev, BTN_B,	data[4] & 0x20);
+	input_report_key(dev, BTN_X,	data[4] & 0x40);
+	input_report_key(dev, BTN_Y,	data[4] & 0x80);
+
+	/* digital pad */
+	if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
+		/* dpad as buttons (left, right, up, down) */
+		input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & 0x04);
+		input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & 0x08);
+		input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & 0x01);
+		input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & 0x02);
+	} else {
+		input_report_abs(dev, ABS_HAT0X,
+				 !!(data[5] & 0x08) - !!(data[5] & 0x04));
+		input_report_abs(dev, ABS_HAT0Y,
+				 !!(data[5] & 0x02) - !!(data[5] & 0x01));
+	}
+
+	/* TL/TR */
+	input_report_key(dev, BTN_TL,	data[5] & 0x10);
+	input_report_key(dev, BTN_TR,	data[5] & 0x20);
+
+	/* stick press left/right */
+	input_report_key(dev, BTN_THUMBL, data[5] & 0x40);
+	input_report_key(dev, BTN_THUMBR, data[5] & 0x80);
+
+	if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
+		/* left stick */
+		input_report_abs(dev, ABS_X,
+				 (__s16) le16_to_cpup((__le16 *)(data + 10)));
+		input_report_abs(dev, ABS_Y,
+				 ~(__s16) le16_to_cpup((__le16 *)(data + 12)));
+
+		/* right stick */
+		input_report_abs(dev, ABS_RX,
+				 (__s16) le16_to_cpup((__le16 *)(data + 14)));
+		input_report_abs(dev, ABS_RY,
+				 ~(__s16) le16_to_cpup((__le16 *)(data + 16)));
+	}
+
+	/* triggers left/right */
+	if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
+		input_report_key(dev, BTN_TL2,
+				 (__u16) le16_to_cpup((__le16 *)(data + 6)));
+		input_report_key(dev, BTN_TR2,
+				 (__u16) le16_to_cpup((__le16 *)(data + 8)));
+	} else {
+		input_report_abs(dev, ABS_Z,
+				 (__u16) le16_to_cpup((__le16 *)(data + 6)));
+		input_report_abs(dev, ABS_RZ,
+				 (__u16) le16_to_cpup((__le16 *)(data + 8)));
+	}
+
+	input_sync(dev);
+}
+
+/*
+ *	xpadone_process_packet
+ *
+ *	Completes a request by converting the data into events for the
+ *	input subsystem. This version is for the Xbox One controller.
+ *
+ *	The report format was gleaned from
+ *	https://github.com/kylelemons/xbox/blob/master/xbox.go
+ */
+
+static void xpadone_process_packet(struct usb_xpad *xpad,
+				u16 cmd, unsigned char *data)
+{
+	struct input_dev *dev = xpad->dev;
+
+	switch (data[0]) {
+	case 0x20:
+		xpadone_process_buttons(xpad, dev, data);
+		break;
+
+	case 0x07:
+		/* the xbox button has its own special report */
+		input_report_key(dev, BTN_MODE, data[4] & 0x01);
+		input_sync(dev);
+		break;
+	}
+}
+
+static void xpad_irq_in(struct urb *urb)
+{
+	struct usb_xpad *xpad = urb->context;
+	struct device *dev = &xpad->intf->dev;
+	int retval, status;
+
+	status = urb->status;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto exit;
+	}
+
+	switch (xpad->xtype) {
+	case XTYPE_XBOX360:
+		xpad360_process_packet(xpad, 0, xpad->idata);
+		break;
+	case XTYPE_XBOX360W:
+		xpad360w_process_packet(xpad, 0, xpad->idata);
+		break;
+	case XTYPE_XBOXONE:
+		xpadone_process_packet(xpad, 0, xpad->idata);
+		break;
+	default:
+		xpad_process_packet(xpad, 0, xpad->idata);
+	}
+
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
+			__func__, retval);
+}
+
+/* Callers must hold xpad->odata_lock spinlock */
+static bool xpad_prepare_next_out_packet(struct usb_xpad *xpad)
+{
+	struct xpad_output_packet *pkt, *packet = NULL;
+	int i;
+
+	for (i = 0; i < XPAD_NUM_OUT_PACKETS; i++) {
+		if (++xpad->last_out_packet >= XPAD_NUM_OUT_PACKETS)
+			xpad->last_out_packet = 0;
+
+		pkt = &xpad->out_packets[xpad->last_out_packet];
+		if (pkt->pending) {
+			dev_dbg(&xpad->intf->dev,
+				"%s - found pending output packet %d\n",
+				__func__, xpad->last_out_packet);
+			packet = pkt;
+			break;
+		}
+	}
+
+	if (packet) {
+		memcpy(xpad->odata, packet->data, packet->len);
+		xpad->irq_out->transfer_buffer_length = packet->len;
+		packet->pending = false;
+		return true;
+	}
+
+	return false;
+}
+
+/* Callers must hold xpad->odata_lock spinlock */
+static int xpad_try_sending_next_out_packet(struct usb_xpad *xpad)
+{
+	int error;
+
+	if (!xpad->irq_out_active && xpad_prepare_next_out_packet(xpad)) {
+		error = usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+		if (error) {
+			dev_err(&xpad->intf->dev,
+				"%s - usb_submit_urb failed with result %d\n",
+				__func__, error);
+			return -EIO;
+		}
+
+		xpad->irq_out_active = true;
+	}
+
+	return 0;
+}
+
+static void xpad_irq_out(struct urb *urb)
+{
+	struct usb_xpad *xpad = urb->context;
+	struct device *dev = &xpad->intf->dev;
+	int status = urb->status;
+	int error;
+	unsigned long flags;
+
+	spin_lock_irqsave(&xpad->odata_lock, flags);
+
+	switch (status) {
+	case 0:
+		/* success */
+		xpad->irq_out_active = xpad_prepare_next_out_packet(xpad);
+		break;
+
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		xpad->irq_out_active = false;
+		break;
+
+	default:
+		dev_dbg(dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		break;
+	}
+
+	if (xpad->irq_out_active) {
+		error = usb_submit_urb(urb, GFP_ATOMIC);
+		if (error) {
+			dev_err(dev,
+				"%s - usb_submit_urb failed with result %d\n",
+				__func__, error);
+			xpad->irq_out_active = false;
+		}
+	}
+
+	spin_unlock_irqrestore(&xpad->odata_lock, flags);
+}
+
+static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
+{
+	struct usb_endpoint_descriptor *ep_irq_out;
+	int ep_irq_out_idx;
+	int error;
+
+	if (xpad->xtype == XTYPE_UNKNOWN)
+		return 0;
+
+	xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN,
+					 GFP_KERNEL, &xpad->odata_dma);
+	if (!xpad->odata) {
+		error = -ENOMEM;
+		goto fail1;
+	}
+
+	spin_lock_init(&xpad->odata_lock);
+
+	xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL);
+	if (!xpad->irq_out) {
+		error = -ENOMEM;
+		goto fail2;
+	}
+
+	/* Xbox One controller has in/out endpoints swapped. */
+	ep_irq_out_idx = xpad->xtype == XTYPE_XBOXONE ? 0 : 1;
+	ep_irq_out = &intf->cur_altsetting->endpoint[ep_irq_out_idx].desc;
+
+	usb_fill_int_urb(xpad->irq_out, xpad->udev,
+			 usb_sndintpipe(xpad->udev, ep_irq_out->bEndpointAddress),
+			 xpad->odata, XPAD_PKT_LEN,
+			 xpad_irq_out, xpad, ep_irq_out->bInterval);
+	xpad->irq_out->transfer_dma = xpad->odata_dma;
+	xpad->irq_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return 0;
+
+ fail2:	usb_free_coherent(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma);
+ fail1:	return error;
+}
+
+static void xpad_stop_output(struct usb_xpad *xpad)
+{
+	if (xpad->xtype != XTYPE_UNKNOWN)
+		usb_kill_urb(xpad->irq_out);
+}
+
+static void xpad_deinit_output(struct usb_xpad *xpad)
+{
+	if (xpad->xtype != XTYPE_UNKNOWN) {
+		usb_free_urb(xpad->irq_out);
+		usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
+				xpad->odata, xpad->odata_dma);
+	}
+}
+
+static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
+{
+	struct xpad_output_packet *packet =
+			&xpad->out_packets[XPAD_OUT_CMD_IDX];
+	unsigned long flags;
+	int retval;
+
+	spin_lock_irqsave(&xpad->odata_lock, flags);
+
+	packet->data[0] = 0x08;
+	packet->data[1] = 0x00;
+	packet->data[2] = 0x0F;
+	packet->data[3] = 0xC0;
+	packet->data[4] = 0x00;
+	packet->data[5] = 0x00;
+	packet->data[6] = 0x00;
+	packet->data[7] = 0x00;
+	packet->data[8] = 0x00;
+	packet->data[9] = 0x00;
+	packet->data[10] = 0x00;
+	packet->data[11] = 0x00;
+	packet->len = 12;
+	packet->pending = true;
+
+	/* Reset the sequence so we send out presence first */
+	xpad->last_out_packet = -1;
+	retval = xpad_try_sending_next_out_packet(xpad);
+
+	spin_unlock_irqrestore(&xpad->odata_lock, flags);
+
+	return retval;
+}
+
+static int xpad_start_xbox_one(struct usb_xpad *xpad)
+{
+	struct xpad_output_packet *packet =
+			&xpad->out_packets[XPAD_OUT_CMD_IDX];
+	unsigned long flags;
+	int retval;
+
+	spin_lock_irqsave(&xpad->odata_lock, flags);
+
+	/* Xbox one controller needs to be initialized. */
+	packet->data[0] = 0x05;
+	packet->data[1] = 0x20;
+	packet->len = 2;
+	packet->pending = true;
+
+	/* Reset the sequence so we send out start packet first */
+	xpad->last_out_packet = -1;
+	retval = xpad_try_sending_next_out_packet(xpad);
+
+	spin_unlock_irqrestore(&xpad->odata_lock, flags);
+
+	return retval;
+}
+
+#ifdef CONFIG_JOYSTICK_XPAD_FF
+static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
+{
+	struct usb_xpad *xpad = input_get_drvdata(dev);
+	struct xpad_output_packet *packet = &xpad->out_packets[XPAD_OUT_FF_IDX];
+	__u16 strong;
+	__u16 weak;
+	int retval;
+	unsigned long flags;
+
+	if (effect->type != FF_RUMBLE)
+		return 0;
+
+	strong = effect->u.rumble.strong_magnitude;
+	weak = effect->u.rumble.weak_magnitude;
+
+	spin_lock_irqsave(&xpad->odata_lock, flags);
+
+	switch (xpad->xtype) {
+	case XTYPE_XBOX:
+		packet->data[0] = 0x00;
+		packet->data[1] = 0x06;
+		packet->data[2] = 0x00;
+		packet->data[3] = strong / 256;	/* left actuator */
+		packet->data[4] = 0x00;
+		packet->data[5] = weak / 256;	/* right actuator */
+		packet->len = 6;
+		packet->pending = true;
+		break;
+
+	case XTYPE_XBOX360:
+		packet->data[0] = 0x00;
+		packet->data[1] = 0x08;
+		packet->data[2] = 0x00;
+		packet->data[3] = strong / 256;  /* left actuator? */
+		packet->data[4] = weak / 256;	/* right actuator? */
+		packet->data[5] = 0x00;
+		packet->data[6] = 0x00;
+		packet->data[7] = 0x00;
+		packet->len = 8;
+		packet->pending = true;
+		break;
+
+	case XTYPE_XBOX360W:
+		packet->data[0] = 0x00;
+		packet->data[1] = 0x01;
+		packet->data[2] = 0x0F;
+		packet->data[3] = 0xC0;
+		packet->data[4] = 0x00;
+		packet->data[5] = strong / 256;
+		packet->data[6] = weak / 256;
+		packet->data[7] = 0x00;
+		packet->data[8] = 0x00;
+		packet->data[9] = 0x00;
+		packet->data[10] = 0x00;
+		packet->data[11] = 0x00;
+		packet->len = 12;
+		packet->pending = true;
+		break;
+
+	case XTYPE_XBOXONE:
+		packet->data[0] = 0x09; /* activate rumble */
+		packet->data[1] = 0x08;
+		packet->data[2] = 0x00;
+		packet->data[3] = 0x08; /* continuous effect */
+		packet->data[4] = 0x00; /* simple rumble mode */
+		packet->data[5] = 0x03; /* L and R actuator only */
+		packet->data[6] = 0x00; /* TODO: LT actuator */
+		packet->data[7] = 0x00; /* TODO: RT actuator */
+		packet->data[8] = strong / 256;	/* left actuator */
+		packet->data[9] = weak / 256;	/* right actuator */
+		packet->data[10] = 0x80;	/* length of pulse */
+		packet->data[11] = 0x00;	/* stop period of pulse */
+		packet->len = 12;
+		packet->pending = true;
+		break;
+
+	default:
+		dev_dbg(&xpad->dev->dev,
+			"%s - rumble command sent to unsupported xpad type: %d\n",
+			__func__, xpad->xtype);
+		retval = -EINVAL;
+		goto out;
+	}
+
+	retval = xpad_try_sending_next_out_packet(xpad);
+
+out:
+	spin_unlock_irqrestore(&xpad->odata_lock, flags);
+	return retval;
+}
+
+static int xpad_init_ff(struct usb_xpad *xpad)
+{
+	if (xpad->xtype == XTYPE_UNKNOWN)
+		return 0;
+
+	input_set_capability(xpad->dev, EV_FF, FF_RUMBLE);
+
+	return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect);
+}
+
+#else
+static int xpad_init_ff(struct usb_xpad *xpad) { return 0; }
+#endif
+
+#if defined(CONFIG_JOYSTICK_XPAD_LEDS)
+#include <linux/leds.h>
+#include <linux/idr.h>
+
+static DEFINE_IDA(xpad_pad_seq);
+
+struct xpad_led {
+	char name[16];
+	struct led_classdev led_cdev;
+	struct usb_xpad *xpad;
+};
+
+/**
+ * set the LEDs on Xbox360 / Wireless Controllers
+ * @param command
+ *  0: off
+ *  1: all blink, then previous setting
+ *  2: 1/top-left blink, then on
+ *  3: 2/top-right blink, then on
+ *  4: 3/bottom-left blink, then on
+ *  5: 4/bottom-right blink, then on
+ *  6: 1/top-left on
+ *  7: 2/top-right on
+ *  8: 3/bottom-left on
+ *  9: 4/bottom-right on
+ * 10: rotate
+ * 11: blink, based on previous setting
+ * 12: slow blink, based on previous setting
+ * 13: rotate with two lights
+ * 14: persistent slow all blink
+ * 15: blink once, then previous setting
+ */
+static void xpad_send_led_command(struct usb_xpad *xpad, int command)
+{
+	struct xpad_output_packet *packet =
+			&xpad->out_packets[XPAD_OUT_LED_IDX];
+	unsigned long flags;
+
+	command %= 16;
+
+	spin_lock_irqsave(&xpad->odata_lock, flags);
+
+	switch (xpad->xtype) {
+	case XTYPE_XBOX360:
+		packet->data[0] = 0x01;
+		packet->data[1] = 0x03;
+		packet->data[2] = command;
+		packet->len = 3;
+		packet->pending = true;
+		break;
+
+	case XTYPE_XBOX360W:
+		packet->data[0] = 0x00;
+		packet->data[1] = 0x00;
+		packet->data[2] = 0x08;
+		packet->data[3] = 0x40 + command;
+		packet->data[4] = 0x00;
+		packet->data[5] = 0x00;
+		packet->data[6] = 0x00;
+		packet->data[7] = 0x00;
+		packet->data[8] = 0x00;
+		packet->data[9] = 0x00;
+		packet->data[10] = 0x00;
+		packet->data[11] = 0x00;
+		packet->len = 12;
+		packet->pending = true;
+		break;
+	}
+
+	xpad_try_sending_next_out_packet(xpad);
+
+	spin_unlock_irqrestore(&xpad->odata_lock, flags);
+}
+
+/*
+ * Light up the segment corresponding to the pad number on
+ * Xbox 360 Controllers.
+ */
+static void xpad_identify_controller(struct usb_xpad *xpad)
+{
+	xpad_send_led_command(xpad, (xpad->pad_nr % 4) + 2);
+}
+
+static void xpad_led_set(struct led_classdev *led_cdev,
+			 enum led_brightness value)
+{
+	struct xpad_led *xpad_led = container_of(led_cdev,
+						 struct xpad_led, led_cdev);
+
+	xpad_send_led_command(xpad_led->xpad, value);
+}
+
+static int xpad_led_probe(struct usb_xpad *xpad)
+{
+	struct xpad_led *led;
+	struct led_classdev *led_cdev;
+	int error;
+
+	if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX360W)
+		return 0;
+
+	xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	xpad->pad_nr = ida_simple_get(&xpad_pad_seq, 0, 0, GFP_KERNEL);
+	if (xpad->pad_nr < 0) {
+		error = xpad->pad_nr;
+		goto err_free_mem;
+	}
+
+	snprintf(led->name, sizeof(led->name), "xpad%d", xpad->pad_nr);
+	led->xpad = xpad;
+
+	led_cdev = &led->led_cdev;
+	led_cdev->name = led->name;
+	led_cdev->brightness_set = xpad_led_set;
+
+	error = led_classdev_register(&xpad->udev->dev, led_cdev);
+	if (error)
+		goto err_free_id;
+
+	if (xpad->xtype == XTYPE_XBOX360) {
+		/*
+		 * Light up the segment corresponding to controller
+		 * number on wired devices. On wireless we'll do that
+		 * when they respond to "presence" packet.
+		 */
+		xpad_identify_controller(xpad);
+	}
+
+	return 0;
+
+err_free_id:
+	ida_simple_remove(&xpad_pad_seq, xpad->pad_nr);
+err_free_mem:
+	kfree(led);
+	xpad->led = NULL;
+	return error;
+}
+
+static void xpad_led_disconnect(struct usb_xpad *xpad)
+{
+	struct xpad_led *xpad_led = xpad->led;
+
+	if (xpad_led) {
+		led_classdev_unregister(&xpad_led->led_cdev);
+		ida_simple_remove(&xpad_pad_seq, xpad->pad_nr);
+		kfree(xpad_led);
+	}
+}
+#else
+static int xpad_led_probe(struct usb_xpad *xpad) { return 0; }
+static void xpad_led_disconnect(struct usb_xpad *xpad) { }
+static void xpad_identify_controller(struct usb_xpad *xpad) { }
+#endif
+
+static int xpad_open(struct input_dev *dev)
+{
+	struct usb_xpad *xpad = input_get_drvdata(dev);
+
+	/* URB was submitted in probe */
+	if (xpad->xtype == XTYPE_XBOX360W)
+		return 0;
+
+	xpad->irq_in->dev = xpad->udev;
+	if (usb_submit_urb(xpad->irq_in, GFP_KERNEL))
+		return -EIO;
+
+	if (xpad->xtype == XTYPE_XBOXONE)
+		return xpad_start_xbox_one(xpad);
+
+	return 0;
+}
+
+static void xpad_close(struct input_dev *dev)
+{
+	struct usb_xpad *xpad = input_get_drvdata(dev);
+
+	if (xpad->xtype != XTYPE_XBOX360W)
+		usb_kill_urb(xpad->irq_in);
+
+	xpad_stop_output(xpad);
+}
+
+static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
+{
+	struct usb_xpad *xpad = input_get_drvdata(input_dev);
+	set_bit(abs, input_dev->absbit);
+
+	switch (abs) {
+	case ABS_X:
+	case ABS_Y:
+	case ABS_RX:
+	case ABS_RY:	/* the two sticks */
+		input_set_abs_params(input_dev, abs, -32768, 32767, 16, 128);
+		break;
+	case ABS_Z:
+	case ABS_RZ:	/* the triggers (if mapped to axes) */
+		if (xpad->xtype == XTYPE_XBOXONE)
+			input_set_abs_params(input_dev, abs, 0, 1023, 0, 0);
+		else
+			input_set_abs_params(input_dev, abs, 0, 255, 0, 0);
+		break;
+	case ABS_HAT0X:
+	case ABS_HAT0Y:	/* the d-pad (only if dpad is mapped to axes */
+		input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
+		break;
+	}
+}
+
+static void xpad_deinit_input(struct usb_xpad *xpad)
+{
+	xpad_led_disconnect(xpad);
+	input_unregister_device(xpad->dev);
+}
+
+static int xpad_init_input(struct usb_xpad *xpad)
+{
+	struct input_dev *input_dev;
+	int i, error;
+
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	xpad->dev = input_dev;
+	input_dev->name = xpad->name;
+	input_dev->phys = xpad->phys;
+	usb_to_input_id(xpad->udev, &input_dev->id);
+
+	if (xpad->xtype == XTYPE_XBOX360W) {
+		/* x360w controllers and the receiver have different ids */
+		input_dev->id.product = 0x02a1;
+	}
+
+	input_dev->dev.parent = &xpad->intf->dev;
+
+	input_set_drvdata(input_dev, xpad);
+
+	input_dev->open = xpad_open;
+	input_dev->close = xpad_close;
+
+	__set_bit(EV_KEY, input_dev->evbit);
+
+	if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
+		__set_bit(EV_ABS, input_dev->evbit);
+		/* set up axes */
+		for (i = 0; xpad_abs[i] >= 0; i++)
+			xpad_set_up_abs(input_dev, xpad_abs[i]);
+	}
+
+	/* set up standard buttons */
+	for (i = 0; xpad_common_btn[i] >= 0; i++)
+		__set_bit(xpad_common_btn[i], input_dev->keybit);
+
+	/* set up model-specific ones */
+	if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W ||
+	    xpad->xtype == XTYPE_XBOXONE) {
+		for (i = 0; xpad360_btn[i] >= 0; i++)
+			__set_bit(xpad360_btn[i], input_dev->keybit);
+	} else {
+		for (i = 0; xpad_btn[i] >= 0; i++)
+			__set_bit(xpad_btn[i], input_dev->keybit);
+	}
+
+	if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
+		for (i = 0; xpad_btn_pad[i] >= 0; i++)
+			__set_bit(xpad_btn_pad[i], input_dev->keybit);
+	}
+
+	/*
+	 * This should be a simple else block. However historically
+	 * xbox360w has mapped DPAD to buttons while xbox360 did not. This
+	 * made no sense, but now we can not just switch back and have to
+	 * support both behaviors.
+	 */
+	if (!(xpad->mapping & MAP_DPAD_TO_BUTTONS) ||
+	    xpad->xtype == XTYPE_XBOX360W) {
+		for (i = 0; xpad_abs_pad[i] >= 0; i++)
+			xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
+	}
+
+	if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
+		for (i = 0; xpad_btn_triggers[i] >= 0; i++)
+			__set_bit(xpad_btn_triggers[i], input_dev->keybit);
+	} else {
+		for (i = 0; xpad_abs_triggers[i] >= 0; i++)
+			xpad_set_up_abs(input_dev, xpad_abs_triggers[i]);
+	}
+
+	error = xpad_init_ff(xpad);
+	if (error)
+		goto err_free_input;
+
+	error = xpad_led_probe(xpad);
+	if (error)
+		goto err_destroy_ff;
+
+	error = input_register_device(xpad->dev);
+	if (error)
+		goto err_disconnect_led;
+
+	return 0;
+
+err_disconnect_led:
+	xpad_led_disconnect(xpad);
+err_destroy_ff:
+	input_ff_destroy(input_dev);
+err_free_input:
+	input_free_device(input_dev);
+	return error;
+}
+
+static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_xpad *xpad;
+	struct usb_endpoint_descriptor *ep_irq_in;
+	int ep_irq_in_idx;
+	int i, error;
+
+	if (intf->cur_altsetting->desc.bNumEndpoints != 2)
+		return -ENODEV;
+
+	for (i = 0; xpad_device[i].idVendor; i++) {
+		if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
+		    (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct))
+			break;
+	}
+
+	xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
+	if (!xpad)
+		return -ENOMEM;
+
+	usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
+	strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
+
+	xpad->idata = usb_alloc_coherent(udev, XPAD_PKT_LEN,
+					 GFP_KERNEL, &xpad->idata_dma);
+	if (!xpad->idata) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
+	if (!xpad->irq_in) {
+		error = -ENOMEM;
+		goto err_free_idata;
+	}
+
+	xpad->udev = udev;
+	xpad->intf = intf;
+	xpad->mapping = xpad_device[i].mapping;
+	xpad->xtype = xpad_device[i].xtype;
+	xpad->name = xpad_device[i].name;
+
+	if (xpad->xtype == XTYPE_UNKNOWN) {
+		if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
+			if (intf->cur_altsetting->desc.bInterfaceProtocol == 129)
+				xpad->xtype = XTYPE_XBOX360W;
+			else if (intf->cur_altsetting->desc.bInterfaceProtocol == 208)
+				xpad->xtype = XTYPE_XBOXONE;
+			else
+				xpad->xtype = XTYPE_XBOX360;
+		} else {
+			xpad->xtype = XTYPE_XBOX;
+		}
+
+		if (dpad_to_buttons)
+			xpad->mapping |= MAP_DPAD_TO_BUTTONS;
+		if (triggers_to_buttons)
+			xpad->mapping |= MAP_TRIGGERS_TO_BUTTONS;
+		if (sticks_to_null)
+			xpad->mapping |= MAP_STICKS_TO_NULL;
+	}
+
+	if (xpad->xtype == XTYPE_XBOXONE &&
+	    intf->cur_altsetting->desc.bInterfaceNumber != 0) {
+		/*
+		 * The Xbox One controller lists three interfaces all with the
+		 * same interface class, subclass and protocol. Differentiate by
+		 * interface number.
+		 */
+		error = -ENODEV;
+		goto err_free_in_urb;
+	}
+
+	error = xpad_init_output(intf, xpad);
+	if (error)
+		goto err_free_in_urb;
+
+	/* Xbox One controller has in/out endpoints swapped. */
+	ep_irq_in_idx = xpad->xtype == XTYPE_XBOXONE ? 1 : 0;
+	ep_irq_in = &intf->cur_altsetting->endpoint[ep_irq_in_idx].desc;
+
+	usb_fill_int_urb(xpad->irq_in, udev,
+			 usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),
+			 xpad->idata, XPAD_PKT_LEN, xpad_irq_in,
+			 xpad, ep_irq_in->bInterval);
+	xpad->irq_in->transfer_dma = xpad->idata_dma;
+	xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	usb_set_intfdata(intf, xpad);
+
+	error = xpad_init_input(xpad);
+	if (error)
+		goto err_deinit_output;
+
+	if (xpad->xtype == XTYPE_XBOX360W) {
+		/*
+		 * Submit the int URB immediately rather than waiting for open
+		 * because we get status messages from the device whether
+		 * or not any controllers are attached.  In fact, it's
+		 * exactly the message that a controller has arrived that
+		 * we're waiting for.
+		 */
+		xpad->irq_in->dev = xpad->udev;
+		error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
+		if (error)
+			goto err_deinit_input;
+
+		/*
+		 * Send presence packet.
+		 * This will force the controller to resend connection packets.
+		 * This is useful in the case we activate the module after the
+		 * adapter has been plugged in, as it won't automatically
+		 * send us info about the controllers.
+		 */
+		error = xpad_inquiry_pad_presence(xpad);
+		if (error)
+			goto err_kill_in_urb;
+	}
+	return 0;
+
+err_kill_in_urb:
+	usb_kill_urb(xpad->irq_in);
+err_deinit_input:
+	xpad_deinit_input(xpad);
+err_deinit_output:
+	xpad_deinit_output(xpad);
+err_free_in_urb:
+	usb_free_urb(xpad->irq_in);
+err_free_idata:
+	usb_free_coherent(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
+err_free_mem:
+	kfree(xpad);
+	return error;
+
+}
+
+static void xpad_disconnect(struct usb_interface *intf)
+{
+	struct usb_xpad *xpad = usb_get_intfdata (intf);
+
+	xpad_deinit_input(xpad);
+	xpad_deinit_output(xpad);
+
+	if (xpad->xtype == XTYPE_XBOX360W) {
+		usb_kill_urb(xpad->irq_in);
+	}
+
+	usb_free_urb(xpad->irq_in);
+	usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
+			xpad->idata, xpad->idata_dma);
+
+	kfree(xpad);
+
+	usb_set_intfdata(intf, NULL);
+}
+
+static struct usb_driver xpad_driver = {
+	.name		= "xpad",
+	.probe		= xpad_probe,
+	.disconnect	= xpad_disconnect,
+	.id_table	= xpad_table,
+};
+
+module_usb_driver(xpad_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/joystick/zhenhua.c b/drivers/input/joystick/zhenhua.c
new file mode 100644
index 0000000..4a8258b
--- /dev/null
+++ b/drivers/input/joystick/zhenhua.c
@@ -0,0 +1,218 @@
+/*
+ *  derived from "twidjoy.c"
+ *
+ *  Copyright (c) 2008 Martin Kebert
+ *  Copyright (c) 2001 Arndt Schoenewald
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *  Copyright (c) 2000 Mark Fletcher
+ *
+ */
+
+/*
+ * Driver to use 4CH RC transmitter using Zhen Hua 5-byte protocol (Walkera Lama,
+ * EasyCopter etc.) as a joystick under Linux.
+ *
+ * RC transmitters using Zhen Hua 5-byte protocol are cheap four channels
+ * transmitters for control a RC planes or RC helicopters with possibility to
+ * connect on a serial port.
+ * Data coming from transmitter is in this order:
+ * 1. byte = synchronisation byte
+ * 2. byte = X axis
+ * 3. byte = Y axis
+ * 4. byte = RZ axis
+ * 5. byte = Z axis
+ * (and this is repeated)
+ *
+ * For questions or feedback regarding this driver module please contact:
+ * Martin Kebert <gkmarty@gmail.com> - but I am not a C-programmer nor kernel
+ * coder :-(
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/bitrev.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"RC transmitter with 5-byte Zhen Hua protocol joystick driver"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define ZHENHUA_MAX_LENGTH 5
+
+/*
+ * Zhen Hua data.
+ */
+
+struct zhenhua {
+	struct input_dev *dev;
+	int idx;
+	unsigned char data[ZHENHUA_MAX_LENGTH];
+	char phys[32];
+};
+
+/*
+ * zhenhua_process_packet() decodes packets the driver receives from the
+ * RC transmitter. It updates the data accordingly.
+ */
+
+static void zhenhua_process_packet(struct zhenhua *zhenhua)
+{
+	struct input_dev *dev = zhenhua->dev;
+	unsigned char *data = zhenhua->data;
+
+	input_report_abs(dev, ABS_Y, data[1]);
+	input_report_abs(dev, ABS_X, data[2]);
+	input_report_abs(dev, ABS_RZ, data[3]);
+	input_report_abs(dev, ABS_Z, data[4]);
+
+	input_sync(dev);
+}
+
+/*
+ * zhenhua_interrupt() is called by the low level driver when characters
+ * are ready for us. We then buffer them for further processing, or call the
+ * packet processing routine.
+ */
+
+static irqreturn_t zhenhua_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+	struct zhenhua *zhenhua = serio_get_drvdata(serio);
+
+	/* All Zhen Hua packets are 5 bytes. The fact that the first byte
+	 * is allways 0xf7 and all others are in range 0x32 - 0xc8 (50-200)
+	 * can be used to check and regain sync. */
+
+	if (data == 0xef)
+		zhenhua->idx = 0;	/* this byte starts a new packet */
+	else if (zhenhua->idx == 0)
+		return IRQ_HANDLED;	/* wrong MSB -- ignore this byte */
+
+	if (zhenhua->idx < ZHENHUA_MAX_LENGTH)
+		zhenhua->data[zhenhua->idx++] = bitrev8(data);
+
+	if (zhenhua->idx == ZHENHUA_MAX_LENGTH) {
+		zhenhua_process_packet(zhenhua);
+		zhenhua->idx = 0;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * zhenhua_disconnect() is the opposite of zhenhua_connect()
+ */
+
+static void zhenhua_disconnect(struct serio *serio)
+{
+	struct zhenhua *zhenhua = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(zhenhua->dev);
+	kfree(zhenhua);
+}
+
+/*
+ * zhenhua_connect() is the routine that is called when someone adds a
+ * new serio device. It looks for the Twiddler, and if found, registers
+ * it as an input device.
+ */
+
+static int zhenhua_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct zhenhua *zhenhua;
+	struct input_dev *input_dev;
+	int err = -ENOMEM;
+
+	zhenhua = kzalloc(sizeof(struct zhenhua), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!zhenhua || !input_dev)
+		goto fail1;
+
+	zhenhua->dev = input_dev;
+	snprintf(zhenhua->phys, sizeof(zhenhua->phys), "%s/input0", serio->phys);
+
+	input_dev->name = "Zhen Hua 5-byte device";
+	input_dev->phys = zhenhua->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_ZHENHUA;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT(EV_ABS);
+	input_set_abs_params(input_dev, ABS_X, 50, 200, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 50, 200, 0, 0);
+	input_set_abs_params(input_dev, ABS_Z, 50, 200, 0, 0);
+	input_set_abs_params(input_dev, ABS_RZ, 50, 200, 0, 0);
+
+	serio_set_drvdata(serio, zhenhua);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(zhenhua->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(zhenhua);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id zhenhua_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_ZHENHUA,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, zhenhua_serio_ids);
+
+static struct serio_driver zhenhua_drv = {
+	.driver		= {
+		.name	= "zhenhua",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= zhenhua_serio_ids,
+	.interrupt	= zhenhua_interrupt,
+	.connect	= zhenhua_connect,
+	.disconnect	= zhenhua_disconnect,
+};
+
+module_serio_driver(zhenhua_drv);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
new file mode 100644
index 0000000..ddd8148
--- /dev/null
+++ b/drivers/input/keyboard/Kconfig
@@ -0,0 +1,722 @@
+#
+# Input core configuration
+#
+menuconfig INPUT_KEYBOARD
+	bool "Keyboards"
+	default y
+	help
+	  Say Y here, and a list of supported keyboards will be displayed.
+	  This option doesn't affect the kernel.
+
+	  If unsure, say Y.
+
+if INPUT_KEYBOARD
+
+config KEYBOARD_ADP5520
+	tristate "Keypad Support for ADP5520 PMIC"
+	depends on PMIC_ADP5520
+	help
+	  This option enables support for the keypad scan matrix
+	  on Analog Devices ADP5520 PMICs.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called adp5520-keys.
+
+config KEYBOARD_ADP5588
+	tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander"
+	depends on I2C
+	help
+	  Say Y here if you want to use a ADP5588/87 attached to your
+	  system I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adp5588-keys.
+
+config KEYBOARD_ADP5589
+	tristate "ADP5585/ADP5589 I2C QWERTY Keypad and IO Expander"
+	depends on I2C
+	help
+	  Say Y here if you want to use a ADP5585/ADP5589 attached to your
+	  system I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adp5589-keys.
+
+config KEYBOARD_AMIGA
+	tristate "Amiga keyboard"
+	depends on AMIGA
+	help
+	  Say Y here if you are running Linux on any AMIGA and have a keyboard
+	  attached.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called amikbd.
+
+config ATARI_KBD_CORE
+	bool
+
+config KEYBOARD_ATARI
+	tristate "Atari keyboard"
+	depends on ATARI
+	select ATARI_KBD_CORE
+	help
+	  Say Y here if you are running Linux on any Atari and have a keyboard
+	  attached.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called atakbd.
+
+config KEYBOARD_ATKBD
+	tristate "AT keyboard"
+	default y
+	select SERIO
+	select SERIO_LIBPS2
+	select SERIO_I8042 if ARCH_MIGHT_HAVE_PC_SERIO
+	select SERIO_GSCPS2 if GSC
+	help
+	  Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
+	  you'll need this, unless you have a different type keyboard (USB, ADB
+	  or other). This also works for AT and PS/2 keyboards connected over a
+	  PS/2 to serial converter.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called atkbd.
+
+config KEYBOARD_ATKBD_HP_KEYCODES
+	bool "Use HP keyboard scancodes"
+	depends on PARISC && KEYBOARD_ATKBD
+	default y
+	help
+	  Say Y here if you have a PA-RISC machine and want to use an AT or
+	  PS/2 keyboard, and your keyboard uses keycodes that are specific to
+	  PA-RISC keyboards.
+
+	  Say N if you use a standard keyboard.
+
+config KEYBOARD_ATKBD_RDI_KEYCODES
+	bool "Use PrecisionBook keyboard scancodes"
+	depends on KEYBOARD_ATKBD_HP_KEYCODES
+	default n
+	help
+	  If you have an RDI PrecisionBook, say Y here if you want to use its
+	  built-in keyboard (as opposed to an external keyboard).
+
+	  The PrecisionBook has five keys that conflict with those used by most
+	  AT and PS/2 keyboards. These are as follows:
+
+	    PrecisionBook    Standard AT or PS/2
+
+	    F1               F12
+	    Left Ctrl        Left Alt
+	    Caps Lock        Left Ctrl
+	    Right Ctrl       Caps Lock
+	    Left             102nd key (the key to the right of Left Shift)
+
+	  If you say N here, and use the PrecisionBook keyboard, then each key
+	  in the left-hand column will be interpreted as the corresponding key
+	  in the right-hand column.
+
+	  If you say Y here, and use an external keyboard, then each key in the
+	  right-hand column will be interpreted as the key shown in the
+	  left-hand column.
+
+config KEYBOARD_QT1070
+       tristate "Atmel AT42QT1070 Touch Sensor Chip"
+       depends on I2C
+       help
+         Say Y here if you want to use Atmel AT42QT1070 QTouch
+         Sensor chip as input device.
+
+         To compile this driver as a module, choose M here:
+         the module will be called qt1070
+
+config KEYBOARD_QT2160
+	tristate "Atmel AT42QT2160 Touch Sensor Chip"
+	depends on I2C
+	help
+	  If you say yes here you get support for Atmel AT42QT2160 Touch
+	  Sensor chip as a keyboard input.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called qt2160.
+
+config KEYBOARD_BFIN
+	tristate "Blackfin BF54x keypad support"
+	depends on (BF54x && !BF544)
+	help
+	  Say Y here if you want to use the BF54x keypad.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bf54x-keys.
+
+config KEYBOARD_CLPS711X
+	tristate "CLPS711X Keypad support"
+	depends on OF_GPIO && (ARCH_CLPS711X || COMPILE_TEST)
+	select INPUT_MATRIXKMAP
+	select INPUT_POLLDEV
+	help
+	  Say Y here to enable the matrix keypad on the Cirrus Logic
+	  CLPS711X CPUs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called clps711x-keypad.
+
+config KEYBOARD_LKKBD
+	tristate "DECstation/VAXstation LK201/LK401 keyboard"
+	select SERIO
+	help
+	  Say Y here if you want to use a LK201 or LK401 style serial
+	  keyboard. This keyboard is also useable on PCs if you attach
+	  it with the inputattach program. The connector pinout is
+	  described within lkkbd.c.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called lkkbd.
+
+config KEYBOARD_EP93XX
+	tristate "EP93xx Matrix Keypad support"
+	depends on ARCH_EP93XX
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here to enable the matrix keypad on the Cirrus EP93XX.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ep93xx_keypad.
+
+config KEYBOARD_GPIO
+	tristate "GPIO Buttons"
+	depends on GPIOLIB || COMPILE_TEST
+	help
+	  This driver implements support for buttons connected
+	  to GPIO pins of various CPUs (and some other chips).
+
+	  Say Y here if your device has buttons connected
+	  directly to such GPIO pins.  Your board-specific
+	  setup logic must also provide a platform device,
+	  with configuration data saying which GPIOs are used.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gpio_keys.
+
+config KEYBOARD_GPIO_POLLED
+	tristate "Polled GPIO buttons"
+	depends on GPIOLIB
+	select INPUT_POLLDEV
+	help
+	  This driver implements support for buttons connected
+	  to GPIO pins that are not capable of generating interrupts.
+
+	  Say Y here if your device has buttons connected
+	  directly to such GPIO pins.  Your board-specific
+	  setup logic must also provide a platform device,
+	  with configuration data saying which GPIOs are used.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gpio_keys_polled.
+
+config KEYBOARD_TCA6416
+	tristate "TCA6416/TCA6408A Keypad Support"
+	depends on I2C
+	help
+	  This driver implements basic keypad functionality
+	  for keys connected through TCA6416/TCA6408A IO expanders.
+
+	  Say Y here if your device has keys connected to
+	  TCA6416/TCA6408A IO expander. Your board-specific setup logic
+	  must also provide pin-mask details(of which TCA6416 pins
+	  are used for keypad).
+
+	  If enabled the entire TCA6416 device will be managed through
+	  this driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tca6416_keypad.
+
+config KEYBOARD_TCA8418
+	tristate "TCA8418 Keypad Support"
+	depends on I2C
+	select INPUT_MATRIXKMAP
+	help
+	  This driver implements basic keypad functionality
+	  for keys connected through TCA8418 keypad decoder.
+
+	  Say Y here if your device has keys connected to
+	  TCA8418 keypad decoder.
+
+	  If enabled the complete TCA8418 device will be managed through
+	  this driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tca8418_keypad.
+
+config KEYBOARD_MATRIX
+	tristate "GPIO driven matrix keypad support"
+	depends on GPIOLIB || COMPILE_TEST
+	select INPUT_MATRIXKMAP
+	help
+	  Enable support for GPIO driven matrix keypad.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called matrix_keypad.
+
+config KEYBOARD_HIL_OLD
+	tristate "HP HIL keyboard support (simple driver)"
+	depends on GSC || HP300
+	default y
+	help
+	  The "Human Interface Loop" is a older, 8-channel USB-like
+	  controller used in several Hewlett Packard models. This driver
+	  was adapted from the one written for m68k/hp300, and implements
+	  support for a keyboard attached to the HIL port, but not for
+	  any other types of HIL input devices like mice or tablets.
+	  However, it has been thoroughly tested and is stable.
+
+	  If you want full HIL support including support for multiple
+	  keyboards, mice, and tablets, you have to enable the
+	  "HP System Device Controller i8042 Support" in the input/serio
+	  submenu.
+
+config KEYBOARD_HIL
+	tristate "HP HIL keyboard/pointer support"
+	depends on GSC || HP300
+	default y
+	select HP_SDC
+	select HIL_MLC
+	select SERIO
+	help
+	  The "Human Interface Loop" is a older, 8-channel USB-like
+	  controller used in several Hewlett Packard models.
+	  This driver implements support for HIL-keyboards and pointing
+	  devices (mice, tablets, touchscreens) attached
+	  to your machine, so normally you should say Y here.
+
+config KEYBOARD_HP6XX
+	tristate "HP Jornada 6xx keyboard"
+	depends on SH_HP6XX
+	select INPUT_POLLDEV
+	help
+	  Say Y here if you have a HP Jornada 620/660/680/690 and want to
+	  support the built-in keyboard.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called jornada680_kbd.
+
+config KEYBOARD_HP7XX
+	tristate "HP Jornada 7xx keyboard"
+	depends on SA1100_JORNADA720_SSP && SA1100_SSP
+	help
+	  Say Y here if you have a HP Jornada 710/720/728 and want to
+	  support the built-in keyboard.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called jornada720_kbd.
+
+config KEYBOARD_LM8323
+	tristate "LM8323 keypad chip"
+	depends on I2C
+	depends on LEDS_CLASS
+	help
+	  If you say yes here you get support for the National Semiconductor
+	  LM8323 keypad controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called lm8323.
+
+config KEYBOARD_LM8333
+	tristate "LM8333 keypad chip"
+	depends on I2C
+	select INPUT_MATRIXKMAP
+	help
+	  If you say yes here you get support for the National Semiconductor
+	  LM8333 keypad controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called lm8333.
+
+config KEYBOARD_LOCOMO
+	tristate "LoCoMo Keyboard Support"
+	depends on SHARP_LOCOMO
+	help
+	  Say Y here if you are running Linux on a Sharp Zaurus Collie or Poodle based PDA
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called locomokbd.
+
+config KEYBOARD_LPC32XX
+	tristate "LPC32XX matrix key scanner support"
+	depends on ARCH_LPC32XX && OF
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if you want to use NXP LPC32XX SoC key scanner interface,
+	  connected to a key matrix.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called lpc32xx-keys.
+
+config KEYBOARD_MAPLE
+	tristate "Maple bus keyboard"
+	depends on SH_DREAMCAST && MAPLE
+	help
+	  Say Y here if you have a Dreamcast console running Linux and have
+	  a keyboard attached to its Maple bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called maple_keyb.
+
+config KEYBOARD_MAX7359
+	tristate "Maxim MAX7359 Key Switch Controller"
+	select INPUT_MATRIXKMAP
+	depends on I2C
+	help
+	  If you say yes here you get support for the Maxim MAX7359 Key
+	  Switch Controller chip. This providers microprocessors with
+	  management of up to 64 key switches
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called max7359_keypad.
+
+config KEYBOARD_MCS
+	tristate "MELFAS MCS Touchkey"
+	depends on I2C
+	help
+	  Say Y here if you have the MELFAS MCS5000/5080 touchkey controller
+	  chip in your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mcs_touchkey.
+
+config KEYBOARD_MPR121
+	tristate "Freescale MPR121 Touchkey"
+	depends on I2C
+	help
+	  Say Y here if you have Freescale MPR121 touchkey controller
+	  chip in your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mpr121_touchkey.
+
+config KEYBOARD_SNVS_PWRKEY
+	tristate "IMX SNVS Power Key Driver"
+	depends on SOC_IMX6SX
+	depends on OF
+	help
+	  This is the snvs powerkey driver for the Freescale i.MX application
+	  processors that are newer than i.MX6 SX.
+
+	  To compile this driver as a module, choose M here; the
+	  module will be called snvs_pwrkey.
+
+config KEYBOARD_IMX
+	tristate "IMX keypad support"
+	depends on ARCH_MXC
+	select INPUT_MATRIXKMAP
+	help
+	  Enable support for IMX keypad port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called imx_keypad.
+
+config KEYBOARD_NEWTON
+	tristate "Newton keyboard"
+	select SERIO
+	help
+	  Say Y here if you have a Newton keyboard on a serial port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called newtonkbd.
+
+config KEYBOARD_NOMADIK
+	tristate "ST-Ericsson Nomadik SKE keyboard"
+	depends on (ARCH_NOMADIK || ARCH_U8500)
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if you want to use a keypad provided on the SKE controller
+	  used on the Ux500 and Nomadik platforms
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called nmk-ske-keypad.
+
+config KEYBOARD_NSPIRE
+	tristate "TI-NSPIRE built-in keyboard"
+	depends on ARCH_NSPIRE && OF
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if you want to use the built-in keypad on TI-NSPIRE.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called nspire-keypad.
+
+config KEYBOARD_TEGRA
+	tristate "NVIDIA Tegra internal matrix keyboard controller support"
+	depends on ARCH_TEGRA && OF
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if you want to use a matrix keyboard connected directly
+	  to the internal keyboard controller on Tegra SoCs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tegra-kbc.
+
+config KEYBOARD_OPENCORES
+	tristate "OpenCores Keyboard Controller"
+	depends on HAS_IOMEM
+	help
+	  Say Y here if you want to use the OpenCores Keyboard Controller
+	  http://www.opencores.org/project,keyboardcontroller
+
+	  To compile this driver as a module, choose M here; the
+	  module will be called opencores-kbd.
+
+config KEYBOARD_PXA27x
+	tristate "PXA27x/PXA3xx keypad support"
+	depends on PXA27x || PXA3xx || ARCH_MMP
+	select INPUT_MATRIXKMAP
+	help
+	  Enable support for PXA27x/PXA3xx keypad controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pxa27x_keypad.
+
+config KEYBOARD_PXA930_ROTARY
+	tristate "PXA930/PXA935 Enhanced Rotary Controller Support"
+	depends on CPU_PXA930 || CPU_PXA935
+	help
+	  Enable support for PXA930/PXA935 Enhanced Rotary Controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pxa930_rotary.
+
+config KEYBOARD_PMIC8XXX
+	tristate "Qualcomm PMIC8XXX keypad support"
+	depends on MFD_PM8XXX
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if you want to enable the driver for the PMIC8XXX
+	  keypad provided as a reference design from Qualcomm. This is intended
+	  to support upto 18x8 matrix based keypad design.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called pmic8xxx-keypad.
+
+config KEYBOARD_SAMSUNG
+	tristate "Samsung keypad support"
+	depends on HAVE_CLK
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if you want to use the keypad on your Samsung mobile
+	  device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called samsung-keypad.
+
+config KEYBOARD_GOLDFISH_EVENTS
+	depends on GOLDFISH || COMPILE_TEST
+	tristate "Generic Input Event device for Goldfish"
+	help
+	  Say Y here to get an input event device for the Goldfish virtual
+	  device emulator.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called goldfish-events.
+
+config KEYBOARD_STOWAWAY
+	tristate "Stowaway keyboard"
+	select SERIO
+	help
+	  Say Y here if you have a Stowaway keyboard on a serial port.
+	  Stowaway compatible keyboards like Dicota Input-PDA keyboard
+	  are also supported by this driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called stowaway.
+
+config KEYBOARD_ST_KEYSCAN
+	tristate "STMicroelectronics keyscan support"
+	depends on ARCH_STI || COMPILE_TEST
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if you want to use a keypad attached to the keyscan block
+	  on some STMicroelectronics SoC devices.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called st-keyscan.
+
+config KEYBOARD_SUNKBD
+	tristate "Sun Type 4 and Type 5 keyboard"
+	select SERIO
+	help
+	  Say Y here if you want to use a Sun Type 4 or Type 5 keyboard,
+	  connected either to the Sun keyboard connector or to an serial
+	  (RS-232) port via a simple adapter.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sunkbd.
+
+config KEYBOARD_SH_KEYSC
+	tristate "SuperH KEYSC keypad support"
+	depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
+	help
+	  Say Y here if you want to use a keypad attached to the KEYSC block
+	  on SuperH processors such as sh7722 and sh7343.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sh_keysc.
+
+config KEYBOARD_STMPE
+	tristate "STMPE keypad support"
+	depends on MFD_STMPE
+	depends on OF
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if you want to use the keypad controller on STMPE I/O
+	  expanders.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called stmpe-keypad.
+
+config KEYBOARD_SUN4I_LRADC
+	tristate "Allwinner sun4i low res adc attached tablet keys support"
+	depends on ARCH_SUNXI
+	help
+	  This selects support for the Allwinner low res adc attached tablet
+	  keys found on Allwinner sunxi SoCs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sun4i-lradc-keys.
+
+config KEYBOARD_DAVINCI
+	tristate "TI DaVinci Key Scan"
+	depends on ARCH_DAVINCI_DM365
+	help
+	  Say Y to enable keypad module support for the TI DaVinci
+	  platforms (DM365).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called davinci_keyscan.
+
+config KEYBOARD_IPAQ_MICRO
+	tristate "Buttons on Micro SoC (iPaq h3100,h3600,h3700)"
+	depends on MFD_IPAQ_MICRO
+	help
+	  Say Y to enable support for the buttons attached to
+	  Micro peripheral controller on iPAQ h3100/h3600/h3700
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ipaq-micro-keys.
+
+config KEYBOARD_OMAP
+	tristate "TI OMAP keypad support"
+	depends on ARCH_OMAP1
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if you want to use the OMAP keypad.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called omap-keypad.
+
+config KEYBOARD_OMAP4
+	tristate "TI OMAP4+ keypad support"
+	depends on OF || ARCH_OMAP2PLUS
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if you want to use the OMAP4+ keypad.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called omap4-keypad.
+
+config KEYBOARD_SPEAR
+	tristate "ST SPEAR keyboard support"
+	depends on PLAT_SPEAR
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if you want to use the SPEAR keyboard.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called spear-keboard.
+
+config KEYBOARD_TC3589X
+	tristate "TC3589X Keypad support"
+	depends on MFD_TC3589X
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if you want to use the keypad controller on
+	  TC35892/3 I/O expander.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tc3589x-keypad.
+
+config KEYBOARD_TWL4030
+	tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
+	depends on TWL4030_CORE
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here if your board use the keypad controller on
+	  TWL4030 family chips.  It's safe to say enable this
+	  even on boards that don't use the keypad controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called twl4030_keypad.
+
+config KEYBOARD_XTKBD
+	tristate "XT keyboard"
+	select SERIO
+	help
+	  Say Y here if you want to use the old IBM PC/XT keyboard (or
+	  compatible) on your system. This is only possible with a
+	  parallel port keyboard adapter, you cannot connect it to the
+	  keyboard port on a PC that runs Linux.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called xtkbd.
+
+config KEYBOARD_W90P910
+	tristate "W90P910 Matrix Keypad support"
+	depends on ARCH_W90X900
+	select INPUT_MATRIXKMAP
+	help
+	  Say Y here to enable the matrix keypad on evaluation board
+	  based on W90P910.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called w90p910_keypad.
+
+config KEYBOARD_CROS_EC
+	tristate "ChromeOS EC keyboard"
+	select INPUT_MATRIXKMAP
+	depends on MFD_CROS_EC
+	help
+	  Say Y here to enable the matrix keyboard used by ChromeOS devices
+	  and implemented on the ChromeOS EC. You must enable one bus option
+	  (MFD_CROS_EC_I2C or MFD_CROS_EC_SPI) to use this.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cros_ec_keyb.
+
+config KEYBOARD_CAP11XX
+	tristate "Microchip CAP11XX based touch sensors"
+	depends on OF && I2C
+	select REGMAP_I2C
+	help
+	  Say Y here to enable the CAP11XX touch sensor driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cap11xx.
+
+config KEYBOARD_BCM
+	tristate "Broadcom keypad driver"
+	depends on OF && HAVE_CLK
+	select INPUT_MATRIXKMAP
+	default ARCH_BCM_CYGNUS
+	help
+	  Say Y here if you want to use Broadcom keypad.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bcm-keypad.
+
+endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
new file mode 100644
index 0000000..1d416dd
--- /dev/null
+++ b/drivers/input/keyboard/Makefile
@@ -0,0 +1,65 @@
+#
+# Makefile for the input core drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_KEYBOARD_ADP5520)		+= adp5520-keys.o
+obj-$(CONFIG_KEYBOARD_ADP5588)		+= adp5588-keys.o
+obj-$(CONFIG_KEYBOARD_ADP5589)		+= adp5589-keys.o
+obj-$(CONFIG_KEYBOARD_AMIGA)		+= amikbd.o
+obj-$(CONFIG_KEYBOARD_ATARI)		+= atakbd.o
+obj-$(CONFIG_KEYBOARD_ATKBD)		+= atkbd.o
+obj-$(CONFIG_KEYBOARD_BCM)		+= bcm-keypad.o
+obj-$(CONFIG_KEYBOARD_BFIN)		+= bf54x-keys.o
+obj-$(CONFIG_KEYBOARD_CAP11XX)		+= cap11xx.o
+obj-$(CONFIG_KEYBOARD_CLPS711X)		+= clps711x-keypad.o
+obj-$(CONFIG_KEYBOARD_CROS_EC)		+= cros_ec_keyb.o
+obj-$(CONFIG_KEYBOARD_DAVINCI)		+= davinci_keyscan.o
+obj-$(CONFIG_KEYBOARD_EP93XX)		+= ep93xx_keypad.o
+obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS)	+= goldfish_events.o
+obj-$(CONFIG_KEYBOARD_GPIO)		+= gpio_keys.o
+obj-$(CONFIG_KEYBOARD_GPIO_POLLED)	+= gpio_keys_polled.o
+obj-$(CONFIG_KEYBOARD_TCA6416)		+= tca6416-keypad.o
+obj-$(CONFIG_KEYBOARD_TCA8418)		+= tca8418_keypad.o
+obj-$(CONFIG_KEYBOARD_HIL)		+= hil_kbd.o
+obj-$(CONFIG_KEYBOARD_HIL_OLD)		+= hilkbd.o
+obj-$(CONFIG_KEYBOARD_IPAQ_MICRO)	+= ipaq-micro-keys.o
+obj-$(CONFIG_KEYBOARD_IMX)		+= imx_keypad.o
+obj-$(CONFIG_KEYBOARD_HP6XX)		+= jornada680_kbd.o
+obj-$(CONFIG_KEYBOARD_HP7XX)		+= jornada720_kbd.o
+obj-$(CONFIG_KEYBOARD_LKKBD)		+= lkkbd.o
+obj-$(CONFIG_KEYBOARD_LM8323)		+= lm8323.o
+obj-$(CONFIG_KEYBOARD_LM8333)		+= lm8333.o
+obj-$(CONFIG_KEYBOARD_LOCOMO)		+= locomokbd.o
+obj-$(CONFIG_KEYBOARD_LPC32XX)		+= lpc32xx-keys.o
+obj-$(CONFIG_KEYBOARD_MAPLE)		+= maple_keyb.o
+obj-$(CONFIG_KEYBOARD_MATRIX)		+= matrix_keypad.o
+obj-$(CONFIG_KEYBOARD_MAX7359)		+= max7359_keypad.o
+obj-$(CONFIG_KEYBOARD_MCS)		+= mcs_touchkey.o
+obj-$(CONFIG_KEYBOARD_MPR121)		+= mpr121_touchkey.o
+obj-$(CONFIG_KEYBOARD_NEWTON)		+= newtonkbd.o
+obj-$(CONFIG_KEYBOARD_NOMADIK)		+= nomadik-ske-keypad.o
+obj-$(CONFIG_KEYBOARD_NSPIRE)		+= nspire-keypad.o
+obj-$(CONFIG_KEYBOARD_OMAP)		+= omap-keypad.o
+obj-$(CONFIG_KEYBOARD_OMAP4)		+= omap4-keypad.o
+obj-$(CONFIG_KEYBOARD_OPENCORES)	+= opencores-kbd.o
+obj-$(CONFIG_KEYBOARD_PMIC8XXX)		+= pmic8xxx-keypad.o
+obj-$(CONFIG_KEYBOARD_PXA27x)		+= pxa27x_keypad.o
+obj-$(CONFIG_KEYBOARD_PXA930_ROTARY)	+= pxa930_rotary.o
+obj-$(CONFIG_KEYBOARD_QT1070)           += qt1070.o
+obj-$(CONFIG_KEYBOARD_QT2160)		+= qt2160.o
+obj-$(CONFIG_KEYBOARD_SAMSUNG)		+= samsung-keypad.o
+obj-$(CONFIG_KEYBOARD_SH_KEYSC)		+= sh_keysc.o
+obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY)	+= snvs_pwrkey.o
+obj-$(CONFIG_KEYBOARD_SPEAR)		+= spear-keyboard.o
+obj-$(CONFIG_KEYBOARD_STMPE)		+= stmpe-keypad.o
+obj-$(CONFIG_KEYBOARD_STOWAWAY)		+= stowaway.o
+obj-$(CONFIG_KEYBOARD_ST_KEYSCAN)	+= st-keyscan.o
+obj-$(CONFIG_KEYBOARD_SUN4I_LRADC)	+= sun4i-lradc-keys.o
+obj-$(CONFIG_KEYBOARD_SUNKBD)		+= sunkbd.o
+obj-$(CONFIG_KEYBOARD_TC3589X)		+= tc3589x-keypad.o
+obj-$(CONFIG_KEYBOARD_TEGRA)		+= tegra-kbc.o
+obj-$(CONFIG_KEYBOARD_TWL4030)		+= twl4030_keypad.o
+obj-$(CONFIG_KEYBOARD_XTKBD)		+= xtkbd.o
+obj-$(CONFIG_KEYBOARD_W90P910)		+= w90p910_keypad.o
diff --git a/drivers/input/keyboard/adp5520-keys.c b/drivers/input/keyboard/adp5520-keys.c
new file mode 100644
index 0000000..db1004d
--- /dev/null
+++ b/drivers/input/keyboard/adp5520-keys.c
@@ -0,0 +1,196 @@
+/*
+ * Keypad driver for Analog Devices ADP5520 MFD PMICs
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/mfd/adp5520.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+struct adp5520_keys {
+	struct input_dev *input;
+	struct notifier_block notifier;
+	struct device *master;
+	unsigned short keycode[ADP5520_KEYMAPSIZE];
+};
+
+static void adp5520_keys_report_event(struct adp5520_keys *dev,
+					unsigned short keymask, int value)
+{
+	int i;
+
+	for (i = 0; i < ADP5520_MAXKEYS; i++)
+		if (keymask & (1 << i))
+			input_report_key(dev->input, dev->keycode[i], value);
+
+	input_sync(dev->input);
+}
+
+static int adp5520_keys_notifier(struct notifier_block *nb,
+				 unsigned long event, void *data)
+{
+	struct adp5520_keys *dev;
+	uint8_t reg_val_lo, reg_val_hi;
+	unsigned short keymask;
+
+	dev = container_of(nb, struct adp5520_keys, notifier);
+
+	if (event & ADP5520_KP_INT) {
+		adp5520_read(dev->master, ADP5520_KP_INT_STAT_1, &reg_val_lo);
+		adp5520_read(dev->master, ADP5520_KP_INT_STAT_2, &reg_val_hi);
+
+		keymask = (reg_val_hi << 8) | reg_val_lo;
+		/* Read twice to clear */
+		adp5520_read(dev->master, ADP5520_KP_INT_STAT_1, &reg_val_lo);
+		adp5520_read(dev->master, ADP5520_KP_INT_STAT_2, &reg_val_hi);
+		keymask |= (reg_val_hi << 8) | reg_val_lo;
+		adp5520_keys_report_event(dev, keymask, 1);
+	}
+
+	if (event & ADP5520_KR_INT) {
+		adp5520_read(dev->master, ADP5520_KR_INT_STAT_1, &reg_val_lo);
+		adp5520_read(dev->master, ADP5520_KR_INT_STAT_2, &reg_val_hi);
+
+		keymask = (reg_val_hi << 8) | reg_val_lo;
+		/* Read twice to clear */
+		adp5520_read(dev->master, ADP5520_KR_INT_STAT_1, &reg_val_lo);
+		adp5520_read(dev->master, ADP5520_KR_INT_STAT_2, &reg_val_hi);
+		keymask |= (reg_val_hi << 8) | reg_val_lo;
+		adp5520_keys_report_event(dev, keymask, 0);
+	}
+
+	return 0;
+}
+
+static int adp5520_keys_probe(struct platform_device *pdev)
+{
+	struct adp5520_keys_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct input_dev *input;
+	struct adp5520_keys *dev;
+	int ret, i;
+	unsigned char en_mask, ctl_mask = 0;
+
+	if (pdev->id != ID_ADP5520) {
+		dev_err(&pdev->dev, "only ADP5520 supports Keypad\n");
+		return -EINVAL;
+	}
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "missing platform data\n");
+		return -EINVAL;
+	}
+
+	if (!(pdata->rows_en_mask && pdata->cols_en_mask))
+		return -EINVAL;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		dev_err(&pdev->dev, "failed to alloc memory\n");
+		return -ENOMEM;
+	}
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input)
+		return -ENOMEM;
+
+	dev->master = pdev->dev.parent;
+	dev->input = input;
+
+	input->name = pdev->name;
+	input->phys = "adp5520-keys/input0";
+	input->dev.parent = &pdev->dev;
+
+	input_set_drvdata(input, dev);
+
+	input->id.bustype = BUS_I2C;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x5520;
+	input->id.version = 0x0001;
+
+	input->keycodesize = sizeof(dev->keycode[0]);
+	input->keycodemax = pdata->keymapsize;
+	input->keycode = dev->keycode;
+
+	memcpy(dev->keycode, pdata->keymap,
+		pdata->keymapsize * input->keycodesize);
+
+	/* setup input device */
+	__set_bit(EV_KEY, input->evbit);
+
+	if (pdata->repeat)
+		__set_bit(EV_REP, input->evbit);
+
+	for (i = 0; i < input->keycodemax; i++)
+		__set_bit(dev->keycode[i], input->keybit);
+	__clear_bit(KEY_RESERVED, input->keybit);
+
+	ret = input_register_device(input);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register input device\n");
+		return ret;
+	}
+
+	en_mask = pdata->rows_en_mask | pdata->cols_en_mask;
+
+	ret = adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_1, en_mask);
+
+	if (en_mask & ADP5520_COL_C3)
+		ctl_mask |= ADP5520_C3_MODE;
+
+	if (en_mask & ADP5520_ROW_R3)
+		ctl_mask |= ADP5520_R3_MODE;
+
+	if (ctl_mask)
+		ret |= adp5520_set_bits(dev->master, ADP5520_LED_CONTROL,
+			ctl_mask);
+
+	ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP,
+		pdata->rows_en_mask);
+
+	if (ret) {
+		dev_err(&pdev->dev, "failed to write\n");
+		return -EIO;
+	}
+
+	dev->notifier.notifier_call = adp5520_keys_notifier;
+	ret = adp5520_register_notifier(dev->master, &dev->notifier,
+			ADP5520_KP_IEN | ADP5520_KR_IEN);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register notifier\n");
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, dev);
+	return 0;
+}
+
+static int adp5520_keys_remove(struct platform_device *pdev)
+{
+	struct adp5520_keys *dev = platform_get_drvdata(pdev);
+
+	adp5520_unregister_notifier(dev->master, &dev->notifier,
+				ADP5520_KP_IEN | ADP5520_KR_IEN);
+
+	return 0;
+}
+
+static struct platform_driver adp5520_keys_driver = {
+	.driver	= {
+		.name	= "adp5520-keys",
+	},
+	.probe		= adp5520_keys_probe,
+	.remove		= adp5520_keys_remove,
+};
+module_platform_driver(adp5520_keys_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Keys ADP5520 Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:adp5520-keys");
diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c
new file mode 100644
index 0000000..21a62d0
--- /dev/null
+++ b/drivers/input/keyboard/adp5588-keys.c
@@ -0,0 +1,672 @@
+/*
+ * File: drivers/input/keyboard/adp5588_keys.c
+ * Description:  keypad driver for ADP5588 and ADP5587
+ *		 I2C QWERTY Keypad and IO Expander
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2008-2010 Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/workqueue.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#include <linux/i2c/adp5588.h>
+
+/* Key Event Register xy */
+#define KEY_EV_PRESSED		(1 << 7)
+#define KEY_EV_MASK		(0x7F)
+
+#define KP_SEL(x)		(0xFFFF >> (16 - x))	/* 2^x-1 */
+
+#define KEYP_MAX_EVENT		10
+
+/*
+ * Early pre 4.0 Silicon required to delay readout by at least 25ms,
+ * since the Event Counter Register updated 25ms after the interrupt
+ * asserted.
+ */
+#define WA_DELAYED_READOUT_REVID(rev)		((rev) < 4)
+
+struct adp5588_kpad {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct delayed_work work;
+	unsigned long delay;
+	unsigned short keycode[ADP5588_KEYMAPSIZE];
+	const struct adp5588_gpi_map *gpimap;
+	unsigned short gpimapsize;
+#ifdef CONFIG_GPIOLIB
+	unsigned char gpiomap[ADP5588_MAXGPIO];
+	bool export_gpio;
+	struct gpio_chip gc;
+	struct mutex gpio_lock;	/* Protect cached dir, dat_out */
+	u8 dat_out[3];
+	u8 dir[3];
+#endif
+};
+
+static int adp5588_read(struct i2c_client *client, u8 reg)
+{
+	int ret = i2c_smbus_read_byte_data(client, reg);
+
+	if (ret < 0)
+		dev_err(&client->dev, "Read Error\n");
+
+	return ret;
+}
+
+static int adp5588_write(struct i2c_client *client, u8 reg, u8 val)
+{
+	return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+#ifdef CONFIG_GPIOLIB
+static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off)
+{
+	struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc);
+	unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
+	unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
+	int val;
+
+	mutex_lock(&kpad->gpio_lock);
+
+	if (kpad->dir[bank] & bit)
+		val = kpad->dat_out[bank];
+	else
+		val = adp5588_read(kpad->client, GPIO_DAT_STAT1 + bank);
+
+	mutex_unlock(&kpad->gpio_lock);
+
+	return !!(val & bit);
+}
+
+static void adp5588_gpio_set_value(struct gpio_chip *chip,
+				   unsigned off, int val)
+{
+	struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc);
+	unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
+	unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
+
+	mutex_lock(&kpad->gpio_lock);
+
+	if (val)
+		kpad->dat_out[bank] |= bit;
+	else
+		kpad->dat_out[bank] &= ~bit;
+
+	adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
+			   kpad->dat_out[bank]);
+
+	mutex_unlock(&kpad->gpio_lock);
+}
+
+static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off)
+{
+	struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc);
+	unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
+	unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
+	int ret;
+
+	mutex_lock(&kpad->gpio_lock);
+
+	kpad->dir[bank] &= ~bit;
+	ret = adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]);
+
+	mutex_unlock(&kpad->gpio_lock);
+
+	return ret;
+}
+
+static int adp5588_gpio_direction_output(struct gpio_chip *chip,
+					 unsigned off, int val)
+{
+	struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc);
+	unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
+	unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
+	int ret;
+
+	mutex_lock(&kpad->gpio_lock);
+
+	kpad->dir[bank] |= bit;
+
+	if (val)
+		kpad->dat_out[bank] |= bit;
+	else
+		kpad->dat_out[bank] &= ~bit;
+
+	ret = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
+				 kpad->dat_out[bank]);
+	ret |= adp5588_write(kpad->client, GPIO_DIR1 + bank,
+				 kpad->dir[bank]);
+
+	mutex_unlock(&kpad->gpio_lock);
+
+	return ret;
+}
+
+static int adp5588_build_gpiomap(struct adp5588_kpad *kpad,
+				const struct adp5588_kpad_platform_data *pdata)
+{
+	bool pin_used[ADP5588_MAXGPIO];
+	int n_unused = 0;
+	int i;
+
+	memset(pin_used, 0, sizeof(pin_used));
+
+	for (i = 0; i < pdata->rows; i++)
+		pin_used[i] = true;
+
+	for (i = 0; i < pdata->cols; i++)
+		pin_used[i + GPI_PIN_COL_BASE - GPI_PIN_BASE] = true;
+
+	for (i = 0; i < kpad->gpimapsize; i++)
+		pin_used[kpad->gpimap[i].pin - GPI_PIN_BASE] = true;
+
+	for (i = 0; i < ADP5588_MAXGPIO; i++)
+		if (!pin_used[i])
+			kpad->gpiomap[n_unused++] = i;
+
+	return n_unused;
+}
+
+static int adp5588_gpio_add(struct adp5588_kpad *kpad)
+{
+	struct device *dev = &kpad->client->dev;
+	const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev);
+	const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
+	int i, error;
+
+	if (!gpio_data)
+		return 0;
+
+	kpad->gc.ngpio = adp5588_build_gpiomap(kpad, pdata);
+	if (kpad->gc.ngpio == 0) {
+		dev_info(dev, "No unused gpios left to export\n");
+		return 0;
+	}
+
+	kpad->export_gpio = true;
+
+	kpad->gc.direction_input = adp5588_gpio_direction_input;
+	kpad->gc.direction_output = adp5588_gpio_direction_output;
+	kpad->gc.get = adp5588_gpio_get_value;
+	kpad->gc.set = adp5588_gpio_set_value;
+	kpad->gc.can_sleep = 1;
+
+	kpad->gc.base = gpio_data->gpio_start;
+	kpad->gc.label = kpad->client->name;
+	kpad->gc.owner = THIS_MODULE;
+	kpad->gc.names = gpio_data->names;
+
+	mutex_init(&kpad->gpio_lock);
+
+	error = gpiochip_add(&kpad->gc);
+	if (error) {
+		dev_err(dev, "gpiochip_add failed, err: %d\n", error);
+		return error;
+	}
+
+	for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
+		kpad->dat_out[i] = adp5588_read(kpad->client,
+						GPIO_DAT_OUT1 + i);
+		kpad->dir[i] = adp5588_read(kpad->client, GPIO_DIR1 + i);
+	}
+
+	if (gpio_data->setup) {
+		error = gpio_data->setup(kpad->client,
+					 kpad->gc.base, kpad->gc.ngpio,
+					 gpio_data->context);
+		if (error)
+			dev_warn(dev, "setup failed, %d\n", error);
+	}
+
+	return 0;
+}
+
+static void adp5588_gpio_remove(struct adp5588_kpad *kpad)
+{
+	struct device *dev = &kpad->client->dev;
+	const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev);
+	const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
+	int error;
+
+	if (!kpad->export_gpio)
+		return;
+
+	if (gpio_data->teardown) {
+		error = gpio_data->teardown(kpad->client,
+					    kpad->gc.base, kpad->gc.ngpio,
+					    gpio_data->context);
+		if (error)
+			dev_warn(dev, "teardown failed %d\n", error);
+	}
+
+	gpiochip_remove(&kpad->gc);
+}
+#else
+static inline int adp5588_gpio_add(struct adp5588_kpad *kpad)
+{
+	return 0;
+}
+
+static inline void adp5588_gpio_remove(struct adp5588_kpad *kpad)
+{
+}
+#endif
+
+static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt)
+{
+	int i, j;
+
+	for (i = 0; i < ev_cnt; i++) {
+		int key = adp5588_read(kpad->client, Key_EVENTA + i);
+		int key_val = key & KEY_EV_MASK;
+
+		if (key_val >= GPI_PIN_BASE && key_val <= GPI_PIN_END) {
+			for (j = 0; j < kpad->gpimapsize; j++) {
+				if (key_val == kpad->gpimap[j].pin) {
+					input_report_switch(kpad->input,
+							kpad->gpimap[j].sw_evt,
+							key & KEY_EV_PRESSED);
+					break;
+				}
+			}
+		} else {
+			input_report_key(kpad->input,
+					 kpad->keycode[key_val - 1],
+					 key & KEY_EV_PRESSED);
+		}
+	}
+}
+
+static void adp5588_work(struct work_struct *work)
+{
+	struct adp5588_kpad *kpad = container_of(work,
+						struct adp5588_kpad, work.work);
+	struct i2c_client *client = kpad->client;
+	int status, ev_cnt;
+
+	status = adp5588_read(client, INT_STAT);
+
+	if (status & ADP5588_OVR_FLOW_INT)	/* Unlikely and should never happen */
+		dev_err(&client->dev, "Event Overflow Error\n");
+
+	if (status & ADP5588_KE_INT) {
+		ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & ADP5588_KEC;
+		if (ev_cnt) {
+			adp5588_report_events(kpad, ev_cnt);
+			input_sync(kpad->input);
+		}
+	}
+	adp5588_write(client, INT_STAT, status); /* Status is W1C */
+}
+
+static irqreturn_t adp5588_irq(int irq, void *handle)
+{
+	struct adp5588_kpad *kpad = handle;
+
+	/*
+	 * use keventd context to read the event fifo registers
+	 * Schedule readout at least 25ms after notification for
+	 * REVID < 4
+	 */
+
+	schedule_delayed_work(&kpad->work, kpad->delay);
+
+	return IRQ_HANDLED;
+}
+
+static int adp5588_setup(struct i2c_client *client)
+{
+	const struct adp5588_kpad_platform_data *pdata =
+			dev_get_platdata(&client->dev);
+	const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
+	int i, ret;
+	unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0;
+
+	ret = adp5588_write(client, KP_GPIO1, KP_SEL(pdata->rows));
+	ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF);
+	ret |= adp5588_write(client, KP_GPIO3, KP_SEL(pdata->cols) >> 8);
+
+	if (pdata->en_keylock) {
+		ret |= adp5588_write(client, UNLOCK1, pdata->unlock_key1);
+		ret |= adp5588_write(client, UNLOCK2, pdata->unlock_key2);
+		ret |= adp5588_write(client, KEY_LCK_EC_STAT, ADP5588_K_LCK_EN);
+	}
+
+	for (i = 0; i < KEYP_MAX_EVENT; i++)
+		ret |= adp5588_read(client, Key_EVENTA);
+
+	for (i = 0; i < pdata->gpimapsize; i++) {
+		unsigned short pin = pdata->gpimap[i].pin;
+
+		if (pin <= GPI_PIN_ROW_END) {
+			evt_mode1 |= (1 << (pin - GPI_PIN_ROW_BASE));
+		} else {
+			evt_mode2 |= ((1 << (pin - GPI_PIN_COL_BASE)) & 0xFF);
+			evt_mode3 |= ((1 << (pin - GPI_PIN_COL_BASE)) >> 8);
+		}
+	}
+
+	if (pdata->gpimapsize) {
+		ret |= adp5588_write(client, GPI_EM1, evt_mode1);
+		ret |= adp5588_write(client, GPI_EM2, evt_mode2);
+		ret |= adp5588_write(client, GPI_EM3, evt_mode3);
+	}
+
+	if (gpio_data) {
+		for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
+			int pull_mask = gpio_data->pullup_dis_mask;
+
+			ret |= adp5588_write(client, GPIO_PULL1 + i,
+				(pull_mask >> (8 * i)) & 0xFF);
+		}
+	}
+
+	ret |= adp5588_write(client, INT_STAT,
+				ADP5588_CMP2_INT | ADP5588_CMP1_INT |
+				ADP5588_OVR_FLOW_INT | ADP5588_K_LCK_INT |
+				ADP5588_GPI_INT | ADP5588_KE_INT); /* Status is W1C */
+
+	ret |= adp5588_write(client, CFG, ADP5588_INT_CFG |
+					  ADP5588_OVR_FLOW_IEN |
+					  ADP5588_KE_IEN);
+
+	if (ret < 0) {
+		dev_err(&client->dev, "Write Error\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void adp5588_report_switch_state(struct adp5588_kpad *kpad)
+{
+	int gpi_stat1 = adp5588_read(kpad->client, GPIO_DAT_STAT1);
+	int gpi_stat2 = adp5588_read(kpad->client, GPIO_DAT_STAT2);
+	int gpi_stat3 = adp5588_read(kpad->client, GPIO_DAT_STAT3);
+	int gpi_stat_tmp, pin_loc;
+	int i;
+
+	for (i = 0; i < kpad->gpimapsize; i++) {
+		unsigned short pin = kpad->gpimap[i].pin;
+
+		if (pin <= GPI_PIN_ROW_END) {
+			gpi_stat_tmp = gpi_stat1;
+			pin_loc = pin - GPI_PIN_ROW_BASE;
+		} else if ((pin - GPI_PIN_COL_BASE) < 8) {
+			gpi_stat_tmp = gpi_stat2;
+			pin_loc = pin - GPI_PIN_COL_BASE;
+		} else {
+			gpi_stat_tmp = gpi_stat3;
+			pin_loc = pin - GPI_PIN_COL_BASE - 8;
+		}
+
+		if (gpi_stat_tmp < 0) {
+			dev_err(&kpad->client->dev,
+				"Can't read GPIO_DAT_STAT switch %d default to OFF\n",
+				pin);
+			gpi_stat_tmp = 0;
+		}
+
+		input_report_switch(kpad->input,
+				    kpad->gpimap[i].sw_evt,
+				    !(gpi_stat_tmp & (1 << pin_loc)));
+	}
+
+	input_sync(kpad->input);
+}
+
+
+static int adp5588_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct adp5588_kpad *kpad;
+	const struct adp5588_kpad_platform_data *pdata =
+			dev_get_platdata(&client->dev);
+	struct input_dev *input;
+	unsigned int revid;
+	int ret, i;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+		return -EIO;
+	}
+
+	if (!pdata) {
+		dev_err(&client->dev, "no platform data?\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->rows || !pdata->cols || !pdata->keymap) {
+		dev_err(&client->dev, "no rows, cols or keymap from pdata\n");
+		return -EINVAL;
+	}
+
+	if (pdata->keymapsize != ADP5588_KEYMAPSIZE) {
+		dev_err(&client->dev, "invalid keymapsize\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->gpimap && pdata->gpimapsize) {
+		dev_err(&client->dev, "invalid gpimap from pdata\n");
+		return -EINVAL;
+	}
+
+	if (pdata->gpimapsize > ADP5588_GPIMAPSIZE_MAX) {
+		dev_err(&client->dev, "invalid gpimapsize\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < pdata->gpimapsize; i++) {
+		unsigned short pin = pdata->gpimap[i].pin;
+
+		if (pin < GPI_PIN_BASE || pin > GPI_PIN_END) {
+			dev_err(&client->dev, "invalid gpi pin data\n");
+			return -EINVAL;
+		}
+
+		if (pin <= GPI_PIN_ROW_END) {
+			if (pin - GPI_PIN_ROW_BASE + 1 <= pdata->rows) {
+				dev_err(&client->dev, "invalid gpi row data\n");
+				return -EINVAL;
+			}
+		} else {
+			if (pin - GPI_PIN_COL_BASE + 1 <= pdata->cols) {
+				dev_err(&client->dev, "invalid gpi col data\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	if (!client->irq) {
+		dev_err(&client->dev, "no IRQ?\n");
+		return -EINVAL;
+	}
+
+	kpad = kzalloc(sizeof(*kpad), GFP_KERNEL);
+	input = input_allocate_device();
+	if (!kpad || !input) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	kpad->client = client;
+	kpad->input = input;
+	INIT_DELAYED_WORK(&kpad->work, adp5588_work);
+
+	ret = adp5588_read(client, DEV_ID);
+	if (ret < 0) {
+		error = ret;
+		goto err_free_mem;
+	}
+
+	revid = (u8) ret & ADP5588_DEVICE_ID_MASK;
+	if (WA_DELAYED_READOUT_REVID(revid))
+		kpad->delay = msecs_to_jiffies(30);
+
+	input->name = client->name;
+	input->phys = "adp5588-keys/input0";
+	input->dev.parent = &client->dev;
+
+	input_set_drvdata(input, kpad);
+
+	input->id.bustype = BUS_I2C;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = revid;
+
+	input->keycodesize = sizeof(kpad->keycode[0]);
+	input->keycodemax = pdata->keymapsize;
+	input->keycode = kpad->keycode;
+
+	memcpy(kpad->keycode, pdata->keymap,
+		pdata->keymapsize * input->keycodesize);
+
+	kpad->gpimap = pdata->gpimap;
+	kpad->gpimapsize = pdata->gpimapsize;
+
+	/* setup input device */
+	__set_bit(EV_KEY, input->evbit);
+
+	if (pdata->repeat)
+		__set_bit(EV_REP, input->evbit);
+
+	for (i = 0; i < input->keycodemax; i++)
+		if (kpad->keycode[i] <= KEY_MAX)
+			__set_bit(kpad->keycode[i], input->keybit);
+	__clear_bit(KEY_RESERVED, input->keybit);
+
+	if (kpad->gpimapsize)
+		__set_bit(EV_SW, input->evbit);
+	for (i = 0; i < kpad->gpimapsize; i++)
+		__set_bit(kpad->gpimap[i].sw_evt, input->swbit);
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(&client->dev, "unable to register input device\n");
+		goto err_free_mem;
+	}
+
+	error = request_irq(client->irq, adp5588_irq,
+			    IRQF_TRIGGER_FALLING,
+			    client->dev.driver->name, kpad);
+	if (error) {
+		dev_err(&client->dev, "irq %d busy?\n", client->irq);
+		goto err_unreg_dev;
+	}
+
+	error = adp5588_setup(client);
+	if (error)
+		goto err_free_irq;
+
+	if (kpad->gpimapsize)
+		adp5588_report_switch_state(kpad);
+
+	error = adp5588_gpio_add(kpad);
+	if (error)
+		goto err_free_irq;
+
+	device_init_wakeup(&client->dev, 1);
+	i2c_set_clientdata(client, kpad);
+
+	dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
+	return 0;
+
+ err_free_irq:
+	free_irq(client->irq, kpad);
+	cancel_delayed_work_sync(&kpad->work);
+ err_unreg_dev:
+	input_unregister_device(input);
+	input = NULL;
+ err_free_mem:
+	input_free_device(input);
+	kfree(kpad);
+
+	return error;
+}
+
+static int adp5588_remove(struct i2c_client *client)
+{
+	struct adp5588_kpad *kpad = i2c_get_clientdata(client);
+
+	adp5588_write(client, CFG, 0);
+	free_irq(client->irq, kpad);
+	cancel_delayed_work_sync(&kpad->work);
+	input_unregister_device(kpad->input);
+	adp5588_gpio_remove(kpad);
+	kfree(kpad);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int adp5588_suspend(struct device *dev)
+{
+	struct adp5588_kpad *kpad = dev_get_drvdata(dev);
+	struct i2c_client *client = kpad->client;
+
+	disable_irq(client->irq);
+	cancel_delayed_work_sync(&kpad->work);
+
+	if (device_may_wakeup(&client->dev))
+		enable_irq_wake(client->irq);
+
+	return 0;
+}
+
+static int adp5588_resume(struct device *dev)
+{
+	struct adp5588_kpad *kpad = dev_get_drvdata(dev);
+	struct i2c_client *client = kpad->client;
+
+	if (device_may_wakeup(&client->dev))
+		disable_irq_wake(client->irq);
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static const struct dev_pm_ops adp5588_dev_pm_ops = {
+	.suspend = adp5588_suspend,
+	.resume  = adp5588_resume,
+};
+#endif
+
+static const struct i2c_device_id adp5588_id[] = {
+	{ "adp5588-keys", 0 },
+	{ "adp5587-keys", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adp5588_id);
+
+static struct i2c_driver adp5588_driver = {
+	.driver = {
+		.name = KBUILD_MODNAME,
+#ifdef CONFIG_PM
+		.pm   = &adp5588_dev_pm_ops,
+#endif
+	},
+	.probe    = adp5588_probe,
+	.remove   = adp5588_remove,
+	.id_table = adp5588_id,
+};
+
+module_i2c_driver(adp5588_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADP5588/87 Keypad driver");
diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c
new file mode 100644
index 0000000..4d446d5
--- /dev/null
+++ b/drivers/input/keyboard/adp5589-keys.c
@@ -0,0 +1,1111 @@
+/*
+ * Description:  keypad driver for ADP5589, ADP5585
+ *		 I2C QWERTY Keypad and IO Expander
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2010-2011 Analog Devices Inc.
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/workqueue.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#include <linux/input/adp5589.h>
+
+/* ADP5589/ADP5585 Common Registers */
+#define ADP5589_5_ID			0x00
+#define ADP5589_5_INT_STATUS		0x01
+#define ADP5589_5_STATUS		0x02
+#define ADP5589_5_FIFO_1		0x03
+#define ADP5589_5_FIFO_2		0x04
+#define ADP5589_5_FIFO_3		0x05
+#define ADP5589_5_FIFO_4		0x06
+#define ADP5589_5_FIFO_5		0x07
+#define ADP5589_5_FIFO_6		0x08
+#define ADP5589_5_FIFO_7		0x09
+#define ADP5589_5_FIFO_8		0x0A
+#define ADP5589_5_FIFO_9		0x0B
+#define ADP5589_5_FIFO_10		0x0C
+#define ADP5589_5_FIFO_11		0x0D
+#define ADP5589_5_FIFO_12		0x0E
+#define ADP5589_5_FIFO_13		0x0F
+#define ADP5589_5_FIFO_14		0x10
+#define ADP5589_5_FIFO_15		0x11
+#define ADP5589_5_FIFO_16		0x12
+#define ADP5589_5_GPI_INT_STAT_A	0x13
+#define ADP5589_5_GPI_INT_STAT_B	0x14
+
+/* ADP5589 Registers */
+#define ADP5589_GPI_INT_STAT_C		0x15
+#define ADP5589_GPI_STATUS_A		0x16
+#define ADP5589_GPI_STATUS_B		0x17
+#define ADP5589_GPI_STATUS_C		0x18
+#define ADP5589_RPULL_CONFIG_A		0x19
+#define ADP5589_RPULL_CONFIG_B		0x1A
+#define ADP5589_RPULL_CONFIG_C		0x1B
+#define ADP5589_RPULL_CONFIG_D		0x1C
+#define ADP5589_RPULL_CONFIG_E		0x1D
+#define ADP5589_GPI_INT_LEVEL_A		0x1E
+#define ADP5589_GPI_INT_LEVEL_B		0x1F
+#define ADP5589_GPI_INT_LEVEL_C		0x20
+#define ADP5589_GPI_EVENT_EN_A		0x21
+#define ADP5589_GPI_EVENT_EN_B		0x22
+#define ADP5589_GPI_EVENT_EN_C		0x23
+#define ADP5589_GPI_INTERRUPT_EN_A	0x24
+#define ADP5589_GPI_INTERRUPT_EN_B	0x25
+#define ADP5589_GPI_INTERRUPT_EN_C	0x26
+#define ADP5589_DEBOUNCE_DIS_A		0x27
+#define ADP5589_DEBOUNCE_DIS_B		0x28
+#define ADP5589_DEBOUNCE_DIS_C		0x29
+#define ADP5589_GPO_DATA_OUT_A		0x2A
+#define ADP5589_GPO_DATA_OUT_B		0x2B
+#define ADP5589_GPO_DATA_OUT_C		0x2C
+#define ADP5589_GPO_OUT_MODE_A		0x2D
+#define ADP5589_GPO_OUT_MODE_B		0x2E
+#define ADP5589_GPO_OUT_MODE_C		0x2F
+#define ADP5589_GPIO_DIRECTION_A	0x30
+#define ADP5589_GPIO_DIRECTION_B	0x31
+#define ADP5589_GPIO_DIRECTION_C	0x32
+#define ADP5589_UNLOCK1			0x33
+#define ADP5589_UNLOCK2			0x34
+#define ADP5589_EXT_LOCK_EVENT		0x35
+#define ADP5589_UNLOCK_TIMERS		0x36
+#define ADP5589_LOCK_CFG		0x37
+#define ADP5589_RESET1_EVENT_A		0x38
+#define ADP5589_RESET1_EVENT_B		0x39
+#define ADP5589_RESET1_EVENT_C		0x3A
+#define ADP5589_RESET2_EVENT_A		0x3B
+#define ADP5589_RESET2_EVENT_B		0x3C
+#define ADP5589_RESET_CFG		0x3D
+#define ADP5589_PWM_OFFT_LOW		0x3E
+#define ADP5589_PWM_OFFT_HIGH		0x3F
+#define ADP5589_PWM_ONT_LOW		0x40
+#define ADP5589_PWM_ONT_HIGH		0x41
+#define ADP5589_PWM_CFG			0x42
+#define ADP5589_CLOCK_DIV_CFG		0x43
+#define ADP5589_LOGIC_1_CFG		0x44
+#define ADP5589_LOGIC_2_CFG		0x45
+#define ADP5589_LOGIC_FF_CFG		0x46
+#define ADP5589_LOGIC_INT_EVENT_EN	0x47
+#define ADP5589_POLL_PTIME_CFG		0x48
+#define ADP5589_PIN_CONFIG_A		0x49
+#define ADP5589_PIN_CONFIG_B		0x4A
+#define ADP5589_PIN_CONFIG_C		0x4B
+#define ADP5589_PIN_CONFIG_D		0x4C
+#define ADP5589_GENERAL_CFG		0x4D
+#define ADP5589_INT_EN			0x4E
+
+/* ADP5585 Registers */
+#define ADP5585_GPI_STATUS_A		0x15
+#define ADP5585_GPI_STATUS_B		0x16
+#define ADP5585_RPULL_CONFIG_A		0x17
+#define ADP5585_RPULL_CONFIG_B		0x18
+#define ADP5585_RPULL_CONFIG_C		0x19
+#define ADP5585_RPULL_CONFIG_D		0x1A
+#define ADP5585_GPI_INT_LEVEL_A		0x1B
+#define ADP5585_GPI_INT_LEVEL_B		0x1C
+#define ADP5585_GPI_EVENT_EN_A		0x1D
+#define ADP5585_GPI_EVENT_EN_B		0x1E
+#define ADP5585_GPI_INTERRUPT_EN_A	0x1F
+#define ADP5585_GPI_INTERRUPT_EN_B	0x20
+#define ADP5585_DEBOUNCE_DIS_A		0x21
+#define ADP5585_DEBOUNCE_DIS_B		0x22
+#define ADP5585_GPO_DATA_OUT_A		0x23
+#define ADP5585_GPO_DATA_OUT_B		0x24
+#define ADP5585_GPO_OUT_MODE_A		0x25
+#define ADP5585_GPO_OUT_MODE_B		0x26
+#define ADP5585_GPIO_DIRECTION_A	0x27
+#define ADP5585_GPIO_DIRECTION_B	0x28
+#define ADP5585_RESET1_EVENT_A		0x29
+#define ADP5585_RESET1_EVENT_B		0x2A
+#define ADP5585_RESET1_EVENT_C		0x2B
+#define ADP5585_RESET2_EVENT_A		0x2C
+#define ADP5585_RESET2_EVENT_B		0x2D
+#define ADP5585_RESET_CFG		0x2E
+#define ADP5585_PWM_OFFT_LOW		0x2F
+#define ADP5585_PWM_OFFT_HIGH		0x30
+#define ADP5585_PWM_ONT_LOW		0x31
+#define ADP5585_PWM_ONT_HIGH		0x32
+#define ADP5585_PWM_CFG			0x33
+#define ADP5585_LOGIC_CFG		0x34
+#define ADP5585_LOGIC_FF_CFG		0x35
+#define ADP5585_LOGIC_INT_EVENT_EN	0x36
+#define ADP5585_POLL_PTIME_CFG		0x37
+#define ADP5585_PIN_CONFIG_A		0x38
+#define ADP5585_PIN_CONFIG_B		0x39
+#define ADP5585_PIN_CONFIG_D		0x3A
+#define ADP5585_GENERAL_CFG		0x3B
+#define ADP5585_INT_EN			0x3C
+
+/* ID Register */
+#define ADP5589_5_DEVICE_ID_MASK	0xF
+#define ADP5589_5_MAN_ID_MASK		0xF
+#define ADP5589_5_MAN_ID_SHIFT		4
+#define ADP5589_5_MAN_ID		0x02
+
+/* GENERAL_CFG Register */
+#define OSC_EN		(1 << 7)
+#define CORE_CLK(x)	(((x) & 0x3) << 5)
+#define LCK_TRK_LOGIC	(1 << 4)	/* ADP5589 only */
+#define LCK_TRK_GPI	(1 << 3)	/* ADP5589 only */
+#define INT_CFG		(1 << 1)
+#define RST_CFG		(1 << 0)
+
+/* INT_EN Register */
+#define LOGIC2_IEN	(1 << 5)	/* ADP5589 only */
+#define LOGIC1_IEN	(1 << 4)
+#define LOCK_IEN	(1 << 3)	/* ADP5589 only */
+#define OVRFLOW_IEN	(1 << 2)
+#define GPI_IEN		(1 << 1)
+#define EVENT_IEN	(1 << 0)
+
+/* Interrupt Status Register */
+#define LOGIC2_INT	(1 << 5)	/* ADP5589 only */
+#define LOGIC1_INT	(1 << 4)
+#define LOCK_INT	(1 << 3)	/* ADP5589 only */
+#define OVRFLOW_INT	(1 << 2)
+#define GPI_INT		(1 << 1)
+#define EVENT_INT	(1 << 0)
+
+/* STATUS Register */
+#define LOGIC2_STAT	(1 << 7)	/* ADP5589 only */
+#define LOGIC1_STAT	(1 << 6)
+#define LOCK_STAT	(1 << 5)	/* ADP5589 only */
+#define KEC		0x1F
+
+/* PIN_CONFIG_D Register */
+#define C4_EXTEND_CFG	(1 << 6)	/* RESET2 */
+#define R4_EXTEND_CFG	(1 << 5)	/* RESET1 */
+
+/* LOCK_CFG */
+#define LOCK_EN		(1 << 0)
+
+#define PTIME_MASK	0x3
+#define LTIME_MASK	0x3		/* ADP5589 only */
+
+/* Key Event Register xy */
+#define KEY_EV_PRESSED		(1 << 7)
+#define KEY_EV_MASK		(0x7F)
+
+#define KEYP_MAX_EVENT		16
+#define ADP5589_MAXGPIO		19
+#define ADP5585_MAXGPIO		11 /* 10 on the ADP5585-01, 11 on ADP5585-02 */
+
+enum {
+	ADP5589,
+	ADP5585_01,
+	ADP5585_02
+};
+
+struct adp_constants {
+	u8 maxgpio;
+	u8 keymapsize;
+	u8 gpi_pin_row_base;
+	u8 gpi_pin_row_end;
+	u8 gpi_pin_col_base;
+	u8 gpi_pin_base;
+	u8 gpi_pin_end;
+	u8 gpimapsize_max;
+	u8 max_row_num;
+	u8 max_col_num;
+	u8 row_mask;
+	u8 col_mask;
+	u8 col_shift;
+	u8 c4_extend_cfg;
+	u8 (*bank) (u8 offset);
+	u8 (*bit) (u8 offset);
+	u8 (*reg) (u8 reg);
+};
+
+struct adp5589_kpad {
+	struct i2c_client *client;
+	struct input_dev *input;
+	const struct adp_constants *var;
+	unsigned short keycode[ADP5589_KEYMAPSIZE];
+	const struct adp5589_gpi_map *gpimap;
+	unsigned short gpimapsize;
+	unsigned extend_cfg;
+	bool is_adp5585;
+	bool adp5585_support_row5;
+#ifdef CONFIG_GPIOLIB
+	unsigned char gpiomap[ADP5589_MAXGPIO];
+	bool export_gpio;
+	struct gpio_chip gc;
+	struct mutex gpio_lock;	/* Protect cached dir, dat_out */
+	u8 dat_out[3];
+	u8 dir[3];
+#endif
+};
+
+/*
+ *  ADP5589 / ADP5585 derivative / variant handling
+ */
+
+
+/* ADP5589 */
+
+static unsigned char adp5589_bank(unsigned char offset)
+{
+	return offset >> 3;
+}
+
+static unsigned char adp5589_bit(unsigned char offset)
+{
+	return 1u << (offset & 0x7);
+}
+
+static unsigned char adp5589_reg(unsigned char reg)
+{
+	return reg;
+}
+
+static const struct adp_constants const_adp5589 = {
+	.maxgpio		= ADP5589_MAXGPIO,
+	.keymapsize		= ADP5589_KEYMAPSIZE,
+	.gpi_pin_row_base	= ADP5589_GPI_PIN_ROW_BASE,
+	.gpi_pin_row_end	= ADP5589_GPI_PIN_ROW_END,
+	.gpi_pin_col_base	= ADP5589_GPI_PIN_COL_BASE,
+	.gpi_pin_base		= ADP5589_GPI_PIN_BASE,
+	.gpi_pin_end		= ADP5589_GPI_PIN_END,
+	.gpimapsize_max		= ADP5589_GPIMAPSIZE_MAX,
+	.c4_extend_cfg		= 12,
+	.max_row_num		= ADP5589_MAX_ROW_NUM,
+	.max_col_num		= ADP5589_MAX_COL_NUM,
+	.row_mask		= ADP5589_ROW_MASK,
+	.col_mask		= ADP5589_COL_MASK,
+	.col_shift		= ADP5589_COL_SHIFT,
+	.bank			= adp5589_bank,
+	.bit			= adp5589_bit,
+	.reg			= adp5589_reg,
+};
+
+/* ADP5585 */
+
+static unsigned char adp5585_bank(unsigned char offset)
+{
+	return offset > ADP5585_MAX_ROW_NUM;
+}
+
+static unsigned char adp5585_bit(unsigned char offset)
+{
+	return (offset > ADP5585_MAX_ROW_NUM) ?
+		1u << (offset - ADP5585_COL_SHIFT) : 1u << offset;
+}
+
+static const unsigned char adp5585_reg_lut[] = {
+	[ADP5589_GPI_STATUS_A]		= ADP5585_GPI_STATUS_A,
+	[ADP5589_GPI_STATUS_B]		= ADP5585_GPI_STATUS_B,
+	[ADP5589_RPULL_CONFIG_A]	= ADP5585_RPULL_CONFIG_A,
+	[ADP5589_RPULL_CONFIG_B]	= ADP5585_RPULL_CONFIG_B,
+	[ADP5589_RPULL_CONFIG_C]	= ADP5585_RPULL_CONFIG_C,
+	[ADP5589_RPULL_CONFIG_D]	= ADP5585_RPULL_CONFIG_D,
+	[ADP5589_GPI_INT_LEVEL_A]	= ADP5585_GPI_INT_LEVEL_A,
+	[ADP5589_GPI_INT_LEVEL_B]	= ADP5585_GPI_INT_LEVEL_B,
+	[ADP5589_GPI_EVENT_EN_A]	= ADP5585_GPI_EVENT_EN_A,
+	[ADP5589_GPI_EVENT_EN_B]	= ADP5585_GPI_EVENT_EN_B,
+	[ADP5589_GPI_INTERRUPT_EN_A]	= ADP5585_GPI_INTERRUPT_EN_A,
+	[ADP5589_GPI_INTERRUPT_EN_B]	= ADP5585_GPI_INTERRUPT_EN_B,
+	[ADP5589_DEBOUNCE_DIS_A]	= ADP5585_DEBOUNCE_DIS_A,
+	[ADP5589_DEBOUNCE_DIS_B]	= ADP5585_DEBOUNCE_DIS_B,
+	[ADP5589_GPO_DATA_OUT_A]	= ADP5585_GPO_DATA_OUT_A,
+	[ADP5589_GPO_DATA_OUT_B]	= ADP5585_GPO_DATA_OUT_B,
+	[ADP5589_GPO_OUT_MODE_A]	= ADP5585_GPO_OUT_MODE_A,
+	[ADP5589_GPO_OUT_MODE_B]	= ADP5585_GPO_OUT_MODE_B,
+	[ADP5589_GPIO_DIRECTION_A]	= ADP5585_GPIO_DIRECTION_A,
+	[ADP5589_GPIO_DIRECTION_B]	= ADP5585_GPIO_DIRECTION_B,
+	[ADP5589_RESET1_EVENT_A]	= ADP5585_RESET1_EVENT_A,
+	[ADP5589_RESET1_EVENT_B]	= ADP5585_RESET1_EVENT_B,
+	[ADP5589_RESET1_EVENT_C]	= ADP5585_RESET1_EVENT_C,
+	[ADP5589_RESET2_EVENT_A]	= ADP5585_RESET2_EVENT_A,
+	[ADP5589_RESET2_EVENT_B]	= ADP5585_RESET2_EVENT_B,
+	[ADP5589_RESET_CFG]		= ADP5585_RESET_CFG,
+	[ADP5589_PWM_OFFT_LOW]		= ADP5585_PWM_OFFT_LOW,
+	[ADP5589_PWM_OFFT_HIGH]		= ADP5585_PWM_OFFT_HIGH,
+	[ADP5589_PWM_ONT_LOW]		= ADP5585_PWM_ONT_LOW,
+	[ADP5589_PWM_ONT_HIGH]		= ADP5585_PWM_ONT_HIGH,
+	[ADP5589_PWM_CFG]		= ADP5585_PWM_CFG,
+	[ADP5589_LOGIC_1_CFG]		= ADP5585_LOGIC_CFG,
+	[ADP5589_LOGIC_FF_CFG]		= ADP5585_LOGIC_FF_CFG,
+	[ADP5589_LOGIC_INT_EVENT_EN]	= ADP5585_LOGIC_INT_EVENT_EN,
+	[ADP5589_POLL_PTIME_CFG]	= ADP5585_POLL_PTIME_CFG,
+	[ADP5589_PIN_CONFIG_A]		= ADP5585_PIN_CONFIG_A,
+	[ADP5589_PIN_CONFIG_B]		= ADP5585_PIN_CONFIG_B,
+	[ADP5589_PIN_CONFIG_D]		= ADP5585_PIN_CONFIG_D,
+	[ADP5589_GENERAL_CFG]		= ADP5585_GENERAL_CFG,
+	[ADP5589_INT_EN]		= ADP5585_INT_EN,
+};
+
+static unsigned char adp5585_reg(unsigned char reg)
+{
+	return adp5585_reg_lut[reg];
+}
+
+static const struct adp_constants const_adp5585 = {
+	.maxgpio		= ADP5585_MAXGPIO,
+	.keymapsize		= ADP5585_KEYMAPSIZE,
+	.gpi_pin_row_base	= ADP5585_GPI_PIN_ROW_BASE,
+	.gpi_pin_row_end	= ADP5585_GPI_PIN_ROW_END,
+	.gpi_pin_col_base	= ADP5585_GPI_PIN_COL_BASE,
+	.gpi_pin_base		= ADP5585_GPI_PIN_BASE,
+	.gpi_pin_end		= ADP5585_GPI_PIN_END,
+	.gpimapsize_max		= ADP5585_GPIMAPSIZE_MAX,
+	.c4_extend_cfg		= 10,
+	.max_row_num		= ADP5585_MAX_ROW_NUM,
+	.max_col_num		= ADP5585_MAX_COL_NUM,
+	.row_mask		= ADP5585_ROW_MASK,
+	.col_mask		= ADP5585_COL_MASK,
+	.col_shift		= ADP5585_COL_SHIFT,
+	.bank			= adp5585_bank,
+	.bit			= adp5585_bit,
+	.reg			= adp5585_reg,
+};
+
+static int adp5589_read(struct i2c_client *client, u8 reg)
+{
+	int ret = i2c_smbus_read_byte_data(client, reg);
+
+	if (ret < 0)
+		dev_err(&client->dev, "Read Error\n");
+
+	return ret;
+}
+
+static int adp5589_write(struct i2c_client *client, u8 reg, u8 val)
+{
+	return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+#ifdef CONFIG_GPIOLIB
+static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off)
+{
+	struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
+	unsigned int bank = kpad->var->bank(kpad->gpiomap[off]);
+	unsigned int bit = kpad->var->bit(kpad->gpiomap[off]);
+
+	return !!(adp5589_read(kpad->client,
+			       kpad->var->reg(ADP5589_GPI_STATUS_A) + bank) &
+			       bit);
+}
+
+static void adp5589_gpio_set_value(struct gpio_chip *chip,
+				   unsigned off, int val)
+{
+	struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
+	unsigned int bank = kpad->var->bank(kpad->gpiomap[off]);
+	unsigned int bit = kpad->var->bit(kpad->gpiomap[off]);
+
+	mutex_lock(&kpad->gpio_lock);
+
+	if (val)
+		kpad->dat_out[bank] |= bit;
+	else
+		kpad->dat_out[bank] &= ~bit;
+
+	adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A) +
+		      bank, kpad->dat_out[bank]);
+
+	mutex_unlock(&kpad->gpio_lock);
+}
+
+static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off)
+{
+	struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
+	unsigned int bank = kpad->var->bank(kpad->gpiomap[off]);
+	unsigned int bit = kpad->var->bit(kpad->gpiomap[off]);
+	int ret;
+
+	mutex_lock(&kpad->gpio_lock);
+
+	kpad->dir[bank] &= ~bit;
+	ret = adp5589_write(kpad->client,
+			    kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank,
+			    kpad->dir[bank]);
+
+	mutex_unlock(&kpad->gpio_lock);
+
+	return ret;
+}
+
+static int adp5589_gpio_direction_output(struct gpio_chip *chip,
+					 unsigned off, int val)
+{
+	struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
+	unsigned int bank = kpad->var->bank(kpad->gpiomap[off]);
+	unsigned int bit = kpad->var->bit(kpad->gpiomap[off]);
+	int ret;
+
+	mutex_lock(&kpad->gpio_lock);
+
+	kpad->dir[bank] |= bit;
+
+	if (val)
+		kpad->dat_out[bank] |= bit;
+	else
+		kpad->dat_out[bank] &= ~bit;
+
+	ret = adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A)
+			    + bank, kpad->dat_out[bank]);
+	ret |= adp5589_write(kpad->client,
+			     kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank,
+			     kpad->dir[bank]);
+
+	mutex_unlock(&kpad->gpio_lock);
+
+	return ret;
+}
+
+static int adp5589_build_gpiomap(struct adp5589_kpad *kpad,
+				const struct adp5589_kpad_platform_data *pdata)
+{
+	bool pin_used[ADP5589_MAXGPIO];
+	int n_unused = 0;
+	int i;
+
+	memset(pin_used, false, sizeof(pin_used));
+
+	for (i = 0; i < kpad->var->maxgpio; i++)
+		if (pdata->keypad_en_mask & (1 << i))
+			pin_used[i] = true;
+
+	for (i = 0; i < kpad->gpimapsize; i++)
+		pin_used[kpad->gpimap[i].pin - kpad->var->gpi_pin_base] = true;
+
+	if (kpad->extend_cfg & R4_EXTEND_CFG)
+		pin_used[4] = true;
+
+	if (kpad->extend_cfg & C4_EXTEND_CFG)
+		pin_used[kpad->var->c4_extend_cfg] = true;
+
+	if (!kpad->adp5585_support_row5)
+		pin_used[5] = true;
+
+	for (i = 0; i < kpad->var->maxgpio; i++)
+		if (!pin_used[i])
+			kpad->gpiomap[n_unused++] = i;
+
+	return n_unused;
+}
+
+static int adp5589_gpio_add(struct adp5589_kpad *kpad)
+{
+	struct device *dev = &kpad->client->dev;
+	const struct adp5589_kpad_platform_data *pdata = dev_get_platdata(dev);
+	const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data;
+	int i, error;
+
+	if (!gpio_data)
+		return 0;
+
+	kpad->gc.ngpio = adp5589_build_gpiomap(kpad, pdata);
+	if (kpad->gc.ngpio == 0) {
+		dev_info(dev, "No unused gpios left to export\n");
+		return 0;
+	}
+
+	kpad->export_gpio = true;
+
+	kpad->gc.direction_input = adp5589_gpio_direction_input;
+	kpad->gc.direction_output = adp5589_gpio_direction_output;
+	kpad->gc.get = adp5589_gpio_get_value;
+	kpad->gc.set = adp5589_gpio_set_value;
+	kpad->gc.can_sleep = 1;
+
+	kpad->gc.base = gpio_data->gpio_start;
+	kpad->gc.label = kpad->client->name;
+	kpad->gc.owner = THIS_MODULE;
+
+	mutex_init(&kpad->gpio_lock);
+
+	error = gpiochip_add(&kpad->gc);
+	if (error) {
+		dev_err(dev, "gpiochip_add failed, err: %d\n", error);
+		return error;
+	}
+
+	for (i = 0; i <= kpad->var->bank(kpad->var->maxgpio); i++) {
+		kpad->dat_out[i] = adp5589_read(kpad->client, kpad->var->reg(
+						ADP5589_GPO_DATA_OUT_A) + i);
+		kpad->dir[i] = adp5589_read(kpad->client, kpad->var->reg(
+					    ADP5589_GPIO_DIRECTION_A) + i);
+	}
+
+	if (gpio_data->setup) {
+		error = gpio_data->setup(kpad->client,
+					 kpad->gc.base, kpad->gc.ngpio,
+					 gpio_data->context);
+		if (error)
+			dev_warn(dev, "setup failed, %d\n", error);
+	}
+
+	return 0;
+}
+
+static void adp5589_gpio_remove(struct adp5589_kpad *kpad)
+{
+	struct device *dev = &kpad->client->dev;
+	const struct adp5589_kpad_platform_data *pdata = dev_get_platdata(dev);
+	const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data;
+	int error;
+
+	if (!kpad->export_gpio)
+		return;
+
+	if (gpio_data->teardown) {
+		error = gpio_data->teardown(kpad->client,
+					    kpad->gc.base, kpad->gc.ngpio,
+					    gpio_data->context);
+		if (error)
+			dev_warn(dev, "teardown failed %d\n", error);
+	}
+
+	gpiochip_remove(&kpad->gc);
+}
+#else
+static inline int adp5589_gpio_add(struct adp5589_kpad *kpad)
+{
+	return 0;
+}
+
+static inline void adp5589_gpio_remove(struct adp5589_kpad *kpad)
+{
+}
+#endif
+
+static void adp5589_report_switches(struct adp5589_kpad *kpad,
+				    int key, int key_val)
+{
+	int i;
+
+	for (i = 0; i < kpad->gpimapsize; i++) {
+		if (key_val == kpad->gpimap[i].pin) {
+			input_report_switch(kpad->input,
+					    kpad->gpimap[i].sw_evt,
+					    key & KEY_EV_PRESSED);
+			break;
+		}
+	}
+}
+
+static void adp5589_report_events(struct adp5589_kpad *kpad, int ev_cnt)
+{
+	int i;
+
+	for (i = 0; i < ev_cnt; i++) {
+		int key = adp5589_read(kpad->client, ADP5589_5_FIFO_1 + i);
+		int key_val = key & KEY_EV_MASK;
+
+		if (key_val >= kpad->var->gpi_pin_base &&
+		    key_val <= kpad->var->gpi_pin_end) {
+			adp5589_report_switches(kpad, key, key_val);
+		} else {
+			input_report_key(kpad->input,
+					 kpad->keycode[key_val - 1],
+					 key & KEY_EV_PRESSED);
+		}
+	}
+}
+
+static irqreturn_t adp5589_irq(int irq, void *handle)
+{
+	struct adp5589_kpad *kpad = handle;
+	struct i2c_client *client = kpad->client;
+	int status, ev_cnt;
+
+	status = adp5589_read(client, ADP5589_5_INT_STATUS);
+
+	if (status & OVRFLOW_INT)	/* Unlikely and should never happen */
+		dev_err(&client->dev, "Event Overflow Error\n");
+
+	if (status & EVENT_INT) {
+		ev_cnt = adp5589_read(client, ADP5589_5_STATUS) & KEC;
+		if (ev_cnt) {
+			adp5589_report_events(kpad, ev_cnt);
+			input_sync(kpad->input);
+		}
+	}
+
+	adp5589_write(client, ADP5589_5_INT_STATUS, status); /* Status is W1C */
+
+	return IRQ_HANDLED;
+}
+
+static int adp5589_get_evcode(struct adp5589_kpad *kpad, unsigned short key)
+{
+	int i;
+
+	for (i = 0; i < kpad->var->keymapsize; i++)
+		if (key == kpad->keycode[i])
+			return (i + 1) | KEY_EV_PRESSED;
+
+	dev_err(&kpad->client->dev, "RESET/UNLOCK key not in keycode map\n");
+
+	return -EINVAL;
+}
+
+static int adp5589_setup(struct adp5589_kpad *kpad)
+{
+	struct i2c_client *client = kpad->client;
+	const struct adp5589_kpad_platform_data *pdata =
+		dev_get_platdata(&client->dev);
+	u8 (*reg) (u8) = kpad->var->reg;
+	unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0;
+	unsigned char pull_mask = 0;
+	int i, ret;
+
+	ret = adp5589_write(client, reg(ADP5589_PIN_CONFIG_A),
+			    pdata->keypad_en_mask & kpad->var->row_mask);
+	ret |= adp5589_write(client, reg(ADP5589_PIN_CONFIG_B),
+			     (pdata->keypad_en_mask >> kpad->var->col_shift) &
+			     kpad->var->col_mask);
+
+	if (!kpad->is_adp5585)
+		ret |= adp5589_write(client, ADP5589_PIN_CONFIG_C,
+				     (pdata->keypad_en_mask >> 16) & 0xFF);
+
+	if (!kpad->is_adp5585 && pdata->en_keylock) {
+		ret |= adp5589_write(client, ADP5589_UNLOCK1,
+				     pdata->unlock_key1);
+		ret |= adp5589_write(client, ADP5589_UNLOCK2,
+				     pdata->unlock_key2);
+		ret |= adp5589_write(client, ADP5589_UNLOCK_TIMERS,
+				     pdata->unlock_timer & LTIME_MASK);
+		ret |= adp5589_write(client, ADP5589_LOCK_CFG, LOCK_EN);
+	}
+
+	for (i = 0; i < KEYP_MAX_EVENT; i++)
+		ret |= adp5589_read(client, ADP5589_5_FIFO_1 + i);
+
+	for (i = 0; i < pdata->gpimapsize; i++) {
+		unsigned short pin = pdata->gpimap[i].pin;
+
+		if (pin <= kpad->var->gpi_pin_row_end) {
+			evt_mode1 |= (1 << (pin - kpad->var->gpi_pin_row_base));
+		} else {
+			evt_mode2 |=
+			    ((1 << (pin - kpad->var->gpi_pin_col_base)) & 0xFF);
+			if (!kpad->is_adp5585)
+				evt_mode3 |= ((1 << (pin -
+					kpad->var->gpi_pin_col_base)) >> 8);
+		}
+	}
+
+	if (pdata->gpimapsize) {
+		ret |= adp5589_write(client, reg(ADP5589_GPI_EVENT_EN_A),
+				     evt_mode1);
+		ret |= adp5589_write(client, reg(ADP5589_GPI_EVENT_EN_B),
+				     evt_mode2);
+		if (!kpad->is_adp5585)
+			ret |= adp5589_write(client,
+					     reg(ADP5589_GPI_EVENT_EN_C),
+					     evt_mode3);
+	}
+
+	if (pdata->pull_dis_mask & pdata->pullup_en_100k &
+		pdata->pullup_en_300k & pdata->pulldown_en_300k)
+		dev_warn(&client->dev, "Conflicting pull resistor config\n");
+
+	for (i = 0; i <= kpad->var->max_row_num; i++) {
+		unsigned val = 0, bit = (1 << i);
+		if (pdata->pullup_en_300k & bit)
+			val = 0;
+		else if (pdata->pulldown_en_300k & bit)
+			val = 1;
+		else if (pdata->pullup_en_100k & bit)
+			val = 2;
+		else if (pdata->pull_dis_mask & bit)
+			val = 3;
+
+		pull_mask |= val << (2 * (i & 0x3));
+
+		if (i % 4 == 3 || i == kpad->var->max_row_num) {
+			ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_A)
+					     + (i >> 2), pull_mask);
+			pull_mask = 0;
+		}
+	}
+
+	for (i = 0; i <= kpad->var->max_col_num; i++) {
+		unsigned val = 0, bit = 1 << (i + kpad->var->col_shift);
+		if (pdata->pullup_en_300k & bit)
+			val = 0;
+		else if (pdata->pulldown_en_300k & bit)
+			val = 1;
+		else if (pdata->pullup_en_100k & bit)
+			val = 2;
+		else if (pdata->pull_dis_mask & bit)
+			val = 3;
+
+		pull_mask |= val << (2 * (i & 0x3));
+
+		if (i % 4 == 3 || i == kpad->var->max_col_num) {
+			ret |= adp5589_write(client,
+					     reg(ADP5585_RPULL_CONFIG_C) +
+					     (i >> 2), pull_mask);
+			pull_mask = 0;
+		}
+	}
+
+	if (pdata->reset1_key_1 && pdata->reset1_key_2 && pdata->reset1_key_3) {
+		ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_A),
+				     adp5589_get_evcode(kpad,
+							pdata->reset1_key_1));
+		ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_B),
+				     adp5589_get_evcode(kpad,
+							pdata->reset1_key_2));
+		ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_C),
+				     adp5589_get_evcode(kpad,
+							pdata->reset1_key_3));
+		kpad->extend_cfg |= R4_EXTEND_CFG;
+	}
+
+	if (pdata->reset2_key_1 && pdata->reset2_key_2) {
+		ret |= adp5589_write(client, reg(ADP5589_RESET2_EVENT_A),
+				     adp5589_get_evcode(kpad,
+							pdata->reset2_key_1));
+		ret |= adp5589_write(client, reg(ADP5589_RESET2_EVENT_B),
+				     adp5589_get_evcode(kpad,
+							pdata->reset2_key_2));
+		kpad->extend_cfg |= C4_EXTEND_CFG;
+	}
+
+	if (kpad->extend_cfg) {
+		ret |= adp5589_write(client, reg(ADP5589_RESET_CFG),
+				     pdata->reset_cfg);
+		ret |= adp5589_write(client, reg(ADP5589_PIN_CONFIG_D),
+				     kpad->extend_cfg);
+	}
+
+	ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_A),
+			    pdata->debounce_dis_mask & kpad->var->row_mask);
+
+	ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_B),
+			     (pdata->debounce_dis_mask >> kpad->var->col_shift)
+			     & kpad->var->col_mask);
+
+	if (!kpad->is_adp5585)
+		ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_C),
+				     (pdata->debounce_dis_mask >> 16) & 0xFF);
+
+	ret |= adp5589_write(client, reg(ADP5589_POLL_PTIME_CFG),
+			     pdata->scan_cycle_time & PTIME_MASK);
+	ret |= adp5589_write(client, ADP5589_5_INT_STATUS,
+			     (kpad->is_adp5585 ? 0 : LOGIC2_INT) |
+			     LOGIC1_INT | OVRFLOW_INT |
+			     (kpad->is_adp5585 ? 0 : LOCK_INT) |
+			     GPI_INT | EVENT_INT);	/* Status is W1C */
+
+	ret |= adp5589_write(client, reg(ADP5589_GENERAL_CFG),
+			     INT_CFG | OSC_EN | CORE_CLK(3));
+	ret |= adp5589_write(client, reg(ADP5589_INT_EN),
+			     OVRFLOW_IEN | GPI_IEN | EVENT_IEN);
+
+	if (ret < 0) {
+		dev_err(&client->dev, "Write Error\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void adp5589_report_switch_state(struct adp5589_kpad *kpad)
+{
+	int gpi_stat_tmp, pin_loc;
+	int i;
+	int gpi_stat1 = adp5589_read(kpad->client,
+				     kpad->var->reg(ADP5589_GPI_STATUS_A));
+	int gpi_stat2 = adp5589_read(kpad->client,
+				     kpad->var->reg(ADP5589_GPI_STATUS_B));
+	int gpi_stat3 = !kpad->is_adp5585 ?
+			adp5589_read(kpad->client, ADP5589_GPI_STATUS_C) : 0;
+
+	for (i = 0; i < kpad->gpimapsize; i++) {
+		unsigned short pin = kpad->gpimap[i].pin;
+
+		if (pin <= kpad->var->gpi_pin_row_end) {
+			gpi_stat_tmp = gpi_stat1;
+			pin_loc = pin - kpad->var->gpi_pin_row_base;
+		} else if ((pin - kpad->var->gpi_pin_col_base) < 8) {
+			gpi_stat_tmp = gpi_stat2;
+			pin_loc = pin - kpad->var->gpi_pin_col_base;
+		} else {
+			gpi_stat_tmp = gpi_stat3;
+			pin_loc = pin - kpad->var->gpi_pin_col_base - 8;
+		}
+
+		if (gpi_stat_tmp < 0) {
+			dev_err(&kpad->client->dev,
+				"Can't read GPIO_DAT_STAT switch %d, default to OFF\n",
+				pin);
+			gpi_stat_tmp = 0;
+		}
+
+		input_report_switch(kpad->input,
+				    kpad->gpimap[i].sw_evt,
+				    !(gpi_stat_tmp & (1 << pin_loc)));
+	}
+
+	input_sync(kpad->input);
+}
+
+static int adp5589_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct adp5589_kpad *kpad;
+	const struct adp5589_kpad_platform_data *pdata =
+		dev_get_platdata(&client->dev);
+	struct input_dev *input;
+	unsigned int revid;
+	int ret, i;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+		return -EIO;
+	}
+
+	if (!pdata) {
+		dev_err(&client->dev, "no platform data?\n");
+		return -EINVAL;
+	}
+
+	kpad = kzalloc(sizeof(*kpad), GFP_KERNEL);
+	if (!kpad)
+		return -ENOMEM;
+
+	switch (id->driver_data) {
+	case ADP5585_02:
+		kpad->adp5585_support_row5 = true;
+	case ADP5585_01:
+		kpad->is_adp5585 = true;
+		kpad->var = &const_adp5585;
+		break;
+	case ADP5589:
+		kpad->var = &const_adp5589;
+		break;
+	}
+
+	if (!((pdata->keypad_en_mask & kpad->var->row_mask) &&
+			(pdata->keypad_en_mask >> kpad->var->col_shift)) ||
+			!pdata->keymap) {
+		dev_err(&client->dev, "no rows, cols or keymap from pdata\n");
+		error = -EINVAL;
+		goto err_free_mem;
+	}
+
+	if (pdata->keymapsize != kpad->var->keymapsize) {
+		dev_err(&client->dev, "invalid keymapsize\n");
+		error = -EINVAL;
+		goto err_free_mem;
+	}
+
+	if (!pdata->gpimap && pdata->gpimapsize) {
+		dev_err(&client->dev, "invalid gpimap from pdata\n");
+		error = -EINVAL;
+		goto err_free_mem;
+	}
+
+	if (pdata->gpimapsize > kpad->var->gpimapsize_max) {
+		dev_err(&client->dev, "invalid gpimapsize\n");
+		error = -EINVAL;
+		goto err_free_mem;
+	}
+
+	for (i = 0; i < pdata->gpimapsize; i++) {
+		unsigned short pin = pdata->gpimap[i].pin;
+
+		if (pin < kpad->var->gpi_pin_base ||
+				pin > kpad->var->gpi_pin_end) {
+			dev_err(&client->dev, "invalid gpi pin data\n");
+			error = -EINVAL;
+			goto err_free_mem;
+		}
+
+		if ((1 << (pin - kpad->var->gpi_pin_row_base)) &
+				pdata->keypad_en_mask) {
+			dev_err(&client->dev, "invalid gpi row/col data\n");
+			error = -EINVAL;
+			goto err_free_mem;
+		}
+	}
+
+	if (!client->irq) {
+		dev_err(&client->dev, "no IRQ?\n");
+		error = -EINVAL;
+		goto err_free_mem;
+	}
+
+	input = input_allocate_device();
+	if (!input) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	kpad->client = client;
+	kpad->input = input;
+
+	ret = adp5589_read(client, ADP5589_5_ID);
+	if (ret < 0) {
+		error = ret;
+		goto err_free_input;
+	}
+
+	revid = (u8) ret & ADP5589_5_DEVICE_ID_MASK;
+
+	input->name = client->name;
+	input->phys = "adp5589-keys/input0";
+	input->dev.parent = &client->dev;
+
+	input_set_drvdata(input, kpad);
+
+	input->id.bustype = BUS_I2C;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = revid;
+
+	input->keycodesize = sizeof(kpad->keycode[0]);
+	input->keycodemax = pdata->keymapsize;
+	input->keycode = kpad->keycode;
+
+	memcpy(kpad->keycode, pdata->keymap,
+	       pdata->keymapsize * input->keycodesize);
+
+	kpad->gpimap = pdata->gpimap;
+	kpad->gpimapsize = pdata->gpimapsize;
+
+	/* setup input device */
+	__set_bit(EV_KEY, input->evbit);
+
+	if (pdata->repeat)
+		__set_bit(EV_REP, input->evbit);
+
+	for (i = 0; i < input->keycodemax; i++)
+		if (kpad->keycode[i] <= KEY_MAX)
+			__set_bit(kpad->keycode[i], input->keybit);
+	__clear_bit(KEY_RESERVED, input->keybit);
+
+	if (kpad->gpimapsize)
+		__set_bit(EV_SW, input->evbit);
+	for (i = 0; i < kpad->gpimapsize; i++)
+		__set_bit(kpad->gpimap[i].sw_evt, input->swbit);
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(&client->dev, "unable to register input device\n");
+		goto err_free_input;
+	}
+
+	error = request_threaded_irq(client->irq, NULL, adp5589_irq,
+				     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				     client->dev.driver->name, kpad);
+	if (error) {
+		dev_err(&client->dev, "irq %d busy?\n", client->irq);
+		goto err_unreg_dev;
+	}
+
+	error = adp5589_setup(kpad);
+	if (error)
+		goto err_free_irq;
+
+	if (kpad->gpimapsize)
+		adp5589_report_switch_state(kpad);
+
+	error = adp5589_gpio_add(kpad);
+	if (error)
+		goto err_free_irq;
+
+	device_init_wakeup(&client->dev, 1);
+	i2c_set_clientdata(client, kpad);
+
+	dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
+	return 0;
+
+err_free_irq:
+	free_irq(client->irq, kpad);
+err_unreg_dev:
+	input_unregister_device(input);
+	input = NULL;
+err_free_input:
+	input_free_device(input);
+err_free_mem:
+	kfree(kpad);
+
+	return error;
+}
+
+static int adp5589_remove(struct i2c_client *client)
+{
+	struct adp5589_kpad *kpad = i2c_get_clientdata(client);
+
+	adp5589_write(client, kpad->var->reg(ADP5589_GENERAL_CFG), 0);
+	free_irq(client->irq, kpad);
+	input_unregister_device(kpad->input);
+	adp5589_gpio_remove(kpad);
+	kfree(kpad);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int adp5589_suspend(struct device *dev)
+{
+	struct adp5589_kpad *kpad = dev_get_drvdata(dev);
+	struct i2c_client *client = kpad->client;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(&client->dev))
+		enable_irq_wake(client->irq);
+
+	return 0;
+}
+
+static int adp5589_resume(struct device *dev)
+{
+	struct adp5589_kpad *kpad = dev_get_drvdata(dev);
+	struct i2c_client *client = kpad->client;
+
+	if (device_may_wakeup(&client->dev))
+		disable_irq_wake(client->irq);
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(adp5589_dev_pm_ops, adp5589_suspend, adp5589_resume);
+
+static const struct i2c_device_id adp5589_id[] = {
+	{"adp5589-keys", ADP5589},
+	{"adp5585-keys", ADP5585_01},
+	{"adp5585-02-keys", ADP5585_02}, /* Adds ROW5 to ADP5585 */
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, adp5589_id);
+
+static struct i2c_driver adp5589_driver = {
+	.driver = {
+		.name = KBUILD_MODNAME,
+		.pm = &adp5589_dev_pm_ops,
+	},
+	.probe = adp5589_probe,
+	.remove = adp5589_remove,
+	.id_table = adp5589_id,
+};
+
+module_i2c_driver(adp5589_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADP5589/ADP5585 Keypad driver");
diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c
new file mode 100644
index 0000000..e04a3b4
--- /dev/null
+++ b/drivers/input/keyboard/amikbd.c
@@ -0,0 +1,276 @@
+/*
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Hamish Macdonald
+ */
+
+/*
+ * Amiga keyboard driver for Linux/m68k
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/keyboard.h>
+#include <linux/platform_device.h>
+
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Amiga keyboard driver");
+MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_HW_CONSOLE
+static unsigned char amikbd_keycode[0x78] __initdata = {
+	[0]	 = KEY_GRAVE,
+	[1]	 = KEY_1,
+	[2]	 = KEY_2,
+	[3]	 = KEY_3,
+	[4]	 = KEY_4,
+	[5]	 = KEY_5,
+	[6]	 = KEY_6,
+	[7]	 = KEY_7,
+	[8]	 = KEY_8,
+	[9]	 = KEY_9,
+	[10]	 = KEY_0,
+	[11]	 = KEY_MINUS,
+	[12]	 = KEY_EQUAL,
+	[13]	 = KEY_BACKSLASH,
+	[15]	 = KEY_KP0,
+	[16]	 = KEY_Q,
+	[17]	 = KEY_W,
+	[18]	 = KEY_E,
+	[19]	 = KEY_R,
+	[20]	 = KEY_T,
+	[21]	 = KEY_Y,
+	[22]	 = KEY_U,
+	[23]	 = KEY_I,
+	[24]	 = KEY_O,
+	[25]	 = KEY_P,
+	[26]	 = KEY_LEFTBRACE,
+	[27]	 = KEY_RIGHTBRACE,
+	[29]	 = KEY_KP1,
+	[30]	 = KEY_KP2,
+	[31]	 = KEY_KP3,
+	[32]	 = KEY_A,
+	[33]	 = KEY_S,
+	[34]	 = KEY_D,
+	[35]	 = KEY_F,
+	[36]	 = KEY_G,
+	[37]	 = KEY_H,
+	[38]	 = KEY_J,
+	[39]	 = KEY_K,
+	[40]	 = KEY_L,
+	[41]	 = KEY_SEMICOLON,
+	[42]	 = KEY_APOSTROPHE,
+	[43]	 = KEY_BACKSLASH,
+	[45]	 = KEY_KP4,
+	[46]	 = KEY_KP5,
+	[47]	 = KEY_KP6,
+	[48]	 = KEY_102ND,
+	[49]	 = KEY_Z,
+	[50]	 = KEY_X,
+	[51]	 = KEY_C,
+	[52]	 = KEY_V,
+	[53]	 = KEY_B,
+	[54]	 = KEY_N,
+	[55]	 = KEY_M,
+	[56]	 = KEY_COMMA,
+	[57]	 = KEY_DOT,
+	[58]	 = KEY_SLASH,
+	[60]	 = KEY_KPDOT,
+	[61]	 = KEY_KP7,
+	[62]	 = KEY_KP8,
+	[63]	 = KEY_KP9,
+	[64]	 = KEY_SPACE,
+	[65]	 = KEY_BACKSPACE,
+	[66]	 = KEY_TAB,
+	[67]	 = KEY_KPENTER,
+	[68]	 = KEY_ENTER,
+	[69]	 = KEY_ESC,
+	[70]	 = KEY_DELETE,
+	[74]	 = KEY_KPMINUS,
+	[76]	 = KEY_UP,
+	[77]	 = KEY_DOWN,
+	[78]	 = KEY_RIGHT,
+	[79]	 = KEY_LEFT,
+	[80]	 = KEY_F1,
+	[81]	 = KEY_F2,
+	[82]	 = KEY_F3,
+	[83]	 = KEY_F4,
+	[84]	 = KEY_F5,
+	[85]	 = KEY_F6,
+	[86]	 = KEY_F7,
+	[87]	 = KEY_F8,
+	[88]	 = KEY_F9,
+	[89]	 = KEY_F10,
+	[90]	 = KEY_KPLEFTPAREN,
+	[91]	 = KEY_KPRIGHTPAREN,
+	[92]	 = KEY_KPSLASH,
+	[93]	 = KEY_KPASTERISK,
+	[94]	 = KEY_KPPLUS,
+	[95]	 = KEY_HELP,
+	[96]	 = KEY_LEFTSHIFT,
+	[97]	 = KEY_RIGHTSHIFT,
+	[98]	 = KEY_CAPSLOCK,
+	[99]	 = KEY_LEFTCTRL,
+	[100]	 = KEY_LEFTALT,
+	[101]	 = KEY_RIGHTALT,
+	[102]	 = KEY_LEFTMETA,
+	[103]	 = KEY_RIGHTMETA
+};
+
+static void __init amikbd_init_console_keymaps(void)
+{
+	/* We can spare 512 bytes on stack for temp_map in init path. */
+	unsigned short temp_map[NR_KEYS];
+	int i, j;
+
+	for (i = 0; i < MAX_NR_KEYMAPS; i++) {
+		if (!key_maps[i])
+			continue;
+		memset(temp_map, 0, sizeof(temp_map));
+		for (j = 0; j < 0x78; j++) {
+			if (!amikbd_keycode[j])
+				continue;
+			temp_map[j] = key_maps[i][amikbd_keycode[j]];
+		}
+		for (j = 0; j < NR_KEYS; j++) {
+			if (!temp_map[j])
+				temp_map[j] = 0xf200;
+		}
+		memcpy(key_maps[i], temp_map, sizeof(temp_map));
+	}
+}
+#else /* !CONFIG_HW_CONSOLE */
+static inline void amikbd_init_console_keymaps(void) {}
+#endif /* !CONFIG_HW_CONSOLE */
+
+static const char *amikbd_messages[8] = {
+	[0] = KERN_ALERT "amikbd: Ctrl-Amiga-Amiga reset warning!!\n",
+	[1] = KERN_WARNING "amikbd: keyboard lost sync\n",
+	[2] = KERN_WARNING "amikbd: keyboard buffer overflow\n",
+	[3] = KERN_WARNING "amikbd: keyboard controller failure\n",
+	[4] = KERN_ERR "amikbd: keyboard selftest failure\n",
+	[5] = KERN_INFO "amikbd: initiate power-up key stream\n",
+	[6] = KERN_INFO "amikbd: terminate power-up key stream\n",
+	[7] = KERN_WARNING "amikbd: keyboard interrupt\n"
+};
+
+static irqreturn_t amikbd_interrupt(int irq, void *data)
+{
+	struct input_dev *dev = data;
+	unsigned char scancode, down;
+
+	scancode = ~ciaa.sdr;		/* get and invert scancode (keyboard is active low) */
+	ciaa.cra |= 0x40;		/* switch SP pin to output for handshake */
+	udelay(85);			/* wait until 85 us have expired */
+	ciaa.cra &= ~0x40;		/* switch CIA serial port to input mode */
+
+	down = !(scancode & 1);		/* lowest bit is release bit */
+	scancode >>= 1;
+
+	if (scancode < 0x78) {		/* scancodes < 0x78 are keys */
+		if (scancode == 98) {	/* CapsLock is a toggle switch key on Amiga */
+			input_report_key(dev, scancode, 1);
+			input_report_key(dev, scancode, 0);
+		} else {
+			input_report_key(dev, scancode, down);
+		}
+
+		input_sync(dev);
+	} else				/* scancodes >= 0x78 are error codes */
+		printk(amikbd_messages[scancode - 0x78]);
+
+	return IRQ_HANDLED;
+}
+
+static int __init amikbd_probe(struct platform_device *pdev)
+{
+	struct input_dev *dev;
+	int i, err;
+
+	dev = input_allocate_device();
+	if (!dev) {
+		dev_err(&pdev->dev, "Not enough memory for input device\n");
+		return -ENOMEM;
+	}
+
+	dev->name = pdev->name;
+	dev->phys = "amikbd/input0";
+	dev->id.bustype = BUS_AMIGA;
+	dev->id.vendor = 0x0001;
+	dev->id.product = 0x0001;
+	dev->id.version = 0x0100;
+	dev->dev.parent = &pdev->dev;
+
+	dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+
+	for (i = 0; i < 0x78; i++)
+		set_bit(i, dev->keybit);
+
+	amikbd_init_console_keymaps();
+
+	ciaa.cra &= ~0x41;	 /* serial data in, turn off TA */
+	err = request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd",
+			  dev);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(dev);
+	if (err)
+		goto fail3;
+
+	platform_set_drvdata(pdev, dev);
+
+	return 0;
+
+ fail3:	free_irq(IRQ_AMIGA_CIAA_SP, dev);
+ fail2:	input_free_device(dev);
+	return err;
+}
+
+static int __exit amikbd_remove(struct platform_device *pdev)
+{
+	struct input_dev *dev = platform_get_drvdata(pdev);
+
+	free_irq(IRQ_AMIGA_CIAA_SP, dev);
+	input_unregister_device(dev);
+	return 0;
+}
+
+static struct platform_driver amikbd_driver = {
+	.remove = __exit_p(amikbd_remove),
+	.driver   = {
+		.name	= "amiga-keyboard",
+	},
+};
+
+module_platform_driver_probe(amikbd_driver, amikbd_probe);
+
+MODULE_ALIAS("platform:amiga-keyboard");
diff --git a/drivers/input/keyboard/atakbd.c b/drivers/input/keyboard/atakbd.c
new file mode 100644
index 0000000..f123583
--- /dev/null
+++ b/drivers/input/keyboard/atakbd.c
@@ -0,0 +1,269 @@
+/*
+ *  atakbd.c
+ *
+ *  Copyright (c) 2005 Michael Schmitz
+ *
+ * Based on amikbd.c, which is
+ *
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Hamish Macdonald
+ */
+
+/*
+ * Atari keyboard driver for Linux/m68k
+ *
+ * The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c
+ * (the keyboard ACIA also handles the mouse and joystick data, and the keyboard
+ * interrupt is shared with the MIDI ACIA so MIDI data also get handled there).
+ * This driver only deals with handing key events off to the input layer.
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include <asm/atariints.h>
+#include <asm/atarihw.h>
+#include <asm/atarikb.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Michael Schmitz <schmitz@biophys.uni-duesseldorf.de>");
+MODULE_DESCRIPTION("Atari keyboard driver");
+MODULE_LICENSE("GPL");
+
+/*
+ 0x47: KP_7     71
+ 0x48: KP_8     72
+ 0x49: KP_9     73
+ 0x62: KP_/     98
+ 0x4b: KP_4     75
+ 0x4c: KP_5     76
+ 0x4d: KP_6     77
+ 0x37: KP_*     55
+ 0x4f: KP_1     79
+ 0x50: KP_2     80
+ 0x51: KP_3     81
+ 0x4a: KP_-     74
+ 0x52: KP_0     82
+ 0x53: KP_.     83
+ 0x4e: KP_+     78
+
+ 0x67: Up       103
+ 0x6c: Down     108
+ 0x69: Left     105
+ 0x6a: Right    106
+ */
+
+
+static unsigned char atakbd_keycode[0x72] = {	/* American layout */
+	[0]	 = KEY_GRAVE,
+	[1]	 = KEY_ESC,
+	[2]	 = KEY_1,
+	[3]	 = KEY_2,
+	[4]	 = KEY_3,
+	[5]	 = KEY_4,
+	[6]	 = KEY_5,
+	[7]	 = KEY_6,
+	[8]	 = KEY_7,
+	[9]	 = KEY_8,
+	[10]	 = KEY_9,
+	[11]	 = KEY_0,
+	[12]	 = KEY_MINUS,
+	[13]	 = KEY_EQUAL,
+	[14]	 = KEY_BACKSPACE,
+	[15]	 = KEY_TAB,
+	[16]	 = KEY_Q,
+	[17]	 = KEY_W,
+	[18]	 = KEY_E,
+	[19]	 = KEY_R,
+	[20]	 = KEY_T,
+	[21]	 = KEY_Y,
+	[22]	 = KEY_U,
+	[23]	 = KEY_I,
+	[24]	 = KEY_O,
+	[25]	 = KEY_P,
+	[26]	 = KEY_LEFTBRACE,
+	[27]	 = KEY_RIGHTBRACE,
+	[28]	 = KEY_ENTER,
+	[29]	 = KEY_LEFTCTRL,
+	[30]	 = KEY_A,
+	[31]	 = KEY_S,
+	[32]	 = KEY_D,
+	[33]	 = KEY_F,
+	[34]	 = KEY_G,
+	[35]	 = KEY_H,
+	[36]	 = KEY_J,
+	[37]	 = KEY_K,
+	[38]	 = KEY_L,
+	[39]	 = KEY_SEMICOLON,
+	[40]	 = KEY_APOSTROPHE,
+	[41]	 = KEY_BACKSLASH,	/* FIXME, '#' */
+	[42]	 = KEY_LEFTSHIFT,
+	[43]	 = KEY_GRAVE,		/* FIXME: '~' */
+	[44]	 = KEY_Z,
+	[45]	 = KEY_X,
+	[46]	 = KEY_C,
+	[47]	 = KEY_V,
+	[48]	 = KEY_B,
+	[49]	 = KEY_N,
+	[50]	 = KEY_M,
+	[51]	 = KEY_COMMA,
+	[52]	 = KEY_DOT,
+	[53]	 = KEY_SLASH,
+	[54]	 = KEY_RIGHTSHIFT,
+	[55]	 = KEY_KPASTERISK,
+	[56]	 = KEY_LEFTALT,
+	[57]	 = KEY_SPACE,
+	[58]	 = KEY_CAPSLOCK,
+	[59]	 = KEY_F1,
+	[60]	 = KEY_F2,
+	[61]	 = KEY_F3,
+	[62]	 = KEY_F4,
+	[63]	 = KEY_F5,
+	[64]	 = KEY_F6,
+	[65]	 = KEY_F7,
+	[66]	 = KEY_F8,
+	[67]	 = KEY_F9,
+	[68]	 = KEY_F10,
+	[69]	 = KEY_ESC,
+	[70]	 = KEY_DELETE,
+	[71]	 = KEY_KP7,
+	[72]	 = KEY_KP8,
+	[73]	 = KEY_KP9,
+	[74]	 = KEY_KPMINUS,
+	[75]	 = KEY_KP4,
+	[76]	 = KEY_KP5,
+	[77]	 = KEY_KP6,
+	[78]	 = KEY_KPPLUS,
+	[79]	 = KEY_KP1,
+	[80]	 = KEY_KP2,
+	[81]	 = KEY_KP3,
+	[82]	 = KEY_KP0,
+	[83]	 = KEY_KPDOT,
+	[90]	 = KEY_KPLEFTPAREN,
+	[91]	 = KEY_KPRIGHTPAREN,
+	[92]	 = KEY_KPASTERISK,	/* FIXME */
+	[93]	 = KEY_KPASTERISK,
+	[94]	 = KEY_KPPLUS,
+	[95]	 = KEY_HELP,
+	[96]	 = KEY_102ND,
+	[97]	 = KEY_KPASTERISK,	/* FIXME */
+	[98]	 = KEY_KPSLASH,
+	[99]	 = KEY_KPLEFTPAREN,
+	[100]	 = KEY_KPRIGHTPAREN,
+	[101]	 = KEY_KPSLASH,
+	[102]	 = KEY_KPASTERISK,
+	[103]	 = KEY_UP,
+	[104]	 = KEY_KPASTERISK,	/* FIXME */
+	[105]	 = KEY_LEFT,
+	[106]	 = KEY_RIGHT,
+	[107]	 = KEY_KPASTERISK,	/* FIXME */
+	[108]	 = KEY_DOWN,
+	[109]	 = KEY_KPASTERISK,	/* FIXME */
+	[110]	 = KEY_KPASTERISK,	/* FIXME */
+	[111]	 = KEY_KPASTERISK,	/* FIXME */
+	[112]	 = KEY_KPASTERISK,	/* FIXME */
+	[113]	 = KEY_KPASTERISK	/* FIXME */
+};
+
+static struct input_dev *atakbd_dev;
+
+static void atakbd_interrupt(unsigned char scancode, char down)
+{
+
+	if (scancode < 0x72) {		/* scancodes < 0xf2 are keys */
+
+		// report raw events here?
+
+		scancode = atakbd_keycode[scancode];
+
+		if (scancode == KEY_CAPSLOCK) {	/* CapsLock is a toggle switch key on Amiga */
+			input_report_key(atakbd_dev, scancode, 1);
+			input_report_key(atakbd_dev, scancode, 0);
+			input_sync(atakbd_dev);
+		} else {
+			input_report_key(atakbd_dev, scancode, down);
+			input_sync(atakbd_dev);
+		}
+	} else				/* scancodes >= 0xf2 are mouse data, most likely */
+		printk(KERN_INFO "atakbd: unhandled scancode %x\n", scancode);
+
+	return;
+}
+
+static int __init atakbd_init(void)
+{
+	int i, error;
+
+	if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP))
+		return -ENODEV;
+
+	// need to init core driver if not already done so
+	error = atari_keyb_init();
+	if (error)
+		return error;
+
+	atakbd_dev = input_allocate_device();
+	if (!atakbd_dev)
+		return -ENOMEM;
+
+	atakbd_dev->name = "Atari Keyboard";
+	atakbd_dev->phys = "atakbd/input0";
+	atakbd_dev->id.bustype = BUS_HOST;
+	atakbd_dev->id.vendor = 0x0001;
+	atakbd_dev->id.product = 0x0001;
+	atakbd_dev->id.version = 0x0100;
+
+	atakbd_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+	atakbd_dev->keycode = atakbd_keycode;
+	atakbd_dev->keycodesize = sizeof(unsigned char);
+	atakbd_dev->keycodemax = ARRAY_SIZE(atakbd_keycode);
+
+	for (i = 1; i < 0x72; i++) {
+		set_bit(atakbd_keycode[i], atakbd_dev->keybit);
+	}
+
+	/* error check */
+	error = input_register_device(atakbd_dev);
+	if (error) {
+		input_free_device(atakbd_dev);
+		return error;
+	}
+
+	atari_input_keyboard_interrupt_hook = atakbd_interrupt;
+
+	return 0;
+}
+
+static void __exit atakbd_exit(void)
+{
+	atari_input_keyboard_interrupt_hook = NULL;
+	input_unregister_device(atakbd_dev);
+}
+
+module_init(atakbd_init);
+module_exit(atakbd_exit);
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
new file mode 100644
index 0000000..ec876b5
--- /dev/null
+++ b/drivers/input/keyboard/atkbd.c
@@ -0,0 +1,1821 @@
+/*
+ * AT and PS/2 keyboard driver
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This driver can handle standard AT keyboards and PS/2 keyboards in
+ * Translated and Raw Set 2 and Set 3, as well as AT keyboards on dumb
+ * input-only controllers and AT keyboards connected over a one way RS232
+ * converter.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+#include <linux/libps2.h>
+#include <linux/mutex.h>
+#include <linux/dmi.h>
+
+#define DRIVER_DESC	"AT and PS/2 keyboard driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static int atkbd_set = 2;
+module_param_named(set, atkbd_set, int, 0);
+MODULE_PARM_DESC(set, "Select keyboard code set (2 = default, 3 = PS/2 native)");
+
+#if defined(__i386__) || defined(__x86_64__) || defined(__hppa__)
+static bool atkbd_reset;
+#else
+static bool atkbd_reset = true;
+#endif
+module_param_named(reset, atkbd_reset, bool, 0);
+MODULE_PARM_DESC(reset, "Reset keyboard during initialization");
+
+static bool atkbd_softrepeat;
+module_param_named(softrepeat, atkbd_softrepeat, bool, 0);
+MODULE_PARM_DESC(softrepeat, "Use software keyboard repeat");
+
+static bool atkbd_softraw = true;
+module_param_named(softraw, atkbd_softraw, bool, 0);
+MODULE_PARM_DESC(softraw, "Use software generated rawmode");
+
+static bool atkbd_scroll;
+module_param_named(scroll, atkbd_scroll, bool, 0);
+MODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards");
+
+static bool atkbd_extra;
+module_param_named(extra, atkbd_extra, bool, 0);
+MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and similar keyboards");
+
+static bool atkbd_terminal;
+module_param_named(terminal, atkbd_terminal, bool, 0);
+MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2");
+
+/*
+ * Scancode to keycode tables. These are just the default setting, and
+ * are loadable via a userland utility.
+ */
+
+#define ATKBD_KEYMAP_SIZE	512
+
+static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = {
+
+#ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES
+
+/* XXX: need a more general approach */
+
+#include "hpps2atkbd.h"	/* include the keyboard scancodes */
+
+#else
+	  0, 67, 65, 63, 61, 59, 60, 88,  0, 68, 66, 64, 62, 15, 41,117,
+	  0, 56, 42, 93, 29, 16,  2,  0,  0,  0, 44, 31, 30, 17,  3,  0,
+	  0, 46, 45, 32, 18,  5,  4, 95,  0, 57, 47, 33, 20, 19,  6,183,
+	  0, 49, 48, 35, 34, 21,  7,184,  0,  0, 50, 36, 22,  8,  9,185,
+	  0, 51, 37, 23, 24, 11, 10,  0,  0, 52, 53, 38, 39, 25, 12,  0,
+	  0, 89, 40,  0, 26, 13,  0,  0, 58, 54, 28, 27,  0, 43,  0, 85,
+	  0, 86, 91, 90, 92,  0, 14, 94,  0, 79,124, 75, 71,121,  0,  0,
+	 82, 83, 80, 76, 77, 72,  1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
+
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	217,100,255,  0, 97,165,  0,  0,156,  0,  0,  0,  0,  0,  0,125,
+	173,114,  0,113,  0,  0,  0,126,128,  0,  0,140,  0,  0,  0,127,
+	159,  0,115,  0,164,  0,  0,116,158,  0,172,166,  0,  0,  0,142,
+	157,  0,  0,  0,  0,  0,  0,  0,155,  0, 98,  0,  0,163,  0,  0,
+	226,  0,  0,  0,  0,  0,  0,  0,  0,255, 96,  0,  0,  0,143,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,107,  0,105,102,  0,  0,112,
+	110,111,108,112,106,103,  0,119,  0,118,109,  0, 99,104,119,  0,
+
+	  0,  0,  0, 65, 99,
+#endif
+};
+
+static const unsigned short atkbd_set3_keycode[ATKBD_KEYMAP_SIZE] = {
+
+	  0,  0,  0,  0,  0,  0,  0, 59,  1,138,128,129,130, 15, 41, 60,
+	131, 29, 42, 86, 58, 16,  2, 61,133, 56, 44, 31, 30, 17,  3, 62,
+	134, 46, 45, 32, 18,  5,  4, 63,135, 57, 47, 33, 20, 19,  6, 64,
+	136, 49, 48, 35, 34, 21,  7, 65,137,100, 50, 36, 22,  8,  9, 66,
+	125, 51, 37, 23, 24, 11, 10, 67,126, 52, 53, 38, 39, 25, 12, 68,
+	113,114, 40, 43, 26, 13, 87, 99, 97, 54, 28, 27, 43, 43, 88, 70,
+	108,105,119,103,111,107, 14,110,  0, 79,106, 75, 71,109,102,104,
+	 82, 83, 80, 76, 77, 72, 69, 98,  0, 96, 81,  0, 78, 73, 55,183,
+
+	184,185,186,187, 74, 94, 92, 93,  0,  0,  0,125,126,127,112,  0,
+	  0,139,172,163,165,115,152,172,166,140,160,154,113,114,167,168,
+	148,149,147,140
+};
+
+static const unsigned short atkbd_unxlate_table[128] = {
+          0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
+         21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
+         35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
+         50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88,  5,  6,  4, 12,  3,
+         11,  2, 10,  1,  9,119,126,108,117,125,123,107,115,116,121,105,
+        114,122,112,113,127, 96, 97,120,  7, 15, 23, 31, 39, 47, 55, 63,
+         71, 79, 86, 94,  8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
+         19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
+};
+
+#define ATKBD_CMD_SETLEDS	0x10ed
+#define ATKBD_CMD_GSCANSET	0x11f0
+#define ATKBD_CMD_SSCANSET	0x10f0
+#define ATKBD_CMD_GETID		0x02f2
+#define ATKBD_CMD_SETREP	0x10f3
+#define ATKBD_CMD_ENABLE	0x00f4
+#define ATKBD_CMD_RESET_DIS	0x00f5	/* Reset to defaults and disable */
+#define ATKBD_CMD_RESET_DEF	0x00f6	/* Reset to defaults */
+#define ATKBD_CMD_SETALL_MB	0x00f8	/* Set all keys to give break codes */
+#define ATKBD_CMD_SETALL_MBR	0x00fa  /* ... and repeat */
+#define ATKBD_CMD_RESET_BAT	0x02ff
+#define ATKBD_CMD_RESEND	0x00fe
+#define ATKBD_CMD_EX_ENABLE	0x10ea
+#define ATKBD_CMD_EX_SETLEDS	0x20eb
+#define ATKBD_CMD_OK_GETID	0x02e8
+
+#define ATKBD_RET_ACK		0xfa
+#define ATKBD_RET_NAK		0xfe
+#define ATKBD_RET_BAT		0xaa
+#define ATKBD_RET_EMUL0		0xe0
+#define ATKBD_RET_EMUL1		0xe1
+#define ATKBD_RET_RELEASE	0xf0
+#define ATKBD_RET_HANJA		0xf1
+#define ATKBD_RET_HANGEUL	0xf2
+#define ATKBD_RET_ERR		0xff
+
+#define ATKBD_KEY_UNKNOWN	0
+#define ATKBD_KEY_NULL		255
+
+#define ATKBD_SCR_1		0xfffe
+#define ATKBD_SCR_2		0xfffd
+#define ATKBD_SCR_4		0xfffc
+#define ATKBD_SCR_8		0xfffb
+#define ATKBD_SCR_CLICK		0xfffa
+#define ATKBD_SCR_LEFT		0xfff9
+#define ATKBD_SCR_RIGHT		0xfff8
+
+#define ATKBD_SPECIAL		ATKBD_SCR_RIGHT
+
+#define ATKBD_LED_EVENT_BIT	0
+#define ATKBD_REP_EVENT_BIT	1
+
+#define ATKBD_XL_ERR		0x01
+#define ATKBD_XL_BAT		0x02
+#define ATKBD_XL_ACK		0x04
+#define ATKBD_XL_NAK		0x08
+#define ATKBD_XL_HANGEUL	0x10
+#define ATKBD_XL_HANJA		0x20
+
+static const struct {
+	unsigned short keycode;
+	unsigned char set2;
+} atkbd_scroll_keys[] = {
+	{ ATKBD_SCR_1,     0xc5 },
+	{ ATKBD_SCR_2,     0x9d },
+	{ ATKBD_SCR_4,     0xa4 },
+	{ ATKBD_SCR_8,     0x9b },
+	{ ATKBD_SCR_CLICK, 0xe0 },
+	{ ATKBD_SCR_LEFT,  0xcb },
+	{ ATKBD_SCR_RIGHT, 0xd2 },
+};
+
+/*
+ * The atkbd control structure
+ */
+
+struct atkbd {
+
+	struct ps2dev ps2dev;
+	struct input_dev *dev;
+
+	/* Written only during init */
+	char name[64];
+	char phys[32];
+
+	unsigned short id;
+	unsigned short keycode[ATKBD_KEYMAP_SIZE];
+	DECLARE_BITMAP(force_release_mask, ATKBD_KEYMAP_SIZE);
+	unsigned char set;
+	bool translated;
+	bool extra;
+	bool write;
+	bool softrepeat;
+	bool softraw;
+	bool scroll;
+	bool enabled;
+
+	/* Accessed only from interrupt */
+	unsigned char emul;
+	bool resend;
+	bool release;
+	unsigned long xl_bit;
+	unsigned int last;
+	unsigned long time;
+	unsigned long err_count;
+
+	struct delayed_work event_work;
+	unsigned long event_jiffies;
+	unsigned long event_mask;
+
+	/* Serializes reconnect(), attr->set() and event work */
+	struct mutex mutex;
+};
+
+/*
+ * System-specific keymap fixup routine
+ */
+static void (*atkbd_platform_fixup)(struct atkbd *, const void *data);
+static void *atkbd_platform_fixup_data;
+static unsigned int (*atkbd_platform_scancode_fixup)(struct atkbd *, unsigned int);
+
+/*
+ * Certain keyboards to not like ATKBD_CMD_RESET_DIS and stop responding
+ * to many commands until full reset (ATKBD_CMD_RESET_BAT) is performed.
+ */
+static bool atkbd_skip_deactivate;
+
+static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
+				ssize_t (*handler)(struct atkbd *, char *));
+static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count,
+				ssize_t (*handler)(struct atkbd *, const char *, size_t));
+#define ATKBD_DEFINE_ATTR(_name)						\
+static ssize_t atkbd_show_##_name(struct atkbd *, char *);			\
+static ssize_t atkbd_set_##_name(struct atkbd *, const char *, size_t);		\
+static ssize_t atkbd_do_show_##_name(struct device *d,				\
+				struct device_attribute *attr, char *b)		\
+{										\
+	return atkbd_attr_show_helper(d, b, atkbd_show_##_name);		\
+}										\
+static ssize_t atkbd_do_set_##_name(struct device *d,				\
+			struct device_attribute *attr, const char *b, size_t s)	\
+{										\
+	return atkbd_attr_set_helper(d, b, s, atkbd_set_##_name);		\
+}										\
+static struct device_attribute atkbd_attr_##_name =				\
+	__ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name);
+
+ATKBD_DEFINE_ATTR(extra);
+ATKBD_DEFINE_ATTR(force_release);
+ATKBD_DEFINE_ATTR(scroll);
+ATKBD_DEFINE_ATTR(set);
+ATKBD_DEFINE_ATTR(softrepeat);
+ATKBD_DEFINE_ATTR(softraw);
+
+#define ATKBD_DEFINE_RO_ATTR(_name)						\
+static ssize_t atkbd_show_##_name(struct atkbd *, char *);			\
+static ssize_t atkbd_do_show_##_name(struct device *d,				\
+				struct device_attribute *attr, char *b)		\
+{										\
+	return atkbd_attr_show_helper(d, b, atkbd_show_##_name);		\
+}										\
+static struct device_attribute atkbd_attr_##_name =				\
+	__ATTR(_name, S_IRUGO, atkbd_do_show_##_name, NULL);
+
+ATKBD_DEFINE_RO_ATTR(err_count);
+
+static struct attribute *atkbd_attributes[] = {
+	&atkbd_attr_extra.attr,
+	&atkbd_attr_force_release.attr,
+	&atkbd_attr_scroll.attr,
+	&atkbd_attr_set.attr,
+	&atkbd_attr_softrepeat.attr,
+	&atkbd_attr_softraw.attr,
+	&atkbd_attr_err_count.attr,
+	NULL
+};
+
+static struct attribute_group atkbd_attribute_group = {
+	.attrs	= atkbd_attributes,
+};
+
+static const unsigned int xl_table[] = {
+	ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK,
+	ATKBD_RET_NAK, ATKBD_RET_HANJA, ATKBD_RET_HANGEUL,
+};
+
+/*
+ * Checks if we should mangle the scancode to extract 'release' bit
+ * in translated mode.
+ */
+static bool atkbd_need_xlate(unsigned long xl_bit, unsigned char code)
+{
+	int i;
+
+	if (code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1)
+		return false;
+
+	for (i = 0; i < ARRAY_SIZE(xl_table); i++)
+		if (code == xl_table[i])
+			return test_bit(i, &xl_bit);
+
+	return true;
+}
+
+/*
+ * Calculates new value of xl_bit so the driver can distinguish
+ * between make/break pair of scancodes for select keys and PS/2
+ * protocol responses.
+ */
+static void atkbd_calculate_xl_bit(struct atkbd *atkbd, unsigned char code)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(xl_table); i++) {
+		if (!((code ^ xl_table[i]) & 0x7f)) {
+			if (code & 0x80)
+				__clear_bit(i, &atkbd->xl_bit);
+			else
+				__set_bit(i, &atkbd->xl_bit);
+			break;
+		}
+	}
+}
+
+/*
+ * Encode the scancode, 0xe0 prefix, and high bit into a single integer,
+ * keeping kernel 2.4 compatibility for set 2
+ */
+static unsigned int atkbd_compat_scancode(struct atkbd *atkbd, unsigned int code)
+{
+	if (atkbd->set == 3) {
+		if (atkbd->emul == 1)
+			code |= 0x100;
+        } else {
+		code = (code & 0x7f) | ((code & 0x80) << 1);
+		if (atkbd->emul == 1)
+			code |= 0x80;
+	}
+
+	return code;
+}
+
+/*
+ * atkbd_interrupt(). Here takes place processing of data received from
+ * the keyboard into events.
+ */
+
+static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
+				   unsigned int flags)
+{
+	struct atkbd *atkbd = serio_get_drvdata(serio);
+	struct input_dev *dev = atkbd->dev;
+	unsigned int code = data;
+	int scroll = 0, hscroll = 0, click = -1;
+	int value;
+	unsigned short keycode;
+
+	dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, flags);
+
+#if !defined(__i386__) && !defined (__x86_64__)
+	if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) {
+		dev_warn(&serio->dev, "Frame/parity error: %02x\n", flags);
+		serio_write(serio, ATKBD_CMD_RESEND);
+		atkbd->resend = true;
+		goto out;
+	}
+
+	if (!flags && data == ATKBD_RET_ACK)
+		atkbd->resend = false;
+#endif
+
+	if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_ACK))
+		if  (ps2_handle_ack(&atkbd->ps2dev, data))
+			goto out;
+
+	if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_CMD))
+		if  (ps2_handle_response(&atkbd->ps2dev, data))
+			goto out;
+
+	if (!atkbd->enabled)
+		goto out;
+
+	input_event(dev, EV_MSC, MSC_RAW, code);
+
+	if (atkbd_platform_scancode_fixup)
+		code = atkbd_platform_scancode_fixup(atkbd, code);
+
+	if (atkbd->translated) {
+
+		if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) {
+			atkbd->release = code >> 7;
+			code &= 0x7f;
+		}
+
+		if (!atkbd->emul)
+			atkbd_calculate_xl_bit(atkbd, data);
+	}
+
+	switch (code) {
+	case ATKBD_RET_BAT:
+		atkbd->enabled = false;
+		serio_reconnect(atkbd->ps2dev.serio);
+		goto out;
+	case ATKBD_RET_EMUL0:
+		atkbd->emul = 1;
+		goto out;
+	case ATKBD_RET_EMUL1:
+		atkbd->emul = 2;
+		goto out;
+	case ATKBD_RET_RELEASE:
+		atkbd->release = true;
+		goto out;
+	case ATKBD_RET_ACK:
+	case ATKBD_RET_NAK:
+		if (printk_ratelimit())
+			dev_warn(&serio->dev,
+				 "Spurious %s on %s. "
+				 "Some program might be trying to access hardware directly.\n",
+				 data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
+		goto out;
+	case ATKBD_RET_ERR:
+		atkbd->err_count++;
+		dev_dbg(&serio->dev, "Keyboard on %s reports too many keys pressed.\n",
+			serio->phys);
+		goto out;
+	}
+
+	code = atkbd_compat_scancode(atkbd, code);
+
+	if (atkbd->emul && --atkbd->emul)
+		goto out;
+
+	keycode = atkbd->keycode[code];
+
+	if (!(atkbd->release && test_bit(code, atkbd->force_release_mask)))
+		if (keycode != ATKBD_KEY_NULL)
+			input_event(dev, EV_MSC, MSC_SCAN, code);
+
+	switch (keycode) {
+	case ATKBD_KEY_NULL:
+		break;
+	case ATKBD_KEY_UNKNOWN:
+		dev_warn(&serio->dev,
+			 "Unknown key %s (%s set %d, code %#x on %s).\n",
+			 atkbd->release ? "released" : "pressed",
+			 atkbd->translated ? "translated" : "raw",
+			 atkbd->set, code, serio->phys);
+		dev_warn(&serio->dev,
+			 "Use 'setkeycodes %s%02x <keycode>' to make it known.\n",
+			 code & 0x80 ? "e0" : "", code & 0x7f);
+		input_sync(dev);
+		break;
+	case ATKBD_SCR_1:
+		scroll = 1;
+		break;
+	case ATKBD_SCR_2:
+		scroll = 2;
+		break;
+	case ATKBD_SCR_4:
+		scroll = 4;
+		break;
+	case ATKBD_SCR_8:
+		scroll = 8;
+		break;
+	case ATKBD_SCR_CLICK:
+		click = !atkbd->release;
+		break;
+	case ATKBD_SCR_LEFT:
+		hscroll = -1;
+		break;
+	case ATKBD_SCR_RIGHT:
+		hscroll = 1;
+		break;
+	default:
+		if (atkbd->release) {
+			value = 0;
+			atkbd->last = 0;
+		} else if (!atkbd->softrepeat && test_bit(keycode, dev->key)) {
+			/* Workaround Toshiba laptop multiple keypress */
+			value = time_before(jiffies, atkbd->time) && atkbd->last == code ? 1 : 2;
+		} else {
+			value = 1;
+			atkbd->last = code;
+			atkbd->time = jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]) / 2;
+		}
+
+		input_event(dev, EV_KEY, keycode, value);
+		input_sync(dev);
+
+		if (value && test_bit(code, atkbd->force_release_mask)) {
+			input_event(dev, EV_MSC, MSC_SCAN, code);
+			input_report_key(dev, keycode, 0);
+			input_sync(dev);
+		}
+	}
+
+	if (atkbd->scroll) {
+		if (click != -1)
+			input_report_key(dev, BTN_MIDDLE, click);
+		input_report_rel(dev, REL_WHEEL,
+				 atkbd->release ? -scroll : scroll);
+		input_report_rel(dev, REL_HWHEEL, hscroll);
+		input_sync(dev);
+	}
+
+	atkbd->release = false;
+out:
+	return IRQ_HANDLED;
+}
+
+static int atkbd_set_repeat_rate(struct atkbd *atkbd)
+{
+	const short period[32] =
+		{ 33,  37,  42,  46,  50,  54,  58,  63,  67,  75,  83,  92, 100, 109, 116, 125,
+		 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 };
+	const short delay[4] =
+		{ 250, 500, 750, 1000 };
+
+	struct input_dev *dev = atkbd->dev;
+	unsigned char param;
+	int i = 0, j = 0;
+
+	while (i < ARRAY_SIZE(period) - 1 && period[i] < dev->rep[REP_PERIOD])
+		i++;
+	dev->rep[REP_PERIOD] = period[i];
+
+	while (j < ARRAY_SIZE(delay) - 1 && delay[j] < dev->rep[REP_DELAY])
+		j++;
+	dev->rep[REP_DELAY] = delay[j];
+
+	param = i | (j << 5);
+	return ps2_command(&atkbd->ps2dev, &param, ATKBD_CMD_SETREP);
+}
+
+static int atkbd_set_leds(struct atkbd *atkbd)
+{
+	struct input_dev *dev = atkbd->dev;
+	unsigned char param[2];
+
+	param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
+		 | (test_bit(LED_NUML,    dev->led) ? 2 : 0)
+		 | (test_bit(LED_CAPSL,   dev->led) ? 4 : 0);
+	if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS))
+		return -1;
+
+	if (atkbd->extra) {
+		param[0] = 0;
+		param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0)
+			 | (test_bit(LED_SLEEP,   dev->led) ? 0x02 : 0)
+			 | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0)
+			 | (test_bit(LED_MISC,    dev->led) ? 0x10 : 0)
+			 | (test_bit(LED_MUTE,    dev->led) ? 0x20 : 0);
+		if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS))
+			return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * atkbd_event_work() is used to complete processing of events that
+ * can not be processed by input_event() which is often called from
+ * interrupt context.
+ */
+
+static void atkbd_event_work(struct work_struct *work)
+{
+	struct atkbd *atkbd = container_of(work, struct atkbd, event_work.work);
+
+	mutex_lock(&atkbd->mutex);
+
+	if (!atkbd->enabled) {
+		/*
+		 * Serio ports are resumed asynchronously so while driver core
+		 * thinks that device is already fully operational in reality
+		 * it may not be ready yet. In this case we need to keep
+		 * rescheduling till reconnect completes.
+		 */
+		schedule_delayed_work(&atkbd->event_work,
+					msecs_to_jiffies(100));
+	} else {
+		if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask))
+			atkbd_set_leds(atkbd);
+
+		if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask))
+			atkbd_set_repeat_rate(atkbd);
+	}
+
+	mutex_unlock(&atkbd->mutex);
+}
+
+/*
+ * Schedule switch for execution. We need to throttle requests,
+ * otherwise keyboard may become unresponsive.
+ */
+static void atkbd_schedule_event_work(struct atkbd *atkbd, int event_bit)
+{
+	unsigned long delay = msecs_to_jiffies(50);
+
+	if (time_after(jiffies, atkbd->event_jiffies + delay))
+		delay = 0;
+
+	atkbd->event_jiffies = jiffies;
+	set_bit(event_bit, &atkbd->event_mask);
+	mb();
+	schedule_delayed_work(&atkbd->event_work, delay);
+}
+
+/*
+ * Event callback from the input module. Events that change the state of
+ * the hardware are processed here. If action can not be performed in
+ * interrupt context it is offloaded to atkbd_event_work.
+ */
+
+static int atkbd_event(struct input_dev *dev,
+			unsigned int type, unsigned int code, int value)
+{
+	struct atkbd *atkbd = input_get_drvdata(dev);
+
+	if (!atkbd->write)
+		return -1;
+
+	switch (type) {
+
+	case EV_LED:
+		atkbd_schedule_event_work(atkbd, ATKBD_LED_EVENT_BIT);
+		return 0;
+
+	case EV_REP:
+		if (!atkbd->softrepeat)
+			atkbd_schedule_event_work(atkbd, ATKBD_REP_EVENT_BIT);
+		return 0;
+
+	default:
+		return -1;
+	}
+}
+
+/*
+ * atkbd_enable() signals that interrupt handler is allowed to
+ * generate input events.
+ */
+
+static inline void atkbd_enable(struct atkbd *atkbd)
+{
+	serio_pause_rx(atkbd->ps2dev.serio);
+	atkbd->enabled = true;
+	serio_continue_rx(atkbd->ps2dev.serio);
+}
+
+/*
+ * atkbd_disable() tells input handler that all incoming data except
+ * for ACKs and command response should be dropped.
+ */
+
+static inline void atkbd_disable(struct atkbd *atkbd)
+{
+	serio_pause_rx(atkbd->ps2dev.serio);
+	atkbd->enabled = false;
+	serio_continue_rx(atkbd->ps2dev.serio);
+}
+
+static int atkbd_activate(struct atkbd *atkbd)
+{
+	struct ps2dev *ps2dev = &atkbd->ps2dev;
+
+/*
+ * Enable the keyboard to receive keystrokes.
+ */
+
+	if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
+		dev_err(&ps2dev->serio->dev,
+			"Failed to enable keyboard on %s\n",
+			ps2dev->serio->phys);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * atkbd_deactivate() resets and disables the keyboard from sending
+ * keystrokes.
+ */
+
+static void atkbd_deactivate(struct atkbd *atkbd)
+{
+	struct ps2dev *ps2dev = &atkbd->ps2dev;
+
+	if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_DIS))
+		dev_err(&ps2dev->serio->dev,
+			"Failed to deactivate keyboard on %s\n",
+			ps2dev->serio->phys);
+}
+
+/*
+ * atkbd_probe() probes for an AT keyboard on a serio port.
+ */
+
+static int atkbd_probe(struct atkbd *atkbd)
+{
+	struct ps2dev *ps2dev = &atkbd->ps2dev;
+	unsigned char param[2];
+
+/*
+ * Some systems, where the bit-twiddling when testing the io-lines of the
+ * controller may confuse the keyboard need a full reset of the keyboard. On
+ * these systems the BIOS also usually doesn't do it for us.
+ */
+
+	if (atkbd_reset)
+		if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_BAT))
+			dev_warn(&ps2dev->serio->dev,
+				 "keyboard reset failed on %s\n",
+				 ps2dev->serio->phys);
+
+/*
+ * Then we check the keyboard ID. We should get 0xab83 under normal conditions.
+ * Some keyboards report different values, but the first byte is always 0xab or
+ * 0xac. Some old AT keyboards don't report anything. If a mouse is connected, this
+ * should make sure we don't try to set the LEDs on it.
+ */
+
+	param[0] = param[1] = 0xa5;	/* initialize with invalid values */
+	if (ps2_command(ps2dev, param, ATKBD_CMD_GETID)) {
+
+/*
+ * If the get ID command failed, we check if we can at least set the LEDs on
+ * the keyboard. This should work on every keyboard out there. It also turns
+ * the LEDs off, which we want anyway.
+ */
+		param[0] = 0;
+		if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS))
+			return -1;
+		atkbd->id = 0xabba;
+		return 0;
+	}
+
+	if (!ps2_is_keyboard_id(param[0]))
+		return -1;
+
+	atkbd->id = (param[0] << 8) | param[1];
+
+	if (atkbd->id == 0xaca1 && atkbd->translated) {
+		dev_err(&ps2dev->serio->dev,
+			"NCD terminal keyboards are only supported on non-translating controllers. "
+			"Use i8042.direct=1 to disable translation.\n");
+		return -1;
+	}
+
+/*
+ * Make sure nothing is coming from the keyboard and disturbs our
+ * internal state.
+ */
+	if (!atkbd_skip_deactivate)
+		atkbd_deactivate(atkbd);
+
+	return 0;
+}
+
+/*
+ * atkbd_select_set checks if a keyboard has a working Set 3 support, and
+ * sets it into that. Unfortunately there are keyboards that can be switched
+ * to Set 3, but don't work well in that (BTC Multimedia ...)
+ */
+
+static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra)
+{
+	struct ps2dev *ps2dev = &atkbd->ps2dev;
+	unsigned char param[2];
+
+	atkbd->extra = false;
+/*
+ * For known special keyboards we can go ahead and set the correct set.
+ * We check for NCD PS/2 Sun, NorthGate OmniKey 101 and
+ * IBM RapidAccess / IBM EzButton / Chicony KBP-8993 keyboards.
+ */
+
+	if (atkbd->translated)
+		return 2;
+
+	if (atkbd->id == 0xaca1) {
+		param[0] = 3;
+		ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET);
+		return 3;
+	}
+
+	if (allow_extra) {
+		param[0] = 0x71;
+		if (!ps2_command(ps2dev, param, ATKBD_CMD_EX_ENABLE)) {
+			atkbd->extra = true;
+			return 2;
+		}
+	}
+
+	if (atkbd_terminal) {
+		ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MB);
+		return 3;
+	}
+
+	if (target_set != 3)
+		return 2;
+
+	if (!ps2_command(ps2dev, param, ATKBD_CMD_OK_GETID)) {
+		atkbd->id = param[0] << 8 | param[1];
+		return 2;
+	}
+
+	param[0] = 3;
+	if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET))
+		return 2;
+
+	param[0] = 0;
+	if (ps2_command(ps2dev, param, ATKBD_CMD_GSCANSET))
+		return 2;
+
+	if (param[0] != 3) {
+		param[0] = 2;
+		if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET))
+		return 2;
+	}
+
+	ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MBR);
+
+	return 3;
+}
+
+static int atkbd_reset_state(struct atkbd *atkbd)
+{
+        struct ps2dev *ps2dev = &atkbd->ps2dev;
+	unsigned char param[1];
+
+/*
+ * Set the LEDs to a predefined state (all off).
+ */
+
+	param[0] = 0;
+	if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS))
+		return -1;
+
+/*
+ * Set autorepeat to fastest possible.
+ */
+
+	param[0] = 0;
+	if (ps2_command(ps2dev, param, ATKBD_CMD_SETREP))
+		return -1;
+
+	return 0;
+}
+
+/*
+ * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
+ * reboot.
+ */
+
+static void atkbd_cleanup(struct serio *serio)
+{
+	struct atkbd *atkbd = serio_get_drvdata(serio);
+
+	atkbd_disable(atkbd);
+	ps2_command(&atkbd->ps2dev, NULL, ATKBD_CMD_RESET_DEF);
+}
+
+
+/*
+ * atkbd_disconnect() closes and frees.
+ */
+
+static void atkbd_disconnect(struct serio *serio)
+{
+	struct atkbd *atkbd = serio_get_drvdata(serio);
+
+	sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
+
+	atkbd_disable(atkbd);
+
+	input_unregister_device(atkbd->dev);
+
+	/*
+	 * Make sure we don't have a command in flight.
+	 * Note that since atkbd->enabled is false event work will keep
+	 * rescheduling itself until it gets canceled and will not try
+	 * accessing freed input device or serio port.
+	 */
+	cancel_delayed_work_sync(&atkbd->event_work);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(atkbd);
+}
+
+/*
+ * generate release events for the keycodes given in data
+ */
+static void atkbd_apply_forced_release_keylist(struct atkbd* atkbd,
+						const void *data)
+{
+	const unsigned int *keys = data;
+	unsigned int i;
+
+	if (atkbd->set == 2)
+		for (i = 0; keys[i] != -1U; i++)
+			__set_bit(keys[i], atkbd->force_release_mask);
+}
+
+/*
+ * Most special keys (Fn+F?) on Dell laptops do not generate release
+ * events so we have to do it ourselves.
+ */
+static unsigned int atkbd_dell_laptop_forced_release_keys[] = {
+	0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93, -1U
+};
+
+/*
+ * Perform fixup for HP system that doesn't generate release
+ * for its video switch
+ */
+static unsigned int atkbd_hp_forced_release_keys[] = {
+	0x94, -1U
+};
+
+/*
+ * Samsung NC10,NC20 with Fn+F? key release not working
+ */
+static unsigned int atkbd_samsung_forced_release_keys[] = {
+	0x82, 0x83, 0x84, 0x86, 0x88, 0x89, 0xb3, 0xf7, 0xf9, -1U
+};
+
+/*
+ * Amilo Pi 3525 key release for Fn+Volume keys not working
+ */
+static unsigned int atkbd_amilo_pi3525_forced_release_keys[] = {
+	0x20, 0xa0, 0x2e, 0xae, 0x30, 0xb0, -1U
+};
+
+/*
+ * Amilo Xi 3650 key release for light touch bar not working
+ */
+static unsigned int atkbd_amilo_xi3650_forced_release_keys[] = {
+	0x67, 0xed, 0x90, 0xa2, 0x99, 0xa4, 0xae, 0xb0, -1U
+};
+
+/*
+ * Soltech TA12 system with broken key release on volume keys and mute key
+ */
+static unsigned int atkdb_soltech_ta12_forced_release_keys[] = {
+	0xa0, 0xae, 0xb0, -1U
+};
+
+/*
+ * Many notebooks don't send key release event for volume up/down
+ * keys, with key list below common among them
+ */
+static unsigned int atkbd_volume_forced_release_keys[] = {
+	0xae, 0xb0, -1U
+};
+
+/*
+ * OQO 01+ multimedia keys (64--66) generate e0 6x upon release whereas
+ * they should be generating e4-e6 (0x80 | code).
+ */
+static unsigned int atkbd_oqo_01plus_scancode_fixup(struct atkbd *atkbd,
+						    unsigned int code)
+{
+	if (atkbd->translated && atkbd->emul == 1 &&
+	    (code == 0x64 || code == 0x65 || code == 0x66)) {
+		atkbd->emul = 0;
+		code |= 0x80;
+	}
+
+	return code;
+}
+
+/*
+ * atkbd_set_keycode_table() initializes keyboard's keycode table
+ * according to the selected scancode set
+ */
+
+static void atkbd_set_keycode_table(struct atkbd *atkbd)
+{
+	unsigned int scancode;
+	int i, j;
+
+	memset(atkbd->keycode, 0, sizeof(atkbd->keycode));
+	bitmap_zero(atkbd->force_release_mask, ATKBD_KEYMAP_SIZE);
+
+	if (atkbd->translated) {
+		for (i = 0; i < 128; i++) {
+			scancode = atkbd_unxlate_table[i];
+			atkbd->keycode[i] = atkbd_set2_keycode[scancode];
+			atkbd->keycode[i | 0x80] = atkbd_set2_keycode[scancode | 0x80];
+			if (atkbd->scroll)
+				for (j = 0; j < ARRAY_SIZE(atkbd_scroll_keys); j++)
+					if ((scancode | 0x80) == atkbd_scroll_keys[j].set2)
+						atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode;
+		}
+	} else if (atkbd->set == 3) {
+		memcpy(atkbd->keycode, atkbd_set3_keycode, sizeof(atkbd->keycode));
+	} else {
+		memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode));
+
+		if (atkbd->scroll)
+			for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++) {
+				scancode = atkbd_scroll_keys[i].set2;
+				atkbd->keycode[scancode] = atkbd_scroll_keys[i].keycode;
+		}
+	}
+
+/*
+ * HANGEUL and HANJA keys do not send release events so we need to
+ * generate such events ourselves
+ */
+	scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANGEUL);
+	atkbd->keycode[scancode] = KEY_HANGEUL;
+	__set_bit(scancode, atkbd->force_release_mask);
+
+	scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANJA);
+	atkbd->keycode[scancode] = KEY_HANJA;
+	__set_bit(scancode, atkbd->force_release_mask);
+
+/*
+ * Perform additional fixups
+ */
+	if (atkbd_platform_fixup)
+		atkbd_platform_fixup(atkbd, atkbd_platform_fixup_data);
+}
+
+/*
+ * atkbd_set_device_attrs() sets up keyboard's input device structure
+ */
+
+static void atkbd_set_device_attrs(struct atkbd *atkbd)
+{
+	struct input_dev *input_dev = atkbd->dev;
+	int i;
+
+	if (atkbd->extra)
+		snprintf(atkbd->name, sizeof(atkbd->name),
+			 "AT Set 2 Extra keyboard");
+	else
+		snprintf(atkbd->name, sizeof(atkbd->name),
+			 "AT %s Set %d keyboard",
+			 atkbd->translated ? "Translated" : "Raw", atkbd->set);
+
+	snprintf(atkbd->phys, sizeof(atkbd->phys),
+		 "%s/input0", atkbd->ps2dev.serio->phys);
+
+	input_dev->name = atkbd->name;
+	input_dev->phys = atkbd->phys;
+	input_dev->id.bustype = BUS_I8042;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = atkbd->translated ? 1 : atkbd->set;
+	input_dev->id.version = atkbd->id;
+	input_dev->event = atkbd_event;
+	input_dev->dev.parent = &atkbd->ps2dev.serio->dev;
+
+	input_set_drvdata(input_dev, atkbd);
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
+		BIT_MASK(EV_MSC);
+
+	if (atkbd->write) {
+		input_dev->evbit[0] |= BIT_MASK(EV_LED);
+		input_dev->ledbit[0] = BIT_MASK(LED_NUML) |
+			BIT_MASK(LED_CAPSL) | BIT_MASK(LED_SCROLLL);
+	}
+
+	if (atkbd->extra)
+		input_dev->ledbit[0] |= BIT_MASK(LED_COMPOSE) |
+			BIT_MASK(LED_SUSPEND) | BIT_MASK(LED_SLEEP) |
+			BIT_MASK(LED_MUTE) | BIT_MASK(LED_MISC);
+
+	if (!atkbd->softrepeat) {
+		input_dev->rep[REP_DELAY] = 250;
+		input_dev->rep[REP_PERIOD] = 33;
+	}
+
+	input_dev->mscbit[0] = atkbd->softraw ? BIT_MASK(MSC_SCAN) :
+		BIT_MASK(MSC_RAW) | BIT_MASK(MSC_SCAN);
+
+	if (atkbd->scroll) {
+		input_dev->evbit[0] |= BIT_MASK(EV_REL);
+		input_dev->relbit[0] = BIT_MASK(REL_WHEEL) |
+			BIT_MASK(REL_HWHEEL);
+		__set_bit(BTN_MIDDLE, input_dev->keybit);
+	}
+
+	input_dev->keycode = atkbd->keycode;
+	input_dev->keycodesize = sizeof(unsigned short);
+	input_dev->keycodemax = ARRAY_SIZE(atkbd_set2_keycode);
+
+	for (i = 0; i < ATKBD_KEYMAP_SIZE; i++) {
+		if (atkbd->keycode[i] != KEY_RESERVED &&
+		    atkbd->keycode[i] != ATKBD_KEY_NULL &&
+		    atkbd->keycode[i] < ATKBD_SPECIAL) {
+			__set_bit(atkbd->keycode[i], input_dev->keybit);
+		}
+	}
+}
+
+/*
+ * atkbd_connect() is called when the serio module finds an interface
+ * that isn't handled yet by an appropriate device driver. We check if
+ * there is an AT keyboard out there and if yes, we register ourselves
+ * to the input module.
+ */
+
+static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct atkbd *atkbd;
+	struct input_dev *dev;
+	int err = -ENOMEM;
+
+	atkbd = kzalloc(sizeof(struct atkbd), GFP_KERNEL);
+	dev = input_allocate_device();
+	if (!atkbd || !dev)
+		goto fail1;
+
+	atkbd->dev = dev;
+	ps2_init(&atkbd->ps2dev, serio);
+	INIT_DELAYED_WORK(&atkbd->event_work, atkbd_event_work);
+	mutex_init(&atkbd->mutex);
+
+	switch (serio->id.type) {
+
+	case SERIO_8042_XL:
+		atkbd->translated = true;
+		/* Fall through */
+
+	case SERIO_8042:
+		if (serio->write)
+			atkbd->write = true;
+		break;
+	}
+
+	atkbd->softraw = atkbd_softraw;
+	atkbd->softrepeat = atkbd_softrepeat;
+	atkbd->scroll = atkbd_scroll;
+
+	if (atkbd->softrepeat)
+		atkbd->softraw = true;
+
+	serio_set_drvdata(serio, atkbd);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	if (atkbd->write) {
+
+		if (atkbd_probe(atkbd)) {
+			err = -ENODEV;
+			goto fail3;
+		}
+
+		atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
+		atkbd_reset_state(atkbd);
+
+	} else {
+		atkbd->set = 2;
+		atkbd->id = 0xab00;
+	}
+
+	atkbd_set_keycode_table(atkbd);
+	atkbd_set_device_attrs(atkbd);
+
+	err = sysfs_create_group(&serio->dev.kobj, &atkbd_attribute_group);
+	if (err)
+		goto fail3;
+
+	atkbd_enable(atkbd);
+	if (serio->write)
+		atkbd_activate(atkbd);
+
+	err = input_register_device(atkbd->dev);
+	if (err)
+		goto fail4;
+
+	return 0;
+
+ fail4: sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(dev);
+	kfree(atkbd);
+	return err;
+}
+
+/*
+ * atkbd_reconnect() tries to restore keyboard into a sane state and is
+ * most likely called on resume.
+ */
+
+static int atkbd_reconnect(struct serio *serio)
+{
+	struct atkbd *atkbd = serio_get_drvdata(serio);
+	struct serio_driver *drv = serio->drv;
+	int retval = -1;
+
+	if (!atkbd || !drv) {
+		dev_dbg(&serio->dev,
+			"reconnect request, but serio is disconnected, ignoring...\n");
+		return -1;
+	}
+
+	mutex_lock(&atkbd->mutex);
+
+	atkbd_disable(atkbd);
+
+	if (atkbd->write) {
+		if (atkbd_probe(atkbd))
+			goto out;
+
+		if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
+			goto out;
+
+		/*
+		 * Restore LED state and repeat rate. While input core
+		 * will do this for us at resume time reconnect may happen
+		 * because user requested it via sysfs or simply because
+		 * keyboard was unplugged and plugged in again so we need
+		 * to do it ourselves here.
+		 */
+		atkbd_set_leds(atkbd);
+		if (!atkbd->softrepeat)
+			atkbd_set_repeat_rate(atkbd);
+
+	}
+
+	/*
+	 * Reset our state machine in case reconnect happened in the middle
+	 * of multi-byte scancode.
+	 */
+	atkbd->xl_bit = 0;
+	atkbd->emul = 0;
+
+	atkbd_enable(atkbd);
+	if (atkbd->write)
+		atkbd_activate(atkbd);
+
+	retval = 0;
+
+ out:
+	mutex_unlock(&atkbd->mutex);
+	return retval;
+}
+
+static struct serio_device_id atkbd_serio_ids[] = {
+	{
+		.type	= SERIO_8042,
+		.proto	= SERIO_ANY,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_8042_XL,
+		.proto	= SERIO_ANY,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_PS2SER,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, atkbd_serio_ids);
+
+static struct serio_driver atkbd_drv = {
+	.driver		= {
+		.name	= "atkbd",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= atkbd_serio_ids,
+	.interrupt	= atkbd_interrupt,
+	.connect	= atkbd_connect,
+	.reconnect	= atkbd_reconnect,
+	.disconnect	= atkbd_disconnect,
+	.cleanup	= atkbd_cleanup,
+};
+
+static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
+				ssize_t (*handler)(struct atkbd *, char *))
+{
+	struct serio *serio = to_serio_port(dev);
+	struct atkbd *atkbd = serio_get_drvdata(serio);
+
+	return handler(atkbd, buf);
+}
+
+static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count,
+				ssize_t (*handler)(struct atkbd *, const char *, size_t))
+{
+	struct serio *serio = to_serio_port(dev);
+	struct atkbd *atkbd = serio_get_drvdata(serio);
+	int retval;
+
+	retval = mutex_lock_interruptible(&atkbd->mutex);
+	if (retval)
+		return retval;
+
+	atkbd_disable(atkbd);
+	retval = handler(atkbd, buf, count);
+	atkbd_enable(atkbd);
+
+	mutex_unlock(&atkbd->mutex);
+
+	return retval;
+}
+
+static ssize_t atkbd_show_extra(struct atkbd *atkbd, char *buf)
+{
+	return sprintf(buf, "%d\n", atkbd->extra ? 1 : 0);
+}
+
+static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t count)
+{
+	struct input_dev *old_dev, *new_dev;
+	unsigned int value;
+	int err;
+	bool old_extra;
+	unsigned char old_set;
+
+	if (!atkbd->write)
+		return -EIO;
+
+	err = kstrtouint(buf, 10, &value);
+	if (err)
+		return err;
+
+	if (value > 1)
+		return -EINVAL;
+
+	if (atkbd->extra != value) {
+		/*
+		 * Since device's properties will change we need to
+		 * unregister old device. But allocate and register
+		 * new one first to make sure we have it.
+		 */
+		old_dev = atkbd->dev;
+		old_extra = atkbd->extra;
+		old_set = atkbd->set;
+
+		new_dev = input_allocate_device();
+		if (!new_dev)
+			return -ENOMEM;
+
+		atkbd->dev = new_dev;
+		atkbd->set = atkbd_select_set(atkbd, atkbd->set, value);
+		atkbd_reset_state(atkbd);
+		atkbd_activate(atkbd);
+		atkbd_set_keycode_table(atkbd);
+		atkbd_set_device_attrs(atkbd);
+
+		err = input_register_device(atkbd->dev);
+		if (err) {
+			input_free_device(new_dev);
+
+			atkbd->dev = old_dev;
+			atkbd->set = atkbd_select_set(atkbd, old_set, old_extra);
+			atkbd_set_keycode_table(atkbd);
+			atkbd_set_device_attrs(atkbd);
+
+			return err;
+		}
+		input_unregister_device(old_dev);
+
+	}
+	return count;
+}
+
+static ssize_t atkbd_show_force_release(struct atkbd *atkbd, char *buf)
+{
+	size_t len = scnprintf(buf, PAGE_SIZE - 1, "%*pbl",
+			       ATKBD_KEYMAP_SIZE, atkbd->force_release_mask);
+
+	buf[len++] = '\n';
+	buf[len] = '\0';
+
+	return len;
+}
+
+static ssize_t atkbd_set_force_release(struct atkbd *atkbd,
+					const char *buf, size_t count)
+{
+	/* 64 bytes on stack should be acceptable */
+	DECLARE_BITMAP(new_mask, ATKBD_KEYMAP_SIZE);
+	int err;
+
+	err = bitmap_parselist(buf, new_mask, ATKBD_KEYMAP_SIZE);
+	if (err)
+		return err;
+
+	memcpy(atkbd->force_release_mask, new_mask, sizeof(atkbd->force_release_mask));
+	return count;
+}
+
+
+static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf)
+{
+	return sprintf(buf, "%d\n", atkbd->scroll ? 1 : 0);
+}
+
+static ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t count)
+{
+	struct input_dev *old_dev, *new_dev;
+	unsigned int value;
+	int err;
+	bool old_scroll;
+
+	err = kstrtouint(buf, 10, &value);
+	if (err)
+		return err;
+
+	if (value > 1)
+		return -EINVAL;
+
+	if (atkbd->scroll != value) {
+		old_dev = atkbd->dev;
+		old_scroll = atkbd->scroll;
+
+		new_dev = input_allocate_device();
+		if (!new_dev)
+			return -ENOMEM;
+
+		atkbd->dev = new_dev;
+		atkbd->scroll = value;
+		atkbd_set_keycode_table(atkbd);
+		atkbd_set_device_attrs(atkbd);
+
+		err = input_register_device(atkbd->dev);
+		if (err) {
+			input_free_device(new_dev);
+
+			atkbd->scroll = old_scroll;
+			atkbd->dev = old_dev;
+			atkbd_set_keycode_table(atkbd);
+			atkbd_set_device_attrs(atkbd);
+
+			return err;
+		}
+		input_unregister_device(old_dev);
+	}
+	return count;
+}
+
+static ssize_t atkbd_show_set(struct atkbd *atkbd, char *buf)
+{
+	return sprintf(buf, "%d\n", atkbd->set);
+}
+
+static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count)
+{
+	struct input_dev *old_dev, *new_dev;
+	unsigned int value;
+	int err;
+	unsigned char old_set;
+	bool old_extra;
+
+	if (!atkbd->write)
+		return -EIO;
+
+	err = kstrtouint(buf, 10, &value);
+	if (err)
+		return err;
+
+	if (value != 2 && value != 3)
+		return -EINVAL;
+
+	if (atkbd->set != value) {
+		old_dev = atkbd->dev;
+		old_extra = atkbd->extra;
+		old_set = atkbd->set;
+
+		new_dev = input_allocate_device();
+		if (!new_dev)
+			return -ENOMEM;
+
+		atkbd->dev = new_dev;
+		atkbd->set = atkbd_select_set(atkbd, value, atkbd->extra);
+		atkbd_reset_state(atkbd);
+		atkbd_activate(atkbd);
+		atkbd_set_keycode_table(atkbd);
+		atkbd_set_device_attrs(atkbd);
+
+		err = input_register_device(atkbd->dev);
+		if (err) {
+			input_free_device(new_dev);
+
+			atkbd->dev = old_dev;
+			atkbd->set = atkbd_select_set(atkbd, old_set, old_extra);
+			atkbd_set_keycode_table(atkbd);
+			atkbd_set_device_attrs(atkbd);
+
+			return err;
+		}
+		input_unregister_device(old_dev);
+	}
+	return count;
+}
+
+static ssize_t atkbd_show_softrepeat(struct atkbd *atkbd, char *buf)
+{
+	return sprintf(buf, "%d\n", atkbd->softrepeat ? 1 : 0);
+}
+
+static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t count)
+{
+	struct input_dev *old_dev, *new_dev;
+	unsigned int value;
+	int err;
+	bool old_softrepeat, old_softraw;
+
+	if (!atkbd->write)
+		return -EIO;
+
+	err = kstrtouint(buf, 10, &value);
+	if (err)
+		return err;
+
+	if (value > 1)
+		return -EINVAL;
+
+	if (atkbd->softrepeat != value) {
+		old_dev = atkbd->dev;
+		old_softrepeat = atkbd->softrepeat;
+		old_softraw = atkbd->softraw;
+
+		new_dev = input_allocate_device();
+		if (!new_dev)
+			return -ENOMEM;
+
+		atkbd->dev = new_dev;
+		atkbd->softrepeat = value;
+		if (atkbd->softrepeat)
+			atkbd->softraw = true;
+		atkbd_set_device_attrs(atkbd);
+
+		err = input_register_device(atkbd->dev);
+		if (err) {
+			input_free_device(new_dev);
+
+			atkbd->dev = old_dev;
+			atkbd->softrepeat = old_softrepeat;
+			atkbd->softraw = old_softraw;
+			atkbd_set_device_attrs(atkbd);
+
+			return err;
+		}
+		input_unregister_device(old_dev);
+	}
+	return count;
+}
+
+
+static ssize_t atkbd_show_softraw(struct atkbd *atkbd, char *buf)
+{
+	return sprintf(buf, "%d\n", atkbd->softraw ? 1 : 0);
+}
+
+static ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t count)
+{
+	struct input_dev *old_dev, *new_dev;
+	unsigned int value;
+	int err;
+	bool old_softraw;
+
+	err = kstrtouint(buf, 10, &value);
+	if (err)
+		return err;
+
+	if (value > 1)
+		return -EINVAL;
+
+	if (atkbd->softraw != value) {
+		old_dev = atkbd->dev;
+		old_softraw = atkbd->softraw;
+
+		new_dev = input_allocate_device();
+		if (!new_dev)
+			return -ENOMEM;
+
+		atkbd->dev = new_dev;
+		atkbd->softraw = value;
+		atkbd_set_device_attrs(atkbd);
+
+		err = input_register_device(atkbd->dev);
+		if (err) {
+			input_free_device(new_dev);
+
+			atkbd->dev = old_dev;
+			atkbd->softraw = old_softraw;
+			atkbd_set_device_attrs(atkbd);
+
+			return err;
+		}
+		input_unregister_device(old_dev);
+	}
+	return count;
+}
+
+static ssize_t atkbd_show_err_count(struct atkbd *atkbd, char *buf)
+{
+	return sprintf(buf, "%lu\n", atkbd->err_count);
+}
+
+static int __init atkbd_setup_forced_release(const struct dmi_system_id *id)
+{
+	atkbd_platform_fixup = atkbd_apply_forced_release_keylist;
+	atkbd_platform_fixup_data = id->driver_data;
+
+	return 1;
+}
+
+static int __init atkbd_setup_scancode_fixup(const struct dmi_system_id *id)
+{
+	atkbd_platform_scancode_fixup = id->driver_data;
+
+	return 1;
+}
+
+static int __init atkbd_deactivate_fixup(const struct dmi_system_id *id)
+{
+	atkbd_skip_deactivate = true;
+	return 1;
+}
+
+/*
+ * NOTE: do not add any more "force release" quirks to this table.  The
+ * task of adjusting list of keys that should be "released" automatically
+ * by the driver is now delegated to userspace tools, such as udev, so
+ * submit such quirks there.
+ */
+static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_dell_laptop_forced_release_keys,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+			DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_dell_laptop_forced_release_keys,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "HP 2133"),
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_hp_forced_release_keys,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion ZV6100"),
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_volume_forced_release_keys,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4000"),
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_volume_forced_release_keys,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4100"),
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_volume_forced_release_keys,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4200"),
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_volume_forced_release_keys,
+	},
+	{
+		/* Inventec Symphony */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "SYMPHONY 6.0/7.0"),
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_volume_forced_release_keys,
+	},
+	{
+		/* Samsung NC10 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_samsung_forced_release_keys,
+	},
+	{
+		/* Samsung NC20 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "NC20"),
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_samsung_forced_release_keys,
+	},
+	{
+		/* Samsung SQ45S70S */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_samsung_forced_release_keys,
+	},
+	{
+		/* Fujitsu Amilo PA 1510 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 1510"),
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_volume_forced_release_keys,
+	},
+	{
+		/* Fujitsu Amilo Pi 3525 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 3525"),
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_amilo_pi3525_forced_release_keys,
+	},
+	{
+		/* Fujitsu Amilo Xi 3650 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 3650"),
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkbd_amilo_xi3650_forced_release_keys,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Soltech Corporation"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TA12"),
+		},
+		.callback = atkbd_setup_forced_release,
+		.driver_data = atkdb_soltech_ta12_forced_release_keys,
+	},
+	{
+		/* OQO Model 01+ */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "OQO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"),
+		},
+		.callback = atkbd_setup_scancode_fixup,
+		.driver_data = atkbd_oqo_01plus_scancode_fixup,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
+		},
+		.callback = atkbd_deactivate_fixup,
+	},
+	{ }
+};
+
+static int __init atkbd_init(void)
+{
+	dmi_check_system(atkbd_dmi_quirk_table);
+
+	return serio_register_driver(&atkbd_drv);
+}
+
+static void __exit atkbd_exit(void)
+{
+	serio_unregister_driver(&atkbd_drv);
+}
+
+module_init(atkbd_init);
+module_exit(atkbd_exit);
diff --git a/drivers/input/keyboard/bcm-keypad.c b/drivers/input/keyboard/bcm-keypad.c
new file mode 100644
index 0000000..86a8b72
--- /dev/null
+++ b/drivers/input/keyboard/bcm-keypad.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/gfp.h>
+#include <linux/io.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
+
+#define DEFAULT_CLK_HZ			31250
+#define MAX_ROWS			8
+#define MAX_COLS			8
+
+/* Register/field definitions */
+#define KPCR_OFFSET			0x00000080
+#define KPCR_MODE			0x00000002
+#define KPCR_MODE_SHIFT			1
+#define KPCR_MODE_MASK			1
+#define KPCR_ENABLE			0x00000001
+#define KPCR_STATUSFILTERENABLE		0x00008000
+#define KPCR_STATUSFILTERTYPE_SHIFT	12
+#define KPCR_COLFILTERENABLE		0x00000800
+#define KPCR_COLFILTERTYPE_SHIFT	8
+#define KPCR_ROWWIDTH_SHIFT		20
+#define KPCR_COLUMNWIDTH_SHIFT		16
+
+#define KPIOR_OFFSET			0x00000084
+#define KPIOR_ROWOCONTRL_SHIFT		24
+#define KPIOR_ROWOCONTRL_MASK		0xFF000000
+#define KPIOR_COLUMNOCONTRL_SHIFT	16
+#define KPIOR_COLUMNOCONTRL_MASK	0x00FF0000
+#define KPIOR_COLUMN_IO_DATA_SHIFT	0
+
+#define KPEMR0_OFFSET			0x00000090
+#define KPEMR1_OFFSET			0x00000094
+#define KPEMR2_OFFSET			0x00000098
+#define KPEMR3_OFFSET			0x0000009C
+#define KPEMR_EDGETYPE_BOTH		3
+
+#define KPSSR0_OFFSET			0x000000A0
+#define KPSSR1_OFFSET			0x000000A4
+#define KPSSRN_OFFSET(reg_n)		(KPSSR0_OFFSET + 4 * (reg_n))
+#define KPIMR0_OFFSET			0x000000B0
+#define KPIMR1_OFFSET			0x000000B4
+#define KPICR0_OFFSET			0x000000B8
+#define KPICR1_OFFSET			0x000000BC
+#define KPICRN_OFFSET(reg_n)		(KPICR0_OFFSET + 4 * (reg_n))
+#define KPISR0_OFFSET			0x000000C0
+#define KPISR1_OFFSET			0x000000C4
+
+#define KPCR_STATUSFILTERTYPE_MAX	7
+#define KPCR_COLFILTERTYPE_MAX		7
+
+/* Macros to determine the row/column from a bit that is set in SSR0/1. */
+#define BIT_TO_ROW_SSRN(bit_nr, reg_n)	(((bit_nr) >> 3) + 4 * (reg_n))
+#define BIT_TO_COL(bit_nr)		((bit_nr) % 8)
+
+/* Structure representing various run-time entities */
+struct bcm_kp {
+	void __iomem *base;
+	int irq;
+	struct clk *clk;
+	struct input_dev *input_dev;
+	unsigned long last_state[2];
+	unsigned int n_rows;
+	unsigned int n_cols;
+	u32 kpcr;
+	u32 kpior;
+	u32 kpemr;
+	u32 imr0_val;
+	u32 imr1_val;
+};
+
+/*
+ * Returns the keycode from the input device keymap given the row and
+ * column.
+ */
+static int bcm_kp_get_keycode(struct bcm_kp *kp, int row, int col)
+{
+	unsigned int row_shift = get_count_order(kp->n_cols);
+	unsigned short *keymap = kp->input_dev->keycode;
+
+	return keymap[MATRIX_SCAN_CODE(row, col, row_shift)];
+}
+
+static void bcm_kp_report_keys(struct bcm_kp *kp, int reg_num, int pull_mode)
+{
+	unsigned long state, change;
+	int bit_nr;
+	int key_press;
+	int row, col;
+	unsigned int keycode;
+
+	/* Clear interrupts */
+	writel(0xFFFFFFFF, kp->base + KPICRN_OFFSET(reg_num));
+
+	state = readl(kp->base + KPSSRN_OFFSET(reg_num));
+	change = kp->last_state[reg_num] ^ state;
+	kp->last_state[reg_num] = state;
+
+	for_each_set_bit(bit_nr, &change, BITS_PER_LONG) {
+		key_press = state & BIT(bit_nr);
+		/* The meaning of SSR register depends on pull mode. */
+		key_press = pull_mode ? !key_press : key_press;
+		row = BIT_TO_ROW_SSRN(bit_nr, reg_num);
+		col = BIT_TO_COL(bit_nr);
+		keycode = bcm_kp_get_keycode(kp, row, col);
+		input_report_key(kp->input_dev, keycode, key_press);
+	}
+}
+
+static irqreturn_t bcm_kp_isr_thread(int irq, void *dev_id)
+{
+	struct bcm_kp *kp = dev_id;
+	int pull_mode = (kp->kpcr >> KPCR_MODE_SHIFT) & KPCR_MODE_MASK;
+	int reg_num;
+
+	for (reg_num = 0; reg_num <= 1; reg_num++)
+		bcm_kp_report_keys(kp, reg_num, pull_mode);
+
+	input_sync(kp->input_dev);
+
+	return IRQ_HANDLED;
+}
+
+static int bcm_kp_start(struct bcm_kp *kp)
+{
+	int error;
+
+	if (kp->clk) {
+		error = clk_prepare_enable(kp->clk);
+		if (error)
+			return error;
+	}
+
+	writel(kp->kpior, kp->base + KPIOR_OFFSET);
+
+	writel(kp->imr0_val, kp->base + KPIMR0_OFFSET);
+	writel(kp->imr1_val, kp->base + KPIMR1_OFFSET);
+
+	writel(kp->kpemr, kp->base + KPEMR0_OFFSET);
+	writel(kp->kpemr, kp->base + KPEMR1_OFFSET);
+	writel(kp->kpemr, kp->base + KPEMR2_OFFSET);
+	writel(kp->kpemr, kp->base + KPEMR3_OFFSET);
+
+	writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET);
+	writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET);
+
+	kp->last_state[0] = readl(kp->base + KPSSR0_OFFSET);
+	kp->last_state[0] = readl(kp->base + KPSSR1_OFFSET);
+
+	writel(kp->kpcr | KPCR_ENABLE, kp->base + KPCR_OFFSET);
+
+	return 0;
+}
+
+static void bcm_kp_stop(const struct bcm_kp *kp)
+{
+	u32 val;
+
+	val = readl(kp->base + KPCR_OFFSET);
+	val &= ~KPCR_ENABLE;
+	writel(0, kp->base + KPCR_OFFSET);
+	writel(0, kp->base + KPIMR0_OFFSET);
+	writel(0, kp->base + KPIMR1_OFFSET);
+	writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET);
+	writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET);
+
+	if (kp->clk)
+		clk_disable_unprepare(kp->clk);
+}
+
+static int bcm_kp_open(struct input_dev *dev)
+{
+	struct bcm_kp *kp = input_get_drvdata(dev);
+
+	return bcm_kp_start(kp);
+}
+
+static void bcm_kp_close(struct input_dev *dev)
+{
+	struct bcm_kp *kp = input_get_drvdata(dev);
+
+	bcm_kp_stop(kp);
+}
+
+static int bcm_kp_matrix_key_parse_dt(struct bcm_kp *kp)
+{
+	struct device *dev = kp->input_dev->dev.parent;
+	struct device_node *np = dev->of_node;
+	int error;
+	unsigned int dt_val;
+	unsigned int i;
+	unsigned int num_rows, col_mask, rows_set;
+
+	/* Initialize the KPCR Keypad Configuration Register */
+	kp->kpcr = KPCR_STATUSFILTERENABLE | KPCR_COLFILTERENABLE;
+
+	error = matrix_keypad_parse_of_params(dev, &kp->n_rows, &kp->n_cols);
+	if (error) {
+		dev_err(dev, "failed to parse kp params\n");
+		return error;
+	}
+
+	/* Set row width for the ASIC block. */
+	kp->kpcr |= (kp->n_rows - 1) << KPCR_ROWWIDTH_SHIFT;
+
+	/* Set column width for the ASIC block. */
+	kp->kpcr |= (kp->n_cols - 1) << KPCR_COLUMNWIDTH_SHIFT;
+
+	/* Configure the IMR registers */
+
+	/*
+	 * IMR registers contain interrupt enable bits for 8x8 matrix
+	 * IMR0 register format: <row3> <row2> <row1> <row0>
+	 * IMR1 register format: <row7> <row6> <row5> <row4>
+	 */
+	col_mask = (1 << (kp->n_cols)) - 1;
+	num_rows = kp->n_rows;
+
+	/* Set column bits in rows 0 to 3 in IMR0 */
+	kp->imr0_val = col_mask;
+
+	rows_set = 1;
+	while (--num_rows && rows_set++ < 4)
+		kp->imr0_val |= kp->imr0_val << MAX_COLS;
+
+	/* Set column bits in rows 4 to 7 in IMR1 */
+	kp->imr1_val = 0;
+	if (num_rows) {
+		kp->imr1_val = col_mask;
+		while (--num_rows)
+			kp->imr1_val |= kp->imr1_val << MAX_COLS;
+	}
+
+	/* Initialize the KPEMR Keypress Edge Mode Registers */
+	/* Trigger on both edges */
+	kp->kpemr = 0;
+	for (i = 0; i <= 30; i += 2)
+		kp->kpemr |= (KPEMR_EDGETYPE_BOTH << i);
+
+	/*
+	 * Obtain the Status filter debounce value and verify against the
+	 * possible values specified in the DT binding.
+	 */
+	of_property_read_u32(np, "status-debounce-filter-period", &dt_val);
+
+	if (dt_val > KPCR_STATUSFILTERTYPE_MAX) {
+		dev_err(dev, "Invalid Status filter debounce value %d\n",
+			dt_val);
+		return -EINVAL;
+	}
+
+	kp->kpcr |= dt_val << KPCR_STATUSFILTERTYPE_SHIFT;
+
+	/*
+	 * Obtain the Column filter debounce value and verify against the
+	 * possible values specified in the DT binding.
+	 */
+	of_property_read_u32(np, "col-debounce-filter-period", &dt_val);
+
+	if (dt_val > KPCR_COLFILTERTYPE_MAX) {
+		dev_err(dev, "Invalid Column filter debounce value %d\n",
+			dt_val);
+		return -EINVAL;
+	}
+
+	kp->kpcr |= dt_val << KPCR_COLFILTERTYPE_SHIFT;
+
+	/*
+	 * Determine between the row and column,
+	 * which should be configured as output.
+	 */
+	if (of_property_read_bool(np, "row-output-enabled")) {
+		/*
+		* Set RowOContrl or ColumnOContrl in KPIOR
+		* to the number of pins to drive as outputs
+		*/
+		kp->kpior = ((1 << kp->n_rows) - 1) <<
+				KPIOR_ROWOCONTRL_SHIFT;
+	} else {
+		kp->kpior = ((1 << kp->n_cols) - 1) <<
+				KPIOR_COLUMNOCONTRL_SHIFT;
+	}
+
+	/*
+	 * Determine if the scan pull up needs to be enabled
+	 */
+	if (of_property_read_bool(np, "pull-up-enabled"))
+		kp->kpcr |= KPCR_MODE;
+
+	dev_dbg(dev, "n_rows=%d n_col=%d kpcr=%x kpior=%x kpemr=%x\n",
+		kp->n_rows, kp->n_cols,
+		kp->kpcr, kp->kpior, kp->kpemr);
+
+	return 0;
+}
+
+
+static int bcm_kp_probe(struct platform_device *pdev)
+{
+	struct bcm_kp *kp;
+	struct input_dev *input_dev;
+	struct resource *res;
+	int error;
+
+	kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
+	if (!kp)
+		return -ENOMEM;
+
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!input_dev) {
+		dev_err(&pdev->dev, "failed to allocate the input device\n");
+		return -ENOMEM;
+	}
+
+	__set_bit(EV_KEY, input_dev->evbit);
+
+	/* Enable auto repeat feature of Linux input subsystem */
+	if (of_property_read_bool(pdev->dev.of_node, "autorepeat"))
+		__set_bit(EV_REP, input_dev->evbit);
+
+	input_dev->name = pdev->name;
+	input_dev->phys = "keypad/input0";
+	input_dev->dev.parent = &pdev->dev;
+	input_dev->open = bcm_kp_open;
+	input_dev->close = bcm_kp_close;
+
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+
+	input_set_drvdata(input_dev, kp);
+
+	kp->input_dev = input_dev;
+
+	platform_set_drvdata(pdev, kp);
+
+	error = bcm_kp_matrix_key_parse_dt(kp);
+	if (error)
+		return error;
+
+	error = matrix_keypad_build_keymap(NULL, NULL,
+					   kp->n_rows, kp->n_cols,
+					   NULL, input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to build keymap\n");
+		return error;
+	}
+
+	/* Get the KEYPAD base address */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Missing keypad base address resource\n");
+		return -ENODEV;
+	}
+
+	kp->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(kp->base))
+		return PTR_ERR(kp->base);
+
+	/* Enable clock */
+	kp->clk = devm_clk_get(&pdev->dev, "peri_clk");
+	if (IS_ERR(kp->clk)) {
+		error = PTR_ERR(kp->clk);
+		if (error != -ENOENT) {
+			if (error != -EPROBE_DEFER)
+				dev_err(&pdev->dev, "Failed to get clock\n");
+			return error;
+		}
+		dev_dbg(&pdev->dev,
+			"No clock specified. Assuming it's enabled\n");
+		kp->clk = NULL;
+	} else {
+		unsigned int desired_rate;
+		long actual_rate;
+
+		error = of_property_read_u32(pdev->dev.of_node,
+					     "clock-frequency", &desired_rate);
+		if (error < 0)
+			desired_rate = DEFAULT_CLK_HZ;
+
+		actual_rate = clk_round_rate(kp->clk, desired_rate);
+		if (actual_rate <= 0)
+			return -EINVAL;
+
+		error = clk_set_rate(kp->clk, actual_rate);
+		if (error)
+			return error;
+
+		error = clk_prepare_enable(kp->clk);
+		if (error)
+			return error;
+	}
+
+	/* Put the kp into a known sane state */
+	bcm_kp_stop(kp);
+
+	kp->irq = platform_get_irq(pdev, 0);
+	if (kp->irq < 0) {
+		dev_err(&pdev->dev, "no IRQ specified\n");
+		return -EINVAL;
+	}
+
+	error = devm_request_threaded_irq(&pdev->dev, kp->irq,
+					  NULL, bcm_kp_isr_thread,
+					  IRQF_ONESHOT, pdev->name, kp);
+	if (error) {
+		dev_err(&pdev->dev, "failed to request IRQ\n");
+		return error;
+	}
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id bcm_kp_of_match[] = {
+	{ .compatible = "brcm,bcm-keypad" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, bcm_kp_of_match);
+
+static struct platform_driver bcm_kp_device_driver = {
+	.probe		= bcm_kp_probe,
+	.driver		= {
+		.name	= "bcm-keypad",
+		.of_match_table = of_match_ptr(bcm_kp_of_match),
+	}
+};
+
+module_platform_driver(bcm_kp_device_driver);
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("BCM Keypad Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c
new file mode 100644
index 0000000..81b07dd
--- /dev/null
+++ b/drivers/input/keyboard/bf54x-keys.c
@@ -0,0 +1,399 @@
+/*
+ * File:         drivers/input/keyboard/bf54x-keys.c
+ * Based on:
+ * Author:       Michael Hennerich <hennerich@blackfin.uclinux.org>
+ *
+ * Created:
+ * Description:  keypad driver for Analog Devices Blackfin BF54x Processors
+ *
+ *
+ * Modified:
+ *               Copyright 2007-2008 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+
+#include <asm/portmux.h>
+#include <mach/bf54x_keys.h>
+
+#define DRV_NAME	"bf54x-keys"
+#define TIME_SCALE	100	/* 100 ns */
+#define	MAX_MULT	(0xFF * TIME_SCALE)
+#define MAX_RC		8	/* Max Row/Col */
+
+static const u16 per_rows[] = {
+	P_KEY_ROW7,
+	P_KEY_ROW6,
+	P_KEY_ROW5,
+	P_KEY_ROW4,
+	P_KEY_ROW3,
+	P_KEY_ROW2,
+	P_KEY_ROW1,
+	P_KEY_ROW0,
+	0
+};
+
+static const u16 per_cols[] = {
+	P_KEY_COL7,
+	P_KEY_COL6,
+	P_KEY_COL5,
+	P_KEY_COL4,
+	P_KEY_COL3,
+	P_KEY_COL2,
+	P_KEY_COL1,
+	P_KEY_COL0,
+	0
+};
+
+struct bf54x_kpad {
+	struct input_dev *input;
+	int irq;
+	unsigned short lastkey;
+	unsigned short *keycode;
+	struct timer_list timer;
+	unsigned int keyup_test_jiffies;
+	unsigned short kpad_msel;
+	unsigned short kpad_prescale;
+	unsigned short kpad_ctl;
+};
+
+static inline int bfin_kpad_find_key(struct bf54x_kpad *bf54x_kpad,
+			struct input_dev *input, u16 keyident)
+{
+	u16 i;
+
+	for (i = 0; i < input->keycodemax; i++)
+		if (bf54x_kpad->keycode[i + input->keycodemax] == keyident)
+			return bf54x_kpad->keycode[i];
+	return -1;
+}
+
+static inline void bfin_keycodecpy(unsigned short *keycode,
+			const unsigned int *pdata_kc,
+			unsigned short keymapsize)
+{
+	unsigned int i;
+
+	for (i = 0; i < keymapsize; i++) {
+		keycode[i] = pdata_kc[i] & 0xffff;
+		keycode[i + keymapsize] = pdata_kc[i] >> 16;
+	}
+}
+
+static inline u16 bfin_kpad_get_prescale(u32 timescale)
+{
+	u32 sclk = get_sclk();
+
+	return ((((sclk / 1000) * timescale) / 1024) - 1);
+}
+
+static inline u16 bfin_kpad_get_keypressed(struct bf54x_kpad *bf54x_kpad)
+{
+	return (bfin_read_KPAD_STAT() & KPAD_PRESSED);
+}
+
+static inline void bfin_kpad_clear_irq(void)
+{
+	bfin_write_KPAD_STAT(0xFFFF);
+	bfin_write_KPAD_ROWCOL(0xFFFF);
+}
+
+static void bfin_kpad_timer(unsigned long data)
+{
+	struct platform_device *pdev =  (struct platform_device *) data;
+	struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
+
+	if (bfin_kpad_get_keypressed(bf54x_kpad)) {
+		/* Try again later */
+		mod_timer(&bf54x_kpad->timer,
+			  jiffies + bf54x_kpad->keyup_test_jiffies);
+		return;
+	}
+
+	input_report_key(bf54x_kpad->input, bf54x_kpad->lastkey, 0);
+	input_sync(bf54x_kpad->input);
+
+	/* Clear IRQ Status */
+
+	bfin_kpad_clear_irq();
+	enable_irq(bf54x_kpad->irq);
+}
+
+static irqreturn_t bfin_kpad_isr(int irq, void *dev_id)
+{
+	struct platform_device *pdev = dev_id;
+	struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
+	struct input_dev *input = bf54x_kpad->input;
+	int key;
+	u16 rowcol = bfin_read_KPAD_ROWCOL();
+
+	key = bfin_kpad_find_key(bf54x_kpad, input, rowcol);
+
+	input_report_key(input, key, 1);
+	input_sync(input);
+
+	if (bfin_kpad_get_keypressed(bf54x_kpad)) {
+		disable_irq_nosync(bf54x_kpad->irq);
+		bf54x_kpad->lastkey = key;
+		mod_timer(&bf54x_kpad->timer,
+			  jiffies + bf54x_kpad->keyup_test_jiffies);
+	} else {
+		input_report_key(input, key, 0);
+		input_sync(input);
+
+		bfin_kpad_clear_irq();
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int bfin_kpad_probe(struct platform_device *pdev)
+{
+	struct bf54x_kpad *bf54x_kpad;
+	struct bfin_kpad_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct input_dev *input;
+	int i, error;
+
+	if (!pdata->rows || !pdata->cols || !pdata->keymap) {
+		dev_err(&pdev->dev, "no rows, cols or keymap from pdata\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->keymapsize ||
+	    pdata->keymapsize > (pdata->rows * pdata->cols)) {
+		dev_err(&pdev->dev, "invalid keymapsize\n");
+		return -EINVAL;
+	}
+
+	bf54x_kpad = kzalloc(sizeof(struct bf54x_kpad), GFP_KERNEL);
+	if (!bf54x_kpad)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, bf54x_kpad);
+
+	/* Allocate memory for keymap followed by private LUT */
+	bf54x_kpad->keycode = kmalloc(pdata->keymapsize *
+					sizeof(unsigned short) * 2, GFP_KERNEL);
+	if (!bf54x_kpad->keycode) {
+		error = -ENOMEM;
+		goto out;
+	}
+
+	if (!pdata->debounce_time || pdata->debounce_time > MAX_MULT ||
+	    !pdata->coldrive_time || pdata->coldrive_time > MAX_MULT) {
+		dev_warn(&pdev->dev,
+			"invalid platform debounce/columndrive time\n");
+		bfin_write_KPAD_MSEL(0xFF0);	/* Default MSEL	*/
+	} else {
+		bfin_write_KPAD_MSEL(
+			((pdata->debounce_time / TIME_SCALE)
+						& DBON_SCALE) |
+			(((pdata->coldrive_time / TIME_SCALE) << 8)
+						& COLDRV_SCALE));
+
+	}
+
+	if (!pdata->keyup_test_interval)
+		bf54x_kpad->keyup_test_jiffies = msecs_to_jiffies(50);
+	else
+		bf54x_kpad->keyup_test_jiffies =
+			msecs_to_jiffies(pdata->keyup_test_interval);
+
+	if (peripheral_request_list((u16 *)&per_rows[MAX_RC - pdata->rows],
+				    DRV_NAME)) {
+		dev_err(&pdev->dev, "requesting peripherals failed\n");
+		error = -EFAULT;
+		goto out0;
+	}
+
+	if (peripheral_request_list((u16 *)&per_cols[MAX_RC - pdata->cols],
+				    DRV_NAME)) {
+		dev_err(&pdev->dev, "requesting peripherals failed\n");
+		error = -EFAULT;
+		goto out1;
+	}
+
+	bf54x_kpad->irq = platform_get_irq(pdev, 0);
+	if (bf54x_kpad->irq < 0) {
+		error = -ENODEV;
+		goto out2;
+	}
+
+	error = request_irq(bf54x_kpad->irq, bfin_kpad_isr,
+				0, DRV_NAME, pdev);
+	if (error) {
+		dev_err(&pdev->dev, "unable to claim irq %d\n",
+			bf54x_kpad->irq);
+		goto out2;
+	}
+
+	input = input_allocate_device();
+	if (!input) {
+		error = -ENOMEM;
+		goto out3;
+	}
+
+	bf54x_kpad->input = input;
+
+	input->name = pdev->name;
+	input->phys = "bf54x-keys/input0";
+	input->dev.parent = &pdev->dev;
+
+	input_set_drvdata(input, bf54x_kpad);
+
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0100;
+
+	input->keycodesize = sizeof(unsigned short);
+	input->keycodemax = pdata->keymapsize;
+	input->keycode = bf54x_kpad->keycode;
+
+	bfin_keycodecpy(bf54x_kpad->keycode, pdata->keymap, pdata->keymapsize);
+
+	/* setup input device */
+	__set_bit(EV_KEY, input->evbit);
+
+	if (pdata->repeat)
+		__set_bit(EV_REP, input->evbit);
+
+	for (i = 0; i < input->keycodemax; i++)
+		if (bf54x_kpad->keycode[i] <= KEY_MAX)
+			__set_bit(bf54x_kpad->keycode[i], input->keybit);
+	__clear_bit(KEY_RESERVED, input->keybit);
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(&pdev->dev, "unable to register input device\n");
+		goto out4;
+	}
+
+	/* Init Keypad Key Up/Release test timer */
+
+	setup_timer(&bf54x_kpad->timer, bfin_kpad_timer, (unsigned long) pdev);
+
+	bfin_write_KPAD_PRESCALE(bfin_kpad_get_prescale(TIME_SCALE));
+
+	bfin_write_KPAD_CTL((((pdata->cols - 1) << 13) & KPAD_COLEN) |
+				(((pdata->rows - 1) << 10) & KPAD_ROWEN) |
+				(2 & KPAD_IRQMODE));
+
+	bfin_write_KPAD_CTL(bfin_read_KPAD_CTL() | KPAD_EN);
+
+	device_init_wakeup(&pdev->dev, 1);
+
+	return 0;
+
+out4:
+	input_free_device(input);
+out3:
+	free_irq(bf54x_kpad->irq, pdev);
+out2:
+	peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
+out1:
+	peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
+out0:
+	kfree(bf54x_kpad->keycode);
+out:
+	kfree(bf54x_kpad);
+
+	return error;
+}
+
+static int bfin_kpad_remove(struct platform_device *pdev)
+{
+	struct bfin_kpad_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
+
+	del_timer_sync(&bf54x_kpad->timer);
+	free_irq(bf54x_kpad->irq, pdev);
+
+	input_unregister_device(bf54x_kpad->input);
+
+	peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
+	peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
+
+	kfree(bf54x_kpad->keycode);
+	kfree(bf54x_kpad);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int bfin_kpad_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
+
+	bf54x_kpad->kpad_msel = bfin_read_KPAD_MSEL();
+	bf54x_kpad->kpad_prescale = bfin_read_KPAD_PRESCALE();
+	bf54x_kpad->kpad_ctl = bfin_read_KPAD_CTL();
+
+	if (device_may_wakeup(&pdev->dev))
+		enable_irq_wake(bf54x_kpad->irq);
+
+	return 0;
+}
+
+static int bfin_kpad_resume(struct platform_device *pdev)
+{
+	struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
+
+	bfin_write_KPAD_MSEL(bf54x_kpad->kpad_msel);
+	bfin_write_KPAD_PRESCALE(bf54x_kpad->kpad_prescale);
+	bfin_write_KPAD_CTL(bf54x_kpad->kpad_ctl);
+
+	if (device_may_wakeup(&pdev->dev))
+		disable_irq_wake(bf54x_kpad->irq);
+
+	return 0;
+}
+#else
+# define bfin_kpad_suspend NULL
+# define bfin_kpad_resume  NULL
+#endif
+
+static struct platform_driver bfin_kpad_device_driver = {
+	.driver		= {
+		.name	= DRV_NAME,
+	},
+	.probe		= bfin_kpad_probe,
+	.remove		= bfin_kpad_remove,
+	.suspend	= bfin_kpad_suspend,
+	.resume		= bfin_kpad_resume,
+};
+module_platform_driver(bfin_kpad_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Keypad driver for BF54x Processors");
+MODULE_ALIAS("platform:bf54x-keys");
diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
new file mode 100644
index 0000000..378db10
--- /dev/null
+++ b/drivers/input/keyboard/cap11xx.c
@@ -0,0 +1,512 @@
+/*
+ * Input driver for Microchip CAP11xx based capacitive touch sensors
+ *
+ * (c) 2014 Daniel Mack <linux@zonque.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+
+#define CAP11XX_REG_MAIN_CONTROL	0x00
+#define CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT	(6)
+#define CAP11XX_REG_MAIN_CONTROL_GAIN_MASK	(0xc0)
+#define CAP11XX_REG_MAIN_CONTROL_DLSEEP		BIT(4)
+#define CAP11XX_REG_GENERAL_STATUS	0x02
+#define CAP11XX_REG_SENSOR_INPUT	0x03
+#define CAP11XX_REG_NOISE_FLAG_STATUS	0x0a
+#define CAP11XX_REG_SENOR_DELTA(X)	(0x10 + (X))
+#define CAP11XX_REG_SENSITIVITY_CONTROL	0x1f
+#define CAP11XX_REG_CONFIG		0x20
+#define CAP11XX_REG_SENSOR_ENABLE	0x21
+#define CAP11XX_REG_SENSOR_CONFIG	0x22
+#define CAP11XX_REG_SENSOR_CONFIG2	0x23
+#define CAP11XX_REG_SAMPLING_CONFIG	0x24
+#define CAP11XX_REG_CALIBRATION		0x26
+#define CAP11XX_REG_INT_ENABLE		0x27
+#define CAP11XX_REG_REPEAT_RATE		0x28
+#define CAP11XX_REG_MT_CONFIG		0x2a
+#define CAP11XX_REG_MT_PATTERN_CONFIG	0x2b
+#define CAP11XX_REG_MT_PATTERN		0x2d
+#define CAP11XX_REG_RECALIB_CONFIG	0x2f
+#define CAP11XX_REG_SENSOR_THRESH(X)	(0x30 + (X))
+#define CAP11XX_REG_SENSOR_NOISE_THRESH	0x38
+#define CAP11XX_REG_STANDBY_CHANNEL	0x40
+#define CAP11XX_REG_STANDBY_CONFIG	0x41
+#define CAP11XX_REG_STANDBY_SENSITIVITY	0x42
+#define CAP11XX_REG_STANDBY_THRESH	0x43
+#define CAP11XX_REG_CONFIG2		0x44
+#define CAP11XX_REG_CONFIG2_ALT_POL	BIT(6)
+#define CAP11XX_REG_SENSOR_BASE_CNT(X)	(0x50 + (X))
+#define CAP11XX_REG_LED_POLARITY	0x73
+#define CAP11XX_REG_LED_OUTPUT_CONTROL	0x74
+
+#define CAP11XX_REG_LED_DUTY_CYCLE_1	0x90
+#define CAP11XX_REG_LED_DUTY_CYCLE_2	0x91
+#define CAP11XX_REG_LED_DUTY_CYCLE_3	0x92
+#define CAP11XX_REG_LED_DUTY_CYCLE_4	0x93
+
+#define CAP11XX_REG_LED_DUTY_MIN_MASK	(0x0f)
+#define CAP11XX_REG_LED_DUTY_MIN_MASK_SHIFT	(0)
+#define CAP11XX_REG_LED_DUTY_MAX_MASK	(0xf0)
+#define CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT	(4)
+#define CAP11XX_REG_LED_DUTY_MAX_VALUE	(15)
+
+#define CAP11XX_REG_SENSOR_CALIB	(0xb1 + (X))
+#define CAP11XX_REG_SENSOR_CALIB_LSB1	0xb9
+#define CAP11XX_REG_SENSOR_CALIB_LSB2	0xba
+#define CAP11XX_REG_PRODUCT_ID		0xfd
+#define CAP11XX_REG_MANUFACTURER_ID	0xfe
+#define CAP11XX_REG_REVISION		0xff
+
+#define CAP11XX_MANUFACTURER_ID	0x5d
+
+#ifdef CONFIG_LEDS_CLASS
+struct cap11xx_led {
+	struct cap11xx_priv *priv;
+	struct led_classdev cdev;
+	struct work_struct work;
+	u32 reg;
+	enum led_brightness new_brightness;
+};
+#endif
+
+struct cap11xx_priv {
+	struct regmap *regmap;
+	struct input_dev *idev;
+
+	struct cap11xx_led *leds;
+	int num_leds;
+
+	/* config */
+	u32 keycodes[];
+};
+
+struct cap11xx_hw_model {
+	u8 product_id;
+	unsigned int num_channels;
+	unsigned int num_leds;
+};
+
+enum {
+	CAP1106,
+	CAP1126,
+	CAP1188,
+};
+
+static const struct cap11xx_hw_model cap11xx_devices[] = {
+	[CAP1106] = { .product_id = 0x55, .num_channels = 6, .num_leds = 0 },
+	[CAP1126] = { .product_id = 0x53, .num_channels = 6, .num_leds = 2 },
+	[CAP1188] = { .product_id = 0x50, .num_channels = 8, .num_leds = 8 },
+};
+
+static const struct reg_default cap11xx_reg_defaults[] = {
+	{ CAP11XX_REG_MAIN_CONTROL,		0x00 },
+	{ CAP11XX_REG_GENERAL_STATUS,		0x00 },
+	{ CAP11XX_REG_SENSOR_INPUT,		0x00 },
+	{ CAP11XX_REG_NOISE_FLAG_STATUS,	0x00 },
+	{ CAP11XX_REG_SENSITIVITY_CONTROL,	0x2f },
+	{ CAP11XX_REG_CONFIG,			0x20 },
+	{ CAP11XX_REG_SENSOR_ENABLE,		0x3f },
+	{ CAP11XX_REG_SENSOR_CONFIG,		0xa4 },
+	{ CAP11XX_REG_SENSOR_CONFIG2,		0x07 },
+	{ CAP11XX_REG_SAMPLING_CONFIG,		0x39 },
+	{ CAP11XX_REG_CALIBRATION,		0x00 },
+	{ CAP11XX_REG_INT_ENABLE,		0x3f },
+	{ CAP11XX_REG_REPEAT_RATE,		0x3f },
+	{ CAP11XX_REG_MT_CONFIG,		0x80 },
+	{ CAP11XX_REG_MT_PATTERN_CONFIG,	0x00 },
+	{ CAP11XX_REG_MT_PATTERN,		0x3f },
+	{ CAP11XX_REG_RECALIB_CONFIG,		0x8a },
+	{ CAP11XX_REG_SENSOR_THRESH(0),		0x40 },
+	{ CAP11XX_REG_SENSOR_THRESH(1),		0x40 },
+	{ CAP11XX_REG_SENSOR_THRESH(2),		0x40 },
+	{ CAP11XX_REG_SENSOR_THRESH(3),		0x40 },
+	{ CAP11XX_REG_SENSOR_THRESH(4),		0x40 },
+	{ CAP11XX_REG_SENSOR_THRESH(5),		0x40 },
+	{ CAP11XX_REG_SENSOR_NOISE_THRESH,	0x01 },
+	{ CAP11XX_REG_STANDBY_CHANNEL,		0x00 },
+	{ CAP11XX_REG_STANDBY_CONFIG,		0x39 },
+	{ CAP11XX_REG_STANDBY_SENSITIVITY,	0x02 },
+	{ CAP11XX_REG_STANDBY_THRESH,		0x40 },
+	{ CAP11XX_REG_CONFIG2,			0x40 },
+	{ CAP11XX_REG_LED_POLARITY,		0x00 },
+	{ CAP11XX_REG_SENSOR_CALIB_LSB1,	0x00 },
+	{ CAP11XX_REG_SENSOR_CALIB_LSB2,	0x00 },
+};
+
+static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CAP11XX_REG_MAIN_CONTROL:
+	case CAP11XX_REG_SENSOR_INPUT:
+	case CAP11XX_REG_SENOR_DELTA(0):
+	case CAP11XX_REG_SENOR_DELTA(1):
+	case CAP11XX_REG_SENOR_DELTA(2):
+	case CAP11XX_REG_SENOR_DELTA(3):
+	case CAP11XX_REG_SENOR_DELTA(4):
+	case CAP11XX_REG_SENOR_DELTA(5):
+	case CAP11XX_REG_PRODUCT_ID:
+	case CAP11XX_REG_MANUFACTURER_ID:
+	case CAP11XX_REG_REVISION:
+		return true;
+	}
+
+	return false;
+}
+
+static const struct regmap_config cap11xx_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = CAP11XX_REG_REVISION,
+	.reg_defaults = cap11xx_reg_defaults,
+
+	.num_reg_defaults = ARRAY_SIZE(cap11xx_reg_defaults),
+	.cache_type = REGCACHE_RBTREE,
+	.volatile_reg = cap11xx_volatile_reg,
+};
+
+static irqreturn_t cap11xx_thread_func(int irq_num, void *data)
+{
+	struct cap11xx_priv *priv = data;
+	unsigned int status;
+	int ret, i;
+
+	/*
+	 * Deassert interrupt. This needs to be done before reading the status
+	 * registers, which will not carry valid values otherwise.
+	 */
+	ret = regmap_update_bits(priv->regmap, CAP11XX_REG_MAIN_CONTROL, 1, 0);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_read(priv->regmap, CAP11XX_REG_SENSOR_INPUT, &status);
+	if (ret < 0)
+		goto out;
+
+	for (i = 0; i < priv->idev->keycodemax; i++)
+		input_report_key(priv->idev, priv->keycodes[i],
+				 status & (1 << i));
+
+	input_sync(priv->idev);
+
+out:
+	return IRQ_HANDLED;
+}
+
+static int cap11xx_set_sleep(struct cap11xx_priv *priv, bool sleep)
+{
+	/*
+	 * DLSEEP mode will turn off all LEDS, prevent this
+	 */
+	if (IS_ENABLED(CONFIG_LEDS_CLASS) && priv->num_leds)
+		return 0;
+
+	return regmap_update_bits(priv->regmap, CAP11XX_REG_MAIN_CONTROL,
+				  CAP11XX_REG_MAIN_CONTROL_DLSEEP,
+				  sleep ? CAP11XX_REG_MAIN_CONTROL_DLSEEP : 0);
+}
+
+static int cap11xx_input_open(struct input_dev *idev)
+{
+	struct cap11xx_priv *priv = input_get_drvdata(idev);
+
+	return cap11xx_set_sleep(priv, false);
+}
+
+static void cap11xx_input_close(struct input_dev *idev)
+{
+	struct cap11xx_priv *priv = input_get_drvdata(idev);
+
+	cap11xx_set_sleep(priv, true);
+}
+
+#ifdef CONFIG_LEDS_CLASS
+static void cap11xx_led_work(struct work_struct *work)
+{
+	struct cap11xx_led *led = container_of(work, struct cap11xx_led, work);
+	struct cap11xx_priv *priv = led->priv;
+	int value = led->new_brightness;
+
+	/*
+	 * All LEDs share the same duty cycle as this is a HW limitation.
+	 * Brightness levels per LED are either 0 (OFF) and 1 (ON).
+	 */
+	regmap_update_bits(priv->regmap, CAP11XX_REG_LED_OUTPUT_CONTROL,
+				BIT(led->reg), value ? BIT(led->reg) : 0);
+}
+
+static void cap11xx_led_set(struct led_classdev *cdev,
+			   enum led_brightness value)
+{
+	struct cap11xx_led *led = container_of(cdev, struct cap11xx_led, cdev);
+
+	if (led->new_brightness == value)
+		return;
+
+	led->new_brightness = value;
+	schedule_work(&led->work);
+}
+
+static int cap11xx_init_leds(struct device *dev,
+			     struct cap11xx_priv *priv, int num_leds)
+{
+	struct device_node *node = dev->of_node, *child;
+	struct cap11xx_led *led;
+	int cnt = of_get_child_count(node);
+	int error;
+
+	if (!num_leds || !cnt)
+		return 0;
+
+	if (cnt > num_leds)
+		return -EINVAL;
+
+	led = devm_kcalloc(dev, cnt, sizeof(struct cap11xx_led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	priv->leds = led;
+
+	error = regmap_update_bits(priv->regmap,
+				CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0);
+	if (error)
+		return error;
+
+	error = regmap_update_bits(priv->regmap, CAP11XX_REG_LED_DUTY_CYCLE_4,
+				CAP11XX_REG_LED_DUTY_MAX_MASK,
+				CAP11XX_REG_LED_DUTY_MAX_VALUE <<
+				CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT);
+	if (error)
+		return error;
+
+	for_each_child_of_node(node, child) {
+		u32 reg;
+
+		led->cdev.name =
+			of_get_property(child, "label", NULL) ? : child->name;
+		led->cdev.default_trigger =
+			of_get_property(child, "linux,default-trigger", NULL);
+		led->cdev.flags = 0;
+		led->cdev.brightness_set = cap11xx_led_set;
+		led->cdev.max_brightness = 1;
+		led->cdev.brightness = LED_OFF;
+
+		error = of_property_read_u32(child, "reg", &reg);
+		if (error != 0 || reg >= num_leds)
+			return -EINVAL;
+
+		led->reg = reg;
+		led->priv = priv;
+
+		INIT_WORK(&led->work, cap11xx_led_work);
+
+		error = devm_led_classdev_register(dev, &led->cdev);
+		if (error)
+			return error;
+
+		priv->num_leds++;
+		led++;
+	}
+
+	return 0;
+}
+#else
+static int cap11xx_init_leds(struct device *dev,
+			     struct cap11xx_priv *priv, int num_leds)
+{
+	return 0;
+}
+#endif
+
+static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
+			     const struct i2c_device_id *id)
+{
+	struct device *dev = &i2c_client->dev;
+	struct cap11xx_priv *priv;
+	struct device_node *node;
+	const struct cap11xx_hw_model *cap;
+	int i, error, irq, gain = 0;
+	unsigned int val, rev;
+	u32 gain32;
+
+	if (id->driver_data >= ARRAY_SIZE(cap11xx_devices)) {
+		dev_err(dev, "Invalid device ID %lu\n", id->driver_data);
+		return -EINVAL;
+	}
+
+	cap = &cap11xx_devices[id->driver_data];
+	if (!cap || !cap->num_channels) {
+		dev_err(dev, "Invalid device configuration\n");
+		return -EINVAL;
+	}
+
+	priv = devm_kzalloc(dev,
+			    sizeof(*priv) +
+				cap->num_channels * sizeof(priv->keycodes[0]),
+			    GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->regmap = devm_regmap_init_i2c(i2c_client, &cap11xx_regmap_config);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	error = regmap_read(priv->regmap, CAP11XX_REG_PRODUCT_ID, &val);
+	if (error)
+		return error;
+
+	if (val != cap->product_id) {
+		dev_err(dev, "Product ID: Got 0x%02x, expected 0x%02x\n",
+			val, cap->product_id);
+		return -ENXIO;
+	}
+
+	error = regmap_read(priv->regmap, CAP11XX_REG_MANUFACTURER_ID, &val);
+	if (error)
+		return error;
+
+	if (val != CAP11XX_MANUFACTURER_ID) {
+		dev_err(dev, "Manufacturer ID: Got 0x%02x, expected 0x%02x\n",
+			val, CAP11XX_MANUFACTURER_ID);
+		return -ENXIO;
+	}
+
+	error = regmap_read(priv->regmap, CAP11XX_REG_REVISION, &rev);
+	if (error < 0)
+		return error;
+
+	dev_info(dev, "CAP11XX detected, revision 0x%02x\n", rev);
+	i2c_set_clientdata(i2c_client, priv);
+	node = dev->of_node;
+
+	if (!of_property_read_u32(node, "microchip,sensor-gain", &gain32)) {
+		if (is_power_of_2(gain32) && gain32 <= 8)
+			gain = ilog2(gain32);
+		else
+			dev_err(dev, "Invalid sensor-gain value %d\n", gain32);
+	}
+
+	if (of_property_read_bool(node, "microchip,irq-active-high")) {
+		error = regmap_update_bits(priv->regmap, CAP11XX_REG_CONFIG2,
+					   CAP11XX_REG_CONFIG2_ALT_POL, 0);
+		if (error)
+			return error;
+	}
+
+	/* Provide some useful defaults */
+	for (i = 0; i < cap->num_channels; i++)
+		priv->keycodes[i] = KEY_A + i;
+
+	of_property_read_u32_array(node, "linux,keycodes",
+				   priv->keycodes, cap->num_channels);
+
+	error = regmap_update_bits(priv->regmap, CAP11XX_REG_MAIN_CONTROL,
+				   CAP11XX_REG_MAIN_CONTROL_GAIN_MASK,
+				   gain << CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT);
+	if (error)
+		return error;
+
+	/* Disable autorepeat. The Linux input system has its own handling. */
+	error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0);
+	if (error)
+		return error;
+
+	priv->idev = devm_input_allocate_device(dev);
+	if (!priv->idev)
+		return -ENOMEM;
+
+	priv->idev->name = "CAP11XX capacitive touch sensor";
+	priv->idev->id.bustype = BUS_I2C;
+	priv->idev->evbit[0] = BIT_MASK(EV_KEY);
+
+	if (of_property_read_bool(node, "autorepeat"))
+		__set_bit(EV_REP, priv->idev->evbit);
+
+	for (i = 0; i < cap->num_channels; i++)
+		__set_bit(priv->keycodes[i], priv->idev->keybit);
+
+	__clear_bit(KEY_RESERVED, priv->idev->keybit);
+
+	priv->idev->keycode = priv->keycodes;
+	priv->idev->keycodesize = sizeof(priv->keycodes[0]);
+	priv->idev->keycodemax = cap->num_channels;
+
+	priv->idev->id.vendor = CAP11XX_MANUFACTURER_ID;
+	priv->idev->id.product = cap->product_id;
+	priv->idev->id.version = rev;
+
+	priv->idev->open = cap11xx_input_open;
+	priv->idev->close = cap11xx_input_close;
+
+	error = cap11xx_init_leds(dev, priv, cap->num_leds);
+	if (error)
+		return error;
+
+	input_set_drvdata(priv->idev, priv);
+
+	/*
+	 * Put the device in deep sleep mode for now.
+	 * ->open() will bring it back once the it is actually needed.
+	 */
+	cap11xx_set_sleep(priv, true);
+
+	error = input_register_device(priv->idev);
+	if (error)
+		return error;
+
+	irq = irq_of_parse_and_map(node, 0);
+	if (!irq) {
+		dev_err(dev, "Unable to parse or map IRQ\n");
+		return -ENXIO;
+	}
+
+	error = devm_request_threaded_irq(dev, irq, NULL, cap11xx_thread_func,
+					  IRQF_ONESHOT, dev_name(dev), priv);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static const struct of_device_id cap11xx_dt_ids[] = {
+	{ .compatible = "microchip,cap1106", },
+	{ .compatible = "microchip,cap1126", },
+	{ .compatible = "microchip,cap1188", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, cap11xx_dt_ids);
+
+static const struct i2c_device_id cap11xx_i2c_ids[] = {
+	{ "cap1106", CAP1106 },
+	{ "cap1126", CAP1126 },
+	{ "cap1188", CAP1188 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, cap11xx_i2c_ids);
+
+static struct i2c_driver cap11xx_i2c_driver = {
+	.driver = {
+		.name	= "cap11xx",
+		.of_match_table = cap11xx_dt_ids,
+	},
+	.id_table	= cap11xx_i2c_ids,
+	.probe		= cap11xx_i2c_probe,
+};
+
+module_i2c_driver(cap11xx_i2c_driver);
+
+MODULE_DESCRIPTION("Microchip CAP11XX driver");
+MODULE_AUTHOR("Daniel Mack <linux@zonque.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c
new file mode 100644
index 0000000..b637f1a
--- /dev/null
+++ b/drivers/input/keyboard/clps711x-keypad.c
@@ -0,0 +1,201 @@
+/*
+ * Cirrus Logic CLPS711X Keypad driver
+ *
+ * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * 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.
+ */
+
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/sched.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/clps711x.h>
+
+#define CLPS711X_KEYPAD_COL_COUNT	8
+
+struct clps711x_gpio_data {
+	struct gpio_desc *desc;
+	DECLARE_BITMAP(last_state, CLPS711X_KEYPAD_COL_COUNT);
+};
+
+struct clps711x_keypad_data {
+	struct regmap			*syscon;
+	int				row_count;
+	unsigned int			row_shift;
+	struct clps711x_gpio_data	*gpio_data;
+};
+
+static void clps711x_keypad_poll(struct input_polled_dev *dev)
+{
+	const unsigned short *keycodes = dev->input->keycode;
+	struct clps711x_keypad_data *priv = dev->private;
+	bool sync = false;
+	int col, row;
+
+	for (col = 0; col < CLPS711X_KEYPAD_COL_COUNT; col++) {
+		/* Assert column */
+		regmap_update_bits(priv->syscon, SYSCON_OFFSET,
+				   SYSCON1_KBDSCAN_MASK,
+				   SYSCON1_KBDSCAN(8 + col));
+
+		/* Scan rows */
+		for (row = 0; row < priv->row_count; row++) {
+			struct clps711x_gpio_data *data = &priv->gpio_data[row];
+			bool state, state1;
+
+			/* Read twice for protection against fluctuations */
+			do {
+				state = gpiod_get_value_cansleep(data->desc);
+				cond_resched();
+				state1 = gpiod_get_value_cansleep(data->desc);
+			} while (state != state1);
+
+			if (test_bit(col, data->last_state) != state) {
+				int code = MATRIX_SCAN_CODE(row, col,
+							    priv->row_shift);
+
+				if (state) {
+					set_bit(col, data->last_state);
+					input_event(dev->input, EV_MSC,
+						    MSC_SCAN, code);
+				} else {
+					clear_bit(col, data->last_state);
+				}
+
+				if (keycodes[code])
+					input_report_key(dev->input,
+							 keycodes[code], state);
+				sync = true;
+			}
+		}
+
+		/* Set all columns to low */
+		regmap_update_bits(priv->syscon, SYSCON_OFFSET,
+				   SYSCON1_KBDSCAN_MASK, SYSCON1_KBDSCAN(1));
+	}
+
+	if (sync)
+		input_sync(dev->input);
+}
+
+static int clps711x_keypad_probe(struct platform_device *pdev)
+{
+	struct clps711x_keypad_data *priv;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct input_polled_dev *poll_dev;
+	u32 poll_interval;
+	int i, err;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->syscon =
+		syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1");
+	if (IS_ERR(priv->syscon))
+		return PTR_ERR(priv->syscon);
+
+	priv->row_count = of_gpio_named_count(np, "row-gpios");
+	if (priv->row_count < 1)
+		return -EINVAL;
+
+	priv->gpio_data = devm_kzalloc(dev,
+				sizeof(*priv->gpio_data) * priv->row_count,
+				GFP_KERNEL);
+	if (!priv->gpio_data)
+		return -ENOMEM;
+
+	priv->row_shift = get_count_order(CLPS711X_KEYPAD_COL_COUNT);
+
+	for (i = 0; i < priv->row_count; i++) {
+		struct clps711x_gpio_data *data = &priv->gpio_data[i];
+
+		data->desc = devm_gpiod_get_index(dev, "row", i, GPIOD_IN);
+		if (IS_ERR(data->desc))
+			return PTR_ERR(data->desc);
+	}
+
+	err = of_property_read_u32(np, "poll-interval", &poll_interval);
+	if (err)
+		return err;
+
+	poll_dev = input_allocate_polled_device();
+	if (!poll_dev)
+		return -ENOMEM;
+
+	poll_dev->private		= priv;
+	poll_dev->poll			= clps711x_keypad_poll;
+	poll_dev->poll_interval		= poll_interval;
+	poll_dev->input->name		= pdev->name;
+	poll_dev->input->dev.parent	= dev;
+	poll_dev->input->id.bustype	= BUS_HOST;
+	poll_dev->input->id.vendor	= 0x0001;
+	poll_dev->input->id.product	= 0x0001;
+	poll_dev->input->id.version	= 0x0100;
+
+	err = matrix_keypad_build_keymap(NULL, NULL, priv->row_count,
+					 CLPS711X_KEYPAD_COL_COUNT,
+					 NULL, poll_dev->input);
+	if (err)
+		goto out_err;
+
+	input_set_capability(poll_dev->input, EV_MSC, MSC_SCAN);
+	if (of_property_read_bool(np, "autorepeat"))
+		__set_bit(EV_REP, poll_dev->input->evbit);
+
+	platform_set_drvdata(pdev, poll_dev);
+
+	/* Set all columns to low */
+	regmap_update_bits(priv->syscon, SYSCON_OFFSET, SYSCON1_KBDSCAN_MASK,
+			   SYSCON1_KBDSCAN(1));
+
+	err = input_register_polled_device(poll_dev);
+	if (err)
+		goto out_err;
+
+	return 0;
+
+out_err:
+	input_free_polled_device(poll_dev);
+	return err;
+}
+
+static int clps711x_keypad_remove(struct platform_device *pdev)
+{
+	struct input_polled_dev *poll_dev = platform_get_drvdata(pdev);
+
+	input_unregister_polled_device(poll_dev);
+	input_free_polled_device(poll_dev);
+
+	return 0;
+}
+
+static const struct of_device_id clps711x_keypad_of_match[] = {
+	{ .compatible = "cirrus,clps711x-keypad", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match);
+
+static struct platform_driver clps711x_keypad_driver = {
+	.driver	= {
+		.name		= "clps711x-keypad",
+		.of_match_table	= clps711x_keypad_of_match,
+	},
+	.probe	= clps711x_keypad_probe,
+	.remove	= clps711x_keypad_remove,
+};
+module_platform_driver(clps711x_keypad_driver);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CLPS711X Keypad driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
new file mode 100644
index 0000000..b01966d
--- /dev/null
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -0,0 +1,384 @@
+/*
+ * ChromeOS EC keyboard driver
+ *
+ * Copyright (C) 2012 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This driver uses the Chrome OS EC byte-level message-based protocol for
+ * communicating the keyboard state (which keys are pressed) from a keyboard EC
+ * to the AP over some bus (such as i2c, lpc, spi).  The EC does debouncing,
+ * but everything else (including deghosting) is done here.  The main
+ * motivation for this is to keep the EC firmware as simple as possible, since
+ * it cannot be easily upgraded and EC flash/IRAM space is relatively
+ * expensive.
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+
+/*
+ * @rows: Number of rows in the keypad
+ * @cols: Number of columns in the keypad
+ * @row_shift: log2 or number of rows, rounded up
+ * @keymap_data: Matrix keymap data used to convert to keyscan values
+ * @ghost_filter: true to enable the matrix key-ghosting filter
+ * @valid_keys: bitmap of existing keys for each matrix column
+ * @old_kb_state: bitmap of keys pressed last scan
+ * @dev: Device pointer
+ * @idev: Input device
+ * @ec: Top level ChromeOS device to use to talk to EC
+ */
+struct cros_ec_keyb {
+	unsigned int rows;
+	unsigned int cols;
+	int row_shift;
+	const struct matrix_keymap_data *keymap_data;
+	bool ghost_filter;
+	uint8_t *valid_keys;
+	uint8_t *old_kb_state;
+
+	struct device *dev;
+	struct input_dev *idev;
+	struct cros_ec_device *ec;
+};
+
+
+/*
+ * Returns true when there is at least one combination of pressed keys that
+ * results in ghosting.
+ */
+static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf)
+{
+	int col1, col2, buf1, buf2;
+	struct device *dev = ckdev->dev;
+	uint8_t *valid_keys = ckdev->valid_keys;
+
+	/*
+	 * Ghosting happens if for any pressed key X there are other keys
+	 * pressed both in the same row and column of X as, for instance,
+	 * in the following diagram:
+	 *
+	 * . . Y . g .
+	 * . . . . . .
+	 * . . . . . .
+	 * . . X . Z .
+	 *
+	 * In this case only X, Y, and Z are pressed, but g appears to be
+	 * pressed too (see Wikipedia).
+	 */
+	for (col1 = 0; col1 < ckdev->cols; col1++) {
+		buf1 = buf[col1] & valid_keys[col1];
+		for (col2 = col1 + 1; col2 < ckdev->cols; col2++) {
+			buf2 = buf[col2] & valid_keys[col2];
+			if (hweight8(buf1 & buf2) > 1) {
+				dev_dbg(dev, "ghost found at: B[%02d]:0x%02x & B[%02d]:0x%02x",
+					col1, buf1, col2, buf2);
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+
+/*
+ * Compares the new keyboard state to the old one and produces key
+ * press/release events accordingly.  The keyboard state is 13 bytes (one byte
+ * per column)
+ */
+static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev,
+			 uint8_t *kb_state, int len)
+{
+	struct input_dev *idev = ckdev->idev;
+	int col, row;
+	int new_state;
+	int old_state;
+	int num_cols;
+
+	num_cols = len;
+
+	if (ckdev->ghost_filter && cros_ec_keyb_has_ghosting(ckdev, kb_state)) {
+		/*
+		 * Simple-minded solution: ignore this state. The obvious
+		 * improvement is to only ignore changes to keys involved in
+		 * the ghosting, but process the other changes.
+		 */
+		dev_dbg(ckdev->dev, "ghosting found\n");
+		return;
+	}
+
+	for (col = 0; col < ckdev->cols; col++) {
+		for (row = 0; row < ckdev->rows; row++) {
+			int pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift);
+			const unsigned short *keycodes = idev->keycode;
+
+			new_state = kb_state[col] & (1 << row);
+			old_state = ckdev->old_kb_state[col] & (1 << row);
+			if (new_state != old_state) {
+				dev_dbg(ckdev->dev,
+					"changed: [r%d c%d]: byte %02x\n",
+					row, col, new_state);
+
+				input_report_key(idev, keycodes[pos],
+						 new_state);
+			}
+		}
+		ckdev->old_kb_state[col] = kb_state[col];
+	}
+	input_sync(ckdev->idev);
+}
+
+static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state)
+{
+	int ret = 0;
+	struct cros_ec_command *msg;
+
+	msg = kmalloc(sizeof(*msg) + ckdev->cols, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	msg->version = 0;
+	msg->command = EC_CMD_MKBP_STATE;
+	msg->insize = ckdev->cols;
+	msg->outsize = 0;
+
+	ret = cros_ec_cmd_xfer(ckdev->ec, msg);
+	if (ret < 0) {
+		dev_err(ckdev->dev, "Error transferring EC message %d\n", ret);
+		goto exit;
+	}
+
+	memcpy(kb_state, msg->data, ckdev->cols);
+exit:
+	kfree(msg);
+	return ret;
+}
+
+static irqreturn_t cros_ec_keyb_irq(int irq, void *data)
+{
+	struct cros_ec_keyb *ckdev = data;
+	struct cros_ec_device *ec = ckdev->ec;
+	int ret;
+	uint8_t kb_state[ckdev->cols];
+
+	if (device_may_wakeup(ec->dev))
+		pm_wakeup_event(ec->dev, 0);
+
+	ret = cros_ec_keyb_get_state(ckdev, kb_state);
+	if (ret >= 0)
+		cros_ec_keyb_process(ckdev, kb_state, ret);
+	else
+		dev_err(ec->dev, "failed to get keyboard state: %d\n", ret);
+
+	return IRQ_HANDLED;
+}
+
+static int cros_ec_keyb_open(struct input_dev *dev)
+{
+	struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
+	struct cros_ec_device *ec = ckdev->ec;
+
+	return request_threaded_irq(ec->irq, NULL, cros_ec_keyb_irq,
+					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					"cros_ec_keyb", ckdev);
+}
+
+static void cros_ec_keyb_close(struct input_dev *dev)
+{
+	struct cros_ec_keyb *ckdev = input_get_drvdata(dev);
+	struct cros_ec_device *ec = ckdev->ec;
+
+	free_irq(ec->irq, ckdev);
+}
+
+/*
+ * Walks keycodes flipping bit in buffer COLUMNS deep where bit is ROW.  Used by
+ * ghosting logic to ignore NULL or virtual keys.
+ */
+static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev)
+{
+	int row, col;
+	int row_shift = ckdev->row_shift;
+	unsigned short *keymap = ckdev->idev->keycode;
+	unsigned short code;
+
+	BUG_ON(ckdev->idev->keycodesize != sizeof(*keymap));
+
+	for (col = 0; col < ckdev->cols; col++) {
+		for (row = 0; row < ckdev->rows; row++) {
+			code = keymap[MATRIX_SCAN_CODE(row, col, row_shift)];
+			if (code && (code != KEY_BATTERY))
+				ckdev->valid_keys[col] |= 1 << row;
+		}
+		dev_dbg(ckdev->dev, "valid_keys[%02d] = 0x%02x\n",
+			col, ckdev->valid_keys[col]);
+	}
+}
+
+static int cros_ec_keyb_probe(struct platform_device *pdev)
+{
+	struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = ec->dev;
+	struct cros_ec_keyb *ckdev;
+	struct input_dev *idev;
+	struct device_node *np;
+	int err;
+
+	np = pdev->dev.of_node;
+	if (!np)
+		return -ENODEV;
+
+	ckdev = devm_kzalloc(&pdev->dev, sizeof(*ckdev), GFP_KERNEL);
+	if (!ckdev)
+		return -ENOMEM;
+	err = matrix_keypad_parse_of_params(&pdev->dev, &ckdev->rows,
+					    &ckdev->cols);
+	if (err)
+		return err;
+
+	ckdev->valid_keys = devm_kzalloc(&pdev->dev, ckdev->cols, GFP_KERNEL);
+	if (!ckdev->valid_keys)
+		return -ENOMEM;
+
+	ckdev->old_kb_state = devm_kzalloc(&pdev->dev, ckdev->cols, GFP_KERNEL);
+	if (!ckdev->old_kb_state)
+		return -ENOMEM;
+
+	idev = devm_input_allocate_device(&pdev->dev);
+	if (!idev)
+		return -ENOMEM;
+
+	if (!ec->irq) {
+		dev_err(dev, "no EC IRQ specified\n");
+		return -EINVAL;
+	}
+
+	ckdev->ec = ec;
+	ckdev->dev = dev;
+	dev_set_drvdata(&pdev->dev, ckdev);
+
+	idev->name = CROS_EC_DEV_NAME;
+	idev->phys = ec->phys_name;
+	__set_bit(EV_REP, idev->evbit);
+
+	idev->id.bustype = BUS_VIRTUAL;
+	idev->id.version = 1;
+	idev->id.product = 0;
+	idev->dev.parent = &pdev->dev;
+	idev->open = cros_ec_keyb_open;
+	idev->close = cros_ec_keyb_close;
+
+	ckdev->ghost_filter = of_property_read_bool(np,
+					"google,needs-ghost-filter");
+
+	err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols,
+					 NULL, idev);
+	if (err) {
+		dev_err(dev, "cannot build key matrix\n");
+		return err;
+	}
+
+	ckdev->row_shift = get_count_order(ckdev->cols);
+
+	input_set_capability(idev, EV_MSC, MSC_SCAN);
+	input_set_drvdata(idev, ckdev);
+	ckdev->idev = idev;
+	cros_ec_keyb_compute_valid_keys(ckdev);
+
+	err = input_register_device(ckdev->idev);
+	if (err) {
+		dev_err(dev, "cannot register input device\n");
+		return err;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+/* Clear any keys in the buffer */
+static void cros_ec_keyb_clear_keyboard(struct cros_ec_keyb *ckdev)
+{
+	uint8_t old_state[ckdev->cols];
+	uint8_t new_state[ckdev->cols];
+	unsigned long duration;
+	int i, ret;
+
+	/*
+	 * Keep reading until we see that the scan state does not change.
+	 * That indicates that we are done.
+	 *
+	 * Assume that the EC keyscan buffer is at most 32 deep.
+	 */
+	duration = jiffies;
+	ret = cros_ec_keyb_get_state(ckdev, new_state);
+	for (i = 1; !ret && i < 32; i++) {
+		memcpy(old_state, new_state, sizeof(old_state));
+		ret = cros_ec_keyb_get_state(ckdev, new_state);
+		if (0 == memcmp(old_state, new_state, sizeof(old_state)))
+			break;
+	}
+	duration = jiffies - duration;
+	dev_info(ckdev->dev, "Discarded %d keyscan(s) in %dus\n", i,
+		jiffies_to_usecs(duration));
+}
+
+static int cros_ec_keyb_resume(struct device *dev)
+{
+	struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
+
+	/*
+	 * When the EC is not a wake source, then it could not have caused the
+	 * resume, so we clear the EC's key scan buffer. If the EC was a
+	 * wake source (e.g. the lid is open and the user might press a key to
+	 * wake) then the key scan buffer should be preserved.
+	 */
+	if (!ckdev->ec->was_wake_device)
+		cros_ec_keyb_clear_keyboard(ckdev);
+
+	return 0;
+}
+
+#endif
+
+static SIMPLE_DEV_PM_OPS(cros_ec_keyb_pm_ops, NULL, cros_ec_keyb_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id cros_ec_keyb_of_match[] = {
+	{ .compatible = "google,cros-ec-keyb" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, cros_ec_keyb_of_match);
+#endif
+
+static struct platform_driver cros_ec_keyb_driver = {
+	.probe = cros_ec_keyb_probe,
+	.driver = {
+		.name = "cros-ec-keyb",
+		.of_match_table = of_match_ptr(cros_ec_keyb_of_match),
+		.pm	= &cros_ec_keyb_pm_ops,
+	},
+};
+
+module_platform_driver(cros_ec_keyb_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ChromeOS EC keyboard driver");
+MODULE_ALIAS("platform:cros-ec-keyb");
diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c
new file mode 100644
index 0000000..f363d1d
--- /dev/null
+++ b/drivers/input/keyboard/davinci_keyscan.c
@@ -0,0 +1,333 @@
+/*
+ * DaVinci Key Scan Driver for TI platforms
+ *
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
+ *
+ * Initial Code: Sandeep Paulraj <s-paulraj@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <asm/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <linux/platform_data/keyscan-davinci.h>
+
+/* Key scan registers */
+#define DAVINCI_KEYSCAN_KEYCTRL		0x0000
+#define DAVINCI_KEYSCAN_INTENA		0x0004
+#define DAVINCI_KEYSCAN_INTFLAG		0x0008
+#define DAVINCI_KEYSCAN_INTCLR		0x000c
+#define DAVINCI_KEYSCAN_STRBWIDTH	0x0010
+#define DAVINCI_KEYSCAN_INTERVAL	0x0014
+#define DAVINCI_KEYSCAN_CONTTIME	0x0018
+#define DAVINCI_KEYSCAN_CURRENTST	0x001c
+#define DAVINCI_KEYSCAN_PREVSTATE	0x0020
+#define DAVINCI_KEYSCAN_EMUCTRL		0x0024
+#define DAVINCI_KEYSCAN_IODFTCTRL	0x002c
+
+/* Key Control Register (KEYCTRL) */
+#define DAVINCI_KEYSCAN_KEYEN		0x00000001
+#define DAVINCI_KEYSCAN_PREVMODE	0x00000002
+#define DAVINCI_KEYSCAN_CHATOFF		0x00000004
+#define DAVINCI_KEYSCAN_AUTODET		0x00000008
+#define DAVINCI_KEYSCAN_SCANMODE	0x00000010
+#define DAVINCI_KEYSCAN_OUTTYPE		0x00000020
+
+/* Masks for the interrupts */
+#define DAVINCI_KEYSCAN_INT_CONT	0x00000008
+#define DAVINCI_KEYSCAN_INT_OFF		0x00000004
+#define DAVINCI_KEYSCAN_INT_ON		0x00000002
+#define DAVINCI_KEYSCAN_INT_CHANGE	0x00000001
+#define DAVINCI_KEYSCAN_INT_ALL		0x0000000f
+
+struct davinci_ks {
+	struct input_dev		*input;
+	struct davinci_ks_platform_data	*pdata;
+	int				irq;
+	void __iomem			*base;
+	resource_size_t			pbase;
+	size_t				base_size;
+	unsigned short			keymap[];
+};
+
+/* Initializing the kp Module */
+static int __init davinci_ks_initialize(struct davinci_ks *davinci_ks)
+{
+	struct device *dev = &davinci_ks->input->dev;
+	struct davinci_ks_platform_data *pdata = davinci_ks->pdata;
+	u32 matrix_ctrl;
+
+	/* Enable all interrupts */
+	__raw_writel(DAVINCI_KEYSCAN_INT_ALL,
+		     davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
+
+	/* Clear interrupts if any */
+	__raw_writel(DAVINCI_KEYSCAN_INT_ALL,
+		     davinci_ks->base + DAVINCI_KEYSCAN_INTCLR);
+
+	/* Setup the scan period = strobe + interval */
+	__raw_writel(pdata->strobe,
+		     davinci_ks->base + DAVINCI_KEYSCAN_STRBWIDTH);
+	__raw_writel(pdata->interval,
+		     davinci_ks->base + DAVINCI_KEYSCAN_INTERVAL);
+	__raw_writel(0x01,
+		     davinci_ks->base + DAVINCI_KEYSCAN_CONTTIME);
+
+	/* Define matrix type */
+	switch (pdata->matrix_type) {
+	case DAVINCI_KEYSCAN_MATRIX_4X4:
+		matrix_ctrl = 0;
+		break;
+	case DAVINCI_KEYSCAN_MATRIX_5X3:
+		matrix_ctrl = (1 << 6);
+		break;
+	default:
+		dev_err(dev->parent, "wrong matrix type\n");
+		return -EINVAL;
+	}
+
+	/* Enable key scan module and set matrix type */
+	__raw_writel(DAVINCI_KEYSCAN_AUTODET | DAVINCI_KEYSCAN_KEYEN |
+		     matrix_ctrl, davinci_ks->base + DAVINCI_KEYSCAN_KEYCTRL);
+
+	return 0;
+}
+
+static irqreturn_t davinci_ks_interrupt(int irq, void *dev_id)
+{
+	struct davinci_ks *davinci_ks = dev_id;
+	struct device *dev = &davinci_ks->input->dev;
+	unsigned short *keymap = davinci_ks->keymap;
+	int keymapsize = davinci_ks->pdata->keymapsize;
+	u32 prev_status, new_status, changed;
+	bool release;
+	int keycode = KEY_UNKNOWN;
+	int i;
+
+	/* Disable interrupt */
+	__raw_writel(0x0, davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
+
+	/* Reading previous and new status of the key scan */
+	prev_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_PREVSTATE);
+	new_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_CURRENTST);
+
+	changed = prev_status ^ new_status;
+
+	if (changed) {
+		/*
+		 * It goes through all bits in 'changed' to ensure
+		 * that no key changes are being missed
+		 */
+		for (i = 0 ; i < keymapsize; i++) {
+			if ((changed>>i) & 0x1) {
+				keycode = keymap[i];
+				release = (new_status >> i) & 0x1;
+				dev_dbg(dev->parent, "key %d %s\n", keycode,
+					release ? "released" : "pressed");
+				input_report_key(davinci_ks->input, keycode,
+						 !release);
+				input_sync(davinci_ks->input);
+			}
+		}
+		/* Clearing interrupt */
+		__raw_writel(DAVINCI_KEYSCAN_INT_ALL,
+			     davinci_ks->base + DAVINCI_KEYSCAN_INTCLR);
+	}
+
+	/* Enable interrupts */
+	__raw_writel(0x1, davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
+
+	return IRQ_HANDLED;
+}
+
+static int __init davinci_ks_probe(struct platform_device *pdev)
+{
+	struct davinci_ks *davinci_ks;
+	struct input_dev *key_dev;
+	struct resource *res, *mem;
+	struct device *dev = &pdev->dev;
+	struct davinci_ks_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	int error, i;
+
+	if (pdata->device_enable) {
+		error = pdata->device_enable(dev);
+		if (error < 0) {
+			dev_dbg(dev, "device enable function failed\n");
+			return error;
+		}
+	}
+
+	if (!pdata->keymap) {
+		dev_dbg(dev, "no keymap from pdata\n");
+		return -EINVAL;
+	}
+
+	davinci_ks = kzalloc(sizeof(struct davinci_ks) +
+		sizeof(unsigned short) * pdata->keymapsize, GFP_KERNEL);
+	if (!davinci_ks) {
+		dev_dbg(dev, "could not allocate memory for private data\n");
+		return -ENOMEM;
+	}
+
+	memcpy(davinci_ks->keymap, pdata->keymap,
+		sizeof(unsigned short) * pdata->keymapsize);
+
+	key_dev = input_allocate_device();
+	if (!key_dev) {
+		dev_dbg(dev, "could not allocate input device\n");
+		error = -ENOMEM;
+		goto fail1;
+	}
+
+	davinci_ks->input = key_dev;
+
+	davinci_ks->irq = platform_get_irq(pdev, 0);
+	if (davinci_ks->irq < 0) {
+		dev_err(dev, "no key scan irq\n");
+		error = davinci_ks->irq;
+		goto fail2;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "no mem resource\n");
+		error = -EINVAL;
+		goto fail2;
+	}
+
+	davinci_ks->pbase = res->start;
+	davinci_ks->base_size = resource_size(res);
+
+	mem = request_mem_region(davinci_ks->pbase, davinci_ks->base_size,
+				 pdev->name);
+	if (!mem) {
+		dev_err(dev, "key scan registers at %08x are not free\n",
+			davinci_ks->pbase);
+		error = -EBUSY;
+		goto fail2;
+	}
+
+	davinci_ks->base = ioremap(davinci_ks->pbase, davinci_ks->base_size);
+	if (!davinci_ks->base) {
+		dev_err(dev, "can't ioremap MEM resource.\n");
+		error = -ENOMEM;
+		goto fail3;
+	}
+
+	/* Enable auto repeat feature of Linux input subsystem */
+	if (pdata->rep)
+		__set_bit(EV_REP, key_dev->evbit);
+
+	/* Setup input device */
+	__set_bit(EV_KEY, key_dev->evbit);
+
+	/* Setup the platform data */
+	davinci_ks->pdata = pdata;
+
+	for (i = 0; i < davinci_ks->pdata->keymapsize; i++)
+		__set_bit(davinci_ks->pdata->keymap[i], key_dev->keybit);
+
+	key_dev->name = "davinci_keyscan";
+	key_dev->phys = "davinci_keyscan/input0";
+	key_dev->dev.parent = &pdev->dev;
+	key_dev->id.bustype = BUS_HOST;
+	key_dev->id.vendor = 0x0001;
+	key_dev->id.product = 0x0001;
+	key_dev->id.version = 0x0001;
+	key_dev->keycode = davinci_ks->keymap;
+	key_dev->keycodesize = sizeof(davinci_ks->keymap[0]);
+	key_dev->keycodemax = davinci_ks->pdata->keymapsize;
+
+	error = input_register_device(davinci_ks->input);
+	if (error < 0) {
+		dev_err(dev, "unable to register davinci key scan device\n");
+		goto fail4;
+	}
+
+	error = request_irq(davinci_ks->irq, davinci_ks_interrupt,
+			  0, pdev->name, davinci_ks);
+	if (error < 0) {
+		dev_err(dev, "unable to register davinci key scan interrupt\n");
+		goto fail5;
+	}
+
+	error = davinci_ks_initialize(davinci_ks);
+	if (error < 0) {
+		dev_err(dev, "unable to initialize davinci key scan device\n");
+		goto fail6;
+	}
+
+	platform_set_drvdata(pdev, davinci_ks);
+	return 0;
+
+fail6:
+	free_irq(davinci_ks->irq, davinci_ks);
+fail5:
+	input_unregister_device(davinci_ks->input);
+	key_dev = NULL;
+fail4:
+	iounmap(davinci_ks->base);
+fail3:
+	release_mem_region(davinci_ks->pbase, davinci_ks->base_size);
+fail2:
+	input_free_device(key_dev);
+fail1:
+	kfree(davinci_ks);
+
+	return error;
+}
+
+static int davinci_ks_remove(struct platform_device *pdev)
+{
+	struct davinci_ks *davinci_ks = platform_get_drvdata(pdev);
+
+	free_irq(davinci_ks->irq, davinci_ks);
+
+	input_unregister_device(davinci_ks->input);
+
+	iounmap(davinci_ks->base);
+	release_mem_region(davinci_ks->pbase, davinci_ks->base_size);
+
+	kfree(davinci_ks);
+
+	return 0;
+}
+
+static struct platform_driver davinci_ks_driver = {
+	.driver	= {
+		.name = "davinci_keyscan",
+	},
+	.remove	= davinci_ks_remove,
+};
+
+module_platform_driver_probe(davinci_ks_driver, davinci_ks_probe);
+
+MODULE_AUTHOR("Miguel Aguilar");
+MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c
new file mode 100644
index 0000000..f77b295
--- /dev/null
+++ b/drivers/input/keyboard/ep93xx_keypad.c
@@ -0,0 +1,386 @@
+/*
+ * Driver for the Cirrus EP93xx matrix keypad controller.
+ *
+ * Copyright (c) 2008 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Based on the pxa27x matrix keypad controller by Rodolfo Giometti.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * NOTE:
+ *
+ * The 3-key reset is triggered by pressing the 3 keys in
+ * Row 0, Columns 2, 4, and 7 at the same time.  This action can
+ * be disabled by setting the EP93XX_KEYPAD_DISABLE_3_KEY flag.
+ *
+ * Normal operation for the matrix does not autorepeat the key press.
+ * This action can be enabled by setting the EP93XX_KEYPAD_AUTOREPEAT
+ * flag.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+#include <linux/platform_data/keypad-ep93xx.h>
+
+/*
+ * Keypad Interface Register offsets
+ */
+#define KEY_INIT		0x00	/* Key Scan Initialization register */
+#define KEY_DIAG		0x04	/* Key Scan Diagnostic register */
+#define KEY_REG			0x08	/* Key Value Capture register */
+
+/* Key Scan Initialization Register bit defines */
+#define KEY_INIT_DBNC_MASK	(0x00ff0000)
+#define KEY_INIT_DBNC_SHIFT	(16)
+#define KEY_INIT_DIS3KY		(1<<15)
+#define KEY_INIT_DIAG		(1<<14)
+#define KEY_INIT_BACK		(1<<13)
+#define KEY_INIT_T2		(1<<12)
+#define KEY_INIT_PRSCL_MASK	(0x000003ff)
+#define KEY_INIT_PRSCL_SHIFT	(0)
+
+/* Key Scan Diagnostic Register bit defines */
+#define KEY_DIAG_MASK		(0x0000003f)
+#define KEY_DIAG_SHIFT		(0)
+
+/* Key Value Capture Register bit defines */
+#define KEY_REG_K		(1<<15)
+#define KEY_REG_INT		(1<<14)
+#define KEY_REG_2KEYS		(1<<13)
+#define KEY_REG_1KEY		(1<<12)
+#define KEY_REG_KEY2_MASK	(0x00000fc0)
+#define KEY_REG_KEY2_SHIFT	(6)
+#define KEY_REG_KEY1_MASK	(0x0000003f)
+#define KEY_REG_KEY1_SHIFT	(0)
+
+#define EP93XX_MATRIX_SIZE	(EP93XX_MATRIX_ROWS * EP93XX_MATRIX_COLS)
+
+struct ep93xx_keypad {
+	struct ep93xx_keypad_platform_data *pdata;
+	struct input_dev *input_dev;
+	struct clk *clk;
+
+	void __iomem *mmio_base;
+
+	unsigned short keycodes[EP93XX_MATRIX_SIZE];
+
+	int key1;
+	int key2;
+
+	int irq;
+
+	bool enabled;
+};
+
+static irqreturn_t ep93xx_keypad_irq_handler(int irq, void *dev_id)
+{
+	struct ep93xx_keypad *keypad = dev_id;
+	struct input_dev *input_dev = keypad->input_dev;
+	unsigned int status;
+	int keycode, key1, key2;
+
+	status = __raw_readl(keypad->mmio_base + KEY_REG);
+
+	keycode = (status & KEY_REG_KEY1_MASK) >> KEY_REG_KEY1_SHIFT;
+	key1 = keypad->keycodes[keycode];
+
+	keycode = (status & KEY_REG_KEY2_MASK) >> KEY_REG_KEY2_SHIFT;
+	key2 = keypad->keycodes[keycode];
+
+	if (status & KEY_REG_2KEYS) {
+		if (keypad->key1 && key1 != keypad->key1 && key2 != keypad->key1)
+			input_report_key(input_dev, keypad->key1, 0);
+
+		if (keypad->key2 && key1 != keypad->key2 && key2 != keypad->key2)
+			input_report_key(input_dev, keypad->key2, 0);
+
+		input_report_key(input_dev, key1, 1);
+		input_report_key(input_dev, key2, 1);
+
+		keypad->key1 = key1;
+		keypad->key2 = key2;
+
+	} else if (status & KEY_REG_1KEY) {
+		if (keypad->key1 && key1 != keypad->key1)
+			input_report_key(input_dev, keypad->key1, 0);
+
+		if (keypad->key2 && key1 != keypad->key2)
+			input_report_key(input_dev, keypad->key2, 0);
+
+		input_report_key(input_dev, key1, 1);
+
+		keypad->key1 = key1;
+		keypad->key2 = 0;
+
+	} else {
+		input_report_key(input_dev, keypad->key1, 0);
+		input_report_key(input_dev, keypad->key2, 0);
+
+		keypad->key1 = keypad->key2 = 0;
+	}
+	input_sync(input_dev);
+
+	return IRQ_HANDLED;
+}
+
+static void ep93xx_keypad_config(struct ep93xx_keypad *keypad)
+{
+	struct ep93xx_keypad_platform_data *pdata = keypad->pdata;
+	unsigned int val = 0;
+
+	if (pdata->flags & EP93XX_KEYPAD_KDIV)
+		clk_set_rate(keypad->clk, EP93XX_KEYTCHCLK_DIV4);
+	else
+		clk_set_rate(keypad->clk, EP93XX_KEYTCHCLK_DIV16);
+
+	if (pdata->flags & EP93XX_KEYPAD_DISABLE_3_KEY)
+		val |= KEY_INIT_DIS3KY;
+	if (pdata->flags & EP93XX_KEYPAD_DIAG_MODE)
+		val |= KEY_INIT_DIAG;
+	if (pdata->flags & EP93XX_KEYPAD_BACK_DRIVE)
+		val |= KEY_INIT_BACK;
+	if (pdata->flags & EP93XX_KEYPAD_TEST_MODE)
+		val |= KEY_INIT_T2;
+
+	val |= ((pdata->debounce << KEY_INIT_DBNC_SHIFT) & KEY_INIT_DBNC_MASK);
+
+	val |= ((pdata->prescale << KEY_INIT_PRSCL_SHIFT) & KEY_INIT_PRSCL_MASK);
+
+	__raw_writel(val, keypad->mmio_base + KEY_INIT);
+}
+
+static int ep93xx_keypad_open(struct input_dev *pdev)
+{
+	struct ep93xx_keypad *keypad = input_get_drvdata(pdev);
+
+	if (!keypad->enabled) {
+		ep93xx_keypad_config(keypad);
+		clk_enable(keypad->clk);
+		keypad->enabled = true;
+	}
+
+	return 0;
+}
+
+static void ep93xx_keypad_close(struct input_dev *pdev)
+{
+	struct ep93xx_keypad *keypad = input_get_drvdata(pdev);
+
+	if (keypad->enabled) {
+		clk_disable(keypad->clk);
+		keypad->enabled = false;
+	}
+}
+
+
+#ifdef CONFIG_PM_SLEEP
+static int ep93xx_keypad_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
+	struct input_dev *input_dev = keypad->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (keypad->enabled) {
+		clk_disable(keypad->clk);
+		keypad->enabled = false;
+	}
+
+	mutex_unlock(&input_dev->mutex);
+
+	if (device_may_wakeup(&pdev->dev))
+		enable_irq_wake(keypad->irq);
+
+	return 0;
+}
+
+static int ep93xx_keypad_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
+	struct input_dev *input_dev = keypad->input_dev;
+
+	if (device_may_wakeup(&pdev->dev))
+		disable_irq_wake(keypad->irq);
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users) {
+		if (!keypad->enabled) {
+			ep93xx_keypad_config(keypad);
+			clk_enable(keypad->clk);
+			keypad->enabled = true;
+		}
+	}
+
+	mutex_unlock(&input_dev->mutex);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ep93xx_keypad_pm_ops,
+			 ep93xx_keypad_suspend, ep93xx_keypad_resume);
+
+static int ep93xx_keypad_probe(struct platform_device *pdev)
+{
+	struct ep93xx_keypad *keypad;
+	const struct matrix_keymap_data *keymap_data;
+	struct input_dev *input_dev;
+	struct resource *res;
+	int err;
+
+	keypad = kzalloc(sizeof(struct ep93xx_keypad), GFP_KERNEL);
+	if (!keypad)
+		return -ENOMEM;
+
+	keypad->pdata = dev_get_platdata(&pdev->dev);
+	if (!keypad->pdata) {
+		err = -EINVAL;
+		goto failed_free;
+	}
+
+	keymap_data = keypad->pdata->keymap_data;
+	if (!keymap_data) {
+		err = -EINVAL;
+		goto failed_free;
+	}
+
+	keypad->irq = platform_get_irq(pdev, 0);
+	if (!keypad->irq) {
+		err = -ENXIO;
+		goto failed_free;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		err = -ENXIO;
+		goto failed_free;
+	}
+
+	res = request_mem_region(res->start, resource_size(res), pdev->name);
+	if (!res) {
+		err = -EBUSY;
+		goto failed_free;
+	}
+
+	keypad->mmio_base = ioremap(res->start, resource_size(res));
+	if (keypad->mmio_base == NULL) {
+		err = -ENXIO;
+		goto failed_free_mem;
+	}
+
+	err = ep93xx_keypad_acquire_gpio(pdev);
+	if (err)
+		goto failed_free_io;
+
+	keypad->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(keypad->clk)) {
+		err = PTR_ERR(keypad->clk);
+		goto failed_free_gpio;
+	}
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		err = -ENOMEM;
+		goto failed_put_clk;
+	}
+
+	keypad->input_dev = input_dev;
+
+	input_dev->name = pdev->name;
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->open = ep93xx_keypad_open;
+	input_dev->close = ep93xx_keypad_close;
+	input_dev->dev.parent = &pdev->dev;
+
+	err = matrix_keypad_build_keymap(keymap_data, NULL,
+					 EP93XX_MATRIX_ROWS, EP93XX_MATRIX_COLS,
+					 keypad->keycodes, input_dev);
+	if (err)
+		goto failed_free_dev;
+
+	if (keypad->pdata->flags & EP93XX_KEYPAD_AUTOREPEAT)
+		__set_bit(EV_REP, input_dev->evbit);
+	input_set_drvdata(input_dev, keypad);
+
+	err = request_irq(keypad->irq, ep93xx_keypad_irq_handler,
+			  0, pdev->name, keypad);
+	if (err)
+		goto failed_free_dev;
+
+	err = input_register_device(input_dev);
+	if (err)
+		goto failed_free_irq;
+
+	platform_set_drvdata(pdev, keypad);
+	device_init_wakeup(&pdev->dev, 1);
+
+	return 0;
+
+failed_free_irq:
+	free_irq(keypad->irq, keypad);
+failed_free_dev:
+	input_free_device(input_dev);
+failed_put_clk:
+	clk_put(keypad->clk);
+failed_free_gpio:
+	ep93xx_keypad_release_gpio(pdev);
+failed_free_io:
+	iounmap(keypad->mmio_base);
+failed_free_mem:
+	release_mem_region(res->start, resource_size(res));
+failed_free:
+	kfree(keypad);
+	return err;
+}
+
+static int ep93xx_keypad_remove(struct platform_device *pdev)
+{
+	struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
+	struct resource *res;
+
+	free_irq(keypad->irq, keypad);
+
+	if (keypad->enabled)
+		clk_disable(keypad->clk);
+	clk_put(keypad->clk);
+
+	input_unregister_device(keypad->input_dev);
+
+	ep93xx_keypad_release_gpio(pdev);
+
+	iounmap(keypad->mmio_base);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(keypad);
+
+	return 0;
+}
+
+static struct platform_driver ep93xx_keypad_driver = {
+	.driver		= {
+		.name	= "ep93xx-keypad",
+		.pm	= &ep93xx_keypad_pm_ops,
+	},
+	.probe		= ep93xx_keypad_probe,
+	.remove		= ep93xx_keypad_remove,
+};
+module_platform_driver(ep93xx_keypad_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_DESCRIPTION("EP93xx Matrix Keypad Controller");
+MODULE_ALIAS("platform:ep93xx-keypad");
diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c
new file mode 100644
index 0000000..907e4e2
--- /dev/null
+++ b/drivers/input/keyboard/goldfish_events.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2012 Intel, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+enum {
+	REG_READ        = 0x00,
+	REG_SET_PAGE    = 0x00,
+	REG_LEN         = 0x04,
+	REG_DATA        = 0x08,
+
+	PAGE_NAME       = 0x00000,
+	PAGE_EVBITS     = 0x10000,
+	PAGE_ABSDATA    = 0x20000 | EV_ABS,
+};
+
+struct event_dev {
+	struct input_dev *input;
+	int irq;
+	void __iomem *addr;
+	char name[0];
+};
+
+static irqreturn_t events_interrupt(int irq, void *dev_id)
+{
+	struct event_dev *edev = dev_id;
+	unsigned type, code, value;
+
+	type = __raw_readl(edev->addr + REG_READ);
+	code = __raw_readl(edev->addr + REG_READ);
+	value = __raw_readl(edev->addr + REG_READ);
+
+	input_event(edev->input, type, code, value);
+	input_sync(edev->input);
+	return IRQ_HANDLED;
+}
+
+static void events_import_bits(struct event_dev *edev,
+			unsigned long bits[], unsigned type, size_t count)
+{
+	void __iomem *addr = edev->addr;
+	int i, j;
+	size_t size;
+	uint8_t val;
+
+	__raw_writel(PAGE_EVBITS | type, addr + REG_SET_PAGE);
+
+	size = __raw_readl(addr + REG_LEN) * 8;
+	if (size < count)
+		count = size;
+
+	addr += REG_DATA;
+	for (i = 0; i < count; i += 8) {
+		val = __raw_readb(addr++);
+		for (j = 0; j < 8; j++)
+			if (val & 1 << j)
+				set_bit(i + j, bits);
+	}
+}
+
+static void events_import_abs_params(struct event_dev *edev)
+{
+	struct input_dev *input_dev = edev->input;
+	void __iomem *addr = edev->addr;
+	u32 val[4];
+	int count;
+	int i, j;
+
+	__raw_writel(PAGE_ABSDATA, addr + REG_SET_PAGE);
+
+	count = __raw_readl(addr + REG_LEN) / sizeof(val);
+	if (count > ABS_MAX)
+		count = ABS_MAX;
+
+	for (i = 0; i < count; i++) {
+		if (!test_bit(i, input_dev->absbit))
+			continue;
+
+		for (j = 0; j < ARRAY_SIZE(val); j++) {
+			int offset = (i * ARRAY_SIZE(val) + j) * sizeof(u32);
+			val[j] = __raw_readl(edev->addr + REG_DATA + offset);
+		}
+
+		input_set_abs_params(input_dev, i,
+				     val[0], val[1], val[2], val[3]);
+	}
+}
+
+static int events_probe(struct platform_device *pdev)
+{
+	struct input_dev *input_dev;
+	struct event_dev *edev;
+	struct resource *res;
+	unsigned keymapnamelen;
+	void __iomem *addr;
+	int irq;
+	int i;
+	int error;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -EINVAL;
+
+	addr = devm_ioremap(&pdev->dev, res->start, 4096);
+	if (!addr)
+		return -ENOMEM;
+
+	__raw_writel(PAGE_NAME, addr + REG_SET_PAGE);
+	keymapnamelen = __raw_readl(addr + REG_LEN);
+
+	edev = devm_kzalloc(&pdev->dev,
+			    sizeof(struct event_dev) + keymapnamelen + 1,
+			    GFP_KERNEL);
+	if (!edev)
+		return -ENOMEM;
+
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!input_dev)
+		return -ENOMEM;
+
+	edev->input = input_dev;
+	edev->addr = addr;
+	edev->irq = irq;
+
+	for (i = 0; i < keymapnamelen; i++)
+		edev->name[i] = __raw_readb(edev->addr + REG_DATA + i);
+
+	pr_debug("events_probe() keymap=%s\n", edev->name);
+
+	input_dev->name = edev->name;
+	input_dev->id.bustype = BUS_HOST;
+
+	events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
+	events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
+	events_import_bits(edev, input_dev->relbit, EV_REL, REL_MAX);
+	events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX);
+	events_import_bits(edev, input_dev->mscbit, EV_MSC, MSC_MAX);
+	events_import_bits(edev, input_dev->ledbit, EV_LED, LED_MAX);
+	events_import_bits(edev, input_dev->sndbit, EV_SND, SND_MAX);
+	events_import_bits(edev, input_dev->ffbit, EV_FF, FF_MAX);
+	events_import_bits(edev, input_dev->swbit, EV_SW, SW_MAX);
+
+	events_import_abs_params(edev);
+
+	error = devm_request_irq(&pdev->dev, edev->irq, events_interrupt, 0,
+				 "goldfish-events-keypad", edev);
+	if (error)
+		return error;
+
+	error = input_register_device(input_dev);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static struct platform_driver events_driver = {
+	.probe	= events_probe,
+	.driver	= {
+		.name	= "goldfish_events",
+	},
+};
+
+module_platform_driver(events_driver);
+
+MODULE_AUTHOR("Brian Swetland");
+MODULE_DESCRIPTION("Goldfish Event Device");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
new file mode 100644
index 0000000..bef317f
--- /dev/null
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -0,0 +1,877 @@
+/*
+ * Driver for keys on GPIO lines capable of generating interrupts.
+ *
+ * Copyright 2005 Phil Blundell
+ * Copyright 2010, 2011 David Jander <david@protonic.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/gpio_keys.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/spinlock.h>
+
+struct gpio_button_data {
+	const struct gpio_keys_button *button;
+	struct input_dev *input;
+
+	struct timer_list release_timer;
+	unsigned int release_delay;	/* in msecs, for IRQ-only buttons */
+
+	struct delayed_work work;
+	unsigned int software_debounce;	/* in msecs, for GPIO-driven buttons */
+
+	unsigned int irq;
+	spinlock_t lock;
+	bool disabled;
+	bool key_pressed;
+};
+
+struct gpio_keys_drvdata {
+	const struct gpio_keys_platform_data *pdata;
+	struct input_dev *input;
+	struct mutex disable_lock;
+	struct gpio_button_data data[0];
+};
+
+/*
+ * SYSFS interface for enabling/disabling keys and switches:
+ *
+ * There are 4 attributes under /sys/devices/platform/gpio-keys/
+ *	keys [ro]              - bitmap of keys (EV_KEY) which can be
+ *	                         disabled
+ *	switches [ro]          - bitmap of switches (EV_SW) which can be
+ *	                         disabled
+ *	disabled_keys [rw]     - bitmap of keys currently disabled
+ *	disabled_switches [rw] - bitmap of switches currently disabled
+ *
+ * Userland can change these values and hence disable event generation
+ * for each key (or switch). Disabling a key means its interrupt line
+ * is disabled.
+ *
+ * For example, if we have following switches set up as gpio-keys:
+ *	SW_DOCK = 5
+ *	SW_CAMERA_LENS_COVER = 9
+ *	SW_KEYPAD_SLIDE = 10
+ *	SW_FRONT_PROXIMITY = 11
+ * This is read from switches:
+ *	11-9,5
+ * Next we want to disable proximity (11) and dock (5), we write:
+ *	11,5
+ * to file disabled_switches. Now proximity and dock IRQs are disabled.
+ * This can be verified by reading the file disabled_switches:
+ *	11,5
+ * If we now want to enable proximity (11) switch we write:
+ *	5
+ * to disabled_switches.
+ *
+ * We can disable only those keys which don't allow sharing the irq.
+ */
+
+/**
+ * get_n_events_by_type() - returns maximum number of events per @type
+ * @type: type of button (%EV_KEY, %EV_SW)
+ *
+ * Return value of this function can be used to allocate bitmap
+ * large enough to hold all bits for given type.
+ */
+static inline int get_n_events_by_type(int type)
+{
+	BUG_ON(type != EV_SW && type != EV_KEY);
+
+	return (type == EV_KEY) ? KEY_CNT : SW_CNT;
+}
+
+/**
+ * gpio_keys_disable_button() - disables given GPIO button
+ * @bdata: button data for button to be disabled
+ *
+ * Disables button pointed by @bdata. This is done by masking
+ * IRQ line. After this function is called, button won't generate
+ * input events anymore. Note that one can only disable buttons
+ * that don't share IRQs.
+ *
+ * Make sure that @bdata->disable_lock is locked when entering
+ * this function to avoid races when concurrent threads are
+ * disabling buttons at the same time.
+ */
+static void gpio_keys_disable_button(struct gpio_button_data *bdata)
+{
+	if (!bdata->disabled) {
+		/*
+		 * Disable IRQ and associated timer/work structure.
+		 */
+		disable_irq(bdata->irq);
+
+		if (gpio_is_valid(bdata->button->gpio))
+			cancel_delayed_work_sync(&bdata->work);
+		else
+			del_timer_sync(&bdata->release_timer);
+
+		bdata->disabled = true;
+	}
+}
+
+/**
+ * gpio_keys_enable_button() - enables given GPIO button
+ * @bdata: button data for button to be disabled
+ *
+ * Enables given button pointed by @bdata.
+ *
+ * Make sure that @bdata->disable_lock is locked when entering
+ * this function to avoid races with concurrent threads trying
+ * to enable the same button at the same time.
+ */
+static void gpio_keys_enable_button(struct gpio_button_data *bdata)
+{
+	if (bdata->disabled) {
+		enable_irq(bdata->irq);
+		bdata->disabled = false;
+	}
+}
+
+/**
+ * gpio_keys_attr_show_helper() - fill in stringified bitmap of buttons
+ * @ddata: pointer to drvdata
+ * @buf: buffer where stringified bitmap is written
+ * @type: button type (%EV_KEY, %EV_SW)
+ * @only_disabled: does caller want only those buttons that are
+ *                 currently disabled or all buttons that can be
+ *                 disabled
+ *
+ * This function writes buttons that can be disabled to @buf. If
+ * @only_disabled is true, then @buf contains only those buttons
+ * that are currently disabled. Returns 0 on success or negative
+ * errno on failure.
+ */
+static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata,
+					  char *buf, unsigned int type,
+					  bool only_disabled)
+{
+	int n_events = get_n_events_by_type(type);
+	unsigned long *bits;
+	ssize_t ret;
+	int i;
+
+	bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL);
+	if (!bits)
+		return -ENOMEM;
+
+	for (i = 0; i < ddata->pdata->nbuttons; i++) {
+		struct gpio_button_data *bdata = &ddata->data[i];
+
+		if (bdata->button->type != type)
+			continue;
+
+		if (only_disabled && !bdata->disabled)
+			continue;
+
+		__set_bit(bdata->button->code, bits);
+	}
+
+	ret = scnprintf(buf, PAGE_SIZE - 1, "%*pbl", n_events, bits);
+	buf[ret++] = '\n';
+	buf[ret] = '\0';
+
+	kfree(bits);
+
+	return ret;
+}
+
+/**
+ * gpio_keys_attr_store_helper() - enable/disable buttons based on given bitmap
+ * @ddata: pointer to drvdata
+ * @buf: buffer from userspace that contains stringified bitmap
+ * @type: button type (%EV_KEY, %EV_SW)
+ *
+ * This function parses stringified bitmap from @buf and disables/enables
+ * GPIO buttons accordingly. Returns 0 on success and negative error
+ * on failure.
+ */
+static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
+					   const char *buf, unsigned int type)
+{
+	int n_events = get_n_events_by_type(type);
+	unsigned long *bits;
+	ssize_t error;
+	int i;
+
+	bits = kcalloc(BITS_TO_LONGS(n_events), sizeof(*bits), GFP_KERNEL);
+	if (!bits)
+		return -ENOMEM;
+
+	error = bitmap_parselist(buf, bits, n_events);
+	if (error)
+		goto out;
+
+	/* First validate */
+	for (i = 0; i < ddata->pdata->nbuttons; i++) {
+		struct gpio_button_data *bdata = &ddata->data[i];
+
+		if (bdata->button->type != type)
+			continue;
+
+		if (test_bit(bdata->button->code, bits) &&
+		    !bdata->button->can_disable) {
+			error = -EINVAL;
+			goto out;
+		}
+	}
+
+	if (i == ddata->pdata->nbuttons) {
+		error = -EINVAL;
+		goto out;
+	}
+
+	mutex_lock(&ddata->disable_lock);
+
+	for (i = 0; i < ddata->pdata->nbuttons; i++) {
+		struct gpio_button_data *bdata = &ddata->data[i];
+
+		if (bdata->button->type != type)
+			continue;
+
+		if (test_bit(bdata->button->code, bits))
+			gpio_keys_disable_button(bdata);
+		else
+			gpio_keys_enable_button(bdata);
+	}
+
+	mutex_unlock(&ddata->disable_lock);
+
+out:
+	kfree(bits);
+	return error;
+}
+
+#define ATTR_SHOW_FN(name, type, only_disabled)				\
+static ssize_t gpio_keys_show_##name(struct device *dev,		\
+				     struct device_attribute *attr,	\
+				     char *buf)				\
+{									\
+	struct platform_device *pdev = to_platform_device(dev);		\
+	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);	\
+									\
+	return gpio_keys_attr_show_helper(ddata, buf,			\
+					  type, only_disabled);		\
+}
+
+ATTR_SHOW_FN(keys, EV_KEY, false);
+ATTR_SHOW_FN(switches, EV_SW, false);
+ATTR_SHOW_FN(disabled_keys, EV_KEY, true);
+ATTR_SHOW_FN(disabled_switches, EV_SW, true);
+
+/*
+ * ATTRIBUTES:
+ *
+ * /sys/devices/platform/gpio-keys/keys [ro]
+ * /sys/devices/platform/gpio-keys/switches [ro]
+ */
+static DEVICE_ATTR(keys, S_IRUGO, gpio_keys_show_keys, NULL);
+static DEVICE_ATTR(switches, S_IRUGO, gpio_keys_show_switches, NULL);
+
+#define ATTR_STORE_FN(name, type)					\
+static ssize_t gpio_keys_store_##name(struct device *dev,		\
+				      struct device_attribute *attr,	\
+				      const char *buf,			\
+				      size_t count)			\
+{									\
+	struct platform_device *pdev = to_platform_device(dev);		\
+	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);	\
+	ssize_t error;							\
+									\
+	error = gpio_keys_attr_store_helper(ddata, buf, type);		\
+	if (error)							\
+		return error;						\
+									\
+	return count;							\
+}
+
+ATTR_STORE_FN(disabled_keys, EV_KEY);
+ATTR_STORE_FN(disabled_switches, EV_SW);
+
+/*
+ * ATTRIBUTES:
+ *
+ * /sys/devices/platform/gpio-keys/disabled_keys [rw]
+ * /sys/devices/platform/gpio-keys/disables_switches [rw]
+ */
+static DEVICE_ATTR(disabled_keys, S_IWUSR | S_IRUGO,
+		   gpio_keys_show_disabled_keys,
+		   gpio_keys_store_disabled_keys);
+static DEVICE_ATTR(disabled_switches, S_IWUSR | S_IRUGO,
+		   gpio_keys_show_disabled_switches,
+		   gpio_keys_store_disabled_switches);
+
+static struct attribute *gpio_keys_attrs[] = {
+	&dev_attr_keys.attr,
+	&dev_attr_switches.attr,
+	&dev_attr_disabled_keys.attr,
+	&dev_attr_disabled_switches.attr,
+	NULL,
+};
+
+static struct attribute_group gpio_keys_attr_group = {
+	.attrs = gpio_keys_attrs,
+};
+
+static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
+{
+	const struct gpio_keys_button *button = bdata->button;
+	struct input_dev *input = bdata->input;
+	unsigned int type = button->type ?: EV_KEY;
+	int state = gpio_get_value_cansleep(button->gpio);
+
+	if (state < 0) {
+		dev_err(input->dev.parent, "failed to get gpio state\n");
+		return;
+	}
+
+	state = (state ? 1 : 0) ^ button->active_low;
+	if (type == EV_ABS) {
+		if (state)
+			input_event(input, type, button->code, button->value);
+	} else {
+		input_event(input, type, button->code, !!state);
+	}
+	input_sync(input);
+}
+
+static void gpio_keys_gpio_work_func(struct work_struct *work)
+{
+	struct gpio_button_data *bdata =
+		container_of(work, struct gpio_button_data, work.work);
+
+	gpio_keys_gpio_report_event(bdata);
+
+	if (bdata->button->wakeup)
+		pm_relax(bdata->input->dev.parent);
+}
+
+static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
+{
+	struct gpio_button_data *bdata = dev_id;
+
+	BUG_ON(irq != bdata->irq);
+
+	if (bdata->button->wakeup)
+		pm_stay_awake(bdata->input->dev.parent);
+
+	mod_delayed_work(system_wq,
+			 &bdata->work,
+			 msecs_to_jiffies(bdata->software_debounce));
+
+	return IRQ_HANDLED;
+}
+
+static void gpio_keys_irq_timer(unsigned long _data)
+{
+	struct gpio_button_data *bdata = (struct gpio_button_data *)_data;
+	struct input_dev *input = bdata->input;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bdata->lock, flags);
+	if (bdata->key_pressed) {
+		input_event(input, EV_KEY, bdata->button->code, 0);
+		input_sync(input);
+		bdata->key_pressed = false;
+	}
+	spin_unlock_irqrestore(&bdata->lock, flags);
+}
+
+static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
+{
+	struct gpio_button_data *bdata = dev_id;
+	const struct gpio_keys_button *button = bdata->button;
+	struct input_dev *input = bdata->input;
+	unsigned long flags;
+
+	BUG_ON(irq != bdata->irq);
+
+	spin_lock_irqsave(&bdata->lock, flags);
+
+	if (!bdata->key_pressed) {
+		if (bdata->button->wakeup)
+			pm_wakeup_event(bdata->input->dev.parent, 0);
+
+		input_event(input, EV_KEY, button->code, 1);
+		input_sync(input);
+
+		if (!bdata->release_delay) {
+			input_event(input, EV_KEY, button->code, 0);
+			input_sync(input);
+			goto out;
+		}
+
+		bdata->key_pressed = true;
+	}
+
+	if (bdata->release_delay)
+		mod_timer(&bdata->release_timer,
+			jiffies + msecs_to_jiffies(bdata->release_delay));
+out:
+	spin_unlock_irqrestore(&bdata->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void gpio_keys_quiesce_key(void *data)
+{
+	struct gpio_button_data *bdata = data;
+
+	if (gpio_is_valid(bdata->button->gpio))
+		cancel_delayed_work_sync(&bdata->work);
+	else
+		del_timer_sync(&bdata->release_timer);
+}
+
+static int gpio_keys_setup_key(struct platform_device *pdev,
+				struct input_dev *input,
+				struct gpio_button_data *bdata,
+				const struct gpio_keys_button *button)
+{
+	const char *desc = button->desc ? button->desc : "gpio_keys";
+	struct device *dev = &pdev->dev;
+	irq_handler_t isr;
+	unsigned long irqflags;
+	int irq;
+	int error;
+
+	bdata->input = input;
+	bdata->button = button;
+	spin_lock_init(&bdata->lock);
+
+	if (gpio_is_valid(button->gpio)) {
+
+		error = devm_gpio_request_one(&pdev->dev, button->gpio,
+					      GPIOF_IN, desc);
+		if (error < 0) {
+			dev_err(dev, "Failed to request GPIO %d, error %d\n",
+				button->gpio, error);
+			return error;
+		}
+
+		if (button->debounce_interval) {
+			error = gpio_set_debounce(button->gpio,
+					button->debounce_interval * 1000);
+			/* use timer if gpiolib doesn't provide debounce */
+			if (error < 0)
+				bdata->software_debounce =
+						button->debounce_interval;
+		}
+
+		if (button->irq) {
+			bdata->irq = button->irq;
+		} else {
+			irq = gpio_to_irq(button->gpio);
+			if (irq < 0) {
+				error = irq;
+				dev_err(dev,
+					"Unable to get irq number for GPIO %d, error %d\n",
+					button->gpio, error);
+				return error;
+			}
+			bdata->irq = irq;
+		}
+
+		INIT_DELAYED_WORK(&bdata->work, gpio_keys_gpio_work_func);
+
+		isr = gpio_keys_gpio_isr;
+		irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+	} else {
+		if (!button->irq) {
+			dev_err(dev, "No IRQ specified\n");
+			return -EINVAL;
+		}
+		bdata->irq = button->irq;
+
+		if (button->type && button->type != EV_KEY) {
+			dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n");
+			return -EINVAL;
+		}
+
+		bdata->release_delay = button->debounce_interval;
+		setup_timer(&bdata->release_timer,
+			    gpio_keys_irq_timer, (unsigned long)bdata);
+
+		isr = gpio_keys_irq_isr;
+		irqflags = 0;
+	}
+
+	input_set_capability(input, button->type ?: EV_KEY, button->code);
+
+	/*
+	 * Install custom action to cancel release timer and
+	 * workqueue item.
+	 */
+	error = devm_add_action(&pdev->dev, gpio_keys_quiesce_key, bdata);
+	if (error) {
+		dev_err(&pdev->dev,
+			"failed to register quiesce action, error: %d\n",
+			error);
+		return error;
+	}
+
+	/*
+	 * If platform has specified that the button can be disabled,
+	 * we don't want it to share the interrupt line.
+	 */
+	if (!button->can_disable)
+		irqflags |= IRQF_SHARED;
+
+	error = devm_request_any_context_irq(&pdev->dev, bdata->irq,
+					     isr, irqflags, desc, bdata);
+	if (error < 0) {
+		dev_err(dev, "Unable to claim irq %d; error %d\n",
+			bdata->irq, error);
+		return error;
+	}
+
+	return 0;
+}
+
+static void gpio_keys_report_state(struct gpio_keys_drvdata *ddata)
+{
+	struct input_dev *input = ddata->input;
+	int i;
+
+	for (i = 0; i < ddata->pdata->nbuttons; i++) {
+		struct gpio_button_data *bdata = &ddata->data[i];
+		if (gpio_is_valid(bdata->button->gpio))
+			gpio_keys_gpio_report_event(bdata);
+	}
+	input_sync(input);
+}
+
+static int gpio_keys_open(struct input_dev *input)
+{
+	struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
+	const struct gpio_keys_platform_data *pdata = ddata->pdata;
+	int error;
+
+	if (pdata->enable) {
+		error = pdata->enable(input->dev.parent);
+		if (error)
+			return error;
+	}
+
+	/* Report current state of buttons that are connected to GPIOs */
+	gpio_keys_report_state(ddata);
+
+	return 0;
+}
+
+static void gpio_keys_close(struct input_dev *input)
+{
+	struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
+	const struct gpio_keys_platform_data *pdata = ddata->pdata;
+
+	if (pdata->disable)
+		pdata->disable(input->dev.parent);
+}
+
+/*
+ * Handlers for alternative sources of platform_data
+ */
+
+#ifdef CONFIG_OF
+/*
+ * Translate OpenFirmware node properties into platform_data
+ */
+static struct gpio_keys_platform_data *
+gpio_keys_get_devtree_pdata(struct device *dev)
+{
+	struct device_node *node, *pp;
+	struct gpio_keys_platform_data *pdata;
+	struct gpio_keys_button *button;
+	int error;
+	int nbuttons;
+	int i;
+
+	node = dev->of_node;
+	if (!node)
+		return ERR_PTR(-ENODEV);
+
+	nbuttons = of_get_child_count(node);
+	if (nbuttons == 0)
+		return ERR_PTR(-ENODEV);
+
+	pdata = devm_kzalloc(dev,
+			     sizeof(*pdata) + nbuttons * sizeof(*button),
+			     GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	pdata->buttons = (struct gpio_keys_button *)(pdata + 1);
+	pdata->nbuttons = nbuttons;
+
+	pdata->rep = !!of_get_property(node, "autorepeat", NULL);
+
+	i = 0;
+	for_each_child_of_node(node, pp) {
+		enum of_gpio_flags flags;
+
+		button = &pdata->buttons[i++];
+
+		button->gpio = of_get_gpio_flags(pp, 0, &flags);
+		if (button->gpio < 0) {
+			error = button->gpio;
+			if (error != -ENOENT) {
+				if (error != -EPROBE_DEFER)
+					dev_err(dev,
+						"Failed to get gpio flags, error: %d\n",
+						error);
+				return ERR_PTR(error);
+			}
+		} else {
+			button->active_low = flags & OF_GPIO_ACTIVE_LOW;
+		}
+
+		button->irq = irq_of_parse_and_map(pp, 0);
+
+		if (!gpio_is_valid(button->gpio) && !button->irq) {
+			dev_err(dev, "Found button without gpios or irqs\n");
+			return ERR_PTR(-EINVAL);
+		}
+
+		if (of_property_read_u32(pp, "linux,code", &button->code)) {
+			dev_err(dev, "Button without keycode: 0x%x\n",
+				button->gpio);
+			return ERR_PTR(-EINVAL);
+		}
+
+		button->desc = of_get_property(pp, "label", NULL);
+
+		if (of_property_read_u32(pp, "linux,input-type", &button->type))
+			button->type = EV_KEY;
+
+		button->wakeup = of_property_read_bool(pp, "wakeup-source") ||
+				 /* legacy name */
+				 of_property_read_bool(pp, "gpio-key,wakeup");
+
+		button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL);
+
+		if (of_property_read_u32(pp, "debounce-interval",
+					 &button->debounce_interval))
+			button->debounce_interval = 5;
+	}
+
+	if (pdata->nbuttons == 0)
+		return ERR_PTR(-EINVAL);
+
+	return pdata;
+}
+
+static const struct of_device_id gpio_keys_of_match[] = {
+	{ .compatible = "gpio-keys", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, gpio_keys_of_match);
+
+#else
+
+static inline struct gpio_keys_platform_data *
+gpio_keys_get_devtree_pdata(struct device *dev)
+{
+	return ERR_PTR(-ENODEV);
+}
+
+#endif
+
+static int gpio_keys_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
+	struct gpio_keys_drvdata *ddata;
+	struct input_dev *input;
+	size_t size;
+	int i, error;
+	int wakeup = 0;
+
+	if (!pdata) {
+		pdata = gpio_keys_get_devtree_pdata(dev);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+	}
+
+	size = sizeof(struct gpio_keys_drvdata) +
+			pdata->nbuttons * sizeof(struct gpio_button_data);
+	ddata = devm_kzalloc(dev, size, GFP_KERNEL);
+	if (!ddata) {
+		dev_err(dev, "failed to allocate state\n");
+		return -ENOMEM;
+	}
+
+	input = devm_input_allocate_device(dev);
+	if (!input) {
+		dev_err(dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ddata->pdata = pdata;
+	ddata->input = input;
+	mutex_init(&ddata->disable_lock);
+
+	platform_set_drvdata(pdev, ddata);
+	input_set_drvdata(input, ddata);
+
+	input->name = pdata->name ? : pdev->name;
+	input->phys = "gpio-keys/input0";
+	input->dev.parent = &pdev->dev;
+	input->open = gpio_keys_open;
+	input->close = gpio_keys_close;
+
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0100;
+
+	/* Enable auto repeat feature of Linux input subsystem */
+	if (pdata->rep)
+		__set_bit(EV_REP, input->evbit);
+
+	for (i = 0; i < pdata->nbuttons; i++) {
+		const struct gpio_keys_button *button = &pdata->buttons[i];
+		struct gpio_button_data *bdata = &ddata->data[i];
+
+		error = gpio_keys_setup_key(pdev, input, bdata, button);
+		if (error)
+			return error;
+
+		if (button->wakeup)
+			wakeup = 1;
+	}
+
+	error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);
+	if (error) {
+		dev_err(dev, "Unable to export keys/switches, error: %d\n",
+			error);
+		return error;
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(dev, "Unable to register input device, error: %d\n",
+			error);
+		goto err_remove_group;
+	}
+
+	device_init_wakeup(&pdev->dev, wakeup);
+
+	return 0;
+
+err_remove_group:
+	sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
+	return error;
+}
+
+static int gpio_keys_remove(struct platform_device *pdev)
+{
+	sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
+
+	device_init_wakeup(&pdev->dev, 0);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int gpio_keys_suspend(struct device *dev)
+{
+	struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
+	struct input_dev *input = ddata->input;
+	int i;
+
+	if (device_may_wakeup(dev)) {
+		for (i = 0; i < ddata->pdata->nbuttons; i++) {
+			struct gpio_button_data *bdata = &ddata->data[i];
+			if (bdata->button->wakeup)
+				enable_irq_wake(bdata->irq);
+		}
+	} else {
+		mutex_lock(&input->mutex);
+		if (input->users)
+			gpio_keys_close(input);
+		mutex_unlock(&input->mutex);
+	}
+
+	return 0;
+}
+
+static int gpio_keys_resume(struct device *dev)
+{
+	struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
+	struct input_dev *input = ddata->input;
+	int error = 0;
+	int i;
+
+	if (device_may_wakeup(dev)) {
+		for (i = 0; i < ddata->pdata->nbuttons; i++) {
+			struct gpio_button_data *bdata = &ddata->data[i];
+			if (bdata->button->wakeup)
+				disable_irq_wake(bdata->irq);
+		}
+	} else {
+		mutex_lock(&input->mutex);
+		if (input->users)
+			error = gpio_keys_open(input);
+		mutex_unlock(&input->mutex);
+	}
+
+	if (error)
+		return error;
+
+	gpio_keys_report_state(ddata);
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
+
+static struct platform_driver gpio_keys_device_driver = {
+	.probe		= gpio_keys_probe,
+	.remove		= gpio_keys_remove,
+	.driver		= {
+		.name	= "gpio-keys",
+		.pm	= &gpio_keys_pm_ops,
+		.of_match_table = of_match_ptr(gpio_keys_of_match),
+	}
+};
+
+static int __init gpio_keys_init(void)
+{
+	return platform_driver_register(&gpio_keys_device_driver);
+}
+
+static void __exit gpio_keys_exit(void)
+{
+	platform_driver_unregister(&gpio_keys_device_driver);
+}
+
+late_initcall(gpio_keys_init);
+module_exit(gpio_keys_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
+MODULE_DESCRIPTION("Keyboard driver for GPIOs");
+MODULE_ALIAS("platform:gpio-keys");
diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c
new file mode 100644
index 0000000..62bdb1d
--- /dev/null
+++ b/drivers/input/keyboard/gpio_keys_polled.c
@@ -0,0 +1,381 @@
+/*
+ *  Driver for buttons on GPIO lines not capable of generating interrupts
+ *
+ *  Copyright (C) 2007-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2010 Nuno Goncalves <nunojpg@gmail.com>
+ *
+ *  This file was based on: /drivers/input/misc/cobalt_btns.c
+ *	Copyright (C) 2007 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
+ *
+ *  also was based on: /drivers/input/keyboard/gpio_keys.c
+ *	Copyright 2005 Phil Blundell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio_keys.h>
+#include <linux/property.h>
+
+#define DRV_NAME	"gpio-keys-polled"
+
+struct gpio_keys_button_data {
+	int last_state;
+	int count;
+	int threshold;
+	int can_sleep;
+};
+
+struct gpio_keys_polled_dev {
+	struct input_polled_dev *poll_dev;
+	struct device *dev;
+	const struct gpio_keys_platform_data *pdata;
+	unsigned long rel_axis_seen[BITS_TO_LONGS(REL_CNT)];
+	unsigned long abs_axis_seen[BITS_TO_LONGS(ABS_CNT)];
+	struct gpio_keys_button_data data[0];
+};
+
+static void gpio_keys_button_event(struct input_polled_dev *dev,
+				   struct gpio_keys_button *button,
+				   int state)
+{
+	struct gpio_keys_polled_dev *bdev = dev->private;
+	struct input_dev *input = dev->input;
+	unsigned int type = button->type ?: EV_KEY;
+
+	if (type == EV_REL) {
+		if (state) {
+			input_event(input, type, button->code, button->value);
+			__set_bit(button->code, bdev->rel_axis_seen);
+		}
+	} else if (type == EV_ABS) {
+		if (state) {
+			input_event(input, type, button->code, button->value);
+			__set_bit(button->code, bdev->abs_axis_seen);
+		}
+	} else {
+		input_event(input, type, button->code, state);
+		input_sync(input);
+	}
+}
+
+static void gpio_keys_polled_check_state(struct input_polled_dev *dev,
+					 struct gpio_keys_button *button,
+					 struct gpio_keys_button_data *bdata)
+{
+	int state;
+
+	if (bdata->can_sleep)
+		state = !!gpiod_get_value_cansleep(button->gpiod);
+	else
+		state = !!gpiod_get_value(button->gpiod);
+
+	gpio_keys_button_event(dev, button, state);
+
+	if (state != bdata->last_state) {
+		bdata->count = 0;
+		bdata->last_state = state;
+	}
+}
+
+static void gpio_keys_polled_poll(struct input_polled_dev *dev)
+{
+	struct gpio_keys_polled_dev *bdev = dev->private;
+	const struct gpio_keys_platform_data *pdata = bdev->pdata;
+	struct input_dev *input = dev->input;
+	int i;
+
+	memset(bdev->rel_axis_seen, 0, sizeof(bdev->rel_axis_seen));
+	memset(bdev->abs_axis_seen, 0, sizeof(bdev->abs_axis_seen));
+
+	for (i = 0; i < pdata->nbuttons; i++) {
+		struct gpio_keys_button_data *bdata = &bdev->data[i];
+
+		if (bdata->count < bdata->threshold) {
+			bdata->count++;
+			gpio_keys_button_event(dev, &pdata->buttons[i],
+					       bdata->last_state);
+		} else {
+			gpio_keys_polled_check_state(dev, &pdata->buttons[i],
+						     bdata);
+		}
+	}
+
+	for_each_set_bit(i, input->relbit, REL_CNT) {
+		if (!test_bit(i, bdev->rel_axis_seen))
+			input_event(input, EV_REL, i, 0);
+	}
+
+	for_each_set_bit(i, input->absbit, ABS_CNT) {
+		if (!test_bit(i, bdev->abs_axis_seen))
+			input_event(input, EV_ABS, i, 0);
+	}
+
+	input_sync(input);
+}
+
+static void gpio_keys_polled_open(struct input_polled_dev *dev)
+{
+	struct gpio_keys_polled_dev *bdev = dev->private;
+	const struct gpio_keys_platform_data *pdata = bdev->pdata;
+
+	if (pdata->enable)
+		pdata->enable(bdev->dev);
+}
+
+static void gpio_keys_polled_close(struct input_polled_dev *dev)
+{
+	struct gpio_keys_polled_dev *bdev = dev->private;
+	const struct gpio_keys_platform_data *pdata = bdev->pdata;
+
+	if (pdata->disable)
+		pdata->disable(bdev->dev);
+}
+
+static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct device *dev)
+{
+	struct gpio_keys_platform_data *pdata;
+	struct gpio_keys_button *button;
+	struct fwnode_handle *child;
+	int error;
+	int nbuttons;
+
+	nbuttons = device_get_child_node_count(dev);
+	if (nbuttons == 0)
+		return NULL;
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata) + nbuttons * sizeof(*button),
+			     GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	pdata->buttons = (struct gpio_keys_button *)(pdata + 1);
+
+	pdata->rep = device_property_present(dev, "autorepeat");
+	device_property_read_u32(dev, "poll-interval", &pdata->poll_interval);
+
+	device_for_each_child_node(dev, child) {
+		struct gpio_desc *desc;
+
+		desc = devm_get_gpiod_from_child(dev, NULL, child);
+		if (IS_ERR(desc)) {
+			error = PTR_ERR(desc);
+			if (error != -EPROBE_DEFER)
+				dev_err(dev,
+					"Failed to get gpio flags, error: %d\n",
+					error);
+			fwnode_handle_put(child);
+			return ERR_PTR(error);
+		}
+
+		button = &pdata->buttons[pdata->nbuttons++];
+		button->gpiod = desc;
+
+		if (fwnode_property_read_u32(child, "linux,code", &button->code)) {
+			dev_err(dev, "Button without keycode: %d\n",
+				pdata->nbuttons - 1);
+			fwnode_handle_put(child);
+			return ERR_PTR(-EINVAL);
+		}
+
+		fwnode_property_read_string(child, "label", &button->desc);
+
+		if (fwnode_property_read_u32(child, "linux,input-type",
+					     &button->type))
+			button->type = EV_KEY;
+
+		if (fwnode_property_read_u32(child, "linux,input-value",
+					     (u32 *)&button->value))
+			button->value = 1;
+
+		button->wakeup =
+			fwnode_property_read_bool(child, "wakeup-source") ||
+			/* legacy name */
+			fwnode_property_read_bool(child, "gpio-key,wakeup");
+
+		if (fwnode_property_read_u32(child, "debounce-interval",
+					     &button->debounce_interval))
+			button->debounce_interval = 5;
+	}
+
+	if (pdata->nbuttons == 0)
+		return ERR_PTR(-EINVAL);
+
+	return pdata;
+}
+
+static void gpio_keys_polled_set_abs_params(struct input_dev *input,
+	const struct gpio_keys_platform_data *pdata, unsigned int code)
+{
+	int i, min = 0, max = 0;
+
+	for (i = 0; i < pdata->nbuttons; i++) {
+		struct gpio_keys_button *button = &pdata->buttons[i];
+
+		if (button->type != EV_ABS || button->code != code)
+			continue;
+
+		if (button->value < min)
+			min = button->value;
+		if (button->value > max)
+			max = button->value;
+	}
+	input_set_abs_params(input, code, min, max, 0, 0);
+}
+
+static const struct of_device_id gpio_keys_polled_of_match[] = {
+	{ .compatible = "gpio-keys-polled", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, gpio_keys_polled_of_match);
+
+static int gpio_keys_polled_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
+	struct gpio_keys_polled_dev *bdev;
+	struct input_polled_dev *poll_dev;
+	struct input_dev *input;
+	size_t size;
+	int error;
+	int i;
+
+	if (!pdata) {
+		pdata = gpio_keys_polled_get_devtree_pdata(dev);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+		if (!pdata) {
+			dev_err(dev, "missing platform data\n");
+			return -EINVAL;
+		}
+	}
+
+	if (!pdata->poll_interval) {
+		dev_err(dev, "missing poll_interval value\n");
+		return -EINVAL;
+	}
+
+	size = sizeof(struct gpio_keys_polled_dev) +
+			pdata->nbuttons * sizeof(struct gpio_keys_button_data);
+	bdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+	if (!bdev) {
+		dev_err(dev, "no memory for private data\n");
+		return -ENOMEM;
+	}
+
+	poll_dev = devm_input_allocate_polled_device(&pdev->dev);
+	if (!poll_dev) {
+		dev_err(dev, "no memory for polled device\n");
+		return -ENOMEM;
+	}
+
+	poll_dev->private = bdev;
+	poll_dev->poll = gpio_keys_polled_poll;
+	poll_dev->poll_interval = pdata->poll_interval;
+	poll_dev->open = gpio_keys_polled_open;
+	poll_dev->close = gpio_keys_polled_close;
+
+	input = poll_dev->input;
+
+	input->name = pdev->name;
+	input->phys = DRV_NAME"/input0";
+
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0100;
+
+	__set_bit(EV_KEY, input->evbit);
+	if (pdata->rep)
+		__set_bit(EV_REP, input->evbit);
+
+	for (i = 0; i < pdata->nbuttons; i++) {
+		struct gpio_keys_button *button = &pdata->buttons[i];
+		struct gpio_keys_button_data *bdata = &bdev->data[i];
+		unsigned int type = button->type ?: EV_KEY;
+
+		if (button->wakeup) {
+			dev_err(dev, DRV_NAME " does not support wakeup\n");
+			return -EINVAL;
+		}
+
+		/*
+		 * Legacy GPIO number so request the GPIO here and
+		 * convert it to descriptor.
+		 */
+		if (!button->gpiod && gpio_is_valid(button->gpio)) {
+			unsigned flags = GPIOF_IN;
+
+			if (button->active_low)
+				flags |= GPIOF_ACTIVE_LOW;
+
+			error = devm_gpio_request_one(&pdev->dev, button->gpio,
+					flags, button->desc ? : DRV_NAME);
+			if (error) {
+				dev_err(dev, "unable to claim gpio %u, err=%d\n",
+					button->gpio, error);
+				return error;
+			}
+
+			button->gpiod = gpio_to_desc(button->gpio);
+		}
+
+		if (IS_ERR(button->gpiod))
+			return PTR_ERR(button->gpiod);
+
+		bdata->can_sleep = gpiod_cansleep(button->gpiod);
+		bdata->last_state = -1;
+		bdata->threshold = DIV_ROUND_UP(button->debounce_interval,
+						pdata->poll_interval);
+
+		input_set_capability(input, type, button->code);
+		if (type == EV_ABS)
+			gpio_keys_polled_set_abs_params(input, pdata,
+							button->code);
+	}
+
+	bdev->poll_dev = poll_dev;
+	bdev->dev = dev;
+	bdev->pdata = pdata;
+	platform_set_drvdata(pdev, bdev);
+
+	error = input_register_polled_device(poll_dev);
+	if (error) {
+		dev_err(dev, "unable to register polled device, err=%d\n",
+			error);
+		return error;
+	}
+
+	/* report initial state of the buttons */
+	for (i = 0; i < pdata->nbuttons; i++)
+		gpio_keys_polled_check_state(poll_dev, &pdata->buttons[i],
+					     &bdev->data[i]);
+
+	input_sync(input);
+
+	return 0;
+}
+
+static struct platform_driver gpio_keys_polled_driver = {
+	.probe	= gpio_keys_polled_probe,
+	.driver	= {
+		.name	= DRV_NAME,
+		.of_match_table = gpio_keys_polled_of_match,
+	},
+};
+module_platform_driver(gpio_keys_polled_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_DESCRIPTION("Polled GPIO Buttons driver");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
new file mode 100644
index 0000000..5b152f2
--- /dev/null
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -0,0 +1,585 @@
+/*
+ * Generic linux-input device driver for keyboard devices
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *
+ * References:
+ * HP-HIL Technical Reference Manual.  Hewlett Packard Product No. 45918A
+ *
+ */
+
+#include <linux/hil.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include <linux/pci_ids.h>
+
+#define PREFIX "HIL: "
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("HIL keyboard/mouse driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("serio:ty03pr25id00ex*"); /* HIL keyboard */
+MODULE_ALIAS("serio:ty03pr25id0Fex*"); /* HIL mouse */
+
+#define HIL_PACKET_MAX_LENGTH 16
+
+#define HIL_KBD_SET1_UPBIT 0x01
+#define HIL_KBD_SET1_SHIFT 1
+static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly =
+	{ HIL_KEYCODES_SET1 };
+
+#define HIL_KBD_SET2_UPBIT 0x01
+#define HIL_KBD_SET2_SHIFT 1
+/* Set2 is user defined */
+
+#define HIL_KBD_SET3_UPBIT 0x80
+#define HIL_KBD_SET3_SHIFT 0
+static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] __read_mostly =
+	{ HIL_KEYCODES_SET3 };
+
+static const char hil_language[][16] = { HIL_LOCALE_MAP };
+
+struct hil_dev {
+	struct input_dev *dev;
+	struct serio *serio;
+
+	/* Input buffer and index for packets from HIL bus. */
+	hil_packet data[HIL_PACKET_MAX_LENGTH];
+	int idx4; /* four counts per packet */
+
+	/* Raw device info records from HIL bus, see hil.h for fields. */
+	char	idd[HIL_PACKET_MAX_LENGTH];	/* DID byte and IDD record */
+	char	rsc[HIL_PACKET_MAX_LENGTH];	/* RSC record */
+	char	exd[HIL_PACKET_MAX_LENGTH];	/* EXD record */
+	char	rnm[HIL_PACKET_MAX_LENGTH + 1];	/* RNM record + NULL term. */
+
+	struct completion cmd_done;
+
+	bool is_pointer;
+	/* Extra device details needed for pointing devices. */
+	unsigned int nbtn, naxes;
+	unsigned int btnmap[7];
+};
+
+static bool hil_dev_is_command_response(hil_packet p)
+{
+	if ((p & ~HIL_CMDCT_POL) == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
+		return false;
+
+	if ((p & ~HIL_CMDCT_RPL) == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL))
+		return false;
+
+	return true;
+}
+
+static void hil_dev_handle_command_response(struct hil_dev *dev)
+{
+	hil_packet p;
+	char *buf;
+	int i, idx;
+
+	idx = dev->idx4 / 4;
+	p = dev->data[idx - 1];
+
+	switch (p & HIL_PKT_DATA_MASK) {
+	case HIL_CMD_IDD:
+		buf = dev->idd;
+		break;
+
+	case HIL_CMD_RSC:
+		buf = dev->rsc;
+		break;
+
+	case HIL_CMD_EXD:
+		buf = dev->exd;
+		break;
+
+	case HIL_CMD_RNM:
+		dev->rnm[HIL_PACKET_MAX_LENGTH] = 0;
+		buf = dev->rnm;
+		break;
+
+	default:
+		/* These occur when device isn't present */
+		if (p != (HIL_ERR_INT | HIL_PKT_CMD)) {
+			/* Anything else we'd like to know about. */
+			printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
+		}
+		goto out;
+	}
+
+	for (i = 0; i < idx; i++)
+		buf[i] = dev->data[i] & HIL_PKT_DATA_MASK;
+	for (; i < HIL_PACKET_MAX_LENGTH; i++)
+		buf[i] = 0;
+ out:
+	complete(&dev->cmd_done);
+}
+
+static void hil_dev_handle_kbd_events(struct hil_dev *kbd)
+{
+	struct input_dev *dev = kbd->dev;
+	int idx = kbd->idx4 / 4;
+	int i;
+
+	switch (kbd->data[0] & HIL_POL_CHARTYPE_MASK) {
+	case HIL_POL_CHARTYPE_NONE:
+		return;
+
+	case HIL_POL_CHARTYPE_ASCII:
+		for (i = 1; i < idx - 1; i++)
+			input_report_key(dev, kbd->data[i] & 0x7f, 1);
+		break;
+
+	case HIL_POL_CHARTYPE_RSVD1:
+	case HIL_POL_CHARTYPE_RSVD2:
+	case HIL_POL_CHARTYPE_BINARY:
+		for (i = 1; i < idx - 1; i++)
+			input_report_key(dev, kbd->data[i], 1);
+		break;
+
+	case HIL_POL_CHARTYPE_SET1:
+		for (i = 1; i < idx - 1; i++) {
+			unsigned int key = kbd->data[i];
+			int up = key & HIL_KBD_SET1_UPBIT;
+
+			key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+			key = hil_kbd_set1[key >> HIL_KBD_SET1_SHIFT];
+			input_report_key(dev, key, !up);
+		}
+		break;
+
+	case HIL_POL_CHARTYPE_SET2:
+		for (i = 1; i < idx - 1; i++) {
+			unsigned int key = kbd->data[i];
+			int up = key & HIL_KBD_SET2_UPBIT;
+
+			key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+			key = key >> HIL_KBD_SET2_SHIFT;
+			input_report_key(dev, key, !up);
+		}
+		break;
+
+	case HIL_POL_CHARTYPE_SET3:
+		for (i = 1; i < idx - 1; i++) {
+			unsigned int key = kbd->data[i];
+			int up = key & HIL_KBD_SET3_UPBIT;
+
+			key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+			key = hil_kbd_set3[key >> HIL_KBD_SET3_SHIFT];
+			input_report_key(dev, key, !up);
+		}
+		break;
+	}
+
+	input_sync(dev);
+}
+
+static void hil_dev_handle_ptr_events(struct hil_dev *ptr)
+{
+	struct input_dev *dev = ptr->dev;
+	int idx = ptr->idx4 / 4;
+	hil_packet p = ptr->data[idx - 1];
+	int i, cnt, laxis;
+	bool absdev, ax16;
+
+	if ((p & HIL_CMDCT_POL) != idx - 1) {
+		printk(KERN_WARNING PREFIX
+			"Malformed poll packet %x (idx = %i)\n", p, idx);
+		return;
+	}
+
+	i = (p & HIL_POL_AXIS_ALT) ? 3 : 0;
+	laxis = (p & HIL_POL_NUM_AXES_MASK) + i;
+
+	ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */
+	absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS;
+
+	for (cnt = 1; i < laxis; i++) {
+		unsigned int lo, hi, val;
+
+		lo = ptr->data[cnt++] & HIL_PKT_DATA_MASK;
+		hi = ax16 ? (ptr->data[cnt++] & HIL_PKT_DATA_MASK) : 0;
+
+		if (absdev) {
+			val = lo + (hi << 8);
+#ifdef TABLET_AUTOADJUST
+			if (val < input_abs_get_min(dev, ABS_X + i))
+				input_abs_set_min(dev, ABS_X + i, val);
+			if (val > input_abs_get_max(dev, ABS_X + i))
+				input_abs_set_max(dev, ABS_X + i, val);
+#endif
+			if (i % 3)
+				val = input_abs_get_max(dev, ABS_X + i) - val;
+			input_report_abs(dev, ABS_X + i, val);
+		} else {
+			val = (int) (((int8_t) lo) | ((int8_t) hi << 8));
+			if (i % 3)
+				val *= -1;
+			input_report_rel(dev, REL_X + i, val);
+		}
+	}
+
+	while (cnt < idx - 1) {
+		unsigned int btn = ptr->data[cnt++];
+		int up = btn & 1;
+
+		btn &= 0xfe;
+		if (btn == 0x8e)
+			continue; /* TODO: proximity == touch? */
+		if (btn > 0x8c || btn < 0x80)
+			continue;
+		btn = (btn - 0x80) >> 1;
+		btn = ptr->btnmap[btn];
+		input_report_key(dev, btn, !up);
+	}
+
+	input_sync(dev);
+}
+
+static void hil_dev_process_err(struct hil_dev *dev)
+{
+	printk(KERN_WARNING PREFIX "errored HIL packet\n");
+	dev->idx4 = 0;
+	complete(&dev->cmd_done); /* just in case somebody is waiting */
+}
+
+static irqreturn_t hil_dev_interrupt(struct serio *serio,
+				unsigned char data, unsigned int flags)
+{
+	struct hil_dev *dev;
+	hil_packet packet;
+	int idx;
+
+	dev = serio_get_drvdata(serio);
+	BUG_ON(dev == NULL);
+
+	if (dev->idx4 >= HIL_PACKET_MAX_LENGTH * sizeof(hil_packet)) {
+		hil_dev_process_err(dev);
+		goto out;
+	}
+
+	idx = dev->idx4 / 4;
+	if (!(dev->idx4 % 4))
+		dev->data[idx] = 0;
+	packet = dev->data[idx];
+	packet |= ((hil_packet)data) << ((3 - (dev->idx4 % 4)) * 8);
+	dev->data[idx] = packet;
+
+	/* Records of N 4-byte hil_packets must terminate with a command. */
+	if ((++dev->idx4 % 4) == 0) {
+		if ((packet & 0xffff0000) != HIL_ERR_INT) {
+			hil_dev_process_err(dev);
+		} else if (packet & HIL_PKT_CMD) {
+			if (hil_dev_is_command_response(packet))
+				hil_dev_handle_command_response(dev);
+			else if (dev->is_pointer)
+				hil_dev_handle_ptr_events(dev);
+			else
+				hil_dev_handle_kbd_events(dev);
+			dev->idx4 = 0;
+		}
+	}
+ out:
+	return IRQ_HANDLED;
+}
+
+static void hil_dev_disconnect(struct serio *serio)
+{
+	struct hil_dev *dev = serio_get_drvdata(serio);
+
+	BUG_ON(dev == NULL);
+
+	serio_close(serio);
+	input_unregister_device(dev->dev);
+	serio_set_drvdata(serio, NULL);
+	kfree(dev);
+}
+
+static void hil_dev_keyboard_setup(struct hil_dev *kbd)
+{
+	struct input_dev *input_dev = kbd->dev;
+	uint8_t did = kbd->idd[0];
+	int i;
+
+	input_dev->evbit[0]	= BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+	input_dev->ledbit[0]	= BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
+				  BIT_MASK(LED_SCROLLL);
+
+	for (i = 0; i < 128; i++) {
+		__set_bit(hil_kbd_set1[i], input_dev->keybit);
+		__set_bit(hil_kbd_set3[i], input_dev->keybit);
+	}
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+
+	input_dev->keycodemax	= HIL_KEYCODES_SET1_TBLSIZE;
+	input_dev->keycodesize	= sizeof(hil_kbd_set1[0]);
+	input_dev->keycode	= hil_kbd_set1;
+
+	input_dev->name	= strlen(kbd->rnm) ? kbd->rnm : "HIL keyboard";
+	input_dev->phys	= "hpkbd/input0";
+
+	printk(KERN_INFO PREFIX "HIL keyboard found (did = 0x%02x, lang = %s)\n",
+		did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]);
+}
+
+static void hil_dev_pointer_setup(struct hil_dev *ptr)
+{
+	struct input_dev *input_dev = ptr->dev;
+	uint8_t did = ptr->idd[0];
+	uint8_t *idd = ptr->idd + 1;
+	unsigned int naxsets = HIL_IDD_NUM_AXSETS(*idd);
+	unsigned int i, btntype;
+	const char *txt;
+
+	ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd);
+
+	switch (did & HIL_IDD_DID_TYPE_MASK) {
+	case HIL_IDD_DID_TYPE_REL:
+		input_dev->evbit[0] = BIT_MASK(EV_REL);
+
+		for (i = 0; i < ptr->naxes; i++)
+			__set_bit(REL_X + i, input_dev->relbit);
+
+		for (i = 3; naxsets > 1 && i < ptr->naxes + 3; i++)
+			__set_bit(REL_X + i, input_dev->relbit);
+
+		txt = "relative";
+		break;
+
+	case HIL_IDD_DID_TYPE_ABS:
+		input_dev->evbit[0] = BIT_MASK(EV_ABS);
+
+		for (i = 0; i < ptr->naxes; i++)
+			input_set_abs_params(input_dev, ABS_X + i,
+					0, HIL_IDD_AXIS_MAX(idd, i), 0, 0);
+
+		for (i = 3; naxsets > 1 && i < ptr->naxes + 3; i++)
+			input_set_abs_params(input_dev, ABS_X + i,
+					0, HIL_IDD_AXIS_MAX(idd, i - 3), 0, 0);
+
+#ifdef TABLET_AUTOADJUST
+		for (i = 0; i < ABS_MAX; i++) {
+			int diff = input_abs_get_max(input_dev, ABS_X + i) / 10;
+			input_abs_set_min(input_dev, ABS_X + i,
+				input_abs_get_min(input_dev, ABS_X + i) + diff);
+			input_abs_set_max(input_dev, ABS_X + i,
+				input_abs_get_max(input_dev, ABS_X + i) - diff);
+		}
+#endif
+
+		txt = "absolute";
+		break;
+
+	default:
+		BUG();
+	}
+
+	ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd);
+	if (ptr->nbtn)
+		input_dev->evbit[0] |= BIT_MASK(EV_KEY);
+
+	btntype = BTN_MISC;
+	if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET)
+#ifdef TABLET_SIMULATES_MOUSE
+		btntype = BTN_TOUCH;
+#else
+		btntype = BTN_DIGI;
+#endif
+	if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN)
+		btntype = BTN_TOUCH;
+
+	if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE)
+		btntype = BTN_MOUSE;
+
+	for (i = 0; i < ptr->nbtn; i++) {
+		__set_bit(btntype | i, input_dev->keybit);
+		ptr->btnmap[i] = btntype | i;
+	}
+
+	if (btntype == BTN_MOUSE) {
+		/* Swap buttons 2 and 3 */
+		ptr->btnmap[1] = BTN_MIDDLE;
+		ptr->btnmap[2] = BTN_RIGHT;
+	}
+
+	input_dev->name = strlen(ptr->rnm) ? ptr->rnm : "HIL pointer device";
+
+	printk(KERN_INFO PREFIX
+		"HIL pointer device found (did: 0x%02x, axis: %s)\n",
+		did, txt);
+	printk(KERN_INFO PREFIX
+		"HIL pointer has %i buttons and %i sets of %i axes\n",
+		ptr->nbtn, naxsets, ptr->naxes);
+}
+
+static int hil_dev_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct hil_dev *dev;
+	struct input_dev *input_dev;
+	uint8_t did, *idd;
+	int error;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!dev || !input_dev) {
+		error = -ENOMEM;
+		goto bail0;
+	}
+
+	dev->serio = serio;
+	dev->dev = input_dev;
+
+	error = serio_open(serio, drv);
+	if (error)
+		goto bail0;
+
+	serio_set_drvdata(serio, dev);
+
+	/* Get device info.  MLC driver supplies devid/status/etc. */
+	init_completion(&dev->cmd_done);
+	serio_write(serio, 0);
+	serio_write(serio, 0);
+	serio_write(serio, HIL_PKT_CMD >> 8);
+	serio_write(serio, HIL_CMD_IDD);
+	error = wait_for_completion_killable(&dev->cmd_done);
+	if (error)
+		goto bail1;
+
+	reinit_completion(&dev->cmd_done);
+	serio_write(serio, 0);
+	serio_write(serio, 0);
+	serio_write(serio, HIL_PKT_CMD >> 8);
+	serio_write(serio, HIL_CMD_RSC);
+	error = wait_for_completion_killable(&dev->cmd_done);
+	if (error)
+		goto bail1;
+
+	reinit_completion(&dev->cmd_done);
+	serio_write(serio, 0);
+	serio_write(serio, 0);
+	serio_write(serio, HIL_PKT_CMD >> 8);
+	serio_write(serio, HIL_CMD_RNM);
+	error = wait_for_completion_killable(&dev->cmd_done);
+	if (error)
+		goto bail1;
+
+	reinit_completion(&dev->cmd_done);
+	serio_write(serio, 0);
+	serio_write(serio, 0);
+	serio_write(serio, HIL_PKT_CMD >> 8);
+	serio_write(serio, HIL_CMD_EXD);
+	error = wait_for_completion_killable(&dev->cmd_done);
+	if (error)
+		goto bail1;
+
+	did = dev->idd[0];
+	idd = dev->idd + 1;
+
+	switch (did & HIL_IDD_DID_TYPE_MASK) {
+	case HIL_IDD_DID_TYPE_KB_INTEGRAL:
+	case HIL_IDD_DID_TYPE_KB_ITF:
+	case HIL_IDD_DID_TYPE_KB_RSVD:
+	case HIL_IDD_DID_TYPE_CHAR:
+		if (HIL_IDD_NUM_BUTTONS(idd) ||
+		    HIL_IDD_NUM_AXES_PER_SET(*idd)) {
+			printk(KERN_INFO PREFIX
+				"combo devices are not supported.\n");
+			goto bail1;
+		}
+
+		dev->is_pointer = false;
+		hil_dev_keyboard_setup(dev);
+		break;
+
+	case HIL_IDD_DID_TYPE_REL:
+	case HIL_IDD_DID_TYPE_ABS:
+		dev->is_pointer = true;
+		hil_dev_pointer_setup(dev);
+		break;
+
+	default:
+		goto bail1;
+	}
+
+	input_dev->id.bustype	= BUS_HIL;
+	input_dev->id.vendor	= PCI_VENDOR_ID_HP;
+	input_dev->id.product	= 0x0001; /* TODO: get from kbd->rsc */
+	input_dev->id.version	= 0x0100; /* TODO: get from kbd->rsc */
+	input_dev->dev.parent	= &serio->dev;
+
+	if (!dev->is_pointer) {
+		serio_write(serio, 0);
+		serio_write(serio, 0);
+		serio_write(serio, HIL_PKT_CMD >> 8);
+		/* Enable Keyswitch Autorepeat 1 */
+		serio_write(serio, HIL_CMD_EK1);
+		/* No need to wait for completion */
+	}
+
+	error = input_register_device(input_dev);
+	if (error)
+		goto bail1;
+
+	return 0;
+
+ bail1:
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+ bail0:
+	input_free_device(input_dev);
+	kfree(dev);
+	return error;
+}
+
+static struct serio_device_id hil_dev_ids[] = {
+	{
+		.type = SERIO_HIL_MLC,
+		.proto = SERIO_HIL,
+		.id = SERIO_ANY,
+		.extra = SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, hil_dev_ids);
+
+static struct serio_driver hil_serio_drv = {
+	.driver		= {
+		.name	= "hil_dev",
+	},
+	.description	= "HP HIL keyboard/mouse/tablet driver",
+	.id_table	= hil_dev_ids,
+	.connect	= hil_dev_connect,
+	.disconnect	= hil_dev_disconnect,
+	.interrupt	= hil_dev_interrupt
+};
+
+module_serio_driver(hil_serio_drv);
diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c
new file mode 100644
index 0000000..198dc07
--- /dev/null
+++ b/drivers/input/keyboard/hilkbd.c
@@ -0,0 +1,398 @@
+/*
+ *  linux/drivers/hil/hilkbd.c
+ *
+ *  Copyright (C) 1998 Philip Blundell <philb@gnu.org>
+ *  Copyright (C) 1999 Matthew Wilcox <willy@bofh.ai>
+ *  Copyright (C) 1999-2007 Helge Deller <deller@gmx.de>
+ *
+ *  Very basic HP Human Interface Loop (HIL) driver.
+ *  This driver handles the keyboard on HP300 (m68k) and on some
+ *  HP700 (parisc) series machines.
+ *
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2.  See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+#include <linux/pci_ids.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hil.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <asm/irq.h>
+#ifdef CONFIG_HP300
+#include <asm/hwtest.h>
+#endif
+
+
+MODULE_AUTHOR("Philip Blundell, Matthew Wilcox, Helge Deller");
+MODULE_DESCRIPTION("HIL keyboard driver (basic functionality)");
+MODULE_LICENSE("GPL v2");
+
+
+#if defined(CONFIG_PARISC)
+
+ #include <asm/io.h>
+ #include <asm/hardware.h>
+ #include <asm/parisc-device.h>
+ static unsigned long hil_base;	/* HPA for the HIL device */
+ static unsigned int hil_irq;
+ #define HILBASE		hil_base /* HPPA (parisc) port address */
+ #define HIL_DATA		0x800
+ #define HIL_CMD		0x801
+ #define HIL_IRQ		hil_irq
+ #define hil_readb(p)		gsc_readb(p)
+ #define hil_writeb(v,p)	gsc_writeb((v),(p))
+
+#elif defined(CONFIG_HP300)
+
+ #define HILBASE		0xf0428000UL /* HP300 (m68k) port address */
+ #define HIL_DATA		0x1
+ #define HIL_CMD		0x3
+ #define HIL_IRQ		2
+ #define hil_readb(p)		readb(p)
+ #define hil_writeb(v,p)	writeb((v),(p))
+
+#else
+#error "HIL is not supported on this platform"
+#endif
+
+
+
+/* HIL helper functions */
+
+#define hil_busy()              (hil_readb(HILBASE + HIL_CMD) & HIL_BUSY)
+#define hil_data_available()    (hil_readb(HILBASE + HIL_CMD) & HIL_DATA_RDY)
+#define hil_status()            (hil_readb(HILBASE + HIL_CMD))
+#define hil_command(x)          do { hil_writeb((x), HILBASE + HIL_CMD); } while (0)
+#define hil_read_data()         (hil_readb(HILBASE + HIL_DATA))
+#define hil_write_data(x)       do { hil_writeb((x), HILBASE + HIL_DATA); } while (0)
+
+/* HIL constants */
+
+#define	HIL_BUSY		0x02
+#define	HIL_DATA_RDY		0x01
+
+#define	HIL_SETARD		0xA0		/* set auto-repeat delay */
+#define	HIL_SETARR		0xA2		/* set auto-repeat rate */
+#define	HIL_SETTONE		0xA3		/* set tone generator */
+#define	HIL_CNMT		0xB2		/* clear nmi */
+#define	HIL_INTON		0x5C		/* Turn on interrupts. */
+#define	HIL_INTOFF		0x5D		/* Turn off interrupts. */
+
+#define	HIL_READKBDSADR		0xF9
+#define	HIL_WRITEKBDSADR	0xE9
+
+static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly =
+	{ HIL_KEYCODES_SET1 };
+
+/* HIL structure */
+static struct {
+	struct input_dev *dev;
+
+	unsigned int curdev;
+
+	unsigned char s;
+	unsigned char c;
+	int valid;
+
+	unsigned char data[16];
+	unsigned int ptr;
+	spinlock_t lock;
+
+	void *dev_id;	/* native bus device */
+} hil_dev;
+
+
+static void poll_finished(void)
+{
+	int down;
+	int key;
+	unsigned char scode;
+
+	switch (hil_dev.data[0]) {
+	case 0x40:
+		down = (hil_dev.data[1] & 1) == 0;
+		scode = hil_dev.data[1] >> 1;
+		key = hphilkeyb_keycode[scode];
+		input_report_key(hil_dev.dev, key, down);
+		break;
+	}
+	hil_dev.curdev = 0;
+}
+
+
+static inline void handle_status(unsigned char s, unsigned char c)
+{
+	if (c & 0x8) {
+		/* End of block */
+		if (c & 0x10)
+			poll_finished();
+	} else {
+		if (c & 0x10) {
+			if (hil_dev.curdev)
+				poll_finished();  /* just in case */
+			hil_dev.curdev = c & 7;
+			hil_dev.ptr = 0;
+		}
+	}
+}
+
+
+static inline void handle_data(unsigned char s, unsigned char c)
+{
+	if (hil_dev.curdev) {
+		hil_dev.data[hil_dev.ptr++] = c;
+		hil_dev.ptr &= 15;
+	}
+}
+
+
+/* handle HIL interrupts */
+static irqreturn_t hil_interrupt(int irq, void *handle)
+{
+	unsigned char s, c;
+
+	s = hil_status();
+	c = hil_read_data();
+
+	switch (s >> 4) {
+	case 0x5:
+		handle_status(s, c);
+		break;
+	case 0x6:
+		handle_data(s, c);
+		break;
+	case 0x4:
+		hil_dev.s = s;
+		hil_dev.c = c;
+		mb();
+		hil_dev.valid = 1;
+		break;
+	}
+	return IRQ_HANDLED;
+}
+
+
+/* send a command to the HIL */
+static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hil_dev.lock, flags);
+	while (hil_busy())
+		/* wait */;
+	hil_command(cmd);
+	while (len--) {
+		while (hil_busy())
+			/* wait */;
+		hil_write_data(*(data++));
+	}
+	spin_unlock_irqrestore(&hil_dev.lock, flags);
+}
+
+
+/* initialize HIL */
+static int hil_keyb_init(void)
+{
+	unsigned char c;
+	unsigned int i, kbid;
+	wait_queue_head_t hil_wait;
+	int err;
+
+	if (hil_dev.dev)
+		return -ENODEV; /* already initialized */
+
+	init_waitqueue_head(&hil_wait);
+	spin_lock_init(&hil_dev.lock);
+
+	hil_dev.dev = input_allocate_device();
+	if (!hil_dev.dev)
+		return -ENOMEM;
+
+	err = request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id);
+	if (err) {
+		printk(KERN_ERR "HIL: Can't get IRQ\n");
+		goto err1;
+	}
+
+	/* Turn on interrupts */
+	hil_do(HIL_INTON, NULL, 0);
+
+	/* Look for keyboards */
+	hil_dev.valid = 0;	/* clear any pending data */
+	hil_do(HIL_READKBDSADR, NULL, 0);
+
+	wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3 * HZ);
+	if (!hil_dev.valid)
+		printk(KERN_WARNING "HIL: timed out, assuming no keyboard present\n");
+
+	c = hil_dev.c;
+	hil_dev.valid = 0;
+	if (c == 0) {
+		kbid = -1;
+		printk(KERN_WARNING "HIL: no keyboard present\n");
+	} else {
+		kbid = ffz(~c);
+		printk(KERN_INFO "HIL: keyboard found at id %d\n", kbid);
+	}
+
+	/* set it to raw mode */
+	c = 0;
+	hil_do(HIL_WRITEKBDSADR, &c, 1);
+
+	for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++)
+		if (hphilkeyb_keycode[i] != KEY_RESERVED)
+			__set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit);
+
+	hil_dev.dev->evbit[0]	= BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+	hil_dev.dev->ledbit[0]	= BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
+		BIT_MASK(LED_SCROLLL);
+	hil_dev.dev->keycodemax	= HIL_KEYCODES_SET1_TBLSIZE;
+	hil_dev.dev->keycodesize= sizeof(hphilkeyb_keycode[0]);
+	hil_dev.dev->keycode	= hphilkeyb_keycode;
+	hil_dev.dev->name	= "HIL keyboard";
+	hil_dev.dev->phys	= "hpkbd/input0";
+
+	hil_dev.dev->id.bustype	= BUS_HIL;
+	hil_dev.dev->id.vendor	= PCI_VENDOR_ID_HP;
+	hil_dev.dev->id.product	= 0x0001;
+	hil_dev.dev->id.version	= 0x0010;
+
+	err = input_register_device(hil_dev.dev);
+	if (err) {
+		printk(KERN_ERR "HIL: Can't register device\n");
+		goto err2;
+	}
+
+	printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n",
+	       hil_dev.dev->name, kbid, HILBASE, HIL_IRQ);
+
+	return 0;
+
+err2:
+	hil_do(HIL_INTOFF, NULL, 0);
+	free_irq(HIL_IRQ, hil_dev.dev_id);
+err1:
+	input_free_device(hil_dev.dev);
+	hil_dev.dev = NULL;
+	return err;
+}
+
+static void hil_keyb_exit(void)
+{
+	if (HIL_IRQ)
+		free_irq(HIL_IRQ, hil_dev.dev_id);
+
+	/* Turn off interrupts */
+	hil_do(HIL_INTOFF, NULL, 0);
+
+	input_unregister_device(hil_dev.dev);
+	hil_dev.dev = NULL;
+}
+
+#if defined(CONFIG_PARISC)
+static int hil_probe_chip(struct parisc_device *dev)
+{
+	/* Only allow one HIL keyboard */
+	if (hil_dev.dev)
+		return -ENODEV;
+
+	if (!dev->irq) {
+		printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%p\n",
+			(void *)dev->hpa.start);
+		return -ENODEV;
+	}
+
+	hil_base = dev->hpa.start;
+	hil_irq  = dev->irq;
+	hil_dev.dev_id = dev;
+
+	printk(KERN_INFO "Found HIL bus at 0x%08lx, IRQ %d\n", hil_base, hil_irq);
+
+	return hil_keyb_init();
+}
+
+static int hil_remove_chip(struct parisc_device *dev)
+{
+	hil_keyb_exit();
+
+	return 0;
+}
+
+static struct parisc_device_id hil_tbl[] = {
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00073 },
+	{ 0, }
+};
+
+#if 0
+/* Disabled to avoid conflicts with the HP SDC HIL drivers */
+MODULE_DEVICE_TABLE(parisc, hil_tbl);
+#endif
+
+static struct parisc_driver hil_driver = {
+	.name		= "hil",
+	.id_table	= hil_tbl,
+	.probe		= hil_probe_chip,
+	.remove		= hil_remove_chip,
+};
+
+static int __init hil_init(void)
+{
+	return register_parisc_driver(&hil_driver);
+}
+
+static void __exit hil_exit(void)
+{
+	unregister_parisc_driver(&hil_driver);
+}
+
+#else /* !CONFIG_PARISC */
+
+static int __init hil_init(void)
+{
+	int error;
+
+	/* Only allow one HIL keyboard */
+	if (hil_dev.dev)
+		return -EBUSY;
+
+	if (!MACH_IS_HP300)
+		return -ENODEV;
+
+	if (!hwreg_present((void *)(HILBASE + HIL_DATA))) {
+		printk(KERN_ERR "HIL: hardware register was not found\n");
+		return -ENODEV;
+	}
+
+	if (!request_region(HILBASE + HIL_DATA, 2, "hil")) {
+		printk(KERN_ERR "HIL: IOPORT region already used\n");
+		return -EIO;
+	}
+
+	error = hil_keyb_init();
+	if (error) {
+		release_region(HILBASE + HIL_DATA, 2);
+		return error;
+	}
+
+	return 0;
+}
+
+static void __exit hil_exit(void)
+{
+	hil_keyb_exit();
+	release_region(HILBASE + HIL_DATA, 2);
+}
+
+#endif /* CONFIG_PARISC */
+
+module_init(hil_init);
+module_exit(hil_exit);
diff --git a/drivers/input/keyboard/hpps2atkbd.h b/drivers/input/keyboard/hpps2atkbd.h
new file mode 100644
index 0000000..dc33f69
--- /dev/null
+++ b/drivers/input/keyboard/hpps2atkbd.h
@@ -0,0 +1,110 @@
+/*
+ * drivers/input/keyboard/hpps2atkbd.h
+ *
+ * Copyright (c) 2004 Helge Deller <deller@gmx.de>
+ * Copyright (c) 2002 Laurent Canet <canetl@esiee.fr>
+ * Copyright (c) 2002 Thibaut Varene <varenet@parisc-linux.org>
+ * Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr>
+ *
+ * HP PS/2 AT-compatible Keyboard, found in PA/RISC Workstations & Laptops
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+
+/* Is the keyboard an RDI PrecisionBook? */
+#ifndef CONFIG_KEYBOARD_ATKBD_RDI_KEYCODES
+# define CONFLICT(x,y) x
+#else
+# define CONFLICT(x,y) y
+#endif
+
+/* sadly RDI (Tadpole) decided to ship a different keyboard layout
+   than HP for their PS/2 laptop keyboard which leads to conflicting
+   keycodes between a normal HP PS/2 keyboard and a RDI Precisionbook.
+                                HP:		RDI:            */
+#define C_07	CONFLICT(	KEY_F12,	KEY_F1		)
+#define C_11	CONFLICT(	KEY_LEFTALT,	KEY_LEFTCTRL	)
+#define C_14	CONFLICT(	KEY_LEFTCTRL,	KEY_CAPSLOCK	)
+#define C_58	CONFLICT(	KEY_CAPSLOCK,	KEY_RIGHTCTRL	)
+#define C_61	CONFLICT(	KEY_102ND,	KEY_LEFT	)
+
+/* Raw SET 2 scancode table */
+
+/* 00 */  KEY_RESERVED, KEY_F9,        KEY_RESERVED,  KEY_F5,        KEY_F3,        KEY_F1,       KEY_F2,        C_07,
+/* 08 */  KEY_ESC,      KEY_F10,       KEY_F8,        KEY_F6,        KEY_F4,        KEY_TAB,      KEY_GRAVE,     KEY_F2,
+/* 10 */  KEY_RESERVED, C_11,          KEY_LEFTSHIFT, KEY_RESERVED,  C_14,          KEY_Q,        KEY_1,         KEY_F3,
+/* 18 */  KEY_RESERVED, KEY_LEFTALT,   KEY_Z,         KEY_S,         KEY_A,         KEY_W,        KEY_2,         KEY_F4,
+/* 20 */  KEY_RESERVED, KEY_C,         KEY_X,         KEY_D,         KEY_E,         KEY_4,        KEY_3,         KEY_F5,
+/* 28 */  KEY_RESERVED, KEY_SPACE,     KEY_V,         KEY_F,         KEY_T,         KEY_R,        KEY_5,         KEY_F6,
+/* 30 */  KEY_RESERVED, KEY_N,         KEY_B,         KEY_H,         KEY_G,         KEY_Y,        KEY_6,         KEY_F7,
+/* 38 */  KEY_RESERVED, KEY_RIGHTALT,  KEY_M,         KEY_J,         KEY_U,         KEY_7,        KEY_8,         KEY_F8,
+/* 40 */  KEY_RESERVED, KEY_COMMA,     KEY_K,         KEY_I,         KEY_O,         KEY_0,        KEY_9,         KEY_F9,
+/* 48 */  KEY_RESERVED, KEY_DOT,       KEY_SLASH,     KEY_L,         KEY_SEMICOLON, KEY_P,        KEY_MINUS,     KEY_F10,
+/* 50 */  KEY_RESERVED, KEY_RESERVED,  KEY_APOSTROPHE,KEY_RESERVED,  KEY_LEFTBRACE, KEY_EQUAL,    KEY_F11,       KEY_SYSRQ,
+/* 58 */  C_58,         KEY_RIGHTSHIFT,KEY_ENTER,     KEY_RIGHTBRACE,KEY_BACKSLASH, KEY_BACKSLASH,KEY_F12,       KEY_SCROLLLOCK,
+/* 60 */  KEY_DOWN,     C_61,          KEY_PAUSE,     KEY_UP,        KEY_DELETE,    KEY_END,      KEY_BACKSPACE, KEY_INSERT,
+/* 68 */  KEY_RESERVED, KEY_KP1,       KEY_RIGHT,     KEY_KP4,       KEY_KP7,       KEY_PAGEDOWN, KEY_HOME,      KEY_PAGEUP,
+/* 70 */  KEY_KP0,      KEY_KPDOT,     KEY_KP2,       KEY_KP5,       KEY_KP6,       KEY_KP8,      KEY_ESC,       KEY_NUMLOCK,
+/* 78 */  KEY_F11,      KEY_KPPLUS,    KEY_KP3,       KEY_KPMINUS,   KEY_KPASTERISK,KEY_KP9,      KEY_SCROLLLOCK,KEY_102ND,
+/* 80 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 88 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 90 */  KEY_RESERVED, KEY_RIGHTALT,  255,           KEY_RESERVED,  KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 98 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_CAPSLOCK, KEY_RESERVED,  KEY_LEFTMETA,
+/* a0 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RIGHTMETA,
+/* a8 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_COMPOSE,
+/* b0 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* b8 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* c0 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* c8 */  KEY_RESERVED, KEY_RESERVED,  KEY_KPSLASH,   KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* d0 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* d8 */  KEY_RESERVED, KEY_RESERVED,  KEY_KPENTER,   KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* e0 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* e8 */  KEY_RESERVED, KEY_END,       KEY_RESERVED,  KEY_LEFT,      KEY_HOME,      KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* f0 */  KEY_INSERT,   KEY_DELETE,    KEY_DOWN,      KEY_RESERVED,  KEY_RIGHT,     KEY_UP,       KEY_RESERVED,  KEY_PAUSE,
+/* f8 */  KEY_RESERVED, KEY_RESERVED,  KEY_PAGEDOWN,  KEY_RESERVED,  KEY_SYSRQ,     KEY_PAGEUP,   KEY_RESERVED,  KEY_RESERVED,
+
+/* These are offset for escaped keycodes: */
+
+/* 00 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_F7,        KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 08 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_LEFTMETA,  KEY_RIGHTMETA, KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 10 */  KEY_RESERVED, KEY_RIGHTALT,  KEY_RESERVED,  KEY_RESERVED,  KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 18 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 20 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 28 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 30 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 38 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 40 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 48 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 50 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 58 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 60 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 68 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 70 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 78 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 80 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 88 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 90 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* 98 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* a0 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* a8 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* b0 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* b8 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* c0 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* c8 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* d0 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* d8 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* e0 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* e8 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* f0 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,
+/* f8 */  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED,  KEY_RESERVED, KEY_RESERVED,  KEY_RESERVED
+
+#undef CONFLICT
+#undef C_07
+#undef C_11
+#undef C_14
+#undef C_58
+#undef C_61
+
diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c
new file mode 100644
index 0000000..2165f3d
--- /dev/null
+++ b/drivers/input/keyboard/imx_keypad.c
@@ -0,0 +1,592 @@
+/*
+ * Driver for the IMX keypad port.
+ * Copyright (C) 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+
+/*
+ * Keypad Controller registers (halfword)
+ */
+#define KPCR		0x00 /* Keypad Control Register */
+
+#define KPSR		0x02 /* Keypad Status Register */
+#define KBD_STAT_KPKD	(0x1 << 0) /* Key Press Interrupt Status bit (w1c) */
+#define KBD_STAT_KPKR	(0x1 << 1) /* Key Release Interrupt Status bit (w1c) */
+#define KBD_STAT_KDSC	(0x1 << 2) /* Key Depress Synch Chain Status bit (w1c)*/
+#define KBD_STAT_KRSS	(0x1 << 3) /* Key Release Synch Status bit (w1c)*/
+#define KBD_STAT_KDIE	(0x1 << 8) /* Key Depress Interrupt Enable Status bit */
+#define KBD_STAT_KRIE	(0x1 << 9) /* Key Release Interrupt Enable */
+#define KBD_STAT_KPPEN	(0x1 << 10) /* Keypad Clock Enable */
+
+#define KDDR		0x04 /* Keypad Data Direction Register */
+#define KPDR		0x06 /* Keypad Data Register */
+
+#define MAX_MATRIX_KEY_ROWS	8
+#define MAX_MATRIX_KEY_COLS	8
+#define MATRIX_ROW_SHIFT	3
+
+#define MAX_MATRIX_KEY_NUM	(MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS)
+
+struct imx_keypad {
+
+	struct clk *clk;
+	struct input_dev *input_dev;
+	void __iomem *mmio_base;
+
+	int			irq;
+	struct timer_list	check_matrix_timer;
+
+	/*
+	 * The matrix is stable only if no changes are detected after
+	 * IMX_KEYPAD_SCANS_FOR_STABILITY scans
+	 */
+#define IMX_KEYPAD_SCANS_FOR_STABILITY 3
+	int			stable_count;
+
+	bool			enabled;
+
+	/* Masks for enabled rows/cols */
+	unsigned short		rows_en_mask;
+	unsigned short		cols_en_mask;
+
+	unsigned short		keycodes[MAX_MATRIX_KEY_NUM];
+
+	/*
+	 * Matrix states:
+	 * -stable: achieved after a complete debounce process.
+	 * -unstable: used in the debouncing process.
+	 */
+	unsigned short		matrix_stable_state[MAX_MATRIX_KEY_COLS];
+	unsigned short		matrix_unstable_state[MAX_MATRIX_KEY_COLS];
+};
+
+/* Scan the matrix and return the new state in *matrix_volatile_state. */
+static void imx_keypad_scan_matrix(struct imx_keypad *keypad,
+				  unsigned short *matrix_volatile_state)
+{
+	int col;
+	unsigned short reg_val;
+
+	for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {
+		if ((keypad->cols_en_mask & (1 << col)) == 0)
+			continue;
+		/*
+		 * Discharge keypad capacitance:
+		 * 2. write 1s on column data.
+		 * 3. configure columns as totem-pole to discharge capacitance.
+		 * 4. configure columns as open-drain.
+		 */
+		reg_val = readw(keypad->mmio_base + KPDR);
+		reg_val |= 0xff00;
+		writew(reg_val, keypad->mmio_base + KPDR);
+
+		reg_val = readw(keypad->mmio_base + KPCR);
+		reg_val &= ~((keypad->cols_en_mask & 0xff) << 8);
+		writew(reg_val, keypad->mmio_base + KPCR);
+
+		udelay(2);
+
+		reg_val = readw(keypad->mmio_base + KPCR);
+		reg_val |= (keypad->cols_en_mask & 0xff) << 8;
+		writew(reg_val, keypad->mmio_base + KPCR);
+
+		/*
+		 * 5. Write a single column to 0, others to 1.
+		 * 6. Sample row inputs and save data.
+		 * 7. Repeat steps 2 - 6 for remaining columns.
+		 */
+		reg_val = readw(keypad->mmio_base + KPDR);
+		reg_val &= ~(1 << (8 + col));
+		writew(reg_val, keypad->mmio_base + KPDR);
+
+		/*
+		 * Delay added to avoid propagating the 0 from column to row
+		 * when scanning.
+		 */
+		udelay(5);
+
+		/*
+		 * 1s in matrix_volatile_state[col] means key pressures
+		 * throw data from non enabled rows.
+		 */
+		reg_val = readw(keypad->mmio_base + KPDR);
+		matrix_volatile_state[col] = (~reg_val) & keypad->rows_en_mask;
+	}
+
+	/*
+	 * Return in standby mode:
+	 * 9. write 0s to columns
+	 */
+	reg_val = readw(keypad->mmio_base + KPDR);
+	reg_val &= 0x00ff;
+	writew(reg_val, keypad->mmio_base + KPDR);
+}
+
+/*
+ * Compare the new matrix state (volatile) with the stable one stored in
+ * keypad->matrix_stable_state and fire events if changes are detected.
+ */
+static void imx_keypad_fire_events(struct imx_keypad *keypad,
+				   unsigned short *matrix_volatile_state)
+{
+	struct input_dev *input_dev = keypad->input_dev;
+	int row, col;
+
+	for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {
+		unsigned short bits_changed;
+		int code;
+
+		if ((keypad->cols_en_mask & (1 << col)) == 0)
+			continue; /* Column is not enabled */
+
+		bits_changed = keypad->matrix_stable_state[col] ^
+						matrix_volatile_state[col];
+
+		if (bits_changed == 0)
+			continue; /* Column does not contain changes */
+
+		for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) {
+			if ((keypad->rows_en_mask & (1 << row)) == 0)
+				continue; /* Row is not enabled */
+			if ((bits_changed & (1 << row)) == 0)
+				continue; /* Row does not contain changes */
+
+			code = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT);
+			input_event(input_dev, EV_MSC, MSC_SCAN, code);
+			input_report_key(input_dev, keypad->keycodes[code],
+				matrix_volatile_state[col] & (1 << row));
+			dev_dbg(&input_dev->dev, "Event code: %d, val: %d",
+				keypad->keycodes[code],
+				matrix_volatile_state[col] & (1 << row));
+		}
+	}
+	input_sync(input_dev);
+}
+
+/*
+ * imx_keypad_check_for_events is the timer handler.
+ */
+static void imx_keypad_check_for_events(unsigned long data)
+{
+	struct imx_keypad *keypad = (struct imx_keypad *) data;
+	unsigned short matrix_volatile_state[MAX_MATRIX_KEY_COLS];
+	unsigned short reg_val;
+	bool state_changed, is_zero_matrix;
+	int i;
+
+	memset(matrix_volatile_state, 0, sizeof(matrix_volatile_state));
+
+	imx_keypad_scan_matrix(keypad, matrix_volatile_state);
+
+	state_changed = false;
+	for (i = 0; i < MAX_MATRIX_KEY_COLS; i++) {
+		if ((keypad->cols_en_mask & (1 << i)) == 0)
+			continue;
+
+		if (keypad->matrix_unstable_state[i] ^ matrix_volatile_state[i]) {
+			state_changed = true;
+			break;
+		}
+	}
+
+	/*
+	 * If the matrix state is changed from the previous scan
+	 *   (Re)Begin the debouncing process, saving the new state in
+	 *    keypad->matrix_unstable_state.
+	 * else
+	 *   Increase the count of number of scans with a stable state.
+	 */
+	if (state_changed) {
+		memcpy(keypad->matrix_unstable_state, matrix_volatile_state,
+			sizeof(matrix_volatile_state));
+		keypad->stable_count = 0;
+	} else
+		keypad->stable_count++;
+
+	/*
+	 * If the matrix is not as stable as we want reschedule scan
+	 * in the near future.
+	 */
+	if (keypad->stable_count < IMX_KEYPAD_SCANS_FOR_STABILITY) {
+		mod_timer(&keypad->check_matrix_timer,
+			  jiffies + msecs_to_jiffies(10));
+		return;
+	}
+
+	/*
+	 * If the matrix state is stable, fire the events and save the new
+	 * stable state. Note, if the matrix is kept stable for longer
+	 * (keypad->stable_count > IMX_KEYPAD_SCANS_FOR_STABILITY) all
+	 * events have already been generated.
+	 */
+	if (keypad->stable_count == IMX_KEYPAD_SCANS_FOR_STABILITY) {
+		imx_keypad_fire_events(keypad, matrix_volatile_state);
+
+		memcpy(keypad->matrix_stable_state, matrix_volatile_state,
+			sizeof(matrix_volatile_state));
+	}
+
+	is_zero_matrix = true;
+	for (i = 0; i < MAX_MATRIX_KEY_COLS; i++) {
+		if (matrix_volatile_state[i] != 0) {
+			is_zero_matrix = false;
+			break;
+		}
+	}
+
+
+	if (is_zero_matrix) {
+		/*
+		 * All keys have been released. Enable only the KDI
+		 * interrupt for future key presses (clear the KDI
+		 * status bit and its sync chain before that).
+		 */
+		reg_val = readw(keypad->mmio_base + KPSR);
+		reg_val |= KBD_STAT_KPKD | KBD_STAT_KDSC;
+		writew(reg_val, keypad->mmio_base + KPSR);
+
+		reg_val = readw(keypad->mmio_base + KPSR);
+		reg_val |= KBD_STAT_KDIE;
+		reg_val &= ~KBD_STAT_KRIE;
+		writew(reg_val, keypad->mmio_base + KPSR);
+	} else {
+		/*
+		 * Some keys are still pressed. Schedule a rescan in
+		 * attempt to detect multiple key presses and enable
+		 * the KRI interrupt to react quickly to key release
+		 * event.
+		 */
+		mod_timer(&keypad->check_matrix_timer,
+			  jiffies + msecs_to_jiffies(60));
+
+		reg_val = readw(keypad->mmio_base + KPSR);
+		reg_val |= KBD_STAT_KPKR | KBD_STAT_KRSS;
+		writew(reg_val, keypad->mmio_base + KPSR);
+
+		reg_val = readw(keypad->mmio_base + KPSR);
+		reg_val |= KBD_STAT_KRIE;
+		reg_val &= ~KBD_STAT_KDIE;
+		writew(reg_val, keypad->mmio_base + KPSR);
+	}
+}
+
+static irqreturn_t imx_keypad_irq_handler(int irq, void *dev_id)
+{
+	struct imx_keypad *keypad = dev_id;
+	unsigned short reg_val;
+
+	reg_val = readw(keypad->mmio_base + KPSR);
+
+	/* Disable both interrupt types */
+	reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE);
+	/* Clear interrupts status bits */
+	reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD;
+	writew(reg_val, keypad->mmio_base + KPSR);
+
+	if (keypad->enabled) {
+		/* The matrix is supposed to be changed */
+		keypad->stable_count = 0;
+
+		/* Schedule the scanning procedure near in the future */
+		mod_timer(&keypad->check_matrix_timer,
+			  jiffies + msecs_to_jiffies(2));
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void imx_keypad_config(struct imx_keypad *keypad)
+{
+	unsigned short reg_val;
+
+	/*
+	 * Include enabled rows in interrupt generation (KPCR[7:0])
+	 * Configure keypad columns as open-drain (KPCR[15:8])
+	 */
+	reg_val = readw(keypad->mmio_base + KPCR);
+	reg_val |= keypad->rows_en_mask & 0xff;		/* rows */
+	reg_val |= (keypad->cols_en_mask & 0xff) << 8;	/* cols */
+	writew(reg_val, keypad->mmio_base + KPCR);
+
+	/* Write 0's to KPDR[15:8] (Colums) */
+	reg_val = readw(keypad->mmio_base + KPDR);
+	reg_val &= 0x00ff;
+	writew(reg_val, keypad->mmio_base + KPDR);
+
+	/* Configure columns as output, rows as input (KDDR[15:0]) */
+	writew(0xff00, keypad->mmio_base + KDDR);
+
+	/*
+	 * Clear Key Depress and Key Release status bit.
+	 * Clear both synchronizer chain.
+	 */
+	reg_val = readw(keypad->mmio_base + KPSR);
+	reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD |
+		   KBD_STAT_KDSC | KBD_STAT_KRSS;
+	writew(reg_val, keypad->mmio_base + KPSR);
+
+	/* Enable KDI and disable KRI (avoid false release events). */
+	reg_val |= KBD_STAT_KDIE;
+	reg_val &= ~KBD_STAT_KRIE;
+	writew(reg_val, keypad->mmio_base + KPSR);
+}
+
+static void imx_keypad_inhibit(struct imx_keypad *keypad)
+{
+	unsigned short reg_val;
+
+	/* Inhibit KDI and KRI interrupts. */
+	reg_val = readw(keypad->mmio_base + KPSR);
+	reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE);
+	reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD;
+	writew(reg_val, keypad->mmio_base + KPSR);
+
+	/* Colums as open drain and disable all rows */
+	reg_val = (keypad->cols_en_mask & 0xff) << 8;
+	writew(reg_val, keypad->mmio_base + KPCR);
+}
+
+static void imx_keypad_close(struct input_dev *dev)
+{
+	struct imx_keypad *keypad = input_get_drvdata(dev);
+
+	dev_dbg(&dev->dev, ">%s\n", __func__);
+
+	/* Mark keypad as being inactive */
+	keypad->enabled = false;
+	synchronize_irq(keypad->irq);
+	del_timer_sync(&keypad->check_matrix_timer);
+
+	imx_keypad_inhibit(keypad);
+
+	/* Disable clock unit */
+	clk_disable_unprepare(keypad->clk);
+}
+
+static int imx_keypad_open(struct input_dev *dev)
+{
+	struct imx_keypad *keypad = input_get_drvdata(dev);
+	int error;
+
+	dev_dbg(&dev->dev, ">%s\n", __func__);
+
+	/* Enable the kpp clock */
+	error = clk_prepare_enable(keypad->clk);
+	if (error)
+		return error;
+
+	/* We became active from now */
+	keypad->enabled = true;
+
+	imx_keypad_config(keypad);
+
+	/* Sanity control, not all the rows must be actived now. */
+	if ((readw(keypad->mmio_base + KPDR) & keypad->rows_en_mask) == 0) {
+		dev_err(&dev->dev,
+			"too many keys pressed, control pins initialisation\n");
+		goto open_err;
+	}
+
+	return 0;
+
+open_err:
+	imx_keypad_close(dev);
+	return -EIO;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id imx_keypad_of_match[] = {
+	{ .compatible = "fsl,imx21-kpp", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_keypad_of_match);
+#endif
+
+static int imx_keypad_probe(struct platform_device *pdev)
+{
+	const struct matrix_keymap_data *keymap_data =
+			dev_get_platdata(&pdev->dev);
+	struct imx_keypad *keypad;
+	struct input_dev *input_dev;
+	struct resource *res;
+	int irq, error, i, row, col;
+
+	if (!keymap_data && !pdev->dev.of_node) {
+		dev_err(&pdev->dev, "no keymap defined\n");
+		return -EINVAL;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq defined in platform data\n");
+		return irq;
+	}
+
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!input_dev) {
+		dev_err(&pdev->dev, "failed to allocate the input device\n");
+		return -ENOMEM;
+	}
+
+	keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
+	if (!keypad) {
+		dev_err(&pdev->dev, "not enough memory for driver data\n");
+		return -ENOMEM;
+	}
+
+	keypad->input_dev = input_dev;
+	keypad->irq = irq;
+	keypad->stable_count = 0;
+
+	setup_timer(&keypad->check_matrix_timer,
+		    imx_keypad_check_for_events, (unsigned long) keypad);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(keypad->mmio_base))
+		return PTR_ERR(keypad->mmio_base);
+
+	keypad->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(keypad->clk)) {
+		dev_err(&pdev->dev, "failed to get keypad clock\n");
+		return PTR_ERR(keypad->clk);
+	}
+
+	/* Init the Input device */
+	input_dev->name = pdev->name;
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->dev.parent = &pdev->dev;
+	input_dev->open = imx_keypad_open;
+	input_dev->close = imx_keypad_close;
+
+	error = matrix_keypad_build_keymap(keymap_data, NULL,
+					   MAX_MATRIX_KEY_ROWS,
+					   MAX_MATRIX_KEY_COLS,
+					   keypad->keycodes, input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to build keymap\n");
+		return error;
+	}
+
+	/* Search for rows and cols enabled */
+	for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) {
+		for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {
+			i = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT);
+			if (keypad->keycodes[i] != KEY_RESERVED) {
+				keypad->rows_en_mask |= 1 << row;
+				keypad->cols_en_mask |= 1 << col;
+			}
+		}
+	}
+	dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask);
+	dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask);
+
+	__set_bit(EV_REP, input_dev->evbit);
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+	input_set_drvdata(input_dev, keypad);
+
+	/* Ensure that the keypad will stay dormant until opened */
+	error = clk_prepare_enable(keypad->clk);
+	if (error)
+		return error;
+	imx_keypad_inhibit(keypad);
+	clk_disable_unprepare(keypad->clk);
+
+	error = devm_request_irq(&pdev->dev, irq, imx_keypad_irq_handler, 0,
+			    pdev->name, keypad);
+	if (error) {
+		dev_err(&pdev->dev, "failed to request IRQ\n");
+		return error;
+	}
+
+	/* Register the input device */
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		return error;
+	}
+
+	platform_set_drvdata(pdev, keypad);
+	device_init_wakeup(&pdev->dev, 1);
+
+	return 0;
+}
+
+static int __maybe_unused imx_kbd_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct imx_keypad *kbd = platform_get_drvdata(pdev);
+	struct input_dev *input_dev = kbd->input_dev;
+
+	/* imx kbd can wake up system even clock is disabled */
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users)
+		clk_disable_unprepare(kbd->clk);
+
+	mutex_unlock(&input_dev->mutex);
+
+	if (device_may_wakeup(&pdev->dev))
+		enable_irq_wake(kbd->irq);
+
+	return 0;
+}
+
+static int __maybe_unused imx_kbd_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct imx_keypad *kbd = platform_get_drvdata(pdev);
+	struct input_dev *input_dev = kbd->input_dev;
+	int ret = 0;
+
+	if (device_may_wakeup(&pdev->dev))
+		disable_irq_wake(kbd->irq);
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users) {
+		ret = clk_prepare_enable(kbd->clk);
+		if (ret)
+			goto err_clk;
+	}
+
+err_clk:
+	mutex_unlock(&input_dev->mutex);
+
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(imx_kbd_pm_ops, imx_kbd_suspend, imx_kbd_resume);
+
+static struct platform_driver imx_keypad_driver = {
+	.driver		= {
+		.name	= "imx-keypad",
+		.pm	= &imx_kbd_pm_ops,
+		.of_match_table = of_match_ptr(imx_keypad_of_match),
+	},
+	.probe		= imx_keypad_probe,
+};
+module_platform_driver(imx_keypad_driver);
+
+MODULE_AUTHOR("Alberto Panizzo <maramaopercheseimorto@gmail.com>");
+MODULE_DESCRIPTION("IMX Keypad Port Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-keypad");
diff --git a/drivers/input/keyboard/ipaq-micro-keys.c b/drivers/input/keyboard/ipaq-micro-keys.c
new file mode 100644
index 0000000..602900d
--- /dev/null
+++ b/drivers/input/keyboard/ipaq-micro-keys.c
@@ -0,0 +1,168 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * h3600 atmel micro companion support, key subdevice
+ * based on previous kernel 2.4 version
+ * Author : Alessandro Gardich <gremlin@gremlin.it>
+ * Author : Linus Walleij <linus.walleij@linaro.org>
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ipaq-micro.h>
+
+struct ipaq_micro_keys {
+	struct ipaq_micro *micro;
+	struct input_dev *input;
+	u16 *codes;
+};
+
+static const u16 micro_keycodes[] = {
+	KEY_RECORD,		/* 1:  Record button			*/
+	KEY_CALENDAR,		/* 2:  Calendar				*/
+	KEY_ADDRESSBOOK,	/* 3:  Contacts (looks like Outlook)	*/
+	KEY_MAIL,		/* 4:  Envelope (Q on older iPAQs)	*/
+	KEY_HOMEPAGE,		/* 5:  Start (looks like swoopy arrow)	*/
+	KEY_UP,			/* 6:  Up				*/
+	KEY_RIGHT,		/* 7:  Right				*/
+	KEY_LEFT,		/* 8:  Left				*/
+	KEY_DOWN,		/* 9:  Down				*/
+};
+
+static void micro_key_receive(void *data, int len, unsigned char *msg)
+{
+	struct ipaq_micro_keys *keys = data;
+	int key, down;
+
+	down = 0x80 & msg[0];
+	key  = 0x7f & msg[0];
+
+	if (key < ARRAY_SIZE(micro_keycodes)) {
+		input_report_key(keys->input, keys->codes[key], down);
+		input_sync(keys->input);
+	}
+}
+
+static void micro_key_start(struct ipaq_micro_keys *keys)
+{
+	spin_lock(&keys->micro->lock);
+	keys->micro->key = micro_key_receive;
+	keys->micro->key_data = keys;
+	spin_unlock(&keys->micro->lock);
+}
+
+static void micro_key_stop(struct ipaq_micro_keys *keys)
+{
+	spin_lock(&keys->micro->lock);
+	keys->micro->key = NULL;
+	keys->micro->key_data = NULL;
+	spin_unlock(&keys->micro->lock);
+}
+
+static int micro_key_open(struct input_dev *input)
+{
+	struct ipaq_micro_keys *keys = input_get_drvdata(input);
+
+	micro_key_start(keys);
+
+	return 0;
+}
+
+static void micro_key_close(struct input_dev *input)
+{
+	struct ipaq_micro_keys *keys = input_get_drvdata(input);
+
+	micro_key_stop(keys);
+}
+
+static int micro_key_probe(struct platform_device *pdev)
+{
+	struct ipaq_micro_keys *keys;
+	int error;
+	int i;
+
+	keys = devm_kzalloc(&pdev->dev, sizeof(*keys), GFP_KERNEL);
+	if (!keys)
+		return -ENOMEM;
+
+	keys->micro = dev_get_drvdata(pdev->dev.parent);
+
+	keys->input = devm_input_allocate_device(&pdev->dev);
+	if (!keys->input)
+		return -ENOMEM;
+
+	keys->input->keycodesize = sizeof(micro_keycodes[0]);
+	keys->input->keycodemax = ARRAY_SIZE(micro_keycodes);
+	keys->codes = devm_kmemdup(&pdev->dev, micro_keycodes,
+			   keys->input->keycodesize * keys->input->keycodemax,
+			   GFP_KERNEL);
+	keys->input->keycode = keys->codes;
+
+	__set_bit(EV_KEY, keys->input->evbit);
+	for (i = 0; i < ARRAY_SIZE(micro_keycodes); i++)
+		__set_bit(micro_keycodes[i], keys->input->keybit);
+
+	keys->input->name = "h3600 micro keys";
+	keys->input->open = micro_key_open;
+	keys->input->close = micro_key_close;
+	input_set_drvdata(keys->input, keys);
+
+	error = input_register_device(keys->input);
+	if (error)
+		return error;
+
+	platform_set_drvdata(pdev, keys);
+	return 0;
+}
+
+static int __maybe_unused micro_key_suspend(struct device *dev)
+{
+	struct ipaq_micro_keys *keys = dev_get_drvdata(dev);
+
+	micro_key_stop(keys);
+
+	return 0;
+}
+
+static int __maybe_unused micro_key_resume(struct device *dev)
+{
+	struct ipaq_micro_keys *keys = dev_get_drvdata(dev);
+	struct input_dev *input = keys->input;
+
+	mutex_lock(&input->mutex);
+
+	if (input->users)
+		micro_key_start(keys);
+
+	mutex_unlock(&input->mutex);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(micro_key_dev_pm_ops,
+			 micro_key_suspend, micro_key_resume);
+
+static struct platform_driver micro_key_device_driver = {
+	.driver = {
+		.name    = "ipaq-micro-keys",
+		.pm	= &micro_key_dev_pm_ops,
+	},
+	.probe   = micro_key_probe,
+};
+module_platform_driver(micro_key_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("driver for iPAQ Atmel micro keys");
+MODULE_ALIAS("platform:ipaq-micro-keys");
diff --git a/drivers/input/keyboard/jornada680_kbd.c b/drivers/input/keyboard/jornada680_kbd.c
new file mode 100644
index 0000000..80c8127
--- /dev/null
+++ b/drivers/input/keyboard/jornada680_kbd.c
@@ -0,0 +1,248 @@
+/*
+ * drivers/input/keyboard/jornada680_kbd.c
+ *
+ * HP Jornada 620/660/680/690 scan keyboard platform driver
+ *  Copyright (C) 2007  Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
+ *
+ * Based on hp680_keyb.c
+ *  Copyright (C) 2006 Paul Mundt
+ *  Copyright (C) 2005 Andriy Skulysh
+ * Split from drivers/input/keyboard/hp600_keyb.c
+ *  Copyright (C) 2000 Yaegashi Takeshi (hp6xx kbd scan routine and translation table)
+ *  Copyright (C) 2000 Niibe Yutaka (HP620 Keyb translation table)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <asm/delay.h>
+#include <asm/io.h>
+
+#define PCCR 0xa4000104
+#define PDCR 0xa4000106
+#define PECR 0xa4000108
+#define PFCR 0xa400010a
+#define PCDR 0xa4000124
+#define PDDR 0xa4000126
+#define PEDR 0xa4000128
+#define PFDR 0xa400012a
+#define PGDR 0xa400012c
+#define PHDR 0xa400012e
+#define PJDR 0xa4000130
+#define PKDR 0xa4000132
+#define PLDR 0xa4000134
+
+static const unsigned short jornada_scancodes[] = {
+/* PTD1 */	KEY_CAPSLOCK, KEY_MACRO, KEY_LEFTCTRL, 0, KEY_ESC, KEY_KP5, 0, 0,			/*  1  -> 8   */
+		KEY_F1, KEY_F2, KEY_F3, KEY_F8, KEY_F7, KEY_F6, KEY_F4, KEY_F5,				/*  9  -> 16  */
+/* PTD5 */	KEY_SLASH, KEY_APOSTROPHE, KEY_ENTER, 0, KEY_Z, 0, 0, 0,				/*  17 -> 24  */
+		KEY_X, KEY_C, KEY_V, KEY_DOT, KEY_COMMA, KEY_M, KEY_B, KEY_N,				/*  25 -> 32  */
+/* PTD7 */	KEY_KP2, KEY_KP6, KEY_KP3, 0, 0, 0, 0, 0,						/*  33 -> 40  */
+		KEY_F10, KEY_RO, KEY_F9, KEY_KP4, KEY_NUMLOCK, KEY_SCROLLLOCK, KEY_LEFTALT, KEY_HANJA,	/*  41 -> 48  */
+/* PTE0 */	KEY_KATAKANA, KEY_KP0, KEY_GRAVE, 0, KEY_FINANCE, 0, 0, 0,				/*  49 -> 56  */
+		KEY_KPMINUS, KEY_HIRAGANA, KEY_SPACE, KEY_KPDOT, KEY_VOLUMEUP, 249, 0, 0,		/*  57 -> 64  */
+/* PTE1 */	KEY_SEMICOLON, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, KEY_A, 0, 0, 0,			/*  65 -> 72  */
+		KEY_S, KEY_D, KEY_F, KEY_L, KEY_K, KEY_J, KEY_G, KEY_H,					/*  73 -> 80  */
+/* PTE3 */	KEY_KP8, KEY_LEFTMETA, KEY_RIGHTSHIFT, 0, KEY_TAB, 0, 0, 0,				/*  81 -> 88  */
+		0, KEY_LEFTSHIFT, KEY_KP7, KEY_KP9, KEY_KP1, KEY_F11, KEY_KPPLUS, KEY_KPASTERISK,	/*  89 -> 96  */
+/* PTE6 */	KEY_P, KEY_LEFTBRACE, KEY_BACKSPACE, 0, KEY_Q, 0, 0, 0,					/*  97 -> 104 */
+		KEY_W, KEY_E, KEY_R, KEY_O, KEY_I, KEY_U, KEY_T, KEY_Y,					/* 105 -> 112 */
+/* PTE7 */	KEY_0, KEY_MINUS, KEY_EQUAL, 0, KEY_1, 0, 0, 0,						/* 113 -> 120 */
+		KEY_2, KEY_3, KEY_4, KEY_9, KEY_8, KEY_7, KEY_5, KEY_6,					/* 121 -> 128 */
+/* **** */	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0
+};
+
+#define JORNADA_SCAN_SIZE	18
+
+struct jornadakbd {
+	struct input_polled_dev *poll_dev;
+	unsigned short keymap[ARRAY_SIZE(jornada_scancodes)];
+	unsigned char length;
+	unsigned char old_scan[JORNADA_SCAN_SIZE];
+	unsigned char new_scan[JORNADA_SCAN_SIZE];
+};
+
+static void jornada_parse_kbd(struct jornadakbd *jornadakbd)
+{
+	struct input_dev *input_dev = jornadakbd->poll_dev->input;
+	unsigned short *keymap = jornadakbd->keymap;
+	unsigned int sync_me = 0;
+	unsigned int i, j;
+
+	for (i = 0; i < JORNADA_SCAN_SIZE; i++) {
+		unsigned char new = jornadakbd->new_scan[i];
+		unsigned char old = jornadakbd->old_scan[i];
+		unsigned int xor = new ^ old;
+
+		if (xor == 0)
+			continue;
+
+		for (j = 0; j < 8; j++) {
+			unsigned int bit = 1 << j;
+			if (xor & bit) {
+				unsigned int scancode = (i << 3) + j;
+				input_event(input_dev,
+					    EV_MSC, MSC_SCAN, scancode);
+				input_report_key(input_dev,
+						 keymap[scancode],
+						 !(new & bit));
+				sync_me = 1;
+			}
+		}
+	}
+
+	if (sync_me)
+	    input_sync(input_dev);
+}
+
+static void jornada_scan_keyb(unsigned char *s)
+{
+	int i;
+	unsigned short ec_static, dc_static; /* = UINT16_t */
+	unsigned char matrix_switch[] = {
+		0xfd, 0xff,   /* PTD1 PD(1) */
+		0xdf, 0xff,   /* PTD5 PD(5) */
+		0x7f, 0xff,   /* PTD7 PD(7) */
+		0xff, 0xfe,   /* PTE0 PE(0) */
+		0xff, 0xfd,   /* PTE1 PE(1) */
+		0xff, 0xf7,   /* PTE3 PE(3) */
+		0xff, 0xbf,   /* PTE6 PE(6) */
+		0xff, 0x7f,   /* PTE7 PE(7) */
+	}, *t = matrix_switch;
+	/* PD(x) :
+	1.   0xcc0c & (1~(1 << (2*(x)+1)))))
+	2.   (0xf0cf & 0xfffff) */
+	/* PE(x) :
+	1.   0xcc0c & 0xffff
+	2.   0xf0cf & (1~(1 << (2*(x)+1))))) */
+	unsigned short matrix_PDE[] = {
+		0xcc04, 0xf0cf,  /* PD(1) */
+		0xc40c, 0xf0cf,	 /* PD(5) */
+		0x4c0c, 0xf0cf,  /* PD(7) */
+		0xcc0c, 0xf0cd,  /* PE(0) */
+		0xcc0c, 0xf0c7,	 /* PE(1) */
+		0xcc0c, 0xf04f,  /* PE(3) */
+		0xcc0c, 0xd0cf,	 /* PE(6) */
+		0xcc0c, 0x70cf,	 /* PE(7) */
+	}, *y = matrix_PDE;
+
+	/* Save these control reg bits */
+	dc_static = (__raw_readw(PDCR) & (~0xcc0c));
+	ec_static = (__raw_readw(PECR) & (~0xf0cf));
+
+	for (i = 0; i < 8; i++) {
+		/* disable output for all but the one we want to scan */
+		__raw_writew((dc_static | *y++), PDCR);
+		__raw_writew((ec_static | *y++), PECR);
+		udelay(5);
+
+		/* Get scanline row */
+		__raw_writeb(*t++, PDDR);
+		__raw_writeb(*t++, PEDR);
+		udelay(50);
+
+		/* Read data */
+		*s++ = __raw_readb(PCDR);
+		*s++ = __raw_readb(PFDR);
+	}
+	/* Scan no lines */
+	__raw_writeb(0xff, PDDR);
+	__raw_writeb(0xff, PEDR);
+
+	/* Enable all scanlines */
+	__raw_writew((dc_static | (0x5555 & 0xcc0c)),PDCR);
+	__raw_writew((ec_static | (0x5555 & 0xf0cf)),PECR);
+
+	/* Ignore extra keys and events */
+	*s++ = __raw_readb(PGDR);
+	*s++ = __raw_readb(PHDR);
+}
+
+static void jornadakbd680_poll(struct input_polled_dev *dev)
+{
+	struct jornadakbd *jornadakbd = dev->private;
+
+	jornada_scan_keyb(jornadakbd->new_scan);
+	jornada_parse_kbd(jornadakbd);
+	memcpy(jornadakbd->old_scan, jornadakbd->new_scan, JORNADA_SCAN_SIZE);
+}
+
+static int jornada680kbd_probe(struct platform_device *pdev)
+{
+	struct jornadakbd *jornadakbd;
+	struct input_polled_dev *poll_dev;
+	struct input_dev *input_dev;
+	int i, error;
+
+	jornadakbd = devm_kzalloc(&pdev->dev, sizeof(struct jornadakbd),
+				  GFP_KERNEL);
+	if (!jornadakbd)
+		return -ENOMEM;
+
+	poll_dev = devm_input_allocate_polled_device(&pdev->dev);
+	if (!poll_dev) {
+		dev_err(&pdev->dev, "failed to allocate polled input device\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, jornadakbd);
+
+	jornadakbd->poll_dev = poll_dev;
+
+	memcpy(jornadakbd->keymap, jornada_scancodes,
+		sizeof(jornadakbd->keymap));
+
+	poll_dev->private = jornadakbd;
+	poll_dev->poll = jornadakbd680_poll;
+	poll_dev->poll_interval = 50; /* msec */
+
+	input_dev = poll_dev->input;
+	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+	input_dev->name = "HP Jornada 680 keyboard";
+	input_dev->phys = "jornadakbd/input0";
+	input_dev->keycode = jornadakbd->keymap;
+	input_dev->keycodesize = sizeof(unsigned short);
+	input_dev->keycodemax = ARRAY_SIZE(jornada_scancodes);
+	input_dev->dev.parent = &pdev->dev;
+	input_dev->id.bustype = BUS_HOST;
+
+	for (i = 0; i < 128; i++)
+		if (jornadakbd->keymap[i])
+			__set_bit(jornadakbd->keymap[i], input_dev->keybit);
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+
+	error = input_register_polled_device(jornadakbd->poll_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register polled input device\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static struct platform_driver jornada680kbd_driver = {
+	.driver	= {
+		.name	= "jornada680_kbd",
+	},
+	.probe	= jornada680kbd_probe,
+};
+module_platform_driver(jornada680kbd_driver);
+
+MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
+MODULE_DESCRIPTION("HP Jornada 620/660/680/690 Keyboard Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:jornada680_kbd");
diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c
new file mode 100644
index 0000000..421d9c5
--- /dev/null
+++ b/drivers/input/keyboard/jornada720_kbd.c
@@ -0,0 +1,174 @@
+/*
+ * drivers/input/keyboard/jornada720_kbd.c
+ *
+ * HP Jornada 720 keyboard platform driver
+ *
+ * Copyright (C) 2006/2007 Kristoffer Ericson <Kristoffer.Ericson@Gmail.com>
+ *
+ *    Copyright (C) 2006 jornada 720 kbd driver by
+		Filip Zyzniewsk <Filip.Zyzniewski@tefnet.plX
+ *     based on (C) 2004 jornada 720 kbd driver by
+		Alex Lange <chicken@handhelds.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <mach/jornada720.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+
+MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>");
+MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver");
+MODULE_LICENSE("GPL v2");
+
+static unsigned short jornada_std_keymap[128] = {					/* ROW */
+	0, KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7,		/* #1  */
+	KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE,	/*  -> */
+	0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9,		/* #2  */
+	KEY_0, KEY_MINUS, KEY_EQUAL,0, 0, 0,						/*  -> */
+	0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O,		/* #3  */
+	KEY_P, KEY_BACKSLASH, KEY_BACKSPACE, 0, 0, 0,					/*  -> */
+	0, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L,		/* #4  */
+	KEY_SEMICOLON, KEY_LEFTBRACE, KEY_RIGHTBRACE, 0, 0, 0,				/*  -> */
+	0, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_COMMA,			/* #5  */
+	KEY_DOT, KEY_KPMINUS, KEY_APOSTROPHE, KEY_ENTER, 0, 0,0,			/*  -> */
+	0, KEY_TAB, 0, KEY_LEFTSHIFT, 0, KEY_APOSTROPHE, 0, 0, 0, 0,			/* #6  */
+	KEY_UP, 0, KEY_RIGHTSHIFT, 0, 0, 0,0, 0, 0, 0, 0, KEY_LEFTALT, KEY_GRAVE,	/*  -> */
+	0, 0, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0,0, KEY_KPASTERISK,		/*  -> */
+	KEY_LEFTCTRL, 0, KEY_SPACE, 0, 0, 0, KEY_SLASH, KEY_DELETE, 0, 0,		/*  -> */
+	0, 0, 0, KEY_POWER,								/*  -> */
+};
+
+struct jornadakbd {
+	unsigned short keymap[ARRAY_SIZE(jornada_std_keymap)];
+	struct input_dev *input;
+};
+
+static irqreturn_t jornada720_kbd_interrupt(int irq, void *dev_id)
+{
+	struct platform_device *pdev = dev_id;
+	struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
+	struct input_dev *input = jornadakbd->input;
+	u8 count, kbd_data, scan_code;
+
+	/* startup ssp with spinlock */
+	jornada_ssp_start();
+
+	if (jornada_ssp_inout(GETSCANKEYCODE) != TXDUMMY) {
+		printk(KERN_DEBUG
+			"jornada720_kbd: "
+			"GetKeycode command failed with ETIMEDOUT, "
+			"flushed bus\n");
+	} else {
+		/* How many keycodes are waiting for us? */
+		count = jornada_ssp_byte(TXDUMMY);
+
+		/* Lets drag them out one at a time */
+		while (count--) {
+			/* Exchange TxDummy for location (keymap[kbddata]) */
+			kbd_data = jornada_ssp_byte(TXDUMMY);
+			scan_code = kbd_data & 0x7f;
+
+			input_event(input, EV_MSC, MSC_SCAN, scan_code);
+			input_report_key(input, jornadakbd->keymap[scan_code],
+					 !(kbd_data & 0x80));
+			input_sync(input);
+		}
+	}
+
+	/* release spinlock and turn off ssp */
+	jornada_ssp_end();
+
+	return IRQ_HANDLED;
+};
+
+static int jornada720_kbd_probe(struct platform_device *pdev)
+{
+	struct jornadakbd *jornadakbd;
+	struct input_dev *input_dev;
+	int i, err;
+
+	jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!jornadakbd || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	platform_set_drvdata(pdev, jornadakbd);
+
+	memcpy(jornadakbd->keymap, jornada_std_keymap,
+		sizeof(jornada_std_keymap));
+	jornadakbd->input = input_dev;
+
+	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+	input_dev->name = "HP Jornada 720 keyboard";
+	input_dev->phys = "jornadakbd/input0";
+	input_dev->keycode = jornadakbd->keymap;
+	input_dev->keycodesize = sizeof(unsigned short);
+	input_dev->keycodemax = ARRAY_SIZE(jornada_std_keymap);
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->dev.parent = &pdev->dev;
+
+	for (i = 0; i < ARRAY_SIZE(jornadakbd->keymap); i++)
+		__set_bit(jornadakbd->keymap[i], input_dev->keybit);
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+
+	err = request_irq(IRQ_GPIO0,
+			  jornada720_kbd_interrupt,
+			  IRQF_TRIGGER_FALLING,
+			  "jornadakbd", pdev);
+	if (err) {
+		printk(KERN_INFO "jornadakbd720_kbd: Unable to grab IRQ\n");
+		goto fail1;
+	}
+
+	err = input_register_device(jornadakbd->input);
+	if (err)
+		goto fail2;
+
+	return 0;
+
+ fail2:	/* IRQ, DEVICE, MEMORY */
+	free_irq(IRQ_GPIO0, pdev);
+ fail1:	/* DEVICE, MEMORY */
+	input_free_device(input_dev);
+	kfree(jornadakbd);
+	return err;
+};
+
+static int jornada720_kbd_remove(struct platform_device *pdev)
+{
+	struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
+
+	free_irq(IRQ_GPIO0, pdev);
+	input_unregister_device(jornadakbd->input);
+	kfree(jornadakbd);
+
+	return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:jornada720_kbd");
+
+static struct platform_driver jornada720_kbd_driver = {
+	.driver  = {
+		.name    = "jornada720_kbd",
+	 },
+	.probe   = jornada720_kbd_probe,
+	.remove  = jornada720_kbd_remove,
+};
+module_platform_driver(jornada720_kbd_driver);
diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c
new file mode 100644
index 0000000..9fcd9f1
--- /dev/null
+++ b/drivers/input/keyboard/lkkbd.c
@@ -0,0 +1,733 @@
+/*
+ *  Copyright (C) 2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
+ */
+
+/*
+ * LK keyboard driver for Linux, based on sunkbd.c (C) by Vojtech Pavlik
+ */
+
+/*
+ * DEC LK201 and LK401 keyboard driver for Linux (primary for DECstations
+ * and VAXstations, but can also be used on any standard RS232 with an
+ * adaptor).
+ *
+ * DISCLAIMER: This works for _me_. If you break anything by using the
+ * information given below, I will _not_ be liable!
+ *
+ * RJ10 pinout:		To DE9:		Or DB25:
+ *	1 - RxD <---->	Pin 3 (TxD) <->	Pin 2 (TxD)
+ *	2 - GND <---->	Pin 5 (GND) <->	Pin 7 (GND)
+ *	4 - TxD <---->	Pin 2 (RxD) <->	Pin 3 (RxD)
+ *	3 - +12V (from HDD drive connector), DON'T connect to DE9 or DB25!!!
+ *
+ * Pin numbers for DE9 and DB25 are noted on the plug (quite small:). For
+ * RJ10, it's like this:
+ *
+ *      __=__	Hold the plug in front of you, cable downwards,
+ *     /___/|	nose is hidden behind the plug. Now, pin 1 is at
+ *    |1234||	the left side, pin 4 at the right and 2 and 3 are
+ *    |IIII||	in between, of course:)
+ *    |    ||
+ *    |____|/
+ *      ||	So the adaptor consists of three connected cables
+ *      ||	for data transmission (RxD and TxD) and signal ground.
+ *		Additionally, you have to get +12V from somewhere.
+ * Most easily, you'll get that from a floppy or HDD power connector.
+ * It's the yellow cable there (black is ground and red is +5V).
+ *
+ * The keyboard and all the commands it understands are documented in
+ * "VCB02 Video Subsystem - Technical Manual", EK-104AA-TM-001. This
+ * document is LK201 specific, but LK401 is mostly compatible. It comes
+ * up in LK201 mode and doesn't report any of the additional keys it
+ * has. These need to be switched on with the LK_CMD_ENABLE_LK401
+ * command. You'll find this document (scanned .pdf file) on MANX,
+ * a search engine specific to DEC documentation. Try
+ * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+
+#define DRIVER_DESC	"LK keyboard driver"
+
+MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Known parameters:
+ *	bell_volume
+ *	keyclick_volume
+ *	ctrlclick_volume
+ *
+ * Please notice that there's not yet an API to set these at runtime.
+ */
+static int bell_volume = 100; /* % */
+module_param(bell_volume, int, 0);
+MODULE_PARM_DESC(bell_volume, "Bell volume (in %). default is 100%");
+
+static int keyclick_volume = 100; /* % */
+module_param(keyclick_volume, int, 0);
+MODULE_PARM_DESC(keyclick_volume, "Keyclick volume (in %), default is 100%");
+
+static int ctrlclick_volume = 100; /* % */
+module_param(ctrlclick_volume, int, 0);
+MODULE_PARM_DESC(ctrlclick_volume, "Ctrlclick volume (in %), default is 100%");
+
+static int lk201_compose_is_alt;
+module_param(lk201_compose_is_alt, int, 0);
+MODULE_PARM_DESC(lk201_compose_is_alt,
+		 "If set non-zero, LK201' Compose key will act as an Alt key");
+
+
+
+#undef LKKBD_DEBUG
+#ifdef LKKBD_DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...) do {} while (0)
+#endif
+
+/* LED control */
+#define LK_LED_WAIT		0x81
+#define LK_LED_COMPOSE		0x82
+#define LK_LED_SHIFTLOCK	0x84
+#define LK_LED_SCROLLLOCK	0x88
+#define LK_CMD_LED_ON		0x13
+#define LK_CMD_LED_OFF		0x11
+
+/* Mode control */
+#define LK_MODE_DOWN		0x80
+#define LK_MODE_AUTODOWN	0x82
+#define LK_MODE_UPDOWN		0x86
+#define LK_CMD_SET_MODE(mode, div)	((mode) | ((div) << 3))
+
+/* Misc commands */
+#define LK_CMD_ENABLE_KEYCLICK	0x1b
+#define LK_CMD_DISABLE_KEYCLICK	0x99
+#define LK_CMD_DISABLE_BELL	0xa1
+#define LK_CMD_SOUND_BELL	0xa7
+#define LK_CMD_ENABLE_BELL	0x23
+#define LK_CMD_DISABLE_CTRCLICK	0xb9
+#define LK_CMD_ENABLE_CTRCLICK	0xbb
+#define LK_CMD_SET_DEFAULTS	0xd3
+#define LK_CMD_POWERCYCLE_RESET	0xfd
+#define LK_CMD_ENABLE_LK401	0xe9
+#define LK_CMD_REQUEST_ID	0xab
+
+/* Misc responses from keyboard */
+#define LK_STUCK_KEY		0x3d
+#define LK_SELFTEST_FAILED	0x3e
+#define LK_ALL_KEYS_UP		0xb3
+#define LK_METRONOME		0xb4
+#define LK_OUTPUT_ERROR		0xb5
+#define LK_INPUT_ERROR		0xb6
+#define LK_KBD_LOCKED		0xb7
+#define LK_KBD_TEST_MODE_ACK	0xb8
+#define LK_PREFIX_KEY_DOWN	0xb9
+#define LK_MODE_CHANGE_ACK	0xba
+#define LK_RESPONSE_RESERVED	0xbb
+
+#define LK_NUM_KEYCODES		256
+#define LK_NUM_IGNORE_BYTES	6
+
+static unsigned short lkkbd_keycode[LK_NUM_KEYCODES] = {
+	[0x56] = KEY_F1,
+	[0x57] = KEY_F2,
+	[0x58] = KEY_F3,
+	[0x59] = KEY_F4,
+	[0x5a] = KEY_F5,
+	[0x64] = KEY_F6,
+	[0x65] = KEY_F7,
+	[0x66] = KEY_F8,
+	[0x67] = KEY_F9,
+	[0x68] = KEY_F10,
+	[0x71] = KEY_F11,
+	[0x72] = KEY_F12,
+	[0x73] = KEY_F13,
+	[0x74] = KEY_F14,
+	[0x7c] = KEY_F15,
+	[0x7d] = KEY_F16,
+	[0x80] = KEY_F17,
+	[0x81] = KEY_F18,
+	[0x82] = KEY_F19,
+	[0x83] = KEY_F20,
+	[0x8a] = KEY_FIND,
+	[0x8b] = KEY_INSERT,
+	[0x8c] = KEY_DELETE,
+	[0x8d] = KEY_SELECT,
+	[0x8e] = KEY_PAGEUP,
+	[0x8f] = KEY_PAGEDOWN,
+	[0x92] = KEY_KP0,
+	[0x94] = KEY_KPDOT,
+	[0x95] = KEY_KPENTER,
+	[0x96] = KEY_KP1,
+	[0x97] = KEY_KP2,
+	[0x98] = KEY_KP3,
+	[0x99] = KEY_KP4,
+	[0x9a] = KEY_KP5,
+	[0x9b] = KEY_KP6,
+	[0x9c] = KEY_KPCOMMA,
+	[0x9d] = KEY_KP7,
+	[0x9e] = KEY_KP8,
+	[0x9f] = KEY_KP9,
+	[0xa0] = KEY_KPMINUS,
+	[0xa1] = KEY_PROG1,
+	[0xa2] = KEY_PROG2,
+	[0xa3] = KEY_PROG3,
+	[0xa4] = KEY_PROG4,
+	[0xa7] = KEY_LEFT,
+	[0xa8] = KEY_RIGHT,
+	[0xa9] = KEY_DOWN,
+	[0xaa] = KEY_UP,
+	[0xab] = KEY_RIGHTSHIFT,
+	[0xac] = KEY_LEFTALT,
+	[0xad] = KEY_COMPOSE, /* Right Compose, that is. */
+	[0xae] = KEY_LEFTSHIFT, /* Same as KEY_RIGHTSHIFT on LK201 */
+	[0xaf] = KEY_LEFTCTRL,
+	[0xb0] = KEY_CAPSLOCK,
+	[0xb1] = KEY_COMPOSE, /* Left Compose, that is. */
+	[0xb2] = KEY_RIGHTALT,
+	[0xbc] = KEY_BACKSPACE,
+	[0xbd] = KEY_ENTER,
+	[0xbe] = KEY_TAB,
+	[0xbf] = KEY_ESC,
+	[0xc0] = KEY_1,
+	[0xc1] = KEY_Q,
+	[0xc2] = KEY_A,
+	[0xc3] = KEY_Z,
+	[0xc5] = KEY_2,
+	[0xc6] = KEY_W,
+	[0xc7] = KEY_S,
+	[0xc8] = KEY_X,
+	[0xc9] = KEY_102ND,
+	[0xcb] = KEY_3,
+	[0xcc] = KEY_E,
+	[0xcd] = KEY_D,
+	[0xce] = KEY_C,
+	[0xd0] = KEY_4,
+	[0xd1] = KEY_R,
+	[0xd2] = KEY_F,
+	[0xd3] = KEY_V,
+	[0xd4] = KEY_SPACE,
+	[0xd6] = KEY_5,
+	[0xd7] = KEY_T,
+	[0xd8] = KEY_G,
+	[0xd9] = KEY_B,
+	[0xdb] = KEY_6,
+	[0xdc] = KEY_Y,
+	[0xdd] = KEY_H,
+	[0xde] = KEY_N,
+	[0xe0] = KEY_7,
+	[0xe1] = KEY_U,
+	[0xe2] = KEY_J,
+	[0xe3] = KEY_M,
+	[0xe5] = KEY_8,
+	[0xe6] = KEY_I,
+	[0xe7] = KEY_K,
+	[0xe8] = KEY_COMMA,
+	[0xea] = KEY_9,
+	[0xeb] = KEY_O,
+	[0xec] = KEY_L,
+	[0xed] = KEY_DOT,
+	[0xef] = KEY_0,
+	[0xf0] = KEY_P,
+	[0xf2] = KEY_SEMICOLON,
+	[0xf3] = KEY_SLASH,
+	[0xf5] = KEY_EQUAL,
+	[0xf6] = KEY_RIGHTBRACE,
+	[0xf7] = KEY_BACKSLASH,
+	[0xf9] = KEY_MINUS,
+	[0xfa] = KEY_LEFTBRACE,
+	[0xfb] = KEY_APOSTROPHE,
+};
+
+#define CHECK_LED(LK, VAR_ON, VAR_OFF, LED, BITS) do {		\
+	if (test_bit(LED, (LK)->dev->led))			\
+		VAR_ON |= BITS;					\
+	else							\
+		VAR_OFF |= BITS;				\
+	} while (0)
+
+/*
+ * Per-keyboard data
+ */
+struct lkkbd {
+	unsigned short keycode[LK_NUM_KEYCODES];
+	int ignore_bytes;
+	unsigned char id[LK_NUM_IGNORE_BYTES];
+	struct input_dev *dev;
+	struct serio *serio;
+	struct work_struct tq;
+	char name[64];
+	char phys[32];
+	char type;
+	int bell_volume;
+	int keyclick_volume;
+	int ctrlclick_volume;
+};
+
+#ifdef LKKBD_DEBUG
+/*
+ * Responses from the keyboard and mapping back to their names.
+ */
+static struct {
+	unsigned char value;
+	unsigned char *name;
+} lk_response[] = {
+#define RESPONSE(x) { .value = (x), .name = #x, }
+	RESPONSE(LK_STUCK_KEY),
+	RESPONSE(LK_SELFTEST_FAILED),
+	RESPONSE(LK_ALL_KEYS_UP),
+	RESPONSE(LK_METRONOME),
+	RESPONSE(LK_OUTPUT_ERROR),
+	RESPONSE(LK_INPUT_ERROR),
+	RESPONSE(LK_KBD_LOCKED),
+	RESPONSE(LK_KBD_TEST_MODE_ACK),
+	RESPONSE(LK_PREFIX_KEY_DOWN),
+	RESPONSE(LK_MODE_CHANGE_ACK),
+	RESPONSE(LK_RESPONSE_RESERVED),
+#undef RESPONSE
+};
+
+static unsigned char *response_name(unsigned char value)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(lk_response); i++)
+		if (lk_response[i].value == value)
+			return lk_response[i].name;
+
+	return "<unknown>";
+}
+#endif /* LKKBD_DEBUG */
+
+/*
+ * Calculate volume parameter byte for a given volume.
+ */
+static unsigned char volume_to_hw(int volume_percent)
+{
+	unsigned char ret = 0;
+
+	if (volume_percent < 0)
+		volume_percent = 0;
+	if (volume_percent > 100)
+		volume_percent = 100;
+
+	if (volume_percent >= 0)
+		ret = 7;
+	if (volume_percent >= 13)	/* 12.5 */
+		ret = 6;
+	if (volume_percent >= 25)
+		ret = 5;
+	if (volume_percent >= 38)	/* 37.5 */
+		ret = 4;
+	if (volume_percent >= 50)
+		ret = 3;
+	if (volume_percent >= 63)	/* 62.5 */
+		ret = 2;		/* This is the default volume */
+	if (volume_percent >= 75)
+		ret = 1;
+	if (volume_percent >= 88)	/* 87.5 */
+		ret = 0;
+
+	ret |= 0x80;
+
+	return ret;
+}
+
+static void lkkbd_detection_done(struct lkkbd *lk)
+{
+	int i;
+
+	/*
+	 * Reset setting for Compose key. Let Compose be KEY_COMPOSE.
+	 */
+	lk->keycode[0xb1] = KEY_COMPOSE;
+
+	/*
+	 * Print keyboard name and modify Compose=Alt on user's request.
+	 */
+	switch (lk->id[4]) {
+	case 1:
+		strlcpy(lk->name, "DEC LK201 keyboard", sizeof(lk->name));
+
+		if (lk201_compose_is_alt)
+			lk->keycode[0xb1] = KEY_LEFTALT;
+		break;
+
+	case 2:
+		strlcpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name));
+		break;
+
+	default:
+		strlcpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name));
+		printk(KERN_ERR
+			"lkkbd: keyboard on %s is unknown, please report to "
+			"Jan-Benedict Glaw <jbglaw@lug-owl.de>\n", lk->phys);
+		printk(KERN_ERR "lkkbd: keyboard ID'ed as:");
+		for (i = 0; i < LK_NUM_IGNORE_BYTES; i++)
+			printk(" 0x%02x", lk->id[i]);
+		printk("\n");
+		break;
+	}
+
+	printk(KERN_INFO "lkkbd: keyboard on %s identified as: %s\n",
+		lk->phys, lk->name);
+
+	/*
+	 * Report errors during keyboard boot-up.
+	 */
+	switch (lk->id[2]) {
+	case 0x00:
+		/* All okay */
+		break;
+
+	case LK_STUCK_KEY:
+		printk(KERN_ERR "lkkbd: Stuck key on keyboard at %s\n",
+			lk->phys);
+		break;
+
+	case LK_SELFTEST_FAILED:
+		printk(KERN_ERR
+			"lkkbd: Selftest failed on keyboard at %s, "
+			"keyboard may not work properly\n", lk->phys);
+		break;
+
+	default:
+		printk(KERN_ERR
+			"lkkbd: Unknown error %02x on keyboard at %s\n",
+			lk->id[2], lk->phys);
+		break;
+	}
+
+	/*
+	 * Try to hint user if there's a stuck key.
+	 */
+	if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0)
+		printk(KERN_ERR
+			"Scancode of stuck key is 0x%02x, keycode is 0x%04x\n",
+			lk->id[3], lk->keycode[lk->id[3]]);
+}
+
+/*
+ * lkkbd_interrupt() is called by the low level driver when a character
+ * is received.
+ */
+static irqreturn_t lkkbd_interrupt(struct serio *serio,
+				   unsigned char data, unsigned int flags)
+{
+	struct lkkbd *lk = serio_get_drvdata(serio);
+	struct input_dev *input_dev = lk->dev;
+	unsigned int keycode;
+	int i;
+
+	DBG(KERN_INFO "Got byte 0x%02x\n", data);
+
+	if (lk->ignore_bytes > 0) {
+		DBG(KERN_INFO "Ignoring a byte on %s\n", lk->name);
+		lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
+
+		if (lk->ignore_bytes == 0)
+			lkkbd_detection_done(lk);
+
+		return IRQ_HANDLED;
+	}
+
+	switch (data) {
+	case LK_ALL_KEYS_UP:
+		for (i = 0; i < ARRAY_SIZE(lkkbd_keycode); i++)
+			input_report_key(input_dev, lk->keycode[i], 0);
+		input_sync(input_dev);
+		break;
+
+	case 0x01:
+		DBG(KERN_INFO "Got 0x01, scheduling re-initialization\n");
+		lk->ignore_bytes = LK_NUM_IGNORE_BYTES;
+		lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
+		schedule_work(&lk->tq);
+		break;
+
+	case LK_METRONOME:
+	case LK_OUTPUT_ERROR:
+	case LK_INPUT_ERROR:
+	case LK_KBD_LOCKED:
+	case LK_KBD_TEST_MODE_ACK:
+	case LK_PREFIX_KEY_DOWN:
+	case LK_MODE_CHANGE_ACK:
+	case LK_RESPONSE_RESERVED:
+		DBG(KERN_INFO "Got %s and don't know how to handle...\n",
+			response_name(data));
+		break;
+
+	default:
+		keycode = lk->keycode[data];
+		if (keycode != KEY_RESERVED) {
+			input_report_key(input_dev, keycode,
+					 !test_bit(keycode, input_dev->key));
+			input_sync(input_dev);
+		} else {
+			printk(KERN_WARNING
+				"%s: Unknown key with scancode 0x%02x on %s.\n",
+				__FILE__, data, lk->name);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void lkkbd_toggle_leds(struct lkkbd *lk)
+{
+	struct serio *serio = lk->serio;
+	unsigned char leds_on = 0;
+	unsigned char leds_off = 0;
+
+	CHECK_LED(lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
+	CHECK_LED(lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE);
+	CHECK_LED(lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
+	CHECK_LED(lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
+	if (leds_on != 0) {
+		serio_write(serio, LK_CMD_LED_ON);
+		serio_write(serio, leds_on);
+	}
+	if (leds_off != 0) {
+		serio_write(serio, LK_CMD_LED_OFF);
+		serio_write(serio, leds_off);
+	}
+}
+
+static void lkkbd_toggle_keyclick(struct lkkbd *lk, bool on)
+{
+	struct serio *serio = lk->serio;
+
+	if (on) {
+		DBG("%s: Activating key clicks\n", __func__);
+		serio_write(serio, LK_CMD_ENABLE_KEYCLICK);
+		serio_write(serio, volume_to_hw(lk->keyclick_volume));
+		serio_write(serio, LK_CMD_ENABLE_CTRCLICK);
+		serio_write(serio, volume_to_hw(lk->ctrlclick_volume));
+	} else {
+		DBG("%s: Deactivating key clicks\n", __func__);
+		serio_write(serio, LK_CMD_DISABLE_KEYCLICK);
+		serio_write(serio, LK_CMD_DISABLE_CTRCLICK);
+	}
+
+}
+
+/*
+ * lkkbd_event() handles events from the input module.
+ */
+static int lkkbd_event(struct input_dev *dev,
+			unsigned int type, unsigned int code, int value)
+{
+	struct lkkbd *lk = input_get_drvdata(dev);
+
+	switch (type) {
+	case EV_LED:
+		lkkbd_toggle_leds(lk);
+		return 0;
+
+	case EV_SND:
+		switch (code) {
+		case SND_CLICK:
+			lkkbd_toggle_keyclick(lk, value);
+			return 0;
+
+		case SND_BELL:
+			if (value != 0)
+				serio_write(lk->serio, LK_CMD_SOUND_BELL);
+
+			return 0;
+		}
+
+		break;
+
+	default:
+		printk(KERN_ERR "%s(): Got unknown type %d, code %d, value %d\n",
+			__func__, type, code, value);
+	}
+
+	return -1;
+}
+
+/*
+ * lkkbd_reinit() sets leds and beeps to a state the computer remembers they
+ * were in.
+ */
+static void lkkbd_reinit(struct work_struct *work)
+{
+	struct lkkbd *lk = container_of(work, struct lkkbd, tq);
+	int division;
+
+	/* Ask for ID */
+	serio_write(lk->serio, LK_CMD_REQUEST_ID);
+
+	/* Reset parameters */
+	serio_write(lk->serio, LK_CMD_SET_DEFAULTS);
+
+	/* Set LEDs */
+	lkkbd_toggle_leds(lk);
+
+	/*
+	 * Try to activate extended LK401 mode. This command will
+	 * only work with a LK401 keyboard and grants access to
+	 * LAlt, RAlt, RCompose and RShift.
+	 */
+	serio_write(lk->serio, LK_CMD_ENABLE_LK401);
+
+	/* Set all keys to UPDOWN mode */
+	for (division = 1; division <= 14; division++)
+		serio_write(lk->serio,
+			    LK_CMD_SET_MODE(LK_MODE_UPDOWN, division));
+
+	/* Enable bell and set volume */
+	serio_write(lk->serio, LK_CMD_ENABLE_BELL);
+	serio_write(lk->serio, volume_to_hw(lk->bell_volume));
+
+	/* Enable/disable keyclick (and possibly set volume) */
+	lkkbd_toggle_keyclick(lk, test_bit(SND_CLICK, lk->dev->snd));
+
+	/* Sound the bell if needed */
+	if (test_bit(SND_BELL, lk->dev->snd))
+		serio_write(lk->serio, LK_CMD_SOUND_BELL);
+}
+
+/*
+ * lkkbd_connect() probes for a LK keyboard and fills the necessary structures.
+ */
+static int lkkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct lkkbd *lk;
+	struct input_dev *input_dev;
+	int i;
+	int err;
+
+	lk = kzalloc(sizeof(struct lkkbd), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!lk || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	lk->serio = serio;
+	lk->dev = input_dev;
+	INIT_WORK(&lk->tq, lkkbd_reinit);
+	lk->bell_volume = bell_volume;
+	lk->keyclick_volume = keyclick_volume;
+	lk->ctrlclick_volume = ctrlclick_volume;
+	memcpy(lk->keycode, lkkbd_keycode, sizeof(lk->keycode));
+
+	strlcpy(lk->name, "DEC LK keyboard", sizeof(lk->name));
+	snprintf(lk->phys, sizeof(lk->phys), "%s/input0", serio->phys);
+
+	input_dev->name = lk->name;
+	input_dev->phys = lk->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_LKKBD;
+	input_dev->id.product = 0;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+	input_dev->event = lkkbd_event;
+
+	input_set_drvdata(input_dev, lk);
+
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(EV_LED, input_dev->evbit);
+	__set_bit(EV_SND, input_dev->evbit);
+	__set_bit(EV_REP, input_dev->evbit);
+	__set_bit(LED_CAPSL, input_dev->ledbit);
+	__set_bit(LED_SLEEP, input_dev->ledbit);
+	__set_bit(LED_COMPOSE, input_dev->ledbit);
+	__set_bit(LED_SCROLLL, input_dev->ledbit);
+	__set_bit(SND_BELL, input_dev->sndbit);
+	__set_bit(SND_CLICK, input_dev->sndbit);
+
+	input_dev->keycode = lk->keycode;
+	input_dev->keycodesize = sizeof(lk->keycode[0]);
+	input_dev->keycodemax = ARRAY_SIZE(lk->keycode);
+
+	for (i = 0; i < LK_NUM_KEYCODES; i++)
+		__set_bit(lk->keycode[i], input_dev->keybit);
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+
+	serio_set_drvdata(serio, lk);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(lk->dev);
+	if (err)
+		goto fail3;
+
+	serio_write(lk->serio, LK_CMD_POWERCYCLE_RESET);
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(lk);
+	return err;
+}
+
+/*
+ * lkkbd_disconnect() unregisters and closes behind us.
+ */
+static void lkkbd_disconnect(struct serio *serio)
+{
+	struct lkkbd *lk = serio_get_drvdata(serio);
+
+	input_get_device(lk->dev);
+	input_unregister_device(lk->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_put_device(lk->dev);
+	kfree(lk);
+}
+
+static struct serio_device_id lkkbd_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_LKKBD,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, lkkbd_serio_ids);
+
+static struct serio_driver lkkbd_drv = {
+	.driver		= {
+		.name	= "lkkbd",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= lkkbd_serio_ids,
+	.connect	= lkkbd_connect,
+	.disconnect	= lkkbd_disconnect,
+	.interrupt	= lkkbd_interrupt,
+};
+
+module_serio_driver(lkkbd_drv);
diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c
new file mode 100644
index 0000000..21bea52
--- /dev/null
+++ b/drivers/input/keyboard/lm8323.c
@@ -0,0 +1,859 @@
+/*
+ * drivers/i2c/chips/lm8323.c
+ *
+ * Copyright (C) 2007-2009 Nokia Corporation
+ *
+ * Written by Daniel Stone <daniel.stone@nokia.com>
+ *            Timo O. Karjalainen <timo.o.karjalainen@nokia.com>
+ *
+ * Updated by Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * 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 (version 2 of the License only).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+#include <linux/pm.h>
+#include <linux/i2c/lm8323.h>
+#include <linux/slab.h>
+
+/* Commands to send to the chip. */
+#define LM8323_CMD_READ_ID		0x80 /* Read chip ID. */
+#define LM8323_CMD_WRITE_CFG		0x81 /* Set configuration item. */
+#define LM8323_CMD_READ_INT		0x82 /* Get interrupt status. */
+#define LM8323_CMD_RESET		0x83 /* Reset, same as external one */
+#define LM8323_CMD_WRITE_PORT_SEL	0x85 /* Set GPIO in/out. */
+#define LM8323_CMD_WRITE_PORT_STATE	0x86 /* Set GPIO pullup. */
+#define LM8323_CMD_READ_PORT_SEL	0x87 /* Get GPIO in/out. */
+#define LM8323_CMD_READ_PORT_STATE	0x88 /* Get GPIO pullup. */
+#define LM8323_CMD_READ_FIFO		0x89 /* Read byte from FIFO. */
+#define LM8323_CMD_RPT_READ_FIFO	0x8a /* Read FIFO (no increment). */
+#define LM8323_CMD_SET_ACTIVE		0x8b /* Set active time. */
+#define LM8323_CMD_READ_ERR		0x8c /* Get error status. */
+#define LM8323_CMD_READ_ROTATOR		0x8e /* Read rotator status. */
+#define LM8323_CMD_SET_DEBOUNCE		0x8f /* Set debouncing time. */
+#define LM8323_CMD_SET_KEY_SIZE		0x90 /* Set keypad size. */
+#define LM8323_CMD_READ_KEY_SIZE	0x91 /* Get keypad size. */
+#define LM8323_CMD_READ_CFG		0x92 /* Get configuration item. */
+#define LM8323_CMD_WRITE_CLOCK		0x93 /* Set clock config. */
+#define LM8323_CMD_READ_CLOCK		0x94 /* Get clock config. */
+#define LM8323_CMD_PWM_WRITE		0x95 /* Write PWM script. */
+#define LM8323_CMD_START_PWM		0x96 /* Start PWM engine. */
+#define LM8323_CMD_STOP_PWM		0x97 /* Stop PWM engine. */
+
+/* Interrupt status. */
+#define INT_KEYPAD			0x01 /* Key event. */
+#define INT_ROTATOR			0x02 /* Rotator event. */
+#define INT_ERROR			0x08 /* Error: use CMD_READ_ERR. */
+#define INT_NOINIT			0x10 /* Lost configuration. */
+#define INT_PWM1			0x20 /* PWM1 stopped. */
+#define INT_PWM2			0x40 /* PWM2 stopped. */
+#define INT_PWM3			0x80 /* PWM3 stopped. */
+
+/* Errors (signalled by INT_ERROR, read with CMD_READ_ERR). */
+#define ERR_BADPAR			0x01 /* Bad parameter. */
+#define ERR_CMDUNK			0x02 /* Unknown command. */
+#define ERR_KEYOVR			0x04 /* Too many keys pressed. */
+#define ERR_FIFOOVER			0x40 /* FIFO overflow. */
+
+/* Configuration keys (CMD_{WRITE,READ}_CFG). */
+#define CFG_MUX1SEL			0x01 /* Select MUX1_OUT input. */
+#define CFG_MUX1EN			0x02 /* Enable MUX1_OUT. */
+#define CFG_MUX2SEL			0x04 /* Select MUX2_OUT input. */
+#define CFG_MUX2EN			0x08 /* Enable MUX2_OUT. */
+#define CFG_PSIZE			0x20 /* Package size (must be 0). */
+#define CFG_ROTEN			0x40 /* Enable rotator. */
+
+/* Clock settings (CMD_{WRITE,READ}_CLOCK). */
+#define CLK_RCPWM_INTERNAL		0x00
+#define CLK_RCPWM_EXTERNAL		0x03
+#define CLK_SLOWCLKEN			0x08 /* Enable 32.768kHz clock. */
+#define CLK_SLOWCLKOUT			0x40 /* Enable slow pulse output. */
+
+/* The possible addresses corresponding to CONFIG1 and CONFIG2 pin wirings. */
+#define LM8323_I2C_ADDR00		(0x84 >> 1)	/* 1000 010x */
+#define LM8323_I2C_ADDR01		(0x86 >> 1)	/* 1000 011x */
+#define LM8323_I2C_ADDR10		(0x88 >> 1)	/* 1000 100x */
+#define LM8323_I2C_ADDR11		(0x8A >> 1)	/* 1000 101x */
+
+/* Key event fifo length */
+#define LM8323_FIFO_LEN			15
+
+/* Commands for PWM engine; feed in with PWM_WRITE. */
+/* Load ramp counter from duty cycle field (range 0 - 0xff). */
+#define PWM_SET(v)			(0x4000 | ((v) & 0xff))
+/* Go to start of script. */
+#define PWM_GOTOSTART			0x0000
+/*
+ * Stop engine (generates interrupt).  If reset is 1, clear the program
+ * counter, else leave it.
+ */
+#define PWM_END(reset)			(0xc000 | (!!(reset) << 11))
+/*
+ * Ramp.  If s is 1, divide clock by 512, else divide clock by 16.
+ * Take t clock scales (up to 63) per step, for n steps (up to 126).
+ * If u is set, ramp up, else ramp down.
+ */
+#define PWM_RAMP(s, t, n, u)		((!!(s) << 14) | ((t) & 0x3f) << 8 | \
+					 ((n) & 0x7f) | ((u) ? 0 : 0x80))
+/*
+ * Loop (i.e. jump back to pos) for a given number of iterations (up to 63).
+ * If cnt is zero, execute until PWM_END is encountered.
+ */
+#define PWM_LOOP(cnt, pos)		(0xa000 | (((cnt) & 0x3f) << 7) | \
+					 ((pos) & 0x3f))
+/*
+ * Wait for trigger.  Argument is a mask of channels, shifted by the channel
+ * number, e.g. 0xa for channels 3 and 1.  Note that channels are numbered
+ * from 1, not 0.
+ */
+#define PWM_WAIT_TRIG(chans)		(0xe000 | (((chans) & 0x7) << 6))
+/* Send trigger.  Argument is same as PWM_WAIT_TRIG. */
+#define PWM_SEND_TRIG(chans)		(0xe000 | ((chans) & 0x7))
+
+struct lm8323_pwm {
+	int			id;
+	int			fade_time;
+	int			brightness;
+	int			desired_brightness;
+	bool			enabled;
+	bool			running;
+	/* pwm lock */
+	struct mutex		lock;
+	struct work_struct	work;
+	struct led_classdev	cdev;
+	struct lm8323_chip	*chip;
+};
+
+struct lm8323_chip {
+	/* device lock */
+	struct mutex		lock;
+	struct i2c_client	*client;
+	struct input_dev	*idev;
+	bool			kp_enabled;
+	bool			pm_suspend;
+	unsigned		keys_down;
+	char			phys[32];
+	unsigned short		keymap[LM8323_KEYMAP_SIZE];
+	int			size_x;
+	int			size_y;
+	int			debounce_time;
+	int			active_time;
+	struct lm8323_pwm	pwm[LM8323_NUM_PWMS];
+};
+
+#define client_to_lm8323(c)	container_of(c, struct lm8323_chip, client)
+#define dev_to_lm8323(d)	container_of(d, struct lm8323_chip, client->dev)
+#define cdev_to_pwm(c)		container_of(c, struct lm8323_pwm, cdev)
+#define work_to_pwm(w)		container_of(w, struct lm8323_pwm, work)
+
+#define LM8323_MAX_DATA 8
+
+/*
+ * To write, we just access the chip's address in write mode, and dump the
+ * command and data out on the bus.  The command byte and data are taken as
+ * sequential u8s out of varargs, to a maximum of LM8323_MAX_DATA.
+ */
+static int lm8323_write(struct lm8323_chip *lm, int len, ...)
+{
+	int ret, i;
+	va_list ap;
+	u8 data[LM8323_MAX_DATA];
+
+	va_start(ap, len);
+
+	if (unlikely(len > LM8323_MAX_DATA)) {
+		dev_err(&lm->client->dev, "tried to send %d bytes\n", len);
+		va_end(ap);
+		return 0;
+	}
+
+	for (i = 0; i < len; i++)
+		data[i] = va_arg(ap, int);
+
+	va_end(ap);
+
+	/*
+	 * If the host is asleep while we send the data, we can get a NACK
+	 * back while it wakes up, so try again, once.
+	 */
+	ret = i2c_master_send(lm->client, data, len);
+	if (unlikely(ret == -EREMOTEIO))
+		ret = i2c_master_send(lm->client, data, len);
+	if (unlikely(ret != len))
+		dev_err(&lm->client->dev, "sent %d bytes of %d total\n",
+			len, ret);
+
+	return ret;
+}
+
+/*
+ * To read, we first send the command byte to the chip and end the transaction,
+ * then access the chip in read mode, at which point it will send the data.
+ */
+static int lm8323_read(struct lm8323_chip *lm, u8 cmd, u8 *buf, int len)
+{
+	int ret;
+
+	/*
+	 * If the host is asleep while we send the byte, we can get a NACK
+	 * back while it wakes up, so try again, once.
+	 */
+	ret = i2c_master_send(lm->client, &cmd, 1);
+	if (unlikely(ret == -EREMOTEIO))
+		ret = i2c_master_send(lm->client, &cmd, 1);
+	if (unlikely(ret != 1)) {
+		dev_err(&lm->client->dev, "sending read cmd 0x%02x failed\n",
+			cmd);
+		return 0;
+	}
+
+	ret = i2c_master_recv(lm->client, buf, len);
+	if (unlikely(ret != len))
+		dev_err(&lm->client->dev, "wanted %d bytes, got %d\n",
+			len, ret);
+
+	return ret;
+}
+
+/*
+ * Set the chip active time (idle time before it enters halt).
+ */
+static void lm8323_set_active_time(struct lm8323_chip *lm, int time)
+{
+	lm8323_write(lm, 2, LM8323_CMD_SET_ACTIVE, time >> 2);
+}
+
+/*
+ * The signals are AT-style: the low 7 bits are the keycode, and the top
+ * bit indicates the state (1 for down, 0 for up).
+ */
+static inline u8 lm8323_whichkey(u8 event)
+{
+	return event & 0x7f;
+}
+
+static inline int lm8323_ispress(u8 event)
+{
+	return (event & 0x80) ? 1 : 0;
+}
+
+static void process_keys(struct lm8323_chip *lm)
+{
+	u8 event;
+	u8 key_fifo[LM8323_FIFO_LEN + 1];
+	int old_keys_down = lm->keys_down;
+	int ret;
+	int i = 0;
+
+	/*
+	 * Read all key events from the FIFO at once. Next READ_FIFO clears the
+	 * FIFO even if we didn't read all events previously.
+	 */
+	ret = lm8323_read(lm, LM8323_CMD_READ_FIFO, key_fifo, LM8323_FIFO_LEN);
+
+	if (ret < 0) {
+		dev_err(&lm->client->dev, "Failed reading fifo \n");
+		return;
+	}
+	key_fifo[ret] = 0;
+
+	while ((event = key_fifo[i++])) {
+		u8 key = lm8323_whichkey(event);
+		int isdown = lm8323_ispress(event);
+		unsigned short keycode = lm->keymap[key];
+
+		dev_vdbg(&lm->client->dev, "key 0x%02x %s\n",
+			 key, isdown ? "down" : "up");
+
+		if (lm->kp_enabled) {
+			input_event(lm->idev, EV_MSC, MSC_SCAN, key);
+			input_report_key(lm->idev, keycode, isdown);
+			input_sync(lm->idev);
+		}
+
+		if (isdown)
+			lm->keys_down++;
+		else
+			lm->keys_down--;
+	}
+
+	/*
+	 * Errata: We need to ensure that the chip never enters halt mode
+	 * during a keypress, so set active time to 0.  When it's released,
+	 * we can enter halt again, so set the active time back to normal.
+	 */
+	if (!old_keys_down && lm->keys_down)
+		lm8323_set_active_time(lm, 0);
+	if (old_keys_down && !lm->keys_down)
+		lm8323_set_active_time(lm, lm->active_time);
+}
+
+static void lm8323_process_error(struct lm8323_chip *lm)
+{
+	u8 error;
+
+	if (lm8323_read(lm, LM8323_CMD_READ_ERR, &error, 1) == 1) {
+		if (error & ERR_FIFOOVER)
+			dev_vdbg(&lm->client->dev, "fifo overflow!\n");
+		if (error & ERR_KEYOVR)
+			dev_vdbg(&lm->client->dev,
+					"more than two keys pressed\n");
+		if (error & ERR_CMDUNK)
+			dev_vdbg(&lm->client->dev,
+					"unknown command submitted\n");
+		if (error & ERR_BADPAR)
+			dev_vdbg(&lm->client->dev, "bad command parameter\n");
+	}
+}
+
+static void lm8323_reset(struct lm8323_chip *lm)
+{
+	/* The docs say we must pass 0xAA as the data byte. */
+	lm8323_write(lm, 2, LM8323_CMD_RESET, 0xAA);
+}
+
+static int lm8323_configure(struct lm8323_chip *lm)
+{
+	int keysize = (lm->size_x << 4) | lm->size_y;
+	int clock = (CLK_SLOWCLKEN | CLK_RCPWM_EXTERNAL);
+	int debounce = lm->debounce_time >> 2;
+	int active = lm->active_time >> 2;
+
+	/*
+	 * Active time must be greater than the debounce time: if it's
+	 * a close-run thing, give ourselves a 12ms buffer.
+	 */
+	if (debounce >= active)
+		active = debounce + 3;
+
+	lm8323_write(lm, 2, LM8323_CMD_WRITE_CFG, 0);
+	lm8323_write(lm, 2, LM8323_CMD_WRITE_CLOCK, clock);
+	lm8323_write(lm, 2, LM8323_CMD_SET_KEY_SIZE, keysize);
+	lm8323_set_active_time(lm, lm->active_time);
+	lm8323_write(lm, 2, LM8323_CMD_SET_DEBOUNCE, debounce);
+	lm8323_write(lm, 3, LM8323_CMD_WRITE_PORT_STATE, 0xff, 0xff);
+	lm8323_write(lm, 3, LM8323_CMD_WRITE_PORT_SEL, 0, 0);
+
+	/*
+	 * Not much we can do about errors at this point, so just hope
+	 * for the best.
+	 */
+
+	return 0;
+}
+
+static void pwm_done(struct lm8323_pwm *pwm)
+{
+	mutex_lock(&pwm->lock);
+	pwm->running = false;
+	if (pwm->desired_brightness != pwm->brightness)
+		schedule_work(&pwm->work);
+	mutex_unlock(&pwm->lock);
+}
+
+/*
+ * Bottom half: handle the interrupt by posting key events, or dealing with
+ * errors appropriately.
+ */
+static irqreturn_t lm8323_irq(int irq, void *_lm)
+{
+	struct lm8323_chip *lm = _lm;
+	u8 ints;
+	int i;
+
+	mutex_lock(&lm->lock);
+
+	while ((lm8323_read(lm, LM8323_CMD_READ_INT, &ints, 1) == 1) && ints) {
+		if (likely(ints & INT_KEYPAD))
+			process_keys(lm);
+		if (ints & INT_ROTATOR) {
+			/* We don't currently support the rotator. */
+			dev_vdbg(&lm->client->dev, "rotator fired\n");
+		}
+		if (ints & INT_ERROR) {
+			dev_vdbg(&lm->client->dev, "error!\n");
+			lm8323_process_error(lm);
+		}
+		if (ints & INT_NOINIT) {
+			dev_err(&lm->client->dev, "chip lost config; "
+						  "reinitialising\n");
+			lm8323_configure(lm);
+		}
+		for (i = 0; i < LM8323_NUM_PWMS; i++) {
+			if (ints & (INT_PWM1 << i)) {
+				dev_vdbg(&lm->client->dev,
+					 "pwm%d engine completed\n", i);
+				pwm_done(&lm->pwm[i]);
+			}
+		}
+	}
+
+	mutex_unlock(&lm->lock);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Read the chip ID.
+ */
+static int lm8323_read_id(struct lm8323_chip *lm, u8 *buf)
+{
+	int bytes;
+
+	bytes = lm8323_read(lm, LM8323_CMD_READ_ID, buf, 2);
+	if (unlikely(bytes != 2))
+		return -EIO;
+
+	return 0;
+}
+
+static void lm8323_write_pwm_one(struct lm8323_pwm *pwm, int pos, u16 cmd)
+{
+	lm8323_write(pwm->chip, 4, LM8323_CMD_PWM_WRITE, (pos << 2) | pwm->id,
+		     (cmd & 0xff00) >> 8, cmd & 0x00ff);
+}
+
+/*
+ * Write a script into a given PWM engine, concluding with PWM_END.
+ * If 'kill' is nonzero, the engine will be shut down at the end
+ * of the script, producing a zero output. Otherwise the engine
+ * will be kept running at the final PWM level indefinitely.
+ */
+static void lm8323_write_pwm(struct lm8323_pwm *pwm, int kill,
+			     int len, const u16 *cmds)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		lm8323_write_pwm_one(pwm, i, cmds[i]);
+
+	lm8323_write_pwm_one(pwm, i++, PWM_END(kill));
+	lm8323_write(pwm->chip, 2, LM8323_CMD_START_PWM, pwm->id);
+	pwm->running = true;
+}
+
+static void lm8323_pwm_work(struct work_struct *work)
+{
+	struct lm8323_pwm *pwm = work_to_pwm(work);
+	int div512, perstep, steps, hz, up, kill;
+	u16 pwm_cmds[3];
+	int num_cmds = 0;
+
+	mutex_lock(&pwm->lock);
+
+	/*
+	 * Do nothing if we're already at the requested level,
+	 * or previous setting is not yet complete. In the latter
+	 * case we will be called again when the previous PWM script
+	 * finishes.
+	 */
+	if (pwm->running || pwm->desired_brightness == pwm->brightness)
+		goto out;
+
+	kill = (pwm->desired_brightness == 0);
+	up = (pwm->desired_brightness > pwm->brightness);
+	steps = abs(pwm->desired_brightness - pwm->brightness);
+
+	/*
+	 * Convert time (in ms) into a divisor (512 or 16 on a refclk of
+	 * 32768Hz), and number of ticks per step.
+	 */
+	if ((pwm->fade_time / steps) > (32768 / 512)) {
+		div512 = 1;
+		hz = 32768 / 512;
+	} else {
+		div512 = 0;
+		hz = 32768 / 16;
+	}
+
+	perstep = (hz * pwm->fade_time) / (steps * 1000);
+
+	if (perstep == 0)
+		perstep = 1;
+	else if (perstep > 63)
+		perstep = 63;
+
+	while (steps) {
+		int s;
+
+		s = min(126, steps);
+		pwm_cmds[num_cmds++] = PWM_RAMP(div512, perstep, s, up);
+		steps -= s;
+	}
+
+	lm8323_write_pwm(pwm, kill, num_cmds, pwm_cmds);
+	pwm->brightness = pwm->desired_brightness;
+
+ out:
+	mutex_unlock(&pwm->lock);
+}
+
+static void lm8323_pwm_set_brightness(struct led_classdev *led_cdev,
+				      enum led_brightness brightness)
+{
+	struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
+	struct lm8323_chip *lm = pwm->chip;
+
+	mutex_lock(&pwm->lock);
+	pwm->desired_brightness = brightness;
+	mutex_unlock(&pwm->lock);
+
+	if (in_interrupt()) {
+		schedule_work(&pwm->work);
+	} else {
+		/*
+		 * Schedule PWM work as usual unless we are going into suspend
+		 */
+		mutex_lock(&lm->lock);
+		if (likely(!lm->pm_suspend))
+			schedule_work(&pwm->work);
+		else
+			lm8323_pwm_work(&pwm->work);
+		mutex_unlock(&lm->lock);
+	}
+}
+
+static ssize_t lm8323_pwm_show_time(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
+
+	return sprintf(buf, "%d\n", pwm->fade_time);
+}
+
+static ssize_t lm8323_pwm_store_time(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
+	int ret, time;
+
+	ret = kstrtoint(buf, 10, &time);
+	/* Numbers only, please. */
+	if (ret)
+		return ret;
+
+	pwm->fade_time = time;
+
+	return strlen(buf);
+}
+static DEVICE_ATTR(time, 0644, lm8323_pwm_show_time, lm8323_pwm_store_time);
+
+static struct attribute *lm8323_pwm_attrs[] = {
+	&dev_attr_time.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(lm8323_pwm);
+
+static int init_pwm(struct lm8323_chip *lm, int id, struct device *dev,
+		    const char *name)
+{
+	struct lm8323_pwm *pwm;
+
+	BUG_ON(id > 3);
+
+	pwm = &lm->pwm[id - 1];
+
+	pwm->id = id;
+	pwm->fade_time = 0;
+	pwm->brightness = 0;
+	pwm->desired_brightness = 0;
+	pwm->running = false;
+	pwm->enabled = false;
+	INIT_WORK(&pwm->work, lm8323_pwm_work);
+	mutex_init(&pwm->lock);
+	pwm->chip = lm;
+
+	if (name) {
+		pwm->cdev.name = name;
+		pwm->cdev.brightness_set = lm8323_pwm_set_brightness;
+		pwm->cdev.groups = lm8323_pwm_groups;
+		if (led_classdev_register(dev, &pwm->cdev) < 0) {
+			dev_err(dev, "couldn't register PWM %d\n", id);
+			return -1;
+		}
+		pwm->enabled = true;
+	}
+
+	return 0;
+}
+
+static struct i2c_driver lm8323_i2c_driver;
+
+static ssize_t lm8323_show_disable(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct lm8323_chip *lm = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", !lm->kp_enabled);
+}
+
+static ssize_t lm8323_set_disable(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct lm8323_chip *lm = dev_get_drvdata(dev);
+	int ret;
+	unsigned int i;
+
+	ret = kstrtouint(buf, 10, &i);
+	if (ret)
+		return ret;
+
+	mutex_lock(&lm->lock);
+	lm->kp_enabled = !i;
+	mutex_unlock(&lm->lock);
+
+	return count;
+}
+static DEVICE_ATTR(disable_kp, 0644, lm8323_show_disable, lm8323_set_disable);
+
+static int lm8323_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	struct lm8323_platform_data *pdata = dev_get_platdata(&client->dev);
+	struct input_dev *idev;
+	struct lm8323_chip *lm;
+	int pwm;
+	int i, err;
+	unsigned long tmo;
+	u8 data[2];
+
+	if (!pdata || !pdata->size_x || !pdata->size_y) {
+		dev_err(&client->dev, "missing platform_data\n");
+		return -EINVAL;
+	}
+
+	if (pdata->size_x > 8) {
+		dev_err(&client->dev, "invalid x size %d specified\n",
+			pdata->size_x);
+		return -EINVAL;
+	}
+
+	if (pdata->size_y > 12) {
+		dev_err(&client->dev, "invalid y size %d specified\n",
+			pdata->size_y);
+		return -EINVAL;
+	}
+
+	lm = kzalloc(sizeof *lm, GFP_KERNEL);
+	idev = input_allocate_device();
+	if (!lm || !idev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	lm->client = client;
+	lm->idev = idev;
+	mutex_init(&lm->lock);
+
+	lm->size_x = pdata->size_x;
+	lm->size_y = pdata->size_y;
+	dev_vdbg(&client->dev, "Keypad size: %d x %d\n",
+		 lm->size_x, lm->size_y);
+
+	lm->debounce_time = pdata->debounce_time;
+	lm->active_time = pdata->active_time;
+
+	lm8323_reset(lm);
+
+	/* Nothing's set up to service the IRQ yet, so just spin for max.
+	 * 100ms until we can configure. */
+	tmo = jiffies + msecs_to_jiffies(100);
+	while (lm8323_read(lm, LM8323_CMD_READ_INT, data, 1) == 1) {
+		if (data[0] & INT_NOINIT)
+			break;
+
+		if (time_after(jiffies, tmo)) {
+			dev_err(&client->dev,
+				"timeout waiting for initialisation\n");
+			break;
+		}
+
+		msleep(1);
+	}
+
+	lm8323_configure(lm);
+
+	/* If a true probe check the device */
+	if (lm8323_read_id(lm, data) != 0) {
+		dev_err(&client->dev, "device not found\n");
+		err = -ENODEV;
+		goto fail1;
+	}
+
+	for (pwm = 0; pwm < LM8323_NUM_PWMS; pwm++) {
+		err = init_pwm(lm, pwm + 1, &client->dev,
+			       pdata->pwm_names[pwm]);
+		if (err < 0)
+			goto fail2;
+	}
+
+	lm->kp_enabled = true;
+	err = device_create_file(&client->dev, &dev_attr_disable_kp);
+	if (err < 0)
+		goto fail2;
+
+	idev->name = pdata->name ? : "LM8323 keypad";
+	snprintf(lm->phys, sizeof(lm->phys),
+		 "%s/input-kp", dev_name(&client->dev));
+	idev->phys = lm->phys;
+
+	idev->evbit[0] = BIT(EV_KEY) | BIT(EV_MSC);
+	__set_bit(MSC_SCAN, idev->mscbit);
+	for (i = 0; i < LM8323_KEYMAP_SIZE; i++) {
+		__set_bit(pdata->keymap[i], idev->keybit);
+		lm->keymap[i] = pdata->keymap[i];
+	}
+	__clear_bit(KEY_RESERVED, idev->keybit);
+
+	if (pdata->repeat)
+		__set_bit(EV_REP, idev->evbit);
+
+	err = input_register_device(idev);
+	if (err) {
+		dev_dbg(&client->dev, "error registering input device\n");
+		goto fail3;
+	}
+
+	err = request_threaded_irq(client->irq, NULL, lm8323_irq,
+			  IRQF_TRIGGER_LOW|IRQF_ONESHOT, "lm8323", lm);
+	if (err) {
+		dev_err(&client->dev, "could not get IRQ %d\n", client->irq);
+		goto fail4;
+	}
+
+	i2c_set_clientdata(client, lm);
+
+	device_init_wakeup(&client->dev, 1);
+	enable_irq_wake(client->irq);
+
+	return 0;
+
+fail4:
+	input_unregister_device(idev);
+	idev = NULL;
+fail3:
+	device_remove_file(&client->dev, &dev_attr_disable_kp);
+fail2:
+	while (--pwm >= 0)
+		if (lm->pwm[pwm].enabled)
+			led_classdev_unregister(&lm->pwm[pwm].cdev);
+fail1:
+	input_free_device(idev);
+	kfree(lm);
+	return err;
+}
+
+static int lm8323_remove(struct i2c_client *client)
+{
+	struct lm8323_chip *lm = i2c_get_clientdata(client);
+	int i;
+
+	disable_irq_wake(client->irq);
+	free_irq(client->irq, lm);
+
+	input_unregister_device(lm->idev);
+
+	device_remove_file(&lm->client->dev, &dev_attr_disable_kp);
+
+	for (i = 0; i < 3; i++)
+		if (lm->pwm[i].enabled)
+			led_classdev_unregister(&lm->pwm[i].cdev);
+
+	kfree(lm);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * We don't need to explicitly suspend the chip, as it already switches off
+ * when there's no activity.
+ */
+static int lm8323_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm8323_chip *lm = i2c_get_clientdata(client);
+	int i;
+
+	irq_set_irq_wake(client->irq, 0);
+	disable_irq(client->irq);
+
+	mutex_lock(&lm->lock);
+	lm->pm_suspend = true;
+	mutex_unlock(&lm->lock);
+
+	for (i = 0; i < 3; i++)
+		if (lm->pwm[i].enabled)
+			led_classdev_suspend(&lm->pwm[i].cdev);
+
+	return 0;
+}
+
+static int lm8323_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm8323_chip *lm = i2c_get_clientdata(client);
+	int i;
+
+	mutex_lock(&lm->lock);
+	lm->pm_suspend = false;
+	mutex_unlock(&lm->lock);
+
+	for (i = 0; i < 3; i++)
+		if (lm->pwm[i].enabled)
+			led_classdev_resume(&lm->pwm[i].cdev);
+
+	enable_irq(client->irq);
+	irq_set_irq_wake(client->irq, 1);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(lm8323_pm_ops, lm8323_suspend, lm8323_resume);
+
+static const struct i2c_device_id lm8323_id[] = {
+	{ "lm8323", 0 },
+	{ }
+};
+
+static struct i2c_driver lm8323_i2c_driver = {
+	.driver = {
+		.name	= "lm8323",
+		.pm	= &lm8323_pm_ops,
+	},
+	.probe		= lm8323_probe,
+	.remove		= lm8323_remove,
+	.id_table	= lm8323_id,
+};
+MODULE_DEVICE_TABLE(i2c, lm8323_id);
+
+module_i2c_driver(lm8323_i2c_driver);
+
+MODULE_AUTHOR("Timo O. Karjalainen <timo.o.karjalainen@nokia.com>");
+MODULE_AUTHOR("Daniel Stone");
+MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");
+MODULE_DESCRIPTION("LM8323 keypad driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/keyboard/lm8333.c b/drivers/input/keyboard/lm8333.c
new file mode 100644
index 0000000..c717e8f
--- /dev/null
+++ b/drivers/input/keyboard/lm8333.c
@@ -0,0 +1,235 @@
+/*
+ * LM8333 keypad driver
+ * Copyright (C) 2012 Wolfram Sang, Pengutronix <kernel@pengutronix.de>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/input/lm8333.h>
+
+#define LM8333_FIFO_READ		0x20
+#define LM8333_DEBOUNCE			0x22
+#define LM8333_READ_INT			0xD0
+#define LM8333_ACTIVE			0xE4
+#define LM8333_READ_ERROR		0xF0
+
+#define LM8333_KEYPAD_IRQ		(1 << 0)
+#define LM8333_ERROR_IRQ		(1 << 3)
+
+#define LM8333_ERROR_KEYOVR		0x04
+#define LM8333_ERROR_FIFOOVR		0x40
+
+#define LM8333_FIFO_TRANSFER_SIZE	16
+
+#define LM8333_NUM_ROWS		8
+#define LM8333_NUM_COLS		16
+#define LM8333_ROW_SHIFT	4
+
+struct lm8333 {
+	struct i2c_client *client;
+	struct input_dev *input;
+	unsigned short keycodes[LM8333_NUM_ROWS << LM8333_ROW_SHIFT];
+};
+
+/* The accessors try twice because the first access may be needed for wakeup */
+#define LM8333_READ_RETRIES 2
+
+int lm8333_read8(struct lm8333 *lm8333, u8 cmd)
+{
+	int retries = 0, ret;
+
+	do {
+		ret = i2c_smbus_read_byte_data(lm8333->client, cmd);
+	} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
+
+	return ret;
+}
+
+int lm8333_write8(struct lm8333 *lm8333, u8 cmd, u8 val)
+{
+	int retries = 0, ret;
+
+	do {
+		ret = i2c_smbus_write_byte_data(lm8333->client, cmd, val);
+	} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
+
+	return ret;
+}
+
+int lm8333_read_block(struct lm8333 *lm8333, u8 cmd, u8 len, u8 *buf)
+{
+	int retries = 0, ret;
+
+	do {
+		ret = i2c_smbus_read_i2c_block_data(lm8333->client,
+						    cmd, len, buf);
+	} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
+
+	return ret;
+}
+
+static void lm8333_key_handler(struct lm8333 *lm8333)
+{
+	struct input_dev *input = lm8333->input;
+	u8 keys[LM8333_FIFO_TRANSFER_SIZE];
+	u8 code, pressed;
+	int i, ret;
+
+	ret = lm8333_read_block(lm8333, LM8333_FIFO_READ,
+				LM8333_FIFO_TRANSFER_SIZE, keys);
+	if (ret != LM8333_FIFO_TRANSFER_SIZE) {
+		dev_err(&lm8333->client->dev,
+			"Error %d while reading FIFO\n", ret);
+		return;
+	}
+
+	for (i = 0; i < LM8333_FIFO_TRANSFER_SIZE && keys[i]; i++) {
+		pressed = keys[i] & 0x80;
+		code = keys[i] & 0x7f;
+
+		input_event(input, EV_MSC, MSC_SCAN, code);
+		input_report_key(input, lm8333->keycodes[code], pressed);
+	}
+
+	input_sync(input);
+}
+
+static irqreturn_t lm8333_irq_thread(int irq, void *data)
+{
+	struct lm8333 *lm8333 = data;
+	u8 status = lm8333_read8(lm8333, LM8333_READ_INT);
+
+	if (!status)
+		return IRQ_NONE;
+
+	if (status & LM8333_ERROR_IRQ) {
+		u8 err = lm8333_read8(lm8333, LM8333_READ_ERROR);
+
+		if (err & (LM8333_ERROR_KEYOVR | LM8333_ERROR_FIFOOVR)) {
+			u8 dummy[LM8333_FIFO_TRANSFER_SIZE];
+
+			lm8333_read_block(lm8333, LM8333_FIFO_READ,
+					LM8333_FIFO_TRANSFER_SIZE, dummy);
+		}
+		dev_err(&lm8333->client->dev, "Got error %02x\n", err);
+	}
+
+	if (status & LM8333_KEYPAD_IRQ)
+		lm8333_key_handler(lm8333);
+
+	return IRQ_HANDLED;
+}
+
+static int lm8333_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	const struct lm8333_platform_data *pdata =
+			dev_get_platdata(&client->dev);
+	struct lm8333 *lm8333;
+	struct input_dev *input;
+	int err, active_time;
+
+	if (!pdata)
+		return -EINVAL;
+
+	active_time = pdata->active_time ?: 500;
+	if (active_time / 3 <= pdata->debounce_time / 3) {
+		dev_err(&client->dev, "Active time not big enough!\n");
+		return -EINVAL;
+	}
+
+	lm8333 = kzalloc(sizeof(*lm8333), GFP_KERNEL);
+	input = input_allocate_device();
+	if (!lm8333 || !input) {
+		err = -ENOMEM;
+		goto free_mem;
+	}
+
+	lm8333->client = client;
+	lm8333->input = input;
+
+	input->name = client->name;
+	input->dev.parent = &client->dev;
+	input->id.bustype = BUS_I2C;
+
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+
+	err = matrix_keypad_build_keymap(pdata->matrix_data, NULL,
+					 LM8333_NUM_ROWS, LM8333_NUM_COLS,
+					 lm8333->keycodes, input);
+	if (err)
+		goto free_mem;
+
+	if (pdata->debounce_time) {
+		err = lm8333_write8(lm8333, LM8333_DEBOUNCE,
+				    pdata->debounce_time / 3);
+		if (err)
+			dev_warn(&client->dev, "Unable to set debounce time\n");
+	}
+
+	if (pdata->active_time) {
+		err = lm8333_write8(lm8333, LM8333_ACTIVE,
+				    pdata->active_time / 3);
+		if (err)
+			dev_warn(&client->dev, "Unable to set active time\n");
+	}
+
+	err = request_threaded_irq(client->irq, NULL, lm8333_irq_thread,
+				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				   "lm8333", lm8333);
+	if (err)
+		goto free_mem;
+
+	err = input_register_device(input);
+	if (err)
+		goto free_irq;
+
+	i2c_set_clientdata(client, lm8333);
+	return 0;
+
+ free_irq:
+	free_irq(client->irq, lm8333);
+ free_mem:
+	input_free_device(input);
+	kfree(lm8333);
+	return err;
+}
+
+static int lm8333_remove(struct i2c_client *client)
+{
+	struct lm8333 *lm8333 = i2c_get_clientdata(client);
+
+	free_irq(client->irq, lm8333);
+	input_unregister_device(lm8333->input);
+	kfree(lm8333);
+
+	return 0;
+}
+
+static const struct i2c_device_id lm8333_id[] = {
+	{ "lm8333", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, lm8333_id);
+
+static struct i2c_driver lm8333_driver = {
+	.driver = {
+		.name		= "lm8333",
+	},
+	.probe		= lm8333_probe,
+	.remove		= lm8333_remove,
+	.id_table	= lm8333_id,
+};
+module_i2c_driver(lm8333_driver);
+
+MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
+MODULE_DESCRIPTION("LM8333 keyboard driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c
new file mode 100644
index 0000000..c94d610
--- /dev/null
+++ b/drivers/input/keyboard/locomokbd.c
@@ -0,0 +1,362 @@
+/*
+ * LoCoMo keyboard driver for Linux-based ARM PDAs:
+ * 	- SHARP Zaurus Collie (SL-5500)
+ * 	- SHARP Zaurus Poodle (SL-5600)
+ *
+ * Copyright (c) 2005 John Lenz
+ * Based on from xtkbd.c
+ *
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+
+#include <asm/hardware/locomo.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
+MODULE_DESCRIPTION("LoCoMo keyboard driver");
+MODULE_LICENSE("GPL");
+
+#define LOCOMOKBD_NUMKEYS	128
+
+#define KEY_ACTIVITY		KEY_F16
+#define KEY_CONTACT		KEY_F18
+#define KEY_CENTER		KEY_F15
+
+static const unsigned char
+locomokbd_keycode[LOCOMOKBD_NUMKEYS] = {
+	0, KEY_ESC, KEY_ACTIVITY, 0, 0, 0, 0, 0, 0, 0,				/* 0 - 9 */
+	0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_HOME, KEY_CONTACT,			/* 10 - 19 */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,						/* 20 - 29 */
+	0, 0, 0, KEY_CENTER, 0, KEY_MAIL, 0, 0, 0, 0,				/* 30 - 39 */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RIGHT,					/* 40 - 49 */
+	KEY_UP, KEY_LEFT, 0, 0, KEY_P, 0, KEY_O, KEY_I, KEY_Y, KEY_T,		/* 50 - 59 */
+	KEY_E, KEY_W, 0, 0, 0, 0, KEY_DOWN, KEY_ENTER, 0, 0,			/* 60 - 69 */
+	KEY_BACKSPACE, 0, KEY_L, KEY_U, KEY_H, KEY_R, KEY_D, KEY_Q, 0, 0,	/* 70 - 79 */
+	0, 0, 0, 0, 0, 0, KEY_ENTER, KEY_RIGHTSHIFT, KEY_K, KEY_J,		/* 80 - 89 */
+	KEY_G, KEY_F, KEY_X, KEY_S, 0, 0, 0, 0, 0, 0,				/* 90 - 99 */
+	0, 0, KEY_DOT, 0, KEY_COMMA, KEY_N, KEY_B, KEY_C, KEY_Z, KEY_A,		/* 100 - 109 */
+	KEY_LEFTSHIFT, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0, 0, 0,		/* 110 - 119 */
+	KEY_M, KEY_SPACE, KEY_V, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0		/* 120 - 128 */
+};
+
+#define KB_ROWS			16
+#define KB_COLS			8
+#define KB_ROWMASK(r)		(1 << (r))
+#define SCANCODE(c,r)		( ((c)<<4) + (r) + 1 )
+
+#define KB_DELAY		8
+#define SCAN_INTERVAL		(HZ/10)
+
+struct locomokbd {
+	unsigned char keycode[LOCOMOKBD_NUMKEYS];
+	struct input_dev *input;
+	char phys[32];
+
+	unsigned long base;
+	spinlock_t lock;
+
+	struct timer_list timer;
+	unsigned long suspend_jiffies;
+	unsigned int count_cancel;
+};
+
+/* helper functions for reading the keyboard matrix */
+static inline void locomokbd_charge_all(unsigned long membase)
+{
+	locomo_writel(0x00FF, membase + LOCOMO_KSC);
+}
+
+static inline void locomokbd_activate_all(unsigned long membase)
+{
+	unsigned long r;
+
+	locomo_writel(0, membase + LOCOMO_KSC);
+	r = locomo_readl(membase + LOCOMO_KIC);
+	r &= 0xFEFF;
+	locomo_writel(r, membase + LOCOMO_KIC);
+}
+
+static inline void locomokbd_activate_col(unsigned long membase, int col)
+{
+	unsigned short nset;
+	unsigned short nbset;
+
+	nset = 0xFF & ~(1 << col);
+	nbset = (nset << 8) + nset;
+	locomo_writel(nbset, membase + LOCOMO_KSC);
+}
+
+static inline void locomokbd_reset_col(unsigned long membase, int col)
+{
+	unsigned short nbset;
+
+	nbset = ((0xFF & ~(1 << col)) << 8) + 0xFF;
+	locomo_writel(nbset, membase + LOCOMO_KSC);
+}
+
+/*
+ * The LoCoMo keyboard only generates interrupts when a key is pressed.
+ * So when a key is pressed, we enable a timer.  This timer scans the
+ * keyboard, and this is how we detect when the key is released.
+ */
+
+/* Scan the hardware keyboard and push any changes up through the input layer */
+static void locomokbd_scankeyboard(struct locomokbd *locomokbd)
+{
+	unsigned int row, col, rowd;
+	unsigned long flags;
+	unsigned int num_pressed;
+	unsigned long membase = locomokbd->base;
+
+	spin_lock_irqsave(&locomokbd->lock, flags);
+
+	locomokbd_charge_all(membase);
+
+	num_pressed = 0;
+	for (col = 0; col < KB_COLS; col++) {
+
+		locomokbd_activate_col(membase, col);
+		udelay(KB_DELAY);
+
+		rowd = ~locomo_readl(membase + LOCOMO_KIB);
+		for (row = 0; row < KB_ROWS; row++) {
+			unsigned int scancode, pressed, key;
+
+			scancode = SCANCODE(col, row);
+			pressed = rowd & KB_ROWMASK(row);
+			key = locomokbd->keycode[scancode];
+
+			input_report_key(locomokbd->input, key, pressed);
+			if (likely(!pressed))
+				continue;
+
+			num_pressed++;
+
+			/* The "Cancel/ESC" key is labeled "On/Off" on
+			 * Collie and Poodle and should suspend the device
+			 * if it was pressed for more than a second. */
+			if (unlikely(key == KEY_ESC)) {
+				if (!time_after(jiffies,
+					locomokbd->suspend_jiffies + HZ))
+					continue;
+				if (locomokbd->count_cancel++
+					!= (HZ/SCAN_INTERVAL + 1))
+					continue;
+				input_event(locomokbd->input, EV_PWR,
+					KEY_SUSPEND, 1);
+				locomokbd->suspend_jiffies = jiffies;
+			} else
+				locomokbd->count_cancel = 0;
+		}
+		locomokbd_reset_col(membase, col);
+	}
+	locomokbd_activate_all(membase);
+
+	input_sync(locomokbd->input);
+
+	/* if any keys are pressed, enable the timer */
+	if (num_pressed)
+		mod_timer(&locomokbd->timer, jiffies + SCAN_INTERVAL);
+	else
+		locomokbd->count_cancel = 0;
+
+	spin_unlock_irqrestore(&locomokbd->lock, flags);
+}
+
+/*
+ * LoCoMo keyboard interrupt handler.
+ */
+static irqreturn_t locomokbd_interrupt(int irq, void *dev_id)
+{
+	struct locomokbd *locomokbd = dev_id;
+	u16 r;
+
+	r = locomo_readl(locomokbd->base + LOCOMO_KIC);
+	if ((r & 0x0001) == 0)
+		return IRQ_HANDLED;
+
+	locomo_writel(r & ~0x0100, locomokbd->base + LOCOMO_KIC); /* Ack */
+
+	/** wait chattering delay **/
+	udelay(100);
+
+	locomokbd_scankeyboard(locomokbd);
+	return IRQ_HANDLED;
+}
+
+/*
+ * LoCoMo timer checking for released keys
+ */
+static void locomokbd_timer_callback(unsigned long data)
+{
+	struct locomokbd *locomokbd = (struct locomokbd *) data;
+
+	locomokbd_scankeyboard(locomokbd);
+}
+
+static int locomokbd_open(struct input_dev *dev)
+{
+	struct locomokbd *locomokbd = input_get_drvdata(dev);
+	u16 r;
+	
+	r = locomo_readl(locomokbd->base + LOCOMO_KIC) | 0x0010;
+	locomo_writel(r, locomokbd->base + LOCOMO_KIC);
+	return 0;
+}
+
+static void locomokbd_close(struct input_dev *dev)
+{
+	struct locomokbd *locomokbd = input_get_drvdata(dev);
+	u16 r;
+	
+	r = locomo_readl(locomokbd->base + LOCOMO_KIC) & ~0x0010;
+	locomo_writel(r, locomokbd->base + LOCOMO_KIC);
+}
+
+static int locomokbd_probe(struct locomo_dev *dev)
+{
+	struct locomokbd *locomokbd;
+	struct input_dev *input_dev;
+	int i, err;
+
+	locomokbd = kzalloc(sizeof(struct locomokbd), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!locomokbd || !input_dev) {
+		err = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	/* try and claim memory region */
+	if (!request_mem_region((unsigned long) dev->mapbase,
+				dev->length,
+				LOCOMO_DRIVER_NAME(dev))) {
+		err = -EBUSY;
+		printk(KERN_ERR "locomokbd: Can't acquire access to io memory for keyboard\n");
+		goto err_free_mem;
+	}
+
+	locomo_set_drvdata(dev, locomokbd);
+
+	locomokbd->base = (unsigned long) dev->mapbase;
+
+	spin_lock_init(&locomokbd->lock);
+
+	init_timer(&locomokbd->timer);
+	locomokbd->timer.function = locomokbd_timer_callback;
+	locomokbd->timer.data = (unsigned long) locomokbd;
+
+	locomokbd->suspend_jiffies = jiffies;
+
+	locomokbd->input = input_dev;
+	strcpy(locomokbd->phys, "locomokbd/input0");
+
+	input_dev->name = "LoCoMo keyboard";
+	input_dev->phys = locomokbd->phys;
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->open = locomokbd_open;
+	input_dev->close = locomokbd_close;
+	input_dev->dev.parent = &dev->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
+				BIT_MASK(EV_PWR);
+	input_dev->keycode = locomokbd->keycode;
+	input_dev->keycodesize = sizeof(locomokbd_keycode[0]);
+	input_dev->keycodemax = ARRAY_SIZE(locomokbd_keycode);
+
+	input_set_drvdata(input_dev, locomokbd);
+
+	memcpy(locomokbd->keycode, locomokbd_keycode, sizeof(locomokbd->keycode));
+	for (i = 0; i < LOCOMOKBD_NUMKEYS; i++)
+		set_bit(locomokbd->keycode[i], input_dev->keybit);
+	clear_bit(0, input_dev->keybit);
+
+	/* attempt to get the interrupt */
+	err = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd);
+	if (err) {
+		printk(KERN_ERR "locomokbd: Can't get irq for keyboard\n");
+		goto err_release_region;
+	}
+
+	err = input_register_device(locomokbd->input);
+	if (err)
+		goto err_free_irq;
+
+	return 0;
+
+ err_free_irq:
+	free_irq(dev->irq[0], locomokbd);
+ err_release_region:
+	release_mem_region((unsigned long) dev->mapbase, dev->length);
+	locomo_set_drvdata(dev, NULL);
+ err_free_mem:
+	input_free_device(input_dev);
+	kfree(locomokbd);
+
+	return err;
+}
+
+static int locomokbd_remove(struct locomo_dev *dev)
+{
+	struct locomokbd *locomokbd = locomo_get_drvdata(dev);
+
+	free_irq(dev->irq[0], locomokbd);
+
+	del_timer_sync(&locomokbd->timer);
+
+	input_unregister_device(locomokbd->input);
+	locomo_set_drvdata(dev, NULL);
+
+	release_mem_region((unsigned long) dev->mapbase, dev->length);
+
+	kfree(locomokbd);
+
+	return 0;
+}
+
+static struct locomo_driver keyboard_driver = {
+	.drv = {
+		.name = "locomokbd"
+	},
+	.devid	= LOCOMO_DEVID_KEYBOARD,
+	.probe	= locomokbd_probe,
+	.remove	= locomokbd_remove,
+};
+
+static int __init locomokbd_init(void)
+{
+	return locomo_driver_register(&keyboard_driver);
+}
+
+static void __exit locomokbd_exit(void)
+{
+	locomo_driver_unregister(&keyboard_driver);
+}
+
+module_init(locomokbd_init);
+module_exit(locomokbd_exit);
diff --git a/drivers/input/keyboard/lpc32xx-keys.c b/drivers/input/keyboard/lpc32xx-keys.c
new file mode 100644
index 0000000..265d641
--- /dev/null
+++ b/drivers/input/keyboard/lpc32xx-keys.c
@@ -0,0 +1,342 @@
+/*
+ * NXP LPC32xx SoC Key Scan Interface
+ *
+ * Authors:
+ *    Kevin Wells <kevin.wells@nxp.com>
+ *    Roland Stigge <stigge@antcom.de>
+ *
+ * Copyright (C) 2010 NXP Semiconductors
+ * Copyright (C) 2012 Roland Stigge
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ *
+ * This controller supports square key matrices from 1x1 up to 8x8
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/input/matrix_keypad.h>
+
+#define DRV_NAME				"lpc32xx_keys"
+
+/*
+ * Key scanner register offsets
+ */
+#define LPC32XX_KS_DEB(x)			((x) + 0x00)
+#define LPC32XX_KS_STATE_COND(x)		((x) + 0x04)
+#define LPC32XX_KS_IRQ(x)			((x) + 0x08)
+#define LPC32XX_KS_SCAN_CTL(x)			((x) + 0x0C)
+#define LPC32XX_KS_FAST_TST(x)			((x) + 0x10)
+#define LPC32XX_KS_MATRIX_DIM(x)		((x) + 0x14) /* 1..8 */
+#define LPC32XX_KS_DATA(x, y)			((x) + 0x40 + ((y) << 2))
+
+#define LPC32XX_KSCAN_DEB_NUM_DEB_PASS(n)	((n) & 0xFF)
+
+#define LPC32XX_KSCAN_SCOND_IN_IDLE		0x0
+#define LPC32XX_KSCAN_SCOND_IN_SCANONCE		0x1
+#define LPC32XX_KSCAN_SCOND_IN_IRQGEN		0x2
+#define LPC32XX_KSCAN_SCOND_IN_SCAN_MATRIX	0x3
+
+#define LPC32XX_KSCAN_IRQ_PENDING_CLR		0x1
+
+#define LPC32XX_KSCAN_SCTRL_SCAN_DELAY(n)	((n) & 0xFF)
+
+#define LPC32XX_KSCAN_FTST_FORCESCANONCE	0x1
+#define LPC32XX_KSCAN_FTST_USE32K_CLK		0x2
+
+#define LPC32XX_KSCAN_MSEL_SELECT(n)		((n) & 0xF)
+
+struct lpc32xx_kscan_drv {
+	struct input_dev *input;
+	struct clk *clk;
+	void __iomem *kscan_base;
+	unsigned int irq;
+
+	u32 matrix_sz;		/* Size of matrix in XxY, ie. 3 = 3x3 */
+	u32 deb_clks;		/* Debounce clocks (based on 32KHz clock) */
+	u32 scan_delay;		/* Scan delay (based on 32KHz clock) */
+
+	unsigned short *keymap;	/* Pointer to key map for the scan matrix */
+	unsigned int row_shift;
+
+	u8 lastkeystates[8];
+};
+
+static void lpc32xx_mod_states(struct lpc32xx_kscan_drv *kscandat, int col)
+{
+	struct input_dev *input = kscandat->input;
+	unsigned row, changed, scancode, keycode;
+	u8 key;
+
+	key = readl(LPC32XX_KS_DATA(kscandat->kscan_base, col));
+	changed = key ^ kscandat->lastkeystates[col];
+	kscandat->lastkeystates[col] = key;
+
+	for (row = 0; changed; row++, changed >>= 1) {
+		if (changed & 1) {
+			/* Key state changed, signal an event */
+			scancode = MATRIX_SCAN_CODE(row, col,
+						    kscandat->row_shift);
+			keycode = kscandat->keymap[scancode];
+			input_event(input, EV_MSC, MSC_SCAN, scancode);
+			input_report_key(input, keycode, key & (1 << row));
+		}
+	}
+}
+
+static irqreturn_t lpc32xx_kscan_irq(int irq, void *dev_id)
+{
+	struct lpc32xx_kscan_drv *kscandat = dev_id;
+	int i;
+
+	for (i = 0; i < kscandat->matrix_sz; i++)
+		lpc32xx_mod_states(kscandat, i);
+
+	writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
+
+	input_sync(kscandat->input);
+
+	return IRQ_HANDLED;
+}
+
+static int lpc32xx_kscan_open(struct input_dev *dev)
+{
+	struct lpc32xx_kscan_drv *kscandat = input_get_drvdata(dev);
+	int error;
+
+	error = clk_prepare_enable(kscandat->clk);
+	if (error)
+		return error;
+
+	writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
+
+	return 0;
+}
+
+static void lpc32xx_kscan_close(struct input_dev *dev)
+{
+	struct lpc32xx_kscan_drv *kscandat = input_get_drvdata(dev);
+
+	writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
+	clk_disable_unprepare(kscandat->clk);
+}
+
+static int lpc32xx_parse_dt(struct device *dev,
+				      struct lpc32xx_kscan_drv *kscandat)
+{
+	struct device_node *np = dev->of_node;
+	u32 rows = 0, columns = 0;
+	int err;
+
+	err = matrix_keypad_parse_of_params(dev, &rows, &columns);
+	if (err)
+		return err;
+	if (rows != columns) {
+		dev_err(dev, "rows and columns must be equal!\n");
+		return -EINVAL;
+	}
+
+	kscandat->matrix_sz = rows;
+	kscandat->row_shift = get_count_order(columns);
+
+	of_property_read_u32(np, "nxp,debounce-delay-ms", &kscandat->deb_clks);
+	of_property_read_u32(np, "nxp,scan-delay-ms", &kscandat->scan_delay);
+	if (!kscandat->deb_clks || !kscandat->scan_delay) {
+		dev_err(dev, "debounce or scan delay not specified\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int lpc32xx_kscan_probe(struct platform_device *pdev)
+{
+	struct lpc32xx_kscan_drv *kscandat;
+	struct input_dev *input;
+	struct resource *res;
+	size_t keymap_size;
+	int error;
+	int irq;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get platform I/O memory\n");
+		return -EINVAL;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0 || irq >= NR_IRQS) {
+		dev_err(&pdev->dev, "failed to get platform irq\n");
+		return -EINVAL;
+	}
+
+	kscandat = devm_kzalloc(&pdev->dev, sizeof(*kscandat),
+				GFP_KERNEL);
+	if (!kscandat)
+		return -ENOMEM;
+
+	error = lpc32xx_parse_dt(&pdev->dev, kscandat);
+	if (error) {
+		dev_err(&pdev->dev, "failed to parse device tree\n");
+		return error;
+	}
+
+	keymap_size = sizeof(kscandat->keymap[0]) *
+				(kscandat->matrix_sz << kscandat->row_shift);
+	kscandat->keymap = devm_kzalloc(&pdev->dev, keymap_size, GFP_KERNEL);
+	if (!kscandat->keymap)
+		return -ENOMEM;
+
+	kscandat->input = input = devm_input_allocate_device(&pdev->dev);
+	if (!input) {
+		dev_err(&pdev->dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	/* Setup key input */
+	input->name		= pdev->name;
+	input->phys		= "lpc32xx/input0";
+	input->id.vendor	= 0x0001;
+	input->id.product	= 0x0001;
+	input->id.version	= 0x0100;
+	input->open		= lpc32xx_kscan_open;
+	input->close		= lpc32xx_kscan_close;
+	input->dev.parent	= &pdev->dev;
+
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+
+	error = matrix_keypad_build_keymap(NULL, NULL,
+					   kscandat->matrix_sz,
+					   kscandat->matrix_sz,
+					   kscandat->keymap, kscandat->input);
+	if (error) {
+		dev_err(&pdev->dev, "failed to build keymap\n");
+		return error;
+	}
+
+	input_set_drvdata(kscandat->input, kscandat);
+
+	kscandat->kscan_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(kscandat->kscan_base))
+		return PTR_ERR(kscandat->kscan_base);
+
+	/* Get the key scanner clock */
+	kscandat->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(kscandat->clk)) {
+		dev_err(&pdev->dev, "failed to get clock\n");
+		return PTR_ERR(kscandat->clk);
+	}
+
+	/* Configure the key scanner */
+	error = clk_prepare_enable(kscandat->clk);
+	if (error)
+		return error;
+
+	writel(kscandat->deb_clks, LPC32XX_KS_DEB(kscandat->kscan_base));
+	writel(kscandat->scan_delay, LPC32XX_KS_SCAN_CTL(kscandat->kscan_base));
+	writel(LPC32XX_KSCAN_FTST_USE32K_CLK,
+	       LPC32XX_KS_FAST_TST(kscandat->kscan_base));
+	writel(kscandat->matrix_sz,
+	       LPC32XX_KS_MATRIX_DIM(kscandat->kscan_base));
+	writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
+	clk_disable_unprepare(kscandat->clk);
+
+	error = devm_request_irq(&pdev->dev, irq, lpc32xx_kscan_irq, 0,
+				 pdev->name, kscandat);
+	if (error) {
+		dev_err(&pdev->dev, "failed to request irq\n");
+		return error;
+	}
+
+	error = input_register_device(kscandat->input);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		return error;
+	}
+
+	platform_set_drvdata(pdev, kscandat);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int lpc32xx_kscan_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev);
+	struct input_dev *input = kscandat->input;
+
+	mutex_lock(&input->mutex);
+
+	if (input->users) {
+		/* Clear IRQ and disable clock */
+		writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
+		clk_disable_unprepare(kscandat->clk);
+	}
+
+	mutex_unlock(&input->mutex);
+	return 0;
+}
+
+static int lpc32xx_kscan_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev);
+	struct input_dev *input = kscandat->input;
+	int retval = 0;
+
+	mutex_lock(&input->mutex);
+
+	if (input->users) {
+		/* Enable clock and clear IRQ */
+		retval = clk_prepare_enable(kscandat->clk);
+		if (retval == 0)
+			writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
+	}
+
+	mutex_unlock(&input->mutex);
+	return retval;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(lpc32xx_kscan_pm_ops, lpc32xx_kscan_suspend,
+			 lpc32xx_kscan_resume);
+
+static const struct of_device_id lpc32xx_kscan_match[] = {
+	{ .compatible = "nxp,lpc3220-key" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_kscan_match);
+
+static struct platform_driver lpc32xx_kscan_driver = {
+	.probe		= lpc32xx_kscan_probe,
+	.driver		= {
+		.name	= DRV_NAME,
+		.pm	= &lpc32xx_kscan_pm_ops,
+		.of_match_table = lpc32xx_kscan_match,
+	}
+};
+
+module_platform_driver(lpc32xx_kscan_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_DESCRIPTION("Key scanner driver for LPC32XX devices");
diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c
new file mode 100644
index 0000000..5aa2361
--- /dev/null
+++ b/drivers/input/keyboard/maple_keyb.c
@@ -0,0 +1,260 @@
+/*
+ * SEGA Dreamcast keyboard driver
+ * Based on drivers/usb/usbkbd.c
+ * Copyright (c) YAEGASHI Takeshi, 2001
+ * Porting to 2.6 Copyright (c) Adrian McMenamin, 2007 - 2009
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/maple.h>
+
+/* Very simple mutex to ensure proper cleanup */
+static DEFINE_MUTEX(maple_keyb_mutex);
+
+#define NR_SCANCODES 256
+
+MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk");
+MODULE_DESCRIPTION("SEGA Dreamcast keyboard driver");
+MODULE_LICENSE("GPL");
+
+struct dc_kbd {
+	struct input_dev *dev;
+	unsigned short keycode[NR_SCANCODES];
+	unsigned char new[8];
+	unsigned char old[8];
+};
+
+static const unsigned short dc_kbd_keycode[NR_SCANCODES] = {
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_A, KEY_B,
+	KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L,
+	KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V,
+	KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6,
+	KEY_7, KEY_8, KEY_9, KEY_0, KEY_ENTER, KEY_ESC, KEY_BACKSPACE,
+	KEY_TAB, KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE,
+	KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_BACKSLASH, KEY_SEMICOLON,
+	KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, KEY_DOT, KEY_SLASH,
+	KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6,
+	KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_SYSRQ,
+	KEY_SCROLLLOCK, KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP,
+	KEY_DELETE, KEY_END, KEY_PAGEDOWN, KEY_RIGHT, KEY_LEFT, KEY_DOWN,
+	KEY_UP, KEY_NUMLOCK, KEY_KPSLASH, KEY_KPASTERISK, KEY_KPMINUS,
+	KEY_KPPLUS, KEY_KPENTER, KEY_KP1, KEY_KP2, KEY_KP3, KEY_KP4, KEY_KP5,
+	KEY_KP6, KEY_KP7, KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT, KEY_102ND,
+	KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, KEY_F13, KEY_F14, KEY_F15,
+	KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22,
+	KEY_F23, KEY_F24, KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT, KEY_STOP,
+	KEY_AGAIN, KEY_UNDO, KEY_CUT, KEY_COPY, KEY_PASTE, KEY_FIND, KEY_MUTE,
+	KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_KPCOMMA, KEY_RESERVED, KEY_RO, KEY_KATAKANAHIRAGANA , KEY_YEN,
+	KEY_HENKAN, KEY_MUHENKAN, KEY_KPJPCOMMA, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_HANGEUL, KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA,
+	KEY_ZENKAKUHANKAKU, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+	KEY_RESERVED, KEY_RESERVED, KEY_LEFTCTRL, KEY_LEFTSHIFT, KEY_LEFTALT,
+	KEY_LEFTMETA, KEY_RIGHTCTRL, KEY_RIGHTSHIFT, KEY_RIGHTALT,
+	KEY_RIGHTMETA, KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG,
+	KEY_NEXTSONG, KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE,
+	KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, KEY_FIND, KEY_SCROLLUP,
+	KEY_SCROLLDOWN, KEY_EDIT, KEY_SLEEP, KEY_SCREENLOCK, KEY_REFRESH,
+	KEY_CALC, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED
+};
+
+static void dc_scan_kbd(struct dc_kbd *kbd)
+{
+	struct input_dev *dev = kbd->dev;
+	void *ptr;
+	int code, keycode;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		code = i + 224;
+		keycode = kbd->keycode[code];
+		input_event(dev, EV_MSC, MSC_SCAN, code);
+		input_report_key(dev, keycode, (kbd->new[0] >> i) & 1);
+	}
+
+	for (i = 2; i < 8; i++) {
+		ptr = memchr(kbd->new + 2, kbd->old[i], 6);
+		code = kbd->old[i];
+		if (code > 3 && ptr == NULL) {
+			keycode = kbd->keycode[code];
+			if (keycode) {
+				input_event(dev, EV_MSC, MSC_SCAN, code);
+				input_report_key(dev, keycode, 0);
+			} else
+				dev_dbg(&dev->dev,
+					"Unknown key (scancode %#x) released.",
+					code);
+		}
+		ptr = memchr(kbd->old + 2, kbd->new[i], 6);
+		code = kbd->new[i];
+		if (code > 3 && ptr) {
+			keycode = kbd->keycode[code];
+			if (keycode) {
+				input_event(dev, EV_MSC, MSC_SCAN, code);
+				input_report_key(dev, keycode, 1);
+			} else
+				dev_dbg(&dev->dev,
+					"Unknown key (scancode %#x) pressed.",
+					code);
+		}
+	}
+	input_sync(dev);
+	memcpy(kbd->old, kbd->new, 8);
+}
+
+static void dc_kbd_callback(struct mapleq *mq)
+{
+	struct maple_device *mapledev = mq->dev;
+	struct dc_kbd *kbd = maple_get_drvdata(mapledev);
+	unsigned long *buf = (unsigned long *)(mq->recvbuf->buf);
+
+	/*
+	 * We should always get the lock because the only
+	 * time it may be locked is if the driver is in the cleanup phase.
+	 */
+	if (likely(mutex_trylock(&maple_keyb_mutex))) {
+
+		if (buf[1] == mapledev->function) {
+			memcpy(kbd->new, buf + 2, 8);
+			dc_scan_kbd(kbd);
+		}
+
+		mutex_unlock(&maple_keyb_mutex);
+	}
+}
+
+static int probe_maple_kbd(struct device *dev)
+{
+	struct maple_device *mdev;
+	struct maple_driver *mdrv;
+	int i, error;
+	struct dc_kbd *kbd;
+	struct input_dev *idev;
+
+	mdev = to_maple_dev(dev);
+	mdrv = to_maple_driver(dev->driver);
+
+	kbd = kzalloc(sizeof(struct dc_kbd), GFP_KERNEL);
+	if (!kbd) {
+		error = -ENOMEM;
+		goto fail;
+	}
+
+	idev = input_allocate_device();
+	if (!idev) {
+		error = -ENOMEM;
+		goto fail_idev_alloc;
+	}
+
+	kbd->dev = idev;
+	memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode));
+
+	idev->name = mdev->product_name;
+	idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+	idev->keycode = kbd->keycode;
+	idev->keycodesize = sizeof(unsigned short);
+	idev->keycodemax = ARRAY_SIZE(kbd->keycode);
+	idev->id.bustype = BUS_HOST;
+	idev->dev.parent = &mdev->dev;
+
+	for (i = 0; i < NR_SCANCODES; i++)
+		__set_bit(dc_kbd_keycode[i], idev->keybit);
+	__clear_bit(KEY_RESERVED, idev->keybit);
+
+	input_set_capability(idev, EV_MSC, MSC_SCAN);
+	input_set_drvdata(idev, kbd);
+
+	error = input_register_device(idev);
+	if (error)
+		goto fail_register;
+
+	/* Maple polling is locked to VBLANK - which may be just 50/s */
+	maple_getcond_callback(mdev, dc_kbd_callback, HZ/50,
+		MAPLE_FUNC_KEYBOARD);
+
+	mdev->driver = mdrv;
+
+	maple_set_drvdata(mdev, kbd);
+
+	return error;
+
+fail_register:
+	maple_set_drvdata(mdev, NULL);
+	input_free_device(idev);
+fail_idev_alloc:
+	kfree(kbd);
+fail:
+	return error;
+}
+
+static int remove_maple_kbd(struct device *dev)
+{
+	struct maple_device *mdev = to_maple_dev(dev);
+	struct dc_kbd *kbd = maple_get_drvdata(mdev);
+
+	mutex_lock(&maple_keyb_mutex);
+
+	input_unregister_device(kbd->dev);
+	kfree(kbd);
+
+	maple_set_drvdata(mdev, NULL);
+
+	mutex_unlock(&maple_keyb_mutex);
+	return 0;
+}
+
+static struct maple_driver dc_kbd_driver = {
+	.function = MAPLE_FUNC_KEYBOARD,
+	.drv = {
+		.name = "Dreamcast_keyboard",
+		.probe = probe_maple_kbd,
+		.remove = remove_maple_kbd,
+	},
+};
+
+static int __init dc_kbd_init(void)
+{
+	return maple_driver_register(&dc_kbd_driver);
+}
+
+static void __exit dc_kbd_exit(void)
+{
+	maple_driver_unregister(&dc_kbd_driver);
+}
+
+module_init(dc_kbd_init);
+module_exit(dc_kbd_exit);
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
new file mode 100644
index 0000000..7f12b65
--- /dev/null
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -0,0 +1,579 @@
+/*
+ *  GPIO driven matrix keyboard driver
+ *
+ *  Copyright (c) 2008 Marek Vasut <marek.vasut@gmail.com>
+ *
+ *  Based on corgikbd.c
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+
+struct matrix_keypad {
+	const struct matrix_keypad_platform_data *pdata;
+	struct input_dev *input_dev;
+	unsigned int row_shift;
+
+	DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS);
+
+	uint32_t last_key_state[MATRIX_MAX_COLS];
+	struct delayed_work work;
+	spinlock_t lock;
+	bool scan_pending;
+	bool stopped;
+	bool gpio_all_disabled;
+};
+
+/*
+ * NOTE: normally the GPIO has to be put into HiZ when de-activated to cause
+ * minmal side effect when scanning other columns, here it is configured to
+ * be input, and it should work on most platforms.
+ */
+static void __activate_col(const struct matrix_keypad_platform_data *pdata,
+			   int col, bool on)
+{
+	bool level_on = !pdata->active_low;
+
+	if (on) {
+		gpio_direction_output(pdata->col_gpios[col], level_on);
+	} else {
+		gpio_set_value_cansleep(pdata->col_gpios[col], !level_on);
+		gpio_direction_input(pdata->col_gpios[col]);
+	}
+}
+
+static void activate_col(const struct matrix_keypad_platform_data *pdata,
+			 int col, bool on)
+{
+	__activate_col(pdata, col, on);
+
+	if (on && pdata->col_scan_delay_us)
+		udelay(pdata->col_scan_delay_us);
+}
+
+static void activate_all_cols(const struct matrix_keypad_platform_data *pdata,
+			      bool on)
+{
+	int col;
+
+	for (col = 0; col < pdata->num_col_gpios; col++)
+		__activate_col(pdata, col, on);
+}
+
+static bool row_asserted(const struct matrix_keypad_platform_data *pdata,
+			 int row)
+{
+	return gpio_get_value_cansleep(pdata->row_gpios[row]) ?
+			!pdata->active_low : pdata->active_low;
+}
+
+static void enable_row_irqs(struct matrix_keypad *keypad)
+{
+	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+	int i;
+
+	if (pdata->clustered_irq > 0)
+		enable_irq(pdata->clustered_irq);
+	else {
+		for (i = 0; i < pdata->num_row_gpios; i++)
+			enable_irq(gpio_to_irq(pdata->row_gpios[i]));
+	}
+}
+
+static void disable_row_irqs(struct matrix_keypad *keypad)
+{
+	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+	int i;
+
+	if (pdata->clustered_irq > 0)
+		disable_irq_nosync(pdata->clustered_irq);
+	else {
+		for (i = 0; i < pdata->num_row_gpios; i++)
+			disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i]));
+	}
+}
+
+/*
+ * This gets the keys from keyboard and reports it to input subsystem
+ */
+static void matrix_keypad_scan(struct work_struct *work)
+{
+	struct matrix_keypad *keypad =
+		container_of(work, struct matrix_keypad, work.work);
+	struct input_dev *input_dev = keypad->input_dev;
+	const unsigned short *keycodes = input_dev->keycode;
+	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+	uint32_t new_state[MATRIX_MAX_COLS];
+	int row, col, code;
+
+	/* de-activate all columns for scanning */
+	activate_all_cols(pdata, false);
+
+	memset(new_state, 0, sizeof(new_state));
+
+	/* assert each column and read the row status out */
+	for (col = 0; col < pdata->num_col_gpios; col++) {
+
+		activate_col(pdata, col, true);
+
+		for (row = 0; row < pdata->num_row_gpios; row++)
+			new_state[col] |=
+				row_asserted(pdata, row) ? (1 << row) : 0;
+
+		activate_col(pdata, col, false);
+	}
+
+	for (col = 0; col < pdata->num_col_gpios; col++) {
+		uint32_t bits_changed;
+
+		bits_changed = keypad->last_key_state[col] ^ new_state[col];
+		if (bits_changed == 0)
+			continue;
+
+		for (row = 0; row < pdata->num_row_gpios; row++) {
+			if ((bits_changed & (1 << row)) == 0)
+				continue;
+
+			code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
+			input_event(input_dev, EV_MSC, MSC_SCAN, code);
+			input_report_key(input_dev,
+					 keycodes[code],
+					 new_state[col] & (1 << row));
+		}
+	}
+	input_sync(input_dev);
+
+	memcpy(keypad->last_key_state, new_state, sizeof(new_state));
+
+	activate_all_cols(pdata, true);
+
+	/* Enable IRQs again */
+	spin_lock_irq(&keypad->lock);
+	keypad->scan_pending = false;
+	enable_row_irqs(keypad);
+	spin_unlock_irq(&keypad->lock);
+}
+
+static irqreturn_t matrix_keypad_interrupt(int irq, void *id)
+{
+	struct matrix_keypad *keypad = id;
+	unsigned long flags;
+
+	spin_lock_irqsave(&keypad->lock, flags);
+
+	/*
+	 * See if another IRQ beaten us to it and scheduled the
+	 * scan already. In that case we should not try to
+	 * disable IRQs again.
+	 */
+	if (unlikely(keypad->scan_pending || keypad->stopped))
+		goto out;
+
+	disable_row_irqs(keypad);
+	keypad->scan_pending = true;
+	schedule_delayed_work(&keypad->work,
+		msecs_to_jiffies(keypad->pdata->debounce_ms));
+
+out:
+	spin_unlock_irqrestore(&keypad->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static int matrix_keypad_start(struct input_dev *dev)
+{
+	struct matrix_keypad *keypad = input_get_drvdata(dev);
+
+	keypad->stopped = false;
+	mb();
+
+	/*
+	 * Schedule an immediate key scan to capture current key state;
+	 * columns will be activated and IRQs be enabled after the scan.
+	 */
+	schedule_delayed_work(&keypad->work, 0);
+
+	return 0;
+}
+
+static void matrix_keypad_stop(struct input_dev *dev)
+{
+	struct matrix_keypad *keypad = input_get_drvdata(dev);
+
+	keypad->stopped = true;
+	mb();
+	flush_work(&keypad->work.work);
+	/*
+	 * matrix_keypad_scan() will leave IRQs enabled;
+	 * we should disable them now.
+	 */
+	disable_row_irqs(keypad);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
+{
+	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+	unsigned int gpio;
+	int i;
+
+	if (pdata->clustered_irq > 0) {
+		if (enable_irq_wake(pdata->clustered_irq) == 0)
+			keypad->gpio_all_disabled = true;
+	} else {
+
+		for (i = 0; i < pdata->num_row_gpios; i++) {
+			if (!test_bit(i, keypad->disabled_gpios)) {
+				gpio = pdata->row_gpios[i];
+
+				if (enable_irq_wake(gpio_to_irq(gpio)) == 0)
+					__set_bit(i, keypad->disabled_gpios);
+			}
+		}
+	}
+}
+
+static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
+{
+	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+	unsigned int gpio;
+	int i;
+
+	if (pdata->clustered_irq > 0) {
+		if (keypad->gpio_all_disabled) {
+			disable_irq_wake(pdata->clustered_irq);
+			keypad->gpio_all_disabled = false;
+		}
+	} else {
+		for (i = 0; i < pdata->num_row_gpios; i++) {
+			if (test_and_clear_bit(i, keypad->disabled_gpios)) {
+				gpio = pdata->row_gpios[i];
+				disable_irq_wake(gpio_to_irq(gpio));
+			}
+		}
+	}
+}
+
+static int matrix_keypad_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct matrix_keypad *keypad = platform_get_drvdata(pdev);
+
+	matrix_keypad_stop(keypad->input_dev);
+
+	if (device_may_wakeup(&pdev->dev))
+		matrix_keypad_enable_wakeup(keypad);
+
+	return 0;
+}
+
+static int matrix_keypad_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct matrix_keypad *keypad = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(&pdev->dev))
+		matrix_keypad_disable_wakeup(keypad);
+
+	matrix_keypad_start(keypad->input_dev);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops,
+			 matrix_keypad_suspend, matrix_keypad_resume);
+
+static int matrix_keypad_init_gpio(struct platform_device *pdev,
+				   struct matrix_keypad *keypad)
+{
+	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+	int i, err;
+
+	/* initialized strobe lines as outputs, activated */
+	for (i = 0; i < pdata->num_col_gpios; i++) {
+		err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col");
+		if (err) {
+			dev_err(&pdev->dev,
+				"failed to request GPIO%d for COL%d\n",
+				pdata->col_gpios[i], i);
+			goto err_free_cols;
+		}
+
+		gpio_direction_output(pdata->col_gpios[i], !pdata->active_low);
+	}
+
+	for (i = 0; i < pdata->num_row_gpios; i++) {
+		err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row");
+		if (err) {
+			dev_err(&pdev->dev,
+				"failed to request GPIO%d for ROW%d\n",
+				pdata->row_gpios[i], i);
+			goto err_free_rows;
+		}
+
+		gpio_direction_input(pdata->row_gpios[i]);
+	}
+
+	if (pdata->clustered_irq > 0) {
+		err = request_any_context_irq(pdata->clustered_irq,
+				matrix_keypad_interrupt,
+				pdata->clustered_irq_flags,
+				"matrix-keypad", keypad);
+		if (err < 0) {
+			dev_err(&pdev->dev,
+				"Unable to acquire clustered interrupt\n");
+			goto err_free_rows;
+		}
+	} else {
+		for (i = 0; i < pdata->num_row_gpios; i++) {
+			err = request_any_context_irq(
+					gpio_to_irq(pdata->row_gpios[i]),
+					matrix_keypad_interrupt,
+					IRQF_TRIGGER_RISING |
+					IRQF_TRIGGER_FALLING,
+					"matrix-keypad", keypad);
+			if (err < 0) {
+				dev_err(&pdev->dev,
+					"Unable to acquire interrupt for GPIO line %i\n",
+					pdata->row_gpios[i]);
+				goto err_free_irqs;
+			}
+		}
+	}
+
+	/* initialized as disabled - enabled by input->open */
+	disable_row_irqs(keypad);
+	return 0;
+
+err_free_irqs:
+	while (--i >= 0)
+		free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
+	i = pdata->num_row_gpios;
+err_free_rows:
+	while (--i >= 0)
+		gpio_free(pdata->row_gpios[i]);
+	i = pdata->num_col_gpios;
+err_free_cols:
+	while (--i >= 0)
+		gpio_free(pdata->col_gpios[i]);
+
+	return err;
+}
+
+static void matrix_keypad_free_gpio(struct matrix_keypad *keypad)
+{
+	const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+	int i;
+
+	if (pdata->clustered_irq > 0) {
+		free_irq(pdata->clustered_irq, keypad);
+	} else {
+		for (i = 0; i < pdata->num_row_gpios; i++)
+			free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
+	}
+
+	for (i = 0; i < pdata->num_row_gpios; i++)
+		gpio_free(pdata->row_gpios[i]);
+
+	for (i = 0; i < pdata->num_col_gpios; i++)
+		gpio_free(pdata->col_gpios[i]);
+}
+
+#ifdef CONFIG_OF
+static struct matrix_keypad_platform_data *
+matrix_keypad_parse_dt(struct device *dev)
+{
+	struct matrix_keypad_platform_data *pdata;
+	struct device_node *np = dev->of_node;
+	unsigned int *gpios;
+	int i, nrow, ncol;
+
+	if (!np) {
+		dev_err(dev, "device lacks DT data\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		dev_err(dev, "could not allocate memory for platform data\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	pdata->num_row_gpios = nrow = of_gpio_named_count(np, "row-gpios");
+	pdata->num_col_gpios = ncol = of_gpio_named_count(np, "col-gpios");
+	if (nrow <= 0 || ncol <= 0) {
+		dev_err(dev, "number of keypad rows/columns not specified\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (of_get_property(np, "linux,no-autorepeat", NULL))
+		pdata->no_autorepeat = true;
+
+	pdata->wakeup = of_property_read_bool(np, "wakeup-source") ||
+			of_property_read_bool(np, "linux,wakeup"); /* legacy */
+
+	if (of_get_property(np, "gpio-activelow", NULL))
+		pdata->active_low = true;
+
+	of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms);
+	of_property_read_u32(np, "col-scan-delay-us",
+						&pdata->col_scan_delay_us);
+
+	gpios = devm_kzalloc(dev,
+			     sizeof(unsigned int) *
+				(pdata->num_row_gpios + pdata->num_col_gpios),
+			     GFP_KERNEL);
+	if (!gpios) {
+		dev_err(dev, "could not allocate memory for gpios\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	for (i = 0; i < pdata->num_row_gpios; i++)
+		gpios[i] = of_get_named_gpio(np, "row-gpios", i);
+
+	for (i = 0; i < pdata->num_col_gpios; i++)
+		gpios[pdata->num_row_gpios + i] =
+			of_get_named_gpio(np, "col-gpios", i);
+
+	pdata->row_gpios = gpios;
+	pdata->col_gpios = &gpios[pdata->num_row_gpios];
+
+	return pdata;
+}
+#else
+static inline struct matrix_keypad_platform_data *
+matrix_keypad_parse_dt(struct device *dev)
+{
+	dev_err(dev, "no platform data defined\n");
+
+	return ERR_PTR(-EINVAL);
+}
+#endif
+
+static int matrix_keypad_probe(struct platform_device *pdev)
+{
+	const struct matrix_keypad_platform_data *pdata;
+	struct matrix_keypad *keypad;
+	struct input_dev *input_dev;
+	int err;
+
+	pdata = dev_get_platdata(&pdev->dev);
+	if (!pdata) {
+		pdata = matrix_keypad_parse_dt(&pdev->dev);
+		if (IS_ERR(pdata)) {
+			dev_err(&pdev->dev, "no platform data defined\n");
+			return PTR_ERR(pdata);
+		}
+	} else if (!pdata->keymap_data) {
+		dev_err(&pdev->dev, "no keymap data defined\n");
+		return -EINVAL;
+	}
+
+	keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!keypad || !input_dev) {
+		err = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	keypad->input_dev = input_dev;
+	keypad->pdata = pdata;
+	keypad->row_shift = get_count_order(pdata->num_col_gpios);
+	keypad->stopped = true;
+	INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan);
+	spin_lock_init(&keypad->lock);
+
+	input_dev->name		= pdev->name;
+	input_dev->id.bustype	= BUS_HOST;
+	input_dev->dev.parent	= &pdev->dev;
+	input_dev->open		= matrix_keypad_start;
+	input_dev->close	= matrix_keypad_stop;
+
+	err = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
+					 pdata->num_row_gpios,
+					 pdata->num_col_gpios,
+					 NULL, input_dev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to build keymap\n");
+		goto err_free_mem;
+	}
+
+	if (!pdata->no_autorepeat)
+		__set_bit(EV_REP, input_dev->evbit);
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+	input_set_drvdata(input_dev, keypad);
+
+	err = matrix_keypad_init_gpio(pdev, keypad);
+	if (err)
+		goto err_free_mem;
+
+	err = input_register_device(keypad->input_dev);
+	if (err)
+		goto err_free_gpio;
+
+	device_init_wakeup(&pdev->dev, pdata->wakeup);
+	platform_set_drvdata(pdev, keypad);
+
+	return 0;
+
+err_free_gpio:
+	matrix_keypad_free_gpio(keypad);
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(keypad);
+	return err;
+}
+
+static int matrix_keypad_remove(struct platform_device *pdev)
+{
+	struct matrix_keypad *keypad = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+
+	matrix_keypad_free_gpio(keypad);
+	input_unregister_device(keypad->input_dev);
+	kfree(keypad);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id matrix_keypad_dt_match[] = {
+	{ .compatible = "gpio-matrix-keypad" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, matrix_keypad_dt_match);
+#endif
+
+static struct platform_driver matrix_keypad_driver = {
+	.probe		= matrix_keypad_probe,
+	.remove		= matrix_keypad_remove,
+	.driver		= {
+		.name	= "matrix-keypad",
+		.pm	= &matrix_keypad_pm_ops,
+		.of_match_table = of_match_ptr(matrix_keypad_dt_match),
+	},
+};
+module_platform_driver(matrix_keypad_driver);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:matrix-keypad");
diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c
new file mode 100644
index 0000000..5091133
--- /dev/null
+++ b/drivers/input/keyboard/max7359_keypad.c
@@ -0,0 +1,298 @@
+/*
+ * max7359_keypad.c - MAX7359 Key Switch Controller Driver
+ *
+ * Copyright (C) 2009 Samsung Electronics
+ * Kim Kyuwon <q1.kim@samsung.com>
+ *
+ * Based on pxa27x_keypad.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Datasheet: http://www.maxim-ic.com/quick_view2.cfm/qv_pk/5456
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+
+#define MAX7359_MAX_KEY_ROWS	8
+#define MAX7359_MAX_KEY_COLS	8
+#define MAX7359_MAX_KEY_NUM	(MAX7359_MAX_KEY_ROWS * MAX7359_MAX_KEY_COLS)
+#define MAX7359_ROW_SHIFT	3
+
+/*
+ * MAX7359 registers
+ */
+#define MAX7359_REG_KEYFIFO	0x00
+#define MAX7359_REG_CONFIG	0x01
+#define MAX7359_REG_DEBOUNCE	0x02
+#define MAX7359_REG_INTERRUPT	0x03
+#define MAX7359_REG_PORTS	0x04
+#define MAX7359_REG_KEYREP	0x05
+#define MAX7359_REG_SLEEP	0x06
+
+/*
+ * Configuration register bits
+ */
+#define MAX7359_CFG_SLEEP	(1 << 7)
+#define MAX7359_CFG_INTERRUPT	(1 << 5)
+#define MAX7359_CFG_KEY_RELEASE	(1 << 3)
+#define MAX7359_CFG_WAKEUP	(1 << 1)
+#define MAX7359_CFG_TIMEOUT	(1 << 0)
+
+/*
+ * Autosleep register values (ms)
+ */
+#define MAX7359_AUTOSLEEP_8192	0x01
+#define MAX7359_AUTOSLEEP_4096	0x02
+#define MAX7359_AUTOSLEEP_2048	0x03
+#define MAX7359_AUTOSLEEP_1024	0x04
+#define MAX7359_AUTOSLEEP_512	0x05
+#define MAX7359_AUTOSLEEP_256	0x06
+
+struct max7359_keypad {
+	/* matrix key code map */
+	unsigned short keycodes[MAX7359_MAX_KEY_NUM];
+
+	struct input_dev *input_dev;
+	struct i2c_client *client;
+};
+
+static int max7359_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+	int ret = i2c_smbus_write_byte_data(client, reg, val);
+
+	if (ret < 0)
+		dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
+			__func__, reg, val, ret);
+	return ret;
+}
+
+static int max7359_read_reg(struct i2c_client *client, int reg)
+{
+	int ret = i2c_smbus_read_byte_data(client, reg);
+
+	if (ret < 0)
+		dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
+			__func__, reg, ret);
+	return ret;
+}
+
+/* runs in an IRQ thread -- can (and will!) sleep */
+static irqreturn_t max7359_interrupt(int irq, void *dev_id)
+{
+	struct max7359_keypad *keypad = dev_id;
+	struct input_dev *input_dev = keypad->input_dev;
+	int val, row, col, release, code;
+
+	val = max7359_read_reg(keypad->client, MAX7359_REG_KEYFIFO);
+	row = val & 0x7;
+	col = (val >> 3) & 0x7;
+	release = val & 0x40;
+
+	code = MATRIX_SCAN_CODE(row, col, MAX7359_ROW_SHIFT);
+
+	dev_dbg(&keypad->client->dev,
+		"key[%d:%d] %s\n", row, col, release ? "release" : "press");
+
+	input_event(input_dev, EV_MSC, MSC_SCAN, code);
+	input_report_key(input_dev, keypad->keycodes[code], !release);
+	input_sync(input_dev);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Let MAX7359 fall into a deep sleep:
+ * If no keys are pressed, enter sleep mode for 8192 ms. And if any
+ * key is pressed, the MAX7359 returns to normal operating mode.
+ */
+static inline void max7359_fall_deepsleep(struct i2c_client *client)
+{
+	max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_8192);
+}
+
+/*
+ * Let MAX7359 take a catnap:
+ * Autosleep just for 256 ms.
+ */
+static inline void max7359_take_catnap(struct i2c_client *client)
+{
+	max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_256);
+}
+
+static int max7359_open(struct input_dev *dev)
+{
+	struct max7359_keypad *keypad = input_get_drvdata(dev);
+
+	max7359_take_catnap(keypad->client);
+
+	return 0;
+}
+
+static void max7359_close(struct input_dev *dev)
+{
+	struct max7359_keypad *keypad = input_get_drvdata(dev);
+
+	max7359_fall_deepsleep(keypad->client);
+}
+
+static void max7359_initialize(struct i2c_client *client)
+{
+	max7359_write_reg(client, MAX7359_REG_CONFIG,
+		MAX7359_CFG_KEY_RELEASE | /* Key release enable */
+		MAX7359_CFG_WAKEUP); /* Key press wakeup enable */
+
+	/* Full key-scan functionality */
+	max7359_write_reg(client, MAX7359_REG_DEBOUNCE, 0x1F);
+
+	/* nINT asserts every debounce cycles */
+	max7359_write_reg(client, MAX7359_REG_INTERRUPT, 0x01);
+
+	max7359_fall_deepsleep(client);
+}
+
+static int max7359_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	const struct matrix_keymap_data *keymap_data =
+			dev_get_platdata(&client->dev);
+	struct max7359_keypad *keypad;
+	struct input_dev *input_dev;
+	int ret;
+	int error;
+
+	if (!client->irq) {
+		dev_err(&client->dev, "The irq number should not be zero\n");
+		return -EINVAL;
+	}
+
+	/* Detect MAX7359: The initial Keys FIFO value is '0x3F' */
+	ret = max7359_read_reg(client, MAX7359_REG_KEYFIFO);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to detect device\n");
+		return -ENODEV;
+	}
+
+	dev_dbg(&client->dev, "keys FIFO is 0x%02x\n", ret);
+
+	keypad = devm_kzalloc(&client->dev, sizeof(struct max7359_keypad),
+			      GFP_KERNEL);
+	if (!keypad) {
+		dev_err(&client->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	input_dev = devm_input_allocate_device(&client->dev);
+	if (!input_dev) {
+		dev_err(&client->dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	keypad->client = client;
+	keypad->input_dev = input_dev;
+
+	input_dev->name = client->name;
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->open = max7359_open;
+	input_dev->close = max7359_close;
+	input_dev->dev.parent = &client->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+	input_dev->keycodesize = sizeof(keypad->keycodes[0]);
+	input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);
+	input_dev->keycode = keypad->keycodes;
+
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+	input_set_drvdata(input_dev, keypad);
+
+	error = matrix_keypad_build_keymap(keymap_data, NULL,
+					   MAX7359_MAX_KEY_ROWS,
+					   MAX7359_MAX_KEY_COLS,
+					   keypad->keycodes,
+					   input_dev);
+	if (error) {
+		dev_err(&client->dev, "failed to build keymap\n");
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+					  max7359_interrupt,
+					  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					  client->name, keypad);
+	if (error) {
+		dev_err(&client->dev, "failed to register interrupt\n");
+		return error;
+	}
+
+	/* Register the input device */
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(&client->dev, "failed to register input device\n");
+		return error;
+	}
+
+	/* Initialize MAX7359 */
+	max7359_initialize(client);
+
+	i2c_set_clientdata(client, keypad);
+	device_init_wakeup(&client->dev, 1);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max7359_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	max7359_fall_deepsleep(client);
+
+	if (device_may_wakeup(&client->dev))
+		enable_irq_wake(client->irq);
+
+	return 0;
+}
+
+static int max7359_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	if (device_may_wakeup(&client->dev))
+		disable_irq_wake(client->irq);
+
+	/* Restore the default setting */
+	max7359_take_catnap(client);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max7359_pm, max7359_suspend, max7359_resume);
+
+static const struct i2c_device_id max7359_ids[] = {
+	{ "max7359", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max7359_ids);
+
+static struct i2c_driver max7359_i2c_driver = {
+	.driver = {
+		.name = "max7359",
+		.pm   = &max7359_pm,
+	},
+	.probe		= max7359_probe,
+	.id_table	= max7359_ids,
+};
+
+module_i2c_driver(max7359_i2c_driver);
+
+MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
+MODULE_DESCRIPTION("MAX7359 Key Switch Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c
new file mode 100644
index 0000000..31090d7
--- /dev/null
+++ b/drivers/input/keyboard/mcs_touchkey.c
@@ -0,0 +1,282 @@
+/*
+ * Touchkey driver for MELFAS MCS5000/5080 controller
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c/mcs.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+
+/* MCS5000 Touchkey */
+#define MCS5000_TOUCHKEY_STATUS		0x04
+#define MCS5000_TOUCHKEY_STATUS_PRESS	7
+#define MCS5000_TOUCHKEY_FW		0x0a
+#define MCS5000_TOUCHKEY_BASE_VAL	0x61
+
+/* MCS5080 Touchkey */
+#define MCS5080_TOUCHKEY_STATUS		0x00
+#define MCS5080_TOUCHKEY_STATUS_PRESS	3
+#define MCS5080_TOUCHKEY_FW		0x01
+#define MCS5080_TOUCHKEY_BASE_VAL	0x1
+
+enum mcs_touchkey_type {
+	MCS5000_TOUCHKEY,
+	MCS5080_TOUCHKEY,
+};
+
+struct mcs_touchkey_chip {
+	unsigned int status_reg;
+	unsigned int pressbit;
+	unsigned int press_invert;
+	unsigned int baseval;
+};
+
+struct mcs_touchkey_data {
+	void (*poweron)(bool);
+
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	struct mcs_touchkey_chip chip;
+	unsigned int key_code;
+	unsigned int key_val;
+	unsigned short keycodes[];
+};
+
+static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id)
+{
+	struct mcs_touchkey_data *data = dev_id;
+	struct mcs_touchkey_chip *chip = &data->chip;
+	struct i2c_client *client = data->client;
+	struct input_dev *input = data->input_dev;
+	unsigned int key_val;
+	unsigned int pressed;
+	int val;
+
+	val = i2c_smbus_read_byte_data(client, chip->status_reg);
+	if (val < 0) {
+		dev_err(&client->dev, "i2c read error [%d]\n", val);
+		goto out;
+	}
+
+	pressed = (val & (1 << chip->pressbit)) >> chip->pressbit;
+	if (chip->press_invert)
+		pressed ^= chip->press_invert;
+
+	/* key_val is 0 when released, so we should use key_val of press. */
+	if (pressed) {
+		key_val = val & (0xff >> (8 - chip->pressbit));
+		if (!key_val)
+			goto out;
+		key_val -= chip->baseval;
+		data->key_code = data->keycodes[key_val];
+		data->key_val = key_val;
+	}
+
+	input_event(input, EV_MSC, MSC_SCAN, data->key_val);
+	input_report_key(input, data->key_code, pressed);
+	input_sync(input);
+
+	dev_dbg(&client->dev, "key %d %d %s\n", data->key_val, data->key_code,
+		pressed ? "pressed" : "released");
+
+ out:
+	return IRQ_HANDLED;
+}
+
+static int mcs_touchkey_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	const struct mcs_platform_data *pdata;
+	struct mcs_touchkey_data *data;
+	struct input_dev *input_dev;
+	unsigned int fw_reg;
+	int fw_ver;
+	int error;
+	int i;
+
+	pdata = dev_get_platdata(&client->dev);
+	if (!pdata) {
+		dev_err(&client->dev, "no platform data defined\n");
+		return -EINVAL;
+	}
+
+	data = kzalloc(sizeof(struct mcs_touchkey_data) +
+			sizeof(data->keycodes[0]) * (pdata->key_maxval + 1),
+			GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!data || !input_dev) {
+		dev_err(&client->dev, "Failed to allocate memory\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	data->client = client;
+	data->input_dev = input_dev;
+
+	if (id->driver_data == MCS5000_TOUCHKEY) {
+		data->chip.status_reg = MCS5000_TOUCHKEY_STATUS;
+		data->chip.pressbit = MCS5000_TOUCHKEY_STATUS_PRESS;
+		data->chip.baseval = MCS5000_TOUCHKEY_BASE_VAL;
+		fw_reg = MCS5000_TOUCHKEY_FW;
+	} else {
+		data->chip.status_reg = MCS5080_TOUCHKEY_STATUS;
+		data->chip.pressbit = MCS5080_TOUCHKEY_STATUS_PRESS;
+		data->chip.press_invert = 1;
+		data->chip.baseval = MCS5080_TOUCHKEY_BASE_VAL;
+		fw_reg = MCS5080_TOUCHKEY_FW;
+	}
+
+	fw_ver = i2c_smbus_read_byte_data(client, fw_reg);
+	if (fw_ver < 0) {
+		error = fw_ver;
+		dev_err(&client->dev, "i2c read error[%d]\n", error);
+		goto err_free_mem;
+	}
+	dev_info(&client->dev, "Firmware version: %d\n", fw_ver);
+
+	input_dev->name = "MELFAS MCS Touchkey";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &client->dev;
+	input_dev->evbit[0] = BIT_MASK(EV_KEY);
+	if (!pdata->no_autorepeat)
+		input_dev->evbit[0] |= BIT_MASK(EV_REP);
+	input_dev->keycode = data->keycodes;
+	input_dev->keycodesize = sizeof(data->keycodes[0]);
+	input_dev->keycodemax = pdata->key_maxval + 1;
+
+	for (i = 0; i < pdata->keymap_size; i++) {
+		unsigned int val = MCS_KEY_VAL(pdata->keymap[i]);
+		unsigned int code = MCS_KEY_CODE(pdata->keymap[i]);
+
+		data->keycodes[val] = code;
+		__set_bit(code, input_dev->keybit);
+	}
+
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+	input_set_drvdata(input_dev, data);
+
+	if (pdata->cfg_pin)
+		pdata->cfg_pin();
+
+	if (pdata->poweron) {
+		data->poweron = pdata->poweron;
+		data->poweron(true);
+	}
+
+	error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt,
+				     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				     client->dev.driver->name, data);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		goto err_free_mem;
+	}
+
+	error = input_register_device(input_dev);
+	if (error)
+		goto err_free_irq;
+
+	i2c_set_clientdata(client, data);
+	return 0;
+
+err_free_irq:
+	free_irq(client->irq, data);
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(data);
+	return error;
+}
+
+static int mcs_touchkey_remove(struct i2c_client *client)
+{
+	struct mcs_touchkey_data *data = i2c_get_clientdata(client);
+
+	free_irq(client->irq, data);
+	if (data->poweron)
+		data->poweron(false);
+	input_unregister_device(data->input_dev);
+	kfree(data);
+
+	return 0;
+}
+
+static void mcs_touchkey_shutdown(struct i2c_client *client)
+{
+	struct mcs_touchkey_data *data = i2c_get_clientdata(client);
+
+	if (data->poweron)
+		data->poweron(false);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mcs_touchkey_suspend(struct device *dev)
+{
+	struct mcs_touchkey_data *data = dev_get_drvdata(dev);
+	struct i2c_client *client = data->client;
+
+	/* Disable the work */
+	disable_irq(client->irq);
+
+	/* Finally turn off the power */
+	if (data->poweron)
+		data->poweron(false);
+
+	return 0;
+}
+
+static int mcs_touchkey_resume(struct device *dev)
+{
+	struct mcs_touchkey_data *data = dev_get_drvdata(dev);
+	struct i2c_client *client = data->client;
+
+	/* Enable the device first */
+	if (data->poweron)
+		data->poweron(true);
+
+	/* Enable irq again */
+	enable_irq(client->irq);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mcs_touchkey_pm_ops,
+			 mcs_touchkey_suspend, mcs_touchkey_resume);
+
+static const struct i2c_device_id mcs_touchkey_id[] = {
+	{ "mcs5000_touchkey", MCS5000_TOUCHKEY },
+	{ "mcs5080_touchkey", MCS5080_TOUCHKEY },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id);
+
+static struct i2c_driver mcs_touchkey_driver = {
+	.driver = {
+		.name	= "mcs_touchkey",
+		.pm	= &mcs_touchkey_pm_ops,
+	},
+	.probe		= mcs_touchkey_probe,
+	.remove		= mcs_touchkey_remove,
+	.shutdown       = mcs_touchkey_shutdown,
+	.id_table	= mcs_touchkey_id,
+};
+
+module_i2c_driver(mcs_touchkey_driver);
+
+/* Module information */
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>");
+MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c
new file mode 100644
index 0000000..aaf43be
--- /dev/null
+++ b/drivers/input/keyboard/mpr121_touchkey.c
@@ -0,0 +1,324 @@
+/*
+ * Touchkey driver for Freescale MPR121 Controllor
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * Author: Zhang Jiejing <jiejing.zhang@freescale.com>
+ *
+ * Based on mcs_touchkey.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/i2c/mpr121_touchkey.h>
+
+/* Register definitions */
+#define ELE_TOUCH_STATUS_0_ADDR	0x0
+#define ELE_TOUCH_STATUS_1_ADDR	0X1
+#define MHD_RISING_ADDR		0x2b
+#define NHD_RISING_ADDR		0x2c
+#define NCL_RISING_ADDR		0x2d
+#define FDL_RISING_ADDR		0x2e
+#define MHD_FALLING_ADDR	0x2f
+#define NHD_FALLING_ADDR	0x30
+#define NCL_FALLING_ADDR	0x31
+#define FDL_FALLING_ADDR	0x32
+#define ELE0_TOUCH_THRESHOLD_ADDR	0x41
+#define ELE0_RELEASE_THRESHOLD_ADDR	0x42
+#define AFE_CONF_ADDR			0x5c
+#define FILTER_CONF_ADDR		0x5d
+
+/*
+ * ELECTRODE_CONF_ADDR: This register configures the number of
+ * enabled capacitance sensing inputs and its run/suspend mode.
+ */
+#define ELECTRODE_CONF_ADDR		0x5e
+#define ELECTRODE_CONF_QUICK_CHARGE	0x80
+#define AUTO_CONFIG_CTRL_ADDR		0x7b
+#define AUTO_CONFIG_USL_ADDR		0x7d
+#define AUTO_CONFIG_LSL_ADDR		0x7e
+#define AUTO_CONFIG_TL_ADDR		0x7f
+
+/* Threshold of touch/release trigger */
+#define TOUCH_THRESHOLD			0x08
+#define RELEASE_THRESHOLD		0x05
+/* Masks for touch and release triggers */
+#define TOUCH_STATUS_MASK		0xfff
+/* MPR121 has 12 keys */
+#define MPR121_MAX_KEY_COUNT		12
+
+struct mpr121_touchkey {
+	struct i2c_client	*client;
+	struct input_dev	*input_dev;
+	unsigned int		key_val;
+	unsigned int		statusbits;
+	unsigned int		keycount;
+	u16			keycodes[MPR121_MAX_KEY_COUNT];
+};
+
+struct mpr121_init_register {
+	int addr;
+	u8 val;
+};
+
+static const struct mpr121_init_register init_reg_table[] = {
+	{ MHD_RISING_ADDR,	0x1 },
+	{ NHD_RISING_ADDR,	0x1 },
+	{ MHD_FALLING_ADDR,	0x1 },
+	{ NHD_FALLING_ADDR,	0x1 },
+	{ NCL_FALLING_ADDR,	0xff },
+	{ FDL_FALLING_ADDR,	0x02 },
+	{ FILTER_CONF_ADDR,	0x04 },
+	{ AFE_CONF_ADDR,	0x0b },
+	{ AUTO_CONFIG_CTRL_ADDR, 0x0b },
+};
+
+static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id)
+{
+	struct mpr121_touchkey *mpr121 = dev_id;
+	struct i2c_client *client = mpr121->client;
+	struct input_dev *input = mpr121->input_dev;
+	unsigned long bit_changed;
+	unsigned int key_num;
+	int reg;
+
+	reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR);
+	if (reg < 0) {
+		dev_err(&client->dev, "i2c read error [%d]\n", reg);
+		goto out;
+	}
+
+	reg <<= 8;
+	reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR);
+	if (reg < 0) {
+		dev_err(&client->dev, "i2c read error [%d]\n", reg);
+		goto out;
+	}
+
+	reg &= TOUCH_STATUS_MASK;
+	/* use old press bit to figure out which bit changed */
+	bit_changed = reg ^ mpr121->statusbits;
+	mpr121->statusbits = reg;
+	for_each_set_bit(key_num, &bit_changed, mpr121->keycount) {
+		unsigned int key_val, pressed;
+
+		pressed = reg & BIT(key_num);
+		key_val = mpr121->keycodes[key_num];
+
+		input_event(input, EV_MSC, MSC_SCAN, key_num);
+		input_report_key(input, key_val, pressed);
+
+		dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val,
+			pressed ? "pressed" : "released");
+
+	}
+	input_sync(input);
+
+out:
+	return IRQ_HANDLED;
+}
+
+static int mpr121_phys_init(const struct mpr121_platform_data *pdata,
+				      struct mpr121_touchkey *mpr121,
+				      struct i2c_client *client)
+{
+	const struct mpr121_init_register *reg;
+	unsigned char usl, lsl, tl, eleconf;
+	int i, t, vdd, ret;
+
+	/* Set up touch/release threshold for ele0-ele11 */
+	for (i = 0; i <= MPR121_MAX_KEY_COUNT; i++) {
+		t = ELE0_TOUCH_THRESHOLD_ADDR + (i * 2);
+		ret = i2c_smbus_write_byte_data(client, t, TOUCH_THRESHOLD);
+		if (ret < 0)
+			goto err_i2c_write;
+		ret = i2c_smbus_write_byte_data(client, t + 1,
+						RELEASE_THRESHOLD);
+		if (ret < 0)
+			goto err_i2c_write;
+	}
+
+	/* Set up init register */
+	for (i = 0; i < ARRAY_SIZE(init_reg_table); i++) {
+		reg = &init_reg_table[i];
+		ret = i2c_smbus_write_byte_data(client, reg->addr, reg->val);
+		if (ret < 0)
+			goto err_i2c_write;
+	}
+
+
+	/*
+	 * Capacitance on sensing input varies and needs to be compensated.
+	 * The internal MPR121-auto-configuration can do this if it's
+	 * registers are set properly (based on pdata->vdd_uv).
+	 */
+	vdd = pdata->vdd_uv / 1000;
+	usl = ((vdd - 700) * 256) / vdd;
+	lsl = (usl * 65) / 100;
+	tl = (usl * 90) / 100;
+	ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl);
+	ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl);
+	ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl);
+
+	/*
+	 * Quick charge bit will let the capacitive charge to ready
+	 * state quickly, or the buttons may not function after system
+	 * boot.
+	 */
+	eleconf = mpr121->keycount | ELECTRODE_CONF_QUICK_CHARGE;
+	ret |= i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR,
+					 eleconf);
+	if (ret != 0)
+		goto err_i2c_write;
+
+	dev_dbg(&client->dev, "set up with %x keys.\n", mpr121->keycount);
+
+	return 0;
+
+err_i2c_write:
+	dev_err(&client->dev, "i2c write error: %d\n", ret);
+	return ret;
+}
+
+static int mpr_touchkey_probe(struct i2c_client *client,
+			      const struct i2c_device_id *id)
+{
+	const struct mpr121_platform_data *pdata =
+			dev_get_platdata(&client->dev);
+	struct mpr121_touchkey *mpr121;
+	struct input_dev *input_dev;
+	int error;
+	int i;
+
+	if (!pdata) {
+		dev_err(&client->dev, "no platform data defined\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->keymap || !pdata->keymap_size) {
+		dev_err(&client->dev, "missing keymap data\n");
+		return -EINVAL;
+	}
+
+	if (pdata->keymap_size > MPR121_MAX_KEY_COUNT) {
+		dev_err(&client->dev, "too many keys defined\n");
+		return -EINVAL;
+	}
+
+	if (!client->irq) {
+		dev_err(&client->dev, "irq number should not be zero\n");
+		return -EINVAL;
+	}
+
+	mpr121 = devm_kzalloc(&client->dev, sizeof(*mpr121),
+			      GFP_KERNEL);
+	if (!mpr121)
+		return -ENOMEM;
+
+	input_dev = devm_input_allocate_device(&client->dev);
+	if (!input_dev)
+		return -ENOMEM;
+
+	mpr121->client = client;
+	mpr121->input_dev = input_dev;
+	mpr121->keycount = pdata->keymap_size;
+
+	input_dev->name = "Freescale MPR121 Touchkey";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &client->dev;
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+
+	input_dev->keycode = mpr121->keycodes;
+	input_dev->keycodesize = sizeof(mpr121->keycodes[0]);
+	input_dev->keycodemax = mpr121->keycount;
+
+	for (i = 0; i < pdata->keymap_size; i++) {
+		input_set_capability(input_dev, EV_KEY, pdata->keymap[i]);
+		mpr121->keycodes[i] = pdata->keymap[i];
+	}
+
+	error = mpr121_phys_init(pdata, mpr121, client);
+	if (error) {
+		dev_err(&client->dev, "Failed to init register\n");
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+				     mpr_touchkey_interrupt,
+				     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				     client->dev.driver->name, mpr121);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	error = input_register_device(input_dev);
+	if (error)
+		return error;
+
+	i2c_set_clientdata(client, mpr121);
+	device_init_wakeup(&client->dev, pdata->wakeup);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mpr_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	if (device_may_wakeup(&client->dev))
+		enable_irq_wake(client->irq);
+
+	i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00);
+
+	return 0;
+}
+
+static int mpr_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(&client->dev))
+		disable_irq_wake(client->irq);
+
+	i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR,
+				  mpr121->keycount);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mpr121_touchkey_pm_ops, mpr_suspend, mpr_resume);
+
+static const struct i2c_device_id mpr121_id[] = {
+	{ "mpr121_touchkey", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, mpr121_id);
+
+static struct i2c_driver mpr_touchkey_driver = {
+	.driver = {
+		.name	= "mpr121",
+		.pm	= &mpr121_touchkey_pm_ops,
+	},
+	.id_table	= mpr121_id,
+	.probe		= mpr_touchkey_probe,
+};
+
+module_i2c_driver(mpr_touchkey_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>");
+MODULE_DESCRIPTION("Touch Key driver for Freescale MPR121 Chip");
diff --git a/drivers/input/keyboard/newtonkbd.c b/drivers/input/keyboard/newtonkbd.c
new file mode 100644
index 0000000..20f0443
--- /dev/null
+++ b/drivers/input/keyboard/newtonkbd.c
@@ -0,0 +1,168 @@
+/*
+ *  Copyright (c) 2000 Justin Cormack
+ */
+
+/*
+ * Newton keyboard driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <j.cormack@doc.ic.ac.uk>, or by paper mail:
+ * Justin Cormack, 68 Dartmouth Park Road, London NW5 1SN, UK.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Newton keyboard driver"
+
+MODULE_AUTHOR("Justin Cormack <j.cormack@doc.ic.ac.uk>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define NKBD_KEY	0x7f
+#define NKBD_PRESS	0x80
+
+static unsigned char nkbd_keycode[128] = {
+	KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, KEY_X,
+	KEY_C, KEY_V, 0, KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R,
+	KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5,
+	KEY_EQUAL, KEY_9, KEY_7, KEY_MINUS, KEY_8, KEY_0, KEY_RIGHTBRACE, KEY_O,
+	KEY_U, KEY_LEFTBRACE, KEY_I, KEY_P, KEY_ENTER, KEY_L, KEY_J, KEY_APOSTROPHE,
+	KEY_K, KEY_SEMICOLON, KEY_BACKSLASH, KEY_COMMA, KEY_SLASH, KEY_N, KEY_M, KEY_DOT,
+	KEY_TAB, KEY_SPACE, KEY_GRAVE, KEY_DELETE, 0, 0, 0, KEY_LEFTMETA,
+	KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_LEFTALT, KEY_LEFTCTRL, KEY_RIGHTSHIFT, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0
+};
+
+struct nkbd {
+	unsigned char keycode[128];
+	struct input_dev *dev;
+	struct serio *serio;
+	char phys[32];
+};
+
+static irqreturn_t nkbd_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct nkbd *nkbd = serio_get_drvdata(serio);
+
+	/* invalid scan codes are probably the init sequence, so we ignore them */
+	if (nkbd->keycode[data & NKBD_KEY]) {
+		input_report_key(nkbd->dev, nkbd->keycode[data & NKBD_KEY], data & NKBD_PRESS);
+		input_sync(nkbd->dev);
+	}
+
+	else if (data == 0xe7) /* end of init sequence */
+		printk(KERN_INFO "input: %s on %s\n", nkbd->dev->name, serio->phys);
+	return IRQ_HANDLED;
+
+}
+
+static int nkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct nkbd *nkbd;
+	struct input_dev *input_dev;
+	int err = -ENOMEM;
+	int i;
+
+	nkbd = kzalloc(sizeof(struct nkbd), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!nkbd || !input_dev)
+		goto fail1;
+
+	nkbd->serio = serio;
+	nkbd->dev = input_dev;
+	snprintf(nkbd->phys, sizeof(nkbd->phys), "%s/input0", serio->phys);
+	memcpy(nkbd->keycode, nkbd_keycode, sizeof(nkbd->keycode));
+
+	input_dev->name = "Newton Keyboard";
+	input_dev->phys = nkbd->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_NEWTON;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+	input_dev->keycode = nkbd->keycode;
+	input_dev->keycodesize = sizeof(unsigned char);
+	input_dev->keycodemax = ARRAY_SIZE(nkbd_keycode);
+	for (i = 0; i < 128; i++)
+		set_bit(nkbd->keycode[i], input_dev->keybit);
+	clear_bit(0, input_dev->keybit);
+
+	serio_set_drvdata(serio, nkbd);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(nkbd->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(nkbd);
+	return err;
+}
+
+static void nkbd_disconnect(struct serio *serio)
+{
+	struct nkbd *nkbd = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(nkbd->dev);
+	kfree(nkbd);
+}
+
+static struct serio_device_id nkbd_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_NEWTON,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, nkbd_serio_ids);
+
+static struct serio_driver nkbd_drv = {
+	.driver		= {
+		.name	= "newtonkbd",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= nkbd_serio_ids,
+	.interrupt	= nkbd_interrupt,
+	.connect	= nkbd_connect,
+	.disconnect	= nkbd_disconnect,
+};
+
+module_serio_driver(nkbd_drv);
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c
new file mode 100644
index 0000000..8567ee4
--- /dev/null
+++ b/drivers/input/keyboard/nomadik-ske-keypad.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ *
+ * License terms:GNU General Public License (GPL) version 2
+ *
+ * Keypad controller driver for the SKE (Scroll Key Encoder) module used in
+ * the Nomadik 8815 and Ux500 platforms.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+
+#include <linux/platform_data/keypad-nomadik-ske.h>
+
+/* SKE_CR bits */
+#define SKE_KPMLT	(0x1 << 6)
+#define SKE_KPCN	(0x7 << 3)
+#define SKE_KPASEN	(0x1 << 2)
+#define SKE_KPASON	(0x1 << 7)
+
+/* SKE_IMSC bits */
+#define SKE_KPIMA	(0x1 << 2)
+
+/* SKE_ICR bits */
+#define SKE_KPICS	(0x1 << 3)
+#define SKE_KPICA	(0x1 << 2)
+
+/* SKE_RIS bits */
+#define SKE_KPRISA	(0x1 << 2)
+
+#define SKE_KEYPAD_ROW_SHIFT	3
+#define SKE_KPD_NUM_ROWS	8
+#define SKE_KPD_NUM_COLS	8
+
+/* keypad auto scan registers */
+#define SKE_ASR0	0x20
+#define SKE_ASR1	0x24
+#define SKE_ASR2	0x28
+#define SKE_ASR3	0x2C
+
+#define SKE_NUM_ASRX_REGISTERS	(4)
+#define	KEY_PRESSED_DELAY	10
+
+/**
+ * struct ske_keypad  - data structure used by keypad driver
+ * @irq:	irq no
+ * @reg_base:	ske registers base address
+ * @input:	pointer to input device object
+ * @board:	keypad platform device
+ * @keymap:	matrix scan code table for keycodes
+ * @clk:	clock structure pointer
+ */
+struct ske_keypad {
+	int irq;
+	void __iomem *reg_base;
+	struct input_dev *input;
+	const struct ske_keypad_platform_data *board;
+	unsigned short keymap[SKE_KPD_NUM_ROWS * SKE_KPD_NUM_COLS];
+	struct clk *clk;
+	struct clk *pclk;
+	spinlock_t ske_keypad_lock;
+};
+
+static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
+		u8 mask, u8 data)
+{
+	u32 ret;
+
+	spin_lock(&keypad->ske_keypad_lock);
+
+	ret = readl(keypad->reg_base + addr);
+	ret &= ~mask;
+	ret |= data;
+	writel(ret, keypad->reg_base + addr);
+
+	spin_unlock(&keypad->ske_keypad_lock);
+}
+
+/*
+ * ske_keypad_chip_init: init keypad controller configuration
+ *
+ * Enable Multi key press detection, auto scan mode
+ */
+static int __init ske_keypad_chip_init(struct ske_keypad *keypad)
+{
+	u32 value;
+	int timeout = keypad->board->debounce_ms;
+
+	/* check SKE_RIS to be 0 */
+	while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--)
+		cpu_relax();
+
+	if (!timeout)
+		return -EINVAL;
+
+	/*
+	 * set debounce value
+	 * keypad dbounce is configured in DBCR[15:8]
+	 * dbounce value in steps of 32/32.768 ms
+	 */
+	spin_lock(&keypad->ske_keypad_lock);
+	value = readl(keypad->reg_base + SKE_DBCR);
+	value = value & 0xff;
+	value |= ((keypad->board->debounce_ms * 32000)/32768) << 8;
+	writel(value, keypad->reg_base + SKE_DBCR);
+	spin_unlock(&keypad->ske_keypad_lock);
+
+	/* enable multi key detection */
+	ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT);
+
+	/*
+	 * set up the number of columns
+	 * KPCN[5:3] defines no. of keypad columns to be auto scanned
+	 */
+	value = (keypad->board->kcol - 1) << 3;
+	ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value);
+
+	/* clear keypad interrupt for auto(and pending SW) scans */
+	ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS);
+
+	/* un-mask keypad interrupts */
+	ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+
+	/* enable automatic scan */
+	ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN);
+
+	return 0;
+}
+
+static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col)
+{
+	int row = 0, code, pos;
+	struct input_dev *input = keypad->input;
+	u32 ske_ris;
+	int key_pressed;
+	int num_of_rows;
+
+	/* find out the row */
+	num_of_rows = hweight8(status);
+	do {
+		pos = __ffs(status);
+		row = pos;
+		status &= ~(1 << pos);
+
+		code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
+		ske_ris = readl(keypad->reg_base + SKE_RIS);
+		key_pressed = ske_ris & SKE_KPRISA;
+
+		input_event(input, EV_MSC, MSC_SCAN, code);
+		input_report_key(input, keypad->keymap[code], key_pressed);
+		input_sync(input);
+		num_of_rows--;
+	} while (num_of_rows);
+}
+
+static void ske_keypad_read_data(struct ske_keypad *keypad)
+{
+	u8 status;
+	int col = 0;
+	int ske_asr, i;
+
+	/*
+	 * Read the auto scan registers
+	 *
+	 * Each SKE_ASRx (x=0 to x=3) contains two row values.
+	 * lower byte contains row value for column 2*x,
+	 * upper byte contains row value for column 2*x + 1
+	 */
+	for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) {
+		ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i));
+		if (!ske_asr)
+			continue;
+
+		/* now that ASRx is zero, find out the coloumn x and row y */
+		status = ske_asr & 0xff;
+		if (status) {
+			col = i * 2;
+			ske_keypad_report(keypad, status, col);
+		}
+		status = (ske_asr & 0xff00) >> 8;
+		if (status) {
+			col = (i * 2) + 1;
+			ske_keypad_report(keypad, status, col);
+		}
+	}
+}
+
+static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
+{
+	struct ske_keypad *keypad = dev_id;
+	int timeout = keypad->board->debounce_ms;
+
+	/* disable auto scan interrupt; mask the interrupt generated */
+	ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
+	ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
+
+	while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --timeout)
+		cpu_relax();
+
+	/* SKEx registers are stable and can be read */
+	ske_keypad_read_data(keypad);
+
+	/* wait until raw interrupt is clear */
+	while ((readl(keypad->reg_base + SKE_RIS)) && --timeout)
+		msleep(KEY_PRESSED_DELAY);
+
+	/* enable auto scan interrupts */
+	ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+
+	return IRQ_HANDLED;
+}
+
+static int __init ske_keypad_probe(struct platform_device *pdev)
+{
+	const struct ske_keypad_platform_data *plat =
+			dev_get_platdata(&pdev->dev);
+	struct ske_keypad *keypad;
+	struct input_dev *input;
+	struct resource *res;
+	int irq;
+	int error;
+
+	if (!plat) {
+		dev_err(&pdev->dev, "invalid keypad platform data\n");
+		return -EINVAL;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to get keypad irq\n");
+		return -EINVAL;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "missing platform resources\n");
+		return -EINVAL;
+	}
+
+	keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL);
+	input = input_allocate_device();
+	if (!keypad || !input) {
+		dev_err(&pdev->dev, "failed to allocate keypad memory\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	keypad->irq = irq;
+	keypad->board = plat;
+	keypad->input = input;
+	spin_lock_init(&keypad->ske_keypad_lock);
+
+	if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+		dev_err(&pdev->dev, "failed to request I/O memory\n");
+		error = -EBUSY;
+		goto err_free_mem;
+	}
+
+	keypad->reg_base = ioremap(res->start, resource_size(res));
+	if (!keypad->reg_base) {
+		dev_err(&pdev->dev, "failed to remap I/O memory\n");
+		error = -ENXIO;
+		goto err_free_mem_region;
+	}
+
+	keypad->pclk = clk_get(&pdev->dev, "apb_pclk");
+	if (IS_ERR(keypad->pclk)) {
+		dev_err(&pdev->dev, "failed to get pclk\n");
+		error = PTR_ERR(keypad->pclk);
+		goto err_iounmap;
+	}
+
+	keypad->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(keypad->clk)) {
+		dev_err(&pdev->dev, "failed to get clk\n");
+		error = PTR_ERR(keypad->clk);
+		goto err_pclk;
+	}
+
+	input->id.bustype = BUS_HOST;
+	input->name = "ux500-ske-keypad";
+	input->dev.parent = &pdev->dev;
+
+	error = matrix_keypad_build_keymap(plat->keymap_data, NULL,
+					   SKE_KPD_NUM_ROWS, SKE_KPD_NUM_COLS,
+					   keypad->keymap, input);
+	if (error) {
+		dev_err(&pdev->dev, "Failed to build keymap\n");
+		goto err_clk;
+	}
+
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+	if (!plat->no_autorepeat)
+		__set_bit(EV_REP, input->evbit);
+
+	error = clk_prepare_enable(keypad->pclk);
+	if (error) {
+		dev_err(&pdev->dev, "Failed to prepare/enable pclk\n");
+		goto err_clk;
+	}
+
+	error = clk_prepare_enable(keypad->clk);
+	if (error) {
+		dev_err(&pdev->dev, "Failed to prepare/enable clk\n");
+		goto err_pclk_disable;
+	}
+
+
+	/* go through board initialization helpers */
+	if (keypad->board->init)
+		keypad->board->init();
+
+	error = ske_keypad_chip_init(keypad);
+	if (error) {
+		dev_err(&pdev->dev, "unable to init keypad hardware\n");
+		goto err_clk_disable;
+	}
+
+	error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq,
+				     IRQF_ONESHOT, "ske-keypad", keypad);
+	if (error) {
+		dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
+		goto err_clk_disable;
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(&pdev->dev,
+				"unable to register input device: %d\n", error);
+		goto err_free_irq;
+	}
+
+	if (plat->wakeup_enable)
+		device_init_wakeup(&pdev->dev, true);
+
+	platform_set_drvdata(pdev, keypad);
+
+	return 0;
+
+err_free_irq:
+	free_irq(keypad->irq, keypad);
+err_clk_disable:
+	clk_disable_unprepare(keypad->clk);
+err_pclk_disable:
+	clk_disable_unprepare(keypad->pclk);
+err_clk:
+	clk_put(keypad->clk);
+err_pclk:
+	clk_put(keypad->pclk);
+err_iounmap:
+	iounmap(keypad->reg_base);
+err_free_mem_region:
+	release_mem_region(res->start, resource_size(res));
+err_free_mem:
+	input_free_device(input);
+	kfree(keypad);
+	return error;
+}
+
+static int ske_keypad_remove(struct platform_device *pdev)
+{
+	struct ske_keypad *keypad = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	free_irq(keypad->irq, keypad);
+
+	input_unregister_device(keypad->input);
+
+	clk_disable_unprepare(keypad->clk);
+	clk_put(keypad->clk);
+
+	if (keypad->board->exit)
+		keypad->board->exit();
+
+	iounmap(keypad->reg_base);
+	release_mem_region(res->start, resource_size(res));
+	kfree(keypad);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ske_keypad_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ske_keypad *keypad = platform_get_drvdata(pdev);
+	int irq = platform_get_irq(pdev, 0);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(irq);
+	else
+		ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
+
+	return 0;
+}
+
+static int ske_keypad_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ske_keypad *keypad = platform_get_drvdata(pdev);
+	int irq = platform_get_irq(pdev, 0);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(irq);
+	else
+		ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ske_keypad_dev_pm_ops,
+			 ske_keypad_suspend, ske_keypad_resume);
+
+static struct platform_driver ske_keypad_driver = {
+	.driver = {
+		.name = "nmk-ske-keypad",
+		.pm = &ske_keypad_dev_pm_ops,
+	},
+	.remove = ske_keypad_remove,
+};
+
+module_platform_driver_probe(ske_keypad_driver, ske_keypad_probe);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com> / Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver");
+MODULE_ALIAS("platform:nomadik-ske-keypad");
diff --git a/drivers/input/keyboard/nspire-keypad.c b/drivers/input/keyboard/nspire-keypad.c
new file mode 100644
index 0000000..7abfd34
--- /dev/null
+++ b/drivers/input/keyboard/nspire-keypad.c
@@ -0,0 +1,279 @@
+/*
+ *  Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/input/matrix_keypad.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#define KEYPAD_SCAN_MODE	0x00
+#define KEYPAD_CNTL		0x04
+#define KEYPAD_INT		0x08
+#define KEYPAD_INTMSK		0x0C
+
+#define KEYPAD_DATA		0x10
+#define KEYPAD_GPIO		0x30
+
+#define KEYPAD_UNKNOWN_INT	0x40
+#define KEYPAD_UNKNOWN_INT_STS	0x44
+
+#define KEYPAD_BITMASK_COLS	11
+#define KEYPAD_BITMASK_ROWS	8
+
+struct nspire_keypad {
+	void __iomem *reg_base;
+	u32 int_mask;
+
+	struct input_dev *input;
+	struct clk *clk;
+
+	struct matrix_keymap_data *keymap;
+	int row_shift;
+
+	/* Maximum delay estimated assuming 33MHz APB */
+	u32 scan_interval;	/* In microseconds (~2000us max) */
+	u32 row_delay;		/* In microseconds (~500us max) */
+
+	u16 state[KEYPAD_BITMASK_ROWS];
+
+	bool active_low;
+};
+
+static irqreturn_t nspire_keypad_irq(int irq, void *dev_id)
+{
+	struct nspire_keypad *keypad = dev_id;
+	struct input_dev *input = keypad->input;
+	unsigned short *keymap = input->keycode;
+	unsigned int code;
+	int row, col;
+	u32 int_sts;
+	u16 state[8];
+	u16 bits, changed;
+
+	int_sts = readl(keypad->reg_base + KEYPAD_INT) & keypad->int_mask;
+	if (!int_sts)
+		return IRQ_NONE;
+
+	memcpy_fromio(state, keypad->reg_base + KEYPAD_DATA, sizeof(state));
+
+	for (row = 0; row < KEYPAD_BITMASK_ROWS; row++) {
+		bits = state[row];
+		if (keypad->active_low)
+			bits = ~bits;
+
+		changed = bits ^ keypad->state[row];
+		if (!changed)
+			continue;
+
+		keypad->state[row] = bits;
+
+		for (col = 0; col < KEYPAD_BITMASK_COLS; col++) {
+			if (!(changed & (1U << col)))
+				continue;
+
+			code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
+			input_event(input, EV_MSC, MSC_SCAN, code);
+			input_report_key(input, keymap[code],
+					 bits & (1U << col));
+		}
+	}
+
+	input_sync(input);
+
+	writel(0x3, keypad->reg_base + KEYPAD_INT);
+
+	return IRQ_HANDLED;
+}
+
+static int nspire_keypad_chip_init(struct nspire_keypad *keypad)
+{
+	unsigned long val = 0, cycles_per_us, delay_cycles, row_delay_cycles;
+
+	cycles_per_us = (clk_get_rate(keypad->clk) / 1000000);
+	if (cycles_per_us == 0)
+		cycles_per_us = 1;
+
+	delay_cycles = cycles_per_us * keypad->scan_interval;
+	WARN_ON(delay_cycles >= (1 << 16)); /* Overflow */
+	delay_cycles &= 0xffff;
+
+	row_delay_cycles = cycles_per_us * keypad->row_delay;
+	WARN_ON(row_delay_cycles >= (1 << 14)); /* Overflow */
+	row_delay_cycles &= 0x3fff;
+
+	val |= 3 << 0; /* Set scan mode to 3 (continuous scan) */
+	val |= row_delay_cycles << 2; /* Delay between scanning each row */
+	val |= delay_cycles << 16; /* Delay between scans */
+	writel(val, keypad->reg_base + KEYPAD_SCAN_MODE);
+
+	val = (KEYPAD_BITMASK_ROWS & 0xff) | (KEYPAD_BITMASK_COLS & 0xff)<<8;
+	writel(val, keypad->reg_base + KEYPAD_CNTL);
+
+	/* Enable interrupts */
+	keypad->int_mask = 1 << 1;
+	writel(keypad->int_mask, keypad->reg_base + KEYPAD_INTMSK);
+
+	/* Disable GPIO interrupts to prevent hanging on touchpad */
+	/* Possibly used to detect touchpad events */
+	writel(0, keypad->reg_base + KEYPAD_UNKNOWN_INT);
+	/* Acknowledge existing interrupts */
+	writel(~0, keypad->reg_base + KEYPAD_UNKNOWN_INT_STS);
+
+	return 0;
+}
+
+static int nspire_keypad_open(struct input_dev *input)
+{
+	struct nspire_keypad *keypad = input_get_drvdata(input);
+	int error;
+
+	error = clk_prepare_enable(keypad->clk);
+	if (error)
+		return error;
+
+	error = nspire_keypad_chip_init(keypad);
+	if (error) {
+		clk_disable_unprepare(keypad->clk);
+		return error;
+	}
+
+	return 0;
+}
+
+static void nspire_keypad_close(struct input_dev *input)
+{
+	struct nspire_keypad *keypad = input_get_drvdata(input);
+
+	clk_disable_unprepare(keypad->clk);
+}
+
+static int nspire_keypad_probe(struct platform_device *pdev)
+{
+	const struct device_node *of_node = pdev->dev.of_node;
+	struct nspire_keypad *keypad;
+	struct input_dev *input;
+	struct resource *res;
+	int irq;
+	int error;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to get keypad irq\n");
+		return -EINVAL;
+	}
+
+	keypad = devm_kzalloc(&pdev->dev, sizeof(struct nspire_keypad),
+			      GFP_KERNEL);
+	if (!keypad) {
+		dev_err(&pdev->dev, "failed to allocate keypad memory\n");
+		return -ENOMEM;
+	}
+
+	keypad->row_shift = get_count_order(KEYPAD_BITMASK_COLS);
+
+	error = of_property_read_u32(of_node, "scan-interval",
+				     &keypad->scan_interval);
+	if (error) {
+		dev_err(&pdev->dev, "failed to get scan-interval\n");
+		return error;
+	}
+
+	error = of_property_read_u32(of_node, "row-delay",
+				     &keypad->row_delay);
+	if (error) {
+		dev_err(&pdev->dev, "failed to get row-delay\n");
+		return error;
+	}
+
+	keypad->active_low = of_property_read_bool(of_node, "active-low");
+
+	keypad->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(keypad->clk)) {
+		dev_err(&pdev->dev, "unable to get clock\n");
+		return PTR_ERR(keypad->clk);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	keypad->reg_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(keypad->reg_base))
+		return PTR_ERR(keypad->reg_base);
+
+	keypad->input = input = devm_input_allocate_device(&pdev->dev);
+	if (!input) {
+		dev_err(&pdev->dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	input_set_drvdata(input, keypad);
+
+	input->id.bustype = BUS_HOST;
+	input->name = "nspire-keypad";
+	input->open = nspire_keypad_open;
+	input->close = nspire_keypad_close;
+
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(EV_REP, input->evbit);
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+
+	error = matrix_keypad_build_keymap(NULL, NULL,
+					   KEYPAD_BITMASK_ROWS,
+					   KEYPAD_BITMASK_COLS,
+					   NULL, input);
+	if (error) {
+		dev_err(&pdev->dev, "building keymap failed\n");
+		return error;
+	}
+
+	error = devm_request_irq(&pdev->dev, irq, nspire_keypad_irq, 0,
+				 "nspire_keypad", keypad);
+	if (error) {
+		dev_err(&pdev->dev, "allocate irq %d failed\n", irq);
+		return error;
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(&pdev->dev,
+			"unable to register input device: %d\n", error);
+		return error;
+	}
+
+	platform_set_drvdata(pdev, keypad);
+
+	dev_dbg(&pdev->dev,
+		"TI-NSPIRE keypad at %pR (scan_interval=%uus, row_delay=%uus%s)\n",
+		res, keypad->row_delay, keypad->scan_interval,
+		keypad->active_low ? ", active_low" : "");
+
+	return 0;
+}
+
+static const struct of_device_id nspire_keypad_dt_match[] = {
+	{ .compatible = "ti,nspire-keypad" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, nspire_keypad_dt_match);
+
+static struct platform_driver nspire_keypad_driver = {
+	.driver = {
+		.name = "nspire-keypad",
+		.of_match_table = nspire_keypad_dt_match,
+	},
+	.probe = nspire_keypad_probe,
+};
+
+module_platform_driver(nspire_keypad_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("TI-NSPIRE Keypad Driver");
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
new file mode 100644
index 0000000..7502e46
--- /dev/null
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -0,0 +1,393 @@
+/*
+ * linux/drivers/input/keyboard/omap-keypad.c
+ *
+ * OMAP Keypad Driver
+ *
+ * Copyright (C) 2003 Nokia Corporation
+ * Written by Timo Teräs <ext-timo.teras@nokia.com>
+ *
+ * Added support for H2 & H3 Keypad
+ * Copyright (C) 2004 Texas Instruments
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/platform_data/gpio-omap.h>
+#include <linux/platform_data/keypad-omap.h>
+
+#undef NEW_BOARD_LEARNING_MODE
+
+static void omap_kp_tasklet(unsigned long);
+static void omap_kp_timer(unsigned long);
+
+static unsigned char keypad_state[8];
+static DEFINE_MUTEX(kp_enable_mutex);
+static int kp_enable = 1;
+static int kp_cur_group = -1;
+
+struct omap_kp {
+	struct input_dev *input;
+	struct timer_list timer;
+	int irq;
+	unsigned int rows;
+	unsigned int cols;
+	unsigned long delay;
+	unsigned int debounce;
+	unsigned short keymap[];
+};
+
+static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
+
+static unsigned int *row_gpios;
+static unsigned int *col_gpios;
+
+#ifdef CONFIG_ARCH_OMAP2
+static void set_col_gpio_val(struct omap_kp *omap_kp, u8 value)
+{
+	int col;
+
+	for (col = 0; col < omap_kp->cols; col++)
+		gpio_set_value(col_gpios[col], value & (1 << col));
+}
+
+static u8 get_row_gpio_val(struct omap_kp *omap_kp)
+{
+	int row;
+	u8 value = 0;
+
+	for (row = 0; row < omap_kp->rows; row++) {
+		if (gpio_get_value(row_gpios[row]))
+			value |= (1 << row);
+	}
+	return value;
+}
+#else
+#define		set_col_gpio_val(x, y)	do {} while (0)
+#define		get_row_gpio_val(x)	0
+#endif
+
+static irqreturn_t omap_kp_interrupt(int irq, void *dev_id)
+{
+	/* disable keyboard interrupt and schedule for handling */
+	omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+	tasklet_schedule(&kp_tasklet);
+
+	return IRQ_HANDLED;
+}
+
+static void omap_kp_timer(unsigned long data)
+{
+	tasklet_schedule(&kp_tasklet);
+}
+
+static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state)
+{
+	int col = 0;
+
+	/* disable keyboard interrupt and schedule for handling */
+	omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+	/* read the keypad status */
+	omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC);
+	for (col = 0; col < omap_kp->cols; col++) {
+		omap_writew(~(1 << col) & 0xff,
+			    OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC);
+
+		udelay(omap_kp->delay);
+
+		state[col] = ~omap_readw(OMAP1_MPUIO_BASE +
+					 OMAP_MPUIO_KBR_LATCH) & 0xff;
+	}
+	omap_writew(0x00, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC);
+	udelay(2);
+}
+
+static void omap_kp_tasklet(unsigned long data)
+{
+	struct omap_kp *omap_kp_data = (struct omap_kp *) data;
+	unsigned short *keycodes = omap_kp_data->input->keycode;
+	unsigned int row_shift = get_count_order(omap_kp_data->cols);
+	unsigned char new_state[8], changed, key_down = 0;
+	int col, row;
+	int spurious = 0;
+
+	/* check for any changes */
+	omap_kp_scan_keypad(omap_kp_data, new_state);
+
+	/* check for changes and print those */
+	for (col = 0; col < omap_kp_data->cols; col++) {
+		changed = new_state[col] ^ keypad_state[col];
+		key_down |= new_state[col];
+		if (changed == 0)
+			continue;
+
+		for (row = 0; row < omap_kp_data->rows; row++) {
+			int key;
+			if (!(changed & (1 << row)))
+				continue;
+#ifdef NEW_BOARD_LEARNING_MODE
+			printk(KERN_INFO "omap-keypad: key %d-%d %s\n", col,
+			       row, (new_state[col] & (1 << row)) ?
+			       "pressed" : "released");
+#else
+			key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
+			if (key < 0) {
+				printk(KERN_WARNING
+				      "omap-keypad: Spurious key event %d-%d\n",
+				       col, row);
+				/* We scan again after a couple of seconds */
+				spurious = 1;
+				continue;
+			}
+
+			if (!(kp_cur_group == (key & GROUP_MASK) ||
+			      kp_cur_group == -1))
+				continue;
+
+			kp_cur_group = key & GROUP_MASK;
+			input_report_key(omap_kp_data->input, key & ~GROUP_MASK,
+					 new_state[col] & (1 << row));
+#endif
+		}
+	}
+	input_sync(omap_kp_data->input);
+	memcpy(keypad_state, new_state, sizeof(keypad_state));
+
+	if (key_down) {
+		int delay = HZ / 20;
+		/* some key is pressed - keep irq disabled and use timer
+		 * to poll the keypad */
+		if (spurious)
+			delay = 2 * HZ;
+		mod_timer(&omap_kp_data->timer, jiffies + delay);
+	} else {
+		/* enable interrupts */
+		omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+		kp_cur_group = -1;
+	}
+}
+
+static ssize_t omap_kp_enable_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%u\n", kp_enable);
+}
+
+static ssize_t omap_kp_enable_store(struct device *dev, struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct omap_kp *omap_kp = dev_get_drvdata(dev);
+	int state;
+
+	if (sscanf(buf, "%u", &state) != 1)
+		return -EINVAL;
+
+	if ((state != 1) && (state != 0))
+		return -EINVAL;
+
+	mutex_lock(&kp_enable_mutex);
+	if (state != kp_enable) {
+		if (state)
+			enable_irq(omap_kp->irq);
+		else
+			disable_irq(omap_kp->irq);
+		kp_enable = state;
+	}
+	mutex_unlock(&kp_enable_mutex);
+
+	return strnlen(buf, count);
+}
+
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, omap_kp_enable_show, omap_kp_enable_store);
+
+#ifdef CONFIG_PM
+static int omap_kp_suspend(struct platform_device *dev, pm_message_t state)
+{
+	/* Nothing yet */
+
+	return 0;
+}
+
+static int omap_kp_resume(struct platform_device *dev)
+{
+	/* Nothing yet */
+
+	return 0;
+}
+#else
+#define omap_kp_suspend	NULL
+#define omap_kp_resume	NULL
+#endif
+
+static int omap_kp_probe(struct platform_device *pdev)
+{
+	struct omap_kp *omap_kp;
+	struct input_dev *input_dev;
+	struct omap_kp_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	int i, col_idx, row_idx, ret;
+	unsigned int row_shift, keycodemax;
+
+	if (!pdata->rows || !pdata->cols || !pdata->keymap_data) {
+		printk(KERN_ERR "No rows, cols or keymap_data from pdata\n");
+		return -EINVAL;
+	}
+
+	row_shift = get_count_order(pdata->cols);
+	keycodemax = pdata->rows << row_shift;
+
+	omap_kp = kzalloc(sizeof(struct omap_kp) +
+			keycodemax * sizeof(unsigned short), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!omap_kp || !input_dev) {
+		kfree(omap_kp);
+		input_free_device(input_dev);
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, omap_kp);
+
+	omap_kp->input = input_dev;
+
+	/* Disable the interrupt for the MPUIO keyboard */
+	omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+	if (pdata->delay)
+		omap_kp->delay = pdata->delay;
+
+	if (pdata->row_gpios && pdata->col_gpios) {
+		row_gpios = pdata->row_gpios;
+		col_gpios = pdata->col_gpios;
+	}
+
+	omap_kp->rows = pdata->rows;
+	omap_kp->cols = pdata->cols;
+
+	col_idx = 0;
+	row_idx = 0;
+
+	setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp);
+
+	/* get the irq and init timer*/
+	tasklet_enable(&kp_tasklet);
+	kp_tasklet.data = (unsigned long) omap_kp;
+
+	ret = device_create_file(&pdev->dev, &dev_attr_enable);
+	if (ret < 0)
+		goto err2;
+
+	/* setup input device */
+	input_dev->name = "omap-keypad";
+	input_dev->phys = "omap-keypad/input0";
+	input_dev->dev.parent = &pdev->dev;
+
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+
+	if (pdata->rep)
+		__set_bit(EV_REP, input_dev->evbit);
+
+	ret = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
+					 pdata->rows, pdata->cols,
+					 omap_kp->keymap, input_dev);
+	if (ret < 0)
+		goto err3;
+
+	ret = input_register_device(omap_kp->input);
+	if (ret < 0) {
+		printk(KERN_ERR "Unable to register omap-keypad input device\n");
+		goto err3;
+	}
+
+	if (pdata->dbounce)
+		omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING);
+
+	/* scan current status and enable interrupt */
+	omap_kp_scan_keypad(omap_kp, keypad_state);
+	omap_kp->irq = platform_get_irq(pdev, 0);
+	if (omap_kp->irq >= 0) {
+		if (request_irq(omap_kp->irq, omap_kp_interrupt, 0,
+				"omap-keypad", omap_kp) < 0)
+			goto err4;
+	}
+	omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+	return 0;
+
+err4:
+	input_unregister_device(omap_kp->input);
+	input_dev = NULL;
+err3:
+	device_remove_file(&pdev->dev, &dev_attr_enable);
+err2:
+	for (i = row_idx - 1; i >= 0; i--)
+		gpio_free(row_gpios[i]);
+	for (i = col_idx - 1; i >= 0; i--)
+		gpio_free(col_gpios[i]);
+
+	kfree(omap_kp);
+	input_free_device(input_dev);
+
+	return -EINVAL;
+}
+
+static int omap_kp_remove(struct platform_device *pdev)
+{
+	struct omap_kp *omap_kp = platform_get_drvdata(pdev);
+
+	/* disable keypad interrupt handling */
+	tasklet_disable(&kp_tasklet);
+	omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+	free_irq(omap_kp->irq, omap_kp);
+
+	del_timer_sync(&omap_kp->timer);
+	tasklet_kill(&kp_tasklet);
+
+	/* unregister everything */
+	input_unregister_device(omap_kp->input);
+
+	kfree(omap_kp);
+
+	return 0;
+}
+
+static struct platform_driver omap_kp_driver = {
+	.probe		= omap_kp_probe,
+	.remove		= omap_kp_remove,
+	.suspend	= omap_kp_suspend,
+	.resume		= omap_kp_resume,
+	.driver		= {
+		.name	= "omap-keypad",
+	},
+};
+module_platform_driver(omap_kp_driver);
+
+MODULE_AUTHOR("Timo Teräs");
+MODULE_DESCRIPTION("OMAP Keypad Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap-keypad");
diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
new file mode 100644
index 0000000..6639b2b
--- /dev/null
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -0,0 +1,472 @@
+/*
+ * OMAP4 Keypad Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Author: Abraham Arce <x0066660@ti.com>
+ * Initial Code: Syed Rafiuddin <rafiuddin.syed@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+
+/* OMAP4 registers */
+#define OMAP4_KBD_REVISION		0x00
+#define OMAP4_KBD_SYSCONFIG		0x10
+#define OMAP4_KBD_SYSSTATUS		0x14
+#define OMAP4_KBD_IRQSTATUS		0x18
+#define OMAP4_KBD_IRQENABLE		0x1C
+#define OMAP4_KBD_WAKEUPENABLE		0x20
+#define OMAP4_KBD_PENDING		0x24
+#define OMAP4_KBD_CTRL			0x28
+#define OMAP4_KBD_DEBOUNCINGTIME	0x2C
+#define OMAP4_KBD_LONGKEYTIME		0x30
+#define OMAP4_KBD_TIMEOUT		0x34
+#define OMAP4_KBD_STATEMACHINE		0x38
+#define OMAP4_KBD_ROWINPUTS		0x3C
+#define OMAP4_KBD_COLUMNOUTPUTS		0x40
+#define OMAP4_KBD_FULLCODE31_0		0x44
+#define OMAP4_KBD_FULLCODE63_32		0x48
+
+/* OMAP4 bit definitions */
+#define OMAP4_DEF_IRQENABLE_EVENTEN	BIT(0)
+#define OMAP4_DEF_IRQENABLE_LONGKEY	BIT(1)
+#define OMAP4_DEF_WUP_EVENT_ENA		BIT(0)
+#define OMAP4_DEF_WUP_LONG_KEY_ENA	BIT(1)
+#define OMAP4_DEF_CTRL_NOSOFTMODE	BIT(1)
+#define OMAP4_DEF_CTRL_PTV_SHIFT	2
+
+/* OMAP4 values */
+#define OMAP4_VAL_IRQDISABLE		0x0
+#define OMAP4_VAL_DEBOUNCINGTIME	0x7
+#define OMAP4_VAL_PVT			0x7
+
+enum {
+	KBD_REVISION_OMAP4 = 0,
+	KBD_REVISION_OMAP5,
+};
+
+struct omap4_keypad {
+	struct input_dev *input;
+
+	void __iomem *base;
+	bool irq_wake_enabled;
+	unsigned int irq;
+
+	unsigned int rows;
+	unsigned int cols;
+	u32 reg_offset;
+	u32 irqreg_offset;
+	unsigned int row_shift;
+	bool no_autorepeat;
+	unsigned char key_state[8];
+	unsigned short *keymap;
+};
+
+static int kbd_readl(struct omap4_keypad *keypad_data, u32 offset)
+{
+	return __raw_readl(keypad_data->base +
+				keypad_data->reg_offset + offset);
+}
+
+static void kbd_writel(struct omap4_keypad *keypad_data, u32 offset, u32 value)
+{
+	__raw_writel(value,
+		     keypad_data->base + keypad_data->reg_offset + offset);
+}
+
+static int kbd_read_irqreg(struct omap4_keypad *keypad_data, u32 offset)
+{
+	return __raw_readl(keypad_data->base +
+				keypad_data->irqreg_offset + offset);
+}
+
+static void kbd_write_irqreg(struct omap4_keypad *keypad_data,
+			     u32 offset, u32 value)
+{
+	__raw_writel(value,
+		     keypad_data->base + keypad_data->irqreg_offset + offset);
+}
+
+
+/* Interrupt handlers */
+static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id)
+{
+	struct omap4_keypad *keypad_data = dev_id;
+
+	if (kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS)) {
+		/* Disable interrupts */
+		kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
+				 OMAP4_VAL_IRQDISABLE);
+		return IRQ_WAKE_THREAD;
+	}
+
+	return IRQ_NONE;
+}
+
+static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id)
+{
+	struct omap4_keypad *keypad_data = dev_id;
+	struct input_dev *input_dev = keypad_data->input;
+	unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];
+	unsigned int col, row, code, changed;
+	u32 *new_state = (u32 *) key_state;
+
+	*new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0);
+	*(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32);
+
+	for (row = 0; row < keypad_data->rows; row++) {
+		changed = key_state[row] ^ keypad_data->key_state[row];
+		if (!changed)
+			continue;
+
+		for (col = 0; col < keypad_data->cols; col++) {
+			if (changed & (1 << col)) {
+				code = MATRIX_SCAN_CODE(row, col,
+						keypad_data->row_shift);
+				input_event(input_dev, EV_MSC, MSC_SCAN, code);
+				input_report_key(input_dev,
+						 keypad_data->keymap[code],
+						 key_state[row] & (1 << col));
+			}
+		}
+	}
+
+	input_sync(input_dev);
+
+	memcpy(keypad_data->key_state, key_state,
+		sizeof(keypad_data->key_state));
+
+	/* clear pending interrupts */
+	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
+			 kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
+
+	/* enable interrupts */
+	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
+		OMAP4_DEF_IRQENABLE_EVENTEN |
+				OMAP4_DEF_IRQENABLE_LONGKEY);
+
+	return IRQ_HANDLED;
+}
+
+static int omap4_keypad_open(struct input_dev *input)
+{
+	struct omap4_keypad *keypad_data = input_get_drvdata(input);
+
+	pm_runtime_get_sync(input->dev.parent);
+
+	disable_irq(keypad_data->irq);
+
+	kbd_writel(keypad_data, OMAP4_KBD_CTRL,
+			OMAP4_DEF_CTRL_NOSOFTMODE |
+			(OMAP4_VAL_PVT << OMAP4_DEF_CTRL_PTV_SHIFT));
+	kbd_writel(keypad_data, OMAP4_KBD_DEBOUNCINGTIME,
+			OMAP4_VAL_DEBOUNCINGTIME);
+	/* clear pending interrupts */
+	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
+			 kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
+	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
+			OMAP4_DEF_IRQENABLE_EVENTEN |
+				OMAP4_DEF_IRQENABLE_LONGKEY);
+	kbd_writel(keypad_data, OMAP4_KBD_WAKEUPENABLE,
+			OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA);
+
+	enable_irq(keypad_data->irq);
+
+	return 0;
+}
+
+static void omap4_keypad_close(struct input_dev *input)
+{
+	struct omap4_keypad *keypad_data = input_get_drvdata(input);
+
+	disable_irq(keypad_data->irq);
+
+	/* Disable interrupts */
+	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
+			 OMAP4_VAL_IRQDISABLE);
+
+	/* clear pending interrupts */
+	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
+			 kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
+
+	enable_irq(keypad_data->irq);
+
+	pm_runtime_put_sync(input->dev.parent);
+}
+
+static int omap4_keypad_parse_dt(struct device *dev,
+				 struct omap4_keypad *keypad_data)
+{
+	struct device_node *np = dev->of_node;
+	int err;
+
+	err = matrix_keypad_parse_of_params(dev, &keypad_data->rows,
+					    &keypad_data->cols);
+	if (err)
+		return err;
+
+	if (of_get_property(np, "linux,input-no-autorepeat", NULL))
+		keypad_data->no_autorepeat = true;
+
+	return 0;
+}
+
+static int omap4_keypad_probe(struct platform_device *pdev)
+{
+	struct omap4_keypad *keypad_data;
+	struct input_dev *input_dev;
+	struct resource *res;
+	unsigned int max_keys;
+	int rev;
+	int irq;
+	int error;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no base address specified\n");
+		return -EINVAL;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (!irq) {
+		dev_err(&pdev->dev, "no keyboard irq assigned\n");
+		return -EINVAL;
+	}
+
+	keypad_data = kzalloc(sizeof(struct omap4_keypad), GFP_KERNEL);
+	if (!keypad_data) {
+		dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
+		return -ENOMEM;
+	}
+
+	keypad_data->irq = irq;
+
+	error = omap4_keypad_parse_dt(&pdev->dev, keypad_data);
+	if (error)
+		goto err_free_keypad;
+
+	res = request_mem_region(res->start, resource_size(res), pdev->name);
+	if (!res) {
+		dev_err(&pdev->dev, "can't request mem region\n");
+		error = -EBUSY;
+		goto err_free_keypad;
+	}
+
+	keypad_data->base = ioremap(res->start, resource_size(res));
+	if (!keypad_data->base) {
+		dev_err(&pdev->dev, "can't ioremap mem resource\n");
+		error = -ENOMEM;
+		goto err_release_mem;
+	}
+
+
+	/*
+	 * Enable clocks for the keypad module so that we can read
+	 * revision register.
+	 */
+	pm_runtime_enable(&pdev->dev);
+	error = pm_runtime_get_sync(&pdev->dev);
+	if (error) {
+		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
+		goto err_unmap;
+	}
+	rev = __raw_readl(keypad_data->base + OMAP4_KBD_REVISION);
+	rev &= 0x03 << 30;
+	rev >>= 30;
+	switch (rev) {
+	case KBD_REVISION_OMAP4:
+		keypad_data->reg_offset = 0x00;
+		keypad_data->irqreg_offset = 0x00;
+		break;
+	case KBD_REVISION_OMAP5:
+		keypad_data->reg_offset = 0x10;
+		keypad_data->irqreg_offset = 0x0c;
+		break;
+	default:
+		dev_err(&pdev->dev,
+			"Keypad reports unsupported revision %d", rev);
+		error = -EINVAL;
+		goto err_pm_put_sync;
+	}
+
+	/* input device allocation */
+	keypad_data->input = input_dev = input_allocate_device();
+	if (!input_dev) {
+		error = -ENOMEM;
+		goto err_pm_put_sync;
+	}
+
+	input_dev->name = pdev->name;
+	input_dev->dev.parent = &pdev->dev;
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0001;
+
+	input_dev->open = omap4_keypad_open;
+	input_dev->close = omap4_keypad_close;
+
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+	if (!keypad_data->no_autorepeat)
+		__set_bit(EV_REP, input_dev->evbit);
+
+	input_set_drvdata(input_dev, keypad_data);
+
+	keypad_data->row_shift = get_count_order(keypad_data->cols);
+	max_keys = keypad_data->rows << keypad_data->row_shift;
+	keypad_data->keymap = kzalloc(max_keys * sizeof(keypad_data->keymap[0]),
+				      GFP_KERNEL);
+	if (!keypad_data->keymap) {
+		dev_err(&pdev->dev, "Not enough memory for keymap\n");
+		error = -ENOMEM;
+		goto err_free_input;
+	}
+
+	error = matrix_keypad_build_keymap(NULL, NULL,
+					   keypad_data->rows, keypad_data->cols,
+					   keypad_data->keymap, input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to build keymap\n");
+		goto err_free_keymap;
+	}
+
+	error = request_threaded_irq(keypad_data->irq, omap4_keypad_irq_handler,
+				     omap4_keypad_irq_thread_fn, 0,
+				     "omap4-keypad", keypad_data);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register interrupt\n");
+		goto err_free_input;
+	}
+
+	device_init_wakeup(&pdev->dev, true);
+	pm_runtime_put_sync(&pdev->dev);
+
+	error = input_register_device(keypad_data->input);
+	if (error < 0) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		goto err_pm_disable;
+	}
+
+	platform_set_drvdata(pdev, keypad_data);
+	return 0;
+
+err_pm_disable:
+	pm_runtime_disable(&pdev->dev);
+	device_init_wakeup(&pdev->dev, false);
+	free_irq(keypad_data->irq, keypad_data);
+err_free_keymap:
+	kfree(keypad_data->keymap);
+err_free_input:
+	input_free_device(input_dev);
+err_pm_put_sync:
+	pm_runtime_put_sync(&pdev->dev);
+err_unmap:
+	iounmap(keypad_data->base);
+err_release_mem:
+	release_mem_region(res->start, resource_size(res));
+err_free_keypad:
+	kfree(keypad_data);
+	return error;
+}
+
+static int omap4_keypad_remove(struct platform_device *pdev)
+{
+	struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
+	struct resource *res;
+
+	free_irq(keypad_data->irq, keypad_data);
+
+	pm_runtime_disable(&pdev->dev);
+
+	device_init_wakeup(&pdev->dev, false);
+
+	input_unregister_device(keypad_data->input);
+
+	iounmap(keypad_data->base);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(keypad_data->keymap);
+	kfree(keypad_data);
+
+	return 0;
+}
+
+static const struct of_device_id omap_keypad_dt_match[] = {
+	{ .compatible = "ti,omap4-keypad" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, omap_keypad_dt_match);
+
+#ifdef CONFIG_PM_SLEEP
+static int omap4_keypad_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
+	int error;
+
+	if (device_may_wakeup(&pdev->dev)) {
+		error = enable_irq_wake(keypad_data->irq);
+		if (!error)
+			keypad_data->irq_wake_enabled = true;
+	}
+
+	return 0;
+}
+
+static int omap4_keypad_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(&pdev->dev) && keypad_data->irq_wake_enabled) {
+		disable_irq_wake(keypad_data->irq);
+		keypad_data->irq_wake_enabled = false;
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(omap4_keypad_pm_ops,
+			 omap4_keypad_suspend, omap4_keypad_resume);
+
+static struct platform_driver omap4_keypad_driver = {
+	.probe		= omap4_keypad_probe,
+	.remove		= omap4_keypad_remove,
+	.driver		= {
+		.name	= "omap4-keypad",
+		.pm	= &omap4_keypad_pm_ops,
+		.of_match_table = omap_keypad_dt_match,
+	},
+};
+module_platform_driver(omap4_keypad_driver);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("OMAP4 Keypad Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap4-keypad");
diff --git a/drivers/input/keyboard/opencores-kbd.c b/drivers/input/keyboard/opencores-kbd.c
new file mode 100644
index 0000000..f8502bb
--- /dev/null
+++ b/drivers/input/keyboard/opencores-kbd.c
@@ -0,0 +1,130 @@
+/*
+ * OpenCores Keyboard Controller Driver
+ * http://www.opencores.org/project,keyboardcontroller
+ *
+ * Copyright 2007-2009 HV Sistemas S.L.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct opencores_kbd {
+	struct input_dev *input;
+	void __iomem *addr;
+	int irq;
+	unsigned short keycodes[128];
+};
+
+static irqreturn_t opencores_kbd_isr(int irq, void *dev_id)
+{
+	struct opencores_kbd *opencores_kbd = dev_id;
+	struct input_dev *input = opencores_kbd->input;
+	unsigned char c;
+
+	c = readb(opencores_kbd->addr);
+	input_report_key(input, c & 0x7f, c & 0x80 ? 0 : 1);
+	input_sync(input);
+
+	return IRQ_HANDLED;
+}
+
+static int opencores_kbd_probe(struct platform_device *pdev)
+{
+	struct input_dev *input;
+	struct opencores_kbd *opencores_kbd;
+	struct resource *res;
+	int irq, i, error;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "missing board memory resource\n");
+		return -EINVAL;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "missing board IRQ resource\n");
+		return -EINVAL;
+	}
+
+	opencores_kbd = devm_kzalloc(&pdev->dev, sizeof(*opencores_kbd),
+				     GFP_KERNEL);
+	if (!opencores_kbd)
+		return -ENOMEM;
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input) {
+		dev_err(&pdev->dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	opencores_kbd->input = input;
+
+	opencores_kbd->addr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(opencores_kbd->addr))
+		return PTR_ERR(opencores_kbd->addr);
+
+	input->name = pdev->name;
+	input->phys = "opencores-kbd/input0";
+
+	input_set_drvdata(input, opencores_kbd);
+
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0100;
+
+	input->keycode = opencores_kbd->keycodes;
+	input->keycodesize = sizeof(opencores_kbd->keycodes[0]);
+	input->keycodemax = ARRAY_SIZE(opencores_kbd->keycodes);
+
+	__set_bit(EV_KEY, input->evbit);
+
+	for (i = 0; i < ARRAY_SIZE(opencores_kbd->keycodes); i++) {
+		/*
+		 * OpenCores controller happens to have scancodes match
+		 * our KEY_* definitions.
+		 */
+		opencores_kbd->keycodes[i] = i;
+		__set_bit(opencores_kbd->keycodes[i], input->keybit);
+	}
+	__clear_bit(KEY_RESERVED, input->keybit);
+
+	error = devm_request_irq(&pdev->dev, irq, &opencores_kbd_isr,
+				 IRQF_TRIGGER_RISING,
+				 pdev->name, opencores_kbd);
+	if (error) {
+		dev_err(&pdev->dev, "unable to claim irq %d\n", irq);
+		return error;
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(&pdev->dev, "unable to register input device\n");
+		return error;
+	}
+
+	platform_set_drvdata(pdev, opencores_kbd);
+
+	return 0;
+}
+
+static struct platform_driver opencores_kbd_device_driver = {
+	.probe    = opencores_kbd_probe,
+	.driver   = {
+		.name = "opencores-kbd",
+	},
+};
+module_platform_driver(opencores_kbd_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Javier Herrero <jherrero@hvsistemas.es>");
+MODULE_DESCRIPTION("Keyboard driver for OpenCores Keyboard Controller");
diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c
new file mode 100644
index 0000000..5c68e3f
--- /dev/null
+++ b/drivers/input/keyboard/pmic8xxx-keypad.c
@@ -0,0 +1,702 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/input/matrix_keypad.h>
+
+#define PM8XXX_MAX_ROWS		18
+#define PM8XXX_MAX_COLS		8
+#define PM8XXX_ROW_SHIFT	3
+#define PM8XXX_MATRIX_MAX_SIZE	(PM8XXX_MAX_ROWS * PM8XXX_MAX_COLS)
+
+#define PM8XXX_MIN_ROWS		5
+#define PM8XXX_MIN_COLS		5
+
+#define MAX_SCAN_DELAY		128
+#define MIN_SCAN_DELAY		1
+
+/* in nanoseconds */
+#define MAX_ROW_HOLD_DELAY	122000
+#define MIN_ROW_HOLD_DELAY	30500
+
+#define MAX_DEBOUNCE_TIME	20
+#define MIN_DEBOUNCE_TIME	5
+
+#define KEYP_CTRL			0x148
+
+#define KEYP_CTRL_EVNTS			BIT(0)
+#define KEYP_CTRL_EVNTS_MASK		0x3
+
+#define KEYP_CTRL_SCAN_COLS_SHIFT	5
+#define KEYP_CTRL_SCAN_COLS_MIN		5
+#define KEYP_CTRL_SCAN_COLS_BITS	0x3
+
+#define KEYP_CTRL_SCAN_ROWS_SHIFT	2
+#define KEYP_CTRL_SCAN_ROWS_MIN		5
+#define KEYP_CTRL_SCAN_ROWS_BITS	0x7
+
+#define KEYP_CTRL_KEYP_EN		BIT(7)
+
+#define KEYP_SCAN			0x149
+
+#define KEYP_SCAN_READ_STATE		BIT(0)
+#define KEYP_SCAN_DBOUNCE_SHIFT		1
+#define KEYP_SCAN_PAUSE_SHIFT		3
+#define KEYP_SCAN_ROW_HOLD_SHIFT	6
+
+#define KEYP_TEST			0x14A
+
+#define KEYP_TEST_CLEAR_RECENT_SCAN	BIT(6)
+#define KEYP_TEST_CLEAR_OLD_SCAN	BIT(5)
+#define KEYP_TEST_READ_RESET		BIT(4)
+#define KEYP_TEST_DTEST_EN		BIT(3)
+#define KEYP_TEST_ABORT_READ		BIT(0)
+
+#define KEYP_TEST_DBG_SELECT_SHIFT	1
+
+/* bits of these registers represent
+ * '0' for key press
+ * '1' for key release
+ */
+#define KEYP_RECENT_DATA		0x14B
+#define KEYP_OLD_DATA			0x14C
+
+#define KEYP_CLOCK_FREQ			32768
+
+/**
+ * struct pmic8xxx_kp - internal keypad data structure
+ * @num_cols - number of columns of keypad
+ * @num_rows - number of row of keypad
+ * @input - input device pointer for keypad
+ * @regmap - regmap handle
+ * @key_sense_irq - key press/release irq number
+ * @key_stuck_irq - key stuck notification irq number
+ * @keycodes - array to hold the key codes
+ * @dev - parent device pointer
+ * @keystate - present key press/release state
+ * @stuckstate - present state when key stuck irq
+ * @ctrl_reg - control register value
+ */
+struct pmic8xxx_kp {
+	unsigned int num_rows;
+	unsigned int num_cols;
+	struct input_dev *input;
+	struct regmap *regmap;
+	int key_sense_irq;
+	int key_stuck_irq;
+
+	unsigned short keycodes[PM8XXX_MATRIX_MAX_SIZE];
+
+	struct device *dev;
+	u16 keystate[PM8XXX_MAX_ROWS];
+	u16 stuckstate[PM8XXX_MAX_ROWS];
+
+	u8 ctrl_reg;
+};
+
+static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col)
+{
+	/* all keys pressed on that particular row? */
+	if (col == 0x00)
+		return 1 << kp->num_cols;
+	else
+		return col & ((1 << kp->num_cols) - 1);
+}
+
+/*
+ * Synchronous read protocol for RevB0 onwards:
+ *
+ * 1. Write '1' to ReadState bit in KEYP_SCAN register
+ * 2. Wait 2*32KHz clocks, so that HW can successfully enter read mode
+ *    synchronously
+ * 3. Read rows in old array first if events are more than one
+ * 4. Read rows in recent array
+ * 5. Wait 4*32KHz clocks
+ * 6. Write '0' to ReadState bit of KEYP_SCAN register so that hw can
+ *    synchronously exit read mode.
+ */
+static int pmic8xxx_chk_sync_read(struct pmic8xxx_kp *kp)
+{
+	int rc;
+	unsigned int scan_val;
+
+	rc = regmap_read(kp->regmap, KEYP_SCAN, &scan_val);
+	if (rc < 0) {
+		dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc);
+		return rc;
+	}
+
+	scan_val |= 0x1;
+
+	rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val);
+	if (rc < 0) {
+		dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
+		return rc;
+	}
+
+	/* 2 * 32KHz clocks */
+	udelay((2 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1);
+
+	return rc;
+}
+
+static int pmic8xxx_kp_read_data(struct pmic8xxx_kp *kp, u16 *state,
+					u16 data_reg, int read_rows)
+{
+	int rc, row;
+	unsigned int val;
+
+	for (row = 0; row < read_rows; row++) {
+		rc = regmap_read(kp->regmap, data_reg, &val);
+		if (rc)
+			return rc;
+		dev_dbg(kp->dev, "%d = %d\n", row, val);
+		state[row] = pmic8xxx_col_state(kp, val);
+	}
+
+	return 0;
+}
+
+static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
+					 u16 *old_state)
+{
+	int rc, read_rows;
+	unsigned int scan_val;
+
+	if (kp->num_rows < PM8XXX_MIN_ROWS)
+		read_rows = PM8XXX_MIN_ROWS;
+	else
+		read_rows = kp->num_rows;
+
+	pmic8xxx_chk_sync_read(kp);
+
+	if (old_state) {
+		rc = pmic8xxx_kp_read_data(kp, old_state, KEYP_OLD_DATA,
+						read_rows);
+		if (rc < 0) {
+			dev_err(kp->dev,
+				"Error reading KEYP_OLD_DATA, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	rc = pmic8xxx_kp_read_data(kp, new_state, KEYP_RECENT_DATA,
+					 read_rows);
+	if (rc < 0) {
+		dev_err(kp->dev,
+			"Error reading KEYP_RECENT_DATA, rc=%d\n", rc);
+		return rc;
+	}
+
+	/* 4 * 32KHz clocks */
+	udelay((4 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1);
+
+	rc = regmap_read(kp->regmap, KEYP_SCAN, &scan_val);
+	if (rc < 0) {
+		dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc);
+		return rc;
+	}
+
+	scan_val &= 0xFE;
+	rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val);
+	if (rc < 0)
+		dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
+
+	return rc;
+}
+
+static void __pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
+					 u16 *old_state)
+{
+	int row, col, code;
+
+	for (row = 0; row < kp->num_rows; row++) {
+		int bits_changed = new_state[row] ^ old_state[row];
+
+		if (!bits_changed)
+			continue;
+
+		for (col = 0; col < kp->num_cols; col++) {
+			if (!(bits_changed & (1 << col)))
+				continue;
+
+			dev_dbg(kp->dev, "key [%d:%d] %s\n", row, col,
+					!(new_state[row] & (1 << col)) ?
+					"pressed" : "released");
+
+			code = MATRIX_SCAN_CODE(row, col, PM8XXX_ROW_SHIFT);
+
+			input_event(kp->input, EV_MSC, MSC_SCAN, code);
+			input_report_key(kp->input,
+					kp->keycodes[code],
+					!(new_state[row] & (1 << col)));
+
+			input_sync(kp->input);
+		}
+	}
+}
+
+static bool pmic8xxx_detect_ghost_keys(struct pmic8xxx_kp *kp, u16 *new_state)
+{
+	int row, found_first = -1;
+	u16 check, row_state;
+
+	check = 0;
+	for (row = 0; row < kp->num_rows; row++) {
+		row_state = (~new_state[row]) &
+				 ((1 << kp->num_cols) - 1);
+
+		if (hweight16(row_state) > 1) {
+			if (found_first == -1)
+				found_first = row;
+			if (check & row_state) {
+				dev_dbg(kp->dev, "detected ghost key on row[%d]"
+					 " and row[%d]\n", found_first, row);
+				return true;
+			}
+		}
+		check |= row_state;
+	}
+	return false;
+}
+
+static int pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, unsigned int events)
+{
+	u16 new_state[PM8XXX_MAX_ROWS];
+	u16 old_state[PM8XXX_MAX_ROWS];
+	int rc;
+
+	switch (events) {
+	case 0x1:
+		rc = pmic8xxx_kp_read_matrix(kp, new_state, NULL);
+		if (rc < 0)
+			return rc;
+
+		/* detecting ghost key is not an error */
+		if (pmic8xxx_detect_ghost_keys(kp, new_state))
+			return 0;
+		__pmic8xxx_kp_scan_matrix(kp, new_state, kp->keystate);
+		memcpy(kp->keystate, new_state, sizeof(new_state));
+	break;
+	case 0x3: /* two events - eventcounter is gray-coded */
+		rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state);
+		if (rc < 0)
+			return rc;
+
+		__pmic8xxx_kp_scan_matrix(kp, old_state, kp->keystate);
+		__pmic8xxx_kp_scan_matrix(kp, new_state, old_state);
+		memcpy(kp->keystate, new_state, sizeof(new_state));
+	break;
+	case 0x2:
+		dev_dbg(kp->dev, "Some key events were lost\n");
+		rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state);
+		if (rc < 0)
+			return rc;
+		__pmic8xxx_kp_scan_matrix(kp, old_state, kp->keystate);
+		__pmic8xxx_kp_scan_matrix(kp, new_state, old_state);
+		memcpy(kp->keystate, new_state, sizeof(new_state));
+	break;
+	default:
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+/*
+ * NOTE: We are reading recent and old data registers blindly
+ * whenever key-stuck interrupt happens, because events counter doesn't
+ * get updated when this interrupt happens due to key stuck doesn't get
+ * considered as key state change.
+ *
+ * We are not using old data register contents after they are being read
+ * because it might report the key which was pressed before the key being stuck
+ * as stuck key because it's pressed status is stored in the old data
+ * register.
+ */
+static irqreturn_t pmic8xxx_kp_stuck_irq(int irq, void *data)
+{
+	u16 new_state[PM8XXX_MAX_ROWS];
+	u16 old_state[PM8XXX_MAX_ROWS];
+	int rc;
+	struct pmic8xxx_kp *kp = data;
+
+	rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state);
+	if (rc < 0) {
+		dev_err(kp->dev, "failed to read keypad matrix\n");
+		return IRQ_HANDLED;
+	}
+
+	__pmic8xxx_kp_scan_matrix(kp, new_state, kp->stuckstate);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pmic8xxx_kp_irq(int irq, void *data)
+{
+	struct pmic8xxx_kp *kp = data;
+	unsigned int ctrl_val, events;
+	int rc;
+
+	rc = regmap_read(kp->regmap, KEYP_CTRL, &ctrl_val);
+	if (rc < 0) {
+		dev_err(kp->dev, "failed to read keyp_ctrl register\n");
+		return IRQ_HANDLED;
+	}
+
+	events = ctrl_val & KEYP_CTRL_EVNTS_MASK;
+
+	rc = pmic8xxx_kp_scan_matrix(kp, events);
+	if (rc < 0)
+		dev_err(kp->dev, "failed to scan matrix\n");
+
+	return IRQ_HANDLED;
+}
+
+static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp,
+			     struct platform_device *pdev)
+{
+	const struct device_node *of_node = pdev->dev.of_node;
+	unsigned int scan_delay_ms;
+	unsigned int row_hold_ns;
+	unsigned int debounce_ms;
+	int bits, rc, cycles;
+	u8 scan_val = 0, ctrl_val = 0;
+	static const u8 row_bits[] = {
+		0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7,
+	};
+
+	/* Find column bits */
+	if (kp->num_cols < KEYP_CTRL_SCAN_COLS_MIN)
+		bits = 0;
+	else
+		bits = kp->num_cols - KEYP_CTRL_SCAN_COLS_MIN;
+	ctrl_val = (bits & KEYP_CTRL_SCAN_COLS_BITS) <<
+		KEYP_CTRL_SCAN_COLS_SHIFT;
+
+	/* Find row bits */
+	if (kp->num_rows < KEYP_CTRL_SCAN_ROWS_MIN)
+		bits = 0;
+	else
+		bits = row_bits[kp->num_rows - KEYP_CTRL_SCAN_ROWS_MIN];
+
+	ctrl_val |= (bits << KEYP_CTRL_SCAN_ROWS_SHIFT);
+
+	rc = regmap_write(kp->regmap, KEYP_CTRL, ctrl_val);
+	if (rc < 0) {
+		dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (of_property_read_u32(of_node, "scan-delay", &scan_delay_ms))
+		scan_delay_ms = MIN_SCAN_DELAY;
+
+	if (scan_delay_ms > MAX_SCAN_DELAY || scan_delay_ms < MIN_SCAN_DELAY ||
+	    !is_power_of_2(scan_delay_ms)) {
+		dev_err(&pdev->dev, "invalid keypad scan time supplied\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(of_node, "row-hold", &row_hold_ns))
+		row_hold_ns = MIN_ROW_HOLD_DELAY;
+
+	if (row_hold_ns > MAX_ROW_HOLD_DELAY ||
+	    row_hold_ns < MIN_ROW_HOLD_DELAY ||
+	    ((row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) {
+		dev_err(&pdev->dev, "invalid keypad row hold time supplied\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(of_node, "debounce", &debounce_ms))
+		debounce_ms = MIN_DEBOUNCE_TIME;
+
+	if (((debounce_ms % 5) != 0) ||
+	    debounce_ms > MAX_DEBOUNCE_TIME ||
+	    debounce_ms < MIN_DEBOUNCE_TIME) {
+		dev_err(&pdev->dev, "invalid debounce time supplied\n");
+		return -EINVAL;
+	}
+
+	bits = (debounce_ms / 5) - 1;
+
+	scan_val |= (bits << KEYP_SCAN_DBOUNCE_SHIFT);
+
+	bits = fls(scan_delay_ms) - 1;
+	scan_val |= (bits << KEYP_SCAN_PAUSE_SHIFT);
+
+	/* Row hold time is a multiple of 32KHz cycles. */
+	cycles = (row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC;
+
+	scan_val |= (cycles << KEYP_SCAN_ROW_HOLD_SHIFT);
+
+	rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val);
+	if (rc)
+		dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
+
+	return rc;
+
+}
+
+static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp)
+{
+	int rc;
+
+	kp->ctrl_reg |= KEYP_CTRL_KEYP_EN;
+
+	rc = regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg);
+	if (rc < 0)
+		dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int pmic8xxx_kp_disable(struct pmic8xxx_kp *kp)
+{
+	int rc;
+
+	kp->ctrl_reg &= ~KEYP_CTRL_KEYP_EN;
+
+	rc = regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg);
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+
+static int pmic8xxx_kp_open(struct input_dev *dev)
+{
+	struct pmic8xxx_kp *kp = input_get_drvdata(dev);
+
+	return pmic8xxx_kp_enable(kp);
+}
+
+static void pmic8xxx_kp_close(struct input_dev *dev)
+{
+	struct pmic8xxx_kp *kp = input_get_drvdata(dev);
+
+	pmic8xxx_kp_disable(kp);
+}
+
+/*
+ * keypad controller should be initialized in the following sequence
+ * only, otherwise it might get into FSM stuck state.
+ *
+ * - Initialize keypad control parameters, like no. of rows, columns,
+ *   timing values etc.,
+ * - configure rows and column gpios pull up/down.
+ * - set irq edge type.
+ * - enable the keypad controller.
+ */
+static int pmic8xxx_kp_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	unsigned int rows, cols;
+	bool repeat;
+	bool wakeup;
+	struct pmic8xxx_kp *kp;
+	int rc;
+	unsigned int ctrl_val;
+
+	rc = matrix_keypad_parse_of_params(&pdev->dev, &rows, &cols);
+	if (rc)
+		return rc;
+
+	if (cols > PM8XXX_MAX_COLS || rows > PM8XXX_MAX_ROWS ||
+	    cols < PM8XXX_MIN_COLS) {
+		dev_err(&pdev->dev, "invalid platform data\n");
+		return -EINVAL;
+	}
+
+	repeat = !of_property_read_bool(np, "linux,input-no-autorepeat");
+
+	wakeup = of_property_read_bool(np, "wakeup-source") ||
+		 /* legacy name */
+		 of_property_read_bool(np, "linux,keypad-wakeup");
+
+	kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
+	if (!kp)
+		return -ENOMEM;
+
+	kp->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!kp->regmap)
+		return -ENODEV;
+
+	platform_set_drvdata(pdev, kp);
+
+	kp->num_rows	= rows;
+	kp->num_cols	= cols;
+	kp->dev		= &pdev->dev;
+
+	kp->input = devm_input_allocate_device(&pdev->dev);
+	if (!kp->input) {
+		dev_err(&pdev->dev, "unable to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	kp->key_sense_irq = platform_get_irq(pdev, 0);
+	if (kp->key_sense_irq < 0) {
+		dev_err(&pdev->dev, "unable to get keypad sense irq\n");
+		return kp->key_sense_irq;
+	}
+
+	kp->key_stuck_irq = platform_get_irq(pdev, 1);
+	if (kp->key_stuck_irq < 0) {
+		dev_err(&pdev->dev, "unable to get keypad stuck irq\n");
+		return kp->key_stuck_irq;
+	}
+
+	kp->input->name = "PMIC8XXX keypad";
+	kp->input->phys = "pmic8xxx_keypad/input0";
+
+	kp->input->id.bustype	= BUS_I2C;
+	kp->input->id.version	= 0x0001;
+	kp->input->id.product	= 0x0001;
+	kp->input->id.vendor	= 0x0001;
+
+	kp->input->open		= pmic8xxx_kp_open;
+	kp->input->close	= pmic8xxx_kp_close;
+
+	rc = matrix_keypad_build_keymap(NULL, NULL,
+					PM8XXX_MAX_ROWS, PM8XXX_MAX_COLS,
+					kp->keycodes, kp->input);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to build keymap\n");
+		return rc;
+	}
+
+	if (repeat)
+		__set_bit(EV_REP, kp->input->evbit);
+	input_set_capability(kp->input, EV_MSC, MSC_SCAN);
+
+	input_set_drvdata(kp->input, kp);
+
+	/* initialize keypad state */
+	memset(kp->keystate, 0xff, sizeof(kp->keystate));
+	memset(kp->stuckstate, 0xff, sizeof(kp->stuckstate));
+
+	rc = pmic8xxx_kpd_init(kp, pdev);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "unable to initialize keypad controller\n");
+		return rc;
+	}
+
+	rc = devm_request_any_context_irq(&pdev->dev, kp->key_sense_irq,
+			pmic8xxx_kp_irq, IRQF_TRIGGER_RISING, "pmic-keypad",
+			kp);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "failed to request keypad sense irq\n");
+		return rc;
+	}
+
+	rc = devm_request_any_context_irq(&pdev->dev, kp->key_stuck_irq,
+			pmic8xxx_kp_stuck_irq, IRQF_TRIGGER_RISING,
+			"pmic-keypad-stuck", kp);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "failed to request keypad stuck irq\n");
+		return rc;
+	}
+
+	rc = regmap_read(kp->regmap, KEYP_CTRL, &ctrl_val);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n");
+		return rc;
+	}
+
+	kp->ctrl_reg = ctrl_val;
+
+	rc = input_register_device(kp->input);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "unable to register keypad input device\n");
+		return rc;
+	}
+
+	device_init_wakeup(&pdev->dev, wakeup);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pmic8xxx_kp_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
+	struct input_dev *input_dev = kp->input;
+
+	if (device_may_wakeup(dev)) {
+		enable_irq_wake(kp->key_sense_irq);
+	} else {
+		mutex_lock(&input_dev->mutex);
+
+		if (input_dev->users)
+			pmic8xxx_kp_disable(kp);
+
+		mutex_unlock(&input_dev->mutex);
+	}
+
+	return 0;
+}
+
+static int pmic8xxx_kp_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
+	struct input_dev *input_dev = kp->input;
+
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(kp->key_sense_irq);
+	} else {
+		mutex_lock(&input_dev->mutex);
+
+		if (input_dev->users)
+			pmic8xxx_kp_enable(kp);
+
+		mutex_unlock(&input_dev->mutex);
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm8xxx_kp_pm_ops,
+			 pmic8xxx_kp_suspend, pmic8xxx_kp_resume);
+
+static const struct of_device_id pm8xxx_match_table[] = {
+	{ .compatible = "qcom,pm8058-keypad" },
+	{ .compatible = "qcom,pm8921-keypad" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_match_table);
+
+static struct platform_driver pmic8xxx_kp_driver = {
+	.probe		= pmic8xxx_kp_probe,
+	.driver		= {
+		.name = "pm8xxx-keypad",
+		.pm = &pm8xxx_kp_pm_ops,
+		.of_match_table = pm8xxx_match_table,
+	},
+};
+module_platform_driver(pmic8xxx_kp_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8XXX keypad driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8xxx_keypad");
+MODULE_AUTHOR("Trilok Soni <tsoni@codeaurora.org>");
diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c
new file mode 100644
index 0000000..fcef5d1
--- /dev/null
+++ b/drivers/input/keyboard/pxa27x_keypad.c
@@ -0,0 +1,841 @@
+/*
+ * linux/drivers/input/keyboard/pxa27x_keypad.c
+ *
+ * Driver for the pxa27x matrix keyboard controller.
+ *
+ * Created:	Feb 22, 2007
+ * Author:	Rodolfo Giometti <giometti@linux.it>
+ *
+ * Based on a previous implementations by Kevin O'Connor
+ * <kevin_at_koconnor.net> and Alex Osborne <bobofdoom@gmail.com> and
+ * on some suggestions by Nicolas Pitre <nico@fluxnic.net>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+#include <linux/platform_data/keypad-pxa27x.h>
+/*
+ * Keypad Controller registers
+ */
+#define KPC             0x0000 /* Keypad Control register */
+#define KPDK            0x0008 /* Keypad Direct Key register */
+#define KPREC           0x0010 /* Keypad Rotary Encoder register */
+#define KPMK            0x0018 /* Keypad Matrix Key register */
+#define KPAS            0x0020 /* Keypad Automatic Scan register */
+
+/* Keypad Automatic Scan Multiple Key Presser register 0-3 */
+#define KPASMKP0        0x0028
+#define KPASMKP1        0x0030
+#define KPASMKP2        0x0038
+#define KPASMKP3        0x0040
+#define KPKDI           0x0048
+
+/* bit definitions */
+#define KPC_MKRN(n)	((((n) - 1) & 0x7) << 26) /* matrix key row number */
+#define KPC_MKCN(n)	((((n) - 1) & 0x7) << 23) /* matrix key column number */
+#define KPC_DKN(n)	((((n) - 1) & 0x7) << 6)  /* direct key number */
+
+#define KPC_AS          (0x1 << 30)  /* Automatic Scan bit */
+#define KPC_ASACT       (0x1 << 29)  /* Automatic Scan on Activity */
+#define KPC_MI          (0x1 << 22)  /* Matrix interrupt bit */
+#define KPC_IMKP        (0x1 << 21)  /* Ignore Multiple Key Press */
+
+#define KPC_MS(n)	(0x1 << (13 + (n)))	/* Matrix scan line 'n' */
+#define KPC_MS_ALL      (0xff << 13)
+
+#define KPC_ME          (0x1 << 12)  /* Matrix Keypad Enable */
+#define KPC_MIE         (0x1 << 11)  /* Matrix Interrupt Enable */
+#define KPC_DK_DEB_SEL	(0x1 <<  9)  /* Direct Keypad Debounce Select */
+#define KPC_DI          (0x1 <<  5)  /* Direct key interrupt bit */
+#define KPC_RE_ZERO_DEB (0x1 <<  4)  /* Rotary Encoder Zero Debounce */
+#define KPC_REE1        (0x1 <<  3)  /* Rotary Encoder1 Enable */
+#define KPC_REE0        (0x1 <<  2)  /* Rotary Encoder0 Enable */
+#define KPC_DE          (0x1 <<  1)  /* Direct Keypad Enable */
+#define KPC_DIE         (0x1 <<  0)  /* Direct Keypad interrupt Enable */
+
+#define KPDK_DKP        (0x1 << 31)
+#define KPDK_DK(n)	((n) & 0xff)
+
+#define KPREC_OF1       (0x1 << 31)
+#define kPREC_UF1       (0x1 << 30)
+#define KPREC_OF0       (0x1 << 15)
+#define KPREC_UF0       (0x1 << 14)
+
+#define KPREC_RECOUNT0(n)	((n) & 0xff)
+#define KPREC_RECOUNT1(n)	(((n) >> 16) & 0xff)
+
+#define KPMK_MKP        (0x1 << 31)
+#define KPAS_SO         (0x1 << 31)
+#define KPASMKPx_SO     (0x1 << 31)
+
+#define KPAS_MUKP(n)	(((n) >> 26) & 0x1f)
+#define KPAS_RP(n)	(((n) >> 4) & 0xf)
+#define KPAS_CP(n)	((n) & 0xf)
+
+#define KPASMKP_MKC_MASK	(0xff)
+
+#define keypad_readl(off)	__raw_readl(keypad->mmio_base + (off))
+#define keypad_writel(off, v)	__raw_writel((v), keypad->mmio_base + (off))
+
+#define MAX_MATRIX_KEY_NUM	(MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS)
+#define MAX_KEYPAD_KEYS		(MAX_MATRIX_KEY_NUM + MAX_DIRECT_KEY_NUM)
+
+struct pxa27x_keypad {
+	const struct pxa27x_keypad_platform_data *pdata;
+
+	struct clk *clk;
+	struct input_dev *input_dev;
+	void __iomem *mmio_base;
+
+	int irq;
+
+	unsigned short keycodes[MAX_KEYPAD_KEYS];
+	int rotary_rel_code[2];
+
+	unsigned int row_shift;
+
+	/* state row bits of each column scan */
+	uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS];
+	uint32_t direct_key_state;
+
+	unsigned int direct_key_mask;
+};
+
+#ifdef CONFIG_OF
+static int pxa27x_keypad_matrix_key_parse_dt(struct pxa27x_keypad *keypad,
+				struct pxa27x_keypad_platform_data *pdata)
+{
+	struct input_dev *input_dev = keypad->input_dev;
+	struct device *dev = input_dev->dev.parent;
+	u32 rows, cols;
+	int error;
+
+	error = matrix_keypad_parse_of_params(dev, &rows, &cols);
+	if (error)
+		return error;
+
+	if (rows > MAX_MATRIX_KEY_ROWS || cols > MAX_MATRIX_KEY_COLS) {
+		dev_err(dev, "rows or cols exceeds maximum value\n");
+		return -EINVAL;
+	}
+
+	pdata->matrix_key_rows = rows;
+	pdata->matrix_key_cols = cols;
+
+	error = matrix_keypad_build_keymap(NULL, NULL,
+					   pdata->matrix_key_rows,
+					   pdata->matrix_key_cols,
+					   keypad->keycodes, input_dev);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static int pxa27x_keypad_direct_key_parse_dt(struct pxa27x_keypad *keypad,
+				struct pxa27x_keypad_platform_data *pdata)
+{
+	struct input_dev *input_dev = keypad->input_dev;
+	struct device *dev = input_dev->dev.parent;
+	struct device_node *np = dev->of_node;
+	const __be16 *prop;
+	unsigned short code;
+	unsigned int proplen, size;
+	int i;
+	int error;
+
+	error = of_property_read_u32(np, "marvell,direct-key-count",
+				     &pdata->direct_key_num);
+	if (error) {
+		/*
+		 * If do not have marvel,direct-key-count defined,
+		 * it means direct key is not supported.
+		 */
+		return error == -EINVAL ? 0 : error;
+	}
+
+	error = of_property_read_u32(np, "marvell,direct-key-mask",
+				     &pdata->direct_key_mask);
+	if (error) {
+		if (error != -EINVAL)
+			return error;
+
+		/*
+		 * If marvell,direct-key-mask is not defined, driver will use
+		 * default value. Default value is set when configure the keypad.
+		 */
+		pdata->direct_key_mask = 0;
+	}
+
+	pdata->direct_key_low_active = of_property_read_bool(np,
+					"marvell,direct-key-low-active");
+
+	prop = of_get_property(np, "marvell,direct-key-map", &proplen);
+	if (!prop)
+		return -EINVAL;
+
+	if (proplen % sizeof(u16))
+		return -EINVAL;
+
+	size = proplen / sizeof(u16);
+
+	/* Only MAX_DIRECT_KEY_NUM is accepted.*/
+	if (size > MAX_DIRECT_KEY_NUM)
+		return -EINVAL;
+
+	for (i = 0; i < size; i++) {
+		code = be16_to_cpup(prop + i);
+		keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = code;
+		__set_bit(code, input_dev->keybit);
+	}
+
+	return 0;
+}
+
+static int pxa27x_keypad_rotary_parse_dt(struct pxa27x_keypad *keypad,
+				struct pxa27x_keypad_platform_data *pdata)
+{
+	const __be32 *prop;
+	int i, relkey_ret;
+	unsigned int code, proplen;
+	const char *rotaryname[2] = {
+			"marvell,rotary0", "marvell,rotary1"};
+	const char relkeyname[] = {"marvell,rotary-rel-key"};
+	struct input_dev *input_dev = keypad->input_dev;
+	struct device *dev = input_dev->dev.parent;
+	struct device_node *np = dev->of_node;
+
+	relkey_ret = of_property_read_u32(np, relkeyname, &code);
+	/* if can read correct rotary key-code, we do not need this. */
+	if (relkey_ret == 0) {
+		unsigned short relcode;
+
+		/* rotary0 taks lower half, rotary1 taks upper half. */
+		relcode = code & 0xffff;
+		pdata->rotary0_rel_code = (code & 0xffff);
+		__set_bit(relcode, input_dev->relbit);
+
+		relcode = code >> 16;
+		pdata->rotary1_rel_code = relcode;
+		__set_bit(relcode, input_dev->relbit);
+	}
+
+	for (i = 0; i < 2; i++) {
+		prop = of_get_property(np, rotaryname[i], &proplen);
+		/*
+		 * If the prop is not set, it means keypad does not need
+		 * initialize the rotaryX.
+		 */
+		if (!prop)
+			continue;
+
+		code = be32_to_cpup(prop);
+		/*
+		 * Not all up/down key code are valid.
+		 * Now we depends on direct-rel-code.
+		 */
+		if ((!(code & 0xffff) || !(code >> 16)) && relkey_ret) {
+			return relkey_ret;
+		} else {
+			unsigned int n = MAX_MATRIX_KEY_NUM + (i << 1);
+			unsigned short keycode;
+
+			keycode = code & 0xffff;
+			keypad->keycodes[n] = keycode;
+			__set_bit(keycode, input_dev->keybit);
+
+			keycode = code >> 16;
+			keypad->keycodes[n + 1] = keycode;
+			__set_bit(keycode, input_dev->keybit);
+
+			if (i == 0)
+				pdata->rotary0_rel_code = -1;
+			else
+				pdata->rotary1_rel_code = -1;
+		}
+		if (i == 0)
+			pdata->enable_rotary0 = 1;
+		else
+			pdata->enable_rotary1 = 1;
+	}
+
+	keypad->rotary_rel_code[0] = pdata->rotary0_rel_code;
+	keypad->rotary_rel_code[1] = pdata->rotary1_rel_code;
+
+	return 0;
+}
+
+static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad)
+{
+	struct input_dev *input_dev = keypad->input_dev;
+	struct device *dev = input_dev->dev.parent;
+	struct device_node *np = dev->of_node;
+	struct pxa27x_keypad_platform_data *pdata;
+	int error;
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		dev_err(dev, "failed to allocate memory for pdata\n");
+		return -ENOMEM;
+	}
+
+	error = pxa27x_keypad_matrix_key_parse_dt(keypad, pdata);
+	if (error) {
+		dev_err(dev, "failed to parse matrix key\n");
+		return error;
+	}
+
+	error = pxa27x_keypad_direct_key_parse_dt(keypad, pdata);
+	if (error) {
+		dev_err(dev, "failed to parse direct key\n");
+		return error;
+	}
+
+	error = pxa27x_keypad_rotary_parse_dt(keypad, pdata);
+	if (error) {
+		dev_err(dev, "failed to parse rotary key\n");
+		return error;
+	}
+
+	error = of_property_read_u32(np, "marvell,debounce-interval",
+				     &pdata->debounce_interval);
+	if (error) {
+		dev_err(dev, "failed to parse debpunce-interval\n");
+		return error;
+	}
+
+	/*
+	 * The keycodes may not only includes matrix key but also the direct
+	 * key or rotary key.
+	 */
+	input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);
+
+	keypad->pdata = pdata;
+	return 0;
+}
+
+#else
+
+static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad)
+{
+	dev_info(keypad->input_dev->dev.parent, "missing platform data\n");
+
+	return -EINVAL;
+}
+
+#endif
+
+static int pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad)
+{
+	const struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+	struct input_dev *input_dev = keypad->input_dev;
+	unsigned short keycode;
+	int i;
+	int error;
+
+	error = matrix_keypad_build_keymap(pdata->matrix_keymap_data, NULL,
+					   pdata->matrix_key_rows,
+					   pdata->matrix_key_cols,
+					   keypad->keycodes, input_dev);
+	if (error)
+		return error;
+
+	/*
+	 * The keycodes may not only include matrix keys but also the direct
+	 * or rotary keys.
+	 */
+	input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);
+
+	/* For direct keys. */
+	for (i = 0; i < pdata->direct_key_num; i++) {
+		keycode = pdata->direct_key_map[i];
+		keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = keycode;
+		__set_bit(keycode, input_dev->keybit);
+	}
+
+	if (pdata->enable_rotary0) {
+		if (pdata->rotary0_up_key && pdata->rotary0_down_key) {
+			keycode = pdata->rotary0_up_key;
+			keypad->keycodes[MAX_MATRIX_KEY_NUM + 0] = keycode;
+			__set_bit(keycode, input_dev->keybit);
+
+			keycode = pdata->rotary0_down_key;
+			keypad->keycodes[MAX_MATRIX_KEY_NUM + 1] = keycode;
+			__set_bit(keycode, input_dev->keybit);
+
+			keypad->rotary_rel_code[0] = -1;
+		} else {
+			keypad->rotary_rel_code[0] = pdata->rotary0_rel_code;
+			__set_bit(pdata->rotary0_rel_code, input_dev->relbit);
+		}
+	}
+
+	if (pdata->enable_rotary1) {
+		if (pdata->rotary1_up_key && pdata->rotary1_down_key) {
+			keycode = pdata->rotary1_up_key;
+			keypad->keycodes[MAX_MATRIX_KEY_NUM + 2] = keycode;
+			__set_bit(keycode, input_dev->keybit);
+
+			keycode = pdata->rotary1_down_key;
+			keypad->keycodes[MAX_MATRIX_KEY_NUM + 3] = keycode;
+			__set_bit(keycode, input_dev->keybit);
+
+			keypad->rotary_rel_code[1] = -1;
+		} else {
+			keypad->rotary_rel_code[1] = pdata->rotary1_rel_code;
+			__set_bit(pdata->rotary1_rel_code, input_dev->relbit);
+		}
+	}
+
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+
+	return 0;
+}
+
+static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad)
+{
+	const struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+	struct input_dev *input_dev = keypad->input_dev;
+	int row, col, num_keys_pressed = 0;
+	uint32_t new_state[MAX_MATRIX_KEY_COLS];
+	uint32_t kpas = keypad_readl(KPAS);
+
+	num_keys_pressed = KPAS_MUKP(kpas);
+
+	memset(new_state, 0, sizeof(new_state));
+
+	if (num_keys_pressed == 0)
+		goto scan;
+
+	if (num_keys_pressed == 1) {
+		col = KPAS_CP(kpas);
+		row = KPAS_RP(kpas);
+
+		/* if invalid row/col, treat as no key pressed */
+		if (col >= pdata->matrix_key_cols ||
+		    row >= pdata->matrix_key_rows)
+			goto scan;
+
+		new_state[col] = (1 << row);
+		goto scan;
+	}
+
+	if (num_keys_pressed > 1) {
+		uint32_t kpasmkp0 = keypad_readl(KPASMKP0);
+		uint32_t kpasmkp1 = keypad_readl(KPASMKP1);
+		uint32_t kpasmkp2 = keypad_readl(KPASMKP2);
+		uint32_t kpasmkp3 = keypad_readl(KPASMKP3);
+
+		new_state[0] = kpasmkp0 & KPASMKP_MKC_MASK;
+		new_state[1] = (kpasmkp0 >> 16) & KPASMKP_MKC_MASK;
+		new_state[2] = kpasmkp1 & KPASMKP_MKC_MASK;
+		new_state[3] = (kpasmkp1 >> 16) & KPASMKP_MKC_MASK;
+		new_state[4] = kpasmkp2 & KPASMKP_MKC_MASK;
+		new_state[5] = (kpasmkp2 >> 16) & KPASMKP_MKC_MASK;
+		new_state[6] = kpasmkp3 & KPASMKP_MKC_MASK;
+		new_state[7] = (kpasmkp3 >> 16) & KPASMKP_MKC_MASK;
+	}
+scan:
+	for (col = 0; col < pdata->matrix_key_cols; col++) {
+		uint32_t bits_changed;
+		int code;
+
+		bits_changed = keypad->matrix_key_state[col] ^ new_state[col];
+		if (bits_changed == 0)
+			continue;
+
+		for (row = 0; row < pdata->matrix_key_rows; row++) {
+			if ((bits_changed & (1 << row)) == 0)
+				continue;
+
+			code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
+
+			input_event(input_dev, EV_MSC, MSC_SCAN, code);
+			input_report_key(input_dev, keypad->keycodes[code],
+					 new_state[col] & (1 << row));
+		}
+	}
+	input_sync(input_dev);
+	memcpy(keypad->matrix_key_state, new_state, sizeof(new_state));
+}
+
+#define DEFAULT_KPREC	(0x007f007f)
+
+static inline int rotary_delta(uint32_t kprec)
+{
+	if (kprec & KPREC_OF0)
+		return (kprec & 0xff) + 0x7f;
+	else if (kprec & KPREC_UF0)
+		return (kprec & 0xff) - 0x7f - 0xff;
+	else
+		return (kprec & 0xff) - 0x7f;
+}
+
+static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta)
+{
+	struct input_dev *dev = keypad->input_dev;
+
+	if (delta == 0)
+		return;
+
+	if (keypad->rotary_rel_code[r] == -1) {
+		int code = MAX_MATRIX_KEY_NUM + 2 * r + (delta > 0 ? 0 : 1);
+		unsigned char keycode = keypad->keycodes[code];
+
+		/* simulate a press-n-release */
+		input_event(dev, EV_MSC, MSC_SCAN, code);
+		input_report_key(dev, keycode, 1);
+		input_sync(dev);
+		input_event(dev, EV_MSC, MSC_SCAN, code);
+		input_report_key(dev, keycode, 0);
+		input_sync(dev);
+	} else {
+		input_report_rel(dev, keypad->rotary_rel_code[r], delta);
+		input_sync(dev);
+	}
+}
+
+static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad)
+{
+	const struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+	uint32_t kprec;
+
+	/* read and reset to default count value */
+	kprec = keypad_readl(KPREC);
+	keypad_writel(KPREC, DEFAULT_KPREC);
+
+	if (pdata->enable_rotary0)
+		report_rotary_event(keypad, 0, rotary_delta(kprec));
+
+	if (pdata->enable_rotary1)
+		report_rotary_event(keypad, 1, rotary_delta(kprec >> 16));
+}
+
+static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad)
+{
+	const struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+	struct input_dev *input_dev = keypad->input_dev;
+	unsigned int new_state;
+	uint32_t kpdk, bits_changed;
+	int i;
+
+	kpdk = keypad_readl(KPDK);
+
+	if (pdata->enable_rotary0 || pdata->enable_rotary1)
+		pxa27x_keypad_scan_rotary(keypad);
+
+	/*
+	 * The KPDR_DK only output the key pin level, so it relates to board,
+	 * and low level may be active.
+	 */
+	if (pdata->direct_key_low_active)
+		new_state = ~KPDK_DK(kpdk) & keypad->direct_key_mask;
+	else
+		new_state = KPDK_DK(kpdk) & keypad->direct_key_mask;
+
+	bits_changed = keypad->direct_key_state ^ new_state;
+
+	if (bits_changed == 0)
+		return;
+
+	for (i = 0; i < pdata->direct_key_num; i++) {
+		if (bits_changed & (1 << i)) {
+			int code = MAX_MATRIX_KEY_NUM + i;
+
+			input_event(input_dev, EV_MSC, MSC_SCAN, code);
+			input_report_key(input_dev, keypad->keycodes[code],
+					 new_state & (1 << i));
+		}
+	}
+	input_sync(input_dev);
+	keypad->direct_key_state = new_state;
+}
+
+static void clear_wakeup_event(struct pxa27x_keypad *keypad)
+{
+	const struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+
+	if (pdata->clear_wakeup_event)
+		(pdata->clear_wakeup_event)();
+}
+
+static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id)
+{
+	struct pxa27x_keypad *keypad = dev_id;
+	unsigned long kpc = keypad_readl(KPC);
+
+	clear_wakeup_event(keypad);
+
+	if (kpc & KPC_DI)
+		pxa27x_keypad_scan_direct(keypad);
+
+	if (kpc & KPC_MI)
+		pxa27x_keypad_scan_matrix(keypad);
+
+	return IRQ_HANDLED;
+}
+
+static void pxa27x_keypad_config(struct pxa27x_keypad *keypad)
+{
+	const struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+	unsigned int mask = 0, direct_key_num = 0;
+	unsigned long kpc = 0;
+
+	/* clear pending interrupt bit */
+	keypad_readl(KPC);
+
+	/* enable matrix keys with automatic scan */
+	if (pdata->matrix_key_rows && pdata->matrix_key_cols) {
+		kpc |= KPC_ASACT | KPC_MIE | KPC_ME | KPC_MS_ALL;
+		kpc |= KPC_MKRN(pdata->matrix_key_rows) |
+		       KPC_MKCN(pdata->matrix_key_cols);
+	}
+
+	/* enable rotary key, debounce interval same as direct keys */
+	if (pdata->enable_rotary0) {
+		mask |= 0x03;
+		direct_key_num = 2;
+		kpc |= KPC_REE0;
+	}
+
+	if (pdata->enable_rotary1) {
+		mask |= 0x0c;
+		direct_key_num = 4;
+		kpc |= KPC_REE1;
+	}
+
+	if (pdata->direct_key_num > direct_key_num)
+		direct_key_num = pdata->direct_key_num;
+
+	/*
+	 * Direct keys usage may not start from KP_DKIN0, check the platfrom
+	 * mask data to config the specific.
+	 */
+	if (pdata->direct_key_mask)
+		keypad->direct_key_mask = pdata->direct_key_mask;
+	else
+		keypad->direct_key_mask = ((1 << direct_key_num) - 1) & ~mask;
+
+	/* enable direct key */
+	if (direct_key_num)
+		kpc |= KPC_DE | KPC_DIE | KPC_DKN(direct_key_num);
+
+	keypad_writel(KPC, kpc | KPC_RE_ZERO_DEB);
+	keypad_writel(KPREC, DEFAULT_KPREC);
+	keypad_writel(KPKDI, pdata->debounce_interval);
+}
+
+static int pxa27x_keypad_open(struct input_dev *dev)
+{
+	struct pxa27x_keypad *keypad = input_get_drvdata(dev);
+
+	/* Enable unit clock */
+	clk_prepare_enable(keypad->clk);
+	pxa27x_keypad_config(keypad);
+
+	return 0;
+}
+
+static void pxa27x_keypad_close(struct input_dev *dev)
+{
+	struct pxa27x_keypad *keypad = input_get_drvdata(dev);
+
+	/* Disable clock unit */
+	clk_disable_unprepare(keypad->clk);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pxa27x_keypad_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
+
+	/*
+	 * If the keypad is used a wake up source, clock can not be disabled.
+	 * Or it can not detect the key pressing.
+	 */
+	if (device_may_wakeup(&pdev->dev))
+		enable_irq_wake(keypad->irq);
+	else
+		clk_disable_unprepare(keypad->clk);
+
+	return 0;
+}
+
+static int pxa27x_keypad_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
+	struct input_dev *input_dev = keypad->input_dev;
+
+	/*
+	 * If the keypad is used as wake up source, the clock is not turned
+	 * off. So do not need configure it again.
+	 */
+	if (device_may_wakeup(&pdev->dev)) {
+		disable_irq_wake(keypad->irq);
+	} else {
+		mutex_lock(&input_dev->mutex);
+
+		if (input_dev->users) {
+			/* Enable unit clock */
+			clk_prepare_enable(keypad->clk);
+			pxa27x_keypad_config(keypad);
+		}
+
+		mutex_unlock(&input_dev->mutex);
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pxa27x_keypad_pm_ops,
+			 pxa27x_keypad_suspend, pxa27x_keypad_resume);
+
+
+static int pxa27x_keypad_probe(struct platform_device *pdev)
+{
+	const struct pxa27x_keypad_platform_data *pdata =
+					dev_get_platdata(&pdev->dev);
+	struct device_node *np = pdev->dev.of_node;
+	struct pxa27x_keypad *keypad;
+	struct input_dev *input_dev;
+	struct resource *res;
+	int irq, error;
+
+	/* Driver need build keycode from device tree or pdata */
+	if (!np && !pdata)
+		return -EINVAL;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to get keypad irq\n");
+		return -ENXIO;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to get I/O memory\n");
+		return -ENXIO;
+	}
+
+	keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad),
+			      GFP_KERNEL);
+	if (!keypad)
+		return -ENOMEM;
+
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!input_dev)
+		return -ENOMEM;
+
+	keypad->pdata = pdata;
+	keypad->input_dev = input_dev;
+	keypad->irq = irq;
+
+	keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(keypad->mmio_base))
+		return PTR_ERR(keypad->mmio_base);
+
+	keypad->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(keypad->clk)) {
+		dev_err(&pdev->dev, "failed to get keypad clock\n");
+		return PTR_ERR(keypad->clk);
+	}
+
+	input_dev->name = pdev->name;
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->open = pxa27x_keypad_open;
+	input_dev->close = pxa27x_keypad_close;
+	input_dev->dev.parent = &pdev->dev;
+
+	input_dev->keycode = keypad->keycodes;
+	input_dev->keycodesize = sizeof(keypad->keycodes[0]);
+	input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);
+
+	input_set_drvdata(input_dev, keypad);
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+
+	if (pdata) {
+		error = pxa27x_keypad_build_keycode(keypad);
+	} else {
+		error = pxa27x_keypad_build_keycode_from_dt(keypad);
+		/*
+		 * Data that we get from DT resides in dynamically
+		 * allocated memory so we need to update our pdata
+		 * pointer.
+		 */
+		pdata = keypad->pdata;
+	}
+	if (error) {
+		dev_err(&pdev->dev, "failed to build keycode\n");
+		return error;
+	}
+
+	keypad->row_shift = get_count_order(pdata->matrix_key_cols);
+
+	if ((pdata->enable_rotary0 && keypad->rotary_rel_code[0] != -1) ||
+	    (pdata->enable_rotary1 && keypad->rotary_rel_code[1] != -1)) {
+		input_dev->evbit[0] |= BIT_MASK(EV_REL);
+	}
+
+	error = devm_request_irq(&pdev->dev, irq, pxa27x_keypad_irq_handler,
+				 0, pdev->name, keypad);
+	if (error) {
+		dev_err(&pdev->dev, "failed to request IRQ\n");
+		return error;
+	}
+
+	/* Register the input device */
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		return error;
+	}
+
+	platform_set_drvdata(pdev, keypad);
+	device_init_wakeup(&pdev->dev, 1);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pxa27x_keypad_dt_match[] = {
+	{ .compatible = "marvell,pxa27x-keypad" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, pxa27x_keypad_dt_match);
+#endif
+
+static struct platform_driver pxa27x_keypad_driver = {
+	.probe		= pxa27x_keypad_probe,
+	.driver		= {
+		.name	= "pxa27x-keypad",
+		.of_match_table = of_match_ptr(pxa27x_keypad_dt_match),
+		.pm	= &pxa27x_keypad_pm_ops,
+	},
+};
+module_platform_driver(pxa27x_keypad_driver);
+
+MODULE_DESCRIPTION("PXA27x Keypad Controller Driver");
+MODULE_LICENSE("GPL");
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:pxa27x-keypad");
diff --git a/drivers/input/keyboard/pxa930_rotary.c b/drivers/input/keyboard/pxa930_rotary.c
new file mode 100644
index 0000000..1cf5211
--- /dev/null
+++ b/drivers/input/keyboard/pxa930_rotary.c
@@ -0,0 +1,200 @@
+/*
+ * Driver for the enhanced rotary controller on pxa930 and pxa935
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <linux/platform_data/keyboard-pxa930_rotary.h>
+
+#define SBCR	(0x04)
+#define ERCR	(0x0c)
+
+#define SBCR_ERSB	(1 << 5)
+
+struct pxa930_rotary {
+	struct input_dev	*input_dev;
+	void __iomem		*mmio_base;
+	int			last_ercr;
+
+	struct pxa930_rotary_platform_data *pdata;
+};
+
+static void clear_sbcr(struct pxa930_rotary *r)
+{
+	uint32_t sbcr = __raw_readl(r->mmio_base + SBCR);
+
+	__raw_writel(sbcr | SBCR_ERSB, r->mmio_base + SBCR);
+	__raw_writel(sbcr & ~SBCR_ERSB, r->mmio_base + SBCR);
+}
+
+static irqreturn_t rotary_irq(int irq, void *dev_id)
+{
+	struct pxa930_rotary *r = dev_id;
+	struct pxa930_rotary_platform_data *pdata = r->pdata;
+	int ercr, delta, key;
+
+	ercr = __raw_readl(r->mmio_base + ERCR) & 0xf;
+	clear_sbcr(r);
+
+	delta = ercr - r->last_ercr;
+	if (delta == 0)
+		return IRQ_HANDLED;
+
+	r->last_ercr = ercr;
+
+	if (pdata->up_key && pdata->down_key) {
+		key = (delta > 0) ? pdata->up_key : pdata->down_key;
+		input_report_key(r->input_dev, key, 1);
+		input_sync(r->input_dev);
+		input_report_key(r->input_dev, key, 0);
+	} else
+		input_report_rel(r->input_dev, pdata->rel_code, delta);
+
+	input_sync(r->input_dev);
+
+	return IRQ_HANDLED;
+}
+
+static int pxa930_rotary_open(struct input_dev *dev)
+{
+	struct pxa930_rotary *r = input_get_drvdata(dev);
+
+	clear_sbcr(r);
+
+	return 0;
+}
+
+static void pxa930_rotary_close(struct input_dev *dev)
+{
+	struct pxa930_rotary *r = input_get_drvdata(dev);
+
+	clear_sbcr(r);
+}
+
+static int pxa930_rotary_probe(struct platform_device *pdev)
+{
+	struct pxa930_rotary_platform_data *pdata =
+			dev_get_platdata(&pdev->dev);
+	struct pxa930_rotary *r;
+	struct input_dev *input_dev;
+	struct resource *res;
+	int irq;
+	int err;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq for rotary controller\n");
+		return -ENXIO;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no I/O memory defined\n");
+		return -ENXIO;
+	}
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data defined\n");
+		return -EINVAL;
+	}
+
+	r = kzalloc(sizeof(struct pxa930_rotary), GFP_KERNEL);
+	if (!r)
+		return -ENOMEM;
+
+	r->mmio_base = ioremap_nocache(res->start, resource_size(res));
+	if (r->mmio_base == NULL) {
+		dev_err(&pdev->dev, "failed to remap IO memory\n");
+		err = -ENXIO;
+		goto failed_free;
+	}
+
+	r->pdata = pdata;
+	platform_set_drvdata(pdev, r);
+
+	/* allocate and register the input device */
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&pdev->dev, "failed to allocate input device\n");
+		err = -ENOMEM;
+		goto failed_free_io;
+	}
+
+	input_dev->name = pdev->name;
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->open = pxa930_rotary_open;
+	input_dev->close = pxa930_rotary_close;
+	input_dev->dev.parent = &pdev->dev;
+
+	if (pdata->up_key && pdata->down_key) {
+		__set_bit(pdata->up_key, input_dev->keybit);
+		__set_bit(pdata->down_key, input_dev->keybit);
+		__set_bit(EV_KEY, input_dev->evbit);
+	} else {
+		__set_bit(pdata->rel_code, input_dev->relbit);
+		__set_bit(EV_REL, input_dev->evbit);
+	}
+
+	r->input_dev = input_dev;
+	input_set_drvdata(input_dev, r);
+
+	err = request_irq(irq, rotary_irq, 0,
+			"enhanced rotary", r);
+	if (err) {
+		dev_err(&pdev->dev, "failed to request IRQ\n");
+		goto failed_free_input;
+	}
+
+	err = input_register_device(input_dev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		goto failed_free_irq;
+	}
+
+	return 0;
+
+failed_free_irq:
+	free_irq(irq, r);
+failed_free_input:
+	input_free_device(input_dev);
+failed_free_io:
+	iounmap(r->mmio_base);
+failed_free:
+	kfree(r);
+	return err;
+}
+
+static int pxa930_rotary_remove(struct platform_device *pdev)
+{
+	struct pxa930_rotary *r = platform_get_drvdata(pdev);
+
+	free_irq(platform_get_irq(pdev, 0), r);
+	input_unregister_device(r->input_dev);
+	iounmap(r->mmio_base);
+	kfree(r);
+
+	return 0;
+}
+
+static struct platform_driver pxa930_rotary_driver = {
+	.driver		= {
+		.name	= "pxa930-rotary",
+	},
+	.probe		= pxa930_rotary_probe,
+	.remove		= pxa930_rotary_remove,
+};
+module_platform_driver(pxa930_rotary_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for PXA93x Enhanced Rotary Controller");
+MODULE_AUTHOR("Yao Yong <yaoyong@marvell.com>");
diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c
new file mode 100644
index 0000000..5a57787
--- /dev/null
+++ b/drivers/input/keyboard/qt1070.c
@@ -0,0 +1,291 @@
+/*
+ *  Atmel AT42QT1070 QTouch Sensor Controller
+ *
+ *  Copyright (C) 2011 Atmel
+ *
+ *  Authors: Bo Shen <voice.shen@atmel.com>
+ *
+ *  Base on AT42QT2160 driver by:
+ *  Raphael Derosso Pereira <raphaelpereira@gmail.com>
+ *  Copyright (C) 2009
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+
+/* Address for each register */
+#define CHIP_ID            0x00
+#define QT1070_CHIP_ID     0x2E
+
+#define FW_VERSION         0x01
+#define QT1070_FW_VERSION  0x15
+
+#define DET_STATUS         0x02
+
+#define KEY_STATUS         0x03
+
+/* Calibrate */
+#define CALIBRATE_CMD      0x38
+#define QT1070_CAL_TIME    200
+
+/* Reset */
+#define RESET              0x39
+#define QT1070_RESET_TIME  255
+
+/* AT42QT1070 support up to 7 keys */
+static const unsigned short qt1070_key2code[] = {
+	KEY_0, KEY_1, KEY_2, KEY_3,
+	KEY_4, KEY_5, KEY_6,
+};
+
+struct qt1070_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+	unsigned int irq;
+	unsigned short keycodes[ARRAY_SIZE(qt1070_key2code)];
+	u8 last_keys;
+};
+
+static int qt1070_read(struct i2c_client *client, u8 reg)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0)
+		dev_err(&client->dev,
+			"can not read register, returned %d\n", ret);
+
+	return ret;
+}
+
+static int qt1070_write(struct i2c_client *client, u8 reg, u8 data)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, reg, data);
+	if (ret < 0)
+		dev_err(&client->dev,
+			"can not write register, returned %d\n", ret);
+
+	return ret;
+}
+
+static bool qt1070_identify(struct i2c_client *client)
+{
+	int id, ver;
+
+	/* Read Chip ID */
+	id = qt1070_read(client, CHIP_ID);
+	if (id != QT1070_CHIP_ID) {
+		dev_err(&client->dev, "ID %d not supported\n", id);
+		return false;
+	}
+
+	/* Read firmware version */
+	ver = qt1070_read(client, FW_VERSION);
+	if (ver < 0) {
+		dev_err(&client->dev, "could not read the firmware version\n");
+		return false;
+	}
+
+	dev_info(&client->dev, "AT42QT1070 firmware version %x\n", ver);
+
+	return true;
+}
+
+static irqreturn_t qt1070_interrupt(int irq, void *dev_id)
+{
+	struct qt1070_data *data = dev_id;
+	struct i2c_client *client = data->client;
+	struct input_dev *input = data->input;
+	int i;
+	u8 new_keys, keyval, mask = 0x01;
+
+	/* Read the detected status register, thus clearing interrupt */
+	qt1070_read(client, DET_STATUS);
+
+	/* Read which key changed */
+	new_keys = qt1070_read(client, KEY_STATUS);
+
+	for (i = 0; i < ARRAY_SIZE(qt1070_key2code); i++) {
+		keyval = new_keys & mask;
+		if ((data->last_keys & mask) != keyval)
+			input_report_key(input, data->keycodes[i], keyval);
+		mask <<= 1;
+	}
+	input_sync(input);
+
+	data->last_keys = new_keys;
+	return IRQ_HANDLED;
+}
+
+static int qt1070_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	struct qt1070_data *data;
+	struct input_dev *input;
+	int i;
+	int err;
+
+	err = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE);
+	if (!err) {
+		dev_err(&client->dev, "%s adapter not supported\n",
+			dev_driver_string(&client->adapter->dev));
+		return -ENODEV;
+	}
+
+	if (!client->irq) {
+		dev_err(&client->dev, "please assign the irq to this device\n");
+		return -EINVAL;
+	}
+
+	/* Identify the qt1070 chip */
+	if (!qt1070_identify(client))
+		return -ENODEV;
+
+	data = kzalloc(sizeof(struct qt1070_data), GFP_KERNEL);
+	input = input_allocate_device();
+	if (!data || !input) {
+		dev_err(&client->dev, "insufficient memory\n");
+		err = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	data->client = client;
+	data->input = input;
+	data->irq = client->irq;
+
+	input->name = "AT42QT1070 QTouch Sensor";
+	input->dev.parent = &client->dev;
+	input->id.bustype = BUS_I2C;
+
+	/* Add the keycode */
+	input->keycode = data->keycodes;
+	input->keycodesize = sizeof(data->keycodes[0]);
+	input->keycodemax = ARRAY_SIZE(qt1070_key2code);
+
+	__set_bit(EV_KEY, input->evbit);
+
+	for (i = 0; i < ARRAY_SIZE(qt1070_key2code); i++) {
+		data->keycodes[i] = qt1070_key2code[i];
+		__set_bit(qt1070_key2code[i], input->keybit);
+	}
+
+	/* Calibrate device */
+	qt1070_write(client, CALIBRATE_CMD, 1);
+	msleep(QT1070_CAL_TIME);
+
+	/* Soft reset */
+	qt1070_write(client, RESET, 1);
+	msleep(QT1070_RESET_TIME);
+
+	err = request_threaded_irq(client->irq, NULL, qt1070_interrupt,
+				   IRQF_TRIGGER_NONE | IRQF_ONESHOT,
+				   client->dev.driver->name, data);
+	if (err) {
+		dev_err(&client->dev, "fail to request irq\n");
+		goto err_free_mem;
+	}
+
+	/* Register the input device */
+	err = input_register_device(data->input);
+	if (err) {
+		dev_err(&client->dev, "Failed to register input device\n");
+		goto err_free_irq;
+	}
+
+	i2c_set_clientdata(client, data);
+
+	/* Read to clear the chang line */
+	qt1070_read(client, DET_STATUS);
+
+	return 0;
+
+err_free_irq:
+	free_irq(client->irq, data);
+err_free_mem:
+	input_free_device(input);
+	kfree(data);
+	return err;
+}
+
+static int qt1070_remove(struct i2c_client *client)
+{
+	struct qt1070_data *data = i2c_get_clientdata(client);
+
+	/* Release IRQ */
+	free_irq(client->irq, data);
+
+	input_unregister_device(data->input);
+	kfree(data);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int qt1070_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct qt1070_data *data = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(data->irq);
+
+	return 0;
+}
+
+static int qt1070_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct qt1070_data *data = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(data->irq);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(qt1070_pm_ops, qt1070_suspend, qt1070_resume);
+
+static const struct i2c_device_id qt1070_id[] = {
+	{ "qt1070", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, qt1070_id);
+
+static struct i2c_driver qt1070_driver = {
+	.driver	= {
+		.name	= "qt1070",
+		.pm	= &qt1070_pm_ops,
+	},
+	.id_table	= qt1070_id,
+	.probe		= qt1070_probe,
+	.remove		= qt1070_remove,
+};
+
+module_i2c_driver(qt1070_driver);
+
+MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
+MODULE_DESCRIPTION("Driver for AT42QT1070 QTouch sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c
new file mode 100644
index 0000000..43b8648
--- /dev/null
+++ b/drivers/input/keyboard/qt2160.c
@@ -0,0 +1,511 @@
+/*
+ *  qt2160.c - Atmel AT42QT2160 Touch Sense Controller
+ *
+ *  Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.com>
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+
+#define QT2160_VALID_CHIPID  0x11
+
+#define QT2160_CMD_CHIPID     0
+#define QT2160_CMD_CODEVER    1
+#define QT2160_CMD_GSTAT      2
+#define QT2160_CMD_KEYS3      3
+#define QT2160_CMD_KEYS4      4
+#define QT2160_CMD_SLIDE      5
+#define QT2160_CMD_GPIOS      6
+#define QT2160_CMD_SUBVER     7
+#define QT2160_CMD_CALIBRATE  10
+#define QT2160_CMD_DRIVE_X    70
+#define QT2160_CMD_PWMEN_X    74
+#define QT2160_CMD_PWM_DUTY   76
+
+#define QT2160_NUM_LEDS_X	8
+
+#define QT2160_CYCLE_INTERVAL	(2*HZ)
+
+static unsigned char qt2160_key2code[] = {
+	KEY_0, KEY_1, KEY_2, KEY_3,
+	KEY_4, KEY_5, KEY_6, KEY_7,
+	KEY_8, KEY_9, KEY_A, KEY_B,
+	KEY_C, KEY_D, KEY_E, KEY_F,
+};
+
+#ifdef CONFIG_LEDS_CLASS
+struct qt2160_led {
+	struct qt2160_data *qt2160;
+	struct led_classdev cdev;
+	struct work_struct work;
+	char name[32];
+	int id;
+	enum led_brightness new_brightness;
+};
+#endif
+
+struct qt2160_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct delayed_work dwork;
+	spinlock_t lock;        /* Protects canceling/rescheduling of dwork */
+	unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
+	u16 key_matrix;
+#ifdef CONFIG_LEDS_CLASS
+	struct qt2160_led leds[QT2160_NUM_LEDS_X];
+	struct mutex led_lock;
+#endif
+};
+
+static int qt2160_read(struct i2c_client *client, u8 reg);
+static int qt2160_write(struct i2c_client *client, u8 reg, u8 data);
+
+#ifdef CONFIG_LEDS_CLASS
+
+static void qt2160_led_work(struct work_struct *work)
+{
+	struct qt2160_led *led = container_of(work, struct qt2160_led, work);
+	struct qt2160_data *qt2160 = led->qt2160;
+	struct i2c_client *client = qt2160->client;
+	int value = led->new_brightness;
+	u32 drive, pwmen;
+
+	mutex_lock(&qt2160->led_lock);
+
+	drive = qt2160_read(client, QT2160_CMD_DRIVE_X);
+	pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X);
+	if (value != LED_OFF) {
+		drive |= (1 << led->id);
+		pwmen |= (1 << led->id);
+
+	} else {
+		drive &= ~(1 << led->id);
+		pwmen &= ~(1 << led->id);
+	}
+	qt2160_write(client, QT2160_CMD_DRIVE_X, drive);
+	qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen);
+
+	/*
+	 * Changing this register will change the brightness
+	 * of every LED in the qt2160. It's a HW limitation.
+	 */
+	if (value != LED_OFF)
+		qt2160_write(client, QT2160_CMD_PWM_DUTY, value);
+
+	mutex_unlock(&qt2160->led_lock);
+}
+
+static void qt2160_led_set(struct led_classdev *cdev,
+			   enum led_brightness value)
+{
+	struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev);
+
+	led->new_brightness = value;
+	schedule_work(&led->work);
+}
+
+#endif /* CONFIG_LEDS_CLASS */
+
+static int qt2160_read_block(struct i2c_client *client,
+			     u8 inireg, u8 *buffer, unsigned int count)
+{
+	int error, idx = 0;
+
+	/*
+	 * Can't use SMBus block data read. Check for I2C functionality to speed
+	 * things up whenever possible. Otherwise we will be forced to read
+	 * sequentially.
+	 */
+	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))	{
+
+		error = i2c_smbus_write_byte(client, inireg + idx);
+		if (error) {
+			dev_err(&client->dev,
+				"couldn't send request. Returned %d\n", error);
+			return error;
+		}
+
+		error = i2c_master_recv(client, buffer, count);
+		if (error != count) {
+			dev_err(&client->dev,
+				"couldn't read registers. Returned %d bytes\n", error);
+			return error;
+		}
+	} else {
+
+		while (count--) {
+			int data;
+
+			error = i2c_smbus_write_byte(client, inireg + idx);
+			if (error) {
+				dev_err(&client->dev,
+					"couldn't send request. Returned %d\n", error);
+				return error;
+			}
+
+			data = i2c_smbus_read_byte(client);
+			if (data < 0) {
+				dev_err(&client->dev,
+					"couldn't read register. Returned %d\n", data);
+				return data;
+			}
+
+			buffer[idx++] = data;
+		}
+	}
+
+	return 0;
+}
+
+static int qt2160_get_key_matrix(struct qt2160_data *qt2160)
+{
+	struct i2c_client *client = qt2160->client;
+	struct input_dev *input = qt2160->input;
+	u8 regs[6];
+	u16 old_matrix, new_matrix;
+	int ret, i, mask;
+
+	dev_dbg(&client->dev, "requesting keys...\n");
+
+	/*
+	 * Read all registers from General Status Register
+	 * to GPIOs register
+	 */
+	ret = qt2160_read_block(client, QT2160_CMD_GSTAT, regs, 6);
+	if (ret) {
+		dev_err(&client->dev,
+			"could not perform chip read.\n");
+		return ret;
+	}
+
+	old_matrix = qt2160->key_matrix;
+	qt2160->key_matrix = new_matrix = (regs[2] << 8) | regs[1];
+
+	mask = 0x01;
+	for (i = 0; i < 16; ++i, mask <<= 1) {
+		int keyval = new_matrix & mask;
+
+		if ((old_matrix & mask) != keyval) {
+			input_report_key(input, qt2160->keycodes[i], keyval);
+			dev_dbg(&client->dev, "key %d %s\n",
+				i, keyval ? "pressed" : "released");
+		}
+	}
+
+	input_sync(input);
+
+	return 0;
+}
+
+static irqreturn_t qt2160_irq(int irq, void *_qt2160)
+{
+	struct qt2160_data *qt2160 = _qt2160;
+	unsigned long flags;
+
+	spin_lock_irqsave(&qt2160->lock, flags);
+
+	mod_delayed_work(system_wq, &qt2160->dwork, 0);
+
+	spin_unlock_irqrestore(&qt2160->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static void qt2160_schedule_read(struct qt2160_data *qt2160)
+{
+	spin_lock_irq(&qt2160->lock);
+	schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL);
+	spin_unlock_irq(&qt2160->lock);
+}
+
+static void qt2160_worker(struct work_struct *work)
+{
+	struct qt2160_data *qt2160 =
+		container_of(work, struct qt2160_data, dwork.work);
+
+	dev_dbg(&qt2160->client->dev, "worker\n");
+
+	qt2160_get_key_matrix(qt2160);
+
+	/* Avoid device lock up by checking every so often */
+	qt2160_schedule_read(qt2160);
+}
+
+static int qt2160_read(struct i2c_client *client, u8 reg)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte(client, reg);
+	if (ret) {
+		dev_err(&client->dev,
+			"couldn't send request. Returned %d\n", ret);
+		return ret;
+	}
+
+	ret = i2c_smbus_read_byte(client);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"couldn't read register. Returned %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int qt2160_write(struct i2c_client *client, u8 reg, u8 data)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, reg, data);
+	if (ret < 0)
+		dev_err(&client->dev,
+			"couldn't write data. Returned %d\n", ret);
+
+	return ret;
+}
+
+#ifdef CONFIG_LEDS_CLASS
+
+static int qt2160_register_leds(struct qt2160_data *qt2160)
+{
+	struct i2c_client *client = qt2160->client;
+	int ret;
+	int i;
+
+	mutex_init(&qt2160->led_lock);
+
+	for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
+		struct qt2160_led *led = &qt2160->leds[i];
+
+		snprintf(led->name, sizeof(led->name), "qt2160:x%d", i);
+		led->cdev.name = led->name;
+		led->cdev.brightness_set = qt2160_led_set;
+		led->cdev.brightness = LED_OFF;
+		led->id = i;
+		led->qt2160 = qt2160;
+
+		INIT_WORK(&led->work, qt2160_led_work);
+
+		ret = led_classdev_register(&client->dev, &led->cdev);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Tur off LEDs */
+	qt2160_write(client, QT2160_CMD_DRIVE_X, 0);
+	qt2160_write(client, QT2160_CMD_PWMEN_X, 0);
+	qt2160_write(client, QT2160_CMD_PWM_DUTY, 0);
+
+	return 0;
+}
+
+static void qt2160_unregister_leds(struct qt2160_data *qt2160)
+{
+	int i;
+
+	for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
+		led_classdev_unregister(&qt2160->leds[i].cdev);
+		cancel_work_sync(&qt2160->leds[i].work);
+	}
+}
+
+#else
+
+static inline int qt2160_register_leds(struct qt2160_data *qt2160)
+{
+	return 0;
+}
+
+static inline void qt2160_unregister_leds(struct qt2160_data *qt2160)
+{
+}
+
+#endif
+
+static bool qt2160_identify(struct i2c_client *client)
+{
+	int id, ver, rev;
+
+	/* Read Chid ID to check if chip is valid */
+	id = qt2160_read(client, QT2160_CMD_CHIPID);
+	if (id != QT2160_VALID_CHIPID) {
+		dev_err(&client->dev, "ID %d not supported\n", id);
+		return false;
+	}
+
+	/* Read chip firmware version */
+	ver = qt2160_read(client, QT2160_CMD_CODEVER);
+	if (ver < 0) {
+		dev_err(&client->dev, "could not get firmware version\n");
+		return false;
+	}
+
+	/* Read chip firmware revision */
+	rev = qt2160_read(client, QT2160_CMD_SUBVER);
+	if (rev < 0) {
+		dev_err(&client->dev, "could not get firmware revision\n");
+		return false;
+	}
+
+	dev_info(&client->dev, "AT42QT2160 firmware version %d.%d.%d\n",
+			ver >> 4, ver & 0xf, rev);
+
+	return true;
+}
+
+static int qt2160_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct qt2160_data *qt2160;
+	struct input_dev *input;
+	int i;
+	int error;
+
+	/* Check functionality */
+	error = i2c_check_functionality(client->adapter,
+			I2C_FUNC_SMBUS_BYTE);
+	if (!error) {
+		dev_err(&client->dev, "%s adapter not supported\n",
+				dev_driver_string(&client->adapter->dev));
+		return -ENODEV;
+	}
+
+	if (!qt2160_identify(client))
+		return -ENODEV;
+
+	/* Chip is valid and active. Allocate structure */
+	qt2160 = kzalloc(sizeof(struct qt2160_data), GFP_KERNEL);
+	input = input_allocate_device();
+	if (!qt2160 || !input) {
+		dev_err(&client->dev, "insufficient memory\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	qt2160->client = client;
+	qt2160->input = input;
+	INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker);
+	spin_lock_init(&qt2160->lock);
+
+	input->name = "AT42QT2160 Touch Sense Keyboard";
+	input->id.bustype = BUS_I2C;
+
+	input->keycode = qt2160->keycodes;
+	input->keycodesize = sizeof(qt2160->keycodes[0]);
+	input->keycodemax = ARRAY_SIZE(qt2160_key2code);
+
+	__set_bit(EV_KEY, input->evbit);
+	__clear_bit(EV_REP, input->evbit);
+	for (i = 0; i < ARRAY_SIZE(qt2160_key2code); i++) {
+		qt2160->keycodes[i] = qt2160_key2code[i];
+		__set_bit(qt2160_key2code[i], input->keybit);
+	}
+	__clear_bit(KEY_RESERVED, input->keybit);
+
+	/* Calibrate device */
+	error = qt2160_write(client, QT2160_CMD_CALIBRATE, 1);
+	if (error) {
+		dev_err(&client->dev, "failed to calibrate device\n");
+		goto err_free_mem;
+	}
+
+	if (client->irq) {
+		error = request_irq(client->irq, qt2160_irq,
+				    IRQF_TRIGGER_FALLING, "qt2160", qt2160);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to allocate irq %d\n", client->irq);
+			goto err_free_mem;
+		}
+	}
+
+	error = qt2160_register_leds(qt2160);
+	if (error) {
+		dev_err(&client->dev, "Failed to register leds\n");
+		goto err_free_irq;
+	}
+
+	error = input_register_device(qt2160->input);
+	if (error) {
+		dev_err(&client->dev,
+			"Failed to register input device\n");
+		goto err_unregister_leds;
+	}
+
+	i2c_set_clientdata(client, qt2160);
+	qt2160_schedule_read(qt2160);
+
+	return 0;
+
+err_unregister_leds:
+	qt2160_unregister_leds(qt2160);
+err_free_irq:
+	if (client->irq)
+		free_irq(client->irq, qt2160);
+err_free_mem:
+	input_free_device(input);
+	kfree(qt2160);
+	return error;
+}
+
+static int qt2160_remove(struct i2c_client *client)
+{
+	struct qt2160_data *qt2160 = i2c_get_clientdata(client);
+
+	qt2160_unregister_leds(qt2160);
+
+	/* Release IRQ so no queue will be scheduled */
+	if (client->irq)
+		free_irq(client->irq, qt2160);
+
+	cancel_delayed_work_sync(&qt2160->dwork);
+
+	input_unregister_device(qt2160->input);
+	kfree(qt2160);
+
+	return 0;
+}
+
+static const struct i2c_device_id qt2160_idtable[] = {
+	{ "qt2160", 0, },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, qt2160_idtable);
+
+static struct i2c_driver qt2160_driver = {
+	.driver = {
+		.name	= "qt2160",
+	},
+
+	.id_table	= qt2160_idtable,
+	.probe		= qt2160_probe,
+	.remove		= qt2160_remove,
+};
+
+module_i2c_driver(qt2160_driver);
+
+MODULE_AUTHOR("Raphael Derosso Pereira <raphaelpereira@gmail.com>");
+MODULE_DESCRIPTION("Driver for AT42QT2160 Touch Sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c
new file mode 100644
index 0000000..4e319eb
--- /dev/null
+++ b/drivers/input/keyboard/samsung-keypad.c
@@ -0,0 +1,617 @@
+/*
+ * Samsung keypad driver
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/sched.h>
+#include <linux/input/samsung-keypad.h>
+
+#define SAMSUNG_KEYIFCON			0x00
+#define SAMSUNG_KEYIFSTSCLR			0x04
+#define SAMSUNG_KEYIFCOL			0x08
+#define SAMSUNG_KEYIFROW			0x0c
+#define SAMSUNG_KEYIFFC				0x10
+
+/* SAMSUNG_KEYIFCON */
+#define SAMSUNG_KEYIFCON_INT_F_EN		(1 << 0)
+#define SAMSUNG_KEYIFCON_INT_R_EN		(1 << 1)
+#define SAMSUNG_KEYIFCON_DF_EN			(1 << 2)
+#define SAMSUNG_KEYIFCON_FC_EN			(1 << 3)
+#define SAMSUNG_KEYIFCON_WAKEUPEN		(1 << 4)
+
+/* SAMSUNG_KEYIFSTSCLR */
+#define SAMSUNG_KEYIFSTSCLR_P_INT_MASK		(0xff << 0)
+#define SAMSUNG_KEYIFSTSCLR_R_INT_MASK		(0xff << 8)
+#define SAMSUNG_KEYIFSTSCLR_R_INT_OFFSET	8
+#define S5PV210_KEYIFSTSCLR_P_INT_MASK		(0x3fff << 0)
+#define S5PV210_KEYIFSTSCLR_R_INT_MASK		(0x3fff << 16)
+#define S5PV210_KEYIFSTSCLR_R_INT_OFFSET	16
+
+/* SAMSUNG_KEYIFCOL */
+#define SAMSUNG_KEYIFCOL_MASK			(0xff << 0)
+#define S5PV210_KEYIFCOLEN_MASK			(0xff << 8)
+
+/* SAMSUNG_KEYIFROW */
+#define SAMSUNG_KEYIFROW_MASK			(0xff << 0)
+#define S5PV210_KEYIFROW_MASK			(0x3fff << 0)
+
+/* SAMSUNG_KEYIFFC */
+#define SAMSUNG_KEYIFFC_MASK			(0x3ff << 0)
+
+enum samsung_keypad_type {
+	KEYPAD_TYPE_SAMSUNG,
+	KEYPAD_TYPE_S5PV210,
+};
+
+struct samsung_keypad {
+	struct input_dev *input_dev;
+	struct platform_device *pdev;
+	struct clk *clk;
+	void __iomem *base;
+	wait_queue_head_t wait;
+	bool stopped;
+	bool wake_enabled;
+	int irq;
+	enum samsung_keypad_type type;
+	unsigned int row_shift;
+	unsigned int rows;
+	unsigned int cols;
+	unsigned int row_state[SAMSUNG_MAX_COLS];
+	unsigned short keycodes[];
+};
+
+static void samsung_keypad_scan(struct samsung_keypad *keypad,
+				unsigned int *row_state)
+{
+	unsigned int col;
+	unsigned int val;
+
+	for (col = 0; col < keypad->cols; col++) {
+		if (keypad->type == KEYPAD_TYPE_S5PV210) {
+			val = S5PV210_KEYIFCOLEN_MASK;
+			val &= ~(1 << col) << 8;
+		} else {
+			val = SAMSUNG_KEYIFCOL_MASK;
+			val &= ~(1 << col);
+		}
+
+		writel(val, keypad->base + SAMSUNG_KEYIFCOL);
+		mdelay(1);
+
+		val = readl(keypad->base + SAMSUNG_KEYIFROW);
+		row_state[col] = ~val & ((1 << keypad->rows) - 1);
+	}
+
+	/* KEYIFCOL reg clear */
+	writel(0, keypad->base + SAMSUNG_KEYIFCOL);
+}
+
+static bool samsung_keypad_report(struct samsung_keypad *keypad,
+				  unsigned int *row_state)
+{
+	struct input_dev *input_dev = keypad->input_dev;
+	unsigned int changed;
+	unsigned int pressed;
+	unsigned int key_down = 0;
+	unsigned int val;
+	unsigned int col, row;
+
+	for (col = 0; col < keypad->cols; col++) {
+		changed = row_state[col] ^ keypad->row_state[col];
+		key_down |= row_state[col];
+		if (!changed)
+			continue;
+
+		for (row = 0; row < keypad->rows; row++) {
+			if (!(changed & (1 << row)))
+				continue;
+
+			pressed = row_state[col] & (1 << row);
+
+			dev_dbg(&keypad->input_dev->dev,
+				"key %s, row: %d, col: %d\n",
+				pressed ? "pressed" : "released", row, col);
+
+			val = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
+
+			input_event(input_dev, EV_MSC, MSC_SCAN, val);
+			input_report_key(input_dev,
+					keypad->keycodes[val], pressed);
+		}
+		input_sync(keypad->input_dev);
+	}
+
+	memcpy(keypad->row_state, row_state, sizeof(keypad->row_state));
+
+	return key_down;
+}
+
+static irqreturn_t samsung_keypad_irq(int irq, void *dev_id)
+{
+	struct samsung_keypad *keypad = dev_id;
+	unsigned int row_state[SAMSUNG_MAX_COLS];
+	unsigned int val;
+	bool key_down;
+
+	pm_runtime_get_sync(&keypad->pdev->dev);
+
+	do {
+		val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR);
+		/* Clear interrupt. */
+		writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
+
+		samsung_keypad_scan(keypad, row_state);
+
+		key_down = samsung_keypad_report(keypad, row_state);
+		if (key_down)
+			wait_event_timeout(keypad->wait, keypad->stopped,
+					   msecs_to_jiffies(50));
+
+	} while (key_down && !keypad->stopped);
+
+	pm_runtime_put(&keypad->pdev->dev);
+
+	return IRQ_HANDLED;
+}
+
+static void samsung_keypad_start(struct samsung_keypad *keypad)
+{
+	unsigned int val;
+
+	pm_runtime_get_sync(&keypad->pdev->dev);
+
+	/* Tell IRQ thread that it may poll the device. */
+	keypad->stopped = false;
+
+	clk_enable(keypad->clk);
+
+	/* Enable interrupt bits. */
+	val = readl(keypad->base + SAMSUNG_KEYIFCON);
+	val |= SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN;
+	writel(val, keypad->base + SAMSUNG_KEYIFCON);
+
+	/* KEYIFCOL reg clear. */
+	writel(0, keypad->base + SAMSUNG_KEYIFCOL);
+
+	pm_runtime_put(&keypad->pdev->dev);
+}
+
+static void samsung_keypad_stop(struct samsung_keypad *keypad)
+{
+	unsigned int val;
+
+	pm_runtime_get_sync(&keypad->pdev->dev);
+
+	/* Signal IRQ thread to stop polling and disable the handler. */
+	keypad->stopped = true;
+	wake_up(&keypad->wait);
+	disable_irq(keypad->irq);
+
+	/* Clear interrupt. */
+	writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
+
+	/* Disable interrupt bits. */
+	val = readl(keypad->base + SAMSUNG_KEYIFCON);
+	val &= ~(SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN);
+	writel(val, keypad->base + SAMSUNG_KEYIFCON);
+
+	clk_disable(keypad->clk);
+
+	/*
+	 * Now that chip should not generate interrupts we can safely
+	 * re-enable the handler.
+	 */
+	enable_irq(keypad->irq);
+
+	pm_runtime_put(&keypad->pdev->dev);
+}
+
+static int samsung_keypad_open(struct input_dev *input_dev)
+{
+	struct samsung_keypad *keypad = input_get_drvdata(input_dev);
+
+	samsung_keypad_start(keypad);
+
+	return 0;
+}
+
+static void samsung_keypad_close(struct input_dev *input_dev)
+{
+	struct samsung_keypad *keypad = input_get_drvdata(input_dev);
+
+	samsung_keypad_stop(keypad);
+}
+
+#ifdef CONFIG_OF
+static struct samsung_keypad_platdata *
+samsung_keypad_parse_dt(struct device *dev)
+{
+	struct samsung_keypad_platdata *pdata;
+	struct matrix_keymap_data *keymap_data;
+	uint32_t *keymap, num_rows = 0, num_cols = 0;
+	struct device_node *np = dev->of_node, *key_np;
+	unsigned int key_count;
+
+	if (!np) {
+		dev_err(dev, "missing device tree data\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		dev_err(dev, "could not allocate memory for platform data\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	of_property_read_u32(np, "samsung,keypad-num-rows", &num_rows);
+	of_property_read_u32(np, "samsung,keypad-num-columns", &num_cols);
+	if (!num_rows || !num_cols) {
+		dev_err(dev, "number of keypad rows/columns not specified\n");
+		return ERR_PTR(-EINVAL);
+	}
+	pdata->rows = num_rows;
+	pdata->cols = num_cols;
+
+	keymap_data = devm_kzalloc(dev, sizeof(*keymap_data), GFP_KERNEL);
+	if (!keymap_data) {
+		dev_err(dev, "could not allocate memory for keymap data\n");
+		return ERR_PTR(-ENOMEM);
+	}
+	pdata->keymap_data = keymap_data;
+
+	key_count = of_get_child_count(np);
+	keymap_data->keymap_size = key_count;
+	keymap = devm_kzalloc(dev, sizeof(uint32_t) * key_count, GFP_KERNEL);
+	if (!keymap) {
+		dev_err(dev, "could not allocate memory for keymap\n");
+		return ERR_PTR(-ENOMEM);
+	}
+	keymap_data->keymap = keymap;
+
+	for_each_child_of_node(np, key_np) {
+		u32 row, col, key_code;
+		of_property_read_u32(key_np, "keypad,row", &row);
+		of_property_read_u32(key_np, "keypad,column", &col);
+		of_property_read_u32(key_np, "linux,code", &key_code);
+		*keymap++ = KEY(row, col, key_code);
+	}
+
+	if (of_get_property(np, "linux,input-no-autorepeat", NULL))
+		pdata->no_autorepeat = true;
+
+	pdata->wakeup = of_property_read_bool(np, "wakeup-source") ||
+			/* legacy name */
+			of_property_read_bool(np, "linux,input-wakeup");
+
+
+	return pdata;
+}
+#else
+static struct samsung_keypad_platdata *
+samsung_keypad_parse_dt(struct device *dev)
+{
+	dev_err(dev, "no platform data defined\n");
+
+	return ERR_PTR(-EINVAL);
+}
+#endif
+
+static int samsung_keypad_probe(struct platform_device *pdev)
+{
+	const struct samsung_keypad_platdata *pdata;
+	const struct matrix_keymap_data *keymap_data;
+	struct samsung_keypad *keypad;
+	struct resource *res;
+	struct input_dev *input_dev;
+	unsigned int row_shift;
+	unsigned int keymap_size;
+	int error;
+
+	pdata = dev_get_platdata(&pdev->dev);
+	if (!pdata) {
+		pdata = samsung_keypad_parse_dt(&pdev->dev);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+	}
+
+	keymap_data = pdata->keymap_data;
+	if (!keymap_data) {
+		dev_err(&pdev->dev, "no keymap data defined\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->rows || pdata->rows > SAMSUNG_MAX_ROWS)
+		return -EINVAL;
+
+	if (!pdata->cols || pdata->cols > SAMSUNG_MAX_COLS)
+		return -EINVAL;
+
+	/* initialize the gpio */
+	if (pdata->cfg_gpio)
+		pdata->cfg_gpio(pdata->rows, pdata->cols);
+
+	row_shift = get_count_order(pdata->cols);
+	keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]);
+
+	keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad) + keymap_size,
+			      GFP_KERNEL);
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!keypad || !input_dev)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	keypad->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!keypad->base)
+		return -EBUSY;
+
+	keypad->clk = devm_clk_get(&pdev->dev, "keypad");
+	if (IS_ERR(keypad->clk)) {
+		dev_err(&pdev->dev, "failed to get keypad clk\n");
+		return PTR_ERR(keypad->clk);
+	}
+
+	error = clk_prepare(keypad->clk);
+	if (error) {
+		dev_err(&pdev->dev, "keypad clock prepare failed\n");
+		return error;
+	}
+
+	keypad->input_dev = input_dev;
+	keypad->pdev = pdev;
+	keypad->row_shift = row_shift;
+	keypad->rows = pdata->rows;
+	keypad->cols = pdata->cols;
+	keypad->stopped = true;
+	init_waitqueue_head(&keypad->wait);
+
+	if (pdev->dev.of_node)
+		keypad->type = of_device_is_compatible(pdev->dev.of_node,
+					"samsung,s5pv210-keypad");
+	else
+		keypad->type = platform_get_device_id(pdev)->driver_data;
+
+	input_dev->name = pdev->name;
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->dev.parent = &pdev->dev;
+
+	input_dev->open = samsung_keypad_open;
+	input_dev->close = samsung_keypad_close;
+
+	error = matrix_keypad_build_keymap(keymap_data, NULL,
+					   pdata->rows, pdata->cols,
+					   keypad->keycodes, input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to build keymap\n");
+		goto err_unprepare_clk;
+	}
+
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+	if (!pdata->no_autorepeat)
+		__set_bit(EV_REP, input_dev->evbit);
+
+	input_set_drvdata(input_dev, keypad);
+
+	keypad->irq = platform_get_irq(pdev, 0);
+	if (keypad->irq < 0) {
+		error = keypad->irq;
+		goto err_unprepare_clk;
+	}
+
+	error = devm_request_threaded_irq(&pdev->dev, keypad->irq, NULL,
+					  samsung_keypad_irq, IRQF_ONESHOT,
+					  dev_name(&pdev->dev), keypad);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register keypad interrupt\n");
+		goto err_unprepare_clk;
+	}
+
+	device_init_wakeup(&pdev->dev, pdata->wakeup);
+	platform_set_drvdata(pdev, keypad);
+	pm_runtime_enable(&pdev->dev);
+
+	error = input_register_device(keypad->input_dev);
+	if (error)
+		goto err_disable_runtime_pm;
+
+	if (pdev->dev.of_node) {
+		devm_kfree(&pdev->dev, (void *)pdata->keymap_data->keymap);
+		devm_kfree(&pdev->dev, (void *)pdata->keymap_data);
+		devm_kfree(&pdev->dev, (void *)pdata);
+	}
+	return 0;
+
+err_disable_runtime_pm:
+	pm_runtime_disable(&pdev->dev);
+	device_init_wakeup(&pdev->dev, 0);
+err_unprepare_clk:
+	clk_unprepare(keypad->clk);
+	return error;
+}
+
+static int samsung_keypad_remove(struct platform_device *pdev)
+{
+	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
+
+	pm_runtime_disable(&pdev->dev);
+	device_init_wakeup(&pdev->dev, 0);
+
+	input_unregister_device(keypad->input_dev);
+
+	clk_unprepare(keypad->clk);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int samsung_keypad_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
+	unsigned int val;
+	int error;
+
+	if (keypad->stopped)
+		return 0;
+
+	/* This may fail on some SoCs due to lack of controller support */
+	error = enable_irq_wake(keypad->irq);
+	if (!error)
+		keypad->wake_enabled = true;
+
+	val = readl(keypad->base + SAMSUNG_KEYIFCON);
+	val |= SAMSUNG_KEYIFCON_WAKEUPEN;
+	writel(val, keypad->base + SAMSUNG_KEYIFCON);
+
+	clk_disable(keypad->clk);
+
+	return 0;
+}
+
+static int samsung_keypad_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
+	unsigned int val;
+
+	if (keypad->stopped)
+		return 0;
+
+	clk_enable(keypad->clk);
+
+	val = readl(keypad->base + SAMSUNG_KEYIFCON);
+	val &= ~SAMSUNG_KEYIFCON_WAKEUPEN;
+	writel(val, keypad->base + SAMSUNG_KEYIFCON);
+
+	if (keypad->wake_enabled)
+		disable_irq_wake(keypad->irq);
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad,
+					 bool enable)
+{
+	unsigned int val;
+
+	clk_enable(keypad->clk);
+
+	val = readl(keypad->base + SAMSUNG_KEYIFCON);
+	if (enable) {
+		val |= SAMSUNG_KEYIFCON_WAKEUPEN;
+		if (device_may_wakeup(&keypad->pdev->dev))
+			enable_irq_wake(keypad->irq);
+	} else {
+		val &= ~SAMSUNG_KEYIFCON_WAKEUPEN;
+		if (device_may_wakeup(&keypad->pdev->dev))
+			disable_irq_wake(keypad->irq);
+	}
+	writel(val, keypad->base + SAMSUNG_KEYIFCON);
+
+	clk_disable(keypad->clk);
+}
+
+static int samsung_keypad_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
+	struct input_dev *input_dev = keypad->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users)
+		samsung_keypad_stop(keypad);
+
+	samsung_keypad_toggle_wakeup(keypad, true);
+
+	mutex_unlock(&input_dev->mutex);
+
+	return 0;
+}
+
+static int samsung_keypad_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
+	struct input_dev *input_dev = keypad->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+
+	samsung_keypad_toggle_wakeup(keypad, false);
+
+	if (input_dev->users)
+		samsung_keypad_start(keypad);
+
+	mutex_unlock(&input_dev->mutex);
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops samsung_keypad_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(samsung_keypad_suspend, samsung_keypad_resume)
+	SET_RUNTIME_PM_OPS(samsung_keypad_runtime_suspend,
+			   samsung_keypad_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id samsung_keypad_dt_match[] = {
+	{ .compatible = "samsung,s3c6410-keypad" },
+	{ .compatible = "samsung,s5pv210-keypad" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match);
+#endif
+
+static const struct platform_device_id samsung_keypad_driver_ids[] = {
+	{
+		.name		= "samsung-keypad",
+		.driver_data	= KEYPAD_TYPE_SAMSUNG,
+	}, {
+		.name		= "s5pv210-keypad",
+		.driver_data	= KEYPAD_TYPE_S5PV210,
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids);
+
+static struct platform_driver samsung_keypad_driver = {
+	.probe		= samsung_keypad_probe,
+	.remove		= samsung_keypad_remove,
+	.driver		= {
+		.name	= "samsung-keypad",
+		.of_match_table = of_match_ptr(samsung_keypad_dt_match),
+		.pm	= &samsung_keypad_pm_ops,
+	},
+	.id_table	= samsung_keypad_driver_ids,
+};
+module_platform_driver(samsung_keypad_driver);
+
+MODULE_DESCRIPTION("Samsung keypad driver");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
new file mode 100644
index 0000000..7abf03b
--- /dev/null
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -0,0 +1,341 @@
+/*
+ * SuperH KEYSC Keypad Driver
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on gpio_keys.c, Copyright 2005 Phil Blundell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/input/sh_keysc.h>
+#include <linux/bitmap.h>
+#include <linux/pm_runtime.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+static const struct {
+	unsigned char kymd, keyout, keyin;
+} sh_keysc_mode[] = {
+	[SH_KEYSC_MODE_1] = { 0, 6, 5 },
+	[SH_KEYSC_MODE_2] = { 1, 5, 6 },
+	[SH_KEYSC_MODE_3] = { 2, 4, 7 },
+	[SH_KEYSC_MODE_4] = { 3, 6, 6 },
+	[SH_KEYSC_MODE_5] = { 4, 6, 7 },
+	[SH_KEYSC_MODE_6] = { 5, 8, 8 },
+};
+
+struct sh_keysc_priv {
+	void __iomem *iomem_base;
+	DECLARE_BITMAP(last_keys, SH_KEYSC_MAXKEYS);
+	struct input_dev *input;
+	struct sh_keysc_info pdata;
+};
+
+#define KYCR1 0
+#define KYCR2 1
+#define KYINDR 2
+#define KYOUTDR 3
+
+#define KYCR2_IRQ_LEVEL    0x10
+#define KYCR2_IRQ_DISABLED 0x00
+
+static unsigned long sh_keysc_read(struct sh_keysc_priv *p, int reg_nr)
+{
+	return ioread16(p->iomem_base + (reg_nr << 2));
+}
+
+static void sh_keysc_write(struct sh_keysc_priv *p, int reg_nr,
+			   unsigned long value)
+{
+	iowrite16(value, p->iomem_base + (reg_nr << 2));
+}
+
+static void sh_keysc_level_mode(struct sh_keysc_priv *p,
+				unsigned long keys_set)
+{
+	struct sh_keysc_info *pdata = &p->pdata;
+
+	sh_keysc_write(p, KYOUTDR, 0);
+	sh_keysc_write(p, KYCR2, KYCR2_IRQ_LEVEL | (keys_set << 8));
+
+	if (pdata->kycr2_delay)
+		udelay(pdata->kycr2_delay);
+}
+
+static void sh_keysc_map_dbg(struct device *dev, unsigned long *map,
+			     const char *str)
+{
+	int k;
+
+	for (k = 0; k < BITS_TO_LONGS(SH_KEYSC_MAXKEYS); k++)
+		dev_dbg(dev, "%s[%d] 0x%lx\n", str, k, map[k]);
+}
+
+static irqreturn_t sh_keysc_isr(int irq, void *dev_id)
+{
+	struct platform_device *pdev = dev_id;
+	struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
+	struct sh_keysc_info *pdata = &priv->pdata;
+	int keyout_nr = sh_keysc_mode[pdata->mode].keyout;
+	int keyin_nr = sh_keysc_mode[pdata->mode].keyin;
+	DECLARE_BITMAP(keys, SH_KEYSC_MAXKEYS);
+	DECLARE_BITMAP(keys0, SH_KEYSC_MAXKEYS);
+	DECLARE_BITMAP(keys1, SH_KEYSC_MAXKEYS);
+	unsigned char keyin_set, tmp;
+	int i, k, n;
+
+	dev_dbg(&pdev->dev, "isr!\n");
+
+	bitmap_fill(keys1, SH_KEYSC_MAXKEYS);
+	bitmap_zero(keys0, SH_KEYSC_MAXKEYS);
+
+	do {
+		bitmap_zero(keys, SH_KEYSC_MAXKEYS);
+		keyin_set = 0;
+
+		sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED);
+
+		for (i = 0; i < keyout_nr; i++) {
+			n = keyin_nr * i;
+
+			/* drive one KEYOUT pin low, read KEYIN pins */
+			sh_keysc_write(priv, KYOUTDR, 0xffff ^ (3 << (i * 2)));
+			udelay(pdata->delay);
+			tmp = sh_keysc_read(priv, KYINDR);
+
+			/* set bit if key press has been detected */
+			for (k = 0; k < keyin_nr; k++) {
+				if (tmp & (1 << k))
+					__set_bit(n + k, keys);
+			}
+
+			/* keep track of which KEYIN bits that have been set */
+			keyin_set |= tmp ^ ((1 << keyin_nr) - 1);
+		}
+
+		sh_keysc_level_mode(priv, keyin_set);
+
+		bitmap_complement(keys, keys, SH_KEYSC_MAXKEYS);
+		bitmap_and(keys1, keys1, keys, SH_KEYSC_MAXKEYS);
+		bitmap_or(keys0, keys0, keys, SH_KEYSC_MAXKEYS);
+
+		sh_keysc_map_dbg(&pdev->dev, keys, "keys");
+
+	} while (sh_keysc_read(priv, KYCR2) & 0x01);
+
+	sh_keysc_map_dbg(&pdev->dev, priv->last_keys, "last_keys");
+	sh_keysc_map_dbg(&pdev->dev, keys0, "keys0");
+	sh_keysc_map_dbg(&pdev->dev, keys1, "keys1");
+
+	for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
+		k = pdata->keycodes[i];
+		if (!k)
+			continue;
+
+		if (test_bit(i, keys0) == test_bit(i, priv->last_keys))
+			continue;
+
+		if (test_bit(i, keys1) || test_bit(i, keys0)) {
+			input_event(priv->input, EV_KEY, k, 1);
+			__set_bit(i, priv->last_keys);
+		}
+
+		if (!test_bit(i, keys1)) {
+			input_event(priv->input, EV_KEY, k, 0);
+			__clear_bit(i, priv->last_keys);
+		}
+
+	}
+	input_sync(priv->input);
+
+	return IRQ_HANDLED;
+}
+
+static int sh_keysc_probe(struct platform_device *pdev)
+{
+	struct sh_keysc_priv *priv;
+	struct sh_keysc_info *pdata;
+	struct resource *res;
+	struct input_dev *input;
+	int i;
+	int irq, error;
+
+	if (!dev_get_platdata(&pdev->dev)) {
+		dev_err(&pdev->dev, "no platform data defined\n");
+		error = -EINVAL;
+		goto err0;
+	}
+
+	error = -ENXIO;
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to get I/O memory\n");
+		goto err0;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to get irq\n");
+		goto err0;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL) {
+		dev_err(&pdev->dev, "failed to allocate driver data\n");
+		error = -ENOMEM;
+		goto err0;
+	}
+
+	platform_set_drvdata(pdev, priv);
+	memcpy(&priv->pdata, dev_get_platdata(&pdev->dev), sizeof(priv->pdata));
+	pdata = &priv->pdata;
+
+	priv->iomem_base = ioremap_nocache(res->start, resource_size(res));
+	if (priv->iomem_base == NULL) {
+		dev_err(&pdev->dev, "failed to remap I/O memory\n");
+		error = -ENXIO;
+		goto err1;
+	}
+
+	priv->input = input_allocate_device();
+	if (!priv->input) {
+		dev_err(&pdev->dev, "failed to allocate input device\n");
+		error = -ENOMEM;
+		goto err2;
+	}
+
+	input = priv->input;
+	input->evbit[0] = BIT_MASK(EV_KEY);
+
+	input->name = pdev->name;
+	input->phys = "sh-keysc-keys/input0";
+	input->dev.parent = &pdev->dev;
+
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0100;
+
+	input->keycode = pdata->keycodes;
+	input->keycodesize = sizeof(pdata->keycodes[0]);
+	input->keycodemax = ARRAY_SIZE(pdata->keycodes);
+
+	error = request_threaded_irq(irq, NULL, sh_keysc_isr, IRQF_ONESHOT,
+				     dev_name(&pdev->dev), pdev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to request IRQ\n");
+		goto err3;
+	}
+
+	for (i = 0; i < SH_KEYSC_MAXKEYS; i++)
+		__set_bit(pdata->keycodes[i], input->keybit);
+	__clear_bit(KEY_RESERVED, input->keybit);
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		goto err4;
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_get_sync(&pdev->dev);
+
+	sh_keysc_write(priv, KYCR1, (sh_keysc_mode[pdata->mode].kymd << 8) |
+		       pdata->scan_timing);
+	sh_keysc_level_mode(priv, 0);
+
+	device_init_wakeup(&pdev->dev, 1);
+
+	return 0;
+
+ err4:
+	free_irq(irq, pdev);
+ err3:
+	input_free_device(input);
+ err2:
+	iounmap(priv->iomem_base);
+ err1:
+	kfree(priv);
+ err0:
+	return error;
+}
+
+static int sh_keysc_remove(struct platform_device *pdev)
+{
+	struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
+
+	sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED);
+
+	input_unregister_device(priv->input);
+	free_irq(platform_get_irq(pdev, 0), pdev);
+	iounmap(priv->iomem_base);
+
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	kfree(priv);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sh_keysc_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
+	int irq = platform_get_irq(pdev, 0);
+	unsigned short value;
+
+	value = sh_keysc_read(priv, KYCR1);
+
+	if (device_may_wakeup(dev)) {
+		sh_keysc_write(priv, KYCR1, value | 0x80);
+		enable_irq_wake(irq);
+	} else {
+		sh_keysc_write(priv, KYCR1, value & ~0x80);
+		pm_runtime_put_sync(dev);
+	}
+
+	return 0;
+}
+
+static int sh_keysc_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	int irq = platform_get_irq(pdev, 0);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(irq);
+	else
+		pm_runtime_get_sync(dev);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(sh_keysc_dev_pm_ops,
+			 sh_keysc_suspend, sh_keysc_resume);
+
+static struct platform_driver sh_keysc_device_driver = {
+	.probe		= sh_keysc_probe,
+	.remove		= sh_keysc_remove,
+	.driver		= {
+		.name	= "sh_keysc",
+		.pm	= &sh_keysc_dev_pm_ops,
+	}
+};
+module_platform_driver(sh_keysc_device_driver);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("SuperH KEYSC Keypad Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
new file mode 100644
index 0000000..9adf13a
--- /dev/null
+++ b/drivers/input/keyboard/snvs_pwrkey.c
@@ -0,0 +1,226 @@
+/*
+ * Driver for the IMX SNVS ON/OFF Power Key
+ * Copyright (C) 2015 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#define SNVS_LPSR_REG	0x4C	/* LP Status Register */
+#define SNVS_LPCR_REG	0x38	/* LP Control Register */
+#define SNVS_HPSR_REG	0x14
+#define SNVS_HPSR_BTN	BIT(6)
+#define SNVS_LPSR_SPO	BIT(18)
+#define SNVS_LPCR_DEP_EN BIT(5)
+
+#define DEBOUNCE_TIME 30
+#define REPEAT_INTERVAL 60
+
+struct pwrkey_drv_data {
+	struct regmap *snvs;
+	int irq;
+	int keycode;
+	int keystate;  /* 1:pressed */
+	int wakeup;
+	struct timer_list check_timer;
+	struct input_dev *input;
+};
+
+static void imx_imx_snvs_check_for_events(unsigned long data)
+{
+	struct pwrkey_drv_data *pdata = (struct pwrkey_drv_data *) data;
+	struct input_dev *input = pdata->input;
+	u32 state;
+
+	regmap_read(pdata->snvs, SNVS_HPSR_REG, &state);
+	state = state & SNVS_HPSR_BTN ? 1 : 0;
+
+	/* only report new event if status changed */
+	if (state ^ pdata->keystate) {
+		pdata->keystate = state;
+		input_event(input, EV_KEY, pdata->keycode, state);
+		input_sync(input);
+		pm_relax(pdata->input->dev.parent);
+	}
+
+	/* repeat check if pressed long */
+	if (state) {
+		mod_timer(&pdata->check_timer,
+			  jiffies + msecs_to_jiffies(REPEAT_INTERVAL));
+	}
+}
+
+static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id)
+{
+	struct platform_device *pdev = dev_id;
+	struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+	u32 lp_status;
+
+	pm_wakeup_event(pdata->input->dev.parent, 0);
+
+	regmap_read(pdata->snvs, SNVS_LPSR_REG, &lp_status);
+	if (lp_status & SNVS_LPSR_SPO)
+		mod_timer(&pdata->check_timer, jiffies + msecs_to_jiffies(DEBOUNCE_TIME));
+
+	/* clear SPO status */
+	regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
+
+	return IRQ_HANDLED;
+}
+
+static void imx_snvs_pwrkey_act(void *pdata)
+{
+	struct pwrkey_drv_data *pd = pdata;
+
+	del_timer_sync(&pd->check_timer);
+}
+
+static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
+{
+	struct pwrkey_drv_data *pdata = NULL;
+	struct input_dev *input = NULL;
+	struct device_node *np;
+	int error;
+
+	/* Get SNVS register Page */
+	np = pdev->dev.of_node;
+	if (!np)
+		return -ENODEV;
+
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap");
+	if (!pdata->snvs) {
+		dev_err(&pdev->dev, "Can't get snvs syscon\n");
+		return -ENODEV;
+	}
+
+	if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
+		pdata->keycode = KEY_POWER;
+		dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
+	}
+
+	pdata->wakeup = of_property_read_bool(np, "wakeup-source");
+
+	pdata->irq = platform_get_irq(pdev, 0);
+	if (pdata->irq < 0) {
+		dev_err(&pdev->dev, "no irq defined in platform data\n");
+		return -EINVAL;
+	}
+
+	regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_DEP_EN, SNVS_LPCR_DEP_EN);
+
+	/* clear the unexpected interrupt before driver ready */
+	regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
+
+	setup_timer(&pdata->check_timer,
+		    imx_imx_snvs_check_for_events, (unsigned long) pdata);
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input) {
+		dev_err(&pdev->dev, "failed to allocate the input device\n");
+		return -ENOMEM;
+	}
+
+	input->name = pdev->name;
+	input->phys = "snvs-pwrkey/input0";
+	input->id.bustype = BUS_HOST;
+
+	input_set_capability(input, EV_KEY, pdata->keycode);
+
+	/* input customer action to cancel release timer */
+	error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register remove action\n");
+		return error;
+	}
+
+	error = devm_request_irq(&pdev->dev, pdata->irq,
+			       imx_snvs_pwrkey_interrupt,
+			       0, pdev->name, pdev);
+
+	if (error) {
+		dev_err(&pdev->dev, "interrupt not available.\n");
+		return error;
+	}
+
+	error = input_register_device(input);
+	if (error < 0) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		input_free_device(input);
+		return error;
+	}
+
+	pdata->input = input;
+	platform_set_drvdata(pdev, pdata);
+
+	device_init_wakeup(&pdev->dev, pdata->wakeup);
+
+	return 0;
+}
+
+static int imx_snvs_pwrkey_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(&pdev->dev))
+		enable_irq_wake(pdata->irq);
+
+	return 0;
+}
+
+static int imx_snvs_pwrkey_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(&pdev->dev))
+		disable_irq_wake(pdata->irq);
+
+	return 0;
+}
+
+static const struct of_device_id imx_snvs_pwrkey_ids[] = {
+	{ .compatible = "fsl,sec-v4.0-pwrkey" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_snvs_pwrkey_ids);
+
+static SIMPLE_DEV_PM_OPS(imx_snvs_pwrkey_pm_ops, imx_snvs_pwrkey_suspend,
+				imx_snvs_pwrkey_resume);
+
+static struct platform_driver imx_snvs_pwrkey_driver = {
+	.driver = {
+		.name = "snvs_pwrkey",
+		.pm     = &imx_snvs_pwrkey_pm_ops,
+		.of_match_table = imx_snvs_pwrkey_ids,
+	},
+	.probe = imx_snvs_pwrkey_probe,
+};
+module_platform_driver(imx_snvs_pwrkey_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("i.MX snvs power key Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c
new file mode 100644
index 0000000..623d451
--- /dev/null
+++ b/drivers/input/keyboard/spear-keyboard.c
@@ -0,0 +1,396 @@
+/*
+ * SPEAr Keyboard Driver
+ * Based on omap-keypad driver
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Rajeev Kumar <rajeevkumar.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_wakeup.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/platform_data/keyboard-spear.h>
+
+/* Keyboard Registers */
+#define MODE_CTL_REG	0x00
+#define STATUS_REG	0x0C
+#define DATA_REG	0x10
+#define INTR_MASK	0x54
+
+/* Register Values */
+#define NUM_ROWS	16
+#define NUM_COLS	16
+#define MODE_CTL_PCLK_FREQ_SHIFT	9
+#define MODE_CTL_PCLK_FREQ_MSK		0x7F
+
+#define MODE_CTL_KEYBOARD	(0x2 << 0)
+#define MODE_CTL_SCAN_RATE_10	(0x0 << 2)
+#define MODE_CTL_SCAN_RATE_20	(0x1 << 2)
+#define MODE_CTL_SCAN_RATE_40	(0x2 << 2)
+#define MODE_CTL_SCAN_RATE_80	(0x3 << 2)
+#define MODE_CTL_KEYNUM_SHIFT	6
+#define MODE_CTL_START_SCAN	(0x1 << 8)
+
+#define STATUS_DATA_AVAIL	(0x1 << 1)
+
+#define DATA_ROW_MASK		0xF0
+#define DATA_COLUMN_MASK	0x0F
+
+#define ROW_SHIFT		4
+
+struct spear_kbd {
+	struct input_dev *input;
+	void __iomem *io_base;
+	struct clk *clk;
+	unsigned int irq;
+	unsigned int mode;
+	unsigned int suspended_rate;
+	unsigned short last_key;
+	unsigned short keycodes[NUM_ROWS * NUM_COLS];
+	bool rep;
+	bool irq_wake_enabled;
+	u32 mode_ctl_reg;
+};
+
+static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id)
+{
+	struct spear_kbd *kbd = dev_id;
+	struct input_dev *input = kbd->input;
+	unsigned int key;
+	u32 sts, val;
+
+	sts = readl_relaxed(kbd->io_base + STATUS_REG);
+	if (!(sts & STATUS_DATA_AVAIL))
+		return IRQ_NONE;
+
+	if (kbd->last_key != KEY_RESERVED) {
+		input_report_key(input, kbd->last_key, 0);
+		kbd->last_key = KEY_RESERVED;
+	}
+
+	/* following reads active (row, col) pair */
+	val = readl_relaxed(kbd->io_base + DATA_REG) &
+		(DATA_ROW_MASK | DATA_COLUMN_MASK);
+	key = kbd->keycodes[val];
+
+	input_event(input, EV_MSC, MSC_SCAN, val);
+	input_report_key(input, key, 1);
+	input_sync(input);
+
+	kbd->last_key = key;
+
+	/* clear interrupt */
+	writel_relaxed(0, kbd->io_base + STATUS_REG);
+
+	return IRQ_HANDLED;
+}
+
+static int spear_kbd_open(struct input_dev *dev)
+{
+	struct spear_kbd *kbd = input_get_drvdata(dev);
+	int error;
+	u32 val;
+
+	kbd->last_key = KEY_RESERVED;
+
+	error = clk_enable(kbd->clk);
+	if (error)
+		return error;
+
+	/* keyboard rate to be programmed is input clock (in MHz) - 1 */
+	val = clk_get_rate(kbd->clk) / 1000000 - 1;
+	val = (val & MODE_CTL_PCLK_FREQ_MSK) << MODE_CTL_PCLK_FREQ_SHIFT;
+
+	/* program keyboard */
+	val = MODE_CTL_SCAN_RATE_80 | MODE_CTL_KEYBOARD | val |
+		(kbd->mode << MODE_CTL_KEYNUM_SHIFT);
+	writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
+	writel_relaxed(1, kbd->io_base + STATUS_REG);
+
+	/* start key scan */
+	val = readl_relaxed(kbd->io_base + MODE_CTL_REG);
+	val |= MODE_CTL_START_SCAN;
+	writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
+
+	return 0;
+}
+
+static void spear_kbd_close(struct input_dev *dev)
+{
+	struct spear_kbd *kbd = input_get_drvdata(dev);
+	u32 val;
+
+	/* stop key scan */
+	val = readl_relaxed(kbd->io_base + MODE_CTL_REG);
+	val &= ~MODE_CTL_START_SCAN;
+	writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
+
+	clk_disable(kbd->clk);
+
+	kbd->last_key = KEY_RESERVED;
+}
+
+#ifdef CONFIG_OF
+static int spear_kbd_parse_dt(struct platform_device *pdev,
+                                        struct spear_kbd *kbd)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int error;
+	u32 val, suspended_rate;
+
+	if (!np) {
+		dev_err(&pdev->dev, "Missing DT data\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_bool(np, "autorepeat"))
+		kbd->rep = true;
+
+	if (of_property_read_u32(np, "suspended_rate", &suspended_rate))
+		kbd->suspended_rate = suspended_rate;
+
+	error = of_property_read_u32(np, "st,mode", &val);
+	if (error) {
+		dev_err(&pdev->dev, "DT: Invalid or missing mode\n");
+		return error;
+	}
+
+	kbd->mode = val;
+	return 0;
+}
+#else
+static inline int spear_kbd_parse_dt(struct platform_device *pdev,
+				     struct spear_kbd *kbd)
+{
+	return -ENOSYS;
+}
+#endif
+
+static int spear_kbd_probe(struct platform_device *pdev)
+{
+	struct kbd_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	const struct matrix_keymap_data *keymap = pdata ? pdata->keymap : NULL;
+	struct spear_kbd *kbd;
+	struct input_dev *input_dev;
+	struct resource *res;
+	int irq;
+	int error;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "not able to get irq for the device\n");
+		return irq;
+	}
+
+	kbd = devm_kzalloc(&pdev->dev, sizeof(*kbd), GFP_KERNEL);
+	if (!kbd) {
+		dev_err(&pdev->dev, "not enough memory for driver data\n");
+		return -ENOMEM;
+	}
+
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!input_dev) {
+		dev_err(&pdev->dev, "unable to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	kbd->input = input_dev;
+	kbd->irq = irq;
+
+	if (!pdata) {
+		error = spear_kbd_parse_dt(pdev, kbd);
+		if (error)
+			return error;
+	} else {
+		kbd->mode = pdata->mode;
+		kbd->rep = pdata->rep;
+		kbd->suspended_rate = pdata->suspended_rate;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	kbd->io_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(kbd->io_base))
+		return PTR_ERR(kbd->io_base);
+
+	kbd->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(kbd->clk))
+		return PTR_ERR(kbd->clk);
+
+	input_dev->name = "Spear Keyboard";
+	input_dev->phys = "keyboard/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->open = spear_kbd_open;
+	input_dev->close = spear_kbd_close;
+
+	error = matrix_keypad_build_keymap(keymap, NULL, NUM_ROWS, NUM_COLS,
+					   kbd->keycodes, input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "Failed to build keymap\n");
+		return error;
+	}
+
+	if (kbd->rep)
+		__set_bit(EV_REP, input_dev->evbit);
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+
+	input_set_drvdata(input_dev, kbd);
+
+	error = devm_request_irq(&pdev->dev, irq, spear_kbd_interrupt, 0,
+			"keyboard", kbd);
+	if (error) {
+		dev_err(&pdev->dev, "request_irq failed\n");
+		return error;
+	}
+
+	error = clk_prepare(kbd->clk);
+	if (error)
+		return error;
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "Unable to register keyboard device\n");
+		clk_unprepare(kbd->clk);
+		return error;
+	}
+
+	device_init_wakeup(&pdev->dev, 1);
+	platform_set_drvdata(pdev, kbd);
+
+	return 0;
+}
+
+static int spear_kbd_remove(struct platform_device *pdev)
+{
+	struct spear_kbd *kbd = platform_get_drvdata(pdev);
+
+	input_unregister_device(kbd->input);
+	clk_unprepare(kbd->clk);
+
+	device_init_wakeup(&pdev->dev, 0);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int spear_kbd_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct spear_kbd *kbd = platform_get_drvdata(pdev);
+	struct input_dev *input_dev = kbd->input;
+	unsigned int rate = 0, mode_ctl_reg, val;
+
+	mutex_lock(&input_dev->mutex);
+
+	/* explicitly enable clock as we may program device */
+	clk_enable(kbd->clk);
+
+	mode_ctl_reg = readl_relaxed(kbd->io_base + MODE_CTL_REG);
+
+	if (device_may_wakeup(&pdev->dev)) {
+		if (!enable_irq_wake(kbd->irq))
+			kbd->irq_wake_enabled = true;
+
+		/*
+		 * reprogram the keyboard operating frequency as on some
+		 * platform it may change during system suspended
+		 */
+		if (kbd->suspended_rate)
+			rate = kbd->suspended_rate / 1000000 - 1;
+		else
+			rate = clk_get_rate(kbd->clk) / 1000000 - 1;
+
+		val = mode_ctl_reg &
+			~(MODE_CTL_PCLK_FREQ_MSK << MODE_CTL_PCLK_FREQ_SHIFT);
+		val |= (rate & MODE_CTL_PCLK_FREQ_MSK)
+			<< MODE_CTL_PCLK_FREQ_SHIFT;
+		writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
+
+	} else {
+		if (input_dev->users) {
+			writel_relaxed(mode_ctl_reg & ~MODE_CTL_START_SCAN,
+					kbd->io_base + MODE_CTL_REG);
+			clk_disable(kbd->clk);
+		}
+	}
+
+	/* store current configuration */
+	if (input_dev->users)
+		kbd->mode_ctl_reg = mode_ctl_reg;
+
+	/* restore previous clk state */
+	clk_disable(kbd->clk);
+
+	mutex_unlock(&input_dev->mutex);
+
+	return 0;
+}
+
+static int spear_kbd_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct spear_kbd *kbd = platform_get_drvdata(pdev);
+	struct input_dev *input_dev = kbd->input;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (device_may_wakeup(&pdev->dev)) {
+		if (kbd->irq_wake_enabled) {
+			kbd->irq_wake_enabled = false;
+			disable_irq_wake(kbd->irq);
+		}
+	} else {
+		if (input_dev->users)
+			clk_enable(kbd->clk);
+	}
+
+	/* restore current configuration */
+	if (input_dev->users)
+		writel_relaxed(kbd->mode_ctl_reg, kbd->io_base + MODE_CTL_REG);
+
+	mutex_unlock(&input_dev->mutex);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id spear_kbd_id_table[] = {
+	{ .compatible = "st,spear300-kbd" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, spear_kbd_id_table);
+#endif
+
+static struct platform_driver spear_kbd_driver = {
+	.probe		= spear_kbd_probe,
+	.remove		= spear_kbd_remove,
+	.driver		= {
+		.name	= "keyboard",
+		.pm	= &spear_kbd_pm_ops,
+		.of_match_table = of_match_ptr(spear_kbd_id_table),
+	},
+};
+module_platform_driver(spear_kbd_driver);
+
+MODULE_AUTHOR("Rajeev Kumar");
+MODULE_DESCRIPTION("SPEAr Keyboard Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/st-keyscan.c b/drivers/input/keyboard/st-keyscan.c
new file mode 100644
index 0000000..de7be4f
--- /dev/null
+++ b/drivers/input/keyboard/st-keyscan.c
@@ -0,0 +1,276 @@
+/*
+ * STMicroelectronics Key Scanning driver
+ *
+ * Copyright (c) 2014 STMicroelectonics Ltd.
+ * Author: Stuart Menefy <stuart.menefy@st.com>
+ *
+ * Based on sh_keysc.c, copyright 2008 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/input/matrix_keypad.h>
+
+#define ST_KEYSCAN_MAXKEYS 16
+
+#define KEYSCAN_CONFIG_OFF		0x0
+#define KEYSCAN_CONFIG_ENABLE		0x1
+#define KEYSCAN_DEBOUNCE_TIME_OFF	0x4
+#define KEYSCAN_MATRIX_STATE_OFF	0x8
+#define KEYSCAN_MATRIX_DIM_OFF		0xc
+#define KEYSCAN_MATRIX_DIM_X_SHIFT	0x0
+#define KEYSCAN_MATRIX_DIM_Y_SHIFT	0x2
+
+struct st_keyscan {
+	void __iomem *base;
+	int irq;
+	struct clk *clk;
+	struct input_dev *input_dev;
+	unsigned long last_state;
+	unsigned int n_rows;
+	unsigned int n_cols;
+	unsigned int debounce_us;
+};
+
+static irqreturn_t keyscan_isr(int irq, void *dev_id)
+{
+	struct st_keyscan *keypad = dev_id;
+	unsigned short *keycode = keypad->input_dev->keycode;
+	unsigned long state, change;
+	int bit_nr;
+
+	state = readl(keypad->base + KEYSCAN_MATRIX_STATE_OFF) & 0xffff;
+	change = keypad->last_state ^ state;
+	keypad->last_state = state;
+
+	for_each_set_bit(bit_nr, &change, BITS_PER_LONG)
+		input_report_key(keypad->input_dev,
+				 keycode[bit_nr], state & BIT(bit_nr));
+
+	input_sync(keypad->input_dev);
+
+	return IRQ_HANDLED;
+}
+
+static int keyscan_start(struct st_keyscan *keypad)
+{
+	int error;
+
+	error = clk_enable(keypad->clk);
+	if (error)
+		return error;
+
+	writel(keypad->debounce_us * (clk_get_rate(keypad->clk) / 1000000),
+	       keypad->base + KEYSCAN_DEBOUNCE_TIME_OFF);
+
+	writel(((keypad->n_cols - 1) << KEYSCAN_MATRIX_DIM_X_SHIFT) |
+	       ((keypad->n_rows - 1) << KEYSCAN_MATRIX_DIM_Y_SHIFT),
+	       keypad->base + KEYSCAN_MATRIX_DIM_OFF);
+
+	writel(KEYSCAN_CONFIG_ENABLE, keypad->base + KEYSCAN_CONFIG_OFF);
+
+	return 0;
+}
+
+static void keyscan_stop(struct st_keyscan *keypad)
+{
+	writel(0, keypad->base + KEYSCAN_CONFIG_OFF);
+
+	clk_disable(keypad->clk);
+}
+
+static int keyscan_open(struct input_dev *dev)
+{
+	struct st_keyscan *keypad = input_get_drvdata(dev);
+
+	return keyscan_start(keypad);
+}
+
+static void keyscan_close(struct input_dev *dev)
+{
+	struct st_keyscan *keypad = input_get_drvdata(dev);
+
+	keyscan_stop(keypad);
+}
+
+static int keypad_matrix_key_parse_dt(struct st_keyscan *keypad_data)
+{
+	struct device *dev = keypad_data->input_dev->dev.parent;
+	struct device_node *np = dev->of_node;
+	int error;
+
+	error = matrix_keypad_parse_of_params(dev, &keypad_data->n_rows,
+					      &keypad_data->n_cols);
+	if (error) {
+		dev_err(dev, "failed to parse keypad params\n");
+		return error;
+	}
+
+	of_property_read_u32(np, "st,debounce-us", &keypad_data->debounce_us);
+
+	dev_dbg(dev, "n_rows=%d n_col=%d debounce=%d\n",
+		keypad_data->n_rows, keypad_data->n_cols,
+		keypad_data->debounce_us);
+
+	return 0;
+}
+
+static int keyscan_probe(struct platform_device *pdev)
+{
+	struct st_keyscan *keypad_data;
+	struct input_dev *input_dev;
+	struct resource *res;
+	int error;
+
+	if (!pdev->dev.of_node) {
+		dev_err(&pdev->dev, "no DT data present\n");
+		return -EINVAL;
+	}
+
+	keypad_data = devm_kzalloc(&pdev->dev, sizeof(*keypad_data),
+				   GFP_KERNEL);
+	if (!keypad_data)
+		return -ENOMEM;
+
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!input_dev) {
+		dev_err(&pdev->dev, "failed to allocate the input device\n");
+		return -ENOMEM;
+	}
+
+	input_dev->name = pdev->name;
+	input_dev->phys = "keyscan-keys/input0";
+	input_dev->dev.parent = &pdev->dev;
+	input_dev->open = keyscan_open;
+	input_dev->close = keyscan_close;
+
+	input_dev->id.bustype = BUS_HOST;
+
+	error = keypad_matrix_key_parse_dt(keypad_data);
+	if (error)
+		return error;
+
+	error = matrix_keypad_build_keymap(NULL, NULL,
+					   keypad_data->n_rows,
+					   keypad_data->n_cols,
+					   NULL, input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to build keymap\n");
+		return error;
+	}
+
+	input_set_drvdata(input_dev, keypad_data);
+
+	keypad_data->input_dev = input_dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	keypad_data->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(keypad_data->base))
+		return PTR_ERR(keypad_data->base);
+
+	keypad_data->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(keypad_data->clk)) {
+		dev_err(&pdev->dev, "cannot get clock\n");
+		return PTR_ERR(keypad_data->clk);
+	}
+
+	error = clk_enable(keypad_data->clk);
+	if (error) {
+		dev_err(&pdev->dev, "failed to enable clock\n");
+		return error;
+	}
+
+	keyscan_stop(keypad_data);
+
+	keypad_data->irq = platform_get_irq(pdev, 0);
+	if (keypad_data->irq < 0) {
+		dev_err(&pdev->dev, "no IRQ specified\n");
+		return -EINVAL;
+	}
+
+	error = devm_request_irq(&pdev->dev, keypad_data->irq, keyscan_isr, 0,
+				 pdev->name, keypad_data);
+	if (error) {
+		dev_err(&pdev->dev, "failed to request IRQ\n");
+		return error;
+	}
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		return error;
+	}
+
+	platform_set_drvdata(pdev, keypad_data);
+
+	device_set_wakeup_capable(&pdev->dev, 1);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int keyscan_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct st_keyscan *keypad = platform_get_drvdata(pdev);
+	struct input_dev *input = keypad->input_dev;
+
+	mutex_lock(&input->mutex);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(keypad->irq);
+	else if (input->users)
+		keyscan_stop(keypad);
+
+	mutex_unlock(&input->mutex);
+	return 0;
+}
+
+static int keyscan_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct st_keyscan *keypad = platform_get_drvdata(pdev);
+	struct input_dev *input = keypad->input_dev;
+	int retval = 0;
+
+	mutex_lock(&input->mutex);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(keypad->irq);
+	else if (input->users)
+		retval = keyscan_start(keypad);
+
+	mutex_unlock(&input->mutex);
+	return retval;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(keyscan_dev_pm_ops, keyscan_suspend, keyscan_resume);
+
+static const struct of_device_id keyscan_of_match[] = {
+	{ .compatible = "st,sti-keyscan" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, keyscan_of_match);
+
+static struct platform_driver keyscan_device_driver = {
+	.probe		= keyscan_probe,
+	.driver		= {
+		.name	= "st-keyscan",
+		.pm	= &keyscan_dev_pm_ops,
+		.of_match_table = of_match_ptr(keyscan_of_match),
+	}
+};
+
+module_platform_driver(keyscan_device_driver);
+
+MODULE_AUTHOR("Stuart Menefy <stuart.menefy@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics keyscan device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c
new file mode 100644
index 0000000..fe6e3f2
--- /dev/null
+++ b/drivers/input/keyboard/stmpe-keypad.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/mfd/stmpe.h>
+
+/* These are at the same addresses in all STMPE variants */
+#define STMPE_KPC_COL			0x60
+#define STMPE_KPC_ROW_MSB		0x61
+#define STMPE_KPC_ROW_LSB		0x62
+#define STMPE_KPC_CTRL_MSB		0x63
+#define STMPE_KPC_CTRL_LSB		0x64
+#define STMPE_KPC_COMBI_KEY_0		0x65
+#define STMPE_KPC_COMBI_KEY_1		0x66
+#define STMPE_KPC_COMBI_KEY_2		0x67
+#define STMPE_KPC_DATA_BYTE0		0x68
+#define STMPE_KPC_DATA_BYTE1		0x69
+#define STMPE_KPC_DATA_BYTE2		0x6a
+#define STMPE_KPC_DATA_BYTE3		0x6b
+#define STMPE_KPC_DATA_BYTE4		0x6c
+
+#define STMPE_KPC_CTRL_LSB_SCAN		(0x1 << 0)
+#define STMPE_KPC_CTRL_LSB_DEBOUNCE	(0x7f << 1)
+#define STMPE_KPC_CTRL_MSB_SCAN_COUNT	(0xf << 4)
+
+#define STMPE_KPC_ROW_MSB_ROWS		0xff
+
+#define STMPE_KPC_DATA_UP		(0x1 << 7)
+#define STMPE_KPC_DATA_ROW		(0xf << 3)
+#define STMPE_KPC_DATA_COL		(0x7 << 0)
+#define STMPE_KPC_DATA_NOKEY_MASK	0x78
+
+#define STMPE_KEYPAD_MAX_DEBOUNCE	127
+#define STMPE_KEYPAD_MAX_SCAN_COUNT	15
+
+#define STMPE_KEYPAD_MAX_ROWS		8
+#define STMPE_KEYPAD_MAX_COLS		8
+#define STMPE_KEYPAD_ROW_SHIFT		3
+#define STMPE_KEYPAD_KEYMAP_MAX_SIZE \
+	(STMPE_KEYPAD_MAX_ROWS * STMPE_KEYPAD_MAX_COLS)
+
+/**
+ * struct stmpe_keypad_variant - model-specific attributes
+ * @auto_increment: whether the KPC_DATA_BYTE register address
+ *		    auto-increments on multiple read
+ * @set_pullup: whether the pins need to have their pull-ups set
+ * @num_data: number of data bytes
+ * @num_normal_data: number of normal keys' data bytes
+ * @max_cols: maximum number of columns supported
+ * @max_rows: maximum number of rows supported
+ * @col_gpios: bitmask of gpios which can be used for columns
+ * @row_gpios: bitmask of gpios which can be used for rows
+ */
+struct stmpe_keypad_variant {
+	bool		auto_increment;
+	bool		set_pullup;
+	int		num_data;
+	int		num_normal_data;
+	int		max_cols;
+	int		max_rows;
+	unsigned int	col_gpios;
+	unsigned int	row_gpios;
+};
+
+static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
+	[STMPE1601] = {
+		.auto_increment		= true,
+		.num_data		= 5,
+		.num_normal_data	= 3,
+		.max_cols		= 8,
+		.max_rows		= 8,
+		.col_gpios		= 0x000ff,	/* GPIO 0 - 7 */
+		.row_gpios		= 0x0ff00,	/* GPIO 8 - 15 */
+	},
+	[STMPE2401] = {
+		.auto_increment		= false,
+		.set_pullup		= true,
+		.num_data		= 3,
+		.num_normal_data	= 2,
+		.max_cols		= 8,
+		.max_rows		= 12,
+		.col_gpios		= 0x0000ff,	/* GPIO 0 - 7*/
+		.row_gpios		= 0x1f7f00,	/* GPIO 8-14, 16-20 */
+	},
+	[STMPE2403] = {
+		.auto_increment		= true,
+		.set_pullup		= true,
+		.num_data		= 5,
+		.num_normal_data	= 3,
+		.max_cols		= 8,
+		.max_rows		= 12,
+		.col_gpios		= 0x0000ff,	/* GPIO 0 - 7*/
+		.row_gpios		= 0x1fef00,	/* GPIO 8-14, 16-20 */
+	},
+};
+
+/**
+ * struct stmpe_keypad - STMPE keypad state container
+ * @stmpe: pointer to parent STMPE device
+ * @input: spawned input device
+ * @variant: STMPE variant
+ * @debounce_ms: debounce interval, in ms.  Maximum is
+ *		 %STMPE_KEYPAD_MAX_DEBOUNCE.
+ * @scan_count: number of key scanning cycles to confirm key data.
+ *		Maximum is %STMPE_KEYPAD_MAX_SCAN_COUNT.
+ * @no_autorepeat: disable key autorepeat
+ * @rows: bitmask for the rows
+ * @cols: bitmask for the columns
+ * @keymap: the keymap
+ */
+struct stmpe_keypad {
+	struct stmpe *stmpe;
+	struct input_dev *input;
+	const struct stmpe_keypad_variant *variant;
+	unsigned int debounce_ms;
+	unsigned int scan_count;
+	bool no_autorepeat;
+	unsigned int rows;
+	unsigned int cols;
+	unsigned short keymap[STMPE_KEYPAD_KEYMAP_MAX_SIZE];
+};
+
+static int stmpe_keypad_read_data(struct stmpe_keypad *keypad, u8 *data)
+{
+	const struct stmpe_keypad_variant *variant = keypad->variant;
+	struct stmpe *stmpe = keypad->stmpe;
+	int ret;
+	int i;
+
+	if (variant->auto_increment)
+		return stmpe_block_read(stmpe, STMPE_KPC_DATA_BYTE0,
+					variant->num_data, data);
+
+	for (i = 0; i < variant->num_data; i++) {
+		ret = stmpe_reg_read(stmpe, STMPE_KPC_DATA_BYTE0 + i);
+		if (ret < 0)
+			return ret;
+
+		data[i] = ret;
+	}
+
+	return 0;
+}
+
+static irqreturn_t stmpe_keypad_irq(int irq, void *dev)
+{
+	struct stmpe_keypad *keypad = dev;
+	struct input_dev *input = keypad->input;
+	const struct stmpe_keypad_variant *variant = keypad->variant;
+	u8 fifo[variant->num_data];
+	int ret;
+	int i;
+
+	ret = stmpe_keypad_read_data(keypad, fifo);
+	if (ret < 0)
+		return IRQ_NONE;
+
+	for (i = 0; i < variant->num_normal_data; i++) {
+		u8 data = fifo[i];
+		int row = (data & STMPE_KPC_DATA_ROW) >> 3;
+		int col = data & STMPE_KPC_DATA_COL;
+		int code = MATRIX_SCAN_CODE(row, col, STMPE_KEYPAD_ROW_SHIFT);
+		bool up = data & STMPE_KPC_DATA_UP;
+
+		if ((data & STMPE_KPC_DATA_NOKEY_MASK)
+			== STMPE_KPC_DATA_NOKEY_MASK)
+			continue;
+
+		input_event(input, EV_MSC, MSC_SCAN, code);
+		input_report_key(input, keypad->keymap[code], !up);
+		input_sync(input);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int stmpe_keypad_altfunc_init(struct stmpe_keypad *keypad)
+{
+	const struct stmpe_keypad_variant *variant = keypad->variant;
+	unsigned int col_gpios = variant->col_gpios;
+	unsigned int row_gpios = variant->row_gpios;
+	struct stmpe *stmpe = keypad->stmpe;
+	u8 pureg = stmpe->regs[STMPE_IDX_GPPUR_LSB];
+	unsigned int pins = 0;
+	unsigned int pu_pins = 0;
+	int ret;
+	int i;
+
+	/*
+	 * Figure out which pins need to be set to the keypad alternate
+	 * function.
+	 *
+	 * {cols,rows}_gpios are bitmasks of which pins on the chip can be used
+	 * for the keypad.
+	 *
+	 * keypad->{cols,rows} are a bitmask of which pins (of the ones useable
+	 * for the keypad) are used on the board.
+	 */
+
+	for (i = 0; i < variant->max_cols; i++) {
+		int num = __ffs(col_gpios);
+
+		if (keypad->cols & (1 << i)) {
+			pins |= 1 << num;
+			pu_pins |= 1 << num;
+		}
+
+		col_gpios &= ~(1 << num);
+	}
+
+	for (i = 0; i < variant->max_rows; i++) {
+		int num = __ffs(row_gpios);
+
+		if (keypad->rows & (1 << i))
+			pins |= 1 << num;
+
+		row_gpios &= ~(1 << num);
+	}
+
+	ret = stmpe_set_altfunc(stmpe, pins, STMPE_BLOCK_KEYPAD);
+	if (ret)
+		return ret;
+
+	/*
+	 * On STMPE24xx, set pin bias to pull-up on all keypad input
+	 * pins (columns), this incidentally happen to be maximum 8 pins
+	 * and placed at GPIO0-7 so only the LSB of the pull up register
+	 * ever needs to be written.
+	 */
+	if (variant->set_pullup) {
+		u8 val;
+
+		ret = stmpe_reg_read(stmpe, pureg);
+		if (ret)
+			return ret;
+
+		/* Do not touch unused pins, may be used for GPIO */
+		val = ret & ~pu_pins;
+		val |= pu_pins;
+
+		ret = stmpe_reg_write(stmpe, pureg, val);
+	}
+
+	return 0;
+}
+
+static int stmpe_keypad_chip_init(struct stmpe_keypad *keypad)
+{
+	const struct stmpe_keypad_variant *variant = keypad->variant;
+	struct stmpe *stmpe = keypad->stmpe;
+	int ret;
+
+	if (keypad->debounce_ms > STMPE_KEYPAD_MAX_DEBOUNCE)
+		return -EINVAL;
+
+	if (keypad->scan_count > STMPE_KEYPAD_MAX_SCAN_COUNT)
+		return -EINVAL;
+
+	ret = stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD);
+	if (ret < 0)
+		return ret;
+
+	ret = stmpe_keypad_altfunc_init(keypad);
+	if (ret < 0)
+		return ret;
+
+	ret = stmpe_reg_write(stmpe, STMPE_KPC_COL, keypad->cols);
+	if (ret < 0)
+		return ret;
+
+	ret = stmpe_reg_write(stmpe, STMPE_KPC_ROW_LSB, keypad->rows);
+	if (ret < 0)
+		return ret;
+
+	if (variant->max_rows > 8) {
+		ret = stmpe_set_bits(stmpe, STMPE_KPC_ROW_MSB,
+				     STMPE_KPC_ROW_MSB_ROWS,
+				     keypad->rows >> 8);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = stmpe_set_bits(stmpe, STMPE_KPC_CTRL_MSB,
+			     STMPE_KPC_CTRL_MSB_SCAN_COUNT,
+			     keypad->scan_count << 4);
+	if (ret < 0)
+		return ret;
+
+	return stmpe_set_bits(stmpe, STMPE_KPC_CTRL_LSB,
+			      STMPE_KPC_CTRL_LSB_SCAN |
+			      STMPE_KPC_CTRL_LSB_DEBOUNCE,
+			      STMPE_KPC_CTRL_LSB_SCAN |
+			      (keypad->debounce_ms << 1));
+}
+
+static void stmpe_keypad_fill_used_pins(struct stmpe_keypad *keypad,
+					u32 used_rows, u32 used_cols)
+{
+	int row, col;
+
+	for (row = 0; row < used_rows; row++) {
+		for (col = 0; col < used_cols; col++) {
+			int code = MATRIX_SCAN_CODE(row, col,
+						    STMPE_KEYPAD_ROW_SHIFT);
+			if (keypad->keymap[code] != KEY_RESERVED) {
+				keypad->rows |= 1 << row;
+				keypad->cols |= 1 << col;
+			}
+		}
+	}
+}
+
+static int stmpe_keypad_probe(struct platform_device *pdev)
+{
+	struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+	struct device_node *np = pdev->dev.of_node;
+	struct stmpe_keypad *keypad;
+	struct input_dev *input;
+	u32 rows;
+	u32 cols;
+	int error;
+	int irq;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	keypad = devm_kzalloc(&pdev->dev, sizeof(struct stmpe_keypad),
+			      GFP_KERNEL);
+	if (!keypad)
+		return -ENOMEM;
+
+	keypad->stmpe = stmpe;
+	keypad->variant = &stmpe_keypad_variants[stmpe->partnum];
+
+	of_property_read_u32(np, "debounce-interval", &keypad->debounce_ms);
+	of_property_read_u32(np, "st,scan-count", &keypad->scan_count);
+	keypad->no_autorepeat = of_property_read_bool(np, "st,no-autorepeat");
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input)
+		return -ENOMEM;
+
+	input->name = "STMPE keypad";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &pdev->dev;
+
+	error = matrix_keypad_parse_of_params(&pdev->dev, &rows, &cols);
+	if (error)
+		return error;
+
+	error = matrix_keypad_build_keymap(NULL, NULL, rows, cols,
+					   keypad->keymap, input);
+	if (error)
+		return error;
+
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+	if (!keypad->no_autorepeat)
+		__set_bit(EV_REP, input->evbit);
+
+	stmpe_keypad_fill_used_pins(keypad, rows, cols);
+
+	keypad->input = input;
+
+	error = stmpe_keypad_chip_init(keypad);
+	if (error < 0)
+		return error;
+
+	error = devm_request_threaded_irq(&pdev->dev, irq,
+					  NULL, stmpe_keypad_irq,
+					  IRQF_ONESHOT, "stmpe-keypad", keypad);
+	if (error) {
+		dev_err(&pdev->dev, "unable to get irq: %d\n", error);
+		return error;
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(&pdev->dev,
+			"unable to register input device: %d\n", error);
+		return error;
+	}
+
+	platform_set_drvdata(pdev, keypad);
+
+	return 0;
+}
+
+static int stmpe_keypad_remove(struct platform_device *pdev)
+{
+	struct stmpe_keypad *keypad = platform_get_drvdata(pdev);
+
+	stmpe_disable(keypad->stmpe, STMPE_BLOCK_KEYPAD);
+
+	return 0;
+}
+
+static struct platform_driver stmpe_keypad_driver = {
+	.driver.name	= "stmpe-keypad",
+	.driver.owner	= THIS_MODULE,
+	.probe		= stmpe_keypad_probe,
+	.remove		= stmpe_keypad_remove,
+};
+module_platform_driver(stmpe_keypad_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPExxxx keypad driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
diff --git a/drivers/input/keyboard/stowaway.c b/drivers/input/keyboard/stowaway.c
new file mode 100644
index 0000000..a6e0d56
--- /dev/null
+++ b/drivers/input/keyboard/stowaway.c
@@ -0,0 +1,172 @@
+/*
+ * Stowaway keyboard driver for Linux
+ */
+
+/*
+ *  Copyright (c) 2006 Marek Vasut
+ *
+ *  Based on Newton keyboard driver for Linux
+ *  by Justin Cormack
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <marek.vasut@gmail.com>, or by paper mail:
+ * Marek Vasut, Liskovecka 559, Frydek-Mistek, 738 01 Czech Republic
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Stowaway keyboard driver"
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define SKBD_KEY_MASK	0x7f
+#define SKBD_RELEASE	0x80
+
+static unsigned char skbd_keycode[128] = {
+	KEY_1, KEY_2, KEY_3, KEY_Z, KEY_4, KEY_5, KEY_6, KEY_7,
+	0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_GRAVE,
+	KEY_X, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_SPACE,
+	KEY_CAPSLOCK, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0,
+	0, 0, 0, KEY_LEFTALT, 0, 0, 0, 0,
+	0, 0, 0, 0, KEY_C, KEY_V, KEY_B, KEY_N,
+	KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_HOME, KEY_8, KEY_9, KEY_0, KEY_ESC,
+	KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_END, KEY_U, KEY_I, KEY_O, KEY_P,
+	KEY_APOSTROPHE, KEY_ENTER, KEY_PAGEUP,0, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON,
+	KEY_SLASH, KEY_UP, KEY_PAGEDOWN, 0,KEY_M, KEY_COMMA, KEY_DOT, KEY_INSERT,
+	KEY_DELETE, KEY_LEFT, KEY_DOWN, KEY_RIGHT,  0, 0, 0,
+	KEY_LEFTSHIFT, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7,
+	KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, 0, 0, 0
+};
+
+struct skbd {
+	unsigned char keycode[128];
+	struct input_dev *dev;
+	struct serio *serio;
+	char phys[32];
+};
+
+static irqreturn_t skbd_interrupt(struct serio *serio, unsigned char data,
+				  unsigned int flags)
+{
+	struct skbd *skbd = serio_get_drvdata(serio);
+	struct input_dev *dev = skbd->dev;
+
+	if (skbd->keycode[data & SKBD_KEY_MASK]) {
+		input_report_key(dev, skbd->keycode[data & SKBD_KEY_MASK],
+				 !(data & SKBD_RELEASE));
+		input_sync(dev);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int skbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct skbd *skbd;
+	struct input_dev *input_dev;
+	int err = -ENOMEM;
+	int i;
+
+	skbd = kzalloc(sizeof(struct skbd), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!skbd || !input_dev)
+		goto fail1;
+
+	skbd->serio = serio;
+	skbd->dev = input_dev;
+	snprintf(skbd->phys, sizeof(skbd->phys), "%s/input0", serio->phys);
+	memcpy(skbd->keycode, skbd_keycode, sizeof(skbd->keycode));
+
+	input_dev->name = "Stowaway Keyboard";
+	input_dev->phys = skbd->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_STOWAWAY;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+	input_dev->keycode = skbd->keycode;
+	input_dev->keycodesize = sizeof(unsigned char);
+	input_dev->keycodemax = ARRAY_SIZE(skbd_keycode);
+	for (i = 0; i < ARRAY_SIZE(skbd_keycode); i++)
+		set_bit(skbd_keycode[i], input_dev->keybit);
+	clear_bit(0, input_dev->keybit);
+
+	serio_set_drvdata(serio, skbd);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(skbd->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3: serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(skbd);
+	return err;
+}
+
+static void skbd_disconnect(struct serio *serio)
+{
+	struct skbd *skbd = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(skbd->dev);
+	kfree(skbd);
+}
+
+static struct serio_device_id skbd_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_STOWAWAY,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, skbd_serio_ids);
+
+static struct serio_driver skbd_drv = {
+	.driver		= {
+		.name	= "stowaway",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= skbd_serio_ids,
+	.interrupt	= skbd_interrupt,
+	.connect	= skbd_connect,
+	.disconnect	= skbd_disconnect,
+};
+
+module_serio_driver(skbd_drv);
diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c b/drivers/input/keyboard/sun4i-lradc-keys.c
new file mode 100644
index 0000000..cc8f7dd
--- /dev/null
+++ b/drivers/input/keyboard/sun4i-lradc-keys.c
@@ -0,0 +1,286 @@
+/*
+ * Allwinner sun4i low res adc attached tablet keys driver
+ *
+ * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Allwinnner sunxi SoCs have a lradc which is specifically designed to have
+ * various (tablet) keys (ie home, back, search, etc). attached to it using
+ * a resistor network. This driver is for the keys on such boards.
+ *
+ * There are 2 channels, currently this driver only supports channel 0 since
+ * there are no boards known to use channel 1.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#define LRADC_CTRL		0x00
+#define LRADC_INTC		0x04
+#define LRADC_INTS		0x08
+#define LRADC_DATA0		0x0c
+#define LRADC_DATA1		0x10
+
+/* LRADC_CTRL bits */
+#define FIRST_CONVERT_DLY(x)	((x) << 24) /* 8 bits */
+#define CHAN_SELECT(x)		((x) << 22) /* 2 bits */
+#define CONTINUE_TIME_SEL(x)	((x) << 16) /* 4 bits */
+#define KEY_MODE_SEL(x)		((x) << 12) /* 2 bits */
+#define LEVELA_B_CNT(x)		((x) << 8)  /* 4 bits */
+#define HOLD_EN(x)		((x) << 6)
+#define LEVELB_VOL(x)		((x) << 4)  /* 2 bits */
+#define SAMPLE_RATE(x)		((x) << 2)  /* 2 bits */
+#define ENABLE(x)		((x) << 0)
+
+/* LRADC_INTC and LRADC_INTS bits */
+#define CHAN1_KEYUP_IRQ		BIT(12)
+#define CHAN1_ALRDY_HOLD_IRQ	BIT(11)
+#define CHAN1_HOLD_IRQ		BIT(10)
+#define	CHAN1_KEYDOWN_IRQ	BIT(9)
+#define CHAN1_DATA_IRQ		BIT(8)
+#define CHAN0_KEYUP_IRQ		BIT(4)
+#define CHAN0_ALRDY_HOLD_IRQ	BIT(3)
+#define CHAN0_HOLD_IRQ		BIT(2)
+#define	CHAN0_KEYDOWN_IRQ	BIT(1)
+#define CHAN0_DATA_IRQ		BIT(0)
+
+struct sun4i_lradc_keymap {
+	u32 voltage;
+	u32 keycode;
+};
+
+struct sun4i_lradc_data {
+	struct device *dev;
+	struct input_dev *input;
+	void __iomem *base;
+	struct regulator *vref_supply;
+	struct sun4i_lradc_keymap *chan0_map;
+	u32 chan0_map_count;
+	u32 chan0_keycode;
+	u32 vref;
+};
+
+static irqreturn_t sun4i_lradc_irq(int irq, void *dev_id)
+{
+	struct sun4i_lradc_data *lradc = dev_id;
+	u32 i, ints, val, voltage, diff, keycode = 0, closest = 0xffffffff;
+
+	ints  = readl(lradc->base + LRADC_INTS);
+
+	/*
+	 * lradc supports only one keypress at a time, release does not give
+	 * any info as to which key was released, so we cache the keycode.
+	 */
+
+	if (ints & CHAN0_KEYUP_IRQ) {
+		input_report_key(lradc->input, lradc->chan0_keycode, 0);
+		lradc->chan0_keycode = 0;
+	}
+
+	if ((ints & CHAN0_KEYDOWN_IRQ) && lradc->chan0_keycode == 0) {
+		val = readl(lradc->base + LRADC_DATA0) & 0x3f;
+		voltage = val * lradc->vref / 63;
+
+		for (i = 0; i < lradc->chan0_map_count; i++) {
+			diff = abs(lradc->chan0_map[i].voltage - voltage);
+			if (diff < closest) {
+				closest = diff;
+				keycode = lradc->chan0_map[i].keycode;
+			}
+		}
+
+		lradc->chan0_keycode = keycode;
+		input_report_key(lradc->input, lradc->chan0_keycode, 1);
+	}
+
+	input_sync(lradc->input);
+
+	writel(ints, lradc->base + LRADC_INTS);
+
+	return IRQ_HANDLED;
+}
+
+static int sun4i_lradc_open(struct input_dev *dev)
+{
+	struct sun4i_lradc_data *lradc = input_get_drvdata(dev);
+	int error;
+
+	error = regulator_enable(lradc->vref_supply);
+	if (error)
+		return error;
+
+	/* lradc Vref internally is divided by 2/3 */
+	lradc->vref = regulator_get_voltage(lradc->vref_supply) * 2 / 3;
+
+	/*
+	 * Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to
+	 * stabilize on press, wait (1 + 1) * 4 ms for key release
+	 */
+	writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |
+		SAMPLE_RATE(0) | ENABLE(1), lradc->base + LRADC_CTRL);
+
+	writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC);
+
+	return 0;
+}
+
+static void sun4i_lradc_close(struct input_dev *dev)
+{
+	struct sun4i_lradc_data *lradc = input_get_drvdata(dev);
+
+	/* Disable lradc, leave other settings unchanged */
+	writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |
+		SAMPLE_RATE(2), lradc->base + LRADC_CTRL);
+	writel(0, lradc->base + LRADC_INTC);
+
+	regulator_disable(lradc->vref_supply);
+}
+
+static int sun4i_lradc_load_dt_keymap(struct device *dev,
+				      struct sun4i_lradc_data *lradc)
+{
+	struct device_node *np, *pp;
+	int i;
+	int error;
+
+	np = dev->of_node;
+	if (!np)
+		return -EINVAL;
+
+	lradc->chan0_map_count = of_get_child_count(np);
+	if (lradc->chan0_map_count == 0) {
+		dev_err(dev, "keymap is missing in device tree\n");
+		return -EINVAL;
+	}
+
+	lradc->chan0_map = devm_kmalloc_array(dev, lradc->chan0_map_count,
+					      sizeof(struct sun4i_lradc_keymap),
+					      GFP_KERNEL);
+	if (!lradc->chan0_map)
+		return -ENOMEM;
+
+	i = 0;
+	for_each_child_of_node(np, pp) {
+		struct sun4i_lradc_keymap *map = &lradc->chan0_map[i];
+		u32 channel;
+
+		error = of_property_read_u32(pp, "channel", &channel);
+		if (error || channel != 0) {
+			dev_err(dev, "%s: Inval channel prop\n", pp->name);
+			return -EINVAL;
+		}
+
+		error = of_property_read_u32(pp, "voltage", &map->voltage);
+		if (error) {
+			dev_err(dev, "%s: Inval voltage prop\n", pp->name);
+			return -EINVAL;
+		}
+
+		error = of_property_read_u32(pp, "linux,code", &map->keycode);
+		if (error) {
+			dev_err(dev, "%s: Inval linux,code prop\n", pp->name);
+			return -EINVAL;
+		}
+
+		i++;
+	}
+
+	return 0;
+}
+
+static int sun4i_lradc_probe(struct platform_device *pdev)
+{
+	struct sun4i_lradc_data *lradc;
+	struct device *dev = &pdev->dev;
+	int i;
+	int error;
+
+	lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL);
+	if (!lradc)
+		return -ENOMEM;
+
+	error = sun4i_lradc_load_dt_keymap(dev, lradc);
+	if (error)
+		return error;
+
+	lradc->vref_supply = devm_regulator_get(dev, "vref");
+	if (IS_ERR(lradc->vref_supply))
+		return PTR_ERR(lradc->vref_supply);
+
+	lradc->dev = dev;
+	lradc->input = devm_input_allocate_device(dev);
+	if (!lradc->input)
+		return -ENOMEM;
+
+	lradc->input->name = pdev->name;
+	lradc->input->phys = "sun4i_lradc/input0";
+	lradc->input->open = sun4i_lradc_open;
+	lradc->input->close = sun4i_lradc_close;
+	lradc->input->id.bustype = BUS_HOST;
+	lradc->input->id.vendor = 0x0001;
+	lradc->input->id.product = 0x0001;
+	lradc->input->id.version = 0x0100;
+
+	__set_bit(EV_KEY, lradc->input->evbit);
+	for (i = 0; i < lradc->chan0_map_count; i++)
+		__set_bit(lradc->chan0_map[i].keycode, lradc->input->keybit);
+
+	input_set_drvdata(lradc->input, lradc);
+
+	lradc->base = devm_ioremap_resource(dev,
+			      platform_get_resource(pdev, IORESOURCE_MEM, 0));
+	if (IS_ERR(lradc->base))
+		return PTR_ERR(lradc->base);
+
+	error = devm_request_irq(dev, platform_get_irq(pdev, 0),
+				 sun4i_lradc_irq, 0,
+				 "sun4i-a10-lradc-keys", lradc);
+	if (error)
+		return error;
+
+	error = input_register_device(lradc->input);
+	if (error)
+		return error;
+
+	platform_set_drvdata(pdev, lradc);
+	return 0;
+}
+
+static const struct of_device_id sun4i_lradc_of_match[] = {
+	{ .compatible = "allwinner,sun4i-a10-lradc-keys", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match);
+
+static struct platform_driver sun4i_lradc_driver = {
+	.driver = {
+		.name	= "sun4i-a10-lradc-keys",
+		.of_match_table = of_match_ptr(sun4i_lradc_of_match),
+	},
+	.probe	= sun4i_lradc_probe,
+};
+
+module_platform_driver(sun4i_lradc_driver);
+
+MODULE_DESCRIPTION("Allwinner sun4i low res adc attached tablet keys driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c
new file mode 100644
index 0000000..dc6bb9d
--- /dev/null
+++ b/drivers/input/keyboard/sunkbd.c
@@ -0,0 +1,371 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Sun keyboard driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+
+#define DRIVER_DESC	"Sun keyboard driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static unsigned char sunkbd_keycode[128] = {
+	  0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112,
+	 65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106,  1,  2,  3,
+	  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
+	116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+	 26, 27,111,127, 71, 72, 73, 74,134,135,107,  0, 29, 30, 31, 32,
+	 33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
+	104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
+	 79, 80, 81,  0,  0,  0,138, 58,125, 57,126,109, 86, 78
+};
+
+#define SUNKBD_CMD_RESET	0x1
+#define SUNKBD_CMD_BELLON	0x2
+#define SUNKBD_CMD_BELLOFF	0x3
+#define SUNKBD_CMD_CLICK	0xa
+#define SUNKBD_CMD_NOCLICK	0xb
+#define SUNKBD_CMD_SETLED	0xe
+#define SUNKBD_CMD_LAYOUT	0xf
+
+#define SUNKBD_RET_RESET	0xff
+#define SUNKBD_RET_ALLUP	0x7f
+#define SUNKBD_RET_LAYOUT	0xfe
+
+#define SUNKBD_LAYOUT_5_MASK	0x20
+#define SUNKBD_RELEASE		0x80
+#define SUNKBD_KEY		0x7f
+
+/*
+ * Per-keyboard data.
+ */
+
+struct sunkbd {
+	unsigned char keycode[ARRAY_SIZE(sunkbd_keycode)];
+	struct input_dev *dev;
+	struct serio *serio;
+	struct work_struct tq;
+	wait_queue_head_t wait;
+	char name[64];
+	char phys[32];
+	char type;
+	bool enabled;
+	volatile s8 reset;
+	volatile s8 layout;
+};
+
+/*
+ * sunkbd_interrupt() is called by the low level driver when a character
+ * is received.
+ */
+
+static irqreturn_t sunkbd_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct sunkbd *sunkbd = serio_get_drvdata(serio);
+
+	if (sunkbd->reset <= -1) {
+		/*
+		 * If cp[i] is 0xff, sunkbd->reset will stay -1.
+		 * The keyboard sends 0xff 0xff 0xID on powerup.
+		 */
+		sunkbd->reset = data;
+		wake_up_interruptible(&sunkbd->wait);
+		goto out;
+	}
+
+	if (sunkbd->layout == -1) {
+		sunkbd->layout = data;
+		wake_up_interruptible(&sunkbd->wait);
+		goto out;
+	}
+
+	switch (data) {
+
+	case SUNKBD_RET_RESET:
+		schedule_work(&sunkbd->tq);
+		sunkbd->reset = -1;
+		break;
+
+	case SUNKBD_RET_LAYOUT:
+		sunkbd->layout = -1;
+		break;
+
+	case SUNKBD_RET_ALLUP: /* All keys released */
+		break;
+
+	default:
+		if (!sunkbd->enabled)
+			break;
+
+		if (sunkbd->keycode[data & SUNKBD_KEY]) {
+			input_report_key(sunkbd->dev,
+					 sunkbd->keycode[data & SUNKBD_KEY],
+					 !(data & SUNKBD_RELEASE));
+			input_sync(sunkbd->dev);
+		} else {
+			printk(KERN_WARNING
+				"sunkbd.c: Unknown key (scancode %#x) %s.\n",
+				data & SUNKBD_KEY,
+				data & SUNKBD_RELEASE ? "released" : "pressed");
+		}
+	}
+out:
+	return IRQ_HANDLED;
+}
+
+/*
+ * sunkbd_event() handles events from the input module.
+ */
+
+static int sunkbd_event(struct input_dev *dev,
+			unsigned int type, unsigned int code, int value)
+{
+	struct sunkbd *sunkbd = input_get_drvdata(dev);
+
+	switch (type) {
+
+	case EV_LED:
+
+		serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
+		serio_write(sunkbd->serio,
+			(!!test_bit(LED_CAPSL,   dev->led) << 3) |
+			(!!test_bit(LED_SCROLLL, dev->led) << 2) |
+			(!!test_bit(LED_COMPOSE, dev->led) << 1) |
+			 !!test_bit(LED_NUML,    dev->led));
+		return 0;
+
+	case EV_SND:
+
+		switch (code) {
+
+		case SND_CLICK:
+			serio_write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
+			return 0;
+
+		case SND_BELL:
+			serio_write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
+			return 0;
+		}
+
+		break;
+	}
+
+	return -1;
+}
+
+/*
+ * sunkbd_initialize() checks for a Sun keyboard attached, and determines
+ * its type.
+ */
+
+static int sunkbd_initialize(struct sunkbd *sunkbd)
+{
+	sunkbd->reset = -2;
+	serio_write(sunkbd->serio, SUNKBD_CMD_RESET);
+	wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
+	if (sunkbd->reset < 0)
+		return -1;
+
+	sunkbd->type = sunkbd->reset;
+
+	if (sunkbd->type == 4) {	/* Type 4 keyboard */
+		sunkbd->layout = -2;
+		serio_write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
+		wait_event_interruptible_timeout(sunkbd->wait,
+						 sunkbd->layout >= 0, HZ / 4);
+		if (sunkbd->layout < 0)
+			return -1;
+		if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK)
+			sunkbd->type = 5;
+	}
+
+	return 0;
+}
+
+/*
+ * sunkbd_reinit() sets leds and beeps to a state the computer remembers they
+ * were in.
+ */
+
+static void sunkbd_reinit(struct work_struct *work)
+{
+	struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
+
+	wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
+
+	serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
+	serio_write(sunkbd->serio,
+		(!!test_bit(LED_CAPSL,   sunkbd->dev->led) << 3) |
+		(!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
+		(!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) |
+		 !!test_bit(LED_NUML,    sunkbd->dev->led));
+	serio_write(sunkbd->serio,
+		SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
+	serio_write(sunkbd->serio,
+		SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
+}
+
+static void sunkbd_enable(struct sunkbd *sunkbd, bool enable)
+{
+	serio_pause_rx(sunkbd->serio);
+	sunkbd->enabled = enable;
+	serio_continue_rx(sunkbd->serio);
+}
+
+/*
+ * sunkbd_connect() probes for a Sun keyboard and fills the necessary
+ * structures.
+ */
+
+static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct sunkbd *sunkbd;
+	struct input_dev *input_dev;
+	int err = -ENOMEM;
+	int i;
+
+	sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!sunkbd || !input_dev)
+		goto fail1;
+
+	sunkbd->serio = serio;
+	sunkbd->dev = input_dev;
+	init_waitqueue_head(&sunkbd->wait);
+	INIT_WORK(&sunkbd->tq, sunkbd_reinit);
+	snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
+
+	serio_set_drvdata(serio, sunkbd);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	if (sunkbd_initialize(sunkbd) < 0) {
+		err = -ENODEV;
+		goto fail3;
+	}
+
+	snprintf(sunkbd->name, sizeof(sunkbd->name),
+		 "Sun Type %d keyboard", sunkbd->type);
+	memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
+
+	input_dev->name = sunkbd->name;
+	input_dev->phys = sunkbd->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor  = SERIO_SUNKBD;
+	input_dev->id.product = sunkbd->type;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_set_drvdata(input_dev, sunkbd);
+
+	input_dev->event = sunkbd_event;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
+		BIT_MASK(EV_SND) | BIT_MASK(EV_REP);
+	input_dev->ledbit[0] = BIT_MASK(LED_CAPSL) | BIT_MASK(LED_COMPOSE) |
+		BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_NUML);
+	input_dev->sndbit[0] = BIT_MASK(SND_CLICK) | BIT_MASK(SND_BELL);
+
+	input_dev->keycode = sunkbd->keycode;
+	input_dev->keycodesize = sizeof(unsigned char);
+	input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
+	for (i = 0; i < ARRAY_SIZE(sunkbd_keycode); i++)
+		__set_bit(sunkbd->keycode[i], input_dev->keybit);
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+
+	sunkbd_enable(sunkbd, true);
+
+	err = input_register_device(sunkbd->dev);
+	if (err)
+		goto fail4;
+
+	return 0;
+
+ fail4:	sunkbd_enable(sunkbd, false);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(sunkbd);
+	return err;
+}
+
+/*
+ * sunkbd_disconnect() unregisters and closes behind us.
+ */
+
+static void sunkbd_disconnect(struct serio *serio)
+{
+	struct sunkbd *sunkbd = serio_get_drvdata(serio);
+
+	sunkbd_enable(sunkbd, false);
+	input_unregister_device(sunkbd->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(sunkbd);
+}
+
+static struct serio_device_id sunkbd_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_SUNKBD,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_UNKNOWN, /* sunkbd does probe */
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
+
+static struct serio_driver sunkbd_drv = {
+	.driver		= {
+		.name	= "sunkbd",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= sunkbd_serio_ids,
+	.interrupt	= sunkbd_interrupt,
+	.connect	= sunkbd_connect,
+	.disconnect	= sunkbd_disconnect,
+};
+
+module_serio_driver(sunkbd_drv);
diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c
new file mode 100644
index 0000000..e92dfd8
--- /dev/null
+++ b/drivers/input/keyboard/tc3589x-keypad.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Jayeeta Banerjee <jayeeta.banerjee@stericsson.com>
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com>
+ *
+ * License Terms: GNU General Public License, version 2
+ *
+ * TC35893 MFD Keypad Controller driver
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/mfd/tc3589x.h>
+#include <linux/device.h>
+
+/* Maximum supported keypad matrix row/columns size */
+#define TC3589x_MAX_KPROW               8
+#define TC3589x_MAX_KPCOL               12
+
+/* keypad related Constants */
+#define TC3589x_MAX_DEBOUNCE_SETTLE     0xFF
+#define DEDICATED_KEY_VAL		0xFF
+
+/* Pull up/down masks */
+#define TC3589x_NO_PULL_MASK		0x0
+#define TC3589x_PULL_DOWN_MASK		0x1
+#define TC3589x_PULL_UP_MASK		0x2
+#define TC3589x_PULLUP_ALL_MASK		0xAA
+#define TC3589x_IO_PULL_VAL(index, mask)	((mask)<<((index)%4)*2))
+
+/* Bit masks for IOCFG register */
+#define IOCFG_BALLCFG		0x01
+#define IOCFG_IG		0x08
+
+#define KP_EVCODE_COL_MASK	0x0F
+#define KP_EVCODE_ROW_MASK	0x70
+#define KP_RELEASE_EVT_MASK	0x80
+
+#define KP_ROW_SHIFT		4
+
+#define KP_NO_VALID_KEY_MASK	0x7F
+
+/* bit masks for RESTCTRL register */
+#define TC3589x_KBDRST		0x2
+#define TC3589x_IRQRST		0x10
+#define TC3589x_RESET_ALL	0x1B
+
+/* KBDMFS register bit mask */
+#define TC3589x_KBDMFS_EN	0x1
+
+/* CLKEN register bitmask */
+#define KPD_CLK_EN		0x1
+
+/* RSTINTCLR register bit mask */
+#define IRQ_CLEAR		0x1
+
+/* bit masks for keyboard interrupts*/
+#define TC3589x_EVT_LOSS_INT	0x8
+#define TC3589x_EVT_INT		0x4
+#define TC3589x_KBD_LOSS_INT	0x2
+#define TC3589x_KBD_INT		0x1
+
+/* bit masks for keyboard interrupt clear*/
+#define TC3589x_EVT_INT_CLR	0x2
+#define TC3589x_KBD_INT_CLR	0x1
+
+/**
+ * struct tc35893_keypad_platform_data - platform specific keypad data
+ * @keymap_data:        matrix scan code table for keycodes
+ * @krow:               mask for available rows, value is 0xFF
+ * @kcol:               mask for available columns, value is 0xFF
+ * @debounce_period:    platform specific debounce time
+ * @settle_time:        platform specific settle down time
+ * @irqtype:            type of interrupt, falling or rising edge
+ * @enable_wakeup:      specifies if keypad event can wake up system from sleep
+ * @no_autorepeat:      flag for auto repetition
+ */
+struct tc3589x_keypad_platform_data {
+	const struct matrix_keymap_data *keymap_data;
+	u8                      krow;
+	u8                      kcol;
+	u8                      debounce_period;
+	u8                      settle_time;
+	unsigned long           irqtype;
+	bool                    enable_wakeup;
+	bool                    no_autorepeat;
+};
+
+/**
+ * struct tc_keypad - data structure used by keypad driver
+ * @tc3589x:    pointer to tc35893
+ * @input:      pointer to input device object
+ * @board:      keypad platform device
+ * @krow:	number of rows
+ * @kcol:	number of columns
+ * @keymap:     matrix scan code table for keycodes
+ * @keypad_stopped: holds keypad status
+ */
+struct tc_keypad {
+	struct tc3589x *tc3589x;
+	struct input_dev *input;
+	const struct tc3589x_keypad_platform_data *board;
+	unsigned int krow;
+	unsigned int kcol;
+	unsigned short *keymap;
+	bool keypad_stopped;
+};
+
+static int tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad)
+{
+	int ret;
+	struct tc3589x *tc3589x = keypad->tc3589x;
+	const struct tc3589x_keypad_platform_data *board = keypad->board;
+
+	/* validate platform configuration */
+	if (board->kcol > TC3589x_MAX_KPCOL || board->krow > TC3589x_MAX_KPROW)
+		return -EINVAL;
+
+	/* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */
+	ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSIZE,
+			(board->krow << KP_ROW_SHIFT) | board->kcol);
+	if (ret < 0)
+		return ret;
+
+	/* configure dedicated key config, no dedicated key selected */
+	ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_LSB, DEDICATED_KEY_VAL);
+	if (ret < 0)
+		return ret;
+
+	ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_MSB, DEDICATED_KEY_VAL);
+	if (ret < 0)
+		return ret;
+
+	/* Configure settle time */
+	ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSETTLE_REG,
+				board->settle_time);
+	if (ret < 0)
+		return ret;
+
+	/* Configure debounce time */
+	ret = tc3589x_reg_write(tc3589x, TC3589x_KBDBOUNCE,
+				board->debounce_period);
+	if (ret < 0)
+		return ret;
+
+	/* Start of initialise keypad GPIOs */
+	ret = tc3589x_set_bits(tc3589x, TC3589x_IOCFG, 0x0, IOCFG_IG);
+	if (ret < 0)
+		return ret;
+
+	/* Configure pull-up resistors for all row GPIOs */
+	ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_LSB,
+					TC3589x_PULLUP_ALL_MASK);
+	if (ret < 0)
+		return ret;
+
+	ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_MSB,
+					TC3589x_PULLUP_ALL_MASK);
+	if (ret < 0)
+		return ret;
+
+	/* Configure pull-up resistors for all column GPIOs */
+	ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_LSB,
+			TC3589x_PULLUP_ALL_MASK);
+	if (ret < 0)
+		return ret;
+
+	ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_MSB,
+			TC3589x_PULLUP_ALL_MASK);
+	if (ret < 0)
+		return ret;
+
+	ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG2_LSB,
+			TC3589x_PULLUP_ALL_MASK);
+
+	return ret;
+}
+
+#define TC35893_DATA_REGS		4
+#define TC35893_KEYCODE_FIFO_EMPTY	0x7f
+#define TC35893_KEYCODE_FIFO_CLEAR	0xff
+#define TC35893_KEYPAD_ROW_SHIFT	0x3
+
+static irqreturn_t tc3589x_keypad_irq(int irq, void *dev)
+{
+	struct tc_keypad *keypad = dev;
+	struct tc3589x *tc3589x = keypad->tc3589x;
+	u8 i, row_index, col_index, kbd_code, up;
+	u8 code;
+
+	for (i = 0; i < TC35893_DATA_REGS * 2; i++) {
+		kbd_code = tc3589x_reg_read(tc3589x, TC3589x_EVTCODE_FIFO);
+
+		/* loop till fifo is empty and no more keys are pressed */
+		if (kbd_code == TC35893_KEYCODE_FIFO_EMPTY ||
+				kbd_code == TC35893_KEYCODE_FIFO_CLEAR)
+			continue;
+
+		/* valid key is found */
+		col_index = kbd_code & KP_EVCODE_COL_MASK;
+		row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> KP_ROW_SHIFT;
+		code = MATRIX_SCAN_CODE(row_index, col_index,
+						TC35893_KEYPAD_ROW_SHIFT);
+		up = kbd_code & KP_RELEASE_EVT_MASK;
+
+		input_event(keypad->input, EV_MSC, MSC_SCAN, code);
+		input_report_key(keypad->input, keypad->keymap[code], !up);
+		input_sync(keypad->input);
+	}
+
+	/* clear IRQ */
+	tc3589x_set_bits(tc3589x, TC3589x_KBDIC,
+			0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);
+	/* enable IRQ */
+	tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,
+			0x0, TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);
+
+	return IRQ_HANDLED;
+}
+
+static int tc3589x_keypad_enable(struct tc_keypad *keypad)
+{
+	struct tc3589x *tc3589x = keypad->tc3589x;
+	int ret;
+
+	/* pull the keypad module out of reset */
+	ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x0);
+	if (ret < 0)
+		return ret;
+
+	/* configure KBDMFS */
+	ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMFS, 0x0, TC3589x_KBDMFS_EN);
+	if (ret < 0)
+		return ret;
+
+	/* enable the keypad clock */
+	ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x0, KPD_CLK_EN);
+	if (ret < 0)
+		return ret;
+
+	/* clear pending IRQs */
+	ret =  tc3589x_set_bits(tc3589x, TC3589x_RSTINTCLR, 0x0, 0x1);
+	if (ret < 0)
+		return ret;
+
+	/* enable the IRQs */
+	ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, 0x0,
+					TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);
+	if (ret < 0)
+		return ret;
+
+	keypad->keypad_stopped = false;
+
+	return ret;
+}
+
+static int tc3589x_keypad_disable(struct tc_keypad *keypad)
+{
+	struct tc3589x *tc3589x = keypad->tc3589x;
+	int ret;
+
+	/* clear IRQ */
+	ret = tc3589x_set_bits(tc3589x, TC3589x_KBDIC,
+			0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);
+	if (ret < 0)
+		return ret;
+
+	/* disable all interrupts */
+	ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,
+			~(TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT), 0x0);
+	if (ret < 0)
+		return ret;
+
+	/* disable the keypad module */
+	ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x1, 0x0);
+	if (ret < 0)
+		return ret;
+
+	/* put the keypad module into reset */
+	ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x1);
+
+	keypad->keypad_stopped = true;
+
+	return ret;
+}
+
+static int tc3589x_keypad_open(struct input_dev *input)
+{
+	int error;
+	struct tc_keypad *keypad = input_get_drvdata(input);
+
+	/* enable the keypad module */
+	error = tc3589x_keypad_enable(keypad);
+	if (error < 0) {
+		dev_err(&input->dev, "failed to enable keypad module\n");
+		return error;
+	}
+
+	error = tc3589x_keypad_init_key_hardware(keypad);
+	if (error < 0) {
+		dev_err(&input->dev, "failed to configure keypad module\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static void tc3589x_keypad_close(struct input_dev *input)
+{
+	struct tc_keypad *keypad = input_get_drvdata(input);
+
+	/* disable the keypad module */
+	tc3589x_keypad_disable(keypad);
+}
+
+static const struct tc3589x_keypad_platform_data *
+tc3589x_keypad_of_probe(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	struct tc3589x_keypad_platform_data *plat;
+	u32 cols, rows;
+	u32 debounce_ms;
+	int proplen;
+
+	if (!np)
+		return ERR_PTR(-ENODEV);
+
+	plat = devm_kzalloc(dev, sizeof(*plat), GFP_KERNEL);
+	if (!plat)
+		return ERR_PTR(-ENOMEM);
+
+	of_property_read_u32(np, "keypad,num-columns", &cols);
+	of_property_read_u32(np, "keypad,num-rows", &rows);
+	plat->kcol = (u8) cols;
+	plat->krow = (u8) rows;
+	if (!plat->krow || !plat->kcol ||
+	     plat->krow > TC_KPD_ROWS || plat->kcol > TC_KPD_COLUMNS) {
+		dev_err(dev,
+			"keypad columns/rows not properly specified (%ux%u)\n",
+			plat->kcol, plat->krow);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (!of_get_property(np, "linux,keymap", &proplen)) {
+		dev_err(dev, "property linux,keymap not found\n");
+		return ERR_PTR(-ENOENT);
+	}
+
+	plat->no_autorepeat = of_property_read_bool(np, "linux,no-autorepeat");
+
+	plat->enable_wakeup = of_property_read_bool(np, "wakeup-source") ||
+			      /* legacy name */
+			      of_property_read_bool(np, "linux,wakeup");
+
+	/* The custom delay format is ms/16 */
+	of_property_read_u32(np, "debounce-delay-ms", &debounce_ms);
+	if (debounce_ms)
+		plat->debounce_period = debounce_ms * 16;
+	else
+		plat->debounce_period = TC_KPD_DEBOUNCE_PERIOD;
+
+	plat->settle_time = TC_KPD_SETTLE_TIME;
+	/* FIXME: should be property of the IRQ resource? */
+	plat->irqtype = IRQF_TRIGGER_FALLING;
+
+	return plat;
+}
+
+static int tc3589x_keypad_probe(struct platform_device *pdev)
+{
+	struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
+	struct tc_keypad *keypad;
+	struct input_dev *input;
+	const struct tc3589x_keypad_platform_data *plat;
+	int error, irq;
+
+	plat = tc3589x_keypad_of_probe(&pdev->dev);
+	if (IS_ERR(plat)) {
+		dev_err(&pdev->dev, "invalid keypad platform data\n");
+		return PTR_ERR(plat);
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	keypad = devm_kzalloc(&pdev->dev, sizeof(struct tc_keypad),
+			      GFP_KERNEL);
+	if (!keypad)
+		return -ENOMEM;
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input) {
+		dev_err(&pdev->dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	keypad->board = plat;
+	keypad->input = input;
+	keypad->tc3589x = tc3589x;
+
+	input->id.bustype = BUS_I2C;
+	input->name = pdev->name;
+	input->dev.parent = &pdev->dev;
+
+	input->open = tc3589x_keypad_open;
+	input->close = tc3589x_keypad_close;
+
+	error = matrix_keypad_build_keymap(plat->keymap_data, NULL,
+					   TC3589x_MAX_KPROW, TC3589x_MAX_KPCOL,
+					   NULL, input);
+	if (error) {
+		dev_err(&pdev->dev, "Failed to build keymap\n");
+		return error;
+	}
+
+	keypad->keymap = input->keycode;
+
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+	if (!plat->no_autorepeat)
+		__set_bit(EV_REP, input->evbit);
+
+	input_set_drvdata(input, keypad);
+
+	tc3589x_keypad_disable(keypad);
+
+	error = devm_request_threaded_irq(&pdev->dev, irq,
+					  NULL, tc3589x_keypad_irq,
+					  plat->irqtype | IRQF_ONESHOT,
+					  "tc3589x-keypad", keypad);
+	if (error) {
+		dev_err(&pdev->dev,
+				"Could not allocate irq %d,error %d\n",
+				irq, error);
+		return error;
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(&pdev->dev, "Could not register input device\n");
+		return error;
+	}
+
+	/* let platform decide if keypad is a wakeup source or not */
+	device_init_wakeup(&pdev->dev, plat->enable_wakeup);
+	device_set_wakeup_capable(&pdev->dev, plat->enable_wakeup);
+
+	platform_set_drvdata(pdev, keypad);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tc3589x_keypad_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct tc_keypad *keypad = platform_get_drvdata(pdev);
+	int irq = platform_get_irq(pdev, 0);
+
+	/* keypad is already off; we do nothing */
+	if (keypad->keypad_stopped)
+		return 0;
+
+	/* if device is not a wakeup source, disable it for powersave */
+	if (!device_may_wakeup(&pdev->dev))
+		tc3589x_keypad_disable(keypad);
+	else
+		enable_irq_wake(irq);
+
+	return 0;
+}
+
+static int tc3589x_keypad_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct tc_keypad *keypad = platform_get_drvdata(pdev);
+	int irq = platform_get_irq(pdev, 0);
+
+	if (!keypad->keypad_stopped)
+		return 0;
+
+	/* enable the device to resume normal operations */
+	if (!device_may_wakeup(&pdev->dev))
+		tc3589x_keypad_enable(keypad);
+	else
+		disable_irq_wake(irq);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tc3589x_keypad_dev_pm_ops,
+			 tc3589x_keypad_suspend, tc3589x_keypad_resume);
+
+static struct platform_driver tc3589x_keypad_driver = {
+	.driver	= {
+		.name	= "tc3589x-keypad",
+		.pm	= &tc3589x_keypad_dev_pm_ops,
+	},
+	.probe	= tc3589x_keypad_probe,
+};
+module_platform_driver(tc3589x_keypad_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer");
+MODULE_DESCRIPTION("TC35893 Keypad Driver");
+MODULE_ALIAS("platform:tc3589x-keypad");
diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c
new file mode 100644
index 0000000..dc983ab
--- /dev/null
+++ b/drivers/input/keyboard/tca6416-keypad.c
@@ -0,0 +1,383 @@
+/*
+ * Driver for keys on TCA6416 I2C IO expander
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Author : Sriramakrishnan.A.G. <srk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/tca6416_keypad.h>
+
+#define TCA6416_INPUT          0
+#define TCA6416_OUTPUT         1
+#define TCA6416_INVERT         2
+#define TCA6416_DIRECTION      3
+
+static const struct i2c_device_id tca6416_id[] = {
+	{ "tca6416-keys", 16, },
+	{ "tca6408-keys", 8, },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tca6416_id);
+
+struct tca6416_drv_data {
+	struct input_dev *input;
+	struct tca6416_button data[0];
+};
+
+struct tca6416_keypad_chip {
+	uint16_t reg_output;
+	uint16_t reg_direction;
+	uint16_t reg_input;
+
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct delayed_work dwork;
+	int io_size;
+	int irqnum;
+	u16 pinmask;
+	bool use_polling;
+	struct tca6416_button buttons[0];
+};
+
+static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val)
+{
+	int error;
+
+	error = chip->io_size > 8 ?
+		i2c_smbus_write_word_data(chip->client, reg << 1, val) :
+		i2c_smbus_write_byte_data(chip->client, reg, val);
+	if (error < 0) {
+		dev_err(&chip->client->dev,
+			"%s failed, reg: %d, val: %d, error: %d\n",
+			__func__, reg, val, error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val)
+{
+	int retval;
+
+	retval = chip->io_size > 8 ?
+		 i2c_smbus_read_word_data(chip->client, reg << 1) :
+		 i2c_smbus_read_byte_data(chip->client, reg);
+	if (retval < 0) {
+		dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n",
+			__func__, reg, retval);
+		return retval;
+	}
+
+	*val = (u16)retval;
+	return 0;
+}
+
+static void tca6416_keys_scan(struct tca6416_keypad_chip *chip)
+{
+	struct input_dev *input = chip->input;
+	u16 reg_val, val;
+	int error, i, pin_index;
+
+	error = tca6416_read_reg(chip, TCA6416_INPUT, &reg_val);
+	if (error)
+		return;
+
+	reg_val &= chip->pinmask;
+
+	/* Figure out which lines have changed */
+	val = reg_val ^ chip->reg_input;
+	chip->reg_input = reg_val;
+
+	for (i = 0, pin_index = 0; i < 16; i++) {
+		if (val & (1 << i)) {
+			struct tca6416_button *button = &chip->buttons[pin_index];
+			unsigned int type = button->type ?: EV_KEY;
+			int state = ((reg_val & (1 << i)) ? 1 : 0)
+						^ button->active_low;
+
+			input_event(input, type, button->code, !!state);
+			input_sync(input);
+		}
+
+		if (chip->pinmask & (1 << i))
+			pin_index++;
+	}
+}
+
+/*
+ * This is threaded IRQ handler and this can (and will) sleep.
+ */
+static irqreturn_t tca6416_keys_isr(int irq, void *dev_id)
+{
+	struct tca6416_keypad_chip *chip = dev_id;
+
+	tca6416_keys_scan(chip);
+
+	return IRQ_HANDLED;
+}
+
+static void tca6416_keys_work_func(struct work_struct *work)
+{
+	struct tca6416_keypad_chip *chip =
+		container_of(work, struct tca6416_keypad_chip, dwork.work);
+
+	tca6416_keys_scan(chip);
+	schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100));
+}
+
+static int tca6416_keys_open(struct input_dev *dev)
+{
+	struct tca6416_keypad_chip *chip = input_get_drvdata(dev);
+
+	/* Get initial device state in case it has switches */
+	tca6416_keys_scan(chip);
+
+	if (chip->use_polling)
+		schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100));
+	else
+		enable_irq(chip->irqnum);
+
+	return 0;
+}
+
+static void tca6416_keys_close(struct input_dev *dev)
+{
+	struct tca6416_keypad_chip *chip = input_get_drvdata(dev);
+
+	if (chip->use_polling)
+		cancel_delayed_work_sync(&chip->dwork);
+	else
+		disable_irq(chip->irqnum);
+}
+
+static int tca6416_setup_registers(struct tca6416_keypad_chip *chip)
+{
+	int error;
+
+	error = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output);
+	if (error)
+		return error;
+
+	error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction);
+	if (error)
+		return error;
+
+	/* ensure that keypad pins are set to input */
+	error = tca6416_write_reg(chip, TCA6416_DIRECTION,
+				  chip->reg_direction | chip->pinmask);
+	if (error)
+		return error;
+
+	error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction);
+	if (error)
+		return error;
+
+	error = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input);
+	if (error)
+		return error;
+
+	chip->reg_input &= chip->pinmask;
+
+	return 0;
+}
+
+static int tca6416_keypad_probe(struct i2c_client *client,
+				   const struct i2c_device_id *id)
+{
+	struct tca6416_keys_platform_data *pdata;
+	struct tca6416_keypad_chip *chip;
+	struct input_dev *input;
+	int error;
+	int i;
+
+	/* Check functionality */
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
+		dev_err(&client->dev, "%s adapter not supported\n",
+			dev_driver_string(&client->adapter->dev));
+		return -ENODEV;
+	}
+
+	pdata = dev_get_platdata(&client->dev);
+	if (!pdata) {
+		dev_dbg(&client->dev, "no platform data\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct tca6416_keypad_chip) +
+		       pdata->nbuttons * sizeof(struct tca6416_button),
+		       GFP_KERNEL);
+	input = input_allocate_device();
+	if (!chip || !input) {
+		error = -ENOMEM;
+		goto fail1;
+	}
+
+	chip->client = client;
+	chip->input = input;
+	chip->io_size = id->driver_data;
+	chip->pinmask = pdata->pinmask;
+	chip->use_polling = pdata->use_polling;
+
+	INIT_DELAYED_WORK(&chip->dwork, tca6416_keys_work_func);
+
+	input->phys = "tca6416-keys/input0";
+	input->name = client->name;
+	input->dev.parent = &client->dev;
+
+	input->open = tca6416_keys_open;
+	input->close = tca6416_keys_close;
+
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0100;
+
+	/* Enable auto repeat feature of Linux input subsystem */
+	if (pdata->rep)
+		__set_bit(EV_REP, input->evbit);
+
+	for (i = 0; i < pdata->nbuttons; i++) {
+		unsigned int type;
+
+		chip->buttons[i] = pdata->buttons[i];
+		type = (pdata->buttons[i].type) ?: EV_KEY;
+		input_set_capability(input, type, pdata->buttons[i].code);
+	}
+
+	input_set_drvdata(input, chip);
+
+	/*
+	 * Initialize cached registers from their original values.
+	 * we can't share this chip with another i2c master.
+	 */
+	error = tca6416_setup_registers(chip);
+	if (error)
+		goto fail1;
+
+	if (!chip->use_polling) {
+		if (pdata->irq_is_gpio)
+			chip->irqnum = gpio_to_irq(client->irq);
+		else
+			chip->irqnum = client->irq;
+
+		error = request_threaded_irq(chip->irqnum, NULL,
+					     tca6416_keys_isr,
+					     IRQF_TRIGGER_FALLING |
+						IRQF_ONESHOT,
+					     "tca6416-keypad", chip);
+		if (error) {
+			dev_dbg(&client->dev,
+				"Unable to claim irq %d; error %d\n",
+				chip->irqnum, error);
+			goto fail1;
+		}
+		disable_irq(chip->irqnum);
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		dev_dbg(&client->dev,
+			"Unable to register input device, error: %d\n", error);
+		goto fail2;
+	}
+
+	i2c_set_clientdata(client, chip);
+	device_init_wakeup(&client->dev, 1);
+
+	return 0;
+
+fail2:
+	if (!chip->use_polling) {
+		free_irq(chip->irqnum, chip);
+		enable_irq(chip->irqnum);
+	}
+fail1:
+	input_free_device(input);
+	kfree(chip);
+	return error;
+}
+
+static int tca6416_keypad_remove(struct i2c_client *client)
+{
+	struct tca6416_keypad_chip *chip = i2c_get_clientdata(client);
+
+	if (!chip->use_polling) {
+		free_irq(chip->irqnum, chip);
+		enable_irq(chip->irqnum);
+	}
+
+	input_unregister_device(chip->input);
+	kfree(chip);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tca6416_keypad_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct tca6416_keypad_chip *chip = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(chip->irqnum);
+
+	return 0;
+}
+
+static int tca6416_keypad_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct tca6416_keypad_chip *chip = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(chip->irqnum);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tca6416_keypad_dev_pm_ops,
+			 tca6416_keypad_suspend, tca6416_keypad_resume);
+
+static struct i2c_driver tca6416_keypad_driver = {
+	.driver = {
+		.name	= "tca6416-keypad",
+		.pm	= &tca6416_keypad_dev_pm_ops,
+	},
+	.probe		= tca6416_keypad_probe,
+	.remove		= tca6416_keypad_remove,
+	.id_table	= tca6416_id,
+};
+
+static int __init tca6416_keypad_init(void)
+{
+	return i2c_add_driver(&tca6416_keypad_driver);
+}
+
+subsys_initcall(tca6416_keypad_init);
+
+static void __exit tca6416_keypad_exit(void)
+{
+	i2c_del_driver(&tca6416_keypad_driver);
+}
+module_exit(tca6416_keypad_exit);
+
+MODULE_AUTHOR("Sriramakrishnan <srk@ti.com>");
+MODULE_DESCRIPTION("Keypad driver over tca6146 IO expander");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c
new file mode 100644
index 0000000..9002298
--- /dev/null
+++ b/drivers/input/keyboard/tca8418_keypad.c
@@ -0,0 +1,427 @@
+/*
+ * Driver for TCA8418 I2C keyboard
+ *
+ * Copyright (C) 2011 Fuel7, Inc.  All rights reserved.
+ *
+ * Author: Kyle Manna <kyle.manna@fuel7.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ *
+ * If you can't comply with GPLv2, alternative licensing terms may be
+ * arranged. Please contact Fuel7, Inc. (http://fuel7.com/) for proprietary
+ * alternative licensing inquiries.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/tca8418_keypad.h>
+#include <linux/of.h>
+
+/* TCA8418 hardware limits */
+#define TCA8418_MAX_ROWS	8
+#define TCA8418_MAX_COLS	10
+
+/* TCA8418 register offsets */
+#define REG_CFG			0x01
+#define REG_INT_STAT		0x02
+#define REG_KEY_LCK_EC		0x03
+#define REG_KEY_EVENT_A		0x04
+#define REG_KEY_EVENT_B		0x05
+#define REG_KEY_EVENT_C		0x06
+#define REG_KEY_EVENT_D		0x07
+#define REG_KEY_EVENT_E		0x08
+#define REG_KEY_EVENT_F		0x09
+#define REG_KEY_EVENT_G		0x0A
+#define REG_KEY_EVENT_H		0x0B
+#define REG_KEY_EVENT_I		0x0C
+#define REG_KEY_EVENT_J		0x0D
+#define REG_KP_LCK_TIMER	0x0E
+#define REG_UNLOCK1		0x0F
+#define REG_UNLOCK2		0x10
+#define REG_GPIO_INT_STAT1	0x11
+#define REG_GPIO_INT_STAT2	0x12
+#define REG_GPIO_INT_STAT3	0x13
+#define REG_GPIO_DAT_STAT1	0x14
+#define REG_GPIO_DAT_STAT2	0x15
+#define REG_GPIO_DAT_STAT3	0x16
+#define REG_GPIO_DAT_OUT1	0x17
+#define REG_GPIO_DAT_OUT2	0x18
+#define REG_GPIO_DAT_OUT3	0x19
+#define REG_GPIO_INT_EN1	0x1A
+#define REG_GPIO_INT_EN2	0x1B
+#define REG_GPIO_INT_EN3	0x1C
+#define REG_KP_GPIO1		0x1D
+#define REG_KP_GPIO2		0x1E
+#define REG_KP_GPIO3		0x1F
+#define REG_GPI_EM1		0x20
+#define REG_GPI_EM2		0x21
+#define REG_GPI_EM3		0x22
+#define REG_GPIO_DIR1		0x23
+#define REG_GPIO_DIR2		0x24
+#define REG_GPIO_DIR3		0x25
+#define REG_GPIO_INT_LVL1	0x26
+#define REG_GPIO_INT_LVL2	0x27
+#define REG_GPIO_INT_LVL3	0x28
+#define REG_DEBOUNCE_DIS1	0x29
+#define REG_DEBOUNCE_DIS2	0x2A
+#define REG_DEBOUNCE_DIS3	0x2B
+#define REG_GPIO_PULL1		0x2C
+#define REG_GPIO_PULL2		0x2D
+#define REG_GPIO_PULL3		0x2E
+
+/* TCA8418 bit definitions */
+#define CFG_AI			BIT(7)
+#define CFG_GPI_E_CFG		BIT(6)
+#define CFG_OVR_FLOW_M		BIT(5)
+#define CFG_INT_CFG		BIT(4)
+#define CFG_OVR_FLOW_IEN	BIT(3)
+#define CFG_K_LCK_IEN		BIT(2)
+#define CFG_GPI_IEN		BIT(1)
+#define CFG_KE_IEN		BIT(0)
+
+#define INT_STAT_CAD_INT	BIT(4)
+#define INT_STAT_OVR_FLOW_INT	BIT(3)
+#define INT_STAT_K_LCK_INT	BIT(2)
+#define INT_STAT_GPI_INT	BIT(1)
+#define INT_STAT_K_INT		BIT(0)
+
+/* TCA8418 register masks */
+#define KEY_LCK_EC_KEC		0x7
+#define KEY_EVENT_CODE		0x7f
+#define KEY_EVENT_VALUE		0x80
+
+struct tca8418_keypad {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	unsigned int row_shift;
+};
+
+/*
+ * Write a byte to the TCA8418
+ */
+static int tca8418_write_byte(struct tca8418_keypad *keypad_data,
+			      int reg, u8 val)
+{
+	int error;
+
+	error = i2c_smbus_write_byte_data(keypad_data->client, reg, val);
+	if (error < 0) {
+		dev_err(&keypad_data->client->dev,
+			"%s failed, reg: %d, val: %d, error: %d\n",
+			__func__, reg, val, error);
+		return error;
+	}
+
+	return 0;
+}
+
+/*
+ * Read a byte from the TCA8418
+ */
+static int tca8418_read_byte(struct tca8418_keypad *keypad_data,
+			     int reg, u8 *val)
+{
+	int error;
+
+	error = i2c_smbus_read_byte_data(keypad_data->client, reg);
+	if (error < 0) {
+		dev_err(&keypad_data->client->dev,
+				"%s failed, reg: %d, error: %d\n",
+				__func__, reg, error);
+		return error;
+	}
+
+	*val = (u8)error;
+
+	return 0;
+}
+
+static void tca8418_read_keypad(struct tca8418_keypad *keypad_data)
+{
+	struct input_dev *input = keypad_data->input;
+	unsigned short *keymap = input->keycode;
+	int error, col, row;
+	u8 reg, state, code;
+
+	/* Initial read of the key event FIFO */
+	error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, &reg);
+
+	/* Assume that key code 0 signifies empty FIFO */
+	while (error >= 0 && reg > 0) {
+		state = reg & KEY_EVENT_VALUE;
+		code  = reg & KEY_EVENT_CODE;
+
+		row = code / TCA8418_MAX_COLS;
+		col = code % TCA8418_MAX_COLS;
+
+		row = (col) ? row : row - 1;
+		col = (col) ? col - 1 : TCA8418_MAX_COLS - 1;
+
+		code = MATRIX_SCAN_CODE(row, col, keypad_data->row_shift);
+		input_event(input, EV_MSC, MSC_SCAN, code);
+		input_report_key(input, keymap[code], state);
+
+		/* Read for next loop */
+		error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, &reg);
+	}
+
+	if (error < 0)
+		dev_err(&keypad_data->client->dev,
+			"unable to read REG_KEY_EVENT_A\n");
+
+	input_sync(input);
+}
+
+/*
+ * Threaded IRQ handler and this can (and will) sleep.
+ */
+static irqreturn_t tca8418_irq_handler(int irq, void *dev_id)
+{
+	struct tca8418_keypad *keypad_data = dev_id;
+	u8 reg;
+	int error;
+
+	error = tca8418_read_byte(keypad_data, REG_INT_STAT, &reg);
+	if (error) {
+		dev_err(&keypad_data->client->dev,
+			"unable to read REG_INT_STAT\n");
+		return IRQ_NONE;
+	}
+
+	if (!reg)
+		return IRQ_NONE;
+
+	if (reg & INT_STAT_OVR_FLOW_INT)
+		dev_warn(&keypad_data->client->dev, "overflow occurred\n");
+
+	if (reg & INT_STAT_K_INT)
+		tca8418_read_keypad(keypad_data);
+
+	/* Clear all interrupts, even IRQs we didn't check (GPI, CAD, LCK) */
+	reg = 0xff;
+	error = tca8418_write_byte(keypad_data, REG_INT_STAT, reg);
+	if (error)
+		dev_err(&keypad_data->client->dev,
+			"unable to clear REG_INT_STAT\n");
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Configure the TCA8418 for keypad operation
+ */
+static int tca8418_configure(struct tca8418_keypad *keypad_data,
+			     u32 rows, u32 cols)
+{
+	int reg, error;
+
+	/* Write config register, if this fails assume device not present */
+	error = tca8418_write_byte(keypad_data, REG_CFG,
+				CFG_INT_CFG | CFG_OVR_FLOW_IEN | CFG_KE_IEN);
+	if (error < 0)
+		return -ENODEV;
+
+
+	/* Assemble a mask for row and column registers */
+	reg  =  ~(~0 << rows);
+	reg += (~(~0 << cols)) << 8;
+
+	/* Set registers to keypad mode */
+	error |= tca8418_write_byte(keypad_data, REG_KP_GPIO1, reg);
+	error |= tca8418_write_byte(keypad_data, REG_KP_GPIO2, reg >> 8);
+	error |= tca8418_write_byte(keypad_data, REG_KP_GPIO3, reg >> 16);
+
+	/* Enable column debouncing */
+	error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS1, reg);
+	error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS2, reg >> 8);
+	error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS3, reg >> 16);
+
+	return error;
+}
+
+static int tca8418_keypad_probe(struct i2c_client *client,
+					  const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	const struct tca8418_keypad_platform_data *pdata =
+						dev_get_platdata(dev);
+	struct tca8418_keypad *keypad_data;
+	struct input_dev *input;
+	const struct matrix_keymap_data *keymap_data = NULL;
+	u32 rows = 0, cols = 0;
+	bool rep = false;
+	bool irq_is_gpio = false;
+	int irq;
+	int error, row_shift, max_keys;
+
+	/* Copy the platform data */
+	if (pdata) {
+		if (!pdata->keymap_data) {
+			dev_err(dev, "no keymap data defined\n");
+			return -EINVAL;
+		}
+		keymap_data = pdata->keymap_data;
+		rows = pdata->rows;
+		cols = pdata->cols;
+		rep  = pdata->rep;
+		irq_is_gpio = pdata->irq_is_gpio;
+	} else {
+		struct device_node *np = dev->of_node;
+		int err;
+
+		err = matrix_keypad_parse_of_params(dev, &rows, &cols);
+		if (err)
+			return err;
+		rep = of_property_read_bool(np, "keypad,autorepeat");
+	}
+
+	if (!rows || rows > TCA8418_MAX_ROWS) {
+		dev_err(dev, "invalid rows\n");
+		return -EINVAL;
+	}
+
+	if (!cols || cols > TCA8418_MAX_COLS) {
+		dev_err(dev, "invalid columns\n");
+		return -EINVAL;
+	}
+
+	/* Check i2c driver capabilities */
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
+		dev_err(dev, "%s adapter not supported\n",
+			dev_driver_string(&client->adapter->dev));
+		return -ENODEV;
+	}
+
+	row_shift = get_count_order(cols);
+	max_keys = rows << row_shift;
+
+	/* Allocate memory for keypad_data and input device */
+	keypad_data = devm_kzalloc(dev, sizeof(*keypad_data), GFP_KERNEL);
+	if (!keypad_data)
+		return -ENOMEM;
+
+	keypad_data->client = client;
+	keypad_data->row_shift = row_shift;
+
+	/* Initialize the chip or fail if chip isn't present */
+	error = tca8418_configure(keypad_data, rows, cols);
+	if (error < 0)
+		return error;
+
+	/* Configure input device */
+	input = devm_input_allocate_device(dev);
+	if (!input)
+		return -ENOMEM;
+
+	keypad_data->input = input;
+
+	input->name = client->name;
+	input->id.bustype = BUS_I2C;
+	input->id.vendor  = 0x0001;
+	input->id.product = 0x001;
+	input->id.version = 0x0001;
+
+	error = matrix_keypad_build_keymap(keymap_data, NULL, rows, cols,
+					   NULL, input);
+	if (error) {
+		dev_err(dev, "Failed to build keymap\n");
+		return error;
+	}
+
+	if (rep)
+		__set_bit(EV_REP, input->evbit);
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+
+	input_set_drvdata(input, keypad_data);
+
+	irq = client->irq;
+	if (irq_is_gpio)
+		irq = gpio_to_irq(irq);
+
+	error = devm_request_threaded_irq(dev, irq, NULL, tca8418_irq_handler,
+					  IRQF_TRIGGER_FALLING |
+						IRQF_SHARED |
+						IRQF_ONESHOT,
+					  client->name, keypad_data);
+	if (error) {
+		dev_err(dev, "Unable to claim irq %d; error %d\n",
+			client->irq, error);
+		return error;
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(dev, "Unable to register input device, error: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static const struct i2c_device_id tca8418_id[] = {
+	{ TCA8418_NAME, 8418, },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tca8418_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id tca8418_dt_ids[] = {
+	{ .compatible = "ti,tca8418", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, tca8418_dt_ids);
+
+/*
+ * The device tree based i2c loader looks for
+ * "i2c:" + second_component_of(property("compatible"))
+ * and therefore we need an alias to be found.
+ */
+MODULE_ALIAS("i2c:tca8418");
+#endif
+
+static struct i2c_driver tca8418_keypad_driver = {
+	.driver = {
+		.name	= TCA8418_NAME,
+		.of_match_table = of_match_ptr(tca8418_dt_ids),
+	},
+	.probe		= tca8418_keypad_probe,
+	.id_table	= tca8418_id,
+};
+
+static int __init tca8418_keypad_init(void)
+{
+	return i2c_add_driver(&tca8418_keypad_driver);
+}
+subsys_initcall(tca8418_keypad_init);
+
+static void __exit tca8418_keypad_exit(void)
+{
+	i2c_del_driver(&tca8418_keypad_driver);
+}
+module_exit(tca8418_keypad_exit);
+
+MODULE_AUTHOR("Kyle Manna <kyle.manna@fuel7.com>");
+MODULE_DESCRIPTION("Keypad driver for TCA8418");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
new file mode 100644
index 0000000..29485bc
--- /dev/null
+++ b/drivers/input/keyboard/tegra-kbc.c
@@ -0,0 +1,835 @@
+/*
+ * Keyboard class input driver for the NVIDIA Tegra SoC internal matrix
+ * keyboard controller
+ *
+ * Copyright (c) 2009-2011, NVIDIA Corporation.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/reset.h>
+#include <linux/err.h>
+
+#define KBC_MAX_KPENT	8
+
+/* Maximum row/column supported by Tegra KBC yet  is 16x8 */
+#define KBC_MAX_GPIO	24
+/* Maximum keys supported by Tegra KBC yet is 16 x 8*/
+#define KBC_MAX_KEY	(16 * 8)
+
+#define KBC_MAX_DEBOUNCE_CNT	0x3ffu
+
+/* KBC row scan time and delay for beginning the row scan. */
+#define KBC_ROW_SCAN_TIME	16
+#define KBC_ROW_SCAN_DLY	5
+
+/* KBC uses a 32KHz clock so a cycle = 1/32Khz */
+#define KBC_CYCLE_MS	32
+
+/* KBC Registers */
+
+/* KBC Control Register */
+#define KBC_CONTROL_0	0x0
+#define KBC_FIFO_TH_CNT_SHIFT(cnt)	(cnt << 14)
+#define KBC_DEBOUNCE_CNT_SHIFT(cnt)	(cnt << 4)
+#define KBC_CONTROL_FIFO_CNT_INT_EN	(1 << 3)
+#define KBC_CONTROL_KEYPRESS_INT_EN	(1 << 1)
+#define KBC_CONTROL_KBC_EN		(1 << 0)
+
+/* KBC Interrupt Register */
+#define KBC_INT_0	0x4
+#define KBC_INT_FIFO_CNT_INT_STATUS	(1 << 2)
+#define KBC_INT_KEYPRESS_INT_STATUS	(1 << 0)
+
+#define KBC_ROW_CFG0_0	0x8
+#define KBC_COL_CFG0_0	0x18
+#define KBC_TO_CNT_0	0x24
+#define KBC_INIT_DLY_0	0x28
+#define KBC_RPT_DLY_0	0x2c
+#define KBC_KP_ENT0_0	0x30
+#define KBC_KP_ENT1_0	0x34
+#define KBC_ROW0_MASK_0	0x38
+
+#define KBC_ROW_SHIFT	3
+
+enum tegra_pin_type {
+	PIN_CFG_IGNORE,
+	PIN_CFG_COL,
+	PIN_CFG_ROW,
+};
+
+/* Tegra KBC hw support */
+struct tegra_kbc_hw_support {
+	int max_rows;
+	int max_columns;
+};
+
+struct tegra_kbc_pin_cfg {
+	enum tegra_pin_type type;
+	unsigned char num;
+};
+
+struct tegra_kbc {
+	struct device *dev;
+	unsigned int debounce_cnt;
+	unsigned int repeat_cnt;
+	struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO];
+	const struct matrix_keymap_data *keymap_data;
+	bool wakeup;
+	void __iomem *mmio;
+	struct input_dev *idev;
+	int irq;
+	spinlock_t lock;
+	unsigned int repoll_dly;
+	unsigned long cp_dly_jiffies;
+	unsigned int cp_to_wkup_dly;
+	bool use_fn_map;
+	bool use_ghost_filter;
+	bool keypress_caused_wake;
+	unsigned short keycode[KBC_MAX_KEY * 2];
+	unsigned short current_keys[KBC_MAX_KPENT];
+	unsigned int num_pressed_keys;
+	u32 wakeup_key;
+	struct timer_list timer;
+	struct clk *clk;
+	struct reset_control *rst;
+	const struct tegra_kbc_hw_support *hw_support;
+	int max_keys;
+	int num_rows_and_columns;
+};
+
+static void tegra_kbc_report_released_keys(struct input_dev *input,
+					   unsigned short old_keycodes[],
+					   unsigned int old_num_keys,
+					   unsigned short new_keycodes[],
+					   unsigned int new_num_keys)
+{
+	unsigned int i, j;
+
+	for (i = 0; i < old_num_keys; i++) {
+		for (j = 0; j < new_num_keys; j++)
+			if (old_keycodes[i] == new_keycodes[j])
+				break;
+
+		if (j == new_num_keys)
+			input_report_key(input, old_keycodes[i], 0);
+	}
+}
+
+static void tegra_kbc_report_pressed_keys(struct input_dev *input,
+					  unsigned char scancodes[],
+					  unsigned short keycodes[],
+					  unsigned int num_pressed_keys)
+{
+	unsigned int i;
+
+	for (i = 0; i < num_pressed_keys; i++) {
+		input_event(input, EV_MSC, MSC_SCAN, scancodes[i]);
+		input_report_key(input, keycodes[i], 1);
+	}
+}
+
+static void tegra_kbc_report_keys(struct tegra_kbc *kbc)
+{
+	unsigned char scancodes[KBC_MAX_KPENT];
+	unsigned short keycodes[KBC_MAX_KPENT];
+	u32 val = 0;
+	unsigned int i;
+	unsigned int num_down = 0;
+	bool fn_keypress = false;
+	bool key_in_same_row = false;
+	bool key_in_same_col = false;
+
+	for (i = 0; i < KBC_MAX_KPENT; i++) {
+		if ((i % 4) == 0)
+			val = readl(kbc->mmio + KBC_KP_ENT0_0 + i);
+
+		if (val & 0x80) {
+			unsigned int col = val & 0x07;
+			unsigned int row = (val >> 3) & 0x0f;
+			unsigned char scancode =
+				MATRIX_SCAN_CODE(row, col, KBC_ROW_SHIFT);
+
+			scancodes[num_down] = scancode;
+			keycodes[num_down] = kbc->keycode[scancode];
+			/* If driver uses Fn map, do not report the Fn key. */
+			if ((keycodes[num_down] == KEY_FN) && kbc->use_fn_map)
+				fn_keypress = true;
+			else
+				num_down++;
+		}
+
+		val >>= 8;
+	}
+
+	/*
+	 * Matrix keyboard designs are prone to keyboard ghosting.
+	 * Ghosting occurs if there are 3 keys such that -
+	 * any 2 of the 3 keys share a row, and any 2 of them share a column.
+	 * If so ignore the key presses for this iteration.
+	 */
+	if (kbc->use_ghost_filter && num_down >= 3) {
+		for (i = 0; i < num_down; i++) {
+			unsigned int j;
+			u8 curr_col = scancodes[i] & 0x07;
+			u8 curr_row = scancodes[i] >> KBC_ROW_SHIFT;
+
+			/*
+			 * Find 2 keys such that one key is in the same row
+			 * and the other is in the same column as the i-th key.
+			 */
+			for (j = i + 1; j < num_down; j++) {
+				u8 col = scancodes[j] & 0x07;
+				u8 row = scancodes[j] >> KBC_ROW_SHIFT;
+
+				if (col == curr_col)
+					key_in_same_col = true;
+				if (row == curr_row)
+					key_in_same_row = true;
+			}
+		}
+	}
+
+	/*
+	 * If the platform uses Fn keymaps, translate keys on a Fn keypress.
+	 * Function keycodes are max_keys apart from the plain keycodes.
+	 */
+	if (fn_keypress) {
+		for (i = 0; i < num_down; i++) {
+			scancodes[i] += kbc->max_keys;
+			keycodes[i] = kbc->keycode[scancodes[i]];
+		}
+	}
+
+	/* Ignore the key presses for this iteration? */
+	if (key_in_same_col && key_in_same_row)
+		return;
+
+	tegra_kbc_report_released_keys(kbc->idev,
+				       kbc->current_keys, kbc->num_pressed_keys,
+				       keycodes, num_down);
+	tegra_kbc_report_pressed_keys(kbc->idev, scancodes, keycodes, num_down);
+	input_sync(kbc->idev);
+
+	memcpy(kbc->current_keys, keycodes, sizeof(kbc->current_keys));
+	kbc->num_pressed_keys = num_down;
+}
+
+static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable)
+{
+	u32 val;
+
+	val = readl(kbc->mmio + KBC_CONTROL_0);
+	if (enable)
+		val |= KBC_CONTROL_FIFO_CNT_INT_EN;
+	else
+		val &= ~KBC_CONTROL_FIFO_CNT_INT_EN;
+	writel(val, kbc->mmio + KBC_CONTROL_0);
+}
+
+static void tegra_kbc_keypress_timer(unsigned long data)
+{
+	struct tegra_kbc *kbc = (struct tegra_kbc *)data;
+	unsigned long flags;
+	u32 val;
+	unsigned int i;
+
+	spin_lock_irqsave(&kbc->lock, flags);
+
+	val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf;
+	if (val) {
+		unsigned long dly;
+
+		tegra_kbc_report_keys(kbc);
+
+		/*
+		 * If more than one keys are pressed we need not wait
+		 * for the repoll delay.
+		 */
+		dly = (val == 1) ? kbc->repoll_dly : 1;
+		mod_timer(&kbc->timer, jiffies + msecs_to_jiffies(dly));
+	} else {
+		/* Release any pressed keys and exit the polling loop */
+		for (i = 0; i < kbc->num_pressed_keys; i++)
+			input_report_key(kbc->idev, kbc->current_keys[i], 0);
+		input_sync(kbc->idev);
+
+		kbc->num_pressed_keys = 0;
+
+		/* All keys are released so enable the keypress interrupt */
+		tegra_kbc_set_fifo_interrupt(kbc, true);
+	}
+
+	spin_unlock_irqrestore(&kbc->lock, flags);
+}
+
+static irqreturn_t tegra_kbc_isr(int irq, void *args)
+{
+	struct tegra_kbc *kbc = args;
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&kbc->lock, flags);
+
+	/*
+	 * Quickly bail out & reenable interrupts if the fifo threshold
+	 * count interrupt wasn't the interrupt source
+	 */
+	val = readl(kbc->mmio + KBC_INT_0);
+	writel(val, kbc->mmio + KBC_INT_0);
+
+	if (val & KBC_INT_FIFO_CNT_INT_STATUS) {
+		/*
+		 * Until all keys are released, defer further processing to
+		 * the polling loop in tegra_kbc_keypress_timer.
+		 */
+		tegra_kbc_set_fifo_interrupt(kbc, false);
+		mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies);
+	} else if (val & KBC_INT_KEYPRESS_INT_STATUS) {
+		/* We can be here only through system resume path */
+		kbc->keypress_caused_wake = true;
+	}
+
+	spin_unlock_irqrestore(&kbc->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
+{
+	int i;
+	unsigned int rst_val;
+
+	/* Either mask all keys or none. */
+	rst_val = (filter && !kbc->wakeup) ? ~0 : 0;
+
+	for (i = 0; i < kbc->hw_support->max_rows; i++)
+		writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4);
+}
+
+static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
+{
+	int i;
+
+	for (i = 0; i < KBC_MAX_GPIO; i++) {
+		u32 r_shft = 5 * (i % 6);
+		u32 c_shft = 4 * (i % 8);
+		u32 r_mask = 0x1f << r_shft;
+		u32 c_mask = 0x0f << c_shft;
+		u32 r_offs = (i / 6) * 4 + KBC_ROW_CFG0_0;
+		u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0;
+		u32 row_cfg = readl(kbc->mmio + r_offs);
+		u32 col_cfg = readl(kbc->mmio + c_offs);
+
+		row_cfg &= ~r_mask;
+		col_cfg &= ~c_mask;
+
+		switch (kbc->pin_cfg[i].type) {
+		case PIN_CFG_ROW:
+			row_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << r_shft;
+			break;
+
+		case PIN_CFG_COL:
+			col_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << c_shft;
+			break;
+
+		case PIN_CFG_IGNORE:
+			break;
+		}
+
+		writel(row_cfg, kbc->mmio + r_offs);
+		writel(col_cfg, kbc->mmio + c_offs);
+	}
+}
+
+static int tegra_kbc_start(struct tegra_kbc *kbc)
+{
+	unsigned int debounce_cnt;
+	u32 val = 0;
+
+	clk_prepare_enable(kbc->clk);
+
+	/* Reset the KBC controller to clear all previous status.*/
+	reset_control_assert(kbc->rst);
+	udelay(100);
+	reset_control_deassert(kbc->rst);
+	udelay(100);
+
+	tegra_kbc_config_pins(kbc);
+	tegra_kbc_setup_wakekeys(kbc, false);
+
+	writel(kbc->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0);
+
+	/* Keyboard debounce count is maximum of 12 bits. */
+	debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
+	val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt);
+	val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */
+	val |= KBC_CONTROL_FIFO_CNT_INT_EN;  /* interrupt on FIFO threshold */
+	val |= KBC_CONTROL_KBC_EN;     /* enable */
+	writel(val, kbc->mmio + KBC_CONTROL_0);
+
+	/*
+	 * Compute the delay(ns) from interrupt mode to continuous polling
+	 * mode so the timer routine is scheduled appropriately.
+	 */
+	val = readl(kbc->mmio + KBC_INIT_DLY_0);
+	kbc->cp_dly_jiffies = usecs_to_jiffies((val & 0xfffff) * 32);
+
+	kbc->num_pressed_keys = 0;
+
+	/*
+	 * Atomically clear out any remaining entries in the key FIFO
+	 * and enable keyboard interrupts.
+	 */
+	while (1) {
+		val = readl(kbc->mmio + KBC_INT_0);
+		val >>= 4;
+		if (!val)
+			break;
+
+		val = readl(kbc->mmio + KBC_KP_ENT0_0);
+		val = readl(kbc->mmio + KBC_KP_ENT1_0);
+	}
+	writel(0x7, kbc->mmio + KBC_INT_0);
+
+	enable_irq(kbc->irq);
+
+	return 0;
+}
+
+static void tegra_kbc_stop(struct tegra_kbc *kbc)
+{
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&kbc->lock, flags);
+	val = readl(kbc->mmio + KBC_CONTROL_0);
+	val &= ~1;
+	writel(val, kbc->mmio + KBC_CONTROL_0);
+	spin_unlock_irqrestore(&kbc->lock, flags);
+
+	disable_irq(kbc->irq);
+	del_timer_sync(&kbc->timer);
+
+	clk_disable_unprepare(kbc->clk);
+}
+
+static int tegra_kbc_open(struct input_dev *dev)
+{
+	struct tegra_kbc *kbc = input_get_drvdata(dev);
+
+	return tegra_kbc_start(kbc);
+}
+
+static void tegra_kbc_close(struct input_dev *dev)
+{
+	struct tegra_kbc *kbc = input_get_drvdata(dev);
+
+	return tegra_kbc_stop(kbc);
+}
+
+static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc,
+					unsigned int *num_rows)
+{
+	int i;
+
+	*num_rows = 0;
+
+	for (i = 0; i < KBC_MAX_GPIO; i++) {
+		const struct tegra_kbc_pin_cfg *pin_cfg = &kbc->pin_cfg[i];
+
+		switch (pin_cfg->type) {
+		case PIN_CFG_ROW:
+			if (pin_cfg->num >= kbc->hw_support->max_rows) {
+				dev_err(kbc->dev,
+					"pin_cfg[%d]: invalid row number %d\n",
+					i, pin_cfg->num);
+				return false;
+			}
+			(*num_rows)++;
+			break;
+
+		case PIN_CFG_COL:
+			if (pin_cfg->num >= kbc->hw_support->max_columns) {
+				dev_err(kbc->dev,
+					"pin_cfg[%d]: invalid column number %d\n",
+					i, pin_cfg->num);
+				return false;
+			}
+			break;
+
+		case PIN_CFG_IGNORE:
+			break;
+
+		default:
+			dev_err(kbc->dev,
+				"pin_cfg[%d]: invalid entry type %d\n",
+				pin_cfg->type, pin_cfg->num);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
+{
+	struct device_node *np = kbc->dev->of_node;
+	u32 prop;
+	int i;
+	u32 num_rows = 0;
+	u32 num_cols = 0;
+	u32 cols_cfg[KBC_MAX_GPIO];
+	u32 rows_cfg[KBC_MAX_GPIO];
+	int proplen;
+	int ret;
+
+	if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop))
+		kbc->debounce_cnt = prop;
+
+	if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop))
+		kbc->repeat_cnt = prop;
+
+	if (of_find_property(np, "nvidia,needs-ghost-filter", NULL))
+		kbc->use_ghost_filter = true;
+
+	if (of_property_read_bool(np, "wakeup-source") ||
+	    of_property_read_bool(np, "nvidia,wakeup-source")) /* legacy */
+		kbc->wakeup = true;
+
+	if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) {
+		dev_err(kbc->dev, "property nvidia,kbc-row-pins not found\n");
+		return -ENOENT;
+	}
+	num_rows = proplen / sizeof(u32);
+
+	if (!of_get_property(np, "nvidia,kbc-col-pins", &proplen)) {
+		dev_err(kbc->dev, "property nvidia,kbc-col-pins not found\n");
+		return -ENOENT;
+	}
+	num_cols = proplen / sizeof(u32);
+
+	if (num_rows > kbc->hw_support->max_rows) {
+		dev_err(kbc->dev,
+			"Number of rows is more than supported by hardware\n");
+		return -EINVAL;
+	}
+
+	if (num_cols > kbc->hw_support->max_columns) {
+		dev_err(kbc->dev,
+			"Number of cols is more than supported by hardware\n");
+		return -EINVAL;
+	}
+
+	if (!of_get_property(np, "linux,keymap", &proplen)) {
+		dev_err(kbc->dev, "property linux,keymap not found\n");
+		return -ENOENT;
+	}
+
+	if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) {
+		dev_err(kbc->dev,
+			"keypad rows/columns not porperly specified\n");
+		return -EINVAL;
+	}
+
+	/* Set all pins as non-configured */
+	for (i = 0; i < kbc->num_rows_and_columns; i++)
+		kbc->pin_cfg[i].type = PIN_CFG_IGNORE;
+
+	ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins",
+				rows_cfg, num_rows);
+	if (ret < 0) {
+		dev_err(kbc->dev, "Rows configurations are not proper\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_array(np, "nvidia,kbc-col-pins",
+				cols_cfg, num_cols);
+	if (ret < 0) {
+		dev_err(kbc->dev, "Cols configurations are not proper\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_rows; i++) {
+		kbc->pin_cfg[rows_cfg[i]].type = PIN_CFG_ROW;
+		kbc->pin_cfg[rows_cfg[i]].num = i;
+	}
+
+	for (i = 0; i < num_cols; i++) {
+		kbc->pin_cfg[cols_cfg[i]].type = PIN_CFG_COL;
+		kbc->pin_cfg[cols_cfg[i]].num = i;
+	}
+
+	return 0;
+}
+
+static const struct tegra_kbc_hw_support tegra20_kbc_hw_support = {
+	.max_rows	= 16,
+	.max_columns	= 8,
+};
+
+static const struct tegra_kbc_hw_support tegra11_kbc_hw_support = {
+	.max_rows	= 11,
+	.max_columns	= 8,
+};
+
+static const struct of_device_id tegra_kbc_of_match[] = {
+	{ .compatible = "nvidia,tegra114-kbc", .data = &tegra11_kbc_hw_support},
+	{ .compatible = "nvidia,tegra30-kbc", .data = &tegra20_kbc_hw_support},
+	{ .compatible = "nvidia,tegra20-kbc", .data = &tegra20_kbc_hw_support},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, tegra_kbc_of_match);
+
+static int tegra_kbc_probe(struct platform_device *pdev)
+{
+	struct tegra_kbc *kbc;
+	struct resource *res;
+	int err;
+	int num_rows = 0;
+	unsigned int debounce_cnt;
+	unsigned int scan_time_rows;
+	unsigned int keymap_rows;
+	const struct of_device_id *match;
+
+	match = of_match_device(tegra_kbc_of_match, &pdev->dev);
+
+	kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL);
+	if (!kbc) {
+		dev_err(&pdev->dev, "failed to alloc memory for kbc\n");
+		return -ENOMEM;
+	}
+
+	kbc->dev = &pdev->dev;
+	kbc->hw_support = match->data;
+	kbc->max_keys = kbc->hw_support->max_rows *
+				kbc->hw_support->max_columns;
+	kbc->num_rows_and_columns = kbc->hw_support->max_rows +
+					kbc->hw_support->max_columns;
+	keymap_rows = kbc->max_keys;
+	spin_lock_init(&kbc->lock);
+
+	err = tegra_kbc_parse_dt(kbc);
+	if (err)
+		return err;
+
+	if (!tegra_kbc_check_pin_cfg(kbc, &num_rows))
+		return -EINVAL;
+
+	kbc->irq = platform_get_irq(pdev, 0);
+	if (kbc->irq < 0) {
+		dev_err(&pdev->dev, "failed to get keyboard IRQ\n");
+		return -ENXIO;
+	}
+
+	kbc->idev = devm_input_allocate_device(&pdev->dev);
+	if (!kbc->idev) {
+		dev_err(&pdev->dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	kbc->mmio = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(kbc->mmio))
+		return PTR_ERR(kbc->mmio);
+
+	kbc->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(kbc->clk)) {
+		dev_err(&pdev->dev, "failed to get keyboard clock\n");
+		return PTR_ERR(kbc->clk);
+	}
+
+	kbc->rst = devm_reset_control_get(&pdev->dev, "kbc");
+	if (IS_ERR(kbc->rst)) {
+		dev_err(&pdev->dev, "failed to get keyboard reset\n");
+		return PTR_ERR(kbc->rst);
+	}
+
+	/*
+	 * The time delay between two consecutive reads of the FIFO is
+	 * the sum of the repeat time and the time taken for scanning
+	 * the rows. There is an additional delay before the row scanning
+	 * starts. The repoll delay is computed in milliseconds.
+	 */
+	debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
+	scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows;
+	kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + kbc->repeat_cnt;
+	kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS);
+
+	kbc->idev->name = pdev->name;
+	kbc->idev->id.bustype = BUS_HOST;
+	kbc->idev->dev.parent = &pdev->dev;
+	kbc->idev->open = tegra_kbc_open;
+	kbc->idev->close = tegra_kbc_close;
+
+	if (kbc->keymap_data && kbc->use_fn_map)
+		keymap_rows *= 2;
+
+	err = matrix_keypad_build_keymap(kbc->keymap_data, NULL,
+					 keymap_rows,
+					 kbc->hw_support->max_columns,
+					 kbc->keycode, kbc->idev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to setup keymap\n");
+		return err;
+	}
+
+	__set_bit(EV_REP, kbc->idev->evbit);
+	input_set_capability(kbc->idev, EV_MSC, MSC_SCAN);
+
+	input_set_drvdata(kbc->idev, kbc);
+
+	err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr,
+			       IRQF_TRIGGER_HIGH, pdev->name, kbc);
+	if (err) {
+		dev_err(&pdev->dev, "failed to request keyboard IRQ\n");
+		return err;
+	}
+
+	disable_irq(kbc->irq);
+
+	err = input_register_device(kbc->idev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		return err;
+	}
+
+	platform_set_drvdata(pdev, kbc);
+	device_init_wakeup(&pdev->dev, kbc->wakeup);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable)
+{
+	u32 val;
+
+	val = readl(kbc->mmio + KBC_CONTROL_0);
+	if (enable)
+		val |= KBC_CONTROL_KEYPRESS_INT_EN;
+	else
+		val &= ~KBC_CONTROL_KEYPRESS_INT_EN;
+	writel(val, kbc->mmio + KBC_CONTROL_0);
+}
+
+static int tegra_kbc_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct tegra_kbc *kbc = platform_get_drvdata(pdev);
+
+	mutex_lock(&kbc->idev->mutex);
+	if (device_may_wakeup(&pdev->dev)) {
+		disable_irq(kbc->irq);
+		del_timer_sync(&kbc->timer);
+		tegra_kbc_set_fifo_interrupt(kbc, false);
+
+		/* Forcefully clear the interrupt status */
+		writel(0x7, kbc->mmio + KBC_INT_0);
+		/*
+		 * Store the previous resident time of continuous polling mode.
+		 * Force the keyboard into interrupt mode.
+		 */
+		kbc->cp_to_wkup_dly = readl(kbc->mmio + KBC_TO_CNT_0);
+		writel(0, kbc->mmio + KBC_TO_CNT_0);
+
+		tegra_kbc_setup_wakekeys(kbc, true);
+		msleep(30);
+
+		kbc->keypress_caused_wake = false;
+		/* Enable keypress interrupt before going into suspend. */
+		tegra_kbc_set_keypress_interrupt(kbc, true);
+		enable_irq(kbc->irq);
+		enable_irq_wake(kbc->irq);
+	} else {
+		if (kbc->idev->users)
+			tegra_kbc_stop(kbc);
+	}
+	mutex_unlock(&kbc->idev->mutex);
+
+	return 0;
+}
+
+static int tegra_kbc_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct tegra_kbc *kbc = platform_get_drvdata(pdev);
+	int err = 0;
+
+	mutex_lock(&kbc->idev->mutex);
+	if (device_may_wakeup(&pdev->dev)) {
+		disable_irq_wake(kbc->irq);
+		tegra_kbc_setup_wakekeys(kbc, false);
+		/* We will use fifo interrupts for key detection. */
+		tegra_kbc_set_keypress_interrupt(kbc, false);
+
+		/* Restore the resident time of continuous polling mode. */
+		writel(kbc->cp_to_wkup_dly, kbc->mmio + KBC_TO_CNT_0);
+
+		tegra_kbc_set_fifo_interrupt(kbc, true);
+
+		if (kbc->keypress_caused_wake && kbc->wakeup_key) {
+			/*
+			 * We can't report events directly from the ISR
+			 * because timekeeping is stopped when processing
+			 * wakeup request and we get a nasty warning when
+			 * we try to call do_gettimeofday() in evdev
+			 * handler.
+			 */
+			input_report_key(kbc->idev, kbc->wakeup_key, 1);
+			input_sync(kbc->idev);
+			input_report_key(kbc->idev, kbc->wakeup_key, 0);
+			input_sync(kbc->idev);
+		}
+	} else {
+		if (kbc->idev->users)
+			err = tegra_kbc_start(kbc);
+	}
+	mutex_unlock(&kbc->idev->mutex);
+
+	return err;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tegra_kbc_pm_ops, tegra_kbc_suspend, tegra_kbc_resume);
+
+static struct platform_driver tegra_kbc_driver = {
+	.probe		= tegra_kbc_probe,
+	.driver	= {
+		.name	= "tegra-kbc",
+		.pm	= &tegra_kbc_pm_ops,
+		.of_match_table = tegra_kbc_of_match,
+	},
+};
+module_platform_driver(tegra_kbc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Rakesh Iyer <riyer@nvidia.com>");
+MODULE_DESCRIPTION("Tegra matrix keyboard controller driver");
+MODULE_ALIAS("platform:tegra-kbc");
diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
new file mode 100644
index 0000000..bbcccd6
--- /dev/null
+++ b/drivers/input/keyboard/twl4030_keypad.c
@@ -0,0 +1,470 @@
+/*
+ * twl4030_keypad.c - driver for 8x8 keypad controller in twl4030 chips
+ *
+ * Copyright (C) 2007 Texas Instruments, Inc.
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Code re-written for 2430SDP by:
+ * Syed Mohammed Khasim <x0khasim@ti.com>
+ *
+ * Initial Code:
+ * Manjunatha G K <manjugk@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+/*
+ * The TWL4030 family chips include a keypad controller that supports
+ * up to an 8x8 switch matrix.  The controller can issue system wakeup
+ * events, since it uses only the always-on 32KiHz oscillator, and has
+ * an internal state machine that decodes pressed keys, including
+ * multi-key combinations.
+ *
+ * This driver lets boards define what keycodes they wish to report for
+ * which scancodes, as part of the "struct twl4030_keypad_data" used in
+ * the probe() routine.
+ *
+ * See the TPS65950 documentation; that's the general availability
+ * version of the TWL5030 second generation part.
+ */
+#define TWL4030_MAX_ROWS	8	/* TWL4030 hard limit */
+#define TWL4030_MAX_COLS	8
+/*
+ * Note that we add space for an extra column so that we can handle
+ * row lines connected to the gnd (see twl4030_col_xlate()).
+ */
+#define TWL4030_ROW_SHIFT	4
+#define TWL4030_KEYMAP_SIZE	(TWL4030_MAX_ROWS << TWL4030_ROW_SHIFT)
+
+struct twl4030_keypad {
+	unsigned short	keymap[TWL4030_KEYMAP_SIZE];
+	u16		kp_state[TWL4030_MAX_ROWS];
+	bool		autorepeat;
+	unsigned	n_rows;
+	unsigned	n_cols;
+	unsigned	irq;
+
+	struct device *dbg_dev;
+	struct input_dev *input;
+};
+
+/*----------------------------------------------------------------------*/
+
+/* arbitrary prescaler value 0..7 */
+#define PTV_PRESCALER			4
+
+/* Register Offsets */
+#define KEYP_CTRL			0x00
+#define KEYP_DEB			0x01
+#define KEYP_LONG_KEY			0x02
+#define KEYP_LK_PTV			0x03
+#define KEYP_TIMEOUT_L			0x04
+#define KEYP_TIMEOUT_H			0x05
+#define KEYP_KBC			0x06
+#define KEYP_KBR			0x07
+#define KEYP_SMS			0x08
+#define KEYP_FULL_CODE_7_0		0x09	/* row 0 column status */
+#define KEYP_FULL_CODE_15_8		0x0a	/* ... row 1 ... */
+#define KEYP_FULL_CODE_23_16		0x0b
+#define KEYP_FULL_CODE_31_24		0x0c
+#define KEYP_FULL_CODE_39_32		0x0d
+#define KEYP_FULL_CODE_47_40		0x0e
+#define KEYP_FULL_CODE_55_48		0x0f
+#define KEYP_FULL_CODE_63_56		0x10
+#define KEYP_ISR1			0x11
+#define KEYP_IMR1			0x12
+#define KEYP_ISR2			0x13
+#define KEYP_IMR2			0x14
+#define KEYP_SIR			0x15
+#define KEYP_EDR			0x16	/* edge triggers */
+#define KEYP_SIH_CTRL			0x17
+
+/* KEYP_CTRL_REG Fields */
+#define KEYP_CTRL_SOFT_NRST		BIT(0)
+#define KEYP_CTRL_SOFTMODEN		BIT(1)
+#define KEYP_CTRL_LK_EN			BIT(2)
+#define KEYP_CTRL_TOE_EN		BIT(3)
+#define KEYP_CTRL_TOLE_EN		BIT(4)
+#define KEYP_CTRL_RP_EN			BIT(5)
+#define KEYP_CTRL_KBD_ON		BIT(6)
+
+/* KEYP_DEB, KEYP_LONG_KEY, KEYP_TIMEOUT_x*/
+#define KEYP_PERIOD_US(t, prescale)	((t) / (31 << (prescale + 1)) - 1)
+
+/* KEYP_LK_PTV_REG Fields */
+#define KEYP_LK_PTV_PTV_SHIFT		5
+
+/* KEYP_{IMR,ISR,SIR} Fields */
+#define KEYP_IMR1_MIS			BIT(3)
+#define KEYP_IMR1_TO			BIT(2)
+#define KEYP_IMR1_LK			BIT(1)
+#define KEYP_IMR1_KP			BIT(0)
+
+/* KEYP_EDR Fields */
+#define KEYP_EDR_KP_FALLING		0x01
+#define KEYP_EDR_KP_RISING		0x02
+#define KEYP_EDR_KP_BOTH		0x03
+#define KEYP_EDR_LK_FALLING		0x04
+#define KEYP_EDR_LK_RISING		0x08
+#define KEYP_EDR_TO_FALLING		0x10
+#define KEYP_EDR_TO_RISING		0x20
+#define KEYP_EDR_MIS_FALLING		0x40
+#define KEYP_EDR_MIS_RISING		0x80
+
+
+/*----------------------------------------------------------------------*/
+
+static int twl4030_kpread(struct twl4030_keypad *kp,
+		u8 *data, u32 reg, u8 num_bytes)
+{
+	int ret = twl_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes);
+
+	if (ret < 0)
+		dev_warn(kp->dbg_dev,
+			"Couldn't read TWL4030: %X - ret %d[%x]\n",
+			 reg, ret, ret);
+
+	return ret;
+}
+
+static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg)
+{
+	int ret = twl_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg);
+
+	if (ret < 0)
+		dev_warn(kp->dbg_dev,
+			"Could not write TWL4030: %X - ret %d[%x]\n",
+			 reg, ret, ret);
+
+	return ret;
+}
+
+static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col)
+{
+	/* If all bits in a row are active for all coloumns then
+	 * we have that row line connected to gnd. Mark this
+	 * key on as if it was on matrix position n_cols (ie
+	 * one higher than the size of the matrix).
+	 */
+	if (col == 0xFF)
+		return 1 << kp->n_cols;
+	else
+		return col & ((1 << kp->n_cols) - 1);
+}
+
+static int twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state)
+{
+	u8 new_state[TWL4030_MAX_ROWS];
+	int row;
+	int ret = twl4030_kpread(kp, new_state,
+				 KEYP_FULL_CODE_7_0, kp->n_rows);
+	if (ret >= 0)
+		for (row = 0; row < kp->n_rows; row++)
+			state[row] = twl4030_col_xlate(kp, new_state[row]);
+
+	return ret;
+}
+
+static bool twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state)
+{
+	int i;
+	u16 check = 0;
+
+	for (i = 0; i < kp->n_rows; i++) {
+		u16 col = key_state[i];
+
+		if ((col & check) && hweight16(col) > 1)
+			return true;
+
+		check |= col;
+	}
+
+	return false;
+}
+
+static void twl4030_kp_scan(struct twl4030_keypad *kp, bool release_all)
+{
+	struct input_dev *input = kp->input;
+	u16 new_state[TWL4030_MAX_ROWS];
+	int col, row;
+
+	if (release_all)
+		memset(new_state, 0, sizeof(new_state));
+	else {
+		/* check for any changes */
+		int ret = twl4030_read_kp_matrix_state(kp, new_state);
+
+		if (ret < 0)	/* panic ... */
+			return;
+
+		if (twl4030_is_in_ghost_state(kp, new_state))
+			return;
+	}
+
+	/* check for changes and print those */
+	for (row = 0; row < kp->n_rows; row++) {
+		int changed = new_state[row] ^ kp->kp_state[row];
+
+		if (!changed)
+			continue;
+
+		/* Extra column handles "all gnd" rows */
+		for (col = 0; col < kp->n_cols + 1; col++) {
+			int code;
+
+			if (!(changed & (1 << col)))
+				continue;
+
+			dev_dbg(kp->dbg_dev, "key [%d:%d] %s\n", row, col,
+				(new_state[row] & (1 << col)) ?
+				"press" : "release");
+
+			code = MATRIX_SCAN_CODE(row, col, TWL4030_ROW_SHIFT);
+			input_event(input, EV_MSC, MSC_SCAN, code);
+			input_report_key(input, kp->keymap[code],
+					 new_state[row] & (1 << col));
+		}
+		kp->kp_state[row] = new_state[row];
+	}
+	input_sync(input);
+}
+
+/*
+ * Keypad interrupt handler
+ */
+static irqreturn_t do_kp_irq(int irq, void *_kp)
+{
+	struct twl4030_keypad *kp = _kp;
+	u8 reg;
+	int ret;
+
+	/* Read & Clear TWL4030 pending interrupt */
+	ret = twl4030_kpread(kp, &reg, KEYP_ISR1, 1);
+
+	/* Release all keys if I2C has gone bad or
+	 * the KEYP has gone to idle state */
+	if (ret >= 0 && (reg & KEYP_IMR1_KP))
+		twl4030_kp_scan(kp, false);
+	else
+		twl4030_kp_scan(kp, true);
+
+	return IRQ_HANDLED;
+}
+
+static int twl4030_kp_program(struct twl4030_keypad *kp)
+{
+	u8 reg;
+	int i;
+
+	/* Enable controller, with hardware decoding but not autorepeat */
+	reg = KEYP_CTRL_SOFT_NRST | KEYP_CTRL_SOFTMODEN
+		| KEYP_CTRL_TOE_EN | KEYP_CTRL_KBD_ON;
+	if (twl4030_kpwrite_u8(kp, reg, KEYP_CTRL) < 0)
+		return -EIO;
+
+	/* NOTE:  we could use sih_setup() here to package keypad
+	 * event sources as four different IRQs ... but we don't.
+	 */
+
+	/* Enable TO rising and KP rising and falling edge detection */
+	reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING;
+	if (twl4030_kpwrite_u8(kp, reg, KEYP_EDR) < 0)
+		return -EIO;
+
+	/* Set PTV prescaler Field */
+	reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT);
+	if (twl4030_kpwrite_u8(kp, reg, KEYP_LK_PTV) < 0)
+		return -EIO;
+
+	/* Set key debounce time to 20 ms */
+	i = KEYP_PERIOD_US(20000, PTV_PRESCALER);
+	if (twl4030_kpwrite_u8(kp, i, KEYP_DEB) < 0)
+		return -EIO;
+
+	/* Set timeout period to 200 ms */
+	i = KEYP_PERIOD_US(200000, PTV_PRESCALER);
+	if (twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L) < 0)
+		return -EIO;
+
+	if (twl4030_kpwrite_u8(kp, (i >> 8), KEYP_TIMEOUT_H) < 0)
+		return -EIO;
+
+	/*
+	 * Enable Clear-on-Read; disable remembering events that fire
+	 * after the IRQ but before our handler acks (reads) them,
+	 */
+	reg = TWL4030_SIH_CTRL_COR_MASK | TWL4030_SIH_CTRL_PENDDIS_MASK;
+	if (twl4030_kpwrite_u8(kp, reg, KEYP_SIH_CTRL) < 0)
+		return -EIO;
+
+	/* initialize key state; irqs update it from here on */
+	if (twl4030_read_kp_matrix_state(kp, kp->kp_state) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+/*
+ * Registers keypad device with input subsystem
+ * and configures TWL4030 keypad registers
+ */
+static int twl4030_kp_probe(struct platform_device *pdev)
+{
+	struct twl4030_keypad_data *pdata = dev_get_platdata(&pdev->dev);
+	const struct matrix_keymap_data *keymap_data = NULL;
+	struct twl4030_keypad *kp;
+	struct input_dev *input;
+	u8 reg;
+	int error;
+
+	kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
+	if (!kp)
+		return -ENOMEM;
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input)
+		return -ENOMEM;
+
+	/* get the debug device */
+	kp->dbg_dev		= &pdev->dev;
+	kp->input		= input;
+
+	/* setup input device */
+	input->name		= "TWL4030 Keypad";
+	input->phys		= "twl4030_keypad/input0";
+
+	input->id.bustype	= BUS_HOST;
+	input->id.vendor	= 0x0001;
+	input->id.product	= 0x0001;
+	input->id.version	= 0x0003;
+
+	if (pdata) {
+		if (!pdata->rows || !pdata->cols || !pdata->keymap_data) {
+			dev_err(&pdev->dev, "Missing platform_data\n");
+			return -EINVAL;
+		}
+
+		kp->n_rows = pdata->rows;
+		kp->n_cols = pdata->cols;
+		kp->autorepeat = pdata->rep;
+		keymap_data = pdata->keymap_data;
+	} else {
+		error = matrix_keypad_parse_of_params(&pdev->dev, &kp->n_rows,
+						      &kp->n_cols);
+		if (error)
+			return error;
+
+		kp->autorepeat = true;
+	}
+
+	if (kp->n_rows > TWL4030_MAX_ROWS || kp->n_cols > TWL4030_MAX_COLS) {
+		dev_err(&pdev->dev,
+			"Invalid rows/cols amount specified in platform/devicetree data\n");
+		return -EINVAL;
+	}
+
+	kp->irq = platform_get_irq(pdev, 0);
+	if (!kp->irq) {
+		dev_err(&pdev->dev, "no keyboard irq assigned\n");
+		return -EINVAL;
+	}
+
+	error = matrix_keypad_build_keymap(keymap_data, NULL,
+					   TWL4030_MAX_ROWS,
+					   1 << TWL4030_ROW_SHIFT,
+					   kp->keymap, input);
+	if (error) {
+		dev_err(kp->dbg_dev, "Failed to build keymap\n");
+		return error;
+	}
+
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+	/* Enable auto repeat feature of Linux input subsystem */
+	if (kp->autorepeat)
+		__set_bit(EV_REP, input->evbit);
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(kp->dbg_dev,
+			"Unable to register twl4030 keypad device\n");
+		return error;
+	}
+
+	error = twl4030_kp_program(kp);
+	if (error)
+		return error;
+
+	/*
+	 * This ISR will always execute in kernel thread context because of
+	 * the need to access the TWL4030 over the I2C bus.
+	 *
+	 * NOTE:  we assume this host is wired to TWL4040 INT1, not INT2 ...
+	 */
+	error = devm_request_threaded_irq(&pdev->dev, kp->irq, NULL, do_kp_irq,
+					  0, pdev->name, kp);
+	if (error) {
+		dev_info(kp->dbg_dev, "request_irq failed for irq no=%d: %d\n",
+			kp->irq, error);
+		return error;
+	}
+
+	/* Enable KP and TO interrupts now. */
+	reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
+	if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) {
+		/* mask all events - we don't care about the result */
+		(void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
+		return -EIO;
+	}
+
+	platform_set_drvdata(pdev, kp);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id twl4030_keypad_dt_match_table[] = {
+	{ .compatible = "ti,twl4030-keypad" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, twl4030_keypad_dt_match_table);
+#endif
+
+/*
+ * NOTE: twl4030 are multi-function devices connected via I2C.
+ * So this device is a child of an I2C parent, thus it needs to
+ * support unplug/replug (which most platform devices don't).
+ */
+
+static struct platform_driver twl4030_kp_driver = {
+	.probe		= twl4030_kp_probe,
+	.driver		= {
+		.name	= "twl4030_keypad",
+		.of_match_table = of_match_ptr(twl4030_keypad_dt_match_table),
+	},
+};
+module_platform_driver(twl4030_kp_driver);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("TWL4030 Keypad Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:twl4030_keypad");
diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c
new file mode 100644
index 0000000..a1ff69c
--- /dev/null
+++ b/drivers/input/keyboard/w90p910_keypad.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2008-2009 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * 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;version 2 of the License.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <linux/platform_data/keypad-w90p910.h>
+
+/* Keypad Interface Control Registers */
+#define KPI_CONF		0x00
+#define KPI_3KCONF		0x04
+#define KPI_LPCONF		0x08
+#define KPI_STATUS		0x0C
+
+#define IS1KEY			(0x01 << 16)
+#define INTTR			(0x01 << 21)
+#define KEY0R			(0x0f << 3)
+#define KEY0C			0x07
+#define DEBOUNCE_BIT		0x08
+#define KSIZE0			(0x01 << 16)
+#define KSIZE1			(0x01 << 17)
+#define KPSEL			(0x01 << 19)
+#define ENKP			(0x01 << 18)
+
+#define KGET_RAW(n)		(((n) & KEY0R) >> 3)
+#define KGET_COLUMN(n)		((n) & KEY0C)
+
+#define W90P910_NUM_ROWS	8
+#define W90P910_NUM_COLS	8
+#define W90P910_ROW_SHIFT	3
+
+struct w90p910_keypad {
+	const struct w90p910_keypad_platform_data *pdata;
+	struct clk *clk;
+	struct input_dev *input_dev;
+	void __iomem *mmio_base;
+	int irq;
+	unsigned short keymap[W90P910_NUM_ROWS * W90P910_NUM_COLS];
+};
+
+static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad,
+							unsigned int status)
+{
+	struct input_dev *input_dev = keypad->input_dev;
+	unsigned int row = KGET_RAW(status);
+	unsigned int col = KGET_COLUMN(status);
+	unsigned int code = MATRIX_SCAN_CODE(row, col, W90P910_ROW_SHIFT);
+	unsigned int key = keypad->keymap[code];
+
+	input_event(input_dev, EV_MSC, MSC_SCAN, code);
+	input_report_key(input_dev, key, 1);
+	input_sync(input_dev);
+
+	input_event(input_dev, EV_MSC, MSC_SCAN, code);
+	input_report_key(input_dev, key, 0);
+	input_sync(input_dev);
+}
+
+static irqreturn_t w90p910_keypad_irq_handler(int irq, void *dev_id)
+{
+	struct w90p910_keypad *keypad = dev_id;
+	unsigned int  kstatus, val;
+
+	kstatus = __raw_readl(keypad->mmio_base + KPI_STATUS);
+
+	val = INTTR | IS1KEY;
+
+	if (kstatus & val)
+		w90p910_keypad_scan_matrix(keypad, kstatus);
+
+	return IRQ_HANDLED;
+}
+
+static int w90p910_keypad_open(struct input_dev *dev)
+{
+	struct w90p910_keypad *keypad = input_get_drvdata(dev);
+	const struct w90p910_keypad_platform_data *pdata = keypad->pdata;
+	unsigned int val, config;
+
+	/* Enable unit clock */
+	clk_enable(keypad->clk);
+
+	val = __raw_readl(keypad->mmio_base + KPI_CONF);
+	val |= (KPSEL | ENKP);
+	val &= ~(KSIZE0 | KSIZE1);
+
+	config = pdata->prescale | (pdata->debounce << DEBOUNCE_BIT);
+
+	val |= config;
+
+	__raw_writel(val, keypad->mmio_base + KPI_CONF);
+
+	return 0;
+}
+
+static void w90p910_keypad_close(struct input_dev *dev)
+{
+	struct w90p910_keypad *keypad = input_get_drvdata(dev);
+
+	/* Disable clock unit */
+	clk_disable(keypad->clk);
+}
+
+static int w90p910_keypad_probe(struct platform_device *pdev)
+{
+	const struct w90p910_keypad_platform_data *pdata =
+						dev_get_platdata(&pdev->dev);
+	const struct matrix_keymap_data *keymap_data;
+	struct w90p910_keypad *keypad;
+	struct input_dev *input_dev;
+	struct resource *res;
+	int irq;
+	int error;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data defined\n");
+		return -EINVAL;
+	}
+
+	keymap_data = pdata->keymap_data;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to get keypad irq\n");
+		return -ENXIO;
+	}
+
+	keypad = kzalloc(sizeof(struct w90p910_keypad), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!keypad || !input_dev) {
+		dev_err(&pdev->dev, "failed to allocate driver data\n");
+		error = -ENOMEM;
+		goto failed_free;
+	}
+
+	keypad->pdata = pdata;
+	keypad->input_dev = input_dev;
+	keypad->irq = irq;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to get I/O memory\n");
+		error = -ENXIO;
+		goto failed_free;
+	}
+
+	res = request_mem_region(res->start, resource_size(res), pdev->name);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to request I/O memory\n");
+		error = -EBUSY;
+		goto failed_free;
+	}
+
+	keypad->mmio_base = ioremap(res->start, resource_size(res));
+	if (keypad->mmio_base == NULL) {
+		dev_err(&pdev->dev, "failed to remap I/O memory\n");
+		error = -ENXIO;
+		goto failed_free_res;
+	}
+
+	keypad->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(keypad->clk)) {
+		dev_err(&pdev->dev, "failed to get keypad clock\n");
+		error = PTR_ERR(keypad->clk);
+		goto failed_free_io;
+	}
+
+	/* set multi-function pin for w90p910 kpi. */
+	mfp_set_groupi(&pdev->dev);
+
+	input_dev->name = pdev->name;
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->open = w90p910_keypad_open;
+	input_dev->close = w90p910_keypad_close;
+	input_dev->dev.parent = &pdev->dev;
+
+	error = matrix_keypad_build_keymap(keymap_data, NULL,
+					   W90P910_NUM_ROWS, W90P910_NUM_COLS,
+					   keypad->keymap, input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to build keymap\n");
+		goto failed_put_clk;
+	}
+
+	error = request_irq(keypad->irq, w90p910_keypad_irq_handler,
+			    0, pdev->name, keypad);
+	if (error) {
+		dev_err(&pdev->dev, "failed to request IRQ\n");
+		goto failed_put_clk;
+	}
+
+	__set_bit(EV_REP, input_dev->evbit);
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+	input_set_drvdata(input_dev, keypad);
+
+	/* Register the input device */
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		goto failed_free_irq;
+	}
+
+	platform_set_drvdata(pdev, keypad);
+	return 0;
+
+failed_free_irq:
+	free_irq(irq, keypad);
+failed_put_clk:
+	clk_put(keypad->clk);
+failed_free_io:
+	iounmap(keypad->mmio_base);
+failed_free_res:
+	release_mem_region(res->start, resource_size(res));
+failed_free:
+	input_free_device(input_dev);
+	kfree(keypad);
+	return error;
+}
+
+static int w90p910_keypad_remove(struct platform_device *pdev)
+{
+	struct w90p910_keypad *keypad = platform_get_drvdata(pdev);
+	struct resource *res;
+
+	free_irq(keypad->irq, keypad);
+
+	clk_put(keypad->clk);
+
+	input_unregister_device(keypad->input_dev);
+
+	iounmap(keypad->mmio_base);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(keypad);
+
+	return 0;
+}
+
+static struct platform_driver w90p910_keypad_driver = {
+	.probe		= w90p910_keypad_probe,
+	.remove		= w90p910_keypad_remove,
+	.driver		= {
+		.name	= "nuc900-kpi",
+	},
+};
+module_platform_driver(w90p910_keypad_driver);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("w90p910 keypad driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nuc900-keypad");
diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c
new file mode 100644
index 0000000..7c2325b
--- /dev/null
+++ b/drivers/input/keyboard/xtkbd.c
@@ -0,0 +1,171 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * XT keyboard driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"XT keyboard driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define XTKBD_EMUL0	0xe0
+#define XTKBD_EMUL1	0xe1
+#define XTKBD_KEY	0x7f
+#define XTKBD_RELEASE	0x80
+
+static unsigned char xtkbd_keycode[256] = {
+	  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+	 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+	 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+	 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+	 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+	 80, 81, 82, 83,  0,  0,  0, 87, 88,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0, 87, 88,  0,  0,  0,  0,110,111,103,108,105,
+	106
+};
+
+struct xtkbd {
+	unsigned char keycode[256];
+	struct input_dev *dev;
+	struct serio *serio;
+	char phys[32];
+};
+
+static irqreturn_t xtkbd_interrupt(struct serio *serio,
+	unsigned char data, unsigned int flags)
+{
+	struct xtkbd *xtkbd = serio_get_drvdata(serio);
+
+	switch (data) {
+		case XTKBD_EMUL0:
+		case XTKBD_EMUL1:
+			break;
+		default:
+
+			if (xtkbd->keycode[data & XTKBD_KEY]) {
+				input_report_key(xtkbd->dev, xtkbd->keycode[data & XTKBD_KEY], !(data & XTKBD_RELEASE));
+				input_sync(xtkbd->dev);
+			} else {
+				printk(KERN_WARNING "xtkbd.c: Unknown key (scancode %#x) %s.\n",
+					data & XTKBD_KEY, data & XTKBD_RELEASE ? "released" : "pressed");
+			}
+	}
+	return IRQ_HANDLED;
+}
+
+static int xtkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct xtkbd *xtkbd;
+	struct input_dev *input_dev;
+	int err = -ENOMEM;
+	int i;
+
+	xtkbd = kmalloc(sizeof(struct xtkbd), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!xtkbd || !input_dev)
+		goto fail1;
+
+	xtkbd->serio = serio;
+	xtkbd->dev = input_dev;
+	snprintf(xtkbd->phys, sizeof(xtkbd->phys), "%s/input0", serio->phys);
+	memcpy(xtkbd->keycode, xtkbd_keycode, sizeof(xtkbd->keycode));
+
+	input_dev->name = "XT Keyboard";
+	input_dev->phys = xtkbd->phys;
+	input_dev->id.bustype = BUS_XTKBD;
+	input_dev->id.vendor  = 0x0001;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+	input_dev->keycode = xtkbd->keycode;
+	input_dev->keycodesize = sizeof(unsigned char);
+	input_dev->keycodemax = ARRAY_SIZE(xtkbd_keycode);
+
+	for (i = 0; i < 255; i++)
+		set_bit(xtkbd->keycode[i], input_dev->keybit);
+	clear_bit(0, input_dev->keybit);
+
+	serio_set_drvdata(serio, xtkbd);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(xtkbd->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(xtkbd);
+	return err;
+}
+
+static void xtkbd_disconnect(struct serio *serio)
+{
+	struct xtkbd *xtkbd = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(xtkbd->dev);
+	kfree(xtkbd);
+}
+
+static struct serio_device_id xtkbd_serio_ids[] = {
+	{
+		.type	= SERIO_XT,
+		.proto	= SERIO_ANY,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, xtkbd_serio_ids);
+
+static struct serio_driver xtkbd_drv = {
+	.driver		= {
+		.name	= "xtkbd",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= xtkbd_serio_ids,
+	.interrupt	= xtkbd_interrupt,
+	.connect	= xtkbd_connect,
+	.disconnect	= xtkbd_disconnect,
+};
+
+module_serio_driver(xtkbd_drv);
diff --git a/drivers/input/matrix-keymap.c b/drivers/input/matrix-keymap.c
new file mode 100644
index 0000000..08b61f5
--- /dev/null
+++ b/drivers/input/matrix-keymap.c
@@ -0,0 +1,207 @@
+/*
+ * Helpers for matrix keyboard bindings
+ *
+ * Copyright (C) 2012 Google, Inc
+ *
+ * Author:
+ *	Olof Johansson <olof@lixom.net>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/of.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/input/matrix_keypad.h>
+
+static bool matrix_keypad_map_key(struct input_dev *input_dev,
+				  unsigned int rows, unsigned int cols,
+				  unsigned int row_shift, unsigned int key)
+{
+	unsigned short *keymap = input_dev->keycode;
+	unsigned int row = KEY_ROW(key);
+	unsigned int col = KEY_COL(key);
+	unsigned short code = KEY_VAL(key);
+
+	if (row >= rows || col >= cols) {
+		dev_err(input_dev->dev.parent,
+			"%s: invalid keymap entry 0x%x (row: %d, col: %d, rows: %d, cols: %d)\n",
+			__func__, key, row, col, rows, cols);
+		return false;
+	}
+
+	keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code;
+	__set_bit(code, input_dev->keybit);
+
+	return true;
+}
+
+#ifdef CONFIG_OF
+int matrix_keypad_parse_of_params(struct device *dev,
+				  unsigned int *rows, unsigned int *cols)
+{
+	struct device_node *np = dev->of_node;
+
+	if (!np) {
+		dev_err(dev, "missing DT data");
+		return -EINVAL;
+	}
+	of_property_read_u32(np, "keypad,num-rows", rows);
+	of_property_read_u32(np, "keypad,num-columns", cols);
+	if (!*rows || !*cols) {
+		dev_err(dev, "number of keypad rows/columns not specified\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(matrix_keypad_parse_of_params);
+
+static int matrix_keypad_parse_of_keymap(const char *propname,
+					 unsigned int rows, unsigned int cols,
+					 struct input_dev *input_dev)
+{
+	struct device *dev = input_dev->dev.parent;
+	struct device_node *np = dev->of_node;
+	unsigned int row_shift = get_count_order(cols);
+	unsigned int max_keys = rows << row_shift;
+	unsigned int proplen, i, size;
+	const __be32 *prop;
+
+	if (!np)
+		return -ENOENT;
+
+	if (!propname)
+		propname = "linux,keymap";
+
+	prop = of_get_property(np, propname, &proplen);
+	if (!prop) {
+		dev_err(dev, "OF: %s property not defined in %s\n",
+			propname, np->full_name);
+		return -ENOENT;
+	}
+
+	if (proplen % sizeof(u32)) {
+		dev_err(dev, "OF: Malformed keycode property %s in %s\n",
+			propname, np->full_name);
+		return -EINVAL;
+	}
+
+	size = proplen / sizeof(u32);
+	if (size > max_keys) {
+		dev_err(dev, "OF: %s size overflow\n", propname);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < size; i++) {
+		unsigned int key = be32_to_cpup(prop + i);
+
+		if (!matrix_keypad_map_key(input_dev, rows, cols,
+					   row_shift, key))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+#else
+static int matrix_keypad_parse_of_keymap(const char *propname,
+					 unsigned int rows, unsigned int cols,
+					 struct input_dev *input_dev)
+{
+	return -ENOSYS;
+}
+#endif
+
+/**
+ * matrix_keypad_build_keymap - convert platform keymap into matrix keymap
+ * @keymap_data: keymap supplied by the platform code
+ * @keymap_name: name of device tree property containing keymap (if device
+ *	tree support is enabled).
+ * @rows: number of rows in target keymap array
+ * @cols: number of cols in target keymap array
+ * @keymap: expanded version of keymap that is suitable for use by
+ * matrix keyboard driver
+ * @input_dev: input devices for which we are setting up the keymap
+ *
+ * This function converts platform keymap (encoded with KEY() macro) into
+ * an array of keycodes that is suitable for using in a standard matrix
+ * keyboard driver that uses row and col as indices.
+ *
+ * If @keymap_data is not supplied and device tree support is enabled
+ * it will attempt load the keymap from property specified by @keymap_name
+ * argument (or "linux,keymap" if @keymap_name is %NULL).
+ *
+ * If @keymap is %NULL the function will automatically allocate managed
+ * block of memory to store the keymap. This memory will be associated with
+ * the parent device and automatically freed when device unbinds from the
+ * driver.
+ *
+ * Callers are expected to set up input_dev->dev.parent before calling this
+ * function.
+ */
+int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data,
+			       const char *keymap_name,
+			       unsigned int rows, unsigned int cols,
+			       unsigned short *keymap,
+			       struct input_dev *input_dev)
+{
+	unsigned int row_shift = get_count_order(cols);
+	size_t max_keys = rows << row_shift;
+	int i;
+	int error;
+
+	if (WARN_ON(!input_dev->dev.parent))
+		return -EINVAL;
+
+	if (!keymap) {
+		keymap = devm_kzalloc(input_dev->dev.parent,
+				      max_keys * sizeof(*keymap),
+				      GFP_KERNEL);
+		if (!keymap) {
+			dev_err(input_dev->dev.parent,
+				"Unable to allocate memory for keymap");
+			return -ENOMEM;
+		}
+	}
+
+	input_dev->keycode = keymap;
+	input_dev->keycodesize = sizeof(*keymap);
+	input_dev->keycodemax = max_keys;
+
+	__set_bit(EV_KEY, input_dev->evbit);
+
+	if (keymap_data) {
+		for (i = 0; i < keymap_data->keymap_size; i++) {
+			unsigned int key = keymap_data->keymap[i];
+
+			if (!matrix_keypad_map_key(input_dev, rows, cols,
+						   row_shift, key))
+				return -EINVAL;
+		}
+	} else {
+		error = matrix_keypad_parse_of_keymap(keymap_name, rows, cols,
+						      input_dev);
+		if (error)
+			return error;
+	}
+
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+
+	return 0;
+}
+EXPORT_SYMBOL(matrix_keypad_build_keymap);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/88pm80x_onkey.c b/drivers/input/misc/88pm80x_onkey.c
new file mode 100644
index 0000000..cf9908f
--- /dev/null
+++ b/drivers/input/misc/88pm80x_onkey.c
@@ -0,0 +1,167 @@
+/*
+ * Marvell 88PM80x ONKEY driver
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ * Qiao Zhou <zhouqiao@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define PM800_LONG_ONKEY_EN		(1 << 0)
+#define PM800_LONG_KEY_DELAY		(8)	/* 1 .. 16 seconds */
+#define PM800_LONKEY_PRESS_TIME		((PM800_LONG_KEY_DELAY-1) << 4)
+#define PM800_LONKEY_PRESS_TIME_MASK	(0xF0)
+#define PM800_SW_PDOWN			(1 << 5)
+
+struct pm80x_onkey_info {
+	struct input_dev *idev;
+	struct pm80x_chip *pm80x;
+	struct regmap *map;
+	int irq;
+};
+
+/* 88PM80x gives us an interrupt when ONKEY is held */
+static irqreturn_t pm80x_onkey_handler(int irq, void *data)
+{
+	struct pm80x_onkey_info *info = data;
+	int ret = 0;
+	unsigned int val;
+
+	ret = regmap_read(info->map, PM800_STATUS_1, &val);
+	if (ret < 0) {
+		dev_err(info->idev->dev.parent, "failed to read status: %d\n", ret);
+		return IRQ_NONE;
+	}
+	val &= PM800_ONKEY_STS1;
+
+	input_report_key(info->idev, KEY_POWER, val);
+	input_sync(info->idev);
+
+	return IRQ_HANDLED;
+}
+
+static SIMPLE_DEV_PM_OPS(pm80x_onkey_pm_ops, pm80x_dev_suspend,
+			 pm80x_dev_resume);
+
+static int pm80x_onkey_probe(struct platform_device *pdev)
+{
+
+	struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+	struct pm80x_onkey_info *info;
+	int err;
+
+	info = kzalloc(sizeof(struct pm80x_onkey_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->pm80x = chip;
+
+	info->irq = platform_get_irq(pdev, 0);
+	if (info->irq < 0) {
+		dev_err(&pdev->dev, "No IRQ resource!\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	info->map = info->pm80x->regmap;
+	if (!info->map) {
+		dev_err(&pdev->dev, "no regmap!\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	info->idev = input_allocate_device();
+	if (!info->idev) {
+		dev_err(&pdev->dev, "Failed to allocate input dev\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	info->idev->name = "88pm80x_on";
+	info->idev->phys = "88pm80x_on/input0";
+	info->idev->id.bustype = BUS_I2C;
+	info->idev->dev.parent = &pdev->dev;
+	info->idev->evbit[0] = BIT_MASK(EV_KEY);
+	__set_bit(KEY_POWER, info->idev->keybit);
+
+	err = pm80x_request_irq(info->pm80x, info->irq, pm80x_onkey_handler,
+					    IRQF_ONESHOT, "onkey", info);
+	if (err < 0) {
+		dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n",
+			info->irq, err);
+		goto out_reg;
+	}
+
+	err = input_register_device(info->idev);
+	if (err) {
+		dev_err(&pdev->dev, "Can't register input device: %d\n", err);
+		goto out_irq;
+	}
+
+	platform_set_drvdata(pdev, info);
+
+	/* Enable long onkey detection */
+	regmap_update_bits(info->map, PM800_RTC_MISC4, PM800_LONG_ONKEY_EN,
+			   PM800_LONG_ONKEY_EN);
+	/* Set 8-second interval */
+	regmap_update_bits(info->map, PM800_RTC_MISC3,
+			   PM800_LONKEY_PRESS_TIME_MASK,
+			   PM800_LONKEY_PRESS_TIME);
+
+	device_init_wakeup(&pdev->dev, 1);
+	return 0;
+
+out_irq:
+	pm80x_free_irq(info->pm80x, info->irq, info);
+out_reg:
+	input_free_device(info->idev);
+out:
+	kfree(info);
+	return err;
+}
+
+static int pm80x_onkey_remove(struct platform_device *pdev)
+{
+	struct pm80x_onkey_info *info = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	pm80x_free_irq(info->pm80x, info->irq, info);
+	input_unregister_device(info->idev);
+	kfree(info);
+	return 0;
+}
+
+static struct platform_driver pm80x_onkey_driver = {
+	.driver = {
+		   .name = "88pm80x-onkey",
+		   .pm = &pm80x_onkey_pm_ops,
+		   },
+	.probe = pm80x_onkey_probe,
+	.remove = pm80x_onkey_remove,
+};
+
+module_platform_driver(pm80x_onkey_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Marvell 88PM80x ONKEY driver");
+MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
+MODULE_ALIAS("platform:88pm80x-onkey");
diff --git a/drivers/input/misc/88pm860x_onkey.c b/drivers/input/misc/88pm860x_onkey.c
new file mode 100644
index 0000000..cc87443
--- /dev/null
+++ b/drivers/input/misc/88pm860x_onkey.c
@@ -0,0 +1,147 @@
+/*
+ * 88pm860x_onkey.c - Marvell 88PM860x ONKEY driver
+ *
+ * Copyright (C) 2009-2010 Marvell International Ltd.
+ *      Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/88pm860x.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#define PM8607_WAKEUP		0x0b
+
+#define LONG_ONKEY_EN		(1 << 1)
+#define ONKEY_STATUS		(1 << 0)
+
+struct pm860x_onkey_info {
+	struct input_dev	*idev;
+	struct pm860x_chip	*chip;
+	struct i2c_client	*i2c;
+	struct device		*dev;
+	int			irq;
+};
+
+/* 88PM860x gives us an interrupt when ONKEY is held */
+static irqreturn_t pm860x_onkey_handler(int irq, void *data)
+{
+	struct pm860x_onkey_info *info = data;
+	int ret;
+
+	ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
+	ret &= ONKEY_STATUS;
+	input_report_key(info->idev, KEY_POWER, ret);
+	input_sync(info->idev);
+
+	/* Enable 8-second long onkey detection */
+	pm860x_set_bits(info->i2c, PM8607_WAKEUP, 3, LONG_ONKEY_EN);
+	return IRQ_HANDLED;
+}
+
+static int pm860x_onkey_probe(struct platform_device *pdev)
+{
+	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+	struct pm860x_onkey_info *info;
+	int irq, ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "No IRQ resource!\n");
+		return -EINVAL;
+	}
+
+	info = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_onkey_info),
+			    GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+	info->chip = chip;
+	info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
+	info->dev = &pdev->dev;
+	info->irq = irq;
+
+	info->idev = devm_input_allocate_device(&pdev->dev);
+	if (!info->idev) {
+		dev_err(chip->dev, "Failed to allocate input dev\n");
+		return -ENOMEM;
+	}
+
+	info->idev->name = "88pm860x_on";
+	info->idev->phys = "88pm860x_on/input0";
+	info->idev->id.bustype = BUS_I2C;
+	info->idev->dev.parent = &pdev->dev;
+	info->idev->evbit[0] = BIT_MASK(EV_KEY);
+	info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+
+	ret = input_register_device(info->idev);
+	if (ret) {
+		dev_err(chip->dev, "Can't register input device: %d\n", ret);
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
+					pm860x_onkey_handler, IRQF_ONESHOT,
+					"onkey", info);
+	if (ret < 0) {
+		dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+			info->irq, ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, info);
+	device_init_wakeup(&pdev->dev, 1);
+
+	return 0;
+}
+
+static int __maybe_unused pm860x_onkey_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+	if (device_may_wakeup(dev))
+		chip->wakeup_flag |= 1 << PM8607_IRQ_ONKEY;
+	return 0;
+}
+static int __maybe_unused pm860x_onkey_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+	if (device_may_wakeup(dev))
+		chip->wakeup_flag &= ~(1 << PM8607_IRQ_ONKEY);
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pm860x_onkey_pm_ops, pm860x_onkey_suspend, pm860x_onkey_resume);
+
+static struct platform_driver pm860x_onkey_driver = {
+	.driver		= {
+		.name	= "88pm860x-onkey",
+		.pm	= &pm860x_onkey_pm_ops,
+	},
+	.probe		= pm860x_onkey_probe,
+};
+module_platform_driver(pm860x_onkey_driver);
+
+MODULE_DESCRIPTION("Marvell 88PM860x ONKEY driver");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
new file mode 100644
index 0000000..d6d16fa
--- /dev/null
+++ b/drivers/input/misc/Kconfig
@@ -0,0 +1,799 @@
+#
+# Input misc drivers configuration
+#
+menuconfig INPUT_MISC
+	bool "Miscellaneous devices"
+	help
+	  Say Y here, and a list of miscellaneous input drivers will be displayed.
+	  Everything that didn't fit into the other categories is here. This option
+	  doesn't affect the kernel.
+
+	  If unsure, say Y.
+
+if INPUT_MISC
+
+config INPUT_88PM860X_ONKEY
+	tristate "88PM860x ONKEY support"
+	depends on MFD_88PM860X
+	help
+	  Support the ONKEY of Marvell 88PM860x PMICs as an input device
+	  reporting power button status.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called 88pm860x_onkey.
+
+config INPUT_88PM80X_ONKEY
+	tristate "88PM80x ONKEY support"
+	depends on MFD_88PM800
+	help
+	  Support the ONKEY of Marvell 88PM80x PMICs as an input device
+	  reporting power button status.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called 88pm80x_onkey.
+
+config INPUT_AB8500_PONKEY
+	tristate "AB8500 Pon (PowerOn) Key"
+	depends on AB8500_CORE
+	help
+	  Say Y here to use the PowerOn Key for ST-Ericsson's AB8500
+	  Mix-Sig PMIC.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called ab8500-ponkey.
+
+config INPUT_AD714X
+	tristate "Analog Devices AD714x Capacitance Touch Sensor"
+	help
+	  Say Y here if you want to support an AD7142/3/7/8/7A touch sensor.
+
+	  You should select a bus connection too.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad714x.
+
+config INPUT_AD714X_I2C
+	tristate "support I2C bus connection"
+	depends on INPUT_AD714X && I2C
+	default y
+	help
+	  Say Y here if you have AD7142/AD7147 hooked to an I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad714x-i2c.
+
+config INPUT_AD714X_SPI
+	tristate "support SPI bus connection"
+	depends on INPUT_AD714X && SPI
+	default y
+	help
+	  Say Y here if you have AD7142/AD7147 hooked to a SPI bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad714x-spi.
+
+config INPUT_ARIZONA_HAPTICS
+	tristate "Arizona haptics support"
+	depends on MFD_ARIZONA && SND_SOC
+	select INPUT_FF_MEMLESS
+	help
+	  Say Y to enable support for the haptics module in Arizona CODECs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called arizona-haptics.
+
+config INPUT_BMA150
+	tristate "BMA150/SMB380 acceleration sensor support"
+	depends on I2C
+	select INPUT_POLLDEV
+	help
+	  Say Y here if you have Bosch Sensortec's BMA150 or SMB380
+	  acceleration sensor hooked to an I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bma150.
+
+config INPUT_E3X0_BUTTON
+	tristate "NI Ettus Research USRP E3xx Button support."
+	default n
+	help
+	  Say Y here to enable support for the NI Ettus Research
+	  USRP E3xx Button.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called e3x0_button.
+
+config INPUT_PCSPKR
+	tristate "PC Speaker support"
+	depends on PCSPKR_PLATFORM
+	help
+	  Say Y here if you want the standard PC Speaker to be used for
+	  bells and whistles.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pcspkr.
+
+config INPUT_PM8941_PWRKEY
+	tristate "Qualcomm PM8941 power key support"
+	depends on MFD_SPMI_PMIC
+	help
+	  Say Y here if you want support for the power key usually found
+	  on boards using a Qualcomm PM8941 compatible PMIC.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pm8941-pwrkey.
+
+config INPUT_PM8XXX_VIBRATOR
+	tristate "Qualcomm PM8XXX vibrator support"
+	depends on MFD_PM8XXX
+	select INPUT_FF_MEMLESS
+	help
+	  This option enables device driver support for the vibrator
+	  on Qualcomm PM8xxx chip. This driver supports ff-memless interface
+	  from input framework.
+
+	  To compile this driver as module, choose M here: the
+	  module will be called pm8xxx-vibrator.
+
+config INPUT_PMIC8XXX_PWRKEY
+	tristate "PMIC8XXX power key support"
+	depends on MFD_PM8XXX
+	help
+	  Say Y here if you want support for the PMIC8XXX power key.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pmic8xxx-pwrkey.
+
+config INPUT_SPARCSPKR
+	tristate "SPARC Speaker support"
+	depends on PCI && SPARC64
+	help
+	  Say Y here if you want the standard Speaker on Sparc PCI systems
+	  to be used for bells and whistles.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sparcspkr.
+
+config INPUT_M68K_BEEP
+	tristate "M68k Beeper support"
+	depends on M68K
+
+config INPUT_MAX77693_HAPTIC
+	tristate "MAXIM MAX77693/MAX77843 haptic controller support"
+	depends on (MFD_MAX77693 || MFD_MAX77843) && PWM
+	select INPUT_FF_MEMLESS
+	help
+	  This option enables support for the haptic controller on
+	  MAXIM MAX77693 and MAX77843 chips.
+
+	  To compile this driver as module, choose M here: the
+	  module will be called max77693-haptic.
+
+config INPUT_MAX8925_ONKEY
+	tristate "MAX8925 ONKEY support"
+	depends on MFD_MAX8925
+	help
+	  Support the ONKEY of MAX8925 PMICs as an input device
+	  reporting power button status.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called max8925_onkey.
+
+config INPUT_MAX8997_HAPTIC
+	tristate "MAXIM MAX8997 haptic controller support"
+	depends on PWM && MFD_MAX8997
+	select INPUT_FF_MEMLESS
+	help
+	  This option enables device driver support for the haptic controller
+	  on MAXIM MAX8997 chip. This driver supports ff-memless interface
+	  from input framework.
+
+	  To compile this driver as module, choose M here: the
+	  module will be called max8997-haptic.
+
+config INPUT_MC13783_PWRBUTTON
+	tristate "MC13783 ON buttons"
+	depends on MFD_MC13XXX
+	help
+	  Support the ON buttons of MC13783 PMIC as an input device
+	  reporting power button status.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called mc13783-pwrbutton.
+
+config INPUT_MMA8450
+	tristate "MMA8450 - Freescale's 3-Axis, 8/12-bit Digital Accelerometer"
+	depends on I2C
+	select INPUT_POLLDEV
+	help
+	  Say Y here if you want to support Freescale's MMA8450 Accelerometer
+	  through I2C interface.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mma8450.
+
+config INPUT_MPU3050
+	tristate "MPU3050 Triaxial gyroscope sensor"
+	depends on I2C
+	help
+	  Say Y here if you want to support InvenSense MPU3050
+	  connected via an I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mpu3050.
+
+config INPUT_APANEL
+	tristate "Fujitsu Lifebook Application Panel buttons"
+	depends on X86 && I2C && LEDS_CLASS
+	select INPUT_POLLDEV
+	select CHECK_SIGNATURE
+	help
+	 Say Y here for support of the Application Panel buttons, used on
+	 Fujitsu Lifebook. These are attached to the mainboard through
+	 an SMBus interface managed by the I2C Intel ICH (i801) driver,
+	 which you should also build for this kernel.
+
+	 To compile this driver as a module, choose M here: the module will
+	 be called apanel.
+
+config INPUT_GP2A
+	tristate "Sharp GP2AP002A00F I2C Proximity/Opto sensor driver"
+	depends on I2C
+	depends on GPIOLIB || COMPILE_TEST
+	help
+	  Say Y here if you have a Sharp GP2AP002A00F proximity/als combo-chip
+	  hooked to an I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gp2ap002a00f.
+
+config INPUT_GPIO_BEEPER
+	tristate "Generic GPIO Beeper support"
+	depends on GPIOLIB || COMPILE_TEST
+	help
+	  Say Y here if you have a beeper connected to a GPIO pin.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gpio-beeper.
+
+config INPUT_GPIO_TILT_POLLED
+	tristate "Polled GPIO tilt switch"
+	depends on GPIOLIB || COMPILE_TEST
+	select INPUT_POLLDEV
+	help
+	  This driver implements support for tilt switches connected
+	  to GPIO pins that are not capable of generating interrupts.
+
+	  The list of gpios to use and the mapping of their states
+	  to specific angles is done via platform data.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gpio_tilt_polled.
+
+config INPUT_IXP4XX_BEEPER
+	tristate "IXP4XX Beeper support"
+	depends on ARCH_IXP4XX
+	help
+	  If you say yes here, you can connect a beeper to the
+	  ixp4xx gpio pins. This is used by the LinkSys NSLU2.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ixp4xx-beeper.
+
+config INPUT_COBALT_BTNS
+	tristate "Cobalt button interface"
+	depends on MIPS_COBALT
+	select INPUT_POLLDEV
+	help
+	  Say Y here if you want to support MIPS Cobalt button interface.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cobalt_btns.
+
+config INPUT_WISTRON_BTNS
+	tristate "x86 Wistron laptop button interface"
+	depends on X86_32
+	select INPUT_POLLDEV
+	select INPUT_SPARSEKMAP
+	select NEW_LEDS
+	select LEDS_CLASS
+	select CHECK_SIGNATURE
+	help
+	  Say Y here for support of Wistron laptop button interfaces, used on
+	  laptops of various brands, including Acer and Fujitsu-Siemens. If
+	  available, mail and wifi LEDs will be controllable via /sys/class/leds.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called wistron_btns.
+
+config INPUT_ATLAS_BTNS
+	tristate "x86 Atlas button interface"
+	depends on X86 && ACPI
+	help
+	  Say Y here for support of Atlas wallmount touchscreen buttons.
+	  The events will show up as scancodes F1 through F9 via evdev.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called atlas_btns.
+
+config INPUT_ATI_REMOTE2
+	tristate "ATI / Philips USB RF remote control"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you want to use an ATI or Philips USB RF remote control.
+	  These are RF remotes with USB receivers.
+	  ATI Remote Wonder II comes with some ATI's All-In-Wonder video cards
+	  and is also available as a separate product.
+	  This driver provides mouse pointer, left and right mouse buttons,
+	  and maps all the other remote buttons to keypress events.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ati_remote2.
+
+config INPUT_KEYSPAN_REMOTE
+	tristate "Keyspan DMR USB remote control"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you want to use a Keyspan DMR USB remote control.
+	  Currently only the UIA-11 type of receiver has been tested.  The tag
+	  on the receiver that connects to the USB port should have a P/N that
+	  will tell you what type of DMR you have.  The UIA-10 type is not
+	  supported at this time.  This driver maps all buttons to keypress
+	  events.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called keyspan_remote.
+
+config INPUT_KXTJ9
+	tristate "Kionix KXTJ9 tri-axis digital accelerometer"
+	depends on I2C
+	help
+	  Say Y here to enable support for the Kionix KXTJ9 digital tri-axis
+	  accelerometer.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called kxtj9.
+
+config INPUT_KXTJ9_POLLED_MODE
+	bool "Enable polling mode support"
+	depends on INPUT_KXTJ9
+	select INPUT_POLLDEV
+	help
+	  Say Y here if you need accelerometer to work in polling mode.
+
+config INPUT_POWERMATE
+	tristate "Griffin PowerMate and Contour Jog support"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you want to use Griffin PowerMate or Contour Jog devices.
+	  These are aluminum dials which can measure clockwise and anticlockwise
+	  rotation.  The dial also acts as a pushbutton.  The base contains an LED
+	  which can be instructed to pulse or to switch to a particular intensity.
+
+	  You can download userspace tools from
+	  <http://sowerbutts.com/powermate/>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called powermate.
+
+config INPUT_YEALINK
+	tristate "Yealink usb-p1k voip phone"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you want to enable keyboard and LCD functions of the
+	  Yealink usb-p1k usb phones. The audio part is enabled by the generic
+	  usb sound driver, so you might want to enable that as well.
+
+	  For information about how to use these additional functions, see
+	  <file:Documentation/input/yealink.txt>.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called yealink.
+
+config INPUT_CM109
+	tristate "C-Media CM109 USB I/O Controller"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you want to enable keyboard and buzzer functions of the
+	  C-Media CM109 usb phones. The audio part is enabled by the generic
+	  usb sound driver, so you might want to enable that as well.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called cm109.
+
+config INPUT_REGULATOR_HAPTIC
+	tristate "Regulator haptics support"
+	depends on REGULATOR
+	select INPUT_FF_MEMLESS
+	help
+	  This option enables device driver support for the haptic controlled
+	  by a regulator. This driver supports ff-memless interface
+	  from input framework.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called regulator-haptic.
+
+config INPUT_RETU_PWRBUTTON
+	tristate "Retu Power button Driver"
+	depends on MFD_RETU
+	help
+	  Say Y here if you want to enable power key reporting via the
+	  Retu chips found in Nokia Internet Tablets (770, N800, N810).
+
+	  To compile this driver as a module, choose M here. The module will
+	  be called retu-pwrbutton.
+
+config INPUT_TPS65218_PWRBUTTON
+	tristate "TPS65218 Power button driver"
+	depends on MFD_TPS65218
+	help
+	  Say Y here if you want to enable power buttong reporting for
+	  the TPS65218 Power Management IC device.
+
+	  To compile this driver as a module, choose M here. The module will
+	  be called tps65218-pwrbutton.
+
+config INPUT_AXP20X_PEK
+	tristate "X-Powers AXP20X power button driver"
+	depends on MFD_AXP20X
+	help
+	  Say Y here if you want to enable power key reporting via the
+	  AXP20X PMIC.
+
+	  To compile this driver as a module, choose M here. The module will
+	  be called axp20x-pek.
+
+
+config INPUT_TWL4030_PWRBUTTON
+	tristate "TWL4030 Power button Driver"
+	depends on TWL4030_CORE
+	help
+	  Say Y here if you want to enable power key reporting via the
+	  TWL4030 family of chips.
+
+	  To compile this driver as a module, choose M here. The module will
+	  be called twl4030_pwrbutton.
+
+config INPUT_TWL4030_VIBRA
+	tristate "Support for TWL4030 Vibrator"
+	depends on TWL4030_CORE
+	select MFD_TWL4030_AUDIO
+	select INPUT_FF_MEMLESS
+	help
+	  This option enables support for TWL4030 Vibrator Driver.
+
+	  To compile this driver as a module, choose M here. The module will
+	  be called twl4030_vibra.
+
+config INPUT_TWL6040_VIBRA
+	tristate "Support for TWL6040 Vibrator"
+	depends on TWL6040_CORE
+	select INPUT_FF_MEMLESS
+	help
+	  This option enables support for TWL6040 Vibrator Driver.
+
+	  To compile this driver as a module, choose M here. The module will
+	  be called twl6040_vibra.
+
+config INPUT_UINPUT
+	tristate "User level driver support"
+	help
+	  Say Y here if you want to support user level drivers for input
+	  subsystem accessible under char device 10:223 - /dev/input/uinput.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called uinput.
+
+config INPUT_SGI_BTNS
+	tristate "SGI Indy/O2 volume button interface"
+	depends on SGI_IP22 || SGI_IP32
+	select INPUT_POLLDEV
+	help
+	  Say Y here if you want to support SGI Indy/O2 volume button interface.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sgi_btns.
+
+config HP_SDC_RTC
+	tristate "HP SDC Real Time Clock"
+	depends on (GSC || HP300) && SERIO
+	select HP_SDC
+	help
+	  Say Y here if you want to support the built-in real time clock
+	  of the HP SDC controller.
+
+config INPUT_PALMAS_PWRBUTTON
+	tristate "Palmas Power button Driver"
+	depends on MFD_PALMAS
+	help
+	  Say Y here if you want to enable power key reporting via the
+	  Palmas family of PMICs.
+
+	  To compile this driver as a module, choose M here. The module will
+	  be called palmas_pwrbutton.
+
+config INPUT_PCF50633_PMU
+	tristate "PCF50633 PMU events"
+	depends on MFD_PCF50633
+	help
+	 Say Y to include support for delivering  PMU events via  input
+	 layer on NXP PCF50633.
+
+config INPUT_PCF8574
+	tristate "PCF8574 Keypad input device"
+	depends on I2C
+	help
+	  Say Y here if you want to support a keypad connected via I2C
+	  with a PCF8574.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pcf8574_keypad.
+
+config INPUT_PWM_BEEPER
+	tristate "PWM beeper support"
+	depends on PWM
+	help
+	  Say Y here to get support for PWM based beeper devices.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called pwm-beeper.
+
+config INPUT_GPIO_ROTARY_ENCODER
+	tristate "Rotary encoders connected to GPIO pins"
+	depends on GPIOLIB || COMPILE_TEST
+	help
+	  Say Y here to add support for rotary encoders connected to GPIO lines.
+	  Check file:Documentation/input/rotary-encoder.txt for more
+	  information.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rotary_encoder.
+
+config INPUT_RB532_BUTTON
+	tristate "Mikrotik Routerboard 532 button interface"
+	depends on MIKROTIK_RB532
+	depends on GPIOLIB
+	select INPUT_POLLDEV
+	help
+	  Say Y here if you want support for the S1 button built into
+	  Mikrotik's Routerboard 532.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rb532_button.
+
+config INPUT_DA9052_ONKEY
+	tristate "Dialog DA9052/DA9053 Onkey"
+	depends on PMIC_DA9052
+	help
+	  Support the ONKEY of Dialog DA9052 PMICs as an input device
+	  reporting power button status.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called da9052_onkey.
+
+config INPUT_DA9055_ONKEY
+	tristate "Dialog Semiconductor DA9055 ONKEY"
+	depends on MFD_DA9055
+	help
+	  Support the ONKEY of DA9055 PMICs as an input device
+	  reporting power button status.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called da9055_onkey.
+
+config INPUT_DA9063_ONKEY
+	tristate "Dialog DA9062/63 OnKey"
+	depends on MFD_DA9063 || MFD_DA9062
+	help
+	  Support the ONKEY of Dialog DA9063 and DA9062 Power Management ICs
+	  as an input device capable of reporting the power button status.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called da9063_onkey.
+
+config INPUT_DM355EVM
+	tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
+	depends on MFD_DM355EVM_MSP
+	select INPUT_SPARSEKMAP
+	help
+	  Supports the pushbuttons and IR remote used with
+	  the DM355 EVM board.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called dm355evm_keys.
+
+config INPUT_BFIN_ROTARY
+	tristate "Blackfin Rotary support"
+	depends on BF54x || BF52x
+	help
+	  Say Y here if you want to use the Blackfin Rotary.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bfin-rotary.
+
+config INPUT_WM831X_ON
+	tristate "WM831X ON pin"
+	depends on MFD_WM831X
+	help
+	  Support the ON pin of WM831X PMICs as an input device
+	  reporting power button status.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called wm831x_on.
+
+config INPUT_PCAP
+	tristate "Motorola EZX PCAP misc input events"
+	depends on EZX_PCAP
+	help
+	  Say Y here if you want to use Power key and Headphone button
+	  on Motorola EZX phones.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pcap_keys.
+
+config INPUT_ADXL34X
+	tristate "Analog Devices ADXL34x Three-Axis Digital Accelerometer"
+	default n
+	help
+	  Say Y here if you have a Accelerometer interface using the
+	  ADXL345/6 controller, and your board-specific initialization
+	  code includes that in its table of devices.
+
+	  This driver can use either I2C or SPI communication to the
+	  ADXL345/6 controller.  Select the appropriate method for
+	  your system.
+
+	  If unsure, say N (but it's safe to say "Y").
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adxl34x.
+
+config INPUT_ADXL34X_I2C
+	tristate "support I2C bus connection"
+	depends on INPUT_ADXL34X && I2C
+	default y
+	help
+	  Say Y here if you have ADXL345/6 hooked to an I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adxl34x-i2c.
+
+config INPUT_ADXL34X_SPI
+	tristate "support SPI bus connection"
+	depends on INPUT_ADXL34X && SPI
+	default y
+	help
+	  Say Y here if you have ADXL345/6 hooked to a SPI bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adxl34x-spi.
+
+config INPUT_IMS_PCU
+	tristate "IMS Passenger Control Unit driver"
+	depends on USB
+	depends on LEDS_CLASS
+	help
+	  Say Y here if you have system with IMS Rave Passenger Control Unit.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ims_pcu.
+
+config INPUT_CMA3000
+	tristate "VTI CMA3000 Tri-axis accelerometer"
+	help
+	  Say Y here if you want to use VTI CMA3000_D0x Accelerometer
+	  driver
+
+	  This driver currently only supports I2C interface to the
+	  controller. Also select the I2C method.
+
+	  If unsure, say N
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cma3000_d0x.
+
+config INPUT_CMA3000_I2C
+	tristate "Support I2C bus connection"
+	depends on INPUT_CMA3000 && I2C
+	help
+	  Say Y here if you want to use VTI CMA3000_D0x Accelerometer
+	  through I2C interface.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cma3000_d0x_i2c.
+
+config INPUT_XEN_KBDDEV_FRONTEND
+	tristate "Xen virtual keyboard and mouse support"
+	depends on XEN
+	default y
+	select XEN_XENBUS_FRONTEND
+	help
+	  This driver implements the front-end of the Xen virtual
+	  keyboard and mouse device driver.  It communicates with a back-end
+	  in another domain.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called xen-kbdfront.
+
+config INPUT_SIRFSOC_ONKEY
+	bool "CSR SiRFSoC power on/off/suspend key support"
+	depends on ARCH_SIRF && OF
+	default y
+	help
+	  Say Y here if you want to support for the SiRFSoC power on/off/suspend key
+	  in Linux, after you press the onkey, system will suspend.
+
+	  If unsure, say N.
+
+config INPUT_IDEAPAD_SLIDEBAR
+	tristate "IdeaPad Laptop Slidebar"
+	depends on INPUT
+	depends on SERIO_I8042
+	help
+	  Say Y here if you have an IdeaPad laptop with a slidebar.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ideapad_slidebar.
+
+config INPUT_SOC_BUTTON_ARRAY
+	tristate "Windows-compatible SoC Button Array"
+	depends on KEYBOARD_GPIO
+	help
+	  Say Y here if you have a SoC-based tablet that originally
+	  runs Windows 8.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called soc_button_array.
+
+config INPUT_DRV260X_HAPTICS
+	tristate "TI DRV260X haptics support"
+	depends on INPUT && I2C
+	depends on GPIOLIB || COMPILE_TEST
+	select INPUT_FF_MEMLESS
+	select REGMAP_I2C
+	help
+	  Say Y to enable support for the TI DRV260X haptics driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called drv260x-haptics.
+
+config INPUT_DRV2665_HAPTICS
+	tristate "TI DRV2665 haptics support"
+	depends on INPUT && I2C
+	select INPUT_FF_MEMLESS
+	select REGMAP_I2C
+	help
+	  Say Y to enable support for the TI DRV2665 haptics driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called drv2665-haptics.
+
+config INPUT_DRV2667_HAPTICS
+	tristate "TI DRV2667 haptics support"
+	depends on INPUT && I2C
+	select INPUT_FF_MEMLESS
+	select REGMAP_I2C
+	help
+	  Say Y to enable support for the TI DRV2667 haptics driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called drv2667-haptics.
+
+endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
new file mode 100644
index 0000000..0357a08
--- /dev/null
+++ b/drivers/input/misc/Makefile
@@ -0,0 +1,77 @@
+#
+# Makefile for the input misc drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_INPUT_88PM860X_ONKEY)	+= 88pm860x_onkey.o
+obj-$(CONFIG_INPUT_88PM80X_ONKEY)	+= 88pm80x_onkey.o
+obj-$(CONFIG_INPUT_AB8500_PONKEY)	+= ab8500-ponkey.o
+obj-$(CONFIG_INPUT_AD714X)		+= ad714x.o
+obj-$(CONFIG_INPUT_AD714X_I2C)		+= ad714x-i2c.o
+obj-$(CONFIG_INPUT_AD714X_SPI)		+= ad714x-spi.o
+obj-$(CONFIG_INPUT_ADXL34X)		+= adxl34x.o
+obj-$(CONFIG_INPUT_ADXL34X_I2C)		+= adxl34x-i2c.o
+obj-$(CONFIG_INPUT_ADXL34X_SPI)		+= adxl34x-spi.o
+obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
+obj-$(CONFIG_INPUT_ARIZONA_HAPTICS)	+= arizona-haptics.o
+obj-$(CONFIG_INPUT_ATI_REMOTE2)		+= ati_remote2.o
+obj-$(CONFIG_INPUT_ATLAS_BTNS)		+= atlas_btns.o
+obj-$(CONFIG_INPUT_BFIN_ROTARY)		+= bfin_rotary.o
+obj-$(CONFIG_INPUT_BMA150)		+= bma150.o
+obj-$(CONFIG_INPUT_CM109)		+= cm109.o
+obj-$(CONFIG_INPUT_CMA3000)		+= cma3000_d0x.o
+obj-$(CONFIG_INPUT_CMA3000_I2C)		+= cma3000_d0x_i2c.o
+obj-$(CONFIG_INPUT_COBALT_BTNS)		+= cobalt_btns.o
+obj-$(CONFIG_INPUT_DA9052_ONKEY)	+= da9052_onkey.o
+obj-$(CONFIG_INPUT_DA9055_ONKEY)	+= da9055_onkey.o
+obj-$(CONFIG_INPUT_DA9063_ONKEY)	+= da9063_onkey.o
+obj-$(CONFIG_INPUT_DM355EVM)		+= dm355evm_keys.o
+obj-$(CONFIG_INPUT_E3X0_BUTTON)		+= e3x0-button.o
+obj-$(CONFIG_INPUT_DRV260X_HAPTICS)	+= drv260x.o
+obj-$(CONFIG_INPUT_DRV2665_HAPTICS)	+= drv2665.o
+obj-$(CONFIG_INPUT_DRV2667_HAPTICS)	+= drv2667.o
+obj-$(CONFIG_INPUT_GP2A)		+= gp2ap002a00f.o
+obj-$(CONFIG_INPUT_GPIO_BEEPER)		+= gpio-beeper.o
+obj-$(CONFIG_INPUT_GPIO_TILT_POLLED)	+= gpio_tilt_polled.o
+obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
+obj-$(CONFIG_INPUT_IMS_PCU)		+= ims-pcu.o
+obj-$(CONFIG_INPUT_IXP4XX_BEEPER)	+= ixp4xx-beeper.o
+obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)	+= keyspan_remote.o
+obj-$(CONFIG_INPUT_KXTJ9)		+= kxtj9.o
+obj-$(CONFIG_INPUT_M68K_BEEP)		+= m68kspkr.o
+obj-$(CONFIG_INPUT_MAX77693_HAPTIC)	+= max77693-haptic.o
+obj-$(CONFIG_INPUT_MAX8925_ONKEY)	+= max8925_onkey.o
+obj-$(CONFIG_INPUT_MAX8997_HAPTIC)	+= max8997_haptic.o
+obj-$(CONFIG_INPUT_MC13783_PWRBUTTON)	+= mc13783-pwrbutton.o
+obj-$(CONFIG_INPUT_MMA8450)		+= mma8450.o
+obj-$(CONFIG_INPUT_MPU3050)		+= mpu3050.o
+obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON)	+= palmas-pwrbutton.o
+obj-$(CONFIG_INPUT_PCAP)		+= pcap_keys.o
+obj-$(CONFIG_INPUT_PCF50633_PMU)	+= pcf50633-input.o
+obj-$(CONFIG_INPUT_PCF8574)		+= pcf8574_keypad.o
+obj-$(CONFIG_INPUT_PCSPKR)		+= pcspkr.o
+obj-$(CONFIG_INPUT_PM8941_PWRKEY)	+= pm8941-pwrkey.o
+obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR)	+= pm8xxx-vibrator.o
+obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY)	+= pmic8xxx-pwrkey.o
+obj-$(CONFIG_INPUT_POWERMATE)		+= powermate.o
+obj-$(CONFIG_INPUT_PWM_BEEPER)		+= pwm-beeper.o
+obj-$(CONFIG_INPUT_RB532_BUTTON)	+= rb532_button.o
+obj-$(CONFIG_INPUT_REGULATOR_HAPTIC)	+= regulator-haptic.o
+obj-$(CONFIG_INPUT_RETU_PWRBUTTON)	+= retu-pwrbutton.o
+obj-$(CONFIG_INPUT_AXP20X_PEK)		+= axp20x-pek.o
+obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER)	+= rotary_encoder.o
+obj-$(CONFIG_INPUT_SGI_BTNS)		+= sgi_btns.o
+obj-$(CONFIG_INPUT_SIRFSOC_ONKEY)	+= sirfsoc-onkey.o
+obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY)	+= soc_button_array.o
+obj-$(CONFIG_INPUT_SPARCSPKR)		+= sparcspkr.o
+obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON)	+= tps65218-pwrbutton.o
+obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON)	+= twl4030-pwrbutton.o
+obj-$(CONFIG_INPUT_TWL4030_VIBRA)	+= twl4030-vibra.o
+obj-$(CONFIG_INPUT_TWL6040_VIBRA)	+= twl6040-vibra.o
+obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
+obj-$(CONFIG_INPUT_WISTRON_BTNS)	+= wistron_btns.o
+obj-$(CONFIG_INPUT_WM831X_ON)		+= wm831x-on.o
+obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
+obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
+obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR)	+= ideapad_slidebar.o
diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c
new file mode 100644
index 0000000..4f5ef5b
--- /dev/null
+++ b/drivers/input/misc/ab8500-ponkey.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ *
+ * AB8500 Power-On Key handler
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+/**
+ * struct ab8500_ponkey - ab8500 ponkey information
+ * @input_dev: pointer to input device
+ * @ab8500: ab8500 parent
+ * @irq_dbf: irq number for falling transition
+ * @irq_dbr: irq number for rising transition
+ */
+struct ab8500_ponkey {
+	struct input_dev	*idev;
+	struct ab8500		*ab8500;
+	int			irq_dbf;
+	int			irq_dbr;
+};
+
+/* AB8500 gives us an interrupt when ONKEY is held */
+static irqreturn_t ab8500_ponkey_handler(int irq, void *data)
+{
+	struct ab8500_ponkey *ponkey = data;
+
+	if (irq == ponkey->irq_dbf)
+		input_report_key(ponkey->idev, KEY_POWER, true);
+	else if (irq == ponkey->irq_dbr)
+		input_report_key(ponkey->idev, KEY_POWER, false);
+
+	input_sync(ponkey->idev);
+
+	return IRQ_HANDLED;
+}
+
+static int ab8500_ponkey_probe(struct platform_device *pdev)
+{
+	struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+	struct ab8500_ponkey *ponkey;
+	struct input_dev *input;
+	int irq_dbf, irq_dbr;
+	int error;
+
+	irq_dbf = platform_get_irq_byname(pdev, "ONKEY_DBF");
+	if (irq_dbf < 0) {
+		dev_err(&pdev->dev, "No IRQ for ONKEY_DBF, error=%d\n", irq_dbf);
+		return irq_dbf;
+	}
+
+	irq_dbr = platform_get_irq_byname(pdev, "ONKEY_DBR");
+	if (irq_dbr < 0) {
+		dev_err(&pdev->dev, "No IRQ for ONKEY_DBR, error=%d\n", irq_dbr);
+		return irq_dbr;
+	}
+
+	ponkey = devm_kzalloc(&pdev->dev, sizeof(struct ab8500_ponkey),
+			      GFP_KERNEL);
+	if (!ponkey)
+		return -ENOMEM;
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input)
+		return -ENOMEM;
+
+	ponkey->idev = input;
+	ponkey->ab8500 = ab8500;
+	ponkey->irq_dbf = irq_dbf;
+	ponkey->irq_dbr = irq_dbr;
+
+	input->name = "AB8500 POn(PowerOn) Key";
+	input->dev.parent = &pdev->dev;
+
+	input_set_capability(input, EV_KEY, KEY_POWER);
+
+	error = devm_request_any_context_irq(&pdev->dev, ponkey->irq_dbf,
+					     ab8500_ponkey_handler, 0,
+					     "ab8500-ponkey-dbf", ponkey);
+	if (error < 0) {
+		dev_err(ab8500->dev, "Failed to request dbf IRQ#%d: %d\n",
+			ponkey->irq_dbf, error);
+		return error;
+	}
+
+	error = devm_request_any_context_irq(&pdev->dev, ponkey->irq_dbr,
+					     ab8500_ponkey_handler, 0,
+					     "ab8500-ponkey-dbr", ponkey);
+	if (error < 0) {
+		dev_err(ab8500->dev, "Failed to request dbr IRQ#%d: %d\n",
+			ponkey->irq_dbr, error);
+		return error;
+	}
+
+	error = input_register_device(ponkey->idev);
+	if (error) {
+		dev_err(ab8500->dev, "Can't register input device: %d\n", error);
+		return error;
+	}
+
+	platform_set_drvdata(pdev, ponkey);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ab8500_ponkey_match[] = {
+	{ .compatible = "stericsson,ab8500-ponkey", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ab8500_ponkey_match);
+#endif
+
+static struct platform_driver ab8500_ponkey_driver = {
+	.driver		= {
+		.name	= "ab8500-poweron-key",
+		.of_match_table = of_match_ptr(ab8500_ponkey_match),
+	},
+	.probe		= ab8500_ponkey_probe,
+};
+module_platform_driver(ab8500_ponkey_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_DESCRIPTION("ST-Ericsson AB8500 Power-ON(Pon) Key driver");
diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c
new file mode 100644
index 0000000..2f04773
--- /dev/null
+++ b/drivers/input/misc/ad714x-i2c.c
@@ -0,0 +1,111 @@
+/*
+ * AD714X CapTouch Programmable Controller driver (I2C bus)
+ *
+ * Copyright 2009-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h>	/* BUS_I2C */
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/pm.h>
+#include "ad714x.h"
+
+static int __maybe_unused ad714x_i2c_suspend(struct device *dev)
+{
+	return ad714x_disable(i2c_get_clientdata(to_i2c_client(dev)));
+}
+
+static int __maybe_unused ad714x_i2c_resume(struct device *dev)
+{
+	return ad714x_enable(i2c_get_clientdata(to_i2c_client(dev)));
+}
+
+static SIMPLE_DEV_PM_OPS(ad714x_i2c_pm, ad714x_i2c_suspend, ad714x_i2c_resume);
+
+static int ad714x_i2c_write(struct ad714x_chip *chip,
+			    unsigned short reg, unsigned short data)
+{
+	struct i2c_client *client = to_i2c_client(chip->dev);
+	int error;
+
+	chip->xfer_buf[0] = cpu_to_be16(reg);
+	chip->xfer_buf[1] = cpu_to_be16(data);
+
+	error = i2c_master_send(client, (u8 *)chip->xfer_buf,
+				2 * sizeof(*chip->xfer_buf));
+	if (unlikely(error < 0)) {
+		dev_err(&client->dev, "I2C write error: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int ad714x_i2c_read(struct ad714x_chip *chip,
+			   unsigned short reg, unsigned short *data, size_t len)
+{
+	struct i2c_client *client = to_i2c_client(chip->dev);
+	int i;
+	int error;
+
+	chip->xfer_buf[0] = cpu_to_be16(reg);
+
+	error = i2c_master_send(client, (u8 *)chip->xfer_buf,
+				sizeof(*chip->xfer_buf));
+	if (error >= 0)
+		error = i2c_master_recv(client, (u8 *)chip->xfer_buf,
+					len * sizeof(*chip->xfer_buf));
+
+	if (unlikely(error < 0)) {
+		dev_err(&client->dev, "I2C read error: %d\n", error);
+		return error;
+	}
+
+	for (i = 0; i < len; i++)
+		data[i] = be16_to_cpu(chip->xfer_buf[i]);
+
+	return 0;
+}
+
+static int ad714x_i2c_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	struct ad714x_chip *chip;
+
+	chip = ad714x_probe(&client->dev, BUS_I2C, client->irq,
+			    ad714x_i2c_read, ad714x_i2c_write);
+	if (IS_ERR(chip))
+		return PTR_ERR(chip);
+
+	i2c_set_clientdata(client, chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id ad714x_id[] = {
+	{ "ad7142_captouch", 0 },
+	{ "ad7143_captouch", 0 },
+	{ "ad7147_captouch", 0 },
+	{ "ad7147a_captouch", 0 },
+	{ "ad7148_captouch", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ad714x_id);
+
+static struct i2c_driver ad714x_i2c_driver = {
+	.driver = {
+		.name = "ad714x_captouch",
+		.pm   = &ad714x_i2c_pm,
+	},
+	.probe    = ad714x_i2c_probe,
+	.id_table = ad714x_id,
+};
+
+module_i2c_driver(ad714x_i2c_driver);
+
+MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor I2C Bus Driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c
new file mode 100644
index 0000000..aac9103
--- /dev/null
+++ b/drivers/input/misc/ad714x-spi.c
@@ -0,0 +1,116 @@
+/*
+ * AD714X CapTouch Programmable Controller driver (SPI bus)
+ *
+ * Copyright 2009-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h>	/* BUS_SPI */
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/pm.h>
+#include <linux/types.h>
+#include "ad714x.h"
+
+#define AD714x_SPI_CMD_PREFIX      0xE000   /* bits 15:11 */
+#define AD714x_SPI_READ            BIT(10)
+
+static int __maybe_unused ad714x_spi_suspend(struct device *dev)
+{
+	return ad714x_disable(spi_get_drvdata(to_spi_device(dev)));
+}
+
+static int __maybe_unused ad714x_spi_resume(struct device *dev)
+{
+	return ad714x_enable(spi_get_drvdata(to_spi_device(dev)));
+}
+
+static SIMPLE_DEV_PM_OPS(ad714x_spi_pm, ad714x_spi_suspend, ad714x_spi_resume);
+
+static int ad714x_spi_read(struct ad714x_chip *chip,
+			   unsigned short reg, unsigned short *data, size_t len)
+{
+	struct spi_device *spi = to_spi_device(chip->dev);
+	struct spi_message message;
+	struct spi_transfer xfer[2];
+	int i;
+	int error;
+
+	spi_message_init(&message);
+	memset(xfer, 0, sizeof(xfer));
+
+	chip->xfer_buf[0] = cpu_to_be16(AD714x_SPI_CMD_PREFIX |
+					AD714x_SPI_READ | reg);
+	xfer[0].tx_buf = &chip->xfer_buf[0];
+	xfer[0].len = sizeof(chip->xfer_buf[0]);
+	spi_message_add_tail(&xfer[0], &message);
+
+	xfer[1].rx_buf = &chip->xfer_buf[1];
+	xfer[1].len = sizeof(chip->xfer_buf[1]) * len;
+	spi_message_add_tail(&xfer[1], &message);
+
+	error = spi_sync(spi, &message);
+	if (unlikely(error)) {
+		dev_err(chip->dev, "SPI read error: %d\n", error);
+		return error;
+	}
+
+	for (i = 0; i < len; i++)
+		data[i] = be16_to_cpu(chip->xfer_buf[i + 1]);
+
+	return 0;
+}
+
+static int ad714x_spi_write(struct ad714x_chip *chip,
+			    unsigned short reg, unsigned short data)
+{
+	struct spi_device *spi = to_spi_device(chip->dev);
+	int error;
+
+	chip->xfer_buf[0] = cpu_to_be16(AD714x_SPI_CMD_PREFIX | reg);
+	chip->xfer_buf[1] = cpu_to_be16(data);
+
+	error = spi_write(spi, (u8 *)chip->xfer_buf,
+			  2 * sizeof(*chip->xfer_buf));
+	if (unlikely(error)) {
+		dev_err(chip->dev, "SPI write error: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int ad714x_spi_probe(struct spi_device *spi)
+{
+	struct ad714x_chip *chip;
+	int err;
+
+	spi->bits_per_word = 8;
+	err = spi_setup(spi);
+	if (err < 0)
+		return err;
+
+	chip = ad714x_probe(&spi->dev, BUS_SPI, spi->irq,
+			    ad714x_spi_read, ad714x_spi_write);
+	if (IS_ERR(chip))
+		return PTR_ERR(chip);
+
+	spi_set_drvdata(spi, chip);
+
+	return 0;
+}
+
+static struct spi_driver ad714x_spi_driver = {
+	.driver = {
+		.name	= "ad714x_captouch",
+		.pm	= &ad714x_spi_pm,
+	},
+	.probe		= ad714x_spi_probe,
+};
+
+module_spi_driver(ad714x_spi_driver);
+
+MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor SPI Bus Driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c
new file mode 100644
index 0000000..84b51dd
--- /dev/null
+++ b/drivers/input/misc/ad714x.c
@@ -0,0 +1,1210 @@
+/*
+ * AD714X CapTouch Programmable Controller driver supporting AD7142/3/7/8/7A
+ *
+ * Copyright 2009-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input/ad714x.h>
+#include <linux/module.h>
+#include "ad714x.h"
+
+#define AD714X_PWR_CTRL           0x0
+#define AD714X_STG_CAL_EN_REG     0x1
+#define AD714X_AMB_COMP_CTRL0_REG 0x2
+#define AD714X_PARTID_REG         0x17
+#define AD7142_PARTID             0xE620
+#define AD7143_PARTID             0xE630
+#define AD7147_PARTID             0x1470
+#define AD7148_PARTID             0x1480
+#define AD714X_STAGECFG_REG       0x80
+#define AD714X_SYSCFG_REG         0x0
+
+#define STG_LOW_INT_EN_REG     0x5
+#define STG_HIGH_INT_EN_REG    0x6
+#define STG_COM_INT_EN_REG     0x7
+#define STG_LOW_INT_STA_REG    0x8
+#define STG_HIGH_INT_STA_REG   0x9
+#define STG_COM_INT_STA_REG    0xA
+
+#define CDC_RESULT_S0          0xB
+#define CDC_RESULT_S1          0xC
+#define CDC_RESULT_S2          0xD
+#define CDC_RESULT_S3          0xE
+#define CDC_RESULT_S4          0xF
+#define CDC_RESULT_S5          0x10
+#define CDC_RESULT_S6          0x11
+#define CDC_RESULT_S7          0x12
+#define CDC_RESULT_S8          0x13
+#define CDC_RESULT_S9          0x14
+#define CDC_RESULT_S10         0x15
+#define CDC_RESULT_S11         0x16
+
+#define STAGE0_AMBIENT		0xF1
+#define STAGE1_AMBIENT		0x115
+#define STAGE2_AMBIENT		0x139
+#define STAGE3_AMBIENT		0x15D
+#define STAGE4_AMBIENT		0x181
+#define STAGE5_AMBIENT		0x1A5
+#define STAGE6_AMBIENT		0x1C9
+#define STAGE7_AMBIENT		0x1ED
+#define STAGE8_AMBIENT		0x211
+#define STAGE9_AMBIENT		0x234
+#define STAGE10_AMBIENT		0x259
+#define STAGE11_AMBIENT		0x27D
+
+#define PER_STAGE_REG_NUM      36
+#define STAGE_CFGREG_NUM       8
+#define SYS_CFGREG_NUM         8
+
+/*
+ * driver information which will be used to maintain the software flow
+ */
+enum ad714x_device_state { IDLE, JITTER, ACTIVE, SPACE };
+
+struct ad714x_slider_drv {
+	int highest_stage;
+	int abs_pos;
+	int flt_pos;
+	enum ad714x_device_state state;
+	struct input_dev *input;
+};
+
+struct ad714x_wheel_drv {
+	int abs_pos;
+	int flt_pos;
+	int pre_highest_stage;
+	int highest_stage;
+	enum ad714x_device_state state;
+	struct input_dev *input;
+};
+
+struct ad714x_touchpad_drv {
+	int x_highest_stage;
+	int x_flt_pos;
+	int x_abs_pos;
+	int y_highest_stage;
+	int y_flt_pos;
+	int y_abs_pos;
+	int left_ep;
+	int left_ep_val;
+	int right_ep;
+	int right_ep_val;
+	int top_ep;
+	int top_ep_val;
+	int bottom_ep;
+	int bottom_ep_val;
+	enum ad714x_device_state state;
+	struct input_dev *input;
+};
+
+struct ad714x_button_drv {
+	enum ad714x_device_state state;
+	/*
+	 * Unlike slider/wheel/touchpad, all buttons point to
+	 * same input_dev instance
+	 */
+	struct input_dev *input;
+};
+
+struct ad714x_driver_data {
+	struct ad714x_slider_drv *slider;
+	struct ad714x_wheel_drv *wheel;
+	struct ad714x_touchpad_drv *touchpad;
+	struct ad714x_button_drv *button;
+};
+
+/*
+ * information to integrate all things which will be private data
+ * of spi/i2c device
+ */
+
+static void ad714x_use_com_int(struct ad714x_chip *ad714x,
+				int start_stage, int end_stage)
+{
+	unsigned short data;
+	unsigned short mask;
+
+	mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1);
+
+	ad714x->read(ad714x, STG_COM_INT_EN_REG, &data, 1);
+	data |= 1 << end_stage;
+	ad714x->write(ad714x, STG_COM_INT_EN_REG, data);
+
+	ad714x->read(ad714x, STG_HIGH_INT_EN_REG, &data, 1);
+	data &= ~mask;
+	ad714x->write(ad714x, STG_HIGH_INT_EN_REG, data);
+}
+
+static void ad714x_use_thr_int(struct ad714x_chip *ad714x,
+				int start_stage, int end_stage)
+{
+	unsigned short data;
+	unsigned short mask;
+
+	mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1);
+
+	ad714x->read(ad714x, STG_COM_INT_EN_REG, &data, 1);
+	data &= ~(1 << end_stage);
+	ad714x->write(ad714x, STG_COM_INT_EN_REG, data);
+
+	ad714x->read(ad714x, STG_HIGH_INT_EN_REG, &data, 1);
+	data |= mask;
+	ad714x->write(ad714x, STG_HIGH_INT_EN_REG, data);
+}
+
+static int ad714x_cal_highest_stage(struct ad714x_chip *ad714x,
+					int start_stage, int end_stage)
+{
+	int max_res = 0;
+	int max_idx = 0;
+	int i;
+
+	for (i = start_stage; i <= end_stage; i++) {
+		if (ad714x->sensor_val[i] > max_res) {
+			max_res = ad714x->sensor_val[i];
+			max_idx = i;
+		}
+	}
+
+	return max_idx;
+}
+
+static int ad714x_cal_abs_pos(struct ad714x_chip *ad714x,
+				int start_stage, int end_stage,
+				int highest_stage, int max_coord)
+{
+	int a_param, b_param;
+
+	if (highest_stage == start_stage) {
+		a_param = ad714x->sensor_val[start_stage + 1];
+		b_param = ad714x->sensor_val[start_stage] +
+			ad714x->sensor_val[start_stage + 1];
+	} else if (highest_stage == end_stage) {
+		a_param = ad714x->sensor_val[end_stage] *
+			(end_stage - start_stage) +
+			ad714x->sensor_val[end_stage - 1] *
+			(end_stage - start_stage - 1);
+		b_param = ad714x->sensor_val[end_stage] +
+			ad714x->sensor_val[end_stage - 1];
+	} else {
+		a_param = ad714x->sensor_val[highest_stage] *
+			(highest_stage - start_stage) +
+			ad714x->sensor_val[highest_stage - 1] *
+			(highest_stage - start_stage - 1) +
+			ad714x->sensor_val[highest_stage + 1] *
+			(highest_stage - start_stage + 1);
+		b_param = ad714x->sensor_val[highest_stage] +
+			ad714x->sensor_val[highest_stage - 1] +
+			ad714x->sensor_val[highest_stage + 1];
+	}
+
+	return (max_coord / (end_stage - start_stage)) * a_param / b_param;
+}
+
+/*
+ * One button can connect to multi positive and negative of CDCs
+ * Multi-buttons can connect to same positive/negative of one CDC
+ */
+static void ad714x_button_state_machine(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_button_plat *hw = &ad714x->hw->button[idx];
+	struct ad714x_button_drv *sw = &ad714x->sw->button[idx];
+
+	switch (sw->state) {
+	case IDLE:
+		if (((ad714x->h_state & hw->h_mask) == hw->h_mask) &&
+		    ((ad714x->l_state & hw->l_mask) == hw->l_mask)) {
+			dev_dbg(ad714x->dev, "button %d touched\n", idx);
+			input_report_key(sw->input, hw->keycode, 1);
+			input_sync(sw->input);
+			sw->state = ACTIVE;
+		}
+		break;
+
+	case ACTIVE:
+		if (((ad714x->h_state & hw->h_mask) != hw->h_mask) ||
+		    ((ad714x->l_state & hw->l_mask) != hw->l_mask)) {
+			dev_dbg(ad714x->dev, "button %d released\n", idx);
+			input_report_key(sw->input, hw->keycode, 0);
+			input_sync(sw->input);
+			sw->state = IDLE;
+		}
+		break;
+
+	default:
+		break;
+	}
+}
+
+/*
+ * The response of a sensor is defined by the absolute number of codes
+ * between the current CDC value and the ambient value.
+ */
+static void ad714x_slider_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
+	int i;
+
+	ad714x->read(ad714x, CDC_RESULT_S0 + hw->start_stage,
+			&ad714x->adc_reg[hw->start_stage],
+			hw->end_stage - hw->start_stage + 1);
+
+	for (i = hw->start_stage; i <= hw->end_stage; i++) {
+		ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
+				&ad714x->amb_reg[i], 1);
+
+		ad714x->sensor_val[i] =
+			abs(ad714x->adc_reg[i] - ad714x->amb_reg[i]);
+	}
+}
+
+static void ad714x_slider_cal_highest_stage(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
+	struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
+
+	sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage,
+			hw->end_stage);
+
+	dev_dbg(ad714x->dev, "slider %d highest_stage:%d\n", idx,
+		sw->highest_stage);
+}
+
+/*
+ * The formulae are very straight forward. It uses the sensor with the
+ * highest response and the 2 adjacent ones.
+ * When Sensor 0 has the highest response, only sensor 0 and sensor 1
+ * are used in the calculations. Similarly when the last sensor has the
+ * highest response, only the last sensor and the second last sensors
+ * are used in the calculations.
+ *
+ * For i= idx_of_peak_Sensor-1 to i= idx_of_peak_Sensor+1
+ *         v += Sensor response(i)*i
+ *         w += Sensor response(i)
+ * POS=(Number_of_Positions_Wanted/(Number_of_Sensors_Used-1)) *(v/w)
+ */
+static void ad714x_slider_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
+	struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
+
+	sw->abs_pos = ad714x_cal_abs_pos(ad714x, hw->start_stage, hw->end_stage,
+		sw->highest_stage, hw->max_coord);
+
+	dev_dbg(ad714x->dev, "slider %d absolute position:%d\n", idx,
+		sw->abs_pos);
+}
+
+/*
+ * To minimise the Impact of the noise on the algorithm, ADI developed a
+ * routine that filters the CDC results after they have been read by the
+ * host processor.
+ * The filter used is an Infinite Input Response(IIR) filter implemented
+ * in firmware and attenuates the noise on the CDC results after they've
+ * been read by the host processor.
+ * Filtered_CDC_result = (Filtered_CDC_result * (10 - Coefficient) +
+ *				Latest_CDC_result * Coefficient)/10
+ */
+static void ad714x_slider_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
+
+	sw->flt_pos = (sw->flt_pos * (10 - 4) +
+			sw->abs_pos * 4)/10;
+
+	dev_dbg(ad714x->dev, "slider %d filter position:%d\n", idx,
+		sw->flt_pos);
+}
+
+static void ad714x_slider_use_com_int(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
+
+	ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage);
+}
+
+static void ad714x_slider_use_thr_int(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
+
+	ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage);
+}
+
+static void ad714x_slider_state_machine(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
+	struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
+	unsigned short h_state, c_state;
+	unsigned short mask;
+
+	mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1);
+
+	h_state = ad714x->h_state & mask;
+	c_state = ad714x->c_state & mask;
+
+	switch (sw->state) {
+	case IDLE:
+		if (h_state) {
+			sw->state = JITTER;
+			/* In End of Conversion interrupt mode, the AD714X
+			 * continuously generates hardware interrupts.
+			 */
+			ad714x_slider_use_com_int(ad714x, idx);
+			dev_dbg(ad714x->dev, "slider %d touched\n", idx);
+		}
+		break;
+
+	case JITTER:
+		if (c_state == mask) {
+			ad714x_slider_cal_sensor_val(ad714x, idx);
+			ad714x_slider_cal_highest_stage(ad714x, idx);
+			ad714x_slider_cal_abs_pos(ad714x, idx);
+			sw->flt_pos = sw->abs_pos;
+			sw->state = ACTIVE;
+		}
+		break;
+
+	case ACTIVE:
+		if (c_state == mask) {
+			if (h_state) {
+				ad714x_slider_cal_sensor_val(ad714x, idx);
+				ad714x_slider_cal_highest_stage(ad714x, idx);
+				ad714x_slider_cal_abs_pos(ad714x, idx);
+				ad714x_slider_cal_flt_pos(ad714x, idx);
+				input_report_abs(sw->input, ABS_X, sw->flt_pos);
+				input_report_key(sw->input, BTN_TOUCH, 1);
+			} else {
+				/* When the user lifts off the sensor, configure
+				 * the AD714X back to threshold interrupt mode.
+				 */
+				ad714x_slider_use_thr_int(ad714x, idx);
+				sw->state = IDLE;
+				input_report_key(sw->input, BTN_TOUCH, 0);
+				dev_dbg(ad714x->dev, "slider %d released\n",
+					idx);
+			}
+			input_sync(sw->input);
+		}
+		break;
+
+	default:
+		break;
+	}
+}
+
+/*
+ * When the scroll wheel is activated, we compute the absolute position based
+ * on the sensor values. To calculate the position, we first determine the
+ * sensor that has the greatest response among the 8 sensors that constitutes
+ * the scrollwheel. Then we determined the 2 sensors on either sides of the
+ * sensor with the highest response and we apply weights to these sensors.
+ */
+static void ad714x_wheel_cal_highest_stage(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+	struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
+
+	sw->pre_highest_stage = sw->highest_stage;
+	sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage,
+			hw->end_stage);
+
+	dev_dbg(ad714x->dev, "wheel %d highest_stage:%d\n", idx,
+		sw->highest_stage);
+}
+
+static void ad714x_wheel_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+	int i;
+
+	ad714x->read(ad714x, CDC_RESULT_S0 + hw->start_stage,
+			&ad714x->adc_reg[hw->start_stage],
+			hw->end_stage - hw->start_stage + 1);
+
+	for (i = hw->start_stage; i <= hw->end_stage; i++) {
+		ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
+				&ad714x->amb_reg[i], 1);
+		if (ad714x->adc_reg[i] > ad714x->amb_reg[i])
+			ad714x->sensor_val[i] =
+				ad714x->adc_reg[i] - ad714x->amb_reg[i];
+		else
+			ad714x->sensor_val[i] = 0;
+	}
+}
+
+/*
+ * When the scroll wheel is activated, we compute the absolute position based
+ * on the sensor values. To calculate the position, we first determine the
+ * sensor that has the greatest response among the sensors that constitutes
+ * the scrollwheel. Then we determined the sensors on either sides of the
+ * sensor with the highest response and we apply weights to these sensors. The
+ * result of this computation gives us the mean value.
+ */
+
+static void ad714x_wheel_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+	struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
+	int stage_num = hw->end_stage - hw->start_stage + 1;
+	int first_before, highest, first_after;
+	int a_param, b_param;
+
+	first_before = (sw->highest_stage + stage_num - 1) % stage_num;
+	highest = sw->highest_stage;
+	first_after = (sw->highest_stage + stage_num + 1) % stage_num;
+
+	a_param = ad714x->sensor_val[highest] *
+		(highest - hw->start_stage) +
+		ad714x->sensor_val[first_before] *
+		(highest - hw->start_stage - 1) +
+		ad714x->sensor_val[first_after] *
+		(highest - hw->start_stage + 1);
+	b_param = ad714x->sensor_val[highest] +
+		ad714x->sensor_val[first_before] +
+		ad714x->sensor_val[first_after];
+
+	sw->abs_pos = ((hw->max_coord / (hw->end_stage - hw->start_stage)) *
+			a_param) / b_param;
+
+	if (sw->abs_pos > hw->max_coord)
+		sw->abs_pos = hw->max_coord;
+	else if (sw->abs_pos < 0)
+		sw->abs_pos = 0;
+}
+
+static void ad714x_wheel_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+	struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
+	if (((sw->pre_highest_stage == hw->end_stage) &&
+			(sw->highest_stage == hw->start_stage)) ||
+	    ((sw->pre_highest_stage == hw->start_stage) &&
+			(sw->highest_stage == hw->end_stage)))
+		sw->flt_pos = sw->abs_pos;
+	else
+		sw->flt_pos = ((sw->flt_pos * 30) + (sw->abs_pos * 71)) / 100;
+
+	if (sw->flt_pos > hw->max_coord)
+		sw->flt_pos = hw->max_coord;
+}
+
+static void ad714x_wheel_use_com_int(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+
+	ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage);
+}
+
+static void ad714x_wheel_use_thr_int(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+
+	ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage);
+}
+
+static void ad714x_wheel_state_machine(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+	struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
+	unsigned short h_state, c_state;
+	unsigned short mask;
+
+	mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1);
+
+	h_state = ad714x->h_state & mask;
+	c_state = ad714x->c_state & mask;
+
+	switch (sw->state) {
+	case IDLE:
+		if (h_state) {
+			sw->state = JITTER;
+			/* In End of Conversion interrupt mode, the AD714X
+			 * continuously generates hardware interrupts.
+			 */
+			ad714x_wheel_use_com_int(ad714x, idx);
+			dev_dbg(ad714x->dev, "wheel %d touched\n", idx);
+		}
+		break;
+
+	case JITTER:
+		if (c_state == mask)	{
+			ad714x_wheel_cal_sensor_val(ad714x, idx);
+			ad714x_wheel_cal_highest_stage(ad714x, idx);
+			ad714x_wheel_cal_abs_pos(ad714x, idx);
+			sw->flt_pos = sw->abs_pos;
+			sw->state = ACTIVE;
+		}
+		break;
+
+	case ACTIVE:
+		if (c_state == mask) {
+			if (h_state) {
+				ad714x_wheel_cal_sensor_val(ad714x, idx);
+				ad714x_wheel_cal_highest_stage(ad714x, idx);
+				ad714x_wheel_cal_abs_pos(ad714x, idx);
+				ad714x_wheel_cal_flt_pos(ad714x, idx);
+				input_report_abs(sw->input, ABS_WHEEL,
+					sw->flt_pos);
+				input_report_key(sw->input, BTN_TOUCH, 1);
+			} else {
+				/* When the user lifts off the sensor, configure
+				 * the AD714X back to threshold interrupt mode.
+				 */
+				ad714x_wheel_use_thr_int(ad714x, idx);
+				sw->state = IDLE;
+				input_report_key(sw->input, BTN_TOUCH, 0);
+
+				dev_dbg(ad714x->dev, "wheel %d released\n",
+					idx);
+			}
+			input_sync(sw->input);
+		}
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void touchpad_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+	int i;
+
+	ad714x->read(ad714x, CDC_RESULT_S0 + hw->x_start_stage,
+			&ad714x->adc_reg[hw->x_start_stage],
+			hw->x_end_stage - hw->x_start_stage + 1);
+
+	for (i = hw->x_start_stage; i <= hw->x_end_stage; i++) {
+		ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
+				&ad714x->amb_reg[i], 1);
+		if (ad714x->adc_reg[i] > ad714x->amb_reg[i])
+			ad714x->sensor_val[i] =
+				ad714x->adc_reg[i] - ad714x->amb_reg[i];
+		else
+			ad714x->sensor_val[i] = 0;
+	}
+}
+
+static void touchpad_cal_highest_stage(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+	struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
+
+	sw->x_highest_stage = ad714x_cal_highest_stage(ad714x,
+		hw->x_start_stage, hw->x_end_stage);
+	sw->y_highest_stage = ad714x_cal_highest_stage(ad714x,
+		hw->y_start_stage, hw->y_end_stage);
+
+	dev_dbg(ad714x->dev,
+		"touchpad %d x_highest_stage:%d, y_highest_stage:%d\n",
+		idx, sw->x_highest_stage, sw->y_highest_stage);
+}
+
+/*
+ * If 2 fingers are touching the sensor then 2 peaks can be observed in the
+ * distribution.
+ * The arithmetic doesn't support to get absolute coordinates for multi-touch
+ * yet.
+ */
+static int touchpad_check_second_peak(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+	struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
+	int i;
+
+	for (i = hw->x_start_stage; i < sw->x_highest_stage; i++) {
+		if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1])
+			> (ad714x->sensor_val[i + 1] / 10))
+			return 1;
+	}
+
+	for (i = sw->x_highest_stage; i < hw->x_end_stage; i++) {
+		if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i])
+			> (ad714x->sensor_val[i] / 10))
+			return 1;
+	}
+
+	for (i = hw->y_start_stage; i < sw->y_highest_stage; i++) {
+		if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1])
+			> (ad714x->sensor_val[i + 1] / 10))
+			return 1;
+	}
+
+	for (i = sw->y_highest_stage; i < hw->y_end_stage; i++) {
+		if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i])
+			> (ad714x->sensor_val[i] / 10))
+			return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * If only one finger is used to activate the touch pad then only 1 peak will be
+ * registered in the distribution. This peak and the 2 adjacent sensors will be
+ * used in the calculation of the absolute position. This will prevent hand
+ * shadows to affect the absolute position calculation.
+ */
+static void touchpad_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+	struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
+
+	sw->x_abs_pos = ad714x_cal_abs_pos(ad714x, hw->x_start_stage,
+			hw->x_end_stage, sw->x_highest_stage, hw->x_max_coord);
+	sw->y_abs_pos = ad714x_cal_abs_pos(ad714x, hw->y_start_stage,
+			hw->y_end_stage, sw->y_highest_stage, hw->y_max_coord);
+
+	dev_dbg(ad714x->dev, "touchpad %d absolute position:(%d, %d)\n", idx,
+			sw->x_abs_pos, sw->y_abs_pos);
+}
+
+static void touchpad_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
+
+	sw->x_flt_pos = (sw->x_flt_pos * (10 - 4) +
+			sw->x_abs_pos * 4)/10;
+	sw->y_flt_pos = (sw->y_flt_pos * (10 - 4) +
+			sw->y_abs_pos * 4)/10;
+
+	dev_dbg(ad714x->dev, "touchpad %d filter position:(%d, %d)\n",
+			idx, sw->x_flt_pos, sw->y_flt_pos);
+}
+
+/*
+ * To prevent distortion from showing in the absolute position, it is
+ * necessary to detect the end points. When endpoints are detected, the
+ * driver stops updating the status variables with absolute positions.
+ * End points are detected on the 4 edges of the touchpad sensor. The
+ * method to detect them is the same for all 4.
+ * To detect the end points, the firmware computes the difference in
+ * percent between the sensor on the edge and the adjacent one. The
+ * difference is calculated in percent in order to make the end point
+ * detection independent of the pressure.
+ */
+
+#define LEFT_END_POINT_DETECTION_LEVEL                  550
+#define RIGHT_END_POINT_DETECTION_LEVEL                 750
+#define LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL         850
+#define TOP_END_POINT_DETECTION_LEVEL                   550
+#define BOTTOM_END_POINT_DETECTION_LEVEL                950
+#define TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL         700
+static int touchpad_check_endpoint(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+	struct ad714x_touchpad_drv *sw  = &ad714x->sw->touchpad[idx];
+	int percent_sensor_diff;
+
+	/* left endpoint detect */
+	percent_sensor_diff = (ad714x->sensor_val[hw->x_start_stage] -
+			ad714x->sensor_val[hw->x_start_stage + 1]) * 100 /
+			ad714x->sensor_val[hw->x_start_stage + 1];
+	if (!sw->left_ep) {
+		if (percent_sensor_diff >= LEFT_END_POINT_DETECTION_LEVEL)  {
+			sw->left_ep = 1;
+			sw->left_ep_val =
+				ad714x->sensor_val[hw->x_start_stage + 1];
+		}
+	} else {
+		if ((percent_sensor_diff < LEFT_END_POINT_DETECTION_LEVEL) &&
+		    (ad714x->sensor_val[hw->x_start_stage + 1] >
+		     LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->left_ep_val))
+			sw->left_ep = 0;
+	}
+
+	/* right endpoint detect */
+	percent_sensor_diff = (ad714x->sensor_val[hw->x_end_stage] -
+			ad714x->sensor_val[hw->x_end_stage - 1]) * 100 /
+			ad714x->sensor_val[hw->x_end_stage - 1];
+	if (!sw->right_ep) {
+		if (percent_sensor_diff >= RIGHT_END_POINT_DETECTION_LEVEL)  {
+			sw->right_ep = 1;
+			sw->right_ep_val =
+				ad714x->sensor_val[hw->x_end_stage - 1];
+		}
+	} else {
+		if ((percent_sensor_diff < RIGHT_END_POINT_DETECTION_LEVEL) &&
+		(ad714x->sensor_val[hw->x_end_stage - 1] >
+		LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->right_ep_val))
+			sw->right_ep = 0;
+	}
+
+	/* top endpoint detect */
+	percent_sensor_diff = (ad714x->sensor_val[hw->y_start_stage] -
+			ad714x->sensor_val[hw->y_start_stage + 1]) * 100 /
+			ad714x->sensor_val[hw->y_start_stage + 1];
+	if (!sw->top_ep) {
+		if (percent_sensor_diff >= TOP_END_POINT_DETECTION_LEVEL)  {
+			sw->top_ep = 1;
+			sw->top_ep_val =
+				ad714x->sensor_val[hw->y_start_stage + 1];
+		}
+	} else {
+		if ((percent_sensor_diff < TOP_END_POINT_DETECTION_LEVEL) &&
+		(ad714x->sensor_val[hw->y_start_stage + 1] >
+		TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->top_ep_val))
+			sw->top_ep = 0;
+	}
+
+	/* bottom endpoint detect */
+	percent_sensor_diff = (ad714x->sensor_val[hw->y_end_stage] -
+		ad714x->sensor_val[hw->y_end_stage - 1]) * 100 /
+		ad714x->sensor_val[hw->y_end_stage - 1];
+	if (!sw->bottom_ep) {
+		if (percent_sensor_diff >= BOTTOM_END_POINT_DETECTION_LEVEL)  {
+			sw->bottom_ep = 1;
+			sw->bottom_ep_val =
+				ad714x->sensor_val[hw->y_end_stage - 1];
+		}
+	} else {
+		if ((percent_sensor_diff < BOTTOM_END_POINT_DETECTION_LEVEL) &&
+		(ad714x->sensor_val[hw->y_end_stage - 1] >
+		 TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->bottom_ep_val))
+			sw->bottom_ep = 0;
+	}
+
+	return sw->left_ep || sw->right_ep || sw->top_ep || sw->bottom_ep;
+}
+
+static void touchpad_use_com_int(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+
+	ad714x_use_com_int(ad714x, hw->x_start_stage, hw->x_end_stage);
+}
+
+static void touchpad_use_thr_int(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+
+	ad714x_use_thr_int(ad714x, hw->x_start_stage, hw->x_end_stage);
+	ad714x_use_thr_int(ad714x, hw->y_start_stage, hw->y_end_stage);
+}
+
+static void ad714x_touchpad_state_machine(struct ad714x_chip *ad714x, int idx)
+{
+	struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+	struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
+	unsigned short h_state, c_state;
+	unsigned short mask;
+
+	mask = (((1 << (hw->x_end_stage + 1)) - 1) -
+		((1 << hw->x_start_stage) - 1)) +
+		(((1 << (hw->y_end_stage + 1)) - 1) -
+		((1 << hw->y_start_stage) - 1));
+
+	h_state = ad714x->h_state & mask;
+	c_state = ad714x->c_state & mask;
+
+	switch (sw->state) {
+	case IDLE:
+		if (h_state) {
+			sw->state = JITTER;
+			/* In End of Conversion interrupt mode, the AD714X
+			 * continuously generates hardware interrupts.
+			 */
+			touchpad_use_com_int(ad714x, idx);
+			dev_dbg(ad714x->dev, "touchpad %d touched\n", idx);
+		}
+		break;
+
+	case JITTER:
+		if (c_state == mask) {
+			touchpad_cal_sensor_val(ad714x, idx);
+			touchpad_cal_highest_stage(ad714x, idx);
+			if ((!touchpad_check_second_peak(ad714x, idx)) &&
+				(!touchpad_check_endpoint(ad714x, idx))) {
+				dev_dbg(ad714x->dev,
+					"touchpad%d, 2 fingers or endpoint\n",
+					idx);
+				touchpad_cal_abs_pos(ad714x, idx);
+				sw->x_flt_pos = sw->x_abs_pos;
+				sw->y_flt_pos = sw->y_abs_pos;
+				sw->state = ACTIVE;
+			}
+		}
+		break;
+
+	case ACTIVE:
+		if (c_state == mask) {
+			if (h_state) {
+				touchpad_cal_sensor_val(ad714x, idx);
+				touchpad_cal_highest_stage(ad714x, idx);
+				if ((!touchpad_check_second_peak(ad714x, idx))
+				  && (!touchpad_check_endpoint(ad714x, idx))) {
+					touchpad_cal_abs_pos(ad714x, idx);
+					touchpad_cal_flt_pos(ad714x, idx);
+					input_report_abs(sw->input, ABS_X,
+						sw->x_flt_pos);
+					input_report_abs(sw->input, ABS_Y,
+						sw->y_flt_pos);
+					input_report_key(sw->input, BTN_TOUCH,
+						1);
+				}
+			} else {
+				/* When the user lifts off the sensor, configure
+				 * the AD714X back to threshold interrupt mode.
+				 */
+				touchpad_use_thr_int(ad714x, idx);
+				sw->state = IDLE;
+				input_report_key(sw->input, BTN_TOUCH, 0);
+				dev_dbg(ad714x->dev, "touchpad %d released\n",
+					idx);
+			}
+			input_sync(sw->input);
+		}
+		break;
+
+	default:
+		break;
+	}
+}
+
+static int ad714x_hw_detect(struct ad714x_chip *ad714x)
+{
+	unsigned short data;
+
+	ad714x->read(ad714x, AD714X_PARTID_REG, &data, 1);
+	switch (data & 0xFFF0) {
+	case AD7142_PARTID:
+		ad714x->product = 0x7142;
+		ad714x->version = data & 0xF;
+		dev_info(ad714x->dev, "found AD7142 captouch, rev:%d\n",
+				ad714x->version);
+		return 0;
+
+	case AD7143_PARTID:
+		ad714x->product = 0x7143;
+		ad714x->version = data & 0xF;
+		dev_info(ad714x->dev, "found AD7143 captouch, rev:%d\n",
+				ad714x->version);
+		return 0;
+
+	case AD7147_PARTID:
+		ad714x->product = 0x7147;
+		ad714x->version = data & 0xF;
+		dev_info(ad714x->dev, "found AD7147(A) captouch, rev:%d\n",
+				ad714x->version);
+		return 0;
+
+	case AD7148_PARTID:
+		ad714x->product = 0x7148;
+		ad714x->version = data & 0xF;
+		dev_info(ad714x->dev, "found AD7148 captouch, rev:%d\n",
+				ad714x->version);
+		return 0;
+
+	default:
+		dev_err(ad714x->dev,
+			"fail to detect AD714X captouch, read ID is %04x\n",
+			data);
+		return -ENODEV;
+	}
+}
+
+static void ad714x_hw_init(struct ad714x_chip *ad714x)
+{
+	int i, j;
+	unsigned short reg_base;
+	unsigned short data;
+
+	/* configuration CDC and interrupts */
+
+	for (i = 0; i < STAGE_NUM; i++) {
+		reg_base = AD714X_STAGECFG_REG + i * STAGE_CFGREG_NUM;
+		for (j = 0; j < STAGE_CFGREG_NUM; j++)
+			ad714x->write(ad714x, reg_base + j,
+					ad714x->hw->stage_cfg_reg[i][j]);
+	}
+
+	for (i = 0; i < SYS_CFGREG_NUM; i++)
+		ad714x->write(ad714x, AD714X_SYSCFG_REG + i,
+			ad714x->hw->sys_cfg_reg[i]);
+	for (i = 0; i < SYS_CFGREG_NUM; i++)
+		ad714x->read(ad714x, AD714X_SYSCFG_REG + i, &data, 1);
+
+	ad714x->write(ad714x, AD714X_STG_CAL_EN_REG, 0xFFF);
+
+	/* clear all interrupts */
+	ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
+}
+
+static irqreturn_t ad714x_interrupt_thread(int irq, void *data)
+{
+	struct ad714x_chip *ad714x = data;
+	int i;
+
+	mutex_lock(&ad714x->mutex);
+
+	ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
+
+	for (i = 0; i < ad714x->hw->button_num; i++)
+		ad714x_button_state_machine(ad714x, i);
+	for (i = 0; i < ad714x->hw->slider_num; i++)
+		ad714x_slider_state_machine(ad714x, i);
+	for (i = 0; i < ad714x->hw->wheel_num; i++)
+		ad714x_wheel_state_machine(ad714x, i);
+	for (i = 0; i < ad714x->hw->touchpad_num; i++)
+		ad714x_touchpad_state_machine(ad714x, i);
+
+	mutex_unlock(&ad714x->mutex);
+
+	return IRQ_HANDLED;
+}
+
+struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
+				 ad714x_read_t read, ad714x_write_t write)
+{
+	int i;
+	int error;
+	struct input_dev *input;
+
+	struct ad714x_platform_data *plat_data = dev_get_platdata(dev);
+	struct ad714x_chip *ad714x;
+	void *drv_mem;
+	unsigned long irqflags;
+
+	struct ad714x_button_drv *bt_drv;
+	struct ad714x_slider_drv *sd_drv;
+	struct ad714x_wheel_drv *wl_drv;
+	struct ad714x_touchpad_drv *tp_drv;
+
+
+	if (irq <= 0) {
+		dev_err(dev, "IRQ not configured!\n");
+		error = -EINVAL;
+		return ERR_PTR(error);
+	}
+
+	if (dev_get_platdata(dev) == NULL) {
+		dev_err(dev, "platform data for ad714x doesn't exist\n");
+		error = -EINVAL;
+		return ERR_PTR(error);
+	}
+
+	ad714x = devm_kzalloc(dev, sizeof(*ad714x) + sizeof(*ad714x->sw) +
+				   sizeof(*sd_drv) * plat_data->slider_num +
+				   sizeof(*wl_drv) * plat_data->wheel_num +
+				   sizeof(*tp_drv) * plat_data->touchpad_num +
+				   sizeof(*bt_drv) * plat_data->button_num,
+			      GFP_KERNEL);
+	if (!ad714x) {
+		error = -ENOMEM;
+		return ERR_PTR(error);
+	}
+	ad714x->hw = plat_data;
+
+	drv_mem = ad714x + 1;
+	ad714x->sw = drv_mem;
+	drv_mem += sizeof(*ad714x->sw);
+	ad714x->sw->slider = sd_drv = drv_mem;
+	drv_mem += sizeof(*sd_drv) * ad714x->hw->slider_num;
+	ad714x->sw->wheel = wl_drv = drv_mem;
+	drv_mem += sizeof(*wl_drv) * ad714x->hw->wheel_num;
+	ad714x->sw->touchpad = tp_drv = drv_mem;
+	drv_mem += sizeof(*tp_drv) * ad714x->hw->touchpad_num;
+	ad714x->sw->button = bt_drv = drv_mem;
+	drv_mem += sizeof(*bt_drv) * ad714x->hw->button_num;
+
+	ad714x->read = read;
+	ad714x->write = write;
+	ad714x->irq = irq;
+	ad714x->dev = dev;
+
+	error = ad714x_hw_detect(ad714x);
+	if (error)
+		return ERR_PTR(error);
+
+	/* initialize and request sw/hw resources */
+
+	ad714x_hw_init(ad714x);
+	mutex_init(&ad714x->mutex);
+
+	/* a slider uses one input_dev instance */
+	if (ad714x->hw->slider_num > 0) {
+		struct ad714x_slider_plat *sd_plat = ad714x->hw->slider;
+
+		for (i = 0; i < ad714x->hw->slider_num; i++) {
+			input = devm_input_allocate_device(dev);
+			if (!input)
+				return ERR_PTR(-ENOMEM);
+
+			__set_bit(EV_ABS, input->evbit);
+			__set_bit(EV_KEY, input->evbit);
+			__set_bit(ABS_X, input->absbit);
+			__set_bit(BTN_TOUCH, input->keybit);
+			input_set_abs_params(input,
+				ABS_X, 0, sd_plat->max_coord, 0, 0);
+
+			input->id.bustype = bus_type;
+			input->id.product = ad714x->product;
+			input->id.version = ad714x->version;
+			input->name = "ad714x_captouch_slider";
+			input->dev.parent = dev;
+
+			error = input_register_device(input);
+			if (error)
+				return ERR_PTR(error);
+
+			sd_drv[i].input = input;
+		}
+	}
+
+	/* a wheel uses one input_dev instance */
+	if (ad714x->hw->wheel_num > 0) {
+		struct ad714x_wheel_plat *wl_plat = ad714x->hw->wheel;
+
+		for (i = 0; i < ad714x->hw->wheel_num; i++) {
+			input = devm_input_allocate_device(dev);
+			if (!input)
+				return ERR_PTR(-ENOMEM);
+
+			__set_bit(EV_KEY, input->evbit);
+			__set_bit(EV_ABS, input->evbit);
+			__set_bit(ABS_WHEEL, input->absbit);
+			__set_bit(BTN_TOUCH, input->keybit);
+			input_set_abs_params(input,
+				ABS_WHEEL, 0, wl_plat->max_coord, 0, 0);
+
+			input->id.bustype = bus_type;
+			input->id.product = ad714x->product;
+			input->id.version = ad714x->version;
+			input->name = "ad714x_captouch_wheel";
+			input->dev.parent = dev;
+
+			error = input_register_device(input);
+			if (error)
+				return ERR_PTR(error);
+
+			wl_drv[i].input = input;
+		}
+	}
+
+	/* a touchpad uses one input_dev instance */
+	if (ad714x->hw->touchpad_num > 0) {
+		struct ad714x_touchpad_plat *tp_plat = ad714x->hw->touchpad;
+
+		for (i = 0; i < ad714x->hw->touchpad_num; i++) {
+			input = devm_input_allocate_device(dev);
+			if (!input)
+				return ERR_PTR(-ENOMEM);
+
+			__set_bit(EV_ABS, input->evbit);
+			__set_bit(EV_KEY, input->evbit);
+			__set_bit(ABS_X, input->absbit);
+			__set_bit(ABS_Y, input->absbit);
+			__set_bit(BTN_TOUCH, input->keybit);
+			input_set_abs_params(input,
+				ABS_X, 0, tp_plat->x_max_coord, 0, 0);
+			input_set_abs_params(input,
+				ABS_Y, 0, tp_plat->y_max_coord, 0, 0);
+
+			input->id.bustype = bus_type;
+			input->id.product = ad714x->product;
+			input->id.version = ad714x->version;
+			input->name = "ad714x_captouch_pad";
+			input->dev.parent = dev;
+
+			error = input_register_device(input);
+			if (error)
+				return ERR_PTR(error);
+
+			tp_drv[i].input = input;
+		}
+	}
+
+	/* all buttons use one input node */
+	if (ad714x->hw->button_num > 0) {
+		struct ad714x_button_plat *bt_plat = ad714x->hw->button;
+
+		input = devm_input_allocate_device(dev);
+		if (!input) {
+			error = -ENOMEM;
+			return ERR_PTR(error);
+		}
+
+		__set_bit(EV_KEY, input->evbit);
+		for (i = 0; i < ad714x->hw->button_num; i++) {
+			bt_drv[i].input = input;
+			__set_bit(bt_plat[i].keycode, input->keybit);
+		}
+
+		input->id.bustype = bus_type;
+		input->id.product = ad714x->product;
+		input->id.version = ad714x->version;
+		input->name = "ad714x_captouch_button";
+		input->dev.parent = dev;
+
+		error = input_register_device(input);
+		if (error)
+			return ERR_PTR(error);
+	}
+
+	irqflags = plat_data->irqflags ?: IRQF_TRIGGER_FALLING;
+	irqflags |= IRQF_ONESHOT;
+
+	error = devm_request_threaded_irq(dev, ad714x->irq, NULL,
+					  ad714x_interrupt_thread,
+					  irqflags, "ad714x_captouch", ad714x);
+	if (error) {
+		dev_err(dev, "can't allocate irq %d\n", ad714x->irq);
+		return ERR_PTR(error);
+	}
+
+	return ad714x;
+}
+EXPORT_SYMBOL(ad714x_probe);
+
+#ifdef CONFIG_PM
+int ad714x_disable(struct ad714x_chip *ad714x)
+{
+	unsigned short data;
+
+	dev_dbg(ad714x->dev, "%s enter\n", __func__);
+
+	mutex_lock(&ad714x->mutex);
+
+	data = ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL] | 0x3;
+	ad714x->write(ad714x, AD714X_PWR_CTRL, data);
+
+	mutex_unlock(&ad714x->mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(ad714x_disable);
+
+int ad714x_enable(struct ad714x_chip *ad714x)
+{
+	dev_dbg(ad714x->dev, "%s enter\n", __func__);
+
+	mutex_lock(&ad714x->mutex);
+
+	/* resume to non-shutdown mode */
+
+	ad714x->write(ad714x, AD714X_PWR_CTRL,
+			ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL]);
+
+	/* make sure the interrupt output line is not low level after resume,
+	 * otherwise we will get no chance to enter falling-edge irq again
+	 */
+
+	ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
+
+	mutex_unlock(&ad714x->mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(ad714x_enable);
+#endif
+
+MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor Driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ad714x.h b/drivers/input/misc/ad714x.h
new file mode 100644
index 0000000..5d65d30
--- /dev/null
+++ b/drivers/input/misc/ad714x.h
@@ -0,0 +1,54 @@
+/*
+ * AD714X CapTouch Programmable Controller driver (bus interfaces)
+ *
+ * Copyright 2009-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _AD714X_H_
+#define _AD714X_H_
+
+#include <linux/types.h>
+
+#define STAGE_NUM              12
+
+struct device;
+struct ad714x_platform_data;
+struct ad714x_driver_data;
+struct ad714x_chip;
+
+typedef int (*ad714x_read_t)(struct ad714x_chip *, unsigned short, unsigned short *, size_t);
+typedef int (*ad714x_write_t)(struct ad714x_chip *, unsigned short, unsigned short);
+
+struct ad714x_chip {
+	unsigned short l_state;
+	unsigned short h_state;
+	unsigned short c_state;
+	unsigned short adc_reg[STAGE_NUM];
+	unsigned short amb_reg[STAGE_NUM];
+	unsigned short sensor_val[STAGE_NUM];
+
+	struct ad714x_platform_data *hw;
+	struct ad714x_driver_data *sw;
+
+	int irq;
+	struct device *dev;
+	ad714x_read_t read;
+	ad714x_write_t write;
+
+	struct mutex mutex;
+
+	unsigned product;
+	unsigned version;
+
+	__be16 xfer_buf[16] ____cacheline_aligned;
+
+};
+
+int ad714x_disable(struct ad714x_chip *ad714x);
+int ad714x_enable(struct ad714x_chip *ad714x);
+struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
+				 ad714x_read_t read, ad714x_write_t write);
+
+#endif
diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c
new file mode 100644
index 0000000..a8b0a2e
--- /dev/null
+++ b/drivers/input/misc/adxl34x-i2c.c
@@ -0,0 +1,173 @@
+/*
+ * ADLX345/346 Three-Axis Digital Accelerometers (I2C Interface)
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h>	/* BUS_I2C */
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/types.h>
+#include <linux/pm.h>
+#include "adxl34x.h"
+
+static int adxl34x_smbus_read(struct device *dev, unsigned char reg)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int adxl34x_smbus_write(struct device *dev,
+			       unsigned char reg, unsigned char val)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int adxl34x_smbus_read_block(struct device *dev,
+				    unsigned char reg, int count,
+				    void *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	return i2c_smbus_read_i2c_block_data(client, reg, count, buf);
+}
+
+static int adxl34x_i2c_read_block(struct device *dev,
+				  unsigned char reg, int count,
+				  void *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int ret;
+
+	ret = i2c_master_send(client, &reg, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = i2c_master_recv(client, buf, count);
+	if (ret < 0)
+		return ret;
+
+	if (ret != count)
+		return -EIO;
+
+	return 0;
+}
+
+static const struct adxl34x_bus_ops adxl34x_smbus_bops = {
+	.bustype	= BUS_I2C,
+	.write		= adxl34x_smbus_write,
+	.read		= adxl34x_smbus_read,
+	.read_block	= adxl34x_smbus_read_block,
+};
+
+static const struct adxl34x_bus_ops adxl34x_i2c_bops = {
+	.bustype	= BUS_I2C,
+	.write		= adxl34x_smbus_write,
+	.read		= adxl34x_smbus_read,
+	.read_block	= adxl34x_i2c_read_block,
+};
+
+static int adxl34x_i2c_probe(struct i2c_client *client,
+				       const struct i2c_device_id *id)
+{
+	struct adxl34x *ac;
+	int error;
+
+	error = i2c_check_functionality(client->adapter,
+			I2C_FUNC_SMBUS_BYTE_DATA);
+	if (!error) {
+		dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+		return -EIO;
+	}
+
+	ac = adxl34x_probe(&client->dev, client->irq, false,
+			   i2c_check_functionality(client->adapter,
+						   I2C_FUNC_SMBUS_READ_I2C_BLOCK) ?
+				&adxl34x_smbus_bops : &adxl34x_i2c_bops);
+	if (IS_ERR(ac))
+		return PTR_ERR(ac);
+
+	i2c_set_clientdata(client, ac);
+
+	return 0;
+}
+
+static int adxl34x_i2c_remove(struct i2c_client *client)
+{
+	struct adxl34x *ac = i2c_get_clientdata(client);
+
+	return adxl34x_remove(ac);
+}
+
+static int __maybe_unused adxl34x_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adxl34x *ac = i2c_get_clientdata(client);
+
+	adxl34x_suspend(ac);
+
+	return 0;
+}
+
+static int __maybe_unused adxl34x_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adxl34x *ac = i2c_get_clientdata(client);
+
+	adxl34x_resume(ac);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(adxl34x_i2c_pm, adxl34x_i2c_suspend,
+			 adxl34x_i2c_resume);
+
+static const struct i2c_device_id adxl34x_id[] = {
+	{ "adxl34x", 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, adxl34x_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id adxl34x_of_id[] = {
+	/*
+	 * The ADXL346 is backward-compatible with the ADXL345. Differences are
+	 * handled by runtime detection of the device model, there's thus no
+	 * need for listing the "adi,adxl346" compatible value explicitly.
+	 */
+	{ .compatible = "adi,adxl345", },
+	/*
+	 * Deprecated, DT nodes should use one or more of the device-specific
+	 * compatible values "adi,adxl345" and "adi,adxl346".
+	 */
+	{ .compatible = "adi,adxl34x", },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, adxl34x_of_id);
+#endif
+
+static struct i2c_driver adxl34x_driver = {
+	.driver = {
+		.name = "adxl34x",
+		.pm = &adxl34x_i2c_pm,
+		.of_match_table = of_match_ptr(adxl34x_of_id),
+	},
+	.probe    = adxl34x_i2c_probe,
+	.remove   = adxl34x_i2c_remove,
+	.id_table = adxl34x_id,
+};
+
+module_i2c_driver(adxl34x_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer I2C Bus Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c
new file mode 100644
index 0000000..3ec03ad
--- /dev/null
+++ b/drivers/input/misc/adxl34x-spi.c
@@ -0,0 +1,133 @@
+/*
+ * ADLX345/346 Three-Axis Digital Accelerometers (SPI Interface)
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h>	/* BUS_SPI */
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/pm.h>
+#include <linux/types.h>
+#include "adxl34x.h"
+
+#define MAX_SPI_FREQ_HZ		5000000
+#define MAX_FREQ_NO_FIFODELAY	1500000
+#define ADXL34X_CMD_MULTB	(1 << 6)
+#define ADXL34X_CMD_READ	(1 << 7)
+#define ADXL34X_WRITECMD(reg)	(reg & 0x3F)
+#define ADXL34X_READCMD(reg)	(ADXL34X_CMD_READ | (reg & 0x3F))
+#define ADXL34X_READMB_CMD(reg) (ADXL34X_CMD_READ | ADXL34X_CMD_MULTB \
+					| (reg & 0x3F))
+
+static int adxl34x_spi_read(struct device *dev, unsigned char reg)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	unsigned char cmd;
+
+	cmd = ADXL34X_READCMD(reg);
+
+	return spi_w8r8(spi, cmd);
+}
+
+static int adxl34x_spi_write(struct device *dev,
+			     unsigned char reg, unsigned char val)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	unsigned char buf[2];
+
+	buf[0] = ADXL34X_WRITECMD(reg);
+	buf[1] = val;
+
+	return spi_write(spi, buf, sizeof(buf));
+}
+
+static int adxl34x_spi_read_block(struct device *dev,
+				  unsigned char reg, int count,
+				  void *buf)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	ssize_t status;
+
+	reg = ADXL34X_READMB_CMD(reg);
+	status = spi_write_then_read(spi, &reg, 1, buf, count);
+
+	return (status < 0) ? status : 0;
+}
+
+static const struct adxl34x_bus_ops adxl34x_spi_bops = {
+	.bustype	= BUS_SPI,
+	.write		= adxl34x_spi_write,
+	.read		= adxl34x_spi_read,
+	.read_block	= adxl34x_spi_read_block,
+};
+
+static int adxl34x_spi_probe(struct spi_device *spi)
+{
+	struct adxl34x *ac;
+
+	/* don't exceed max specified SPI CLK frequency */
+	if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
+		dev_err(&spi->dev, "SPI CLK %d Hz too fast\n", spi->max_speed_hz);
+		return -EINVAL;
+	}
+
+	ac = adxl34x_probe(&spi->dev, spi->irq,
+			   spi->max_speed_hz > MAX_FREQ_NO_FIFODELAY,
+			   &adxl34x_spi_bops);
+
+	if (IS_ERR(ac))
+		return PTR_ERR(ac);
+
+	spi_set_drvdata(spi, ac);
+
+	return 0;
+}
+
+static int adxl34x_spi_remove(struct spi_device *spi)
+{
+	struct adxl34x *ac = spi_get_drvdata(spi);
+
+	return adxl34x_remove(ac);
+}
+
+static int __maybe_unused adxl34x_spi_suspend(struct device *dev)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct adxl34x *ac = spi_get_drvdata(spi);
+
+	adxl34x_suspend(ac);
+
+	return 0;
+}
+
+static int __maybe_unused adxl34x_spi_resume(struct device *dev)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct adxl34x *ac = spi_get_drvdata(spi);
+
+	adxl34x_resume(ac);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(adxl34x_spi_pm, adxl34x_spi_suspend,
+			 adxl34x_spi_resume);
+
+static struct spi_driver adxl34x_driver = {
+	.driver = {
+		.name = "adxl34x",
+		.pm = &adxl34x_spi_pm,
+	},
+	.probe   = adxl34x_spi_probe,
+	.remove  = adxl34x_spi_remove,
+};
+
+module_spi_driver(adxl34x_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer SPI Bus Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c
new file mode 100644
index 0000000..2b2d02f
--- /dev/null
+++ b/drivers/input/misc/adxl34x.c
@@ -0,0 +1,913 @@
+/*
+ * ADXL345/346 Three-Axis Digital Accelerometers
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/input/adxl34x.h>
+#include <linux/module.h>
+
+#include "adxl34x.h"
+
+/* ADXL345/6 Register Map */
+#define DEVID		0x00	/* R   Device ID */
+#define THRESH_TAP	0x1D	/* R/W Tap threshold */
+#define OFSX		0x1E	/* R/W X-axis offset */
+#define OFSY		0x1F	/* R/W Y-axis offset */
+#define OFSZ		0x20	/* R/W Z-axis offset */
+#define DUR		0x21	/* R/W Tap duration */
+#define LATENT		0x22	/* R/W Tap latency */
+#define WINDOW		0x23	/* R/W Tap window */
+#define THRESH_ACT	0x24	/* R/W Activity threshold */
+#define THRESH_INACT	0x25	/* R/W Inactivity threshold */
+#define TIME_INACT	0x26	/* R/W Inactivity time */
+#define ACT_INACT_CTL	0x27	/* R/W Axis enable control for activity and */
+				/* inactivity detection */
+#define THRESH_FF	0x28	/* R/W Free-fall threshold */
+#define TIME_FF		0x29	/* R/W Free-fall time */
+#define TAP_AXES	0x2A	/* R/W Axis control for tap/double tap */
+#define ACT_TAP_STATUS	0x2B	/* R   Source of tap/double tap */
+#define BW_RATE		0x2C	/* R/W Data rate and power mode control */
+#define POWER_CTL	0x2D	/* R/W Power saving features control */
+#define INT_ENABLE	0x2E	/* R/W Interrupt enable control */
+#define INT_MAP		0x2F	/* R/W Interrupt mapping control */
+#define INT_SOURCE	0x30	/* R   Source of interrupts */
+#define DATA_FORMAT	0x31	/* R/W Data format control */
+#define DATAX0		0x32	/* R   X-Axis Data 0 */
+#define DATAX1		0x33	/* R   X-Axis Data 1 */
+#define DATAY0		0x34	/* R   Y-Axis Data 0 */
+#define DATAY1		0x35	/* R   Y-Axis Data 1 */
+#define DATAZ0		0x36	/* R   Z-Axis Data 0 */
+#define DATAZ1		0x37	/* R   Z-Axis Data 1 */
+#define FIFO_CTL	0x38	/* R/W FIFO control */
+#define FIFO_STATUS	0x39	/* R   FIFO status */
+#define TAP_SIGN	0x3A	/* R   Sign and source for tap/double tap */
+/* Orientation ADXL346 only */
+#define ORIENT_CONF	0x3B	/* R/W Orientation configuration */
+#define ORIENT		0x3C	/* R   Orientation status */
+
+/* DEVIDs */
+#define ID_ADXL345	0xE5
+#define ID_ADXL346	0xE6
+
+/* INT_ENABLE/INT_MAP/INT_SOURCE Bits */
+#define DATA_READY	(1 << 7)
+#define SINGLE_TAP	(1 << 6)
+#define DOUBLE_TAP	(1 << 5)
+#define ACTIVITY	(1 << 4)
+#define INACTIVITY	(1 << 3)
+#define FREE_FALL	(1 << 2)
+#define WATERMARK	(1 << 1)
+#define OVERRUN		(1 << 0)
+
+/* ACT_INACT_CONTROL Bits */
+#define ACT_ACDC	(1 << 7)
+#define ACT_X_EN	(1 << 6)
+#define ACT_Y_EN	(1 << 5)
+#define ACT_Z_EN	(1 << 4)
+#define INACT_ACDC	(1 << 3)
+#define INACT_X_EN	(1 << 2)
+#define INACT_Y_EN	(1 << 1)
+#define INACT_Z_EN	(1 << 0)
+
+/* TAP_AXES Bits */
+#define SUPPRESS	(1 << 3)
+#define TAP_X_EN	(1 << 2)
+#define TAP_Y_EN	(1 << 1)
+#define TAP_Z_EN	(1 << 0)
+
+/* ACT_TAP_STATUS Bits */
+#define ACT_X_SRC	(1 << 6)
+#define ACT_Y_SRC	(1 << 5)
+#define ACT_Z_SRC	(1 << 4)
+#define ASLEEP		(1 << 3)
+#define TAP_X_SRC	(1 << 2)
+#define TAP_Y_SRC	(1 << 1)
+#define TAP_Z_SRC	(1 << 0)
+
+/* BW_RATE Bits */
+#define LOW_POWER	(1 << 4)
+#define RATE(x)		((x) & 0xF)
+
+/* POWER_CTL Bits */
+#define PCTL_LINK	(1 << 5)
+#define PCTL_AUTO_SLEEP (1 << 4)
+#define PCTL_MEASURE	(1 << 3)
+#define PCTL_SLEEP	(1 << 2)
+#define PCTL_WAKEUP(x)	((x) & 0x3)
+
+/* DATA_FORMAT Bits */
+#define SELF_TEST	(1 << 7)
+#define SPI		(1 << 6)
+#define INT_INVERT	(1 << 5)
+#define FULL_RES	(1 << 3)
+#define JUSTIFY		(1 << 2)
+#define RANGE(x)	((x) & 0x3)
+#define RANGE_PM_2g	0
+#define RANGE_PM_4g	1
+#define RANGE_PM_8g	2
+#define RANGE_PM_16g	3
+
+/*
+ * Maximum value our axis may get in full res mode for the input device
+ * (signed 13 bits)
+ */
+#define ADXL_FULLRES_MAX_VAL 4096
+
+/*
+ * Maximum value our axis may get in fixed res mode for the input device
+ * (signed 10 bits)
+ */
+#define ADXL_FIXEDRES_MAX_VAL 512
+
+/* FIFO_CTL Bits */
+#define FIFO_MODE(x)	(((x) & 0x3) << 6)
+#define FIFO_BYPASS	0
+#define FIFO_FIFO	1
+#define FIFO_STREAM	2
+#define FIFO_TRIGGER	3
+#define TRIGGER		(1 << 5)
+#define SAMPLES(x)	((x) & 0x1F)
+
+/* FIFO_STATUS Bits */
+#define FIFO_TRIG	(1 << 7)
+#define ENTRIES(x)	((x) & 0x3F)
+
+/* TAP_SIGN Bits ADXL346 only */
+#define XSIGN		(1 << 6)
+#define YSIGN		(1 << 5)
+#define ZSIGN		(1 << 4)
+#define XTAP		(1 << 3)
+#define YTAP		(1 << 2)
+#define ZTAP		(1 << 1)
+
+/* ORIENT_CONF ADXL346 only */
+#define ORIENT_DEADZONE(x)	(((x) & 0x7) << 4)
+#define ORIENT_DIVISOR(x)	((x) & 0x7)
+
+/* ORIENT ADXL346 only */
+#define ADXL346_2D_VALID		(1 << 6)
+#define ADXL346_2D_ORIENT(x)		(((x) & 0x30) >> 4)
+#define ADXL346_3D_VALID		(1 << 3)
+#define ADXL346_3D_ORIENT(x)		((x) & 0x7)
+#define ADXL346_2D_PORTRAIT_POS		0	/* +X */
+#define ADXL346_2D_PORTRAIT_NEG		1	/* -X */
+#define ADXL346_2D_LANDSCAPE_POS	2	/* +Y */
+#define ADXL346_2D_LANDSCAPE_NEG	3	/* -Y */
+
+#define ADXL346_3D_FRONT		3	/* +X */
+#define ADXL346_3D_BACK			4	/* -X */
+#define ADXL346_3D_RIGHT		2	/* +Y */
+#define ADXL346_3D_LEFT			5	/* -Y */
+#define ADXL346_3D_TOP			1	/* +Z */
+#define ADXL346_3D_BOTTOM		6	/* -Z */
+
+#undef ADXL_DEBUG
+
+#define ADXL_X_AXIS			0
+#define ADXL_Y_AXIS			1
+#define ADXL_Z_AXIS			2
+
+#define AC_READ(ac, reg)	((ac)->bops->read((ac)->dev, reg))
+#define AC_WRITE(ac, reg, val)	((ac)->bops->write((ac)->dev, reg, val))
+
+struct axis_triple {
+	int x;
+	int y;
+	int z;
+};
+
+struct adxl34x {
+	struct device *dev;
+	struct input_dev *input;
+	struct mutex mutex;	/* reentrant protection for struct */
+	struct adxl34x_platform_data pdata;
+	struct axis_triple swcal;
+	struct axis_triple hwcal;
+	struct axis_triple saved;
+	char phys[32];
+	unsigned orient2d_saved;
+	unsigned orient3d_saved;
+	bool disabled;	/* P: mutex */
+	bool opened;	/* P: mutex */
+	bool suspended;	/* P: mutex */
+	bool fifo_delay;
+	int irq;
+	unsigned model;
+	unsigned int_mask;
+
+	const struct adxl34x_bus_ops *bops;
+};
+
+static const struct adxl34x_platform_data adxl34x_default_init = {
+	.tap_threshold = 35,
+	.tap_duration = 3,
+	.tap_latency = 20,
+	.tap_window = 20,
+	.tap_axis_control = ADXL_TAP_X_EN | ADXL_TAP_Y_EN | ADXL_TAP_Z_EN,
+	.act_axis_control = 0xFF,
+	.activity_threshold = 6,
+	.inactivity_threshold = 4,
+	.inactivity_time = 3,
+	.free_fall_threshold = 8,
+	.free_fall_time = 0x20,
+	.data_rate = 8,
+	.data_range = ADXL_FULL_RES,
+
+	.ev_type = EV_ABS,
+	.ev_code_x = ABS_X,	/* EV_REL */
+	.ev_code_y = ABS_Y,	/* EV_REL */
+	.ev_code_z = ABS_Z,	/* EV_REL */
+
+	.ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */
+	.power_mode = ADXL_AUTO_SLEEP | ADXL_LINK,
+	.fifo_mode = ADXL_FIFO_STREAM,
+	.watermark = 0,
+};
+
+static void adxl34x_get_triple(struct adxl34x *ac, struct axis_triple *axis)
+{
+	short buf[3];
+
+	ac->bops->read_block(ac->dev, DATAX0, DATAZ1 - DATAX0 + 1, buf);
+
+	mutex_lock(&ac->mutex);
+	ac->saved.x = (s16) le16_to_cpu(buf[0]);
+	axis->x = ac->saved.x;
+
+	ac->saved.y = (s16) le16_to_cpu(buf[1]);
+	axis->y = ac->saved.y;
+
+	ac->saved.z = (s16) le16_to_cpu(buf[2]);
+	axis->z = ac->saved.z;
+	mutex_unlock(&ac->mutex);
+}
+
+static void adxl34x_service_ev_fifo(struct adxl34x *ac)
+{
+	struct adxl34x_platform_data *pdata = &ac->pdata;
+	struct axis_triple axis;
+
+	adxl34x_get_triple(ac, &axis);
+
+	input_event(ac->input, pdata->ev_type, pdata->ev_code_x,
+		    axis.x - ac->swcal.x);
+	input_event(ac->input, pdata->ev_type, pdata->ev_code_y,
+		    axis.y - ac->swcal.y);
+	input_event(ac->input, pdata->ev_type, pdata->ev_code_z,
+		    axis.z - ac->swcal.z);
+}
+
+static void adxl34x_report_key_single(struct input_dev *input, int key)
+{
+	input_report_key(input, key, true);
+	input_sync(input);
+	input_report_key(input, key, false);
+}
+
+static void adxl34x_send_key_events(struct adxl34x *ac,
+		struct adxl34x_platform_data *pdata, int status, int press)
+{
+	int i;
+
+	for (i = ADXL_X_AXIS; i <= ADXL_Z_AXIS; i++) {
+		if (status & (1 << (ADXL_Z_AXIS - i)))
+			input_report_key(ac->input,
+					 pdata->ev_code_tap[i], press);
+	}
+}
+
+static void adxl34x_do_tap(struct adxl34x *ac,
+		struct adxl34x_platform_data *pdata, int status)
+{
+	adxl34x_send_key_events(ac, pdata, status, true);
+	input_sync(ac->input);
+	adxl34x_send_key_events(ac, pdata, status, false);
+}
+
+static irqreturn_t adxl34x_irq(int irq, void *handle)
+{
+	struct adxl34x *ac = handle;
+	struct adxl34x_platform_data *pdata = &ac->pdata;
+	int int_stat, tap_stat, samples, orient, orient_code;
+
+	/*
+	 * ACT_TAP_STATUS should be read before clearing the interrupt
+	 * Avoid reading ACT_TAP_STATUS in case TAP detection is disabled
+	 */
+
+	if (pdata->tap_axis_control & (TAP_X_EN | TAP_Y_EN | TAP_Z_EN))
+		tap_stat = AC_READ(ac, ACT_TAP_STATUS);
+	else
+		tap_stat = 0;
+
+	int_stat = AC_READ(ac, INT_SOURCE);
+
+	if (int_stat & FREE_FALL)
+		adxl34x_report_key_single(ac->input, pdata->ev_code_ff);
+
+	if (int_stat & OVERRUN)
+		dev_dbg(ac->dev, "OVERRUN\n");
+
+	if (int_stat & (SINGLE_TAP | DOUBLE_TAP)) {
+		adxl34x_do_tap(ac, pdata, tap_stat);
+
+		if (int_stat & DOUBLE_TAP)
+			adxl34x_do_tap(ac, pdata, tap_stat);
+	}
+
+	if (pdata->ev_code_act_inactivity) {
+		if (int_stat & ACTIVITY)
+			input_report_key(ac->input,
+					 pdata->ev_code_act_inactivity, 1);
+		if (int_stat & INACTIVITY)
+			input_report_key(ac->input,
+					 pdata->ev_code_act_inactivity, 0);
+	}
+
+	/*
+	 * ORIENTATION SENSING ADXL346 only
+	 */
+	if (pdata->orientation_enable) {
+		orient = AC_READ(ac, ORIENT);
+		if ((pdata->orientation_enable & ADXL_EN_ORIENTATION_2D) &&
+		    (orient & ADXL346_2D_VALID)) {
+
+			orient_code = ADXL346_2D_ORIENT(orient);
+			/* Report orientation only when it changes */
+			if (ac->orient2d_saved != orient_code) {
+				ac->orient2d_saved = orient_code;
+				adxl34x_report_key_single(ac->input,
+					pdata->ev_codes_orient_2d[orient_code]);
+			}
+		}
+
+		if ((pdata->orientation_enable & ADXL_EN_ORIENTATION_3D) &&
+		    (orient & ADXL346_3D_VALID)) {
+
+			orient_code = ADXL346_3D_ORIENT(orient) - 1;
+			/* Report orientation only when it changes */
+			if (ac->orient3d_saved != orient_code) {
+				ac->orient3d_saved = orient_code;
+				adxl34x_report_key_single(ac->input,
+					pdata->ev_codes_orient_3d[orient_code]);
+			}
+		}
+	}
+
+	if (int_stat & (DATA_READY | WATERMARK)) {
+
+		if (pdata->fifo_mode)
+			samples = ENTRIES(AC_READ(ac, FIFO_STATUS)) + 1;
+		else
+			samples = 1;
+
+		for (; samples > 0; samples--) {
+			adxl34x_service_ev_fifo(ac);
+			/*
+			 * To ensure that the FIFO has
+			 * completely popped, there must be at least 5 us between
+			 * the end of reading the data registers, signified by the
+			 * transition to register 0x38 from 0x37 or the CS pin
+			 * going high, and the start of new reads of the FIFO or
+			 * reading the FIFO_STATUS register. For SPI operation at
+			 * 1.5 MHz or lower, the register addressing portion of the
+			 * transmission is sufficient delay to ensure the FIFO has
+			 * completely popped. It is necessary for SPI operation
+			 * greater than 1.5 MHz to de-assert the CS pin to ensure a
+			 * total of 5 us, which is at most 3.4 us at 5 MHz
+			 * operation.
+			 */
+			if (ac->fifo_delay && (samples > 1))
+				udelay(3);
+		}
+	}
+
+	input_sync(ac->input);
+
+	return IRQ_HANDLED;
+}
+
+static void __adxl34x_disable(struct adxl34x *ac)
+{
+	/*
+	 * A '0' places the ADXL34x into standby mode
+	 * with minimum power consumption.
+	 */
+	AC_WRITE(ac, POWER_CTL, 0);
+}
+
+static void __adxl34x_enable(struct adxl34x *ac)
+{
+	AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE);
+}
+
+void adxl34x_suspend(struct adxl34x *ac)
+{
+	mutex_lock(&ac->mutex);
+
+	if (!ac->suspended && !ac->disabled && ac->opened)
+		__adxl34x_disable(ac);
+
+	ac->suspended = true;
+
+	mutex_unlock(&ac->mutex);
+}
+EXPORT_SYMBOL_GPL(adxl34x_suspend);
+
+void adxl34x_resume(struct adxl34x *ac)
+{
+	mutex_lock(&ac->mutex);
+
+	if (ac->suspended && !ac->disabled && ac->opened)
+		__adxl34x_enable(ac);
+
+	ac->suspended = false;
+
+	mutex_unlock(&ac->mutex);
+}
+EXPORT_SYMBOL_GPL(adxl34x_resume);
+
+static ssize_t adxl34x_disable_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct adxl34x *ac = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", ac->disabled);
+}
+
+static ssize_t adxl34x_disable_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct adxl34x *ac = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	mutex_lock(&ac->mutex);
+
+	if (!ac->suspended && ac->opened) {
+		if (val) {
+			if (!ac->disabled)
+				__adxl34x_disable(ac);
+		} else {
+			if (ac->disabled)
+				__adxl34x_enable(ac);
+		}
+	}
+
+	ac->disabled = !!val;
+
+	mutex_unlock(&ac->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(disable, 0664, adxl34x_disable_show, adxl34x_disable_store);
+
+static ssize_t adxl34x_calibrate_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct adxl34x *ac = dev_get_drvdata(dev);
+	ssize_t count;
+
+	mutex_lock(&ac->mutex);
+	count = sprintf(buf, "%d,%d,%d\n",
+			ac->hwcal.x * 4 + ac->swcal.x,
+			ac->hwcal.y * 4 + ac->swcal.y,
+			ac->hwcal.z * 4 + ac->swcal.z);
+	mutex_unlock(&ac->mutex);
+
+	return count;
+}
+
+static ssize_t adxl34x_calibrate_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count)
+{
+	struct adxl34x *ac = dev_get_drvdata(dev);
+
+	/*
+	 * Hardware offset calibration has a resolution of 15.6 mg/LSB.
+	 * We use HW calibration and handle the remaining bits in SW. (4mg/LSB)
+	 */
+
+	mutex_lock(&ac->mutex);
+	ac->hwcal.x -= (ac->saved.x / 4);
+	ac->swcal.x = ac->saved.x % 4;
+
+	ac->hwcal.y -= (ac->saved.y / 4);
+	ac->swcal.y = ac->saved.y % 4;
+
+	ac->hwcal.z -= (ac->saved.z / 4);
+	ac->swcal.z = ac->saved.z % 4;
+
+	AC_WRITE(ac, OFSX, (s8) ac->hwcal.x);
+	AC_WRITE(ac, OFSY, (s8) ac->hwcal.y);
+	AC_WRITE(ac, OFSZ, (s8) ac->hwcal.z);
+	mutex_unlock(&ac->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(calibrate, 0664,
+		   adxl34x_calibrate_show, adxl34x_calibrate_store);
+
+static ssize_t adxl34x_rate_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct adxl34x *ac = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", RATE(ac->pdata.data_rate));
+}
+
+static ssize_t adxl34x_rate_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct adxl34x *ac = dev_get_drvdata(dev);
+	unsigned char val;
+	int error;
+
+	error = kstrtou8(buf, 10, &val);
+	if (error)
+		return error;
+
+	mutex_lock(&ac->mutex);
+
+	ac->pdata.data_rate = RATE(val);
+	AC_WRITE(ac, BW_RATE,
+		 ac->pdata.data_rate |
+			(ac->pdata.low_power_mode ? LOW_POWER : 0));
+
+	mutex_unlock(&ac->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(rate, 0664, adxl34x_rate_show, adxl34x_rate_store);
+
+static ssize_t adxl34x_autosleep_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct adxl34x *ac = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n",
+		ac->pdata.power_mode & (PCTL_AUTO_SLEEP | PCTL_LINK) ? 1 : 0);
+}
+
+static ssize_t adxl34x_autosleep_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct adxl34x *ac = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	mutex_lock(&ac->mutex);
+
+	if (val)
+		ac->pdata.power_mode |= (PCTL_AUTO_SLEEP | PCTL_LINK);
+	else
+		ac->pdata.power_mode &= ~(PCTL_AUTO_SLEEP | PCTL_LINK);
+
+	if (!ac->disabled && !ac->suspended && ac->opened)
+		AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE);
+
+	mutex_unlock(&ac->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(autosleep, 0664,
+		   adxl34x_autosleep_show, adxl34x_autosleep_store);
+
+static ssize_t adxl34x_position_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct adxl34x *ac = dev_get_drvdata(dev);
+	ssize_t count;
+
+	mutex_lock(&ac->mutex);
+	count = sprintf(buf, "(%d, %d, %d)\n",
+			ac->saved.x, ac->saved.y, ac->saved.z);
+	mutex_unlock(&ac->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(position, S_IRUGO, adxl34x_position_show, NULL);
+
+#ifdef ADXL_DEBUG
+static ssize_t adxl34x_write_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	struct adxl34x *ac = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	/*
+	 * This allows basic ADXL register write access for debug purposes.
+	 */
+	error = kstrtouint(buf, 16, &val);
+	if (error)
+		return error;
+
+	mutex_lock(&ac->mutex);
+	AC_WRITE(ac, val >> 8, val & 0xFF);
+	mutex_unlock(&ac->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(write, 0664, NULL, adxl34x_write_store);
+#endif
+
+static struct attribute *adxl34x_attributes[] = {
+	&dev_attr_disable.attr,
+	&dev_attr_calibrate.attr,
+	&dev_attr_rate.attr,
+	&dev_attr_autosleep.attr,
+	&dev_attr_position.attr,
+#ifdef ADXL_DEBUG
+	&dev_attr_write.attr,
+#endif
+	NULL
+};
+
+static const struct attribute_group adxl34x_attr_group = {
+	.attrs = adxl34x_attributes,
+};
+
+static int adxl34x_input_open(struct input_dev *input)
+{
+	struct adxl34x *ac = input_get_drvdata(input);
+
+	mutex_lock(&ac->mutex);
+
+	if (!ac->suspended && !ac->disabled)
+		__adxl34x_enable(ac);
+
+	ac->opened = true;
+
+	mutex_unlock(&ac->mutex);
+
+	return 0;
+}
+
+static void adxl34x_input_close(struct input_dev *input)
+{
+	struct adxl34x *ac = input_get_drvdata(input);
+
+	mutex_lock(&ac->mutex);
+
+	if (!ac->suspended && !ac->disabled)
+		__adxl34x_disable(ac);
+
+	ac->opened = false;
+
+	mutex_unlock(&ac->mutex);
+}
+
+struct adxl34x *adxl34x_probe(struct device *dev, int irq,
+			      bool fifo_delay_default,
+			      const struct adxl34x_bus_ops *bops)
+{
+	struct adxl34x *ac;
+	struct input_dev *input_dev;
+	const struct adxl34x_platform_data *pdata;
+	int err, range, i;
+	unsigned char revid;
+
+	if (!irq) {
+		dev_err(dev, "no IRQ?\n");
+		err = -ENODEV;
+		goto err_out;
+	}
+
+	ac = kzalloc(sizeof(*ac), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!ac || !input_dev) {
+		err = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	ac->fifo_delay = fifo_delay_default;
+
+	pdata = dev_get_platdata(dev);
+	if (!pdata) {
+		dev_dbg(dev,
+			"No platform data: Using default initialization\n");
+		pdata = &adxl34x_default_init;
+	}
+
+	ac->pdata = *pdata;
+	pdata = &ac->pdata;
+
+	ac->input = input_dev;
+	ac->dev = dev;
+	ac->irq = irq;
+	ac->bops = bops;
+
+	mutex_init(&ac->mutex);
+
+	input_dev->name = "ADXL34x accelerometer";
+	revid = AC_READ(ac, DEVID);
+
+	switch (revid) {
+	case ID_ADXL345:
+		ac->model = 345;
+		break;
+	case ID_ADXL346:
+		ac->model = 346;
+		break;
+	default:
+		dev_err(dev, "Failed to probe %s\n", input_dev->name);
+		err = -ENODEV;
+		goto err_free_mem;
+	}
+
+	snprintf(ac->phys, sizeof(ac->phys), "%s/input0", dev_name(dev));
+
+	input_dev->phys = ac->phys;
+	input_dev->dev.parent = dev;
+	input_dev->id.product = ac->model;
+	input_dev->id.bustype = bops->bustype;
+	input_dev->open = adxl34x_input_open;
+	input_dev->close = adxl34x_input_close;
+
+	input_set_drvdata(input_dev, ac);
+
+	__set_bit(ac->pdata.ev_type, input_dev->evbit);
+
+	if (ac->pdata.ev_type == EV_REL) {
+		__set_bit(REL_X, input_dev->relbit);
+		__set_bit(REL_Y, input_dev->relbit);
+		__set_bit(REL_Z, input_dev->relbit);
+	} else {
+		/* EV_ABS */
+		__set_bit(ABS_X, input_dev->absbit);
+		__set_bit(ABS_Y, input_dev->absbit);
+		__set_bit(ABS_Z, input_dev->absbit);
+
+		if (pdata->data_range & FULL_RES)
+			range = ADXL_FULLRES_MAX_VAL;	/* Signed 13-bit */
+		else
+			range = ADXL_FIXEDRES_MAX_VAL;	/* Signed 10-bit */
+
+		input_set_abs_params(input_dev, ABS_X, -range, range, 3, 3);
+		input_set_abs_params(input_dev, ABS_Y, -range, range, 3, 3);
+		input_set_abs_params(input_dev, ABS_Z, -range, range, 3, 3);
+	}
+
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(pdata->ev_code_tap[ADXL_X_AXIS], input_dev->keybit);
+	__set_bit(pdata->ev_code_tap[ADXL_Y_AXIS], input_dev->keybit);
+	__set_bit(pdata->ev_code_tap[ADXL_Z_AXIS], input_dev->keybit);
+
+	if (pdata->ev_code_ff) {
+		ac->int_mask = FREE_FALL;
+		__set_bit(pdata->ev_code_ff, input_dev->keybit);
+	}
+
+	if (pdata->ev_code_act_inactivity)
+		__set_bit(pdata->ev_code_act_inactivity, input_dev->keybit);
+
+	ac->int_mask |= ACTIVITY | INACTIVITY;
+
+	if (pdata->watermark) {
+		ac->int_mask |= WATERMARK;
+		if (!FIFO_MODE(pdata->fifo_mode))
+			ac->pdata.fifo_mode |= FIFO_STREAM;
+	} else {
+		ac->int_mask |= DATA_READY;
+	}
+
+	if (pdata->tap_axis_control & (TAP_X_EN | TAP_Y_EN | TAP_Z_EN))
+		ac->int_mask |= SINGLE_TAP | DOUBLE_TAP;
+
+	if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS)
+		ac->fifo_delay = false;
+
+	AC_WRITE(ac, POWER_CTL, 0);
+
+	err = request_threaded_irq(ac->irq, NULL, adxl34x_irq,
+				   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				   dev_name(dev), ac);
+	if (err) {
+		dev_err(dev, "irq %d busy?\n", ac->irq);
+		goto err_free_mem;
+	}
+
+	err = sysfs_create_group(&dev->kobj, &adxl34x_attr_group);
+	if (err)
+		goto err_free_irq;
+
+	err = input_register_device(input_dev);
+	if (err)
+		goto err_remove_attr;
+
+	AC_WRITE(ac, OFSX, pdata->x_axis_offset);
+	ac->hwcal.x = pdata->x_axis_offset;
+	AC_WRITE(ac, OFSY, pdata->y_axis_offset);
+	ac->hwcal.y = pdata->y_axis_offset;
+	AC_WRITE(ac, OFSZ, pdata->z_axis_offset);
+	ac->hwcal.z = pdata->z_axis_offset;
+	AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold);
+	AC_WRITE(ac, DUR, pdata->tap_duration);
+	AC_WRITE(ac, LATENT, pdata->tap_latency);
+	AC_WRITE(ac, WINDOW, pdata->tap_window);
+	AC_WRITE(ac, THRESH_ACT, pdata->activity_threshold);
+	AC_WRITE(ac, THRESH_INACT, pdata->inactivity_threshold);
+	AC_WRITE(ac, TIME_INACT, pdata->inactivity_time);
+	AC_WRITE(ac, THRESH_FF, pdata->free_fall_threshold);
+	AC_WRITE(ac, TIME_FF, pdata->free_fall_time);
+	AC_WRITE(ac, TAP_AXES, pdata->tap_axis_control);
+	AC_WRITE(ac, ACT_INACT_CTL, pdata->act_axis_control);
+	AC_WRITE(ac, BW_RATE, RATE(ac->pdata.data_rate) |
+		 (pdata->low_power_mode ? LOW_POWER : 0));
+	AC_WRITE(ac, DATA_FORMAT, pdata->data_range);
+	AC_WRITE(ac, FIFO_CTL, FIFO_MODE(pdata->fifo_mode) |
+			SAMPLES(pdata->watermark));
+
+	if (pdata->use_int2) {
+		/* Map all INTs to INT2 */
+		AC_WRITE(ac, INT_MAP, ac->int_mask | OVERRUN);
+	} else {
+		/* Map all INTs to INT1 */
+		AC_WRITE(ac, INT_MAP, 0);
+	}
+
+	if (ac->model == 346 && ac->pdata.orientation_enable) {
+		AC_WRITE(ac, ORIENT_CONF,
+			ORIENT_DEADZONE(ac->pdata.deadzone_angle) |
+			ORIENT_DIVISOR(ac->pdata.divisor_length));
+
+		ac->orient2d_saved = 1234;
+		ac->orient3d_saved = 1234;
+
+		if (pdata->orientation_enable & ADXL_EN_ORIENTATION_3D)
+			for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_3d); i++)
+				__set_bit(pdata->ev_codes_orient_3d[i],
+					  input_dev->keybit);
+
+		if (pdata->orientation_enable & ADXL_EN_ORIENTATION_2D)
+			for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_2d); i++)
+				__set_bit(pdata->ev_codes_orient_2d[i],
+					  input_dev->keybit);
+	} else {
+		ac->pdata.orientation_enable = 0;
+	}
+
+	AC_WRITE(ac, INT_ENABLE, ac->int_mask | OVERRUN);
+
+	ac->pdata.power_mode &= (PCTL_AUTO_SLEEP | PCTL_LINK);
+
+	return ac;
+
+ err_remove_attr:
+	sysfs_remove_group(&dev->kobj, &adxl34x_attr_group);
+ err_free_irq:
+	free_irq(ac->irq, ac);
+ err_free_mem:
+	input_free_device(input_dev);
+	kfree(ac);
+ err_out:
+	return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(adxl34x_probe);
+
+int adxl34x_remove(struct adxl34x *ac)
+{
+	sysfs_remove_group(&ac->dev->kobj, &adxl34x_attr_group);
+	free_irq(ac->irq, ac);
+	input_unregister_device(ac->input);
+	dev_dbg(ac->dev, "unregistered accelerometer\n");
+	kfree(ac);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(adxl34x_remove);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/adxl34x.h b/drivers/input/misc/adxl34x.h
new file mode 100644
index 0000000..bbbc80f
--- /dev/null
+++ b/drivers/input/misc/adxl34x.h
@@ -0,0 +1,30 @@
+/*
+ * ADXL345/346 Three-Axis Digital Accelerometers (I2C/SPI Interface)
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _ADXL34X_H_
+#define _ADXL34X_H_
+
+struct device;
+struct adxl34x;
+
+struct adxl34x_bus_ops {
+	u16 bustype;
+	int (*read)(struct device *, unsigned char);
+	int (*read_block)(struct device *, unsigned char, int, void *);
+	int (*write)(struct device *, unsigned char, unsigned char);
+};
+
+void adxl34x_suspend(struct adxl34x *ac);
+void adxl34x_resume(struct adxl34x *ac);
+struct adxl34x *adxl34x_probe(struct device *dev, int irq,
+			      bool fifo_delay_default,
+			      const struct adxl34x_bus_ops *bops);
+int adxl34x_remove(struct adxl34x *ac);
+
+#endif
diff --git a/drivers/input/misc/apanel.c b/drivers/input/misc/apanel.c
new file mode 100644
index 0000000..a8d2b8d
--- /dev/null
+++ b/drivers/input/misc/apanel.c
@@ -0,0 +1,350 @@
+/*
+ *  Fujitsu Lifebook Application Panel button drive
+ *
+ *  Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org>
+ *  Copyright (C) 2001-2003 Jochen Eisinger <jochen@penguin-breeder.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Many Fujitsu Lifebook laptops have a small panel of buttons that are
+ * accessible via the i2c/smbus interface. This driver polls those
+ * buttons and generates input events.
+ *
+ * For more details see:
+ *	http://apanel.sourceforge.net/tech.php
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/input-polldev.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+
+#define APANEL_NAME	"Fujitsu Application Panel"
+#define APANEL_VERSION	"1.3.1"
+#define APANEL		"apanel"
+
+/* How often we poll keys - msecs */
+#define POLL_INTERVAL_DEFAULT	1000
+
+/* Magic constants in BIOS that tell about buttons */
+enum apanel_devid {
+	APANEL_DEV_NONE	  = 0,
+	APANEL_DEV_APPBTN = 1,
+	APANEL_DEV_CDBTN  = 2,
+	APANEL_DEV_LCD	  = 3,
+	APANEL_DEV_LED	  = 4,
+
+	APANEL_DEV_MAX,
+};
+
+enum apanel_chip {
+	CHIP_NONE    = 0,
+	CHIP_OZ992C  = 1,
+	CHIP_OZ163T  = 2,
+	CHIP_OZ711M3 = 4,
+};
+
+/* Result of BIOS snooping/probing -- what features are supported */
+static enum apanel_chip device_chip[APANEL_DEV_MAX];
+
+#define MAX_PANEL_KEYS	12
+
+struct apanel {
+	struct input_polled_dev *ipdev;
+	struct i2c_client *client;
+	unsigned short keymap[MAX_PANEL_KEYS];
+	u16    nkeys;
+	u16    led_bits;
+	struct work_struct led_work;
+	struct led_classdev mail_led;
+};
+
+
+static int apanel_probe(struct i2c_client *, const struct i2c_device_id *);
+
+static void report_key(struct input_dev *input, unsigned keycode)
+{
+	pr_debug(APANEL ": report key %#x\n", keycode);
+	input_report_key(input, keycode, 1);
+	input_sync(input);
+
+	input_report_key(input, keycode, 0);
+	input_sync(input);
+}
+
+/* Poll for key changes
+ *
+ * Read Application keys via SMI
+ *  A (0x4), B (0x8), Internet (0x2), Email (0x1).
+ *
+ * CD keys:
+ * Forward (0x100), Rewind (0x200), Stop (0x400), Pause (0x800)
+ */
+static void apanel_poll(struct input_polled_dev *ipdev)
+{
+	struct apanel *ap = ipdev->private;
+	struct input_dev *idev = ipdev->input;
+	u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+	s32 data;
+	int i;
+
+	data = i2c_smbus_read_word_data(ap->client, cmd);
+	if (data < 0)
+		return;	/* ignore errors (due to ACPI??) */
+
+	/* write back to clear latch */
+	i2c_smbus_write_word_data(ap->client, cmd, 0);
+
+	if (!data)
+		return;
+
+	dev_dbg(&idev->dev, APANEL ": data %#x\n", data);
+	for (i = 0; i < idev->keycodemax; i++)
+		if ((1u << i) & data)
+			report_key(idev, ap->keymap[i]);
+}
+
+/* Track state changes of LED */
+static void led_update(struct work_struct *work)
+{
+	struct apanel *ap = container_of(work, struct apanel, led_work);
+
+	i2c_smbus_write_word_data(ap->client, 0x10, ap->led_bits);
+}
+
+static void mail_led_set(struct led_classdev *led,
+			 enum led_brightness value)
+{
+	struct apanel *ap = container_of(led, struct apanel, mail_led);
+
+	if (value != LED_OFF)
+		ap->led_bits |= 0x8000;
+	else
+		ap->led_bits &= ~0x8000;
+
+	schedule_work(&ap->led_work);
+}
+
+static int apanel_remove(struct i2c_client *client)
+{
+	struct apanel *ap = i2c_get_clientdata(client);
+
+	if (device_chip[APANEL_DEV_LED] != CHIP_NONE)
+		led_classdev_unregister(&ap->mail_led);
+
+	input_unregister_polled_device(ap->ipdev);
+	input_free_polled_device(ap->ipdev);
+
+	return 0;
+}
+
+static void apanel_shutdown(struct i2c_client *client)
+{
+	apanel_remove(client);
+}
+
+static const struct i2c_device_id apanel_id[] = {
+	{ "fujitsu_apanel", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, apanel_id);
+
+static struct i2c_driver apanel_driver = {
+	.driver = {
+		.name = APANEL,
+	},
+	.probe		= &apanel_probe,
+	.remove		= &apanel_remove,
+	.shutdown	= &apanel_shutdown,
+	.id_table	= apanel_id,
+};
+
+static struct apanel apanel = {
+	.keymap = {
+		[0] = KEY_MAIL,
+		[1] = KEY_WWW,
+		[2] = KEY_PROG2,
+		[3] = KEY_PROG1,
+
+		[8] = KEY_FORWARD,
+		[9] = KEY_REWIND,
+		[10] = KEY_STOPCD,
+		[11] = KEY_PLAYPAUSE,
+
+	},
+	.mail_led = {
+		.name = "mail:blue",
+		.brightness_set = mail_led_set,
+	},
+};
+
+/* NB: Only one panel on the i2c. */
+static int apanel_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct apanel *ap;
+	struct input_polled_dev *ipdev;
+	struct input_dev *idev;
+	u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+	int i, err = -ENOMEM;
+
+	ap = &apanel;
+
+	ipdev = input_allocate_polled_device();
+	if (!ipdev)
+		goto out1;
+
+	ap->ipdev = ipdev;
+	ap->client = client;
+
+	i2c_set_clientdata(client, ap);
+
+	err = i2c_smbus_write_word_data(client, cmd, 0);
+	if (err) {
+		dev_warn(&client->dev, APANEL ": smbus write error %d\n",
+			 err);
+		goto out3;
+	}
+
+	ipdev->poll = apanel_poll;
+	ipdev->poll_interval = POLL_INTERVAL_DEFAULT;
+	ipdev->private = ap;
+
+	idev = ipdev->input;
+	idev->name = APANEL_NAME " buttons";
+	idev->phys = "apanel/input0";
+	idev->id.bustype = BUS_HOST;
+	idev->dev.parent = &client->dev;
+
+	set_bit(EV_KEY, idev->evbit);
+
+	idev->keycode = ap->keymap;
+	idev->keycodesize = sizeof(ap->keymap[0]);
+	idev->keycodemax = (device_chip[APANEL_DEV_CDBTN] != CHIP_NONE) ? 12 : 4;
+
+	for (i = 0; i < idev->keycodemax; i++)
+		if (ap->keymap[i])
+			set_bit(ap->keymap[i], idev->keybit);
+
+	err = input_register_polled_device(ipdev);
+	if (err)
+		goto out3;
+
+	INIT_WORK(&ap->led_work, led_update);
+	if (device_chip[APANEL_DEV_LED] != CHIP_NONE) {
+		err = led_classdev_register(&client->dev, &ap->mail_led);
+		if (err)
+			goto out4;
+	}
+
+	return 0;
+out4:
+	input_unregister_polled_device(ipdev);
+out3:
+	input_free_polled_device(ipdev);
+out1:
+	return err;
+}
+
+/* Scan the system ROM for the signature "FJKEYINF" */
+static __init const void __iomem *bios_signature(const void __iomem *bios)
+{
+	ssize_t offset;
+	const unsigned char signature[] = "FJKEYINF";
+
+	for (offset = 0; offset < 0x10000; offset += 0x10) {
+		if (check_signature(bios + offset, signature,
+				    sizeof(signature)-1))
+			return bios + offset;
+	}
+	pr_notice(APANEL ": Fujitsu BIOS signature '%s' not found...\n",
+		  signature);
+	return NULL;
+}
+
+static int __init apanel_init(void)
+{
+	void __iomem *bios;
+	const void __iomem *p;
+	u8 devno;
+	unsigned char i2c_addr;
+	int found = 0;
+
+	bios = ioremap(0xF0000, 0x10000); /* Can't fail */
+
+	p = bios_signature(bios);
+	if (!p) {
+		iounmap(bios);
+		return -ENODEV;
+	}
+
+	/* just use the first address */
+	p += 8;
+	i2c_addr = readb(p + 3) >> 1;
+
+	for ( ; (devno = readb(p)) & 0x7f; p += 4) {
+		unsigned char method, slave, chip;
+
+		method = readb(p + 1);
+		chip = readb(p + 2);
+		slave = readb(p + 3) >> 1;
+
+		if (slave != i2c_addr) {
+			pr_notice(APANEL ": only one SMBus slave "
+				  "address supported, skiping device...\n");
+			continue;
+		}
+
+		/* translate alternative device numbers */
+		switch (devno) {
+		case 6:
+			devno = APANEL_DEV_APPBTN;
+			break;
+		case 7:
+			devno = APANEL_DEV_LED;
+			break;
+		}
+
+		if (devno >= APANEL_DEV_MAX)
+			pr_notice(APANEL ": unknown device %u found\n", devno);
+		else if (device_chip[devno] != CHIP_NONE)
+			pr_warning(APANEL ": duplicate entry for devno %u\n", devno);
+
+		else if (method != 1 && method != 2 && method != 4) {
+			pr_notice(APANEL ": unknown method %u for devno %u\n",
+				  method, devno);
+		} else {
+			device_chip[devno] = (enum apanel_chip) chip;
+			++found;
+		}
+	}
+	iounmap(bios);
+
+	if (found == 0) {
+		pr_info(APANEL ": no input devices reported by BIOS\n");
+		return -EIO;
+	}
+
+	return i2c_add_driver(&apanel_driver);
+}
+module_init(apanel_init);
+
+static void __exit apanel_cleanup(void)
+{
+	i2c_del_driver(&apanel_driver);
+}
+module_exit(apanel_cleanup);
+
+MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>");
+MODULE_DESCRIPTION(APANEL_NAME " driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(APANEL_VERSION);
+
+MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
+MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");
diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c
new file mode 100644
index 0000000..d5994a7
--- /dev/null
+++ b/drivers/input/misc/arizona-haptics.c
@@ -0,0 +1,216 @@
+/*
+ * Arizona haptics driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/pdata.h>
+#include <linux/mfd/arizona/registers.h>
+
+struct arizona_haptics {
+	struct arizona *arizona;
+	struct input_dev *input_dev;
+	struct work_struct work;
+
+	struct mutex mutex;
+	u8 intensity;
+};
+
+static void arizona_haptics_work(struct work_struct *work)
+{
+	struct arizona_haptics *haptics = container_of(work,
+						       struct arizona_haptics,
+						       work);
+	struct arizona *arizona = haptics->arizona;
+	int ret;
+
+	if (!haptics->arizona->dapm) {
+		dev_err(arizona->dev, "No DAPM context\n");
+		return;
+	}
+
+	if (haptics->intensity) {
+		ret = regmap_update_bits(arizona->regmap,
+					 ARIZONA_HAPTICS_PHASE_2_INTENSITY,
+					 ARIZONA_PHASE2_INTENSITY_MASK,
+					 haptics->intensity);
+		if (ret != 0) {
+			dev_err(arizona->dev, "Failed to set intensity: %d\n",
+				ret);
+			return;
+		}
+
+		/* This enable sequence will be a noop if already enabled */
+		ret = regmap_update_bits(arizona->regmap,
+					 ARIZONA_HAPTICS_CONTROL_1,
+					 ARIZONA_HAP_CTRL_MASK,
+					 1 << ARIZONA_HAP_CTRL_SHIFT);
+		if (ret != 0) {
+			dev_err(arizona->dev, "Failed to start haptics: %d\n",
+				ret);
+			return;
+		}
+
+		ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS");
+		if (ret != 0) {
+			dev_err(arizona->dev, "Failed to start HAPTICS: %d\n",
+				ret);
+			return;
+		}
+
+		ret = snd_soc_dapm_sync(arizona->dapm);
+		if (ret != 0) {
+			dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
+				ret);
+			return;
+		}
+	} else {
+		/* This disable sequence will be a noop if already enabled */
+		ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS");
+		if (ret != 0) {
+			dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n",
+				ret);
+			return;
+		}
+
+		ret = snd_soc_dapm_sync(arizona->dapm);
+		if (ret != 0) {
+			dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
+				ret);
+			return;
+		}
+
+		ret = regmap_update_bits(arizona->regmap,
+					 ARIZONA_HAPTICS_CONTROL_1,
+					 ARIZONA_HAP_CTRL_MASK, 0);
+		if (ret != 0) {
+			dev_err(arizona->dev, "Failed to stop haptics: %d\n",
+				ret);
+			return;
+		}
+	}
+}
+
+static int arizona_haptics_play(struct input_dev *input, void *data,
+				struct ff_effect *effect)
+{
+	struct arizona_haptics *haptics = input_get_drvdata(input);
+	struct arizona *arizona = haptics->arizona;
+
+	if (!arizona->dapm) {
+		dev_err(arizona->dev, "No DAPM context\n");
+		return -EBUSY;
+	}
+
+	if (effect->u.rumble.strong_magnitude) {
+		/* Scale the magnitude into the range the device supports */
+		if (arizona->pdata.hap_act) {
+			haptics->intensity =
+				effect->u.rumble.strong_magnitude >> 9;
+			if (effect->direction < 0x8000)
+				haptics->intensity += 0x7f;
+		} else {
+			haptics->intensity =
+				effect->u.rumble.strong_magnitude >> 8;
+		}
+	} else {
+		haptics->intensity = 0;
+	}
+
+	schedule_work(&haptics->work);
+
+	return 0;
+}
+
+static void arizona_haptics_close(struct input_dev *input)
+{
+	struct arizona_haptics *haptics = input_get_drvdata(input);
+
+	cancel_work_sync(&haptics->work);
+
+	if (haptics->arizona->dapm)
+		snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS");
+}
+
+static int arizona_haptics_probe(struct platform_device *pdev)
+{
+	struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+	struct arizona_haptics *haptics;
+	int ret;
+
+	haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL);
+	if (!haptics)
+		return -ENOMEM;
+
+	haptics->arizona = arizona;
+
+	ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1,
+				 ARIZONA_HAP_ACT, arizona->pdata.hap_act);
+	if (ret != 0) {
+		dev_err(arizona->dev, "Failed to set haptics actuator: %d\n",
+			ret);
+		return ret;
+	}
+
+	INIT_WORK(&haptics->work, arizona_haptics_work);
+
+	haptics->input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!haptics->input_dev) {
+		dev_err(arizona->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	input_set_drvdata(haptics->input_dev, haptics);
+
+	haptics->input_dev->name = "arizona:haptics";
+	haptics->input_dev->dev.parent = pdev->dev.parent;
+	haptics->input_dev->close = arizona_haptics_close;
+	__set_bit(FF_RUMBLE, haptics->input_dev->ffbit);
+
+	ret = input_ff_create_memless(haptics->input_dev, NULL,
+				      arizona_haptics_play);
+	if (ret < 0) {
+		dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = input_register_device(haptics->input_dev);
+	if (ret < 0) {
+		dev_err(arizona->dev, "couldn't register input device: %d\n",
+			ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, haptics);
+
+	return 0;
+}
+
+static struct platform_driver arizona_haptics_driver = {
+	.probe		= arizona_haptics_probe,
+	.driver		= {
+		.name	= "arizona-haptics",
+	},
+};
+module_platform_driver(arizona_haptics_driver);
+
+MODULE_ALIAS("platform:arizona-haptics");
+MODULE_DESCRIPTION("Arizona haptics driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c
new file mode 100644
index 0000000..1c5914c
--- /dev/null
+++ b/drivers/input/misc/ati_remote2.c
@@ -0,0 +1,1040 @@
+/*
+ * ati_remote2 - ATI/Philips USB RF remote driver
+ *
+ * Copyright (C) 2005-2008 Ville Syrjala <syrjala@sci.fi>
+ * Copyright (C) 2007-2008 Peter Stokes <linux@dadeos.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/usb/input.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define DRIVER_DESC    "ATI/Philips USB RF remote driver"
+#define DRIVER_VERSION "0.3"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>");
+MODULE_LICENSE("GPL");
+
+/*
+ * ATI Remote Wonder II Channel Configuration
+ *
+ * The remote control can by assigned one of sixteen "channels" in order to facilitate
+ * the use of multiple remote controls within range of each other.
+ * A remote's "channel" may be altered by pressing and holding the "PC" button for
+ * approximately 3 seconds, after which the button will slowly flash the count of the
+ * currently configured "channel", using the numeric keypad enter a number between 1 and
+ * 16 and then press the "PC" button again, the button will slowly flash the count of the
+ * newly configured "channel".
+ */
+
+enum {
+	ATI_REMOTE2_MAX_CHANNEL_MASK = 0xFFFF,
+	ATI_REMOTE2_MAX_MODE_MASK = 0x1F,
+};
+
+static int ati_remote2_set_mask(const char *val,
+				const struct kernel_param *kp,
+				unsigned int max)
+{
+	unsigned int mask;
+	int ret;
+
+	if (!val)
+		return -EINVAL;
+
+	ret = kstrtouint(val, 0, &mask);
+	if (ret)
+		return ret;
+
+	if (mask & ~max)
+		return -EINVAL;
+
+	*(unsigned int *)kp->arg = mask;
+
+	return 0;
+}
+
+static int ati_remote2_set_channel_mask(const char *val,
+					const struct kernel_param *kp)
+{
+	pr_debug("%s()\n", __func__);
+
+	return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_CHANNEL_MASK);
+}
+
+static int ati_remote2_get_channel_mask(char *buffer,
+					const struct kernel_param *kp)
+{
+	pr_debug("%s()\n", __func__);
+
+	return sprintf(buffer, "0x%04x", *(unsigned int *)kp->arg);
+}
+
+static int ati_remote2_set_mode_mask(const char *val,
+				     const struct kernel_param *kp)
+{
+	pr_debug("%s()\n", __func__);
+
+	return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_MODE_MASK);
+}
+
+static int ati_remote2_get_mode_mask(char *buffer,
+				     const struct kernel_param *kp)
+{
+	pr_debug("%s()\n", __func__);
+
+	return sprintf(buffer, "0x%02x", *(unsigned int *)kp->arg);
+}
+
+static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK;
+#define param_check_channel_mask(name, p) __param_check(name, p, unsigned int)
+static const struct kernel_param_ops param_ops_channel_mask = {
+	.set = ati_remote2_set_channel_mask,
+	.get = ati_remote2_get_channel_mask,
+};
+module_param(channel_mask, channel_mask, 0644);
+MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>");
+
+static unsigned int mode_mask = ATI_REMOTE2_MAX_MODE_MASK;
+#define param_check_mode_mask(name, p) __param_check(name, p, unsigned int)
+static const struct kernel_param_ops param_ops_mode_mask = {
+	.set = ati_remote2_set_mode_mask,
+	.get = ati_remote2_get_mode_mask,
+};
+module_param(mode_mask, mode_mask, 0644);
+MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
+
+static struct usb_device_id ati_remote2_id_table[] = {
+	{ USB_DEVICE(0x0471, 0x0602) },	/* ATI Remote Wonder II */
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, ati_remote2_id_table);
+
+static DEFINE_MUTEX(ati_remote2_mutex);
+
+enum {
+	ATI_REMOTE2_OPENED = 0x1,
+	ATI_REMOTE2_SUSPENDED = 0x2,
+};
+
+enum {
+	ATI_REMOTE2_AUX1,
+	ATI_REMOTE2_AUX2,
+	ATI_REMOTE2_AUX3,
+	ATI_REMOTE2_AUX4,
+	ATI_REMOTE2_PC,
+	ATI_REMOTE2_MODES,
+};
+
+static const struct {
+	u8  hw_code;
+	u16 keycode;
+} ati_remote2_key_table[] = {
+	{ 0x00, KEY_0 },
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+	{ 0x0c, KEY_POWER },
+	{ 0x0d, KEY_MUTE },
+	{ 0x10, KEY_VOLUMEUP },
+	{ 0x11, KEY_VOLUMEDOWN },
+	{ 0x20, KEY_CHANNELUP },
+	{ 0x21, KEY_CHANNELDOWN },
+	{ 0x28, KEY_FORWARD },
+	{ 0x29, KEY_REWIND },
+	{ 0x2c, KEY_PLAY },
+	{ 0x30, KEY_PAUSE },
+	{ 0x31, KEY_STOP },
+	{ 0x37, KEY_RECORD },
+	{ 0x38, KEY_DVD },
+	{ 0x39, KEY_TV },
+	{ 0x3f, KEY_PROG1 }, /* AUX1-AUX4 and PC */
+	{ 0x54, KEY_MENU },
+	{ 0x58, KEY_UP },
+	{ 0x59, KEY_DOWN },
+	{ 0x5a, KEY_LEFT },
+	{ 0x5b, KEY_RIGHT },
+	{ 0x5c, KEY_OK },
+	{ 0x78, KEY_A },
+	{ 0x79, KEY_B },
+	{ 0x7a, KEY_C },
+	{ 0x7b, KEY_D },
+	{ 0x7c, KEY_E },
+	{ 0x7d, KEY_F },
+	{ 0x82, KEY_ENTER },
+	{ 0x8e, KEY_VENDOR },
+	{ 0x96, KEY_COFFEE },
+	{ 0xa9, BTN_LEFT },
+	{ 0xaa, BTN_RIGHT },
+	{ 0xbe, KEY_QUESTION },
+	{ 0xd0, KEY_EDIT },
+	{ 0xd5, KEY_FRONT },
+	{ 0xf9, KEY_INFO },
+};
+
+struct ati_remote2 {
+	struct input_dev *idev;
+	struct usb_device *udev;
+
+	struct usb_interface *intf[2];
+	struct usb_endpoint_descriptor *ep[2];
+	struct urb *urb[2];
+	void *buf[2];
+	dma_addr_t buf_dma[2];
+
+	unsigned long jiffies;
+	int mode;
+
+	char name[64];
+	char phys[64];
+
+	/* Each mode (AUX1-AUX4 and PC) can have an independent keymap. */
+	u16 keycode[ATI_REMOTE2_MODES][ARRAY_SIZE(ati_remote2_key_table)];
+
+	unsigned int flags;
+
+	unsigned int channel_mask;
+	unsigned int mode_mask;
+};
+
+static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id);
+static void ati_remote2_disconnect(struct usb_interface *interface);
+static int ati_remote2_suspend(struct usb_interface *interface, pm_message_t message);
+static int ati_remote2_resume(struct usb_interface *interface);
+static int ati_remote2_reset_resume(struct usb_interface *interface);
+static int ati_remote2_pre_reset(struct usb_interface *interface);
+static int ati_remote2_post_reset(struct usb_interface *interface);
+
+static struct usb_driver ati_remote2_driver = {
+	.name       = "ati_remote2",
+	.probe      = ati_remote2_probe,
+	.disconnect = ati_remote2_disconnect,
+	.id_table   = ati_remote2_id_table,
+	.suspend    = ati_remote2_suspend,
+	.resume     = ati_remote2_resume,
+	.reset_resume = ati_remote2_reset_resume,
+	.pre_reset  = ati_remote2_pre_reset,
+	.post_reset = ati_remote2_post_reset,
+	.supports_autosuspend = 1,
+};
+
+static int ati_remote2_submit_urbs(struct ati_remote2 *ar2)
+{
+	int r;
+
+	r = usb_submit_urb(ar2->urb[0], GFP_KERNEL);
+	if (r) {
+		dev_err(&ar2->intf[0]->dev,
+			"%s(): usb_submit_urb() = %d\n", __func__, r);
+		return r;
+	}
+	r = usb_submit_urb(ar2->urb[1], GFP_KERNEL);
+	if (r) {
+		usb_kill_urb(ar2->urb[0]);
+		dev_err(&ar2->intf[1]->dev,
+			"%s(): usb_submit_urb() = %d\n", __func__, r);
+		return r;
+	}
+
+	return 0;
+}
+
+static void ati_remote2_kill_urbs(struct ati_remote2 *ar2)
+{
+	usb_kill_urb(ar2->urb[1]);
+	usb_kill_urb(ar2->urb[0]);
+}
+
+static int ati_remote2_open(struct input_dev *idev)
+{
+	struct ati_remote2 *ar2 = input_get_drvdata(idev);
+	int r;
+
+	dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+	r = usb_autopm_get_interface(ar2->intf[0]);
+	if (r) {
+		dev_err(&ar2->intf[0]->dev,
+			"%s(): usb_autopm_get_interface() = %d\n", __func__, r);
+		goto fail1;
+	}
+
+	mutex_lock(&ati_remote2_mutex);
+
+	if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) {
+		r = ati_remote2_submit_urbs(ar2);
+		if (r)
+			goto fail2;
+	}
+
+	ar2->flags |= ATI_REMOTE2_OPENED;
+
+	mutex_unlock(&ati_remote2_mutex);
+
+	usb_autopm_put_interface(ar2->intf[0]);
+
+	return 0;
+
+ fail2:
+	mutex_unlock(&ati_remote2_mutex);
+	usb_autopm_put_interface(ar2->intf[0]);
+ fail1:
+	return r;
+}
+
+static void ati_remote2_close(struct input_dev *idev)
+{
+	struct ati_remote2 *ar2 = input_get_drvdata(idev);
+
+	dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+	mutex_lock(&ati_remote2_mutex);
+
+	if (!(ar2->flags & ATI_REMOTE2_SUSPENDED))
+		ati_remote2_kill_urbs(ar2);
+
+	ar2->flags &= ~ATI_REMOTE2_OPENED;
+
+	mutex_unlock(&ati_remote2_mutex);
+}
+
+static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
+{
+	struct input_dev *idev = ar2->idev;
+	u8 *data = ar2->buf[0];
+	int channel, mode;
+
+	channel = data[0] >> 4;
+
+	if (!((1 << channel) & ar2->channel_mask))
+		return;
+
+	mode = data[0] & 0x0F;
+
+	if (mode > ATI_REMOTE2_PC) {
+		dev_err(&ar2->intf[0]->dev,
+			"Unknown mode byte (%02x %02x %02x %02x)\n",
+			data[3], data[2], data[1], data[0]);
+		return;
+	}
+
+	if (!((1 << mode) & ar2->mode_mask))
+		return;
+
+	input_event(idev, EV_REL, REL_X, (s8) data[1]);
+	input_event(idev, EV_REL, REL_Y, (s8) data[2]);
+	input_sync(idev);
+}
+
+static int ati_remote2_lookup(unsigned int hw_code)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ati_remote2_key_table); i++)
+		if (ati_remote2_key_table[i].hw_code == hw_code)
+			return i;
+
+	return -1;
+}
+
+static void ati_remote2_input_key(struct ati_remote2 *ar2)
+{
+	struct input_dev *idev = ar2->idev;
+	u8 *data = ar2->buf[1];
+	int channel, mode, hw_code, index;
+
+	channel = data[0] >> 4;
+
+	if (!((1 << channel) & ar2->channel_mask))
+		return;
+
+	mode = data[0] & 0x0F;
+
+	if (mode > ATI_REMOTE2_PC) {
+		dev_err(&ar2->intf[1]->dev,
+			"Unknown mode byte (%02x %02x %02x %02x)\n",
+			data[3], data[2], data[1], data[0]);
+		return;
+	}
+
+	hw_code = data[2];
+	if (hw_code == 0x3f) {
+		/*
+		 * For some incomprehensible reason the mouse pad generates
+		 * events which look identical to the events from the last
+		 * pressed mode key. Naturally we don't want to generate key
+		 * events for the mouse pad so we filter out any subsequent
+		 * events from the same mode key.
+		 */
+		if (ar2->mode == mode)
+			return;
+
+		if (data[1] == 0)
+			ar2->mode = mode;
+	}
+
+	if (!((1 << mode) & ar2->mode_mask))
+		return;
+
+	index = ati_remote2_lookup(hw_code);
+	if (index < 0) {
+		dev_err(&ar2->intf[1]->dev,
+			"Unknown code byte (%02x %02x %02x %02x)\n",
+			data[3], data[2], data[1], data[0]);
+		return;
+	}
+
+	switch (data[1]) {
+	case 0:	/* release */
+		break;
+	case 1:	/* press */
+		ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]);
+		break;
+	case 2:	/* repeat */
+
+		/* No repeat for mouse buttons. */
+		if (ar2->keycode[mode][index] == BTN_LEFT ||
+		    ar2->keycode[mode][index] == BTN_RIGHT)
+			return;
+
+		if (!time_after_eq(jiffies, ar2->jiffies))
+			return;
+
+		ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]);
+		break;
+	default:
+		dev_err(&ar2->intf[1]->dev,
+			"Unknown state byte (%02x %02x %02x %02x)\n",
+			data[3], data[2], data[1], data[0]);
+		return;
+	}
+
+	input_event(idev, EV_KEY, ar2->keycode[mode][index], data[1]);
+	input_sync(idev);
+}
+
+static void ati_remote2_complete_mouse(struct urb *urb)
+{
+	struct ati_remote2 *ar2 = urb->context;
+	int r;
+
+	switch (urb->status) {
+	case 0:
+		usb_mark_last_busy(ar2->udev);
+		ati_remote2_input_mouse(ar2);
+		break;
+	case -ENOENT:
+	case -EILSEQ:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		dev_dbg(&ar2->intf[0]->dev,
+			"%s(): urb status = %d\n", __func__, urb->status);
+		return;
+	default:
+		usb_mark_last_busy(ar2->udev);
+		dev_err(&ar2->intf[0]->dev,
+			"%s(): urb status = %d\n", __func__, urb->status);
+	}
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_err(&ar2->intf[0]->dev,
+			"%s(): usb_submit_urb() = %d\n", __func__, r);
+}
+
+static void ati_remote2_complete_key(struct urb *urb)
+{
+	struct ati_remote2 *ar2 = urb->context;
+	int r;
+
+	switch (urb->status) {
+	case 0:
+		usb_mark_last_busy(ar2->udev);
+		ati_remote2_input_key(ar2);
+		break;
+	case -ENOENT:
+	case -EILSEQ:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		dev_dbg(&ar2->intf[1]->dev,
+			"%s(): urb status = %d\n", __func__, urb->status);
+		return;
+	default:
+		usb_mark_last_busy(ar2->udev);
+		dev_err(&ar2->intf[1]->dev,
+			"%s(): urb status = %d\n", __func__, urb->status);
+	}
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		dev_err(&ar2->intf[1]->dev,
+			"%s(): usb_submit_urb() = %d\n", __func__, r);
+}
+
+static int ati_remote2_getkeycode(struct input_dev *idev,
+				  struct input_keymap_entry *ke)
+{
+	struct ati_remote2 *ar2 = input_get_drvdata(idev);
+	unsigned int mode;
+	int offset;
+	unsigned int index;
+	unsigned int scancode;
+
+	if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+		index = ke->index;
+		if (index >= ATI_REMOTE2_MODES *
+				ARRAY_SIZE(ati_remote2_key_table))
+			return -EINVAL;
+
+		mode = ke->index / ARRAY_SIZE(ati_remote2_key_table);
+		offset = ke->index % ARRAY_SIZE(ati_remote2_key_table);
+		scancode = (mode << 8) + ati_remote2_key_table[offset].hw_code;
+	} else {
+		if (input_scancode_to_scalar(ke, &scancode))
+			return -EINVAL;
+
+		mode = scancode >> 8;
+		if (mode > ATI_REMOTE2_PC)
+			return -EINVAL;
+
+		offset = ati_remote2_lookup(scancode & 0xff);
+		if (offset < 0)
+			return -EINVAL;
+
+		index = mode * ARRAY_SIZE(ati_remote2_key_table) + offset;
+	}
+
+	ke->keycode = ar2->keycode[mode][offset];
+	ke->len = sizeof(scancode);
+	memcpy(&ke->scancode, &scancode, sizeof(scancode));
+	ke->index = index;
+
+	return 0;
+}
+
+static int ati_remote2_setkeycode(struct input_dev *idev,
+				  const struct input_keymap_entry *ke,
+				  unsigned int *old_keycode)
+{
+	struct ati_remote2 *ar2 = input_get_drvdata(idev);
+	unsigned int mode;
+	int offset;
+	unsigned int index;
+	unsigned int scancode;
+
+	if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+		if (ke->index >= ATI_REMOTE2_MODES *
+				ARRAY_SIZE(ati_remote2_key_table))
+			return -EINVAL;
+
+		mode = ke->index / ARRAY_SIZE(ati_remote2_key_table);
+		offset = ke->index % ARRAY_SIZE(ati_remote2_key_table);
+	} else {
+		if (input_scancode_to_scalar(ke, &scancode))
+			return -EINVAL;
+
+		mode = scancode >> 8;
+		if (mode > ATI_REMOTE2_PC)
+			return -EINVAL;
+
+		offset = ati_remote2_lookup(scancode & 0xff);
+		if (offset < 0)
+			return -EINVAL;
+	}
+
+	*old_keycode = ar2->keycode[mode][offset];
+	ar2->keycode[mode][offset] = ke->keycode;
+	__set_bit(ke->keycode, idev->keybit);
+
+	for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
+		for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
+			if (ar2->keycode[mode][index] == *old_keycode)
+				return 0;
+		}
+	}
+
+	__clear_bit(*old_keycode, idev->keybit);
+
+	return 0;
+}
+
+static int ati_remote2_input_init(struct ati_remote2 *ar2)
+{
+	struct input_dev *idev;
+	int index, mode, retval;
+
+	idev = input_allocate_device();
+	if (!idev)
+		return -ENOMEM;
+
+	ar2->idev = idev;
+	input_set_drvdata(idev, ar2);
+
+	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | BIT_MASK(EV_REL);
+	idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+		BIT_MASK(BTN_RIGHT);
+	idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+	for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
+		for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
+			ar2->keycode[mode][index] = ati_remote2_key_table[index].keycode;
+			__set_bit(ar2->keycode[mode][index], idev->keybit);
+		}
+	}
+
+	/* AUX1-AUX4 and PC generate the same scancode. */
+	index = ati_remote2_lookup(0x3f);
+	ar2->keycode[ATI_REMOTE2_AUX1][index] = KEY_PROG1;
+	ar2->keycode[ATI_REMOTE2_AUX2][index] = KEY_PROG2;
+	ar2->keycode[ATI_REMOTE2_AUX3][index] = KEY_PROG3;
+	ar2->keycode[ATI_REMOTE2_AUX4][index] = KEY_PROG4;
+	ar2->keycode[ATI_REMOTE2_PC][index] = KEY_PC;
+	__set_bit(KEY_PROG1, idev->keybit);
+	__set_bit(KEY_PROG2, idev->keybit);
+	__set_bit(KEY_PROG3, idev->keybit);
+	__set_bit(KEY_PROG4, idev->keybit);
+	__set_bit(KEY_PC, idev->keybit);
+
+	idev->rep[REP_DELAY]  = 250;
+	idev->rep[REP_PERIOD] = 33;
+
+	idev->open = ati_remote2_open;
+	idev->close = ati_remote2_close;
+
+	idev->getkeycode = ati_remote2_getkeycode;
+	idev->setkeycode = ati_remote2_setkeycode;
+
+	idev->name = ar2->name;
+	idev->phys = ar2->phys;
+
+	usb_to_input_id(ar2->udev, &idev->id);
+	idev->dev.parent = &ar2->udev->dev;
+
+	retval = input_register_device(idev);
+	if (retval)
+		input_free_device(idev);
+
+	return retval;
+}
+
+static int ati_remote2_urb_init(struct ati_remote2 *ar2)
+{
+	struct usb_device *udev = ar2->udev;
+	int i, pipe, maxp;
+
+	for (i = 0; i < 2; i++) {
+		ar2->buf[i] = usb_alloc_coherent(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]);
+		if (!ar2->buf[i])
+			return -ENOMEM;
+
+		ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+		if (!ar2->urb[i])
+			return -ENOMEM;
+
+		pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress);
+		maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+		maxp = maxp > 4 ? 4 : maxp;
+
+		usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp,
+				 i ? ati_remote2_complete_key : ati_remote2_complete_mouse,
+				 ar2, ar2->ep[i]->bInterval);
+		ar2->urb[i]->transfer_dma = ar2->buf_dma[i];
+		ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	}
+
+	return 0;
+}
+
+static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		usb_free_urb(ar2->urb[i]);
+		usb_free_coherent(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]);
+	}
+}
+
+static int ati_remote2_setup(struct ati_remote2 *ar2, unsigned int ch_mask)
+{
+	int r, i, channel;
+
+	/*
+	 * Configure receiver to only accept input from remote "channel"
+	 *  channel == 0  -> Accept input from any remote channel
+	 *  channel == 1  -> Only accept input from remote channel 1
+	 *  channel == 2  -> Only accept input from remote channel 2
+	 *  ...
+	 *  channel == 16 -> Only accept input from remote channel 16
+	 */
+
+	channel = 0;
+	for (i = 0; i < 16; i++) {
+		if ((1 << i) & ch_mask) {
+			if (!(~(1 << i) & ch_mask))
+				channel = i + 1;
+			break;
+		}
+	}
+
+	r = usb_control_msg(ar2->udev, usb_sndctrlpipe(ar2->udev, 0),
+			    0x20,
+			    USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+			    channel, 0x0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+	if (r) {
+		dev_err(&ar2->udev->dev, "%s - failed to set channel due to error: %d\n",
+			__func__, r);
+		return r;
+	}
+
+	return 0;
+}
+
+static ssize_t ati_remote2_show_channel_mask(struct device *dev,
+					     struct device_attribute *attr,
+					     char *buf)
+{
+	struct usb_device *udev = to_usb_device(dev);
+	struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+	struct ati_remote2 *ar2 = usb_get_intfdata(intf);
+
+	return sprintf(buf, "0x%04x\n", ar2->channel_mask);
+}
+
+static ssize_t ati_remote2_store_channel_mask(struct device *dev,
+					      struct device_attribute *attr,
+					      const char *buf, size_t count)
+{
+	struct usb_device *udev = to_usb_device(dev);
+	struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+	struct ati_remote2 *ar2 = usb_get_intfdata(intf);
+	unsigned int mask;
+	int r;
+
+	r = kstrtouint(buf, 0, &mask);
+	if (r)
+		return r;
+
+	if (mask & ~ATI_REMOTE2_MAX_CHANNEL_MASK)
+		return -EINVAL;
+
+	r = usb_autopm_get_interface(ar2->intf[0]);
+	if (r) {
+		dev_err(&ar2->intf[0]->dev,
+			"%s(): usb_autopm_get_interface() = %d\n", __func__, r);
+		return r;
+	}
+
+	mutex_lock(&ati_remote2_mutex);
+
+	if (mask != ar2->channel_mask) {
+		r = ati_remote2_setup(ar2, mask);
+		if (!r)
+			ar2->channel_mask = mask;
+	}
+
+	mutex_unlock(&ati_remote2_mutex);
+
+	usb_autopm_put_interface(ar2->intf[0]);
+
+	return r ? r : count;
+}
+
+static ssize_t ati_remote2_show_mode_mask(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct usb_device *udev = to_usb_device(dev);
+	struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+	struct ati_remote2 *ar2 = usb_get_intfdata(intf);
+
+	return sprintf(buf, "0x%02x\n", ar2->mode_mask);
+}
+
+static ssize_t ati_remote2_store_mode_mask(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t count)
+{
+	struct usb_device *udev = to_usb_device(dev);
+	struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+	struct ati_remote2 *ar2 = usb_get_intfdata(intf);
+	unsigned int mask;
+	int err;
+
+	err = kstrtouint(buf, 0, &mask);
+	if (err)
+		return err;
+
+	if (mask & ~ATI_REMOTE2_MAX_MODE_MASK)
+		return -EINVAL;
+
+	ar2->mode_mask = mask;
+
+	return count;
+}
+
+static DEVICE_ATTR(channel_mask, 0644, ati_remote2_show_channel_mask,
+		   ati_remote2_store_channel_mask);
+
+static DEVICE_ATTR(mode_mask, 0644, ati_remote2_show_mode_mask,
+		   ati_remote2_store_mode_mask);
+
+static struct attribute *ati_remote2_attrs[] = {
+	&dev_attr_channel_mask.attr,
+	&dev_attr_mode_mask.attr,
+	NULL,
+};
+
+static struct attribute_group ati_remote2_attr_group = {
+	.attrs = ati_remote2_attrs,
+};
+
+static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct usb_host_interface *alt = interface->cur_altsetting;
+	struct ati_remote2 *ar2;
+	int r;
+
+	if (alt->desc.bInterfaceNumber)
+		return -ENODEV;
+
+	ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL);
+	if (!ar2)
+		return -ENOMEM;
+
+	ar2->udev = udev;
+
+	/* Sanity check, first interface must have an endpoint */
+	if (alt->desc.bNumEndpoints < 1 || !alt->endpoint) {
+		dev_err(&interface->dev,
+			"%s(): interface 0 must have an endpoint\n", __func__);
+		r = -ENODEV;
+		goto fail1;
+	}
+	ar2->intf[0] = interface;
+	ar2->ep[0] = &alt->endpoint[0].desc;
+
+	/* Sanity check, the device must have two interfaces */
+	ar2->intf[1] = usb_ifnum_to_if(udev, 1);
+	if ((udev->actconfig->desc.bNumInterfaces < 2) || !ar2->intf[1]) {
+		dev_err(&interface->dev, "%s(): need 2 interfaces, found %d\n",
+			__func__, udev->actconfig->desc.bNumInterfaces);
+		r = -ENODEV;
+		goto fail1;
+	}
+
+	r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2);
+	if (r)
+		goto fail1;
+
+	/* Sanity check, second interface must have an endpoint */
+	alt = ar2->intf[1]->cur_altsetting;
+	if (alt->desc.bNumEndpoints < 1 || !alt->endpoint) {
+		dev_err(&interface->dev,
+			"%s(): interface 1 must have an endpoint\n", __func__);
+		r = -ENODEV;
+		goto fail2;
+	}
+	ar2->ep[1] = &alt->endpoint[0].desc;
+
+	r = ati_remote2_urb_init(ar2);
+	if (r)
+		goto fail3;
+
+	ar2->channel_mask = channel_mask;
+	ar2->mode_mask = mode_mask;
+
+	r = ati_remote2_setup(ar2, ar2->channel_mask);
+	if (r)
+		goto fail3;
+
+	usb_make_path(udev, ar2->phys, sizeof(ar2->phys));
+	strlcat(ar2->phys, "/input0", sizeof(ar2->phys));
+
+	strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name));
+
+	r = sysfs_create_group(&udev->dev.kobj, &ati_remote2_attr_group);
+	if (r)
+		goto fail3;
+
+	r = ati_remote2_input_init(ar2);
+	if (r)
+		goto fail4;
+
+	usb_set_intfdata(interface, ar2);
+
+	interface->needs_remote_wakeup = 1;
+
+	return 0;
+
+ fail4:
+	sysfs_remove_group(&udev->dev.kobj, &ati_remote2_attr_group);
+ fail3:
+	ati_remote2_urb_cleanup(ar2);
+ fail2:
+	usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
+ fail1:
+	kfree(ar2);
+
+	return r;
+}
+
+static void ati_remote2_disconnect(struct usb_interface *interface)
+{
+	struct ati_remote2 *ar2;
+	struct usb_host_interface *alt = interface->cur_altsetting;
+
+	if (alt->desc.bInterfaceNumber)
+		return;
+
+	ar2 = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+
+	input_unregister_device(ar2->idev);
+
+	sysfs_remove_group(&ar2->udev->dev.kobj, &ati_remote2_attr_group);
+
+	ati_remote2_urb_cleanup(ar2);
+
+	usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
+
+	kfree(ar2);
+}
+
+static int ati_remote2_suspend(struct usb_interface *interface,
+			       pm_message_t message)
+{
+	struct ati_remote2 *ar2;
+	struct usb_host_interface *alt = interface->cur_altsetting;
+
+	if (alt->desc.bInterfaceNumber)
+		return 0;
+
+	ar2 = usb_get_intfdata(interface);
+
+	dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+	mutex_lock(&ati_remote2_mutex);
+
+	if (ar2->flags & ATI_REMOTE2_OPENED)
+		ati_remote2_kill_urbs(ar2);
+
+	ar2->flags |= ATI_REMOTE2_SUSPENDED;
+
+	mutex_unlock(&ati_remote2_mutex);
+
+	return 0;
+}
+
+static int ati_remote2_resume(struct usb_interface *interface)
+{
+	struct ati_remote2 *ar2;
+	struct usb_host_interface *alt = interface->cur_altsetting;
+	int r = 0;
+
+	if (alt->desc.bInterfaceNumber)
+		return 0;
+
+	ar2 = usb_get_intfdata(interface);
+
+	dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+	mutex_lock(&ati_remote2_mutex);
+
+	if (ar2->flags & ATI_REMOTE2_OPENED)
+		r = ati_remote2_submit_urbs(ar2);
+
+	if (!r)
+		ar2->flags &= ~ATI_REMOTE2_SUSPENDED;
+
+	mutex_unlock(&ati_remote2_mutex);
+
+	return r;
+}
+
+static int ati_remote2_reset_resume(struct usb_interface *interface)
+{
+	struct ati_remote2 *ar2;
+	struct usb_host_interface *alt = interface->cur_altsetting;
+	int r = 0;
+
+	if (alt->desc.bInterfaceNumber)
+		return 0;
+
+	ar2 = usb_get_intfdata(interface);
+
+	dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+	mutex_lock(&ati_remote2_mutex);
+
+	r = ati_remote2_setup(ar2, ar2->channel_mask);
+	if (r)
+		goto out;
+
+	if (ar2->flags & ATI_REMOTE2_OPENED)
+		r = ati_remote2_submit_urbs(ar2);
+
+	if (!r)
+		ar2->flags &= ~ATI_REMOTE2_SUSPENDED;
+
+ out:
+	mutex_unlock(&ati_remote2_mutex);
+
+	return r;
+}
+
+static int ati_remote2_pre_reset(struct usb_interface *interface)
+{
+	struct ati_remote2 *ar2;
+	struct usb_host_interface *alt = interface->cur_altsetting;
+
+	if (alt->desc.bInterfaceNumber)
+		return 0;
+
+	ar2 = usb_get_intfdata(interface);
+
+	dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+	mutex_lock(&ati_remote2_mutex);
+
+	if (ar2->flags == ATI_REMOTE2_OPENED)
+		ati_remote2_kill_urbs(ar2);
+
+	return 0;
+}
+
+static int ati_remote2_post_reset(struct usb_interface *interface)
+{
+	struct ati_remote2 *ar2;
+	struct usb_host_interface *alt = interface->cur_altsetting;
+	int r = 0;
+
+	if (alt->desc.bInterfaceNumber)
+		return 0;
+
+	ar2 = usb_get_intfdata(interface);
+
+	dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+	if (ar2->flags == ATI_REMOTE2_OPENED)
+		r = ati_remote2_submit_urbs(ar2);
+
+	mutex_unlock(&ati_remote2_mutex);
+
+	return r;
+}
+
+module_usb_driver(ati_remote2_driver);
diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c
new file mode 100644
index 0000000..638165c
--- /dev/null
+++ b/drivers/input/misc/atlas_btns.c
@@ -0,0 +1,158 @@
+/*
+ *  atlas_btns.c - Atlas Wallmount Touchscreen ACPI Extras
+ *
+ *  Copyright (C) 2006 Jaya Kumar
+ *  Based on Toshiba ACPI by John Belmonte and ASUS ACPI
+ *  This work was sponsored by CIS(M) Sdn Bhd.
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/types.h>
+#include <linux/acpi.h>
+#include <asm/uaccess.h>
+
+#define ACPI_ATLAS_NAME		"Atlas ACPI"
+#define ACPI_ATLAS_CLASS	"Atlas"
+
+static unsigned short atlas_keymap[16];
+static struct input_dev *input_dev;
+
+/* button handling code */
+static acpi_status acpi_atlas_button_setup(acpi_handle region_handle,
+		    u32 function, void *handler_context, void **return_context)
+{
+	*return_context =
+		(function != ACPI_REGION_DEACTIVATE) ? handler_context : NULL;
+
+	return AE_OK;
+}
+
+static acpi_status acpi_atlas_button_handler(u32 function,
+		      acpi_physical_address address,
+		      u32 bit_width, u64 *value,
+		      void *handler_context, void *region_context)
+{
+	acpi_status status;
+
+	if (function == ACPI_WRITE) {
+		int code = address & 0x0f;
+		int key_down = !(address & 0x10);
+
+		input_event(input_dev, EV_MSC, MSC_SCAN, code);
+		input_report_key(input_dev, atlas_keymap[code], key_down);
+		input_sync(input_dev);
+
+		status = AE_OK;
+	} else {
+		pr_warn("shrugged on unexpected function: function=%x,address=%lx,value=%x\n",
+			function, (unsigned long)address, (u32)*value);
+		status = AE_BAD_PARAMETER;
+	}
+
+	return status;
+}
+
+static int atlas_acpi_button_add(struct acpi_device *device)
+{
+	acpi_status status;
+	int i;
+	int err;
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		pr_err("unable to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	input_dev->name = "Atlas ACPI button driver";
+	input_dev->phys = "ASIM0000/atlas/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->keycode = atlas_keymap;
+	input_dev->keycodesize = sizeof(unsigned short);
+	input_dev->keycodemax = ARRAY_SIZE(atlas_keymap);
+
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+	__set_bit(EV_KEY, input_dev->evbit);
+	for (i = 0; i < ARRAY_SIZE(atlas_keymap); i++) {
+		if (i < 9) {
+			atlas_keymap[i] = KEY_F1 + i;
+			__set_bit(KEY_F1 + i, input_dev->keybit);
+		} else
+			atlas_keymap[i] = KEY_RESERVED;
+	}
+
+	err = input_register_device(input_dev);
+	if (err) {
+		pr_err("couldn't register input device\n");
+		input_free_device(input_dev);
+		return err;
+	}
+
+	/* hookup button handler */
+	status = acpi_install_address_space_handler(device->handle,
+				0x81, &acpi_atlas_button_handler,
+				&acpi_atlas_button_setup, device);
+	if (ACPI_FAILURE(status)) {
+		pr_err("error installing addr spc handler\n");
+		input_unregister_device(input_dev);
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
+static int atlas_acpi_button_remove(struct acpi_device *device)
+{
+	acpi_status status;
+
+	status = acpi_remove_address_space_handler(device->handle,
+				0x81, &acpi_atlas_button_handler);
+	if (ACPI_FAILURE(status))
+		pr_err("error removing addr spc handler\n");
+
+	input_unregister_device(input_dev);
+
+	return 0;
+}
+
+static const struct acpi_device_id atlas_device_ids[] = {
+	{"ASIM0000", 0},
+	{"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, atlas_device_ids);
+
+static struct acpi_driver atlas_acpi_driver = {
+	.name	= ACPI_ATLAS_NAME,
+	.class	= ACPI_ATLAS_CLASS,
+	.owner	= THIS_MODULE,
+	.ids	= atlas_device_ids,
+	.ops	= {
+		.add	= atlas_acpi_button_add,
+		.remove	= atlas_acpi_button_remove,
+	},
+};
+module_acpi_driver(atlas_acpi_driver);
+
+MODULE_AUTHOR("Jaya Kumar");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atlas button driver");
+
diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c
new file mode 100644
index 0000000..1ac898d
--- /dev/null
+++ b/drivers/input/misc/axp20x-pek.c
@@ -0,0 +1,295 @@
+/*
+ * axp20x power button driver.
+ *
+ * Copyright (C) 2013 Carlo Caione <carlo@caione.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define AXP20X_PEK_STARTUP_MASK		(0xc0)
+#define AXP20X_PEK_SHUTDOWN_MASK	(0x03)
+
+struct axp20x_pek {
+	struct axp20x_dev *axp20x;
+	struct input_dev *input;
+	int irq_dbr;
+	int irq_dbf;
+};
+
+struct axp20x_time {
+	unsigned int time;
+	unsigned int idx;
+};
+
+static const struct axp20x_time startup_time[] = {
+	{ .time = 128,  .idx = 0 },
+	{ .time = 1000, .idx = 2 },
+	{ .time = 3000, .idx = 1 },
+	{ .time = 2000, .idx = 3 },
+};
+
+static const struct axp20x_time shutdown_time[] = {
+	{ .time = 4000,  .idx = 0 },
+	{ .time = 6000,  .idx = 1 },
+	{ .time = 8000,  .idx = 2 },
+	{ .time = 10000, .idx = 3 },
+};
+
+struct axp20x_pek_ext_attr {
+	const struct axp20x_time *p_time;
+	unsigned int mask;
+};
+
+static struct axp20x_pek_ext_attr axp20x_pek_startup_ext_attr = {
+	.p_time	= startup_time,
+	.mask	= AXP20X_PEK_STARTUP_MASK,
+};
+
+static struct axp20x_pek_ext_attr axp20x_pek_shutdown_ext_attr = {
+	.p_time	= shutdown_time,
+	.mask	= AXP20X_PEK_SHUTDOWN_MASK,
+};
+
+static struct axp20x_pek_ext_attr *get_axp_ext_attr(struct device_attribute *attr)
+{
+	return container_of(attr, struct dev_ext_attribute, attr)->var;
+}
+
+static ssize_t axp20x_show_ext_attr(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
+	struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
+	unsigned int val;
+	int ret, i;
+
+	ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val);
+	if (ret != 0)
+		return ret;
+
+	val &= axp20x_ea->mask;
+	val >>= ffs(axp20x_ea->mask) - 1;
+
+	for (i = 0; i < 4; i++)
+		if (val == axp20x_ea->p_time[i].idx)
+			val = axp20x_ea->p_time[i].time;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t axp20x_store_ext_attr(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
+	struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
+	char val_str[20];
+	size_t len;
+	int ret, i;
+	unsigned int val, idx = 0;
+	unsigned int best_err = UINT_MAX;
+
+	val_str[sizeof(val_str) - 1] = '\0';
+	strncpy(val_str, buf, sizeof(val_str) - 1);
+	len = strlen(val_str);
+
+	if (len && val_str[len - 1] == '\n')
+		val_str[len - 1] = '\0';
+
+	ret = kstrtouint(val_str, 10, &val);
+	if (ret)
+		return ret;
+
+	for (i = 3; i >= 0; i--) {
+		unsigned int err;
+
+		err = abs(axp20x_ea->p_time[i].time - val);
+		if (err < best_err) {
+			best_err = err;
+			idx = axp20x_ea->p_time[i].idx;
+		}
+
+		if (!err)
+			break;
+	}
+
+	idx <<= ffs(axp20x_ea->mask) - 1;
+	ret = regmap_update_bits(axp20x_pek->axp20x->regmap,
+				 AXP20X_PEK_KEY,
+				 axp20x_ea->mask, idx);
+	if (ret != 0)
+		return -EINVAL;
+
+	return count;
+}
+
+static struct dev_ext_attribute axp20x_dev_attr_startup = {
+	.attr	= __ATTR(startup, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
+	.var	= &axp20x_pek_startup_ext_attr,
+};
+
+static struct dev_ext_attribute axp20x_dev_attr_shutdown = {
+	.attr	= __ATTR(shutdown, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
+	.var	= &axp20x_pek_shutdown_ext_attr,
+};
+
+static struct attribute *axp20x_attributes[] = {
+	&axp20x_dev_attr_startup.attr.attr,
+	&axp20x_dev_attr_shutdown.attr.attr,
+	NULL,
+};
+
+static const struct attribute_group axp20x_attribute_group = {
+	.attrs = axp20x_attributes,
+};
+
+static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
+{
+	struct input_dev *idev = pwr;
+	struct axp20x_pek *axp20x_pek = input_get_drvdata(idev);
+
+	/*
+	 * The power-button is connected to ground so a falling edge (dbf)
+	 * means it is pressed.
+	 */
+	if (irq == axp20x_pek->irq_dbf)
+		input_report_key(idev, KEY_POWER, true);
+	else if (irq == axp20x_pek->irq_dbr)
+		input_report_key(idev, KEY_POWER, false);
+
+	input_sync(idev);
+
+	return IRQ_HANDLED;
+}
+
+static void axp20x_remove_sysfs_group(void *_data)
+{
+	struct device *dev = _data;
+
+	sysfs_remove_group(&dev->kobj, &axp20x_attribute_group);
+}
+
+static int axp20x_pek_probe(struct platform_device *pdev)
+{
+	struct axp20x_pek *axp20x_pek;
+	struct axp20x_dev *axp20x;
+	struct input_dev *idev;
+	int error;
+
+	axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
+				  GFP_KERNEL);
+	if (!axp20x_pek)
+		return -ENOMEM;
+
+	axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
+	axp20x = axp20x_pek->axp20x;
+
+	axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
+	if (axp20x_pek->irq_dbr < 0) {
+		dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n",
+				axp20x_pek->irq_dbr);
+		return axp20x_pek->irq_dbr;
+	}
+	axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc,
+						  axp20x_pek->irq_dbr);
+
+	axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF");
+	if (axp20x_pek->irq_dbf < 0) {
+		dev_err(&pdev->dev, "No IRQ for PEK_DBF, error=%d\n",
+				axp20x_pek->irq_dbf);
+		return axp20x_pek->irq_dbf;
+	}
+	axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc,
+						  axp20x_pek->irq_dbf);
+
+	axp20x_pek->input = devm_input_allocate_device(&pdev->dev);
+	if (!axp20x_pek->input)
+		return -ENOMEM;
+
+	idev = axp20x_pek->input;
+
+	idev->name = "axp20x-pek";
+	idev->phys = "m1kbd/input2";
+	idev->dev.parent = &pdev->dev;
+
+	input_set_capability(idev, EV_KEY, KEY_POWER);
+
+	input_set_drvdata(idev, axp20x_pek);
+
+	error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr,
+					     axp20x_pek_irq, 0,
+					     "axp20x-pek-dbr", idev);
+	if (error < 0) {
+		dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n",
+			axp20x_pek->irq_dbr, error);
+		return error;
+	}
+
+	error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf,
+					  axp20x_pek_irq, 0,
+					  "axp20x-pek-dbf", idev);
+	if (error < 0) {
+		dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n",
+			axp20x_pek->irq_dbf, error);
+		return error;
+	}
+
+	error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group);
+	if (error) {
+		dev_err(axp20x->dev, "Failed to create sysfs attributes: %d\n",
+			error);
+		return error;
+	}
+
+	error = devm_add_action(&pdev->dev,
+				axp20x_remove_sysfs_group, &pdev->dev);
+	if (error) {
+		axp20x_remove_sysfs_group(&pdev->dev);
+		dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n",
+			error);
+		return error;
+	}
+
+	error = input_register_device(idev);
+	if (error) {
+		dev_err(axp20x->dev, "Can't register input device: %d\n",
+			error);
+		return error;
+	}
+
+	platform_set_drvdata(pdev, axp20x_pek);
+
+	return 0;
+}
+
+static struct platform_driver axp20x_pek_driver = {
+	.probe		= axp20x_pek_probe,
+	.driver		= {
+		.name		= "axp20x-pek",
+	},
+};
+module_platform_driver(axp20x_pek_driver);
+
+MODULE_DESCRIPTION("axp20x Power Button");
+MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:axp20x-pek");
diff --git a/drivers/input/misc/bfin_rotary.c b/drivers/input/misc/bfin_rotary.c
new file mode 100644
index 0000000..a0fc18f
--- /dev/null
+++ b/drivers/input/misc/bfin_rotary.c
@@ -0,0 +1,295 @@
+/*
+ * Rotary counter driver for Analog Devices Blackfin Processors
+ *
+ * Copyright 2008-2009 Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/platform_data/bfin_rotary.h>
+
+#include <asm/portmux.h>
+
+#define CNT_CONFIG_OFF		0	/* CNT Config Offset */
+#define CNT_IMASK_OFF		4	/* CNT Interrupt Mask Offset */
+#define CNT_STATUS_OFF		8	/* CNT Status Offset */
+#define CNT_COMMAND_OFF		12	/* CNT Command Offset */
+#define CNT_DEBOUNCE_OFF	16	/* CNT Debounce Offset */
+#define CNT_COUNTER_OFF		20	/* CNT Counter Offset */
+#define CNT_MAX_OFF		24	/* CNT Maximum Count Offset */
+#define CNT_MIN_OFF		28	/* CNT Minimum Count Offset */
+
+struct bfin_rot {
+	struct input_dev *input;
+	void __iomem *base;
+	int irq;
+	unsigned int up_key;
+	unsigned int down_key;
+	unsigned int button_key;
+	unsigned int rel_code;
+
+	unsigned short mode;
+	unsigned short debounce;
+
+	unsigned short cnt_config;
+	unsigned short cnt_imask;
+	unsigned short cnt_debounce;
+};
+
+static void report_key_event(struct input_dev *input, int keycode)
+{
+	/* simulate a press-n-release */
+	input_report_key(input, keycode, 1);
+	input_sync(input);
+	input_report_key(input, keycode, 0);
+	input_sync(input);
+}
+
+static void report_rotary_event(struct bfin_rot *rotary, int delta)
+{
+	struct input_dev *input = rotary->input;
+
+	if (rotary->up_key) {
+		report_key_event(input,
+				 delta > 0 ? rotary->up_key : rotary->down_key);
+	} else {
+		input_report_rel(input, rotary->rel_code, delta);
+		input_sync(input);
+	}
+}
+
+static irqreturn_t bfin_rotary_isr(int irq, void *dev_id)
+{
+	struct bfin_rot *rotary = dev_id;
+	int delta;
+
+	switch (readw(rotary->base + CNT_STATUS_OFF)) {
+
+	case ICII:
+		break;
+
+	case UCII:
+	case DCII:
+		delta = readl(rotary->base + CNT_COUNTER_OFF);
+		if (delta)
+			report_rotary_event(rotary, delta);
+		break;
+
+	case CZMII:
+		report_key_event(rotary->input, rotary->button_key);
+		break;
+
+	default:
+		break;
+	}
+
+	writew(W1LCNT_ZERO, rotary->base + CNT_COMMAND_OFF); /* Clear COUNTER */
+	writew(-1, rotary->base + CNT_STATUS_OFF); /* Clear STATUS */
+
+	return IRQ_HANDLED;
+}
+
+static int bfin_rotary_open(struct input_dev *input)
+{
+	struct bfin_rot *rotary = input_get_drvdata(input);
+	unsigned short val;
+
+	if (rotary->mode & ROT_DEBE)
+		writew(rotary->debounce & DPRESCALE,
+			rotary->base + CNT_DEBOUNCE_OFF);
+
+	writew(rotary->mode & ~CNTE, rotary->base + CNT_CONFIG_OFF);
+
+	val = UCIE | DCIE;
+	if (rotary->button_key)
+		val |= CZMIE;
+	writew(val, rotary->base + CNT_IMASK_OFF);
+
+	writew(rotary->mode | CNTE, rotary->base + CNT_CONFIG_OFF);
+
+	return 0;
+}
+
+static void bfin_rotary_close(struct input_dev *input)
+{
+	struct bfin_rot *rotary = input_get_drvdata(input);
+
+	writew(0, rotary->base + CNT_CONFIG_OFF);
+	writew(0, rotary->base + CNT_IMASK_OFF);
+}
+
+static void bfin_rotary_free_action(void *data)
+{
+	peripheral_free_list(data);
+}
+
+static int bfin_rotary_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct bfin_rotary_platform_data *pdata = dev_get_platdata(dev);
+	struct bfin_rot *rotary;
+	struct resource *res;
+	struct input_dev *input;
+	int error;
+
+	/* Basic validation */
+	if ((pdata->rotary_up_key && !pdata->rotary_down_key) ||
+	    (!pdata->rotary_up_key && pdata->rotary_down_key)) {
+		return -EINVAL;
+	}
+
+	if (pdata->pin_list) {
+		error = peripheral_request_list(pdata->pin_list,
+						dev_name(&pdev->dev));
+		if (error) {
+			dev_err(dev, "requesting peripherals failed: %d\n",
+				error);
+			return error;
+		}
+
+		error = devm_add_action(dev, bfin_rotary_free_action,
+					pdata->pin_list);
+		if (error) {
+			dev_err(dev, "setting cleanup action failed: %d\n",
+				error);
+			peripheral_free_list(pdata->pin_list);
+			return error;
+		}
+	}
+
+	rotary = devm_kzalloc(dev, sizeof(struct bfin_rot), GFP_KERNEL);
+	if (!rotary)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	rotary->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(rotary->base))
+		return PTR_ERR(rotary->base);
+
+	input = devm_input_allocate_device(dev);
+	if (!input)
+		return -ENOMEM;
+
+	rotary->input = input;
+
+	rotary->up_key = pdata->rotary_up_key;
+	rotary->down_key = pdata->rotary_down_key;
+	rotary->button_key = pdata->rotary_button_key;
+	rotary->rel_code = pdata->rotary_rel_code;
+
+	rotary->mode = pdata->mode;
+	rotary->debounce = pdata->debounce;
+
+	input->name = pdev->name;
+	input->phys = "bfin-rotary/input0";
+	input->dev.parent = &pdev->dev;
+
+	input_set_drvdata(input, rotary);
+
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0100;
+
+	input->open = bfin_rotary_open;
+	input->close = bfin_rotary_close;
+
+	if (rotary->up_key) {
+		__set_bit(EV_KEY, input->evbit);
+		__set_bit(rotary->up_key, input->keybit);
+		__set_bit(rotary->down_key, input->keybit);
+	} else {
+		__set_bit(EV_REL, input->evbit);
+		__set_bit(rotary->rel_code, input->relbit);
+	}
+
+	if (rotary->button_key) {
+		__set_bit(EV_KEY, input->evbit);
+		__set_bit(rotary->button_key, input->keybit);
+	}
+
+	/* Quiesce the device before requesting irq */
+	bfin_rotary_close(input);
+
+	rotary->irq = platform_get_irq(pdev, 0);
+	if (rotary->irq < 0) {
+		dev_err(dev, "No rotary IRQ specified\n");
+		return -ENOENT;
+	}
+
+	error = devm_request_irq(dev, rotary->irq, bfin_rotary_isr,
+				 0, dev_name(dev), rotary);
+	if (error) {
+		dev_err(dev, "unable to claim irq %d; error %d\n",
+			rotary->irq, error);
+		return error;
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(dev, "unable to register input device (%d)\n", error);
+		return error;
+	}
+
+	platform_set_drvdata(pdev, rotary);
+	device_init_wakeup(&pdev->dev, 1);
+
+	return 0;
+}
+
+static int __maybe_unused bfin_rotary_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct bfin_rot *rotary = platform_get_drvdata(pdev);
+
+	rotary->cnt_config = readw(rotary->base + CNT_CONFIG_OFF);
+	rotary->cnt_imask = readw(rotary->base + CNT_IMASK_OFF);
+	rotary->cnt_debounce = readw(rotary->base + CNT_DEBOUNCE_OFF);
+
+	if (device_may_wakeup(&pdev->dev))
+		enable_irq_wake(rotary->irq);
+
+	return 0;
+}
+
+static int __maybe_unused bfin_rotary_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct bfin_rot *rotary = platform_get_drvdata(pdev);
+
+	writew(rotary->cnt_debounce, rotary->base + CNT_DEBOUNCE_OFF);
+	writew(rotary->cnt_imask, rotary->base + CNT_IMASK_OFF);
+	writew(rotary->cnt_config & ~CNTE, rotary->base + CNT_CONFIG_OFF);
+
+	if (device_may_wakeup(&pdev->dev))
+		disable_irq_wake(rotary->irq);
+
+	if (rotary->cnt_config & CNTE)
+		writew(rotary->cnt_config, rotary->base + CNT_CONFIG_OFF);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(bfin_rotary_pm_ops,
+			 bfin_rotary_suspend, bfin_rotary_resume);
+
+static struct platform_driver bfin_rotary_device_driver = {
+	.probe		= bfin_rotary_probe,
+	.driver		= {
+		.name	= "bfin-rotary",
+		.pm	= &bfin_rotary_pm_ops,
+	},
+};
+module_platform_driver(bfin_rotary_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors");
+MODULE_ALIAS("platform:bfin-rotary");
diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c
new file mode 100644
index 0000000..1d0e61d
--- /dev/null
+++ b/drivers/input/misc/bma150.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright (c) 2011 Bosch Sensortec GmbH
+ * Copyright (c) 2011 Unixphere
+ *
+ * This driver adds support for Bosch Sensortec's digital acceleration
+ * sensors BMA150 and SMB380.
+ * The SMB380 is fully compatible with BMA150 and only differs in packaging.
+ *
+ * The datasheet for the BMA150 chip can be found here:
+ * http://www.bosch-sensortec.com/content/language1/downloads/BST-BMA150-DS000-07.pdf
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/bma150.h>
+
+#define ABSMAX_ACC_VAL		0x01FF
+#define ABSMIN_ACC_VAL		-(ABSMAX_ACC_VAL)
+
+/* Each axis is represented by a 2-byte data word */
+#define BMA150_XYZ_DATA_SIZE	6
+
+/* Input poll interval in milliseconds */
+#define BMA150_POLL_INTERVAL	10
+#define BMA150_POLL_MAX		200
+#define BMA150_POLL_MIN		0
+
+#define BMA150_MODE_NORMAL	0
+#define BMA150_MODE_SLEEP	2
+#define BMA150_MODE_WAKE_UP	3
+
+/* Data register addresses */
+#define BMA150_DATA_0_REG	0x00
+#define BMA150_DATA_1_REG	0x01
+#define BMA150_DATA_2_REG	0x02
+
+/* Control register addresses */
+#define BMA150_CTRL_0_REG	0x0A
+#define BMA150_CTRL_1_REG	0x0B
+#define BMA150_CTRL_2_REG	0x14
+#define BMA150_CTRL_3_REG	0x15
+
+/* Configuration/Setting register addresses */
+#define BMA150_CFG_0_REG	0x0C
+#define BMA150_CFG_1_REG	0x0D
+#define BMA150_CFG_2_REG	0x0E
+#define BMA150_CFG_3_REG	0x0F
+#define BMA150_CFG_4_REG	0x10
+#define BMA150_CFG_5_REG	0x11
+
+#define BMA150_CHIP_ID		2
+#define BMA180_CHIP_ID		3
+#define BMA150_CHIP_ID_REG	BMA150_DATA_0_REG
+
+#define BMA150_ACC_X_LSB_REG	BMA150_DATA_2_REG
+
+#define BMA150_SLEEP_POS	0
+#define BMA150_SLEEP_MSK	0x01
+#define BMA150_SLEEP_REG	BMA150_CTRL_0_REG
+
+#define BMA150_BANDWIDTH_POS	0
+#define BMA150_BANDWIDTH_MSK	0x07
+#define BMA150_BANDWIDTH_REG	BMA150_CTRL_2_REG
+
+#define BMA150_RANGE_POS	3
+#define BMA150_RANGE_MSK	0x18
+#define BMA150_RANGE_REG	BMA150_CTRL_2_REG
+
+#define BMA150_WAKE_UP_POS	0
+#define BMA150_WAKE_UP_MSK	0x01
+#define BMA150_WAKE_UP_REG	BMA150_CTRL_3_REG
+
+#define BMA150_SW_RES_POS	1
+#define BMA150_SW_RES_MSK	0x02
+#define BMA150_SW_RES_REG	BMA150_CTRL_0_REG
+
+/* Any-motion interrupt register fields */
+#define BMA150_ANY_MOTION_EN_POS	6
+#define BMA150_ANY_MOTION_EN_MSK	0x40
+#define BMA150_ANY_MOTION_EN_REG	BMA150_CTRL_1_REG
+
+#define BMA150_ANY_MOTION_DUR_POS	6
+#define BMA150_ANY_MOTION_DUR_MSK	0xC0
+#define BMA150_ANY_MOTION_DUR_REG	BMA150_CFG_5_REG
+
+#define BMA150_ANY_MOTION_THRES_REG	BMA150_CFG_4_REG
+
+/* Advanced interrupt register fields */
+#define BMA150_ADV_INT_EN_POS		6
+#define BMA150_ADV_INT_EN_MSK		0x40
+#define BMA150_ADV_INT_EN_REG		BMA150_CTRL_3_REG
+
+/* High-G interrupt register fields */
+#define BMA150_HIGH_G_EN_POS		1
+#define BMA150_HIGH_G_EN_MSK		0x02
+#define BMA150_HIGH_G_EN_REG		BMA150_CTRL_1_REG
+
+#define BMA150_HIGH_G_HYST_POS		3
+#define BMA150_HIGH_G_HYST_MSK		0x38
+#define BMA150_HIGH_G_HYST_REG		BMA150_CFG_5_REG
+
+#define BMA150_HIGH_G_DUR_REG		BMA150_CFG_3_REG
+#define BMA150_HIGH_G_THRES_REG		BMA150_CFG_2_REG
+
+/* Low-G interrupt register fields */
+#define BMA150_LOW_G_EN_POS		0
+#define BMA150_LOW_G_EN_MSK		0x01
+#define BMA150_LOW_G_EN_REG		BMA150_CTRL_1_REG
+
+#define BMA150_LOW_G_HYST_POS		0
+#define BMA150_LOW_G_HYST_MSK		0x07
+#define BMA150_LOW_G_HYST_REG		BMA150_CFG_5_REG
+
+#define BMA150_LOW_G_DUR_REG		BMA150_CFG_1_REG
+#define BMA150_LOW_G_THRES_REG		BMA150_CFG_0_REG
+
+struct bma150_data {
+	struct i2c_client *client;
+	struct input_polled_dev *input_polled;
+	struct input_dev *input;
+	u8 mode;
+};
+
+/*
+ * The settings for the given range, bandwidth and interrupt features
+ * are stated and verified by Bosch Sensortec where they are configured
+ * to provide a generic sensitivity performance.
+ */
+static struct bma150_cfg default_cfg = {
+	.any_motion_int = 1,
+	.hg_int = 1,
+	.lg_int = 1,
+	.any_motion_dur = 0,
+	.any_motion_thres = 0,
+	.hg_hyst = 0,
+	.hg_dur = 150,
+	.hg_thres = 160,
+	.lg_hyst = 0,
+	.lg_dur = 150,
+	.lg_thres = 20,
+	.range = BMA150_RANGE_2G,
+	.bandwidth = BMA150_BW_50HZ
+};
+
+static int bma150_write_byte(struct i2c_client *client, u8 reg, u8 val)
+{
+	s32 ret;
+
+	/* As per specification, disable irq in between register writes */
+	if (client->irq)
+		disable_irq_nosync(client->irq);
+
+	ret = i2c_smbus_write_byte_data(client, reg, val);
+
+	if (client->irq)
+		enable_irq(client->irq);
+
+	return ret;
+}
+
+static int bma150_set_reg_bits(struct i2c_client *client,
+					int val, int shift, u8 mask, u8 reg)
+{
+	int data;
+
+	data = i2c_smbus_read_byte_data(client, reg);
+	if (data < 0)
+		return data;
+
+	data = (data & ~mask) | ((val << shift) & mask);
+	return bma150_write_byte(client, reg, data);
+}
+
+static int bma150_set_mode(struct bma150_data *bma150, u8 mode)
+{
+	int error;
+
+	error = bma150_set_reg_bits(bma150->client, mode, BMA150_WAKE_UP_POS,
+				BMA150_WAKE_UP_MSK, BMA150_WAKE_UP_REG);
+	if (error)
+		return error;
+
+	error = bma150_set_reg_bits(bma150->client, mode, BMA150_SLEEP_POS,
+				BMA150_SLEEP_MSK, BMA150_SLEEP_REG);
+	if (error)
+		return error;
+
+	if (mode == BMA150_MODE_NORMAL)
+		msleep(2);
+
+	bma150->mode = mode;
+	return 0;
+}
+
+static int bma150_soft_reset(struct bma150_data *bma150)
+{
+	int error;
+
+	error = bma150_set_reg_bits(bma150->client, 1, BMA150_SW_RES_POS,
+				BMA150_SW_RES_MSK, BMA150_SW_RES_REG);
+	if (error)
+		return error;
+
+	msleep(2);
+	return 0;
+}
+
+static int bma150_set_range(struct bma150_data *bma150, u8 range)
+{
+	return bma150_set_reg_bits(bma150->client, range, BMA150_RANGE_POS,
+				BMA150_RANGE_MSK, BMA150_RANGE_REG);
+}
+
+static int bma150_set_bandwidth(struct bma150_data *bma150, u8 bw)
+{
+	return bma150_set_reg_bits(bma150->client, bw, BMA150_BANDWIDTH_POS,
+				BMA150_BANDWIDTH_MSK, BMA150_BANDWIDTH_REG);
+}
+
+static int bma150_set_low_g_interrupt(struct bma150_data *bma150,
+					u8 enable, u8 hyst, u8 dur, u8 thres)
+{
+	int error;
+
+	error = bma150_set_reg_bits(bma150->client, hyst,
+				BMA150_LOW_G_HYST_POS, BMA150_LOW_G_HYST_MSK,
+				BMA150_LOW_G_HYST_REG);
+	if (error)
+		return error;
+
+	error = bma150_write_byte(bma150->client, BMA150_LOW_G_DUR_REG, dur);
+	if (error)
+		return error;
+
+	error = bma150_write_byte(bma150->client, BMA150_LOW_G_THRES_REG, thres);
+	if (error)
+		return error;
+
+	return bma150_set_reg_bits(bma150->client, !!enable,
+				BMA150_LOW_G_EN_POS, BMA150_LOW_G_EN_MSK,
+				BMA150_LOW_G_EN_REG);
+}
+
+static int bma150_set_high_g_interrupt(struct bma150_data *bma150,
+					u8 enable, u8 hyst, u8 dur, u8 thres)
+{
+	int error;
+
+	error = bma150_set_reg_bits(bma150->client, hyst,
+				BMA150_HIGH_G_HYST_POS, BMA150_HIGH_G_HYST_MSK,
+				BMA150_HIGH_G_HYST_REG);
+	if (error)
+		return error;
+
+	error = bma150_write_byte(bma150->client,
+				BMA150_HIGH_G_DUR_REG, dur);
+	if (error)
+		return error;
+
+	error = bma150_write_byte(bma150->client,
+				BMA150_HIGH_G_THRES_REG, thres);
+	if (error)
+		return error;
+
+	return bma150_set_reg_bits(bma150->client, !!enable,
+				BMA150_HIGH_G_EN_POS, BMA150_HIGH_G_EN_MSK,
+				BMA150_HIGH_G_EN_REG);
+}
+
+
+static int bma150_set_any_motion_interrupt(struct bma150_data *bma150,
+						u8 enable, u8 dur, u8 thres)
+{
+	int error;
+
+	error = bma150_set_reg_bits(bma150->client, dur,
+				BMA150_ANY_MOTION_DUR_POS,
+				BMA150_ANY_MOTION_DUR_MSK,
+				BMA150_ANY_MOTION_DUR_REG);
+	if (error)
+		return error;
+
+	error = bma150_write_byte(bma150->client,
+				BMA150_ANY_MOTION_THRES_REG, thres);
+	if (error)
+		return error;
+
+	error = bma150_set_reg_bits(bma150->client, !!enable,
+				BMA150_ADV_INT_EN_POS, BMA150_ADV_INT_EN_MSK,
+				BMA150_ADV_INT_EN_REG);
+	if (error)
+		return error;
+
+	return bma150_set_reg_bits(bma150->client, !!enable,
+				BMA150_ANY_MOTION_EN_POS,
+				BMA150_ANY_MOTION_EN_MSK,
+				BMA150_ANY_MOTION_EN_REG);
+}
+
+static void bma150_report_xyz(struct bma150_data *bma150)
+{
+	u8 data[BMA150_XYZ_DATA_SIZE];
+	s16 x, y, z;
+	s32 ret;
+
+	ret = i2c_smbus_read_i2c_block_data(bma150->client,
+			BMA150_ACC_X_LSB_REG, BMA150_XYZ_DATA_SIZE, data);
+	if (ret != BMA150_XYZ_DATA_SIZE)
+		return;
+
+	x = ((0xc0 & data[0]) >> 6) | (data[1] << 2);
+	y = ((0xc0 & data[2]) >> 6) | (data[3] << 2);
+	z = ((0xc0 & data[4]) >> 6) | (data[5] << 2);
+
+	x = sign_extend32(x, 9);
+	y = sign_extend32(y, 9);
+	z = sign_extend32(z, 9);
+
+	input_report_abs(bma150->input, ABS_X, x);
+	input_report_abs(bma150->input, ABS_Y, y);
+	input_report_abs(bma150->input, ABS_Z, z);
+	input_sync(bma150->input);
+}
+
+static irqreturn_t bma150_irq_thread(int irq, void *dev)
+{
+	bma150_report_xyz(dev);
+
+	return IRQ_HANDLED;
+}
+
+static void bma150_poll(struct input_polled_dev *dev)
+{
+	bma150_report_xyz(dev->private);
+}
+
+static int bma150_open(struct bma150_data *bma150)
+{
+	int error;
+
+	error = pm_runtime_get_sync(&bma150->client->dev);
+	if (error < 0 && error != -ENOSYS)
+		return error;
+
+	/*
+	 * See if runtime PM woke up the device. If runtime PM
+	 * is disabled we need to do it ourselves.
+	 */
+	if (bma150->mode != BMA150_MODE_NORMAL) {
+		error = bma150_set_mode(bma150, BMA150_MODE_NORMAL);
+		if (error)
+			return error;
+	}
+
+	return 0;
+}
+
+static void bma150_close(struct bma150_data *bma150)
+{
+	pm_runtime_put_sync(&bma150->client->dev);
+
+	if (bma150->mode != BMA150_MODE_SLEEP)
+		bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+}
+
+static int bma150_irq_open(struct input_dev *input)
+{
+	struct bma150_data *bma150 = input_get_drvdata(input);
+
+	return bma150_open(bma150);
+}
+
+static void bma150_irq_close(struct input_dev *input)
+{
+	struct bma150_data *bma150 = input_get_drvdata(input);
+
+	bma150_close(bma150);
+}
+
+static void bma150_poll_open(struct input_polled_dev *ipoll_dev)
+{
+	struct bma150_data *bma150 = ipoll_dev->private;
+
+	bma150_open(bma150);
+}
+
+static void bma150_poll_close(struct input_polled_dev *ipoll_dev)
+{
+	struct bma150_data *bma150 = ipoll_dev->private;
+
+	bma150_close(bma150);
+}
+
+static int bma150_initialize(struct bma150_data *bma150,
+				       const struct bma150_cfg *cfg)
+{
+	int error;
+
+	error = bma150_soft_reset(bma150);
+	if (error)
+		return error;
+
+	error = bma150_set_bandwidth(bma150, cfg->bandwidth);
+	if (error)
+		return error;
+
+	error = bma150_set_range(bma150, cfg->range);
+	if (error)
+		return error;
+
+	if (bma150->client->irq) {
+		error = bma150_set_any_motion_interrupt(bma150,
+					cfg->any_motion_int,
+					cfg->any_motion_dur,
+					cfg->any_motion_thres);
+		if (error)
+			return error;
+
+		error = bma150_set_high_g_interrupt(bma150,
+					cfg->hg_int, cfg->hg_hyst,
+					cfg->hg_dur, cfg->hg_thres);
+		if (error)
+			return error;
+
+		error = bma150_set_low_g_interrupt(bma150,
+					cfg->lg_int, cfg->lg_hyst,
+					cfg->lg_dur, cfg->lg_thres);
+		if (error)
+			return error;
+	}
+
+	return bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+}
+
+static void bma150_init_input_device(struct bma150_data *bma150,
+						struct input_dev *idev)
+{
+	idev->name = BMA150_DRIVER;
+	idev->phys = BMA150_DRIVER "/input0";
+	idev->id.bustype = BUS_I2C;
+	idev->dev.parent = &bma150->client->dev;
+
+	idev->evbit[0] = BIT_MASK(EV_ABS);
+	input_set_abs_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+	input_set_abs_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+	input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+}
+
+static int bma150_register_input_device(struct bma150_data *bma150)
+{
+	struct input_dev *idev;
+	int error;
+
+	idev = input_allocate_device();
+	if (!idev)
+		return -ENOMEM;
+
+	bma150_init_input_device(bma150, idev);
+
+	idev->open = bma150_irq_open;
+	idev->close = bma150_irq_close;
+	input_set_drvdata(idev, bma150);
+
+	error = input_register_device(idev);
+	if (error) {
+		input_free_device(idev);
+		return error;
+	}
+
+	bma150->input = idev;
+	return 0;
+}
+
+static int bma150_register_polled_device(struct bma150_data *bma150)
+{
+	struct input_polled_dev *ipoll_dev;
+	int error;
+
+	ipoll_dev = input_allocate_polled_device();
+	if (!ipoll_dev)
+		return -ENOMEM;
+
+	ipoll_dev->private = bma150;
+	ipoll_dev->open = bma150_poll_open;
+	ipoll_dev->close = bma150_poll_close;
+	ipoll_dev->poll = bma150_poll;
+	ipoll_dev->poll_interval = BMA150_POLL_INTERVAL;
+	ipoll_dev->poll_interval_min = BMA150_POLL_MIN;
+	ipoll_dev->poll_interval_max = BMA150_POLL_MAX;
+
+	bma150_init_input_device(bma150, ipoll_dev->input);
+
+	error = input_register_polled_device(ipoll_dev);
+	if (error) {
+		input_free_polled_device(ipoll_dev);
+		return error;
+	}
+
+	bma150->input_polled = ipoll_dev;
+	bma150->input = ipoll_dev->input;
+
+	return 0;
+}
+
+static int bma150_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	const struct bma150_platform_data *pdata =
+			dev_get_platdata(&client->dev);
+	const struct bma150_cfg *cfg;
+	struct bma150_data *bma150;
+	int chip_id;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "i2c_check_functionality error\n");
+		return -EIO;
+	}
+
+	chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG);
+	if (chip_id != BMA150_CHIP_ID && chip_id != BMA180_CHIP_ID) {
+		dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id);
+		return -EINVAL;
+	}
+
+	bma150 = kzalloc(sizeof(struct bma150_data), GFP_KERNEL);
+	if (!bma150)
+		return -ENOMEM;
+
+	bma150->client = client;
+
+	if (pdata) {
+		if (pdata->irq_gpio_cfg) {
+			error = pdata->irq_gpio_cfg();
+			if (error) {
+				dev_err(&client->dev,
+					"IRQ GPIO conf. error %d, error %d\n",
+					client->irq, error);
+				goto err_free_mem;
+			}
+		}
+		cfg = &pdata->cfg;
+	} else {
+		cfg = &default_cfg;
+	}
+
+	error = bma150_initialize(bma150, cfg);
+	if (error)
+		goto err_free_mem;
+
+	if (client->irq > 0) {
+		error = bma150_register_input_device(bma150);
+		if (error)
+			goto err_free_mem;
+
+		error = request_threaded_irq(client->irq,
+					NULL, bma150_irq_thread,
+					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					BMA150_DRIVER, bma150);
+		if (error) {
+			dev_err(&client->dev,
+				"irq request failed %d, error %d\n",
+				client->irq, error);
+			input_unregister_device(bma150->input);
+			goto err_free_mem;
+		}
+	} else {
+		error = bma150_register_polled_device(bma150);
+		if (error)
+			goto err_free_mem;
+	}
+
+	i2c_set_clientdata(client, bma150);
+
+	pm_runtime_enable(&client->dev);
+
+	return 0;
+
+err_free_mem:
+	kfree(bma150);
+	return error;
+}
+
+static int bma150_remove(struct i2c_client *client)
+{
+	struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+	pm_runtime_disable(&client->dev);
+
+	if (client->irq > 0) {
+		free_irq(client->irq, bma150);
+		input_unregister_device(bma150->input);
+	} else {
+		input_unregister_polled_device(bma150->input_polled);
+		input_free_polled_device(bma150->input_polled);
+	}
+
+	kfree(bma150);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int bma150_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+	return bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+}
+
+static int bma150_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+	return bma150_set_mode(bma150, BMA150_MODE_NORMAL);
+}
+#endif
+
+static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL);
+
+static const struct i2c_device_id bma150_id[] = {
+	{ "bma150", 0 },
+	{ "bma180", 0 },
+	{ "smb380", 0 },
+	{ "bma023", 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, bma150_id);
+
+static struct i2c_driver bma150_driver = {
+	.driver = {
+		.name	= BMA150_DRIVER,
+		.pm	= &bma150_pm,
+	},
+	.class		= I2C_CLASS_HWMON,
+	.id_table	= bma150_id,
+	.probe		= bma150_probe,
+	.remove		= bma150_remove,
+};
+
+module_i2c_driver(bma150_driver);
+
+MODULE_AUTHOR("Albert Zhang <xu.zhang@bosch-sensortec.com>");
+MODULE_DESCRIPTION("BMA150 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
new file mode 100644
index 0000000..50a7faa
--- /dev/null
+++ b/drivers/input/misc/cm109.c
@@ -0,0 +1,928 @@
+/*
+ * Driver for the VoIP USB phones with CM109 chipsets.
+ *
+ * Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
+ *
+ *	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, version 2.
+ */
+
+/*
+ *   Tested devices:
+ *	- Komunikate KIP1000
+ *	- Genius G-talk
+ *	- Allied-Telesis Corega USBPH01
+ *	- ...
+ *
+ * This driver is based on the yealink.c driver
+ *
+ * Thanks to:
+ *   - Authors of yealink.c
+ *   - Thomas Reitmayr
+ *   - Oliver Neukum for good review comments and code
+ *   - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
+ *   - Dmitry Torokhov for valuable input and review
+ *
+ * Todo:
+ *   - Read/write EEPROM
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/rwsem.h>
+#include <linux/usb/input.h>
+
+#define DRIVER_VERSION "20080805"
+#define DRIVER_AUTHOR  "Alfred E. Heggestad"
+#define DRIVER_DESC    "CM109 phone driver"
+
+static char *phone = "kip1000";
+module_param(phone, charp, S_IRUSR);
+MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01, atcom}");
+
+enum {
+	/* HID Registers */
+	HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down  */
+	HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0       */
+	HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1            */
+	HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL             */
+	HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
+	HID_OR1 = 0x01, /* GPO - General Purpose Output                 */
+	HID_OR2 = 0x02, /* Set GPIO to input/output mode                */
+	HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL          */
+
+	/* HID_IR0 */
+	RECORD_MUTE   = 1 << 3,
+	PLAYBACK_MUTE = 1 << 2,
+	VOLUME_DOWN   = 1 << 1,
+	VOLUME_UP     = 1 << 0,
+
+	/* HID_OR0 */
+	/* bits 7-6
+	   0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
+	      and SPDIF
+	   1: HID_OR0-3 are used as generic HID registers
+	   2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
+	      EEPROM_DATA0-1, EEPROM_CTRL (see Note)
+	   3: Reserved
+	 */
+	HID_OR_GPO_BUZ_SPDIF   = 0 << 6,
+	HID_OR_GENERIC_HID_REG = 1 << 6,
+	HID_OR_MAP_MCU_EEPROM  = 2 << 6,
+
+	BUZZER_ON = 1 << 5,
+
+	/* up to 256 normal keys, up to 16 special keys */
+	KEYMAP_SIZE = 256 + 16,
+};
+
+/* CM109 protocol packet */
+struct cm109_ctl_packet {
+	u8 byte[4];
+} __attribute__ ((packed));
+
+enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) };
+
+/* CM109 device structure */
+struct cm109_dev {
+	struct input_dev *idev;	 /* input device */
+	struct usb_device *udev; /* usb device */
+	struct usb_interface *intf;
+
+	/* irq input channel */
+	struct cm109_ctl_packet *irq_data;
+	dma_addr_t irq_dma;
+	struct urb *urb_irq;
+
+	/* control output channel */
+	struct cm109_ctl_packet *ctl_data;
+	dma_addr_t ctl_dma;
+	struct usb_ctrlrequest *ctl_req;
+	struct urb *urb_ctl;
+	/*
+	 * The 3 bitfields below are protected by ctl_submit_lock.
+	 * They have to be separate since they are accessed from IRQ
+	 * context.
+	 */
+	unsigned irq_urb_pending:1;	/* irq_urb is in flight */
+	unsigned ctl_urb_pending:1;	/* ctl_urb is in flight */
+	unsigned buzzer_pending:1;	/* need to issue buzz command */
+	spinlock_t ctl_submit_lock;
+
+	unsigned char buzzer_state;	/* on/off */
+
+	/* flags */
+	unsigned open:1;
+	unsigned resetting:1;
+	unsigned shutdown:1;
+
+	/* This mutex protects writes to the above flags */
+	struct mutex pm_mutex;
+
+	unsigned short keymap[KEYMAP_SIZE];
+
+	char phys[64];		/* physical device path */
+	int key_code;		/* last reported key */
+	int keybit;		/* 0=new scan  1,2,4,8=scan columns  */
+	u8 gpi;			/* Cached value of GPI (high nibble) */
+};
+
+/******************************************************************************
+ * CM109 key interface
+ *****************************************************************************/
+
+static unsigned short special_keymap(int code)
+{
+	if (code > 0xff) {
+		switch (code - 0xff) {
+		case RECORD_MUTE:	return KEY_MUTE;
+		case PLAYBACK_MUTE:	return KEY_MUTE;
+		case VOLUME_DOWN:	return KEY_VOLUMEDOWN;
+		case VOLUME_UP:		return KEY_VOLUMEUP;
+		}
+	}
+	return KEY_RESERVED;
+}
+
+/* Map device buttons to internal key events.
+ *
+ * The "up" and "down" keys, are symbolised by arrows on the button.
+ * The "pickup" and "hangup" keys are symbolised by a green and red phone
+ * on the button.
+
+ Komunikate KIP1000 Keyboard Matrix
+
+     -> -- 1 -- 2 -- 3  --> GPI pin 4 (0x10)
+      |    |    |    |
+     <- -- 4 -- 5 -- 6  --> GPI pin 5 (0x20)
+      |    |    |    |
+     END - 7 -- 8 -- 9  --> GPI pin 6 (0x40)
+      |    |    |    |
+     OK -- * -- 0 -- #  --> GPI pin 7 (0x80)
+      |    |    |    |
+
+     /|\  /|\  /|\  /|\
+      |    |    |    |
+GPO
+pin:  3    2    1    0
+     0x8  0x4  0x2  0x1
+
+ */
+static unsigned short keymap_kip1000(int scancode)
+{
+	switch (scancode) {				/* phone key:   */
+	case 0x82: return KEY_NUMERIC_0;		/*   0          */
+	case 0x14: return KEY_NUMERIC_1;		/*   1          */
+	case 0x12: return KEY_NUMERIC_2;		/*   2          */
+	case 0x11: return KEY_NUMERIC_3;		/*   3          */
+	case 0x24: return KEY_NUMERIC_4;		/*   4          */
+	case 0x22: return KEY_NUMERIC_5;		/*   5          */
+	case 0x21: return KEY_NUMERIC_6;		/*   6          */
+	case 0x44: return KEY_NUMERIC_7;		/*   7          */
+	case 0x42: return KEY_NUMERIC_8;		/*   8          */
+	case 0x41: return KEY_NUMERIC_9;		/*   9          */
+	case 0x81: return KEY_NUMERIC_POUND;		/*   #          */
+	case 0x84: return KEY_NUMERIC_STAR;		/*   *          */
+	case 0x88: return KEY_ENTER;			/*   pickup     */
+	case 0x48: return KEY_ESC;			/*   hangup     */
+	case 0x28: return KEY_LEFT;			/*   IN         */
+	case 0x18: return KEY_RIGHT;			/*   OUT        */
+	default:   return special_keymap(scancode);
+	}
+}
+
+/*
+  Contributed by Shaun Jackman <sjackman@gmail.com>
+
+  Genius G-Talk keyboard matrix
+     0 1 2 3
+  4: 0 4 8 Talk
+  5: 1 5 9 End
+  6: 2 6 # Up
+  7: 3 7 * Down
+*/
+static unsigned short keymap_gtalk(int scancode)
+{
+	switch (scancode) {
+	case 0x11: return KEY_NUMERIC_0;
+	case 0x21: return KEY_NUMERIC_1;
+	case 0x41: return KEY_NUMERIC_2;
+	case 0x81: return KEY_NUMERIC_3;
+	case 0x12: return KEY_NUMERIC_4;
+	case 0x22: return KEY_NUMERIC_5;
+	case 0x42: return KEY_NUMERIC_6;
+	case 0x82: return KEY_NUMERIC_7;
+	case 0x14: return KEY_NUMERIC_8;
+	case 0x24: return KEY_NUMERIC_9;
+	case 0x44: return KEY_NUMERIC_POUND;	/* # */
+	case 0x84: return KEY_NUMERIC_STAR;	/* * */
+	case 0x18: return KEY_ENTER;		/* Talk (green handset) */
+	case 0x28: return KEY_ESC;		/* End (red handset) */
+	case 0x48: return KEY_UP;		/* Menu up (rocker switch) */
+	case 0x88: return KEY_DOWN;		/* Menu down (rocker switch) */
+	default:   return special_keymap(scancode);
+	}
+}
+
+/*
+ * Keymap for Allied-Telesis Corega USBPH01
+ * http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
+ *
+ * Contributed by july@nat.bg
+ */
+static unsigned short keymap_usbph01(int scancode)
+{
+	switch (scancode) {
+	case 0x11: return KEY_NUMERIC_0;		/*   0          */
+	case 0x21: return KEY_NUMERIC_1;		/*   1          */
+	case 0x41: return KEY_NUMERIC_2;		/*   2          */
+	case 0x81: return KEY_NUMERIC_3;		/*   3          */
+	case 0x12: return KEY_NUMERIC_4;		/*   4          */
+	case 0x22: return KEY_NUMERIC_5;		/*   5          */
+	case 0x42: return KEY_NUMERIC_6;		/*   6          */
+	case 0x82: return KEY_NUMERIC_7;		/*   7          */
+	case 0x14: return KEY_NUMERIC_8;		/*   8          */
+	case 0x24: return KEY_NUMERIC_9;		/*   9          */
+	case 0x44: return KEY_NUMERIC_POUND;		/*   #          */
+	case 0x84: return KEY_NUMERIC_STAR;		/*   *          */
+	case 0x18: return KEY_ENTER;			/*   pickup     */
+	case 0x28: return KEY_ESC;			/*   hangup     */
+	case 0x48: return KEY_LEFT;			/*   IN         */
+	case 0x88: return KEY_RIGHT;			/*   OUT        */
+	default:   return special_keymap(scancode);
+	}
+}
+
+/*
+ * Keymap for ATCom AU-100
+ * http://www.atcom.cn/products.html 
+ * http://www.packetizer.com/products/au100/
+ * http://www.voip-info.org/wiki/view/AU-100
+ *
+ * Contributed by daniel@gimpelevich.san-francisco.ca.us
+ */
+static unsigned short keymap_atcom(int scancode)
+{
+	switch (scancode) {				/* phone key:   */
+	case 0x82: return KEY_NUMERIC_0;		/*   0          */
+	case 0x11: return KEY_NUMERIC_1;		/*   1          */
+	case 0x12: return KEY_NUMERIC_2;		/*   2          */
+	case 0x14: return KEY_NUMERIC_3;		/*   3          */
+	case 0x21: return KEY_NUMERIC_4;		/*   4          */
+	case 0x22: return KEY_NUMERIC_5;		/*   5          */
+	case 0x24: return KEY_NUMERIC_6;		/*   6          */
+	case 0x41: return KEY_NUMERIC_7;		/*   7          */
+	case 0x42: return KEY_NUMERIC_8;		/*   8          */
+	case 0x44: return KEY_NUMERIC_9;		/*   9          */
+	case 0x84: return KEY_NUMERIC_POUND;		/*   #          */
+	case 0x81: return KEY_NUMERIC_STAR;		/*   *          */
+	case 0x18: return KEY_ENTER;			/*   pickup     */
+	case 0x28: return KEY_ESC;			/*   hangup     */
+	case 0x48: return KEY_LEFT;			/* left arrow   */
+	case 0x88: return KEY_RIGHT;			/* right arrow  */
+	default:   return special_keymap(scancode);
+	}
+}
+
+static unsigned short (*keymap)(int) = keymap_kip1000;
+
+/*
+ * Completes a request by converting the data into events for the
+ * input subsystem.
+ */
+static void report_key(struct cm109_dev *dev, int key)
+{
+	struct input_dev *idev = dev->idev;
+
+	if (dev->key_code >= 0) {
+		/* old key up */
+		input_report_key(idev, dev->key_code, 0);
+	}
+
+	dev->key_code = key;
+	if (key >= 0) {
+		/* new valid key */
+		input_report_key(idev, key, 1);
+	}
+
+	input_sync(idev);
+}
+
+/******************************************************************************
+ * CM109 usb communication interface
+ *****************************************************************************/
+
+static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
+{
+	int error;
+
+	if (dev->buzzer_state)
+		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+	else
+		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+	error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+	if (error)
+		dev_err(&dev->intf->dev,
+			"%s: usb_submit_urb (urb_ctl) failed %d\n",
+			__func__, error);
+}
+
+/*
+ * IRQ handler
+ */
+static void cm109_urb_irq_callback(struct urb *urb)
+{
+	struct cm109_dev *dev = urb->context;
+	const int status = urb->status;
+	int error;
+
+	dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",
+	     dev->irq_data->byte[0],
+	     dev->irq_data->byte[1],
+	     dev->irq_data->byte[2],
+	     dev->irq_data->byte[3],
+	     dev->keybit);
+
+	if (status) {
+		if (status == -ESHUTDOWN)
+			return;
+		dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
+				    __func__, status);
+		goto out;
+	}
+
+	/* Special keys */
+	if (dev->irq_data->byte[HID_IR0] & 0x0f) {
+		const int code = (dev->irq_data->byte[HID_IR0] & 0x0f);
+		report_key(dev, dev->keymap[0xff + code]);
+	}
+
+	/* Scan key column */
+	if (dev->keybit == 0xf) {
+
+		/* Any changes ? */
+		if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
+			goto out;
+
+		dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
+		dev->keybit = 0x1;
+	} else {
+		report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
+
+		dev->keybit <<= 1;
+		if (dev->keybit > 0x8)
+			dev->keybit = 0xf;
+	}
+
+ out:
+
+	spin_lock(&dev->ctl_submit_lock);
+
+	dev->irq_urb_pending = 0;
+
+	if (likely(!dev->shutdown)) {
+
+		if (dev->buzzer_state)
+			dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+		else
+			dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+		dev->ctl_data->byte[HID_OR1] = dev->keybit;
+		dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
+		dev->buzzer_pending = 0;
+		dev->ctl_urb_pending = 1;
+
+		error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+		if (error)
+			dev_err(&dev->intf->dev,
+				"%s: usb_submit_urb (urb_ctl) failed %d\n",
+				__func__, error);
+	}
+
+	spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_urb_ctl_callback(struct urb *urb)
+{
+	struct cm109_dev *dev = urb->context;
+	const int status = urb->status;
+	int error;
+
+	dev_dbg(&dev->intf->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
+	     dev->ctl_data->byte[0],
+	     dev->ctl_data->byte[1],
+	     dev->ctl_data->byte[2],
+	     dev->ctl_data->byte[3]);
+
+	if (status) {
+		if (status == -ESHUTDOWN)
+			return;
+		dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
+				    __func__, status);
+	}
+
+	spin_lock(&dev->ctl_submit_lock);
+
+	dev->ctl_urb_pending = 0;
+
+	if (likely(!dev->shutdown)) {
+
+		if (dev->buzzer_pending || status) {
+			dev->buzzer_pending = 0;
+			dev->ctl_urb_pending = 1;
+			cm109_submit_buzz_toggle(dev);
+		} else if (likely(!dev->irq_urb_pending)) {
+			/* ask for key data */
+			dev->irq_urb_pending = 1;
+			error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
+			if (error)
+				dev_err(&dev->intf->dev,
+					"%s: usb_submit_urb (urb_irq) failed %d\n",
+					__func__, error);
+		}
+	}
+
+	spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->ctl_submit_lock, flags);
+
+	if (dev->ctl_urb_pending) {
+		/* URB completion will resubmit */
+		dev->buzzer_pending = 1;
+	} else {
+		dev->ctl_urb_pending = 1;
+		cm109_submit_buzz_toggle(dev);
+	}
+
+	spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
+}
+
+static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
+{
+	int error;
+
+	if (on)
+		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+	else
+		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+	error = usb_control_msg(dev->udev,
+				usb_sndctrlpipe(dev->udev, 0),
+				dev->ctl_req->bRequest,
+				dev->ctl_req->bRequestType,
+				le16_to_cpu(dev->ctl_req->wValue),
+				le16_to_cpu(dev->ctl_req->wIndex),
+				dev->ctl_data,
+				USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
+	if (error < 0 && error != -EINTR)
+		dev_err(&dev->intf->dev, "%s: usb_control_msg() failed %d\n",
+			__func__, error);
+}
+
+static void cm109_stop_traffic(struct cm109_dev *dev)
+{
+	dev->shutdown = 1;
+	/*
+	 * Make sure other CPUs see this
+	 */
+	smp_wmb();
+
+	usb_kill_urb(dev->urb_ctl);
+	usb_kill_urb(dev->urb_irq);
+
+	cm109_toggle_buzzer_sync(dev, 0);
+
+	dev->shutdown = 0;
+	smp_wmb();
+}
+
+static void cm109_restore_state(struct cm109_dev *dev)
+{
+	if (dev->open) {
+		/*
+		 * Restore buzzer state.
+		 * This will also kick regular URB submission
+		 */
+		cm109_toggle_buzzer_async(dev);
+	}
+}
+
+/******************************************************************************
+ * input event interface
+ *****************************************************************************/
+
+static int cm109_input_open(struct input_dev *idev)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+	int error;
+
+	error = usb_autopm_get_interface(dev->intf);
+	if (error < 0) {
+		dev_err(&idev->dev, "%s - cannot autoresume, result %d\n",
+			__func__, error);
+		return error;
+	}
+
+	mutex_lock(&dev->pm_mutex);
+
+	dev->buzzer_state = 0;
+	dev->key_code = -1;	/* no keys pressed */
+	dev->keybit = 0xf;
+
+	/* issue INIT */
+	dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
+	dev->ctl_data->byte[HID_OR1] = dev->keybit;
+	dev->ctl_data->byte[HID_OR2] = dev->keybit;
+	dev->ctl_data->byte[HID_OR3] = 0x00;
+
+	error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
+	if (error)
+		dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
+			__func__, error);
+	else
+		dev->open = 1;
+
+	mutex_unlock(&dev->pm_mutex);
+
+	if (error)
+		usb_autopm_put_interface(dev->intf);
+
+	return error;
+}
+
+static void cm109_input_close(struct input_dev *idev)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+
+	mutex_lock(&dev->pm_mutex);
+
+	/*
+	 * Once we are here event delivery is stopped so we
+	 * don't need to worry about someone starting buzzer
+	 * again
+	 */
+	cm109_stop_traffic(dev);
+	dev->open = 0;
+
+	mutex_unlock(&dev->pm_mutex);
+
+	usb_autopm_put_interface(dev->intf);
+}
+
+static int cm109_input_ev(struct input_dev *idev, unsigned int type,
+			  unsigned int code, int value)
+{
+	struct cm109_dev *dev = input_get_drvdata(idev);
+
+	dev_dbg(&dev->intf->dev,
+		"input_ev: type=%u code=%u value=%d\n", type, code, value);
+
+	if (type != EV_SND)
+		return -EINVAL;
+
+	switch (code) {
+	case SND_TONE:
+	case SND_BELL:
+		dev->buzzer_state = !!value;
+		if (!dev->resetting)
+			cm109_toggle_buzzer_async(dev);
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+
+/******************************************************************************
+ * Linux interface and usb initialisation
+ *****************************************************************************/
+
+struct driver_info {
+	char *name;
+};
+
+static const struct driver_info info_cm109 = {
+	.name = "CM109 USB driver",
+};
+
+enum {
+	VENDOR_ID        = 0x0d8c, /* C-Media Electronics */
+	PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
+};
+
+/* table of devices that work with this driver */
+static const struct usb_device_id cm109_usb_table[] = {
+	{
+		.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+				USB_DEVICE_ID_MATCH_INT_INFO,
+		.idVendor = VENDOR_ID,
+		.idProduct = PRODUCT_ID_CM109,
+		.bInterfaceClass = USB_CLASS_HID,
+		.bInterfaceSubClass = 0,
+		.bInterfaceProtocol = 0,
+		.driver_info = (kernel_ulong_t) &info_cm109
+	},
+	/* you can add more devices here with product ID 0x0008 - 0x000f */
+	{ }
+};
+
+static void cm109_usb_cleanup(struct cm109_dev *dev)
+{
+	kfree(dev->ctl_req);
+	if (dev->ctl_data)
+		usb_free_coherent(dev->udev, USB_PKT_LEN,
+				  dev->ctl_data, dev->ctl_dma);
+	if (dev->irq_data)
+		usb_free_coherent(dev->udev, USB_PKT_LEN,
+				  dev->irq_data, dev->irq_dma);
+
+	usb_free_urb(dev->urb_irq);	/* parameter validation in core/urb */
+	usb_free_urb(dev->urb_ctl);	/* parameter validation in core/urb */
+	kfree(dev);
+}
+
+static void cm109_usb_disconnect(struct usb_interface *interface)
+{
+	struct cm109_dev *dev = usb_get_intfdata(interface);
+
+	usb_set_intfdata(interface, NULL);
+	input_unregister_device(dev->idev);
+	cm109_usb_cleanup(dev);
+}
+
+static int cm109_usb_probe(struct usb_interface *intf,
+			   const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct driver_info *nfo = (struct driver_info *)id->driver_info;
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct cm109_dev *dev;
+	struct input_dev *input_dev = NULL;
+	int ret, pipe, i;
+	int error = -ENOMEM;
+
+	interface = intf->cur_altsetting;
+
+	if (interface->desc.bNumEndpoints < 1)
+		return -ENODEV;
+
+	endpoint = &interface->endpoint[0].desc;
+
+	if (!usb_endpoint_is_int_in(endpoint))
+		return -ENODEV;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	spin_lock_init(&dev->ctl_submit_lock);
+	mutex_init(&dev->pm_mutex);
+
+	dev->udev = udev;
+	dev->intf = intf;
+
+	dev->idev = input_dev = input_allocate_device();
+	if (!input_dev)
+		goto err_out;
+
+	/* allocate usb buffers */
+	dev->irq_data = usb_alloc_coherent(udev, USB_PKT_LEN,
+					   GFP_KERNEL, &dev->irq_dma);
+	if (!dev->irq_data)
+		goto err_out;
+
+	dev->ctl_data = usb_alloc_coherent(udev, USB_PKT_LEN,
+					   GFP_KERNEL, &dev->ctl_dma);
+	if (!dev->ctl_data)
+		goto err_out;
+
+	dev->ctl_req = kmalloc(sizeof(*(dev->ctl_req)), GFP_KERNEL);
+	if (!dev->ctl_req)
+		goto err_out;
+
+	/* allocate urb structures */
+	dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->urb_irq)
+		goto err_out;
+
+	dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->urb_ctl)
+		goto err_out;
+
+	/* get a handle to the interrupt data pipe */
+	pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+	ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+	if (ret != USB_PKT_LEN)
+		dev_err(&intf->dev, "invalid payload size %d, expected %d\n",
+			ret, USB_PKT_LEN);
+
+	/* initialise irq urb */
+	usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
+			 USB_PKT_LEN,
+			 cm109_urb_irq_callback, dev, endpoint->bInterval);
+	dev->urb_irq->transfer_dma = dev->irq_dma;
+	dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	dev->urb_irq->dev = udev;
+
+	/* initialise ctl urb */
+	dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+					USB_DIR_OUT;
+	dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
+	dev->ctl_req->wValue = cpu_to_le16(0x200);
+	dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
+	dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
+
+	usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
+			     (void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
+			     cm109_urb_ctl_callback, dev);
+	dev->urb_ctl->transfer_dma = dev->ctl_dma;
+	dev->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	dev->urb_ctl->dev = udev;
+
+	/* find out the physical bus location */
+	usb_make_path(udev, dev->phys, sizeof(dev->phys));
+	strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+	/* register settings for the input device */
+	input_dev->name = nfo->name;
+	input_dev->phys = dev->phys;
+	usb_to_input_id(udev, &input_dev->id);
+	input_dev->dev.parent = &intf->dev;
+
+	input_set_drvdata(input_dev, dev);
+	input_dev->open = cm109_input_open;
+	input_dev->close = cm109_input_close;
+	input_dev->event = cm109_input_ev;
+
+	input_dev->keycode = dev->keymap;
+	input_dev->keycodesize = sizeof(unsigned char);
+	input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
+	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+
+	/* register available key events */
+	for (i = 0; i < KEYMAP_SIZE; i++) {
+		unsigned short k = keymap(i);
+		dev->keymap[i] = k;
+		__set_bit(k, input_dev->keybit);
+	}
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+
+	error = input_register_device(dev->idev);
+	if (error)
+		goto err_out;
+
+	usb_set_intfdata(intf, dev);
+
+	return 0;
+
+ err_out:
+	input_free_device(input_dev);
+	cm109_usb_cleanup(dev);
+	return error;
+}
+
+static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event);
+
+	mutex_lock(&dev->pm_mutex);
+	cm109_stop_traffic(dev);
+	mutex_unlock(&dev->pm_mutex);
+
+	return 0;
+}
+
+static int cm109_usb_resume(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	dev_info(&intf->dev, "cm109: usb_resume\n");
+
+	mutex_lock(&dev->pm_mutex);
+	cm109_restore_state(dev);
+	mutex_unlock(&dev->pm_mutex);
+
+	return 0;
+}
+
+static int cm109_usb_pre_reset(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	mutex_lock(&dev->pm_mutex);
+
+	/*
+	 * Make sure input events don't try to toggle buzzer
+	 * while we are resetting
+	 */
+	dev->resetting = 1;
+	smp_wmb();
+
+	cm109_stop_traffic(dev);
+
+	return 0;
+}
+
+static int cm109_usb_post_reset(struct usb_interface *intf)
+{
+	struct cm109_dev *dev = usb_get_intfdata(intf);
+
+	dev->resetting = 0;
+	smp_wmb();
+
+	cm109_restore_state(dev);
+
+	mutex_unlock(&dev->pm_mutex);
+
+	return 0;
+}
+
+static struct usb_driver cm109_driver = {
+	.name		= "cm109",
+	.probe		= cm109_usb_probe,
+	.disconnect	= cm109_usb_disconnect,
+	.suspend	= cm109_usb_suspend,
+	.resume		= cm109_usb_resume,
+	.reset_resume	= cm109_usb_resume,
+	.pre_reset	= cm109_usb_pre_reset,
+	.post_reset	= cm109_usb_post_reset,
+	.id_table	= cm109_usb_table,
+	.supports_autosuspend = 1,
+};
+
+static int __init cm109_select_keymap(void)
+{
+	/* Load the phone keymap */
+	if (!strcasecmp(phone, "kip1000")) {
+		keymap = keymap_kip1000;
+		printk(KERN_INFO KBUILD_MODNAME ": "
+			"Keymap for Komunikate KIP1000 phone loaded\n");
+	} else if (!strcasecmp(phone, "gtalk")) {
+		keymap = keymap_gtalk;
+		printk(KERN_INFO KBUILD_MODNAME ": "
+			"Keymap for Genius G-talk phone loaded\n");
+	} else if (!strcasecmp(phone, "usbph01")) {
+		keymap = keymap_usbph01;
+		printk(KERN_INFO KBUILD_MODNAME ": "
+			"Keymap for Allied-Telesis Corega USBPH01 phone loaded\n");
+	} else if (!strcasecmp(phone, "atcom")) {
+		keymap = keymap_atcom;
+		printk(KERN_INFO KBUILD_MODNAME ": "
+			"Keymap for ATCom AU-100 phone loaded\n");
+	} else {
+		printk(KERN_ERR KBUILD_MODNAME ": "
+			"Unsupported phone: %s\n", phone);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __init cm109_init(void)
+{
+	int err;
+
+	err = cm109_select_keymap();
+	if (err)
+		return err;
+
+	err = usb_register(&cm109_driver);
+	if (err)
+		return err;
+
+	printk(KERN_INFO KBUILD_MODNAME ": "
+		DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR "\n");
+
+	return 0;
+}
+
+static void __exit cm109_exit(void)
+{
+	usb_deregister(&cm109_driver);
+}
+
+module_init(cm109_init);
+module_exit(cm109_exit);
+
+MODULE_DEVICE_TABLE(usb, cm109_usb_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/cma3000_d0x.c b/drivers/input/misc/cma3000_d0x.c
new file mode 100644
index 0000000..c7d0074
--- /dev/null
+++ b/drivers/input/misc/cma3000_d0x.c
@@ -0,0 +1,399 @@
+/*
+ * VTI CMA3000_D0x Accelerometer driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Hemanth V <hemanthv@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/cma3000.h>
+#include <linux/module.h>
+
+#include "cma3000_d0x.h"
+
+#define CMA3000_WHOAMI      0x00
+#define CMA3000_REVID       0x01
+#define CMA3000_CTRL        0x02
+#define CMA3000_STATUS      0x03
+#define CMA3000_RSTR        0x04
+#define CMA3000_INTSTATUS   0x05
+#define CMA3000_DOUTX       0x06
+#define CMA3000_DOUTY       0x07
+#define CMA3000_DOUTZ       0x08
+#define CMA3000_MDTHR       0x09
+#define CMA3000_MDFFTMR     0x0A
+#define CMA3000_FFTHR       0x0B
+
+#define CMA3000_RANGE2G    (1 << 7)
+#define CMA3000_RANGE8G    (0 << 7)
+#define CMA3000_BUSI2C     (0 << 4)
+#define CMA3000_MODEMASK   (7 << 1)
+#define CMA3000_GRANGEMASK (1 << 7)
+
+#define CMA3000_STATUS_PERR    1
+#define CMA3000_INTSTATUS_FFDET (1 << 2)
+
+/* Settling time delay in ms */
+#define CMA3000_SETDELAY    30
+
+/* Delay for clearing interrupt in us */
+#define CMA3000_INTDELAY    44
+
+
+/*
+ * Bit weights in mg for bit 0, other bits need
+ * multiply factor 2^n. Eight bit is the sign bit.
+ */
+#define BIT_TO_2G  18
+#define BIT_TO_8G  71
+
+struct cma3000_accl_data {
+	const struct cma3000_bus_ops *bus_ops;
+	const struct cma3000_platform_data *pdata;
+
+	struct device *dev;
+	struct input_dev *input_dev;
+
+	int bit_to_mg;
+	int irq;
+
+	int g_range;
+	u8 mode;
+
+	struct mutex mutex;
+	bool opened;
+	bool suspended;
+};
+
+#define CMA3000_READ(data, reg, msg) \
+	(data->bus_ops->read(data->dev, reg, msg))
+#define CMA3000_SET(data, reg, val, msg) \
+	((data)->bus_ops->write(data->dev, reg, val, msg))
+
+/*
+ * Conversion for each of the eight modes to g, depending
+ * on G range i.e 2G or 8G. Some modes always operate in
+ * 8G.
+ */
+
+static int mode_to_mg[8][2] = {
+	{ 0, 0 },
+	{ BIT_TO_8G, BIT_TO_2G },
+	{ BIT_TO_8G, BIT_TO_2G },
+	{ BIT_TO_8G, BIT_TO_8G },
+	{ BIT_TO_8G, BIT_TO_8G },
+	{ BIT_TO_8G, BIT_TO_2G },
+	{ BIT_TO_8G, BIT_TO_2G },
+	{ 0, 0},
+};
+
+static void decode_mg(struct cma3000_accl_data *data, int *datax,
+				int *datay, int *dataz)
+{
+	/* Data in 2's complement, convert to mg */
+	*datax = ((s8)*datax) * data->bit_to_mg;
+	*datay = ((s8)*datay) * data->bit_to_mg;
+	*dataz = ((s8)*dataz) * data->bit_to_mg;
+}
+
+static irqreturn_t cma3000_thread_irq(int irq, void *dev_id)
+{
+	struct cma3000_accl_data *data = dev_id;
+	int datax, datay, dataz, intr_status;
+	u8 ctrl, mode, range;
+
+	intr_status = CMA3000_READ(data, CMA3000_INTSTATUS, "interrupt status");
+	if (intr_status < 0)
+		return IRQ_NONE;
+
+	/* Check if free fall is detected, report immediately */
+	if (intr_status & CMA3000_INTSTATUS_FFDET) {
+		input_report_abs(data->input_dev, ABS_MISC, 1);
+		input_sync(data->input_dev);
+	} else {
+		input_report_abs(data->input_dev, ABS_MISC, 0);
+	}
+
+	datax = CMA3000_READ(data, CMA3000_DOUTX, "X");
+	datay = CMA3000_READ(data, CMA3000_DOUTY, "Y");
+	dataz = CMA3000_READ(data, CMA3000_DOUTZ, "Z");
+
+	ctrl = CMA3000_READ(data, CMA3000_CTRL, "ctrl");
+	mode = (ctrl & CMA3000_MODEMASK) >> 1;
+	range = (ctrl & CMA3000_GRANGEMASK) >> 7;
+
+	data->bit_to_mg = mode_to_mg[mode][range];
+
+	/* Interrupt not for this device */
+	if (data->bit_to_mg == 0)
+		return IRQ_NONE;
+
+	/* Decode register values to milli g */
+	decode_mg(data, &datax, &datay, &dataz);
+
+	input_report_abs(data->input_dev, ABS_X, datax);
+	input_report_abs(data->input_dev, ABS_Y, datay);
+	input_report_abs(data->input_dev, ABS_Z, dataz);
+	input_sync(data->input_dev);
+
+	return IRQ_HANDLED;
+}
+
+static int cma3000_reset(struct cma3000_accl_data *data)
+{
+	int val;
+
+	/* Reset sequence */
+	CMA3000_SET(data, CMA3000_RSTR, 0x02, "Reset");
+	CMA3000_SET(data, CMA3000_RSTR, 0x0A, "Reset");
+	CMA3000_SET(data, CMA3000_RSTR, 0x04, "Reset");
+
+	/* Settling time delay */
+	mdelay(10);
+
+	val = CMA3000_READ(data, CMA3000_STATUS, "Status");
+	if (val < 0) {
+		dev_err(data->dev, "Reset failed\n");
+		return val;
+	}
+
+	if (val & CMA3000_STATUS_PERR) {
+		dev_err(data->dev, "Parity Error\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int cma3000_poweron(struct cma3000_accl_data *data)
+{
+	const struct cma3000_platform_data *pdata = data->pdata;
+	u8 ctrl = 0;
+	int ret;
+
+	if (data->g_range == CMARANGE_2G) {
+		ctrl = (data->mode << 1) | CMA3000_RANGE2G;
+	} else if (data->g_range == CMARANGE_8G) {
+		ctrl = (data->mode << 1) | CMA3000_RANGE8G;
+	} else {
+		dev_info(data->dev,
+			 "Invalid G range specified, assuming 8G\n");
+		ctrl = (data->mode << 1) | CMA3000_RANGE8G;
+	}
+
+	ctrl |= data->bus_ops->ctrl_mod;
+
+	CMA3000_SET(data, CMA3000_MDTHR, pdata->mdthr,
+		    "Motion Detect Threshold");
+	CMA3000_SET(data, CMA3000_MDFFTMR, pdata->mdfftmr,
+		    "Time register");
+	CMA3000_SET(data, CMA3000_FFTHR, pdata->ffthr,
+		    "Free fall threshold");
+	ret = CMA3000_SET(data, CMA3000_CTRL, ctrl, "Mode setting");
+	if (ret < 0)
+		return -EIO;
+
+	msleep(CMA3000_SETDELAY);
+
+	return 0;
+}
+
+static int cma3000_poweroff(struct cma3000_accl_data *data)
+{
+	int ret;
+
+	ret = CMA3000_SET(data, CMA3000_CTRL, CMAMODE_POFF, "Mode setting");
+	msleep(CMA3000_SETDELAY);
+
+	return ret;
+}
+
+static int cma3000_open(struct input_dev *input_dev)
+{
+	struct cma3000_accl_data *data = input_get_drvdata(input_dev);
+
+	mutex_lock(&data->mutex);
+
+	if (!data->suspended)
+		cma3000_poweron(data);
+
+	data->opened = true;
+
+	mutex_unlock(&data->mutex);
+
+	return 0;
+}
+
+static void cma3000_close(struct input_dev *input_dev)
+{
+	struct cma3000_accl_data *data = input_get_drvdata(input_dev);
+
+	mutex_lock(&data->mutex);
+
+	if (!data->suspended)
+		cma3000_poweroff(data);
+
+	data->opened = false;
+
+	mutex_unlock(&data->mutex);
+}
+
+void cma3000_suspend(struct cma3000_accl_data *data)
+{
+	mutex_lock(&data->mutex);
+
+	if (!data->suspended && data->opened)
+		cma3000_poweroff(data);
+
+	data->suspended = true;
+
+	mutex_unlock(&data->mutex);
+}
+EXPORT_SYMBOL(cma3000_suspend);
+
+
+void cma3000_resume(struct cma3000_accl_data *data)
+{
+	mutex_lock(&data->mutex);
+
+	if (data->suspended && data->opened)
+		cma3000_poweron(data);
+
+	data->suspended = false;
+
+	mutex_unlock(&data->mutex);
+}
+EXPORT_SYMBOL(cma3000_resume);
+
+struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,
+				       const struct cma3000_bus_ops *bops)
+{
+	const struct cma3000_platform_data *pdata = dev_get_platdata(dev);
+	struct cma3000_accl_data *data;
+	struct input_dev *input_dev;
+	int rev;
+	int error;
+
+	if (!pdata) {
+		dev_err(dev, "platform data not found\n");
+		error = -EINVAL;
+		goto err_out;
+	}
+
+
+	/* if no IRQ return error */
+	if (irq == 0) {
+		error = -EINVAL;
+		goto err_out;
+	}
+
+	data = kzalloc(sizeof(struct cma3000_accl_data), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!data || !input_dev) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	data->dev = dev;
+	data->input_dev = input_dev;
+	data->bus_ops = bops;
+	data->pdata = pdata;
+	data->irq = irq;
+	mutex_init(&data->mutex);
+
+	data->mode = pdata->mode;
+	if (data->mode > CMAMODE_POFF) {
+		data->mode = CMAMODE_MOTDET;
+		dev_warn(dev,
+			 "Invalid mode specified, assuming Motion Detect\n");
+	}
+
+	data->g_range = pdata->g_range;
+	if (data->g_range != CMARANGE_2G && data->g_range != CMARANGE_8G) {
+		dev_info(dev,
+			 "Invalid G range specified, assuming 8G\n");
+		data->g_range = CMARANGE_8G;
+	}
+
+	input_dev->name = "cma3000-accelerometer";
+	input_dev->id.bustype = bops->bustype;
+	input_dev->open = cma3000_open;
+	input_dev->close = cma3000_close;
+
+	 __set_bit(EV_ABS, input_dev->evbit);
+
+	input_set_abs_params(input_dev, ABS_X,
+			-data->g_range, data->g_range, pdata->fuzz_x, 0);
+	input_set_abs_params(input_dev, ABS_Y,
+			-data->g_range, data->g_range, pdata->fuzz_y, 0);
+	input_set_abs_params(input_dev, ABS_Z,
+			-data->g_range, data->g_range, pdata->fuzz_z, 0);
+	input_set_abs_params(input_dev, ABS_MISC, 0, 1, 0, 0);
+
+	input_set_drvdata(input_dev, data);
+
+	error = cma3000_reset(data);
+	if (error)
+		goto err_free_mem;
+
+	rev = CMA3000_READ(data, CMA3000_REVID, "Revid");
+	if (rev < 0) {
+		error = rev;
+		goto err_free_mem;
+	}
+
+	pr_info("CMA3000 Accelerometer: Revision %x\n", rev);
+
+	error = request_threaded_irq(irq, NULL, cma3000_thread_irq,
+				     pdata->irqflags | IRQF_ONESHOT,
+				     "cma3000_d0x", data);
+	if (error) {
+		dev_err(dev, "request_threaded_irq failed\n");
+		goto err_free_mem;
+	}
+
+	error = input_register_device(data->input_dev);
+	if (error) {
+		dev_err(dev, "Unable to register input device\n");
+		goto err_free_irq;
+	}
+
+	return data;
+
+err_free_irq:
+	free_irq(irq, data);
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(data);
+err_out:
+	return ERR_PTR(error);
+}
+EXPORT_SYMBOL(cma3000_init);
+
+void cma3000_exit(struct cma3000_accl_data *data)
+{
+	free_irq(data->irq, data);
+	input_unregister_device(data->input_dev);
+	kfree(data);
+}
+EXPORT_SYMBOL(cma3000_exit);
+
+MODULE_DESCRIPTION("CMA3000-D0x Accelerometer Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>");
diff --git a/drivers/input/misc/cma3000_d0x.h b/drivers/input/misc/cma3000_d0x.h
new file mode 100644
index 0000000..2304ce3
--- /dev/null
+++ b/drivers/input/misc/cma3000_d0x.h
@@ -0,0 +1,42 @@
+/*
+ * VTI CMA3000_D0x Accelerometer driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Hemanth V <hemanthv@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _INPUT_CMA3000_H
+#define _INPUT_CMA3000_H
+
+#include <linux/types.h>
+#include <linux/input.h>
+
+struct device;
+struct cma3000_accl_data;
+
+struct cma3000_bus_ops {
+	u16 bustype;
+	u8 ctrl_mod;
+	int (*read)(struct device *, u8, char *);
+	int (*write)(struct device *, u8, u8, char *);
+};
+
+struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,
+					const struct cma3000_bus_ops *bops);
+void cma3000_exit(struct cma3000_accl_data *);
+void cma3000_suspend(struct cma3000_accl_data *);
+void cma3000_resume(struct cma3000_accl_data *);
+
+#endif
diff --git a/drivers/input/misc/cma3000_d0x_i2c.c b/drivers/input/misc/cma3000_d0x_i2c.c
new file mode 100644
index 0000000..c702191
--- /dev/null
+++ b/drivers/input/misc/cma3000_d0x_i2c.c
@@ -0,0 +1,131 @@
+/*
+ * Implements I2C interface for VTI CMA300_D0x Accelerometer driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Hemanth V <hemanthv@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input/cma3000.h>
+#include "cma3000_d0x.h"
+
+static int cma3000_i2c_set(struct device *dev,
+			   u8 reg, u8 val, char *msg)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, reg, val);
+	if (ret < 0)
+		dev_err(&client->dev,
+			"%s failed (%s, %d)\n", __func__, msg, ret);
+	return ret;
+}
+
+static int cma3000_i2c_read(struct device *dev, u8 reg, char *msg)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0)
+		dev_err(&client->dev,
+			"%s failed (%s, %d)\n", __func__, msg, ret);
+	return ret;
+}
+
+static const struct cma3000_bus_ops cma3000_i2c_bops = {
+	.bustype	= BUS_I2C,
+#define CMA3000_BUSI2C     (0 << 4)
+	.ctrl_mod	= CMA3000_BUSI2C,
+	.read		= cma3000_i2c_read,
+	.write		= cma3000_i2c_set,
+};
+
+static int cma3000_i2c_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	struct cma3000_accl_data *data;
+
+	data = cma3000_init(&client->dev, client->irq, &cma3000_i2c_bops);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	i2c_set_clientdata(client, data);
+
+	return 0;
+}
+
+static int cma3000_i2c_remove(struct i2c_client *client)
+{
+	struct cma3000_accl_data *data = i2c_get_clientdata(client);
+
+	cma3000_exit(data);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int cma3000_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct cma3000_accl_data *data = i2c_get_clientdata(client);
+
+	cma3000_suspend(data);
+
+	return 0;
+}
+
+static int cma3000_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct cma3000_accl_data *data = i2c_get_clientdata(client);
+
+	cma3000_resume(data);
+
+	return 0;
+}
+
+static const struct dev_pm_ops cma3000_i2c_pm_ops = {
+	.suspend	= cma3000_i2c_suspend,
+	.resume		= cma3000_i2c_resume,
+};
+#endif
+
+static const struct i2c_device_id cma3000_i2c_id[] = {
+	{ "cma3000_d01", 0 },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(i2c, cma3000_i2c_id);
+
+static struct i2c_driver cma3000_i2c_driver = {
+	.probe		= cma3000_i2c_probe,
+	.remove		= cma3000_i2c_remove,
+	.id_table	= cma3000_i2c_id,
+	.driver = {
+		.name	= "cma3000_i2c_accl",
+#ifdef CONFIG_PM
+		.pm	= &cma3000_i2c_pm_ops,
+#endif
+	},
+};
+
+module_i2c_driver(cma3000_i2c_driver);
+
+MODULE_DESCRIPTION("CMA3000-D0x Accelerometer I2C Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>");
diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c
new file mode 100644
index 0000000..fbe72af
--- /dev/null
+++ b/drivers/input/misc/cobalt_btns.c
@@ -0,0 +1,162 @@
+/*
+ *  Cobalt button interface driver.
+ *
+ *  Copyright (C) 2007-2008  Yoichi Yuasa <yuasa@linux-mips.org>
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define BUTTONS_POLL_INTERVAL	30	/* msec */
+#define BUTTONS_COUNT_THRESHOLD	3
+#define BUTTONS_STATUS_MASK	0xfe000000
+
+static const unsigned short cobalt_map[] = {
+	KEY_RESERVED,
+	KEY_RESTART,
+	KEY_LEFT,
+	KEY_UP,
+	KEY_DOWN,
+	KEY_RIGHT,
+	KEY_ENTER,
+	KEY_SELECT
+};
+
+struct buttons_dev {
+	struct input_polled_dev *poll_dev;
+	unsigned short keymap[ARRAY_SIZE(cobalt_map)];
+	int count[ARRAY_SIZE(cobalt_map)];
+	void __iomem *reg;
+};
+
+static void handle_buttons(struct input_polled_dev *dev)
+{
+	struct buttons_dev *bdev = dev->private;
+	struct input_dev *input = dev->input;
+	uint32_t status;
+	int i;
+
+	status = ~readl(bdev->reg) >> 24;
+
+	for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
+		if (status & (1U << i)) {
+			if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
+				input_event(input, EV_MSC, MSC_SCAN, i);
+				input_report_key(input, bdev->keymap[i], 1);
+				input_sync(input);
+			}
+		} else {
+			if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
+				input_event(input, EV_MSC, MSC_SCAN, i);
+				input_report_key(input, bdev->keymap[i], 0);
+				input_sync(input);
+			}
+			bdev->count[i] = 0;
+		}
+	}
+}
+
+static int cobalt_buttons_probe(struct platform_device *pdev)
+{
+	struct buttons_dev *bdev;
+	struct input_polled_dev *poll_dev;
+	struct input_dev *input;
+	struct resource *res;
+	int error, i;
+
+	bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
+	poll_dev = input_allocate_polled_device();
+	if (!bdev || !poll_dev) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap));
+
+	poll_dev->private = bdev;
+	poll_dev->poll = handle_buttons;
+	poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
+
+	input = poll_dev->input;
+	input->name = "Cobalt buttons";
+	input->phys = "cobalt/input0";
+	input->id.bustype = BUS_HOST;
+	input->dev.parent = &pdev->dev;
+
+	input->keycode = bdev->keymap;
+	input->keycodemax = ARRAY_SIZE(bdev->keymap);
+	input->keycodesize = sizeof(unsigned short);
+
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+	__set_bit(EV_KEY, input->evbit);
+	for (i = 0; i < ARRAY_SIZE(cobalt_map); i++)
+		__set_bit(bdev->keymap[i], input->keybit);
+	__clear_bit(KEY_RESERVED, input->keybit);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		error = -EBUSY;
+		goto err_free_mem;
+	}
+
+	bdev->poll_dev = poll_dev;
+	bdev->reg = ioremap(res->start, resource_size(res));
+	dev_set_drvdata(&pdev->dev, bdev);
+
+	error = input_register_polled_device(poll_dev);
+	if (error)
+		goto err_iounmap;
+
+	return 0;
+
+ err_iounmap:
+	iounmap(bdev->reg);
+ err_free_mem:
+	input_free_polled_device(poll_dev);
+	kfree(bdev);
+	return error;
+}
+
+static int cobalt_buttons_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct buttons_dev *bdev = dev_get_drvdata(dev);
+
+	input_unregister_polled_device(bdev->poll_dev);
+	input_free_polled_device(bdev->poll_dev);
+	iounmap(bdev->reg);
+	kfree(bdev);
+
+	return 0;
+}
+
+MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
+MODULE_DESCRIPTION("Cobalt button interface driver");
+MODULE_LICENSE("GPL");
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:Cobalt buttons");
+
+static struct platform_driver cobalt_buttons_driver = {
+	.probe	= cobalt_buttons_probe,
+	.remove	= cobalt_buttons_remove,
+	.driver	= {
+		.name	= "Cobalt buttons",
+	},
+};
+module_platform_driver(cobalt_buttons_driver);
diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c
new file mode 100644
index 0000000..266e07f
--- /dev/null
+++ b/drivers/input/misc/da9052_onkey.c
@@ -0,0 +1,159 @@
+/*
+ * ON pin driver for Dialog DA9052 PMICs
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * 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.
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+
+struct da9052_onkey {
+	struct da9052 *da9052;
+	struct input_dev *input;
+	struct delayed_work work;
+};
+
+static void da9052_onkey_query(struct da9052_onkey *onkey)
+{
+	int ret;
+
+	ret = da9052_reg_read(onkey->da9052, DA9052_STATUS_A_REG);
+	if (ret < 0) {
+		dev_err(onkey->da9052->dev,
+			"Failed to read onkey event err=%d\n", ret);
+	} else {
+		/*
+		 * Since interrupt for deassertion of ONKEY pin is not
+		 * generated, onkey event state determines the onkey
+		 * button state.
+		 */
+		bool pressed = !(ret & DA9052_STATUSA_NONKEY);
+
+		input_report_key(onkey->input, KEY_POWER, pressed);
+		input_sync(onkey->input);
+
+		/*
+		 * Interrupt is generated only when the ONKEY pin
+		 * is asserted.  Hence the deassertion of the pin
+		 * is simulated through work queue.
+		 */
+		if (pressed)
+			schedule_delayed_work(&onkey->work,
+						msecs_to_jiffies(50));
+	}
+}
+
+static void da9052_onkey_work(struct work_struct *work)
+{
+	struct da9052_onkey *onkey = container_of(work, struct da9052_onkey,
+						  work.work);
+
+	da9052_onkey_query(onkey);
+}
+
+static irqreturn_t da9052_onkey_irq(int irq, void *data)
+{
+	struct da9052_onkey *onkey = data;
+
+	da9052_onkey_query(onkey);
+
+	return IRQ_HANDLED;
+}
+
+static int da9052_onkey_probe(struct platform_device *pdev)
+{
+	struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent);
+	struct da9052_onkey *onkey;
+	struct input_dev *input_dev;
+	int error;
+
+	if (!da9052) {
+		dev_err(&pdev->dev, "Failed to get the driver's data\n");
+		return -EINVAL;
+	}
+
+	onkey = kzalloc(sizeof(*onkey), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!onkey || !input_dev) {
+		dev_err(&pdev->dev, "Failed to allocate memory\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	onkey->input = input_dev;
+	onkey->da9052 = da9052;
+	INIT_DELAYED_WORK(&onkey->work, da9052_onkey_work);
+
+	input_dev->name = "da9052-onkey";
+	input_dev->phys = "da9052-onkey/input0";
+	input_dev->dev.parent = &pdev->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY);
+	__set_bit(KEY_POWER, input_dev->keybit);
+
+	error = da9052_request_irq(onkey->da9052, DA9052_IRQ_NONKEY, "ONKEY",
+			    da9052_onkey_irq, onkey);
+	if (error < 0) {
+		dev_err(onkey->da9052->dev,
+			"Failed to register ONKEY IRQ: %d\n", error);
+		goto err_free_mem;
+	}
+
+	error = input_register_device(onkey->input);
+	if (error) {
+		dev_err(&pdev->dev, "Unable to register input device, %d\n",
+			error);
+		goto err_free_irq;
+	}
+
+	platform_set_drvdata(pdev, onkey);
+	return 0;
+
+err_free_irq:
+	da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey);
+	cancel_delayed_work_sync(&onkey->work);
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(onkey);
+
+	return error;
+}
+
+static int da9052_onkey_remove(struct platform_device *pdev)
+{
+	struct da9052_onkey *onkey = platform_get_drvdata(pdev);
+
+	da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey);
+	cancel_delayed_work_sync(&onkey->work);
+
+	input_unregister_device(onkey->input);
+	kfree(onkey);
+
+	return 0;
+}
+
+static struct platform_driver da9052_onkey_driver = {
+	.probe	= da9052_onkey_probe,
+	.remove	= da9052_onkey_remove,
+	.driver = {
+		.name	= "da9052-onkey",
+	},
+};
+module_platform_driver(da9052_onkey_driver);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("Onkey driver for DA9052");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-onkey");
diff --git a/drivers/input/misc/da9055_onkey.c b/drivers/input/misc/da9055_onkey.c
new file mode 100644
index 0000000..3251a96
--- /dev/null
+++ b/drivers/input/misc/da9055_onkey.c
@@ -0,0 +1,168 @@
+/*
+ * ON pin driver for Dialog DA9055 PMICs
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * 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.
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/da9055/core.h>
+#include <linux/mfd/da9055/reg.h>
+
+struct da9055_onkey {
+	struct da9055 *da9055;
+	struct input_dev *input;
+	struct delayed_work work;
+};
+
+static void da9055_onkey_query(struct da9055_onkey *onkey)
+{
+	int key_stat;
+
+	key_stat = da9055_reg_read(onkey->da9055, DA9055_REG_STATUS_A);
+	if (key_stat < 0) {
+		dev_err(onkey->da9055->dev,
+			"Failed to read onkey event %d\n", key_stat);
+	} else {
+		key_stat &= DA9055_NOKEY_STS;
+		/*
+		 * Onkey status bit is cleared when onkey button is released.
+		 */
+		if (!key_stat) {
+			input_report_key(onkey->input, KEY_POWER, 0);
+			input_sync(onkey->input);
+		}
+	}
+
+	/*
+	 * Interrupt is generated only when the ONKEY pin is asserted.
+	 * Hence the deassertion of the pin is simulated through work queue.
+	 */
+	if (key_stat)
+		schedule_delayed_work(&onkey->work, msecs_to_jiffies(10));
+
+}
+
+static void da9055_onkey_work(struct work_struct *work)
+{
+	struct da9055_onkey *onkey = container_of(work, struct da9055_onkey,
+						  work.work);
+
+	da9055_onkey_query(onkey);
+}
+
+static irqreturn_t da9055_onkey_irq(int irq, void *data)
+{
+	struct da9055_onkey *onkey = data;
+
+	input_report_key(onkey->input, KEY_POWER, 1);
+	input_sync(onkey->input);
+
+	da9055_onkey_query(onkey);
+
+	return IRQ_HANDLED;
+}
+
+static int da9055_onkey_probe(struct platform_device *pdev)
+{
+	struct da9055 *da9055 = dev_get_drvdata(pdev->dev.parent);
+	struct da9055_onkey *onkey;
+	struct input_dev *input_dev;
+	int irq, err;
+
+	irq = platform_get_irq_byname(pdev, "ONKEY");
+	if (irq < 0) {
+		dev_err(&pdev->dev,
+			"Failed to get an IRQ for input device, %d\n", irq);
+		return -EINVAL;
+	}
+
+	onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);
+	if (!onkey) {
+		dev_err(&pdev->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&pdev->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	onkey->input = input_dev;
+	onkey->da9055 = da9055;
+	input_dev->name = "da9055-onkey";
+	input_dev->phys = "da9055-onkey/input0";
+	input_dev->dev.parent = &pdev->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY);
+	__set_bit(KEY_POWER, input_dev->keybit);
+
+	INIT_DELAYED_WORK(&onkey->work, da9055_onkey_work);
+
+	err = request_threaded_irq(irq, NULL, da9055_onkey_irq,
+				   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				   "ONKEY", onkey);
+	if (err < 0) {
+		dev_err(&pdev->dev,
+			"Failed to register ONKEY IRQ %d, error = %d\n",
+			irq, err);
+		goto err_free_input;
+	}
+
+	err = input_register_device(input_dev);
+	if (err) {
+		dev_err(&pdev->dev, "Unable to register input device, %d\n",
+			err);
+		goto err_free_irq;
+	}
+
+	platform_set_drvdata(pdev, onkey);
+
+	return 0;
+
+err_free_irq:
+	free_irq(irq, onkey);
+	cancel_delayed_work_sync(&onkey->work);
+err_free_input:
+	input_free_device(input_dev);
+
+	return err;
+}
+
+static int da9055_onkey_remove(struct platform_device *pdev)
+{
+	struct da9055_onkey *onkey = platform_get_drvdata(pdev);
+	int irq = platform_get_irq_byname(pdev, "ONKEY");
+
+	irq = regmap_irq_get_virq(onkey->da9055->irq_data, irq);
+	free_irq(irq, onkey);
+	cancel_delayed_work_sync(&onkey->work);
+	input_unregister_device(onkey->input);
+
+	return 0;
+}
+
+static struct platform_driver da9055_onkey_driver = {
+	.probe	= da9055_onkey_probe,
+	.remove	= da9055_onkey_remove,
+	.driver = {
+		.name	= "da9055-onkey",
+	},
+};
+
+module_platform_driver(da9055_onkey_driver);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("Onkey driver for DA9055");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9055-onkey");
diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c
new file mode 100644
index 0000000..8eb697d
--- /dev/null
+++ b/drivers/input/misc/da9063_onkey.c
@@ -0,0 +1,305 @@
+/*
+ * OnKey device driver for DA9063 and DA9062 PMICs
+ * Copyright (C) 2015  Dialog Semiconductor Ltd.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/mfd/da9063/core.h>
+#include <linux/mfd/da9063/pdata.h>
+#include <linux/mfd/da9063/registers.h>
+#include <linux/mfd/da9062/core.h>
+#include <linux/mfd/da9062/registers.h>
+
+struct da906x_chip_config {
+	/* REGS */
+	int onkey_status;
+	int onkey_pwr_signalling;
+	int onkey_fault_log;
+	int onkey_shutdown;
+	/* MASKS */
+	int onkey_nonkey_mask;
+	int onkey_nonkey_lock_mask;
+	int onkey_key_reset_mask;
+	int onkey_shutdown_mask;
+	/* NAMES */
+	const char *name;
+};
+
+struct da9063_onkey {
+	struct delayed_work work;
+	struct input_dev *input;
+	struct device *dev;
+	struct regmap *regmap;
+	const struct da906x_chip_config *config;
+	char phys[32];
+	bool key_power;
+};
+
+static const struct da906x_chip_config da9063_regs = {
+	/* REGS */
+	.onkey_status = DA9063_REG_STATUS_A,
+	.onkey_pwr_signalling = DA9063_REG_CONTROL_B,
+	.onkey_fault_log = DA9063_REG_FAULT_LOG,
+	.onkey_shutdown = DA9063_REG_CONTROL_F,
+	/* MASKS */
+	.onkey_nonkey_mask = DA9063_NONKEY,
+	.onkey_nonkey_lock_mask = DA9063_NONKEY_LOCK,
+	.onkey_key_reset_mask = DA9063_KEY_RESET,
+	.onkey_shutdown_mask = DA9063_SHUTDOWN,
+	/* NAMES */
+	.name = DA9063_DRVNAME_ONKEY,
+};
+
+static const struct da906x_chip_config da9062_regs = {
+	/* REGS */
+	.onkey_status = DA9062AA_STATUS_A,
+	.onkey_pwr_signalling = DA9062AA_CONTROL_B,
+	.onkey_fault_log = DA9062AA_FAULT_LOG,
+	.onkey_shutdown = DA9062AA_CONTROL_F,
+	/* MASKS */
+	.onkey_nonkey_mask = DA9062AA_NONKEY_MASK,
+	.onkey_nonkey_lock_mask = DA9062AA_NONKEY_LOCK_MASK,
+	.onkey_key_reset_mask = DA9062AA_KEY_RESET_MASK,
+	.onkey_shutdown_mask = DA9062AA_SHUTDOWN_MASK,
+	/* NAMES */
+	.name = "da9062-onkey",
+};
+
+static const struct of_device_id da9063_compatible_reg_id_table[] = {
+	{ .compatible = "dlg,da9063-onkey", .data = &da9063_regs },
+	{ .compatible = "dlg,da9062-onkey", .data = &da9062_regs },
+	{ },
+};
+
+static void da9063_poll_on(struct work_struct *work)
+{
+	struct da9063_onkey *onkey = container_of(work,
+						struct da9063_onkey,
+						work.work);
+	const struct da906x_chip_config *config = onkey->config;
+	unsigned int val;
+	int fault_log = 0;
+	bool poll = true;
+	int error;
+
+	/* Poll to see when the pin is released */
+	error = regmap_read(onkey->regmap,
+			    config->onkey_status,
+			    &val);
+	if (error) {
+		dev_err(onkey->dev,
+			"Failed to read ON status: %d\n", error);
+		goto err_poll;
+	}
+
+	if (!(val & config->onkey_nonkey_mask)) {
+		error = regmap_update_bits(onkey->regmap,
+					   config->onkey_pwr_signalling,
+					   config->onkey_nonkey_lock_mask,
+					   0);
+		if (error) {
+			dev_err(onkey->dev,
+				"Failed to reset the Key Delay %d\n", error);
+			goto err_poll;
+		}
+
+		input_report_key(onkey->input, KEY_POWER, 0);
+		input_sync(onkey->input);
+
+		poll = false;
+	}
+
+	/*
+	 * If the fault log KEY_RESET is detected, then clear it
+	 * and shut down the system.
+	 */
+	error = regmap_read(onkey->regmap,
+			    config->onkey_fault_log,
+			    &fault_log);
+	if (error) {
+		dev_warn(&onkey->input->dev,
+			 "Cannot read FAULT_LOG: %d\n", error);
+	} else if (fault_log & config->onkey_key_reset_mask) {
+		error = regmap_write(onkey->regmap,
+				     config->onkey_fault_log,
+				     config->onkey_key_reset_mask);
+		if (error) {
+			dev_warn(&onkey->input->dev,
+				 "Cannot reset KEY_RESET fault log: %d\n",
+				 error);
+		} else {
+			/* at this point we do any S/W housekeeping
+			 * and then send shutdown command
+			 */
+			dev_dbg(&onkey->input->dev,
+				"Sending SHUTDOWN to DA9063 ...\n");
+			error = regmap_write(onkey->regmap,
+					     config->onkey_shutdown,
+					     config->onkey_shutdown_mask);
+			if (error)
+				dev_err(&onkey->input->dev,
+					"Cannot SHUTDOWN DA9063: %d\n",
+					error);
+		}
+	}
+
+err_poll:
+	if (poll)
+		schedule_delayed_work(&onkey->work, msecs_to_jiffies(50));
+}
+
+static irqreturn_t da9063_onkey_irq_handler(int irq, void *data)
+{
+	struct da9063_onkey *onkey = data;
+	const struct da906x_chip_config *config = onkey->config;
+	unsigned int val;
+	int error;
+
+	error = regmap_read(onkey->regmap,
+			    config->onkey_status,
+			    &val);
+	if (onkey->key_power && !error && (val & config->onkey_nonkey_mask)) {
+		input_report_key(onkey->input, KEY_POWER, 1);
+		input_sync(onkey->input);
+		schedule_delayed_work(&onkey->work, 0);
+		dev_dbg(onkey->dev, "KEY_POWER pressed.\n");
+	} else {
+		input_report_key(onkey->input, KEY_SLEEP, 1);
+		input_sync(onkey->input);
+		input_report_key(onkey->input, KEY_SLEEP, 0);
+		input_sync(onkey->input);
+		dev_dbg(onkey->dev, "KEY_SLEEP pressed.\n");
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void da9063_cancel_poll(void *data)
+{
+	struct da9063_onkey *onkey = data;
+
+	cancel_delayed_work_sync(&onkey->work);
+}
+
+static int da9063_onkey_probe(struct platform_device *pdev)
+{
+	struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent);
+	struct da9063_pdata *pdata = dev_get_platdata(da9063->dev);
+	struct da9063_onkey *onkey;
+	const struct of_device_id *match;
+	int irq;
+	int error;
+
+	match = of_match_node(da9063_compatible_reg_id_table,
+			      pdev->dev.of_node);
+	if (!match)
+		return -ENXIO;
+
+	onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9063_onkey),
+			     GFP_KERNEL);
+	if (!onkey) {
+		dev_err(&pdev->dev, "Failed to allocate memory.\n");
+		return -ENOMEM;
+	}
+
+	onkey->config = match->data;
+	onkey->dev = &pdev->dev;
+
+	onkey->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!onkey->regmap) {
+		dev_err(&pdev->dev, "Parent regmap unavailable.\n");
+		return -ENXIO;
+	}
+
+	if (pdata)
+		onkey->key_power = pdata->key_power;
+	else
+		onkey->key_power =
+			!of_property_read_bool(pdev->dev.of_node,
+					       "dlg,disable-key-power");
+
+	onkey->input = devm_input_allocate_device(&pdev->dev);
+	if (!onkey->input) {
+		dev_err(&pdev->dev, "Failed to allocated input device.\n");
+		return -ENOMEM;
+	}
+
+	onkey->input->name = onkey->config->name;
+	snprintf(onkey->phys, sizeof(onkey->phys), "%s/input0",
+		 onkey->config->name);
+	onkey->input->phys = onkey->phys;
+	onkey->input->dev.parent = &pdev->dev;
+
+	if (onkey->key_power)
+		input_set_capability(onkey->input, EV_KEY, KEY_POWER);
+
+	input_set_capability(onkey->input, EV_KEY, KEY_SLEEP);
+
+	INIT_DELAYED_WORK(&onkey->work, da9063_poll_on);
+
+	error = devm_add_action(&pdev->dev, da9063_cancel_poll, onkey);
+	if (error) {
+		dev_err(&pdev->dev,
+			"Failed to add cancel poll action: %d\n",
+			error);
+		return error;
+	}
+
+	irq = platform_get_irq_byname(pdev, "ONKEY");
+	if (irq < 0) {
+		error = irq;
+		dev_err(&pdev->dev, "Failed to get platform IRQ: %d\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&pdev->dev, irq,
+					  NULL, da9063_onkey_irq_handler,
+					  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					  "ONKEY", onkey);
+	if (error) {
+		dev_err(&pdev->dev,
+			"Failed to request IRQ %d: %d\n", irq, error);
+		return error;
+	}
+
+	error = input_register_device(onkey->input);
+	if (error) {
+		dev_err(&pdev->dev,
+			"Failed to register input device: %d\n", error);
+		return error;
+	}
+
+	platform_set_drvdata(pdev, onkey);
+	return 0;
+}
+
+static struct platform_driver da9063_onkey_driver = {
+	.probe	= da9063_onkey_probe,
+	.driver	= {
+		.name	= DA9063_DRVNAME_ONKEY,
+		.of_match_table = da9063_compatible_reg_id_table,
+	},
+};
+module_platform_driver(da9063_onkey_driver);
+
+MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
+MODULE_DESCRIPTION("Onkey device driver for Dialog DA9063 and DA9062");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DA9063_DRVNAME_ONKEY);
diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c
new file mode 100644
index 0000000..b6b7bd4
--- /dev/null
+++ b/drivers/input/misc/dm355evm_keys.c
@@ -0,0 +1,271 @@
+/*
+ * dm355evm_keys.c - support buttons and IR remote on DM355 EVM board
+ *
+ * Copyright (c) 2008 by David Brownell
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+
+#include <linux/i2c/dm355evm_msp.h>
+#include <linux/module.h>
+
+
+/*
+ * The MSP430 firmware on the DM355 EVM monitors on-board pushbuttons
+ * and an IR receptor used for the remote control.  When any key is
+ * pressed, or its autorepeat kicks in, an event is sent.  This driver
+ * read those events from the small (32 event) queue and reports them.
+ *
+ * Note that physically there can only be one of these devices.
+ *
+ * This driver was tested with firmware revision A4.
+ */
+struct dm355evm_keys {
+	struct input_dev	*input;
+	struct device		*dev;
+	int			irq;
+};
+
+/* These initial keycodes can be remapped */
+static const struct key_entry dm355evm_keys[] = {
+	/*
+	 * Pushbuttons on the EVM board ... note that the labels for these
+	 * are SW10/SW11/etc on the PC board.  The left/right orientation
+	 * comes only from the firmware's documentation, and presumes the
+	 * power connector is immediately in front of you and the IR sensor
+	 * is to the right.  (That is, rotate the board counter-clockwise
+	 * by 90 degrees from the SW10/etc and "DM355 EVM" labels.)
+	 */
+	{ KE_KEY, 0x00d8, { KEY_OK } },		/* SW12 */
+	{ KE_KEY, 0x00b8, { KEY_UP } },		/* SW13 */
+	{ KE_KEY, 0x00e8, { KEY_DOWN } },	/* SW11 */
+	{ KE_KEY, 0x0078, { KEY_LEFT } },	/* SW14 */
+	{ KE_KEY, 0x00f0, { KEY_RIGHT } },	/* SW10 */
+
+	/*
+	 * IR buttons ... codes assigned to match the universal remote
+	 * provided with the EVM (Philips PM4S) using DVD code 0020.
+	 *
+	 * These event codes match firmware documentation, but other
+	 * remote controls could easily send more RC5-encoded events.
+	 * The PM4S manual was used in several cases to help select
+	 * a keycode reflecting the intended usage.
+	 *
+	 * RC5 codes are 14 bits, with two start bits (0x3 prefix)
+	 * and a toggle bit (masked out below).
+	 */
+	{ KE_KEY, 0x300c, { KEY_POWER } },	/* NOTE: docs omit this */
+	{ KE_KEY, 0x3000, { KEY_NUMERIC_0 } },
+	{ KE_KEY, 0x3001, { KEY_NUMERIC_1 } },
+	{ KE_KEY, 0x3002, { KEY_NUMERIC_2 } },
+	{ KE_KEY, 0x3003, { KEY_NUMERIC_3 } },
+	{ KE_KEY, 0x3004, { KEY_NUMERIC_4 } },
+	{ KE_KEY, 0x3005, { KEY_NUMERIC_5 } },
+	{ KE_KEY, 0x3006, { KEY_NUMERIC_6 } },
+	{ KE_KEY, 0x3007, { KEY_NUMERIC_7 } },
+	{ KE_KEY, 0x3008, { KEY_NUMERIC_8 } },
+	{ KE_KEY, 0x3009, { KEY_NUMERIC_9 } },
+	{ KE_KEY, 0x3022, { KEY_ENTER } },
+	{ KE_KEY, 0x30ec, { KEY_MODE } },	/* "tv/vcr/..." */
+	{ KE_KEY, 0x300f, { KEY_SELECT } },	/* "info" */
+	{ KE_KEY, 0x3020, { KEY_CHANNELUP } },	/* "up" */
+	{ KE_KEY, 0x302e, { KEY_MENU } },	/* "in/out" */
+	{ KE_KEY, 0x3011, { KEY_VOLUMEDOWN } },	/* "left" */
+	{ KE_KEY, 0x300d, { KEY_MUTE } },	/* "ok" */
+	{ KE_KEY, 0x3010, { KEY_VOLUMEUP } },	/* "right" */
+	{ KE_KEY, 0x301e, { KEY_SUBTITLE } },	/* "cc" */
+	{ KE_KEY, 0x3021, { KEY_CHANNELDOWN } },/* "down" */
+	{ KE_KEY, 0x3022, { KEY_PREVIOUS } },
+	{ KE_KEY, 0x3026, { KEY_SLEEP } },
+	{ KE_KEY, 0x3172, { KEY_REWIND } },	/* NOTE: docs wrongly say 0x30ca */
+	{ KE_KEY, 0x3175, { KEY_PLAY } },
+	{ KE_KEY, 0x3174, { KEY_FASTFORWARD } },
+	{ KE_KEY, 0x3177, { KEY_RECORD } },
+	{ KE_KEY, 0x3176, { KEY_STOP } },
+	{ KE_KEY, 0x3169, { KEY_PAUSE } },
+};
+
+/*
+ * Because we communicate with the MSP430 using I2C, and all I2C calls
+ * in Linux sleep, we use a threaded IRQ handler.  The IRQ itself is
+ * active low, but we go through the GPIO controller so we can trigger
+ * on falling edges and not worry about enabling/disabling the IRQ in
+ * the keypress handling path.
+ */
+static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
+{
+	static u16 last_event;
+	struct dm355evm_keys *keys = _keys;
+	const struct key_entry *ke;
+	unsigned int keycode;
+	int status;
+	u16 event;
+
+	/* For simplicity we ignore INPUT_COUNT and just read
+	 * events until we get the "queue empty" indicator.
+	 * Reading INPUT_LOW decrements the count.
+	 */
+	for (;;) {
+		status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH);
+		if (status < 0) {
+			dev_dbg(keys->dev, "input high err %d\n",
+					status);
+			break;
+		}
+		event = status << 8;
+
+		status = dm355evm_msp_read(DM355EVM_MSP_INPUT_LOW);
+		if (status < 0) {
+			dev_dbg(keys->dev, "input low err %d\n",
+					status);
+			break;
+		}
+		event |= status;
+		if (event == 0xdead)
+			break;
+
+		/* Press and release a button:  two events, same code.
+		 * Press and hold (autorepeat), then release: N events
+		 * (N > 2), same code.  For RC5 buttons the toggle bits
+		 * distinguish (for example) "1-autorepeat" from "1 1";
+		 * but PCB buttons don't support that bit.
+		 *
+		 * So we must synthesize release events.  We do that by
+		 * mapping events to a press/release event pair; then
+		 * to avoid adding extra events, skip the second event
+		 * of each pair.
+		 */
+		if (event == last_event) {
+			last_event = 0;
+			continue;
+		}
+		last_event = event;
+
+		/* ignore the RC5 toggle bit */
+		event &= ~0x0800;
+
+		/* find the key, or report it as unknown */
+		ke = sparse_keymap_entry_from_scancode(keys->input, event);
+		keycode = ke ? ke->keycode : KEY_UNKNOWN;
+		dev_dbg(keys->dev,
+			"input event 0x%04x--> keycode %d\n",
+			event, keycode);
+
+		/* report press + release */
+		input_report_key(keys->input, keycode, 1);
+		input_sync(keys->input);
+		input_report_key(keys->input, keycode, 0);
+		input_sync(keys->input);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------*/
+
+static int dm355evm_keys_probe(struct platform_device *pdev)
+{
+	struct dm355evm_keys	*keys;
+	struct input_dev	*input;
+	int			status;
+
+	/* allocate instance struct and input dev */
+	keys = kzalloc(sizeof *keys, GFP_KERNEL);
+	input = input_allocate_device();
+	if (!keys || !input) {
+		status = -ENOMEM;
+		goto fail1;
+	}
+
+	keys->dev = &pdev->dev;
+	keys->input = input;
+
+	/* set up "threaded IRQ handler" */
+	status = platform_get_irq(pdev, 0);
+	if (status < 0)
+		goto fail1;
+	keys->irq = status;
+
+	input_set_drvdata(input, keys);
+
+	input->name = "DM355 EVM Controls";
+	input->phys = "dm355evm/input0";
+	input->dev.parent = &pdev->dev;
+
+	input->id.bustype = BUS_I2C;
+	input->id.product = 0x0355;
+	input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
+
+	status = sparse_keymap_setup(input, dm355evm_keys, NULL);
+	if (status)
+		goto fail1;
+
+	/* REVISIT:  flush the event queue? */
+
+	status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq,
+				      IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				      dev_name(&pdev->dev), keys);
+	if (status < 0)
+		goto fail2;
+
+	/* register */
+	status = input_register_device(input);
+	if (status < 0)
+		goto fail3;
+
+	platform_set_drvdata(pdev, keys);
+
+	return 0;
+
+fail3:
+	free_irq(keys->irq, keys);
+fail2:
+	sparse_keymap_free(input);
+fail1:
+	input_free_device(input);
+	kfree(keys);
+	dev_err(&pdev->dev, "can't register, err %d\n", status);
+
+	return status;
+}
+
+static int dm355evm_keys_remove(struct platform_device *pdev)
+{
+	struct dm355evm_keys	*keys = platform_get_drvdata(pdev);
+
+	free_irq(keys->irq, keys);
+	sparse_keymap_free(keys->input);
+	input_unregister_device(keys->input);
+	kfree(keys);
+
+	return 0;
+}
+
+/* REVISIT:  add suspend/resume when DaVinci supports it.  The IRQ should
+ * be able to wake up the system.  When device_may_wakeup(&pdev->dev), call
+ * enable_irq_wake() on suspend, and disable_irq_wake() on resume.
+ */
+
+/*
+ * I2C is used to talk to the MSP430, but this platform device is
+ * exposed by an MFD driver that manages I2C communications.
+ */
+static struct platform_driver dm355evm_keys_driver = {
+	.probe		= dm355evm_keys_probe,
+	.remove		= dm355evm_keys_remove,
+	.driver		= {
+		.name	= "dm355evm_keys",
+	},
+};
+module_platform_driver(dm355evm_keys_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
new file mode 100644
index 0000000..930424e
--- /dev/null
+++ b/drivers/input/misc/drv260x.c
@@ -0,0 +1,731 @@
+/*
+ * DRV260X haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright:   (C) 2014 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include <dt-bindings/input/ti-drv260x.h>
+#include <linux/platform_data/drv260x-pdata.h>
+
+#define DRV260X_STATUS		0x0
+#define DRV260X_MODE		0x1
+#define DRV260X_RT_PB_IN	0x2
+#define DRV260X_LIB_SEL		0x3
+#define DRV260X_WV_SEQ_1	0x4
+#define DRV260X_WV_SEQ_2	0x5
+#define DRV260X_WV_SEQ_3	0x6
+#define DRV260X_WV_SEQ_4	0x7
+#define DRV260X_WV_SEQ_5	0x8
+#define DRV260X_WV_SEQ_6	0x9
+#define DRV260X_WV_SEQ_7	0xa
+#define DRV260X_WV_SEQ_8	0xb
+#define DRV260X_GO				0xc
+#define DRV260X_OVERDRIVE_OFF	0xd
+#define DRV260X_SUSTAIN_P_OFF	0xe
+#define DRV260X_SUSTAIN_N_OFF	0xf
+#define DRV260X_BRAKE_OFF		0x10
+#define DRV260X_A_TO_V_CTRL		0x11
+#define DRV260X_A_TO_V_MIN_INPUT	0x12
+#define DRV260X_A_TO_V_MAX_INPUT	0x13
+#define DRV260X_A_TO_V_MIN_OUT	0x14
+#define DRV260X_A_TO_V_MAX_OUT	0x15
+#define DRV260X_RATED_VOLT		0x16
+#define DRV260X_OD_CLAMP_VOLT	0x17
+#define DRV260X_CAL_COMP		0x18
+#define DRV260X_CAL_BACK_EMF	0x19
+#define DRV260X_FEEDBACK_CTRL	0x1a
+#define DRV260X_CTRL1			0x1b
+#define DRV260X_CTRL2			0x1c
+#define DRV260X_CTRL3			0x1d
+#define DRV260X_CTRL4			0x1e
+#define DRV260X_CTRL5			0x1f
+#define DRV260X_LRA_LOOP_PERIOD	0x20
+#define DRV260X_VBAT_MON		0x21
+#define DRV260X_LRA_RES_PERIOD	0x22
+#define DRV260X_MAX_REG			0x23
+
+#define DRV260X_GO_BIT				0x01
+
+/* Library Selection */
+#define DRV260X_LIB_SEL_MASK		0x07
+#define DRV260X_LIB_SEL_RAM			0x0
+#define DRV260X_LIB_SEL_OD			0x1
+#define DRV260X_LIB_SEL_40_60		0x2
+#define DRV260X_LIB_SEL_60_80		0x3
+#define DRV260X_LIB_SEL_100_140		0x4
+#define DRV260X_LIB_SEL_140_PLUS	0x5
+
+#define DRV260X_LIB_SEL_HIZ_MASK	0x10
+#define DRV260X_LIB_SEL_HIZ_EN		0x01
+#define DRV260X_LIB_SEL_HIZ_DIS		0
+
+/* Mode register */
+#define DRV260X_STANDBY				(1 << 6)
+#define DRV260X_STANDBY_MASK		0x40
+#define DRV260X_INTERNAL_TRIGGER	0x00
+#define DRV260X_EXT_TRIGGER_EDGE	0x01
+#define DRV260X_EXT_TRIGGER_LEVEL	0x02
+#define DRV260X_PWM_ANALOG_IN		0x03
+#define DRV260X_AUDIOHAPTIC			0x04
+#define DRV260X_RT_PLAYBACK			0x05
+#define DRV260X_DIAGNOSTICS			0x06
+#define DRV260X_AUTO_CAL			0x07
+
+/* Audio to Haptics Control */
+#define DRV260X_AUDIO_HAPTICS_PEAK_10MS		(0 << 2)
+#define DRV260X_AUDIO_HAPTICS_PEAK_20MS		(1 << 2)
+#define DRV260X_AUDIO_HAPTICS_PEAK_30MS		(2 << 2)
+#define DRV260X_AUDIO_HAPTICS_PEAK_40MS		(3 << 2)
+
+#define DRV260X_AUDIO_HAPTICS_FILTER_100HZ	0x00
+#define DRV260X_AUDIO_HAPTICS_FILTER_125HZ	0x01
+#define DRV260X_AUDIO_HAPTICS_FILTER_150HZ	0x02
+#define DRV260X_AUDIO_HAPTICS_FILTER_200HZ	0x03
+
+/* Min/Max Input/Output Voltages */
+#define DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT	0x19
+#define DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT	0x64
+#define DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT	0x19
+#define DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT	0xFF
+
+/* Feedback register */
+#define DRV260X_FB_REG_ERM_MODE			0x7f
+#define DRV260X_FB_REG_LRA_MODE			(1 << 7)
+
+#define DRV260X_BRAKE_FACTOR_MASK	0x1f
+#define DRV260X_BRAKE_FACTOR_2X		(1 << 0)
+#define DRV260X_BRAKE_FACTOR_3X		(2 << 4)
+#define DRV260X_BRAKE_FACTOR_4X		(3 << 4)
+#define DRV260X_BRAKE_FACTOR_6X		(4 << 4)
+#define DRV260X_BRAKE_FACTOR_8X		(5 << 4)
+#define DRV260X_BRAKE_FACTOR_16		(6 << 4)
+#define DRV260X_BRAKE_FACTOR_DIS	(7 << 4)
+
+#define DRV260X_LOOP_GAIN_LOW		0xf3
+#define DRV260X_LOOP_GAIN_MED		(1 << 2)
+#define DRV260X_LOOP_GAIN_HIGH		(2 << 2)
+#define DRV260X_LOOP_GAIN_VERY_HIGH	(3 << 2)
+
+#define DRV260X_BEMF_GAIN_0			0xfc
+#define DRV260X_BEMF_GAIN_1		(1 << 0)
+#define DRV260X_BEMF_GAIN_2		(2 << 0)
+#define DRV260X_BEMF_GAIN_3		(3 << 0)
+
+/* Control 1 register */
+#define DRV260X_AC_CPLE_EN			(1 << 5)
+#define DRV260X_STARTUP_BOOST		(1 << 7)
+
+/* Control 2 register */
+
+#define DRV260X_IDISS_TIME_45		0
+#define DRV260X_IDISS_TIME_75		(1 << 0)
+#define DRV260X_IDISS_TIME_150		(1 << 1)
+#define DRV260X_IDISS_TIME_225		0x03
+
+#define DRV260X_BLANK_TIME_45	(0 << 2)
+#define DRV260X_BLANK_TIME_75	(1 << 2)
+#define DRV260X_BLANK_TIME_150	(2 << 2)
+#define DRV260X_BLANK_TIME_225	(3 << 2)
+
+#define DRV260X_SAMP_TIME_150	(0 << 4)
+#define DRV260X_SAMP_TIME_200	(1 << 4)
+#define DRV260X_SAMP_TIME_250	(2 << 4)
+#define DRV260X_SAMP_TIME_300	(3 << 4)
+
+#define DRV260X_BRAKE_STABILIZER	(1 << 6)
+#define DRV260X_UNIDIR_IN			(0 << 7)
+#define DRV260X_BIDIR_IN			(1 << 7)
+
+/* Control 3 Register */
+#define DRV260X_LRA_OPEN_LOOP		(1 << 0)
+#define DRV260X_ANANLOG_IN			(1 << 1)
+#define DRV260X_LRA_DRV_MODE		(1 << 2)
+#define DRV260X_RTP_UNSIGNED_DATA	(1 << 3)
+#define DRV260X_SUPPLY_COMP_DIS		(1 << 4)
+#define DRV260X_ERM_OPEN_LOOP		(1 << 5)
+#define DRV260X_NG_THRESH_0			(0 << 6)
+#define DRV260X_NG_THRESH_2			(1 << 6)
+#define DRV260X_NG_THRESH_4			(2 << 6)
+#define DRV260X_NG_THRESH_8			(3 << 6)
+
+/* Control 4 Register */
+#define DRV260X_AUTOCAL_TIME_150MS		(0 << 4)
+#define DRV260X_AUTOCAL_TIME_250MS		(1 << 4)
+#define DRV260X_AUTOCAL_TIME_500MS		(2 << 4)
+#define DRV260X_AUTOCAL_TIME_1000MS		(3 << 4)
+
+/**
+ * struct drv260x_data -
+ * @input_dev - Pointer to the input device
+ * @client - Pointer to the I2C client
+ * @regmap - Register map of the device
+ * @work - Work item used to off load the enable/disable of the vibration
+ * @enable_gpio - Pointer to the gpio used for enable/disabling
+ * @regulator - Pointer to the regulator for the IC
+ * @magnitude - Magnitude of the vibration event
+ * @mode - The operating mode of the IC (LRA_NO_CAL, ERM or LRA)
+ * @library - The vibration library to be used
+ * @rated_voltage - The rated_voltage of the actuator
+ * @overdriver_voltage - The over drive voltage of the actuator
+**/
+struct drv260x_data {
+	struct input_dev *input_dev;
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct work_struct work;
+	struct gpio_desc *enable_gpio;
+	struct regulator *regulator;
+	u32 magnitude;
+	u32 mode;
+	u32 library;
+	int rated_voltage;
+	int overdrive_voltage;
+};
+
+static const struct reg_default drv260x_reg_defs[] = {
+	{ DRV260X_STATUS, 0xe0 },
+	{ DRV260X_MODE, 0x40 },
+	{ DRV260X_RT_PB_IN, 0x00 },
+	{ DRV260X_LIB_SEL, 0x00 },
+	{ DRV260X_WV_SEQ_1, 0x01 },
+	{ DRV260X_WV_SEQ_2, 0x00 },
+	{ DRV260X_WV_SEQ_3, 0x00 },
+	{ DRV260X_WV_SEQ_4, 0x00 },
+	{ DRV260X_WV_SEQ_5, 0x00 },
+	{ DRV260X_WV_SEQ_6, 0x00 },
+	{ DRV260X_WV_SEQ_7, 0x00 },
+	{ DRV260X_WV_SEQ_8, 0x00 },
+	{ DRV260X_GO, 0x00 },
+	{ DRV260X_OVERDRIVE_OFF, 0x00 },
+	{ DRV260X_SUSTAIN_P_OFF, 0x00 },
+	{ DRV260X_SUSTAIN_N_OFF, 0x00 },
+	{ DRV260X_BRAKE_OFF, 0x00 },
+	{ DRV260X_A_TO_V_CTRL, 0x05 },
+	{ DRV260X_A_TO_V_MIN_INPUT, 0x19 },
+	{ DRV260X_A_TO_V_MAX_INPUT, 0xff },
+	{ DRV260X_A_TO_V_MIN_OUT, 0x19 },
+	{ DRV260X_A_TO_V_MAX_OUT, 0xff },
+	{ DRV260X_RATED_VOLT, 0x3e },
+	{ DRV260X_OD_CLAMP_VOLT, 0x8c },
+	{ DRV260X_CAL_COMP, 0x0c },
+	{ DRV260X_CAL_BACK_EMF, 0x6c },
+	{ DRV260X_FEEDBACK_CTRL, 0x36 },
+	{ DRV260X_CTRL1, 0x93 },
+	{ DRV260X_CTRL2, 0xfa },
+	{ DRV260X_CTRL3, 0xa0 },
+	{ DRV260X_CTRL4, 0x20 },
+	{ DRV260X_CTRL5, 0x80 },
+	{ DRV260X_LRA_LOOP_PERIOD, 0x33 },
+	{ DRV260X_VBAT_MON, 0x00 },
+	{ DRV260X_LRA_RES_PERIOD, 0x00 },
+};
+
+#define DRV260X_DEF_RATED_VOLT		0x90
+#define DRV260X_DEF_OD_CLAMP_VOLT	0x90
+
+/**
+ * Rated and Overdriver Voltages:
+ * Calculated using the formula r = v * 255 / 5.6
+ * where r is what will be written to the register
+ * and v is the rated or overdriver voltage of the actuator
+ **/
+static int drv260x_calculate_voltage(unsigned int voltage)
+{
+	return (voltage * 255 / 5600);
+}
+
+static void drv260x_worker(struct work_struct *work)
+{
+	struct drv260x_data *haptics = container_of(work, struct drv260x_data, work);
+	int error;
+
+	gpiod_set_value(haptics->enable_gpio, 1);
+	/* Data sheet says to wait 250us before trying to communicate */
+	udelay(250);
+
+	error = regmap_write(haptics->regmap,
+			     DRV260X_MODE, DRV260X_RT_PLAYBACK);
+	if (error) {
+		dev_err(&haptics->client->dev,
+			"Failed to write set mode: %d\n", error);
+	} else {
+		error = regmap_write(haptics->regmap,
+				     DRV260X_RT_PB_IN, haptics->magnitude);
+		if (error)
+			dev_err(&haptics->client->dev,
+				"Failed to set magnitude: %d\n", error);
+	}
+}
+
+static int drv260x_haptics_play(struct input_dev *input, void *data,
+				struct ff_effect *effect)
+{
+	struct drv260x_data *haptics = input_get_drvdata(input);
+
+	haptics->mode = DRV260X_LRA_NO_CAL_MODE;
+
+	if (effect->u.rumble.strong_magnitude > 0)
+		haptics->magnitude = effect->u.rumble.strong_magnitude;
+	else if (effect->u.rumble.weak_magnitude > 0)
+		haptics->magnitude = effect->u.rumble.weak_magnitude;
+	else
+		haptics->magnitude = 0;
+
+	schedule_work(&haptics->work);
+
+	return 0;
+}
+
+static void drv260x_close(struct input_dev *input)
+{
+	struct drv260x_data *haptics = input_get_drvdata(input);
+	int error;
+
+	cancel_work_sync(&haptics->work);
+
+	error = regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY);
+	if (error)
+		dev_err(&haptics->client->dev,
+			"Failed to enter standby mode: %d\n", error);
+
+	gpiod_set_value(haptics->enable_gpio, 0);
+}
+
+static const struct reg_sequence drv260x_lra_cal_regs[] = {
+	{ DRV260X_MODE, DRV260X_AUTO_CAL },
+	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2 },
+	{ DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE |
+		DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH },
+};
+
+static const struct reg_sequence drv260x_lra_init_regs[] = {
+	{ DRV260X_MODE, DRV260X_RT_PLAYBACK },
+	{ DRV260X_A_TO_V_CTRL, DRV260X_AUDIO_HAPTICS_PEAK_20MS |
+		DRV260X_AUDIO_HAPTICS_FILTER_125HZ },
+	{ DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT },
+	{ DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT },
+	{ DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT },
+	{ DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT },
+	{ DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE |
+		DRV260X_BRAKE_FACTOR_2X | DRV260X_LOOP_GAIN_MED |
+		DRV260X_BEMF_GAIN_3 },
+	{ DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
+	{ DRV260X_CTRL2, DRV260X_SAMP_TIME_250 },
+	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ANANLOG_IN },
+	{ DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
+};
+
+static const struct reg_sequence drv260x_erm_cal_regs[] = {
+	{ DRV260X_MODE, DRV260X_AUTO_CAL },
+	{ DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT },
+	{ DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT },
+	{ DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT },
+	{ DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT },
+	{ DRV260X_FEEDBACK_CTRL, DRV260X_BRAKE_FACTOR_3X |
+		DRV260X_LOOP_GAIN_MED | DRV260X_BEMF_GAIN_2 },
+	{ DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
+	{ DRV260X_CTRL2, DRV260X_SAMP_TIME_250 | DRV260X_BLANK_TIME_75 |
+		DRV260X_IDISS_TIME_75 },
+	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ERM_OPEN_LOOP },
+	{ DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
+};
+
+static int drv260x_init(struct drv260x_data *haptics)
+{
+	int error;
+	unsigned int cal_buf;
+
+	error = regmap_write(haptics->regmap,
+			     DRV260X_RATED_VOLT, haptics->rated_voltage);
+	if (error) {
+		dev_err(&haptics->client->dev,
+			"Failed to write DRV260X_RATED_VOLT register: %d\n",
+			error);
+		return error;
+	}
+
+	error = regmap_write(haptics->regmap,
+			     DRV260X_OD_CLAMP_VOLT, haptics->overdrive_voltage);
+	if (error) {
+		dev_err(&haptics->client->dev,
+			"Failed to write DRV260X_OD_CLAMP_VOLT register: %d\n",
+			error);
+		return error;
+	}
+
+	switch (haptics->mode) {
+	case DRV260X_LRA_MODE:
+		error = regmap_register_patch(haptics->regmap,
+					      drv260x_lra_cal_regs,
+					      ARRAY_SIZE(drv260x_lra_cal_regs));
+		if (error) {
+			dev_err(&haptics->client->dev,
+				"Failed to write LRA calibration registers: %d\n",
+				error);
+			return error;
+		}
+
+		break;
+
+	case DRV260X_ERM_MODE:
+		error = regmap_register_patch(haptics->regmap,
+					      drv260x_erm_cal_regs,
+					      ARRAY_SIZE(drv260x_erm_cal_regs));
+		if (error) {
+			dev_err(&haptics->client->dev,
+				"Failed to write ERM calibration registers: %d\n",
+				error);
+			return error;
+		}
+
+		error = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL,
+					   DRV260X_LIB_SEL_MASK,
+					   haptics->library);
+		if (error) {
+			dev_err(&haptics->client->dev,
+				"Failed to write DRV260X_LIB_SEL register: %d\n",
+				error);
+			return error;
+		}
+
+		break;
+
+	default:
+		error = regmap_register_patch(haptics->regmap,
+					      drv260x_lra_init_regs,
+					      ARRAY_SIZE(drv260x_lra_init_regs));
+		if (error) {
+			dev_err(&haptics->client->dev,
+				"Failed to write LRA init registers: %d\n",
+				error);
+			return error;
+		}
+
+		error = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL,
+					   DRV260X_LIB_SEL_MASK,
+					   haptics->library);
+		if (error) {
+			dev_err(&haptics->client->dev,
+				"Failed to write DRV260X_LIB_SEL register: %d\n",
+				error);
+			return error;
+		}
+
+		/* No need to set GO bit here */
+		return 0;
+	}
+
+	error = regmap_write(haptics->regmap, DRV260X_GO, DRV260X_GO_BIT);
+	if (error) {
+		dev_err(&haptics->client->dev,
+			"Failed to write GO register: %d\n",
+			error);
+		return error;
+	}
+
+	do {
+		error = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf);
+		if (error) {
+			dev_err(&haptics->client->dev,
+				"Failed to read GO register: %d\n",
+				error);
+			return error;
+		}
+	} while (cal_buf == DRV260X_GO_BIT);
+
+	return 0;
+}
+
+static const struct regmap_config drv260x_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = DRV260X_MAX_REG,
+	.reg_defaults = drv260x_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(drv260x_reg_defs),
+	.cache_type = REGCACHE_NONE,
+};
+
+#ifdef CONFIG_OF
+static int drv260x_parse_dt(struct device *dev,
+			    struct drv260x_data *haptics)
+{
+	struct device_node *np = dev->of_node;
+	unsigned int voltage;
+	int error;
+
+	error = of_property_read_u32(np, "mode", &haptics->mode);
+	if (error) {
+		dev_err(dev, "%s: No entry for mode\n", __func__);
+		return error;
+	}
+
+	error = of_property_read_u32(np, "library-sel", &haptics->library);
+	if (error) {
+		dev_err(dev, "%s: No entry for library selection\n",
+			__func__);
+		return error;
+	}
+
+	error = of_property_read_u32(np, "vib-rated-mv", &voltage);
+	if (!error)
+		haptics->rated_voltage = drv260x_calculate_voltage(voltage);
+
+
+	error = of_property_read_u32(np, "vib-overdrive-mv", &voltage);
+	if (!error)
+		haptics->overdrive_voltage = drv260x_calculate_voltage(voltage);
+
+	return 0;
+}
+#else
+static inline int drv260x_parse_dt(struct device *dev,
+				   struct drv260x_data *haptics)
+{
+	dev_err(dev, "no platform data defined\n");
+
+	return -EINVAL;
+}
+#endif
+
+static int drv260x_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	const struct drv260x_platform_data *pdata = dev_get_platdata(&client->dev);
+	struct drv260x_data *haptics;
+	int error;
+
+	haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL);
+	if (!haptics)
+		return -ENOMEM;
+
+	haptics->rated_voltage = DRV260X_DEF_OD_CLAMP_VOLT;
+	haptics->rated_voltage = DRV260X_DEF_RATED_VOLT;
+
+	if (pdata) {
+		haptics->mode = pdata->mode;
+		haptics->library = pdata->library_selection;
+		if (pdata->vib_overdrive_voltage)
+			haptics->overdrive_voltage = drv260x_calculate_voltage(pdata->vib_overdrive_voltage);
+		if (pdata->vib_rated_voltage)
+			haptics->rated_voltage = drv260x_calculate_voltage(pdata->vib_rated_voltage);
+	} else if (client->dev.of_node) {
+		error = drv260x_parse_dt(&client->dev, haptics);
+		if (error)
+			return error;
+	} else {
+		dev_err(&client->dev, "Platform data not set\n");
+		return -ENODEV;
+	}
+
+
+	if (haptics->mode < DRV260X_LRA_MODE ||
+	    haptics->mode > DRV260X_ERM_MODE) {
+		dev_err(&client->dev,
+			"Vibrator mode is invalid: %i\n",
+			haptics->mode);
+		return -EINVAL;
+	}
+
+	if (haptics->library < DRV260X_LIB_EMPTY ||
+	    haptics->library > DRV260X_ERM_LIB_F) {
+		dev_err(&client->dev,
+			"Library value is invalid: %i\n", haptics->library);
+		return -EINVAL;
+	}
+
+	if (haptics->mode == DRV260X_LRA_MODE &&
+	    haptics->library != DRV260X_LIB_EMPTY &&
+	    haptics->library != DRV260X_LIB_LRA) {
+		dev_err(&client->dev,
+			"LRA Mode with ERM Library mismatch\n");
+		return -EINVAL;
+	}
+
+	if (haptics->mode == DRV260X_ERM_MODE &&
+	    (haptics->library == DRV260X_LIB_EMPTY ||
+	     haptics->library == DRV260X_LIB_LRA)) {
+		dev_err(&client->dev,
+			"ERM Mode with LRA Library mismatch\n");
+		return -EINVAL;
+	}
+
+	haptics->regulator = devm_regulator_get(&client->dev, "vbat");
+	if (IS_ERR(haptics->regulator)) {
+		error = PTR_ERR(haptics->regulator);
+		dev_err(&client->dev,
+			"unable to get regulator, error: %d\n", error);
+		return error;
+	}
+
+	haptics->enable_gpio = devm_gpiod_get_optional(&client->dev, "enable",
+						       GPIOD_OUT_HIGH);
+	if (IS_ERR(haptics->enable_gpio))
+		return PTR_ERR(haptics->enable_gpio);
+
+	haptics->input_dev = devm_input_allocate_device(&client->dev);
+	if (!haptics->input_dev) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	haptics->input_dev->name = "drv260x:haptics";
+	haptics->input_dev->close = drv260x_close;
+	input_set_drvdata(haptics->input_dev, haptics);
+	input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
+
+	error = input_ff_create_memless(haptics->input_dev, NULL,
+					drv260x_haptics_play);
+	if (error) {
+		dev_err(&client->dev, "input_ff_create() failed: %d\n",
+			error);
+		return error;
+	}
+
+	INIT_WORK(&haptics->work, drv260x_worker);
+
+	haptics->client = client;
+	i2c_set_clientdata(client, haptics);
+
+	haptics->regmap = devm_regmap_init_i2c(client, &drv260x_regmap_config);
+	if (IS_ERR(haptics->regmap)) {
+		error = PTR_ERR(haptics->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+			error);
+		return error;
+	}
+
+	error = drv260x_init(haptics);
+	if (error) {
+		dev_err(&client->dev, "Device init failed: %d\n", error);
+		return error;
+	}
+
+	error = input_register_device(haptics->input_dev);
+	if (error) {
+		dev_err(&client->dev, "couldn't register input device: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused drv260x_suspend(struct device *dev)
+{
+	struct drv260x_data *haptics = dev_get_drvdata(dev);
+	int ret = 0;
+
+	mutex_lock(&haptics->input_dev->mutex);
+
+	if (haptics->input_dev->users) {
+		ret = regmap_update_bits(haptics->regmap,
+					 DRV260X_MODE,
+					 DRV260X_STANDBY_MASK,
+					 DRV260X_STANDBY);
+		if (ret) {
+			dev_err(dev, "Failed to set standby mode\n");
+			goto out;
+		}
+
+		gpiod_set_value(haptics->enable_gpio, 0);
+
+		ret = regulator_disable(haptics->regulator);
+		if (ret) {
+			dev_err(dev, "Failed to disable regulator\n");
+			regmap_update_bits(haptics->regmap,
+					   DRV260X_MODE,
+					   DRV260X_STANDBY_MASK, 0);
+		}
+	}
+out:
+	mutex_unlock(&haptics->input_dev->mutex);
+	return ret;
+}
+
+static int __maybe_unused drv260x_resume(struct device *dev)
+{
+	struct drv260x_data *haptics = dev_get_drvdata(dev);
+	int ret = 0;
+
+	mutex_lock(&haptics->input_dev->mutex);
+
+	if (haptics->input_dev->users) {
+		ret = regulator_enable(haptics->regulator);
+		if (ret) {
+			dev_err(dev, "Failed to enable regulator\n");
+			goto out;
+		}
+
+		ret = regmap_update_bits(haptics->regmap,
+					 DRV260X_MODE,
+					 DRV260X_STANDBY_MASK, 0);
+		if (ret) {
+			dev_err(dev, "Failed to unset standby mode\n");
+			regulator_disable(haptics->regulator);
+			goto out;
+		}
+
+		gpiod_set_value(haptics->enable_gpio, 1);
+	}
+
+out:
+	mutex_unlock(&haptics->input_dev->mutex);
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume);
+
+static const struct i2c_device_id drv260x_id[] = {
+	{ "drv2605l", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, drv260x_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id drv260x_of_match[] = {
+	{ .compatible = "ti,drv2604", },
+	{ .compatible = "ti,drv2604l", },
+	{ .compatible = "ti,drv2605", },
+	{ .compatible = "ti,drv2605l", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, drv260x_of_match);
+#endif
+
+static struct i2c_driver drv260x_driver = {
+	.probe		= drv260x_probe,
+	.driver		= {
+		.name	= "drv260x-haptics",
+		.of_match_table = of_match_ptr(drv260x_of_match),
+		.pm	= &drv260x_pm_ops,
+	},
+	.id_table = drv260x_id,
+};
+module_i2c_driver(drv260x_driver);
+
+MODULE_DESCRIPTION("TI DRV260x haptics driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
diff --git a/drivers/input/misc/drv2665.c b/drivers/input/misc/drv2665.c
new file mode 100644
index 0000000..ef9bc12
--- /dev/null
+++ b/drivers/input/misc/drv2665.c
@@ -0,0 +1,321 @@
+/*
+ * DRV2665 haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright: (C) 2015 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+/* Contol registers */
+#define DRV2665_STATUS	0x00
+#define DRV2665_CTRL_1	0x01
+#define DRV2665_CTRL_2	0x02
+#define DRV2665_FIFO	0x0b
+
+/* Status Register */
+#define DRV2665_FIFO_FULL		BIT(0)
+#define DRV2665_FIFO_EMPTY		BIT(1)
+
+/* Control 1 Register */
+#define DRV2665_25_VPP_GAIN		0x00
+#define DRV2665_50_VPP_GAIN		0x01
+#define DRV2665_75_VPP_GAIN		0x02
+#define DRV2665_100_VPP_GAIN		0x03
+#define DRV2665_DIGITAL_IN		0xfc
+#define DRV2665_ANALOG_IN		BIT(2)
+
+/* Control 2 Register */
+#define DRV2665_BOOST_EN		BIT(1)
+#define DRV2665_STANDBY			BIT(6)
+#define DRV2665_DEV_RST			BIT(7)
+#define DRV2665_5_MS_IDLE_TOUT		0x00
+#define DRV2665_10_MS_IDLE_TOUT		0x04
+#define DRV2665_15_MS_IDLE_TOUT		0x08
+#define DRV2665_20_MS_IDLE_TOUT		0x0c
+
+/**
+ * struct drv2665_data -
+ * @input_dev - Pointer to the input device
+ * @client - Pointer to the I2C client
+ * @regmap - Register map of the device
+ * @work - Work item used to off load the enable/disable of the vibration
+ * @regulator - Pointer to the regulator for the IC
+ */
+struct drv2665_data {
+	struct input_dev *input_dev;
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct work_struct work;
+	struct regulator *regulator;
+};
+
+/* 8kHz Sine wave to stream to the FIFO */
+static const u8 drv2665_sine_wave_form[] = {
+	0x00, 0x10, 0x20, 0x2e, 0x3c, 0x48, 0x53, 0x5b, 0x61, 0x65, 0x66,
+	0x65, 0x61, 0x5b, 0x53, 0x48, 0x3c, 0x2e, 0x20, 0x10,
+	0x00, 0xf0, 0xe0, 0xd2, 0xc4, 0xb8, 0xad, 0xa5, 0x9f, 0x9b, 0x9a,
+	0x9b, 0x9f, 0xa5, 0xad, 0xb8, 0xc4, 0xd2, 0xe0, 0xf0, 0x00,
+};
+
+static const struct reg_default drv2665_reg_defs[] = {
+	{ DRV2665_STATUS, 0x02 },
+	{ DRV2665_CTRL_1, 0x28 },
+	{ DRV2665_CTRL_2, 0x40 },
+	{ DRV2665_FIFO, 0x00 },
+};
+
+static void drv2665_worker(struct work_struct *work)
+{
+	struct drv2665_data *haptics =
+				container_of(work, struct drv2665_data, work);
+	unsigned int read_buf;
+	int error;
+
+	error = regmap_read(haptics->regmap, DRV2665_STATUS, &read_buf);
+	if (error) {
+		dev_err(&haptics->client->dev,
+			"Failed to read status: %d\n", error);
+		return;
+	}
+
+	if (read_buf & DRV2665_FIFO_EMPTY) {
+		error = regmap_bulk_write(haptics->regmap,
+					  DRV2665_FIFO,
+					  drv2665_sine_wave_form,
+					  ARRAY_SIZE(drv2665_sine_wave_form));
+		if (error) {
+			dev_err(&haptics->client->dev,
+				"Failed to write FIFO: %d\n", error);
+			return;
+		}
+	}
+}
+
+static int drv2665_haptics_play(struct input_dev *input, void *data,
+				struct ff_effect *effect)
+{
+	struct drv2665_data *haptics = input_get_drvdata(input);
+
+	schedule_work(&haptics->work);
+
+	return 0;
+}
+
+static void drv2665_close(struct input_dev *input)
+{
+	struct drv2665_data *haptics = input_get_drvdata(input);
+	int error;
+
+	cancel_work_sync(&haptics->work);
+
+	error = regmap_update_bits(haptics->regmap,
+				   DRV2665_CTRL_2, DRV2665_STANDBY, 1);
+	if (error)
+		dev_err(&haptics->client->dev,
+			"Failed to enter standby mode: %d\n", error);
+}
+
+static const struct reg_sequence drv2665_init_regs[] = {
+	{ DRV2665_CTRL_2, 0 | DRV2665_10_MS_IDLE_TOUT },
+	{ DRV2665_CTRL_1, DRV2665_25_VPP_GAIN },
+};
+
+static int drv2665_init(struct drv2665_data *haptics)
+{
+	int error;
+
+	error = regmap_register_patch(haptics->regmap,
+				      drv2665_init_regs,
+				      ARRAY_SIZE(drv2665_init_regs));
+	if (error) {
+		dev_err(&haptics->client->dev,
+			"Failed to write init registers: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static const struct regmap_config drv2665_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = DRV2665_FIFO,
+	.reg_defaults = drv2665_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(drv2665_reg_defs),
+	.cache_type = REGCACHE_NONE,
+};
+
+static int drv2665_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct drv2665_data *haptics;
+	int error;
+
+	haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL);
+	if (!haptics)
+		return -ENOMEM;
+
+	haptics->regulator = devm_regulator_get(&client->dev, "vbat");
+	if (IS_ERR(haptics->regulator)) {
+		error = PTR_ERR(haptics->regulator);
+		dev_err(&client->dev,
+			"unable to get regulator, error: %d\n", error);
+		return error;
+	}
+
+	haptics->input_dev = devm_input_allocate_device(&client->dev);
+	if (!haptics->input_dev) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	haptics->input_dev->name = "drv2665:haptics";
+	haptics->input_dev->dev.parent = client->dev.parent;
+	haptics->input_dev->close = drv2665_close;
+	input_set_drvdata(haptics->input_dev, haptics);
+	input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
+
+	error = input_ff_create_memless(haptics->input_dev, NULL,
+					drv2665_haptics_play);
+	if (error) {
+		dev_err(&client->dev, "input_ff_create() failed: %d\n",
+			error);
+		return error;
+	}
+
+	INIT_WORK(&haptics->work, drv2665_worker);
+
+	haptics->client = client;
+	i2c_set_clientdata(client, haptics);
+
+	haptics->regmap = devm_regmap_init_i2c(client, &drv2665_regmap_config);
+	if (IS_ERR(haptics->regmap)) {
+		error = PTR_ERR(haptics->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+			error);
+		return error;
+	}
+
+	error = drv2665_init(haptics);
+	if (error) {
+		dev_err(&client->dev, "Device init failed: %d\n", error);
+		return error;
+	}
+
+	error = input_register_device(haptics->input_dev);
+	if (error) {
+		dev_err(&client->dev, "couldn't register input device: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused drv2665_suspend(struct device *dev)
+{
+	struct drv2665_data *haptics = dev_get_drvdata(dev);
+	int ret = 0;
+
+	mutex_lock(&haptics->input_dev->mutex);
+
+	if (haptics->input_dev->users) {
+		ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2,
+				DRV2665_STANDBY, 1);
+		if (ret) {
+			dev_err(dev, "Failed to set standby mode\n");
+			regulator_disable(haptics->regulator);
+			goto out;
+		}
+
+		ret = regulator_disable(haptics->regulator);
+		if (ret) {
+			dev_err(dev, "Failed to disable regulator\n");
+			regmap_update_bits(haptics->regmap,
+					   DRV2665_CTRL_2,
+					   DRV2665_STANDBY, 0);
+		}
+	}
+out:
+	mutex_unlock(&haptics->input_dev->mutex);
+	return ret;
+}
+
+static int __maybe_unused drv2665_resume(struct device *dev)
+{
+	struct drv2665_data *haptics = dev_get_drvdata(dev);
+	int ret = 0;
+
+	mutex_lock(&haptics->input_dev->mutex);
+
+	if (haptics->input_dev->users) {
+		ret = regulator_enable(haptics->regulator);
+		if (ret) {
+			dev_err(dev, "Failed to enable regulator\n");
+			goto out;
+		}
+
+		ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2,
+					 DRV2665_STANDBY, 0);
+		if (ret) {
+			dev_err(dev, "Failed to unset standby mode\n");
+			regulator_disable(haptics->regulator);
+			goto out;
+		}
+
+	}
+
+out:
+	mutex_unlock(&haptics->input_dev->mutex);
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(drv2665_pm_ops, drv2665_suspend, drv2665_resume);
+
+static const struct i2c_device_id drv2665_id[] = {
+	{ "drv2665", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, drv2665_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id drv2665_of_match[] = {
+	{ .compatible = "ti,drv2665", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, drv2665_of_match);
+#endif
+
+static struct i2c_driver drv2665_driver = {
+	.probe		= drv2665_probe,
+	.driver		= {
+		.name	= "drv2665-haptics",
+		.of_match_table = of_match_ptr(drv2665_of_match),
+		.pm	= &drv2665_pm_ops,
+	},
+	.id_table = drv2665_id,
+};
+module_i2c_driver(drv2665_driver);
+
+MODULE_DESCRIPTION("TI DRV2665 haptics driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
diff --git a/drivers/input/misc/drv2667.c b/drivers/input/misc/drv2667.c
new file mode 100644
index 0000000..d5ba748
--- /dev/null
+++ b/drivers/input/misc/drv2667.c
@@ -0,0 +1,496 @@
+/*
+ * DRV2667 haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright: (C) 2014 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+/* Contol registers */
+#define DRV2667_STATUS	0x00
+#define DRV2667_CTRL_1	0x01
+#define DRV2667_CTRL_2	0x02
+/* Waveform sequencer */
+#define DRV2667_WV_SEQ_0	0x03
+#define DRV2667_WV_SEQ_1	0x04
+#define DRV2667_WV_SEQ_2	0x05
+#define DRV2667_WV_SEQ_3	0x06
+#define DRV2667_WV_SEQ_4	0x07
+#define DRV2667_WV_SEQ_5	0x08
+#define DRV2667_WV_SEQ_6	0x09
+#define DRV2667_WV_SEQ_7	0x0A
+#define DRV2667_FIFO		0x0B
+#define DRV2667_PAGE		0xFF
+#define DRV2667_MAX_REG		DRV2667_PAGE
+
+#define DRV2667_PAGE_0		0x00
+#define DRV2667_PAGE_1		0x01
+#define DRV2667_PAGE_2		0x02
+#define DRV2667_PAGE_3		0x03
+#define DRV2667_PAGE_4		0x04
+#define DRV2667_PAGE_5		0x05
+#define DRV2667_PAGE_6		0x06
+#define DRV2667_PAGE_7		0x07
+#define DRV2667_PAGE_8		0x08
+
+/* RAM fields */
+#define DRV2667_RAM_HDR_SZ	0x0
+/* RAM Header addresses */
+#define DRV2667_RAM_START_HI	0x01
+#define DRV2667_RAM_START_LO	0x02
+#define DRV2667_RAM_STOP_HI		0x03
+#define DRV2667_RAM_STOP_LO		0x04
+#define DRV2667_RAM_REPEAT_CT	0x05
+/* RAM data addresses */
+#define DRV2667_RAM_AMP		0x06
+#define DRV2667_RAM_FREQ	0x07
+#define DRV2667_RAM_DURATION	0x08
+#define DRV2667_RAM_ENVELOPE	0x09
+
+/* Control 1 Register */
+#define DRV2667_25_VPP_GAIN		0x00
+#define DRV2667_50_VPP_GAIN		0x01
+#define DRV2667_75_VPP_GAIN		0x02
+#define DRV2667_100_VPP_GAIN	0x03
+#define DRV2667_DIGITAL_IN		0xfc
+#define DRV2667_ANALOG_IN		(1 << 2)
+
+/* Control 2 Register */
+#define DRV2667_GO			(1 << 0)
+#define DRV2667_STANDBY		(1 << 6)
+#define DRV2667_DEV_RST		(1 << 7)
+
+/* RAM Envelope settings */
+#define DRV2667_NO_ENV			0x00
+#define DRV2667_32_MS_ENV		0x01
+#define DRV2667_64_MS_ENV		0x02
+#define DRV2667_96_MS_ENV		0x03
+#define DRV2667_128_MS_ENV		0x04
+#define DRV2667_160_MS_ENV		0x05
+#define DRV2667_192_MS_ENV		0x06
+#define DRV2667_224_MS_ENV		0x07
+#define DRV2667_256_MS_ENV		0x08
+#define DRV2667_512_MS_ENV		0x09
+#define DRV2667_768_MS_ENV		0x0a
+#define DRV2667_1024_MS_ENV		0x0b
+#define DRV2667_1280_MS_ENV		0x0c
+#define DRV2667_1536_MS_ENV		0x0d
+#define DRV2667_1792_MS_ENV		0x0e
+#define DRV2667_2048_MS_ENV		0x0f
+
+/**
+ * struct drv2667_data -
+ * @input_dev - Pointer to the input device
+ * @client - Pointer to the I2C client
+ * @regmap - Register map of the device
+ * @work - Work item used to off load the enable/disable of the vibration
+ * @regulator - Pointer to the regulator for the IC
+ * @magnitude - Magnitude of the vibration event
+**/
+struct drv2667_data {
+	struct input_dev *input_dev;
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct work_struct work;
+	struct regulator *regulator;
+	u32 page;
+	u32 magnitude;
+	u32 frequency;
+};
+
+static const struct reg_default drv2667_reg_defs[] = {
+	{ DRV2667_STATUS, 0x02 },
+	{ DRV2667_CTRL_1, 0x28 },
+	{ DRV2667_CTRL_2, 0x40 },
+	{ DRV2667_WV_SEQ_0, 0x00 },
+	{ DRV2667_WV_SEQ_1, 0x00 },
+	{ DRV2667_WV_SEQ_2, 0x00 },
+	{ DRV2667_WV_SEQ_3, 0x00 },
+	{ DRV2667_WV_SEQ_4, 0x00 },
+	{ DRV2667_WV_SEQ_5, 0x00 },
+	{ DRV2667_WV_SEQ_6, 0x00 },
+	{ DRV2667_WV_SEQ_7, 0x00 },
+	{ DRV2667_FIFO, 0x00 },
+	{ DRV2667_PAGE, 0x00 },
+};
+
+static int drv2667_set_waveform_freq(struct drv2667_data *haptics)
+{
+	unsigned int read_buf;
+	int freq;
+	int error;
+
+	/* Per the data sheet:
+	 * Sinusoid Frequency (Hz) = 7.8125 x Frequency
+	 */
+	freq = (haptics->frequency * 1000) / 78125;
+	if (freq <= 0) {
+		dev_err(&haptics->client->dev,
+			"ERROR: Frequency calculated to %i\n", freq);
+		return -EINVAL;
+	}
+
+	error = regmap_read(haptics->regmap, DRV2667_PAGE, &read_buf);
+	if (error) {
+		dev_err(&haptics->client->dev,
+			"Failed to read the page number: %d\n", error);
+		return -EIO;
+	}
+
+	if (read_buf == DRV2667_PAGE_0 ||
+		haptics->page != read_buf) {
+		error = regmap_write(haptics->regmap,
+				DRV2667_PAGE, haptics->page);
+		if (error) {
+			dev_err(&haptics->client->dev,
+				"Failed to set the page: %d\n", error);
+			return -EIO;
+		}
+	}
+
+	error = regmap_write(haptics->regmap, DRV2667_RAM_FREQ,	freq);
+	if (error)
+		dev_err(&haptics->client->dev,
+				"Failed to set the frequency: %d\n", error);
+
+	/* Reset back to original page */
+	if (read_buf == DRV2667_PAGE_0 ||
+		haptics->page != read_buf) {
+		error = regmap_write(haptics->regmap, DRV2667_PAGE, read_buf);
+		if (error) {
+			dev_err(&haptics->client->dev,
+					"Failed to set the page: %d\n", error);
+				return -EIO;
+			}
+	}
+
+	return error;
+}
+
+static void drv2667_worker(struct work_struct *work)
+{
+	struct drv2667_data *haptics = container_of(work, struct drv2667_data, work);
+	int error;
+
+	if (haptics->magnitude) {
+		error = regmap_write(haptics->regmap,
+				DRV2667_PAGE, haptics->page);
+		if (error) {
+			dev_err(&haptics->client->dev,
+				"Failed to set the page: %d\n", error);
+			return;
+		}
+
+		error = regmap_write(haptics->regmap, DRV2667_RAM_AMP,
+				haptics->magnitude);
+		if (error) {
+			dev_err(&haptics->client->dev,
+				"Failed to set the amplitude: %d\n", error);
+			return;
+		}
+
+		error = regmap_write(haptics->regmap,
+				DRV2667_PAGE, DRV2667_PAGE_0);
+		if (error) {
+			dev_err(&haptics->client->dev,
+				"Failed to set the page: %d\n", error);
+			return;
+		}
+
+		error = regmap_write(haptics->regmap,
+				DRV2667_CTRL_2, DRV2667_GO);
+		if (error) {
+			dev_err(&haptics->client->dev,
+				"Failed to set the GO bit: %d\n", error);
+		}
+	} else {
+		error = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2,
+				DRV2667_GO, 0);
+		if (error) {
+			dev_err(&haptics->client->dev,
+				"Failed to unset the GO bit: %d\n", error);
+		}
+	}
+}
+
+static int drv2667_haptics_play(struct input_dev *input, void *data,
+				struct ff_effect *effect)
+{
+	struct drv2667_data *haptics = input_get_drvdata(input);
+
+	if (effect->u.rumble.strong_magnitude > 0)
+		haptics->magnitude = effect->u.rumble.strong_magnitude;
+	else if (effect->u.rumble.weak_magnitude > 0)
+		haptics->magnitude = effect->u.rumble.weak_magnitude;
+	else
+		haptics->magnitude = 0;
+
+	schedule_work(&haptics->work);
+
+	return 0;
+}
+
+static void drv2667_close(struct input_dev *input)
+{
+	struct drv2667_data *haptics = input_get_drvdata(input);
+	int error;
+
+	cancel_work_sync(&haptics->work);
+
+	error = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2,
+				DRV2667_STANDBY, 1);
+	if (error)
+		dev_err(&haptics->client->dev,
+			"Failed to enter standby mode: %d\n", error);
+}
+
+static const struct reg_sequence drv2667_init_regs[] = {
+	{ DRV2667_CTRL_2, 0 },
+	{ DRV2667_CTRL_1, DRV2667_25_VPP_GAIN },
+	{ DRV2667_WV_SEQ_0, 1 },
+	{ DRV2667_WV_SEQ_1, 0 }
+};
+
+static const struct reg_sequence drv2667_page1_init[] = {
+	{ DRV2667_RAM_HDR_SZ, 0x05 },
+	{ DRV2667_RAM_START_HI, 0x80 },
+	{ DRV2667_RAM_START_LO, 0x06 },
+	{ DRV2667_RAM_STOP_HI, 0x00 },
+	{ DRV2667_RAM_STOP_LO, 0x09 },
+	{ DRV2667_RAM_REPEAT_CT, 0 },
+	{ DRV2667_RAM_DURATION, 0x05 },
+	{ DRV2667_RAM_ENVELOPE, DRV2667_NO_ENV },
+	{ DRV2667_RAM_AMP, 0x60 },
+};
+
+static int drv2667_init(struct drv2667_data *haptics)
+{
+	int error;
+
+	/* Set default haptic frequency to 195Hz on Page 1*/
+	haptics->frequency = 195;
+	haptics->page = DRV2667_PAGE_1;
+
+	error = regmap_register_patch(haptics->regmap,
+				      drv2667_init_regs,
+				      ARRAY_SIZE(drv2667_init_regs));
+	if (error) {
+		dev_err(&haptics->client->dev,
+			"Failed to write init registers: %d\n",
+			error);
+		return error;
+	}
+
+	error = regmap_write(haptics->regmap, DRV2667_PAGE, haptics->page);
+	if (error) {
+		dev_err(&haptics->client->dev, "Failed to set page: %d\n",
+			error);
+		goto error_out;
+	}
+
+	error = drv2667_set_waveform_freq(haptics);
+	if (error)
+		goto error_page;
+
+	error = regmap_register_patch(haptics->regmap,
+				      drv2667_page1_init,
+				      ARRAY_SIZE(drv2667_page1_init));
+	if (error) {
+		dev_err(&haptics->client->dev,
+			"Failed to write page registers: %d\n",
+			error);
+		return error;
+	}
+
+	error = regmap_write(haptics->regmap, DRV2667_PAGE, DRV2667_PAGE_0);
+	return error;
+
+error_page:
+	regmap_write(haptics->regmap, DRV2667_PAGE, DRV2667_PAGE_0);
+error_out:
+	return error;
+}
+
+static const struct regmap_config drv2667_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = DRV2667_MAX_REG,
+	.reg_defaults = drv2667_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(drv2667_reg_defs),
+	.cache_type = REGCACHE_NONE,
+};
+
+static int drv2667_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct drv2667_data *haptics;
+	int error;
+
+	haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL);
+	if (!haptics)
+		return -ENOMEM;
+
+	haptics->regulator = devm_regulator_get(&client->dev, "vbat");
+	if (IS_ERR(haptics->regulator)) {
+		error = PTR_ERR(haptics->regulator);
+		dev_err(&client->dev,
+			"unable to get regulator, error: %d\n", error);
+		return error;
+	}
+
+	haptics->input_dev = devm_input_allocate_device(&client->dev);
+	if (!haptics->input_dev) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	haptics->input_dev->name = "drv2667:haptics";
+	haptics->input_dev->dev.parent = client->dev.parent;
+	haptics->input_dev->close = drv2667_close;
+	input_set_drvdata(haptics->input_dev, haptics);
+	input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
+
+	error = input_ff_create_memless(haptics->input_dev, NULL,
+					drv2667_haptics_play);
+	if (error) {
+		dev_err(&client->dev, "input_ff_create() failed: %d\n",
+			error);
+		return error;
+	}
+
+	INIT_WORK(&haptics->work, drv2667_worker);
+
+	haptics->client = client;
+	i2c_set_clientdata(client, haptics);
+
+	haptics->regmap = devm_regmap_init_i2c(client, &drv2667_regmap_config);
+	if (IS_ERR(haptics->regmap)) {
+		error = PTR_ERR(haptics->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+			error);
+		return error;
+	}
+
+	error = drv2667_init(haptics);
+	if (error) {
+		dev_err(&client->dev, "Device init failed: %d\n", error);
+		return error;
+	}
+
+	error = input_register_device(haptics->input_dev);
+	if (error) {
+		dev_err(&client->dev, "couldn't register input device: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused drv2667_suspend(struct device *dev)
+{
+	struct drv2667_data *haptics = dev_get_drvdata(dev);
+	int ret = 0;
+
+	mutex_lock(&haptics->input_dev->mutex);
+
+	if (haptics->input_dev->users) {
+		ret = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2,
+				DRV2667_STANDBY, 1);
+		if (ret) {
+			dev_err(dev, "Failed to set standby mode\n");
+			regulator_disable(haptics->regulator);
+			goto out;
+		}
+
+		ret = regulator_disable(haptics->regulator);
+		if (ret) {
+			dev_err(dev, "Failed to disable regulator\n");
+			regmap_update_bits(haptics->regmap,
+					   DRV2667_CTRL_2,
+					   DRV2667_STANDBY, 0);
+		}
+	}
+out:
+	mutex_unlock(&haptics->input_dev->mutex);
+	return ret;
+}
+
+static int __maybe_unused drv2667_resume(struct device *dev)
+{
+	struct drv2667_data *haptics = dev_get_drvdata(dev);
+	int ret = 0;
+
+	mutex_lock(&haptics->input_dev->mutex);
+
+	if (haptics->input_dev->users) {
+		ret = regulator_enable(haptics->regulator);
+		if (ret) {
+			dev_err(dev, "Failed to enable regulator\n");
+			goto out;
+		}
+
+		ret = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2,
+					 DRV2667_STANDBY, 0);
+		if (ret) {
+			dev_err(dev, "Failed to unset standby mode\n");
+			regulator_disable(haptics->regulator);
+			goto out;
+		}
+
+	}
+
+out:
+	mutex_unlock(&haptics->input_dev->mutex);
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(drv2667_pm_ops, drv2667_suspend, drv2667_resume);
+
+static const struct i2c_device_id drv2667_id[] = {
+	{ "drv2667", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, drv2667_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id drv2667_of_match[] = {
+	{ .compatible = "ti,drv2667", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, drv2667_of_match);
+#endif
+
+static struct i2c_driver drv2667_driver = {
+	.probe		= drv2667_probe,
+	.driver		= {
+		.name	= "drv2667-haptics",
+		.of_match_table = of_match_ptr(drv2667_of_match),
+		.pm	= &drv2667_pm_ops,
+	},
+	.id_table = drv2667_id,
+};
+module_i2c_driver(drv2667_driver);
+
+MODULE_DESCRIPTION("TI DRV2667 haptics driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
diff --git a/drivers/input/misc/e3x0-button.c b/drivers/input/misc/e3x0-button.c
new file mode 100644
index 0000000..13bfca8
--- /dev/null
+++ b/drivers/input/misc/e3x0-button.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2014, National Instruments Corp. All rights reserved.
+ *
+ * Driver for NI Ettus Research USRP E3x0 Button Driver
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+static irqreturn_t e3x0_button_release_handler(int irq, void *data)
+{
+	struct input_dev *idev = data;
+
+	input_report_key(idev, KEY_POWER, 0);
+	input_sync(idev);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t e3x0_button_press_handler(int irq, void *data)
+{
+	struct input_dev *idev = data;
+
+	input_report_key(idev, KEY_POWER, 1);
+	pm_wakeup_event(idev->dev.parent, 0);
+	input_sync(idev);
+
+	return IRQ_HANDLED;
+}
+
+static int __maybe_unused e3x0_button_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(platform_get_irq_byname(pdev, "press"));
+
+	return 0;
+}
+
+static int __maybe_unused e3x0_button_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(platform_get_irq_byname(pdev, "press"));
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(e3x0_button_pm_ops,
+			 e3x0_button_suspend, e3x0_button_resume);
+
+static int e3x0_button_probe(struct platform_device *pdev)
+{
+	struct input_dev *input;
+	int irq_press, irq_release;
+	int error;
+
+	irq_press = platform_get_irq_byname(pdev, "press");
+	if (irq_press < 0) {
+		dev_err(&pdev->dev, "No IRQ for 'press', error=%d\n",
+			irq_press);
+		return irq_press;
+	}
+
+	irq_release = platform_get_irq_byname(pdev, "release");
+	if (irq_release < 0) {
+		dev_err(&pdev->dev, "No IRQ for 'release', error=%d\n",
+			irq_release);
+		return irq_release;
+	}
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input)
+		return -ENOMEM;
+
+	input->name = "NI Ettus Research USRP E3x0 Button Driver";
+	input->phys = "e3x0_button/input0";
+	input->dev.parent = &pdev->dev;
+
+	input_set_capability(input, EV_KEY, KEY_POWER);
+
+	error = devm_request_irq(&pdev->dev, irq_press,
+				 e3x0_button_press_handler, 0,
+				 "e3x0-button", input);
+	if (error) {
+		dev_err(&pdev->dev, "Failed to request 'press' IRQ#%d: %d\n",
+			irq_press, error);
+		return error;
+	}
+
+	error = devm_request_irq(&pdev->dev, irq_release,
+				 e3x0_button_release_handler, 0,
+				 "e3x0-button", input);
+	if (error) {
+		dev_err(&pdev->dev, "Failed to request 'release' IRQ#%d: %d\n",
+			irq_release, error);
+		return error;
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(&pdev->dev, "Can't register input device: %d\n", error);
+		return error;
+	}
+
+	platform_set_drvdata(pdev, input);
+	device_init_wakeup(&pdev->dev, 1);
+	return 0;
+}
+
+static int e3x0_button_remove(struct platform_device *pdev)
+{
+	device_init_wakeup(&pdev->dev, 0);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id e3x0_button_match[] = {
+	{ .compatible = "ettus,e3x0-button", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, e3x0_button_match);
+#endif
+
+static struct platform_driver e3x0_button_driver = {
+	.driver		= {
+		.name	= "e3x0-button",
+		.of_match_table = of_match_ptr(e3x0_button_match),
+		.pm	= &e3x0_button_pm_ops,
+	},
+	.probe		= e3x0_button_probe,
+	.remove		= e3x0_button_remove,
+};
+
+module_platform_driver(e3x0_button_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Moritz Fischer <moritz.fischer@ettus.com>");
+MODULE_DESCRIPTION("NI Ettus Research USRP E3x0 Button driver");
+MODULE_ALIAS("platform:e3x0-button");
diff --git a/drivers/input/misc/gp2ap002a00f.c b/drivers/input/misc/gp2ap002a00f.c
new file mode 100644
index 0000000..3bfdfcc
--- /dev/null
+++ b/drivers/input/misc/gp2ap002a00f.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications Inc.
+ *
+ * Author: Courtney Cavin <courtney.cavin@sonyericsson.com>
+ * Prepared for up-stream by: Oskar Andero <oskar.andero@sonyericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/input/gp2ap002a00f.h>
+
+struct gp2a_data {
+	struct input_dev *input;
+	const struct gp2a_platform_data *pdata;
+	struct i2c_client *i2c_client;
+};
+
+enum gp2a_addr {
+	GP2A_ADDR_PROX	= 0x0,
+	GP2A_ADDR_GAIN	= 0x1,
+	GP2A_ADDR_HYS	= 0x2,
+	GP2A_ADDR_CYCLE	= 0x3,
+	GP2A_ADDR_OPMOD	= 0x4,
+	GP2A_ADDR_CON	= 0x6
+};
+
+enum gp2a_controls {
+	/* Software Shutdown control: 0 = shutdown, 1 = normal operation */
+	GP2A_CTRL_SSD	= 0x01
+};
+
+static int gp2a_report(struct gp2a_data *dt)
+{
+	int vo = gpio_get_value(dt->pdata->vout_gpio);
+
+	input_report_switch(dt->input, SW_FRONT_PROXIMITY, !vo);
+	input_sync(dt->input);
+
+	return 0;
+}
+
+static irqreturn_t gp2a_irq(int irq, void *handle)
+{
+	struct gp2a_data *dt = handle;
+
+	gp2a_report(dt);
+
+	return IRQ_HANDLED;
+}
+
+static int gp2a_enable(struct gp2a_data *dt)
+{
+	return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD,
+					 GP2A_CTRL_SSD);
+}
+
+static int gp2a_disable(struct gp2a_data *dt)
+{
+	return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD,
+					 0x00);
+}
+
+static int gp2a_device_open(struct input_dev *dev)
+{
+	struct gp2a_data *dt = input_get_drvdata(dev);
+	int error;
+
+	error = gp2a_enable(dt);
+	if (error < 0) {
+		dev_err(&dt->i2c_client->dev,
+			"unable to activate, err %d\n", error);
+		return error;
+	}
+
+	gp2a_report(dt);
+
+	return 0;
+}
+
+static void gp2a_device_close(struct input_dev *dev)
+{
+	struct gp2a_data *dt = input_get_drvdata(dev);
+	int error;
+
+	error = gp2a_disable(dt);
+	if (error < 0)
+		dev_err(&dt->i2c_client->dev,
+			"unable to deactivate, err %d\n", error);
+}
+
+static int gp2a_initialize(struct gp2a_data *dt)
+{
+	int error;
+
+	error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_GAIN,
+					  0x08);
+	if (error < 0)
+		return error;
+
+	error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_HYS,
+					  0xc2);
+	if (error < 0)
+		return error;
+
+	error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_CYCLE,
+					  0x04);
+	if (error < 0)
+		return error;
+
+	error = gp2a_disable(dt);
+
+	return error;
+}
+
+static int gp2a_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	const struct gp2a_platform_data *pdata = dev_get_platdata(&client->dev);
+	struct gp2a_data *dt;
+	int error;
+
+	if (!pdata)
+		return -EINVAL;
+
+	if (pdata->hw_setup) {
+		error = pdata->hw_setup(client);
+		if (error < 0)
+			return error;
+	}
+
+	error = gpio_request_one(pdata->vout_gpio, GPIOF_IN, GP2A_I2C_NAME);
+	if (error)
+		goto err_hw_shutdown;
+
+	dt = kzalloc(sizeof(struct gp2a_data), GFP_KERNEL);
+	if (!dt) {
+		error = -ENOMEM;
+		goto err_free_gpio;
+	}
+
+	dt->pdata = pdata;
+	dt->i2c_client = client;
+
+	error = gp2a_initialize(dt);
+	if (error < 0)
+		goto err_free_mem;
+
+	dt->input = input_allocate_device();
+	if (!dt->input) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	input_set_drvdata(dt->input, dt);
+
+	dt->input->open = gp2a_device_open;
+	dt->input->close = gp2a_device_close;
+	dt->input->name = GP2A_I2C_NAME;
+	dt->input->id.bustype = BUS_I2C;
+	dt->input->dev.parent = &client->dev;
+
+	input_set_capability(dt->input, EV_SW, SW_FRONT_PROXIMITY);
+
+	error = request_threaded_irq(client->irq, NULL, gp2a_irq,
+			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+				IRQF_ONESHOT,
+			GP2A_I2C_NAME, dt);
+	if (error) {
+		dev_err(&client->dev, "irq request failed\n");
+		goto err_free_input_dev;
+	}
+
+	error = input_register_device(dt->input);
+	if (error) {
+		dev_err(&client->dev, "device registration failed\n");
+		goto err_free_irq;
+	}
+
+	device_init_wakeup(&client->dev, pdata->wakeup);
+	i2c_set_clientdata(client, dt);
+
+	return 0;
+
+err_free_irq:
+	free_irq(client->irq, dt);
+err_free_input_dev:
+	input_free_device(dt->input);
+err_free_mem:
+	kfree(dt);
+err_free_gpio:
+	gpio_free(pdata->vout_gpio);
+err_hw_shutdown:
+	if (pdata->hw_shutdown)
+		pdata->hw_shutdown(client);
+	return error;
+}
+
+static int gp2a_remove(struct i2c_client *client)
+{
+	struct gp2a_data *dt = i2c_get_clientdata(client);
+	const struct gp2a_platform_data *pdata = dt->pdata;
+
+	device_init_wakeup(&client->dev, false);
+
+	free_irq(client->irq, dt);
+
+	input_unregister_device(dt->input);
+	kfree(dt);
+
+	gpio_free(pdata->vout_gpio);
+
+	if (pdata->hw_shutdown)
+		pdata->hw_shutdown(client);
+
+	return 0;
+}
+
+static int __maybe_unused gp2a_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct gp2a_data *dt = i2c_get_clientdata(client);
+	int retval = 0;
+
+	if (device_may_wakeup(&client->dev)) {
+		enable_irq_wake(client->irq);
+	} else {
+		mutex_lock(&dt->input->mutex);
+		if (dt->input->users)
+			retval = gp2a_disable(dt);
+		mutex_unlock(&dt->input->mutex);
+	}
+
+	return retval;
+}
+
+static int __maybe_unused gp2a_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct gp2a_data *dt = i2c_get_clientdata(client);
+	int retval = 0;
+
+	if (device_may_wakeup(&client->dev)) {
+		disable_irq_wake(client->irq);
+	} else {
+		mutex_lock(&dt->input->mutex);
+		if (dt->input->users)
+			retval = gp2a_enable(dt);
+		mutex_unlock(&dt->input->mutex);
+	}
+
+	return retval;
+}
+
+static SIMPLE_DEV_PM_OPS(gp2a_pm, gp2a_suspend, gp2a_resume);
+
+static const struct i2c_device_id gp2a_i2c_id[] = {
+	{ GP2A_I2C_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, gp2a_i2c_id);
+
+static struct i2c_driver gp2a_i2c_driver = {
+	.driver = {
+		.name	= GP2A_I2C_NAME,
+		.pm	= &gp2a_pm,
+	},
+	.probe		= gp2a_probe,
+	.remove		= gp2a_remove,
+	.id_table	= gp2a_i2c_id,
+};
+
+module_i2c_driver(gp2a_i2c_driver);
+
+MODULE_AUTHOR("Courtney Cavin <courtney.cavin@sonyericsson.com>");
+MODULE_DESCRIPTION("Sharp GP2AP002A00F I2C Proximity/Opto sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/gpio-beeper.c b/drivers/input/misc/gpio-beeper.c
new file mode 100644
index 0000000..16272ff
--- /dev/null
+++ b/drivers/input/misc/gpio-beeper.c
@@ -0,0 +1,118 @@
+/*
+ * Generic GPIO beeper driver
+ *
+ * Copyright (C) 2013-2014 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * 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.
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+
+#define BEEPER_MODNAME		"gpio-beeper"
+
+struct gpio_beeper {
+	struct work_struct	work;
+	struct gpio_desc	*desc;
+	bool			beeping;
+};
+
+static void gpio_beeper_toggle(struct gpio_beeper *beep, bool on)
+{
+	gpiod_set_value_cansleep(beep->desc, on);
+}
+
+static void gpio_beeper_work(struct work_struct *work)
+{
+	struct gpio_beeper *beep = container_of(work, struct gpio_beeper, work);
+
+	gpio_beeper_toggle(beep, beep->beeping);
+}
+
+static int gpio_beeper_event(struct input_dev *dev, unsigned int type,
+			     unsigned int code, int value)
+{
+	struct gpio_beeper *beep = input_get_drvdata(dev);
+
+	if (type != EV_SND || code != SND_BELL)
+		return -ENOTSUPP;
+
+	if (value < 0)
+		return -EINVAL;
+
+	beep->beeping = value;
+	/* Schedule work to actually turn the beeper on or off */
+	schedule_work(&beep->work);
+
+	return 0;
+}
+
+static void gpio_beeper_close(struct input_dev *input)
+{
+	struct gpio_beeper *beep = input_get_drvdata(input);
+
+	cancel_work_sync(&beep->work);
+	gpio_beeper_toggle(beep, false);
+}
+
+static int gpio_beeper_probe(struct platform_device *pdev)
+{
+	struct gpio_beeper *beep;
+	struct input_dev *input;
+
+	beep = devm_kzalloc(&pdev->dev, sizeof(*beep), GFP_KERNEL);
+	if (!beep)
+		return -ENOMEM;
+
+	beep->desc = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
+	if (IS_ERR(beep->desc))
+		return PTR_ERR(beep->desc);
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input)
+		return -ENOMEM;
+
+	INIT_WORK(&beep->work, gpio_beeper_work);
+
+	input->name		= pdev->name;
+	input->id.bustype	= BUS_HOST;
+	input->id.vendor	= 0x0001;
+	input->id.product	= 0x0001;
+	input->id.version	= 0x0100;
+	input->close		= gpio_beeper_close;
+	input->event		= gpio_beeper_event;
+
+	input_set_capability(input, EV_SND, SND_BELL);
+
+	input_set_drvdata(input, beep);
+
+	return input_register_device(input);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id gpio_beeper_of_match[] = {
+	{ .compatible = BEEPER_MODNAME, },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, gpio_beeper_of_match);
+#endif
+
+static struct platform_driver gpio_beeper_platform_driver = {
+	.driver	= {
+		.name		= BEEPER_MODNAME,
+		.of_match_table	= of_match_ptr(gpio_beeper_of_match),
+	},
+	.probe	= gpio_beeper_probe,
+};
+module_platform_driver(gpio_beeper_platform_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("Generic GPIO beeper driver");
diff --git a/drivers/input/misc/gpio_tilt_polled.c b/drivers/input/misc/gpio_tilt_polled.c
new file mode 100644
index 0000000..f103b99
--- /dev/null
+++ b/drivers/input/misc/gpio_tilt_polled.c
@@ -0,0 +1,210 @@
+/*
+ *  Driver for tilt switches connected via GPIO lines
+ *  not capable of generating interrupts
+ *
+ *  Copyright (C) 2011 Heiko Stuebner <heiko@sntech.de>
+ *
+ *  based on: drivers/input/keyboard/gpio_keys_polled.c
+ *
+ *  Copyright (C) 2007-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2010 Nuno Goncalves <nunojpg@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/input/gpio_tilt.h>
+
+#define DRV_NAME	"gpio-tilt-polled"
+
+struct gpio_tilt_polled_dev {
+	struct input_polled_dev *poll_dev;
+	struct device *dev;
+	const struct gpio_tilt_platform_data *pdata;
+
+	int last_state;
+
+	int threshold;
+	int count;
+};
+
+static void gpio_tilt_polled_poll(struct input_polled_dev *dev)
+{
+	struct gpio_tilt_polled_dev *tdev = dev->private;
+	const struct gpio_tilt_platform_data *pdata = tdev->pdata;
+	struct input_dev *input = dev->input;
+	struct gpio_tilt_state *tilt_state = NULL;
+	int state, i;
+
+	if (tdev->count < tdev->threshold) {
+		tdev->count++;
+	} else {
+		state = 0;
+		for (i = 0; i < pdata->nr_gpios; i++)
+			state |= (!!gpio_get_value(pdata->gpios[i].gpio) << i);
+
+		if (state != tdev->last_state) {
+			for (i = 0; i < pdata->nr_states; i++)
+				if (pdata->states[i].gpios == state)
+					tilt_state = &pdata->states[i];
+
+			if (tilt_state) {
+				for (i = 0; i < pdata->nr_axes; i++)
+					input_report_abs(input,
+							 pdata->axes[i].axis,
+							 tilt_state->axes[i]);
+
+				input_sync(input);
+			}
+
+			tdev->count = 0;
+			tdev->last_state = state;
+		}
+	}
+}
+
+static void gpio_tilt_polled_open(struct input_polled_dev *dev)
+{
+	struct gpio_tilt_polled_dev *tdev = dev->private;
+	const struct gpio_tilt_platform_data *pdata = tdev->pdata;
+
+	if (pdata->enable)
+		pdata->enable(tdev->dev);
+
+	/* report initial state of the axes */
+	tdev->last_state = -1;
+	tdev->count = tdev->threshold;
+	gpio_tilt_polled_poll(tdev->poll_dev);
+}
+
+static void gpio_tilt_polled_close(struct input_polled_dev *dev)
+{
+	struct gpio_tilt_polled_dev *tdev = dev->private;
+	const struct gpio_tilt_platform_data *pdata = tdev->pdata;
+
+	if (pdata->disable)
+		pdata->disable(tdev->dev);
+}
+
+static int gpio_tilt_polled_probe(struct platform_device *pdev)
+{
+	const struct gpio_tilt_platform_data *pdata =
+			dev_get_platdata(&pdev->dev);
+	struct device *dev = &pdev->dev;
+	struct gpio_tilt_polled_dev *tdev;
+	struct input_polled_dev *poll_dev;
+	struct input_dev *input;
+	int error, i;
+
+	if (!pdata || !pdata->poll_interval)
+		return -EINVAL;
+
+	tdev = kzalloc(sizeof(struct gpio_tilt_polled_dev), GFP_KERNEL);
+	if (!tdev) {
+		dev_err(dev, "no memory for private data\n");
+		return -ENOMEM;
+	}
+
+	error = gpio_request_array(pdata->gpios, pdata->nr_gpios);
+	if (error) {
+		dev_err(dev,
+			"Could not request tilt GPIOs: %d\n", error);
+		goto err_free_tdev;
+	}
+
+	poll_dev = input_allocate_polled_device();
+	if (!poll_dev) {
+		dev_err(dev, "no memory for polled device\n");
+		error = -ENOMEM;
+		goto err_free_gpios;
+	}
+
+	poll_dev->private = tdev;
+	poll_dev->poll = gpio_tilt_polled_poll;
+	poll_dev->poll_interval = pdata->poll_interval;
+	poll_dev->open = gpio_tilt_polled_open;
+	poll_dev->close = gpio_tilt_polled_close;
+
+	input = poll_dev->input;
+
+	input->name = pdev->name;
+	input->phys = DRV_NAME"/input0";
+	input->dev.parent = &pdev->dev;
+
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0100;
+
+	__set_bit(EV_ABS, input->evbit);
+	for (i = 0; i < pdata->nr_axes; i++)
+		input_set_abs_params(input, pdata->axes[i].axis,
+				     pdata->axes[i].min, pdata->axes[i].max,
+				     pdata->axes[i].fuzz, pdata->axes[i].flat);
+
+	tdev->threshold = DIV_ROUND_UP(pdata->debounce_interval,
+				       pdata->poll_interval);
+
+	tdev->poll_dev = poll_dev;
+	tdev->dev = dev;
+	tdev->pdata = pdata;
+
+	error = input_register_polled_device(poll_dev);
+	if (error) {
+		dev_err(dev, "unable to register polled device, err=%d\n",
+			error);
+		goto err_free_polldev;
+	}
+
+	platform_set_drvdata(pdev, tdev);
+
+	return 0;
+
+err_free_polldev:
+	input_free_polled_device(poll_dev);
+err_free_gpios:
+	gpio_free_array(pdata->gpios, pdata->nr_gpios);
+err_free_tdev:
+	kfree(tdev);
+
+	return error;
+}
+
+static int gpio_tilt_polled_remove(struct platform_device *pdev)
+{
+	struct gpio_tilt_polled_dev *tdev = platform_get_drvdata(pdev);
+	const struct gpio_tilt_platform_data *pdata = tdev->pdata;
+
+	input_unregister_polled_device(tdev->poll_dev);
+	input_free_polled_device(tdev->poll_dev);
+
+	gpio_free_array(pdata->gpios, pdata->nr_gpios);
+
+	kfree(tdev);
+
+	return 0;
+}
+
+static struct platform_driver gpio_tilt_polled_driver = {
+	.probe	= gpio_tilt_polled_probe,
+	.remove	= gpio_tilt_polled_remove,
+	.driver	= {
+		.name	= DRV_NAME,
+	},
+};
+
+module_platform_driver(gpio_tilt_polled_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("Polled GPIO tilt driver");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
new file mode 100644
index 0000000..1c8c56e
--- /dev/null
+++ b/drivers/input/misc/hp_sdc_rtc.c
@@ -0,0 +1,733 @@
+/*
+ * HP i8042 SDC + MSM-58321 BBRTC driver.
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *
+ * References:
+ * System Device Controller Microprocessor Firmware Theory of Operation
+ *      for Part Number 1820-4784 Revision B.  Dwg No. A-1820-4784-2
+ * efirtc.c by Stephane Eranian/Hewlett Packard
+ *
+ */
+
+#include <linux/hp_sdc.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/miscdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/poll.h>
+#include <linux/rtc.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define RTC_VERSION "1.10d"
+
+static DEFINE_MUTEX(hp_sdc_rtc_mutex);
+static unsigned long epoch = 2000;
+
+static struct semaphore i8042tregs;
+
+static hp_sdc_irqhook hp_sdc_rtc_isr;
+
+static struct fasync_struct *hp_sdc_rtc_async_queue;
+
+static DECLARE_WAIT_QUEUE_HEAD(hp_sdc_rtc_wait);
+
+static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
+			       size_t count, loff_t *ppos);
+
+static long hp_sdc_rtc_unlocked_ioctl(struct file *file,
+				      unsigned int cmd, unsigned long arg);
+
+static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait);
+
+static int hp_sdc_rtc_open(struct inode *inode, struct file *file);
+static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on);
+
+static void hp_sdc_rtc_isr (int irq, void *dev_id, 
+			    uint8_t status, uint8_t data) 
+{
+	return;
+}
+
+static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
+{
+	struct semaphore tsem;
+	hp_sdc_transaction t;
+	uint8_t tseq[91];
+	int i;
+	
+	i = 0;
+	while (i < 91) {
+		tseq[i++] = HP_SDC_ACT_DATAREG |
+			HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN;
+		tseq[i++] = 0x01;			/* write i8042[0x70] */
+	  	tseq[i]   = i / 7;			/* BBRTC reg address */
+		i++;
+		tseq[i++] = HP_SDC_CMD_DO_RTCR;		/* Trigger command   */
+		tseq[i++] = 2;		/* expect 1 stat/dat pair back.   */
+		i++; i++;               /* buffer for stat/dat pair       */
+	}
+	tseq[84] |= HP_SDC_ACT_SEMAPHORE;
+	t.endidx =		91;
+	t.seq =			tseq;
+	t.act.semaphore =	&tsem;
+	sema_init(&tsem, 0);
+	
+	if (hp_sdc_enqueue_transaction(&t)) return -1;
+	
+	/* Put ourselves to sleep for results. */
+	if (WARN_ON(down_interruptible(&tsem)))
+		return -1;
+	
+	/* Check for nonpresence of BBRTC */
+	if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] |
+	       tseq[55] | tseq[62] | tseq[34] | tseq[41] |
+	       tseq[20] | tseq[27] | tseq[6]  | tseq[13]) & 0x0f))
+		return -1;
+
+	memset(rtctm, 0, sizeof(struct rtc_time));
+	rtctm->tm_year = (tseq[83] & 0x0f) + (tseq[90] & 0x0f) * 10;
+	rtctm->tm_mon  = (tseq[69] & 0x0f) + (tseq[76] & 0x0f) * 10;
+	rtctm->tm_mday = (tseq[55] & 0x0f) + (tseq[62] & 0x0f) * 10;
+	rtctm->tm_wday = (tseq[48] & 0x0f);
+	rtctm->tm_hour = (tseq[34] & 0x0f) + (tseq[41] & 0x0f) * 10;
+	rtctm->tm_min  = (tseq[20] & 0x0f) + (tseq[27] & 0x0f) * 10;
+	rtctm->tm_sec  = (tseq[6]  & 0x0f) + (tseq[13] & 0x0f) * 10;
+	
+	return 0;
+}
+
+static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm)
+{
+	struct rtc_time tm, tm_last;
+	int i = 0;
+
+	/* MSM-58321 has no read latch, so must read twice and compare. */
+
+	if (hp_sdc_rtc_do_read_bbrtc(&tm_last)) return -1;
+	if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
+
+	while (memcmp(&tm, &tm_last, sizeof(struct rtc_time))) {
+		if (i++ > 4) return -1;
+		memcpy(&tm_last, &tm, sizeof(struct rtc_time));
+		if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
+	}
+
+	memcpy(rtctm, &tm, sizeof(struct rtc_time));
+
+	return 0;
+}
+
+
+static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
+{
+	hp_sdc_transaction t;
+	uint8_t tseq[26] = {
+		HP_SDC_ACT_PRECMD | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
+		0,
+		HP_SDC_CMD_READ_T1, 2, 0, 0,
+		HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 
+		HP_SDC_CMD_READ_T2, 2, 0, 0,
+		HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 
+		HP_SDC_CMD_READ_T3, 2, 0, 0,
+		HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 
+		HP_SDC_CMD_READ_T4, 2, 0, 0,
+		HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 
+		HP_SDC_CMD_READ_T5, 2, 0, 0
+	};
+
+	t.endidx = numreg * 5;
+
+	tseq[1] = loadcmd;
+	tseq[t.endidx - 4] |= HP_SDC_ACT_SEMAPHORE; /* numreg assumed > 1 */
+
+	t.seq =			tseq;
+	t.act.semaphore =	&i8042tregs;
+
+	/* Sleep if output regs in use. */
+	if (WARN_ON(down_interruptible(&i8042tregs)))
+		return -1;
+
+	if (hp_sdc_enqueue_transaction(&t)) {
+		up(&i8042tregs);
+		return -1;
+	}
+	
+	/* Sleep until results come back. */
+	if (WARN_ON(down_interruptible(&i8042tregs)))
+		return -1;
+
+	up(&i8042tregs);
+
+	return (tseq[5] | 
+		((uint64_t)(tseq[10]) << 8)  | ((uint64_t)(tseq[15]) << 16) |
+		((uint64_t)(tseq[20]) << 24) | ((uint64_t)(tseq[25]) << 32));
+}
+
+
+/* Read the i8042 real-time clock */
+static inline int hp_sdc_rtc_read_rt(struct timespec64 *res) {
+	int64_t raw;
+	uint32_t tenms; 
+	unsigned int days;
+
+	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_RT, 5);
+	if (raw < 0) return -1;
+
+	tenms = (uint32_t)raw & 0xffffff;
+	days  = (unsigned int)(raw >> 24) & 0xffff;
+
+	res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
+	res->tv_sec =  (tenms / 100) + (time64_t)days * 86400;
+
+	return 0;
+}
+
+
+/* Read the i8042 fast handshake timer */
+static inline int hp_sdc_rtc_read_fhs(struct timespec64 *res) {
+	int64_t raw;
+	unsigned int tenms;
+
+	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2);
+	if (raw < 0) return -1;
+
+	tenms = (unsigned int)raw & 0xffff;
+
+	res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
+	res->tv_sec  = (time64_t)(tenms / 100);
+
+	return 0;
+}
+
+
+/* Read the i8042 match timer (a.k.a. alarm) */
+static inline int hp_sdc_rtc_read_mt(struct timespec64 *res) {
+	int64_t raw;	
+	uint32_t tenms; 
+
+	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_MT, 3);
+	if (raw < 0) return -1;
+
+	tenms = (uint32_t)raw & 0xffffff;
+
+	res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
+	res->tv_sec  = (time64_t)(tenms / 100);
+
+	return 0;
+}
+
+
+/* Read the i8042 delay timer */
+static inline int hp_sdc_rtc_read_dt(struct timespec64 *res) {
+	int64_t raw;
+	uint32_t tenms;
+
+	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_DT, 3);
+	if (raw < 0) return -1;
+
+	tenms = (uint32_t)raw & 0xffffff;
+
+	res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
+	res->tv_sec  = (time64_t)(tenms / 100);
+
+	return 0;
+}
+
+
+/* Read the i8042 cycle timer (a.k.a. periodic) */
+static inline int hp_sdc_rtc_read_ct(struct timespec64 *res) {
+	int64_t raw;
+	uint32_t tenms;
+
+	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_CT, 3);
+	if (raw < 0) return -1;
+
+	tenms = (uint32_t)raw & 0xffffff;
+
+	res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
+	res->tv_sec  = (time64_t)(tenms / 100);
+
+	return 0;
+}
+
+
+#if 0 /* not used yet */
+/* Set the i8042 real-time clock */
+static int hp_sdc_rtc_set_rt (struct timeval *setto)
+{
+	uint32_t tenms;
+	unsigned int days;
+	hp_sdc_transaction t;
+	uint8_t tseq[11] = {
+		HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+		HP_SDC_CMD_SET_RTMS, 3, 0, 0, 0,
+		HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+		HP_SDC_CMD_SET_RTD, 2, 0, 0 
+	};
+
+	t.endidx = 10;
+
+	if (0xffff < setto->tv_sec / 86400) return -1;
+	days = setto->tv_sec / 86400;
+	if (0xffff < setto->tv_usec / 1000000 / 86400) return -1;
+	days += ((setto->tv_sec % 86400) + setto->tv_usec / 1000000) / 86400;
+	if (days > 0xffff) return -1;
+
+	if (0xffffff < setto->tv_sec) return -1;
+	tenms  = setto->tv_sec * 100;
+	if (0xffffff < setto->tv_usec / 10000) return -1;
+	tenms += setto->tv_usec / 10000;
+	if (tenms > 0xffffff) return -1;
+
+	tseq[3] = (uint8_t)(tenms & 0xff);
+	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
+	tseq[5] = (uint8_t)((tenms >> 16) & 0xff);
+
+	tseq[9] = (uint8_t)(days & 0xff);
+	tseq[10] = (uint8_t)((days >> 8) & 0xff);
+
+	t.seq =	tseq;
+
+	if (hp_sdc_enqueue_transaction(&t)) return -1;
+	return 0;
+}
+
+/* Set the i8042 fast handshake timer */
+static int hp_sdc_rtc_set_fhs (struct timeval *setto)
+{
+	uint32_t tenms;
+	hp_sdc_transaction t;
+	uint8_t tseq[5] = {
+		HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+		HP_SDC_CMD_SET_FHS, 2, 0, 0
+	};
+
+	t.endidx = 4;
+
+	if (0xffff < setto->tv_sec) return -1;
+	tenms  = setto->tv_sec * 100;
+	if (0xffff < setto->tv_usec / 10000) return -1;
+	tenms += setto->tv_usec / 10000;
+	if (tenms > 0xffff) return -1;
+
+	tseq[3] = (uint8_t)(tenms & 0xff);
+	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
+
+	t.seq =	tseq;
+
+	if (hp_sdc_enqueue_transaction(&t)) return -1;
+	return 0;
+}
+
+
+/* Set the i8042 match timer (a.k.a. alarm) */
+#define hp_sdc_rtc_set_mt (setto) \
+	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_MT)
+
+/* Set the i8042 delay timer */
+#define hp_sdc_rtc_set_dt (setto) \
+	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_DT)
+
+/* Set the i8042 cycle timer (a.k.a. periodic) */
+#define hp_sdc_rtc_set_ct (setto) \
+	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_CT)
+
+/* Set one of the i8042 3-byte wide timers */
+static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
+{
+	uint32_t tenms;
+	hp_sdc_transaction t;
+	uint8_t tseq[6] = {
+		HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+		0, 3, 0, 0, 0
+	};
+
+	t.endidx = 6;
+
+	if (0xffffff < setto->tv_sec) return -1;
+	tenms  = setto->tv_sec * 100;
+	if (0xffffff < setto->tv_usec / 10000) return -1;
+	tenms += setto->tv_usec / 10000;
+	if (tenms > 0xffffff) return -1;
+
+	tseq[1] = setcmd;
+	tseq[3] = (uint8_t)(tenms & 0xff);
+	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
+	tseq[5] = (uint8_t)((tenms >> 16)  & 0xff);
+
+	t.seq =			tseq;
+
+	if (hp_sdc_enqueue_transaction(&t)) { 
+		return -1;
+	}
+	return 0;
+}
+#endif
+
+static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
+			       size_t count, loff_t *ppos) {
+	ssize_t retval;
+
+        if (count < sizeof(unsigned long))
+                return -EINVAL;
+
+	retval = put_user(68, (unsigned long __user *)buf);
+	return retval;
+}
+
+static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait)
+{
+        unsigned long l;
+
+	l = 0;
+        if (l != 0)
+                return POLLIN | POLLRDNORM;
+        return 0;
+}
+
+static int hp_sdc_rtc_open(struct inode *inode, struct file *file)
+{
+        return 0;
+}
+
+static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on)
+{
+        return fasync_helper (fd, filp, on, &hp_sdc_rtc_async_queue);
+}
+
+static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
+{
+#define YN(bit) ("no")
+#define NY(bit) ("yes")
+        struct rtc_time tm;
+	struct timespec64 tv;
+
+	memset(&tm, 0, sizeof(struct rtc_time));
+
+	if (hp_sdc_rtc_read_bbrtc(&tm)) {
+		seq_puts(m, "BBRTC\t\t: READ FAILED!\n");
+	} else {
+		seq_printf(m,
+			     "rtc_time\t: %02d:%02d:%02d\n"
+			     "rtc_date\t: %04d-%02d-%02d\n"
+			     "rtc_epoch\t: %04lu\n",
+			     tm.tm_hour, tm.tm_min, tm.tm_sec,
+			     tm.tm_year + 1900, tm.tm_mon + 1, 
+			     tm.tm_mday, epoch);
+	}
+
+	if (hp_sdc_rtc_read_rt(&tv)) {
+		seq_puts(m, "i8042 rtc\t: READ FAILED!\n");
+	} else {
+		seq_printf(m, "i8042 rtc\t: %lld.%02ld seconds\n",
+			     (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
+	}
+
+	if (hp_sdc_rtc_read_fhs(&tv)) {
+		seq_puts(m, "handshake\t: READ FAILED!\n");
+	} else {
+		seq_printf(m, "handshake\t: %lld.%02ld seconds\n",
+			     (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
+	}
+
+	if (hp_sdc_rtc_read_mt(&tv)) {
+		seq_puts(m, "alarm\t\t: READ FAILED!\n");
+	} else {
+		seq_printf(m, "alarm\t\t: %lld.%02ld seconds\n",
+			     (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
+	}
+
+	if (hp_sdc_rtc_read_dt(&tv)) {
+		seq_puts(m, "delay\t\t: READ FAILED!\n");
+	} else {
+		seq_printf(m, "delay\t\t: %lld.%02ld seconds\n",
+			     (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
+	}
+
+	if (hp_sdc_rtc_read_ct(&tv)) {
+		seq_puts(m, "periodic\t: READ FAILED!\n");
+	} else {
+		seq_printf(m, "periodic\t: %lld.%02ld seconds\n",
+			     (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
+	}
+
+        seq_printf(m,
+                     "DST_enable\t: %s\n"
+                     "BCD\t\t: %s\n"
+                     "24hr\t\t: %s\n"
+                     "square_wave\t: %s\n"
+                     "alarm_IRQ\t: %s\n"
+                     "update_IRQ\t: %s\n"
+                     "periodic_IRQ\t: %s\n"
+		     "periodic_freq\t: %ld\n"
+                     "batt_status\t: %s\n",
+                     YN(RTC_DST_EN),
+                     NY(RTC_DM_BINARY),
+                     YN(RTC_24H),
+                     YN(RTC_SQWE),
+                     YN(RTC_AIE),
+                     YN(RTC_UIE),
+                     YN(RTC_PIE),
+                     1UL,
+                     1 ? "okay" : "dead");
+
+        return 0;
+#undef YN
+#undef NY
+}
+
+static int hp_sdc_rtc_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, hp_sdc_rtc_proc_show, NULL);
+}
+
+static const struct file_operations hp_sdc_rtc_proc_fops = {
+	.open		= hp_sdc_rtc_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int hp_sdc_rtc_ioctl(struct file *file, 
+			    unsigned int cmd, unsigned long arg)
+{
+#if 1
+	return -EINVAL;
+#else
+	
+        struct rtc_time wtime; 
+	struct timeval ttime;
+	int use_wtime = 0;
+
+	/* This needs major work. */
+
+        switch (cmd) {
+
+        case RTC_AIE_OFF:       /* Mask alarm int. enab. bit    */
+        case RTC_AIE_ON:        /* Allow alarm interrupts.      */
+	case RTC_PIE_OFF:       /* Mask periodic int. enab. bit */
+        case RTC_PIE_ON:        /* Allow periodic ints          */
+        case RTC_UIE_ON:        /* Allow ints for RTC updates.  */
+        case RTC_UIE_OFF:       /* Allow ints for RTC updates.  */
+        {
+		/* We cannot mask individual user timers and we
+		   cannot tell them apart when they occur, so it 
+		   would be disingenuous to succeed these IOCTLs */
+		return -EINVAL;
+        }
+        case RTC_ALM_READ:      /* Read the present alarm time */
+        {
+		if (hp_sdc_rtc_read_mt(&ttime)) return -EFAULT;
+		if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
+
+		wtime.tm_hour = ttime.tv_sec / 3600;  ttime.tv_sec %= 3600;
+		wtime.tm_min  = ttime.tv_sec / 60;    ttime.tv_sec %= 60;
+		wtime.tm_sec  = ttime.tv_sec;
+                
+		break;
+        }
+        case RTC_IRQP_READ:     /* Read the periodic IRQ rate.  */
+        {
+                return put_user(hp_sdc_rtc_freq, (unsigned long *)arg);
+        }
+        case RTC_IRQP_SET:      /* Set periodic IRQ rate.       */
+        {
+                /* 
+                 * The max we can do is 100Hz.
+		 */
+
+                if ((arg < 1) || (arg > 100)) return -EINVAL;
+		ttime.tv_sec = 0;
+		ttime.tv_usec = 1000000 / arg;
+		if (hp_sdc_rtc_set_ct(&ttime)) return -EFAULT;
+		hp_sdc_rtc_freq = arg;
+                return 0;
+        }
+        case RTC_ALM_SET:       /* Store a time into the alarm */
+        {
+                /*
+                 * This expects a struct hp_sdc_rtc_time. Writing 0xff means
+                 * "don't care" or "match all" for PC timers.  The HP SDC
+		 * does not support that perk, but it could be emulated fairly
+		 * easily.  Only the tm_hour, tm_min and tm_sec are used.
+		 * We could do it with 10ms accuracy with the HP SDC, if the 
+		 * rtc interface left us a way to do that.
+                 */
+                struct hp_sdc_rtc_time alm_tm;
+
+                if (copy_from_user(&alm_tm, (struct hp_sdc_rtc_time*)arg,
+                                   sizeof(struct hp_sdc_rtc_time)))
+                       return -EFAULT;
+
+                if (alm_tm.tm_hour > 23) return -EINVAL;
+		if (alm_tm.tm_min  > 59) return -EINVAL;
+		if (alm_tm.tm_sec  > 59) return -EINVAL;  
+
+		ttime.sec = alm_tm.tm_hour * 3600 + 
+		  alm_tm.tm_min * 60 + alm_tm.tm_sec;
+		ttime.usec = 0;
+		if (hp_sdc_rtc_set_mt(&ttime)) return -EFAULT;
+                return 0;
+        }
+        case RTC_RD_TIME:       /* Read the time/date from RTC  */
+        {
+		if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
+                break;
+        }
+        case RTC_SET_TIME:      /* Set the RTC */
+        {
+                struct rtc_time hp_sdc_rtc_tm;
+                unsigned char mon, day, hrs, min, sec, leap_yr;
+                unsigned int yrs;
+
+                if (!capable(CAP_SYS_TIME))
+                        return -EACCES;
+		if (copy_from_user(&hp_sdc_rtc_tm, (struct rtc_time *)arg,
+                                   sizeof(struct rtc_time)))
+                        return -EFAULT;
+
+                yrs = hp_sdc_rtc_tm.tm_year + 1900;
+                mon = hp_sdc_rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
+                day = hp_sdc_rtc_tm.tm_mday;
+                hrs = hp_sdc_rtc_tm.tm_hour;
+                min = hp_sdc_rtc_tm.tm_min;
+                sec = hp_sdc_rtc_tm.tm_sec;
+
+                if (yrs < 1970)
+                        return -EINVAL;
+
+                leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
+
+                if ((mon > 12) || (day == 0))
+                        return -EINVAL;
+                if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
+                        return -EINVAL;
+		if ((hrs >= 24) || (min >= 60) || (sec >= 60))
+                        return -EINVAL;
+
+                if ((yrs -= eH) > 255)    /* They are unsigned */
+                        return -EINVAL;
+
+
+                return 0;
+        }
+        case RTC_EPOCH_READ:    /* Read the epoch.      */
+        {
+                return put_user (epoch, (unsigned long *)arg);
+        }
+        case RTC_EPOCH_SET:     /* Set the epoch.       */
+        {
+                /* 
+                 * There were no RTC clocks before 1900.
+                 */
+                if (arg < 1900)
+		  return -EINVAL;
+		if (!capable(CAP_SYS_TIME))
+		  return -EACCES;
+		
+                epoch = arg;
+                return 0;
+        }
+        default:
+                return -EINVAL;
+        }
+        return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+#endif
+}
+
+static long hp_sdc_rtc_unlocked_ioctl(struct file *file,
+				      unsigned int cmd, unsigned long arg)
+{
+	int ret;
+
+	mutex_lock(&hp_sdc_rtc_mutex);
+	ret = hp_sdc_rtc_ioctl(file, cmd, arg);
+	mutex_unlock(&hp_sdc_rtc_mutex);
+
+	return ret;
+}
+
+
+static const struct file_operations hp_sdc_rtc_fops = {
+        .owner =		THIS_MODULE,
+        .llseek =		no_llseek,
+        .read =			hp_sdc_rtc_read,
+        .poll =			hp_sdc_rtc_poll,
+        .unlocked_ioctl =	hp_sdc_rtc_unlocked_ioctl,
+        .open =			hp_sdc_rtc_open,
+        .fasync =		hp_sdc_rtc_fasync,
+};
+
+static struct miscdevice hp_sdc_rtc_dev = {
+        .minor =	RTC_MINOR,
+        .name =		"rtc_HIL",
+        .fops =		&hp_sdc_rtc_fops
+};
+
+static int __init hp_sdc_rtc_init(void)
+{
+	int ret;
+
+#ifdef __mc68000__
+	if (!MACH_IS_HP300)
+		return -ENODEV;
+#endif
+
+	sema_init(&i8042tregs, 1);
+
+	if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr)))
+		return ret;
+	if (misc_register(&hp_sdc_rtc_dev) != 0)
+		printk(KERN_INFO "Could not register misc. dev for i8042 rtc\n");
+
+        proc_create("driver/rtc", 0, NULL, &hp_sdc_rtc_proc_fops);
+
+	printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support loaded "
+			 "(RTC v " RTC_VERSION ")\n");
+
+	return 0;
+}
+
+static void __exit hp_sdc_rtc_exit(void)
+{
+	remove_proc_entry ("driver/rtc", NULL);
+        misc_deregister(&hp_sdc_rtc_dev);
+	hp_sdc_release_timer_irq(hp_sdc_rtc_isr);
+        printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n");
+}
+
+module_init(hp_sdc_rtc_init);
+module_exit(hp_sdc_rtc_exit);
diff --git a/drivers/input/misc/ideapad_slidebar.c b/drivers/input/misc/ideapad_slidebar.c
new file mode 100644
index 0000000..b0acb87
--- /dev/null
+++ b/drivers/input/misc/ideapad_slidebar.c
@@ -0,0 +1,357 @@
+/*
+ * Input driver for slidebars on some Lenovo IdeaPad laptops
+ *
+ * Copyright (C) 2013 Andrey Moiseev <o2g.org.ru@gmail.com>
+ *
+ * Reverse-engineered from Lenovo SlideNav software (SBarHook.dll).
+ *
+ * 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.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+/*
+ * Currently tested and works on:
+ *	Lenovo IdeaPad Y550
+ *	Lenovo IdeaPad Y550P
+ *
+ * Other models can be added easily. To test,
+ * load with 'force' parameter set 'true'.
+ *
+ * LEDs blinking and input mode are managed via sysfs,
+ * (hex, unsigned byte value):
+ * /sys/devices/platform/ideapad_slidebar/slidebar_mode
+ *
+ * The value is in byte range, however, I only figured out
+ * how bits 0b10011001 work. Some other bits, probably,
+ * are meaningfull too.
+ *
+ * Possible states:
+ *
+ * STD_INT, ONMOV_INT, OFF_INT, LAST_POLL, OFF_POLL
+ *
+ * Meaning:
+ *           released      touched
+ * STD       'heartbeat'   lights follow the finger
+ * ONMOV     no lights     lights follow the finger
+ * LAST      at last pos   lights follow the finger
+ * OFF       no lights     no lights
+ *
+ * INT       all input events are generated, interrupts are used
+ * POLL      no input events by default, to get them,
+ *	     send 0b10000000 (read below)
+ *
+ * Commands: write
+ *
+ * All      |  0b01001 -> STD_INT
+ * possible |  0b10001 -> ONMOV_INT
+ * states   |  0b01000 -> OFF_INT
+ *
+ *                      |  0b0 -> LAST_POLL
+ * STD_INT or ONMOV_INT |
+ *                      |  0b1 -> STD_INT
+ *
+ *                      |  0b0 -> OFF_POLL
+ * OFF_INT or OFF_POLL  |
+ *                      |  0b1 -> OFF_INT
+ *
+ * Any state |   0b10000000 ->  if the slidebar has updated data,
+ *				produce one input event (last position),
+ *				switch to respective POLL mode
+ *				(like 0x0), if not in POLL mode yet.
+ *
+ * Get current state: read
+ *
+ * masked by 0x11 read value means:
+ *
+ * 0x00   LAST
+ * 0x01   STD
+ * 0x10   OFF
+ * 0x11   ONMOV
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/dmi.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/i8042.h>
+#include <linux/serio.h>
+
+#define IDEAPAD_BASE	0xff29
+
+static bool force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
+
+static DEFINE_SPINLOCK(io_lock);
+
+static struct input_dev *slidebar_input_dev;
+static struct platform_device *slidebar_platform_dev;
+
+static u8 slidebar_pos_get(void)
+{
+	u8 res;
+	unsigned long flags;
+
+	spin_lock_irqsave(&io_lock, flags);
+	outb(0xf4, 0xff29);
+	outb(0xbf, 0xff2a);
+	res = inb(0xff2b);
+	spin_unlock_irqrestore(&io_lock, flags);
+
+	return res;
+}
+
+static u8 slidebar_mode_get(void)
+{
+	u8 res;
+	unsigned long flags;
+
+	spin_lock_irqsave(&io_lock, flags);
+	outb(0xf7, 0xff29);
+	outb(0x8b, 0xff2a);
+	res = inb(0xff2b);
+	spin_unlock_irqrestore(&io_lock, flags);
+
+	return res;
+}
+
+static void slidebar_mode_set(u8 mode)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&io_lock, flags);
+	outb(0xf7, 0xff29);
+	outb(0x8b, 0xff2a);
+	outb(mode, 0xff2b);
+	spin_unlock_irqrestore(&io_lock, flags);
+}
+
+static bool slidebar_i8042_filter(unsigned char data, unsigned char str,
+				  struct serio *port)
+{
+	static bool extended = false;
+
+	/* We are only interested in data coming form KBC port */
+	if (str & I8042_STR_AUXDATA)
+		return false;
+
+	/* Scancodes: e03b on move, e0bb on release. */
+	if (data == 0xe0) {
+		extended = true;
+		return true;
+	}
+
+	if (!extended)
+		return false;
+
+	extended = false;
+
+	if (likely((data & 0x7f) != 0x3b)) {
+		serio_interrupt(port, 0xe0, 0);
+		return false;
+	}
+
+	if (data & 0x80) {
+		input_report_key(slidebar_input_dev, BTN_TOUCH, 0);
+	} else {
+		input_report_key(slidebar_input_dev, BTN_TOUCH, 1);
+		input_report_abs(slidebar_input_dev, ABS_X, slidebar_pos_get());
+	}
+	input_sync(slidebar_input_dev);
+
+	return true;
+}
+
+static ssize_t show_slidebar_mode(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	return sprintf(buf, "%x\n", slidebar_mode_get());
+}
+
+static ssize_t store_slidebar_mode(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	u8 mode;
+	int error;
+
+	error = kstrtou8(buf, 0, &mode);
+	if (error)
+		return error;
+
+	slidebar_mode_set(mode);
+
+	return count;
+}
+
+static DEVICE_ATTR(slidebar_mode, S_IWUSR | S_IRUGO,
+		   show_slidebar_mode, store_slidebar_mode);
+
+static struct attribute *ideapad_attrs[] = {
+	&dev_attr_slidebar_mode.attr,
+	NULL
+};
+
+static struct attribute_group ideapad_attr_group = {
+	.attrs = ideapad_attrs
+};
+
+static const struct attribute_group *ideapad_attr_groups[] = {
+	&ideapad_attr_group,
+	NULL
+};
+
+static int __init ideapad_probe(struct platform_device* pdev)
+{
+	int err;
+
+	if (!request_region(IDEAPAD_BASE, 3, "ideapad_slidebar")) {
+		dev_err(&pdev->dev, "IO ports are busy\n");
+		return -EBUSY;
+	}
+
+	slidebar_input_dev = input_allocate_device();
+	if (!slidebar_input_dev) {
+		dev_err(&pdev->dev, "Failed to allocate input device\n");
+		err = -ENOMEM;
+		goto err_release_ports;
+	}
+
+	slidebar_input_dev->name = "IdeaPad Slidebar";
+	slidebar_input_dev->id.bustype = BUS_HOST;
+	slidebar_input_dev->dev.parent = &pdev->dev;
+	input_set_capability(slidebar_input_dev, EV_KEY, BTN_TOUCH);
+	input_set_capability(slidebar_input_dev, EV_ABS, ABS_X);
+	input_set_abs_params(slidebar_input_dev, ABS_X, 0, 0xff, 0, 0);
+
+	err = i8042_install_filter(slidebar_i8042_filter);
+	if (err) {
+		dev_err(&pdev->dev,
+			"Failed to install i8042 filter: %d\n", err);
+		goto err_free_dev;
+	}
+
+	err = input_register_device(slidebar_input_dev);
+	if (err) {
+		dev_err(&pdev->dev,
+			"Failed to register input device: %d\n", err);
+		goto err_remove_filter;
+	}
+
+	return 0;
+
+err_remove_filter:
+	i8042_remove_filter(slidebar_i8042_filter);
+err_free_dev:
+	input_free_device(slidebar_input_dev);
+err_release_ports:
+	release_region(IDEAPAD_BASE, 3);
+	return err;
+}
+
+static int ideapad_remove(struct platform_device *pdev)
+{
+	i8042_remove_filter(slidebar_i8042_filter);
+	input_unregister_device(slidebar_input_dev);
+	release_region(IDEAPAD_BASE, 3);
+
+	return 0;
+}
+
+static struct platform_driver slidebar_drv = {
+	.driver = {
+		.name = "ideapad_slidebar",
+	},
+	.remove = ideapad_remove,
+};
+
+static int __init ideapad_dmi_check(const struct dmi_system_id *id)
+{
+	pr_info("Laptop model '%s'\n", id->ident);
+	return 1;
+}
+
+static const struct dmi_system_id ideapad_dmi[] __initconst = {
+	{
+		.ident = "Lenovo IdeaPad Y550",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "20017"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo IdeaPad Y550")
+		},
+		.callback = ideapad_dmi_check
+	},
+	{
+		.ident = "Lenovo IdeaPad Y550P",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "20035"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo IdeaPad Y550P")
+		},
+		.callback = ideapad_dmi_check
+	},
+	{ NULL, }
+};
+MODULE_DEVICE_TABLE(dmi, ideapad_dmi);
+
+static int __init slidebar_init(void)
+{
+	int err;
+
+	if (!force && !dmi_check_system(ideapad_dmi)) {
+		pr_err("DMI does not match\n");
+		return -ENODEV;
+	}
+
+	slidebar_platform_dev = platform_device_alloc("ideapad_slidebar", -1);
+	if (!slidebar_platform_dev) {
+		pr_err("Not enough memory\n");
+		return -ENOMEM;
+	}
+
+	slidebar_platform_dev->dev.groups = ideapad_attr_groups;
+
+	err = platform_device_add(slidebar_platform_dev);
+	if (err) {
+		pr_err("Failed to register platform device\n");
+		goto err_free_dev;
+	}
+
+	err = platform_driver_probe(&slidebar_drv, ideapad_probe);
+	if (err) {
+		pr_err("Failed to register platform driver\n");
+		goto err_delete_dev;
+	}
+
+	return 0;
+
+err_delete_dev:
+	platform_device_del(slidebar_platform_dev);
+err_free_dev:
+	platform_device_put(slidebar_platform_dev);
+	return err;
+}
+
+static void __exit slidebar_exit(void)
+{
+	platform_device_unregister(slidebar_platform_dev);
+	platform_driver_unregister(&slidebar_drv);
+}
+
+module_init(slidebar_init);
+module_exit(slidebar_exit);
+
+MODULE_AUTHOR("Andrey Moiseev <o2g.org.ru@gmail.com>");
+MODULE_DESCRIPTION("Slidebar input support for some Lenovo IdeaPad laptops");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
new file mode 100644
index 0000000..b5304e2
--- /dev/null
+++ b/drivers/input/misc/ims-pcu.c
@@ -0,0 +1,2164 @@
+/*
+ * Driver for IMS Passenger Control Unit Devices
+ *
+ * Copyright (C) 2013 The IMS Company
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/usb/input.h>
+#include <linux/usb/cdc.h>
+#include <asm/unaligned.h>
+
+#define IMS_PCU_KEYMAP_LEN		32
+
+struct ims_pcu_buttons {
+	struct input_dev *input;
+	char name[32];
+	char phys[32];
+	unsigned short keymap[IMS_PCU_KEYMAP_LEN];
+};
+
+struct ims_pcu_gamepad {
+	struct input_dev *input;
+	char name[32];
+	char phys[32];
+};
+
+struct ims_pcu_backlight {
+	struct led_classdev cdev;
+	struct work_struct work;
+	enum led_brightness desired_brightness;
+	char name[32];
+};
+
+#define IMS_PCU_PART_NUMBER_LEN		15
+#define IMS_PCU_SERIAL_NUMBER_LEN	8
+#define IMS_PCU_DOM_LEN			8
+#define IMS_PCU_FW_VERSION_LEN		(9 + 1)
+#define IMS_PCU_BL_VERSION_LEN		(9 + 1)
+#define IMS_PCU_BL_RESET_REASON_LEN	(2 + 1)
+
+#define IMS_PCU_PCU_B_DEVICE_ID		5
+
+#define IMS_PCU_BUF_SIZE		128
+
+struct ims_pcu {
+	struct usb_device *udev;
+	struct device *dev; /* control interface's device, used for logging */
+
+	unsigned int device_no;
+
+	bool bootloader_mode;
+
+	char part_number[IMS_PCU_PART_NUMBER_LEN];
+	char serial_number[IMS_PCU_SERIAL_NUMBER_LEN];
+	char date_of_manufacturing[IMS_PCU_DOM_LEN];
+	char fw_version[IMS_PCU_FW_VERSION_LEN];
+	char bl_version[IMS_PCU_BL_VERSION_LEN];
+	char reset_reason[IMS_PCU_BL_RESET_REASON_LEN];
+	int update_firmware_status;
+	u8 device_id;
+
+	u8 ofn_reg_addr;
+
+	struct usb_interface *ctrl_intf;
+
+	struct usb_endpoint_descriptor *ep_ctrl;
+	struct urb *urb_ctrl;
+	u8 *urb_ctrl_buf;
+	dma_addr_t ctrl_dma;
+	size_t max_ctrl_size;
+
+	struct usb_interface *data_intf;
+
+	struct usb_endpoint_descriptor *ep_in;
+	struct urb *urb_in;
+	u8 *urb_in_buf;
+	dma_addr_t read_dma;
+	size_t max_in_size;
+
+	struct usb_endpoint_descriptor *ep_out;
+	u8 *urb_out_buf;
+	size_t max_out_size;
+
+	u8 read_buf[IMS_PCU_BUF_SIZE];
+	u8 read_pos;
+	u8 check_sum;
+	bool have_stx;
+	bool have_dle;
+
+	u8 cmd_buf[IMS_PCU_BUF_SIZE];
+	u8 ack_id;
+	u8 expected_response;
+	u8 cmd_buf_len;
+	struct completion cmd_done;
+	struct mutex cmd_mutex;
+
+	u32 fw_start_addr;
+	u32 fw_end_addr;
+	struct completion async_firmware_done;
+
+	struct ims_pcu_buttons buttons;
+	struct ims_pcu_gamepad *gamepad;
+	struct ims_pcu_backlight backlight;
+
+	bool setup_complete; /* Input and LED devices have been created */
+};
+
+
+/*********************************************************************
+ *             Buttons Input device support                          *
+ *********************************************************************/
+
+static const unsigned short ims_pcu_keymap_1[] = {
+	[1] = KEY_ATTENDANT_OFF,
+	[2] = KEY_ATTENDANT_ON,
+	[3] = KEY_LIGHTS_TOGGLE,
+	[4] = KEY_VOLUMEUP,
+	[5] = KEY_VOLUMEDOWN,
+	[6] = KEY_INFO,
+};
+
+static const unsigned short ims_pcu_keymap_2[] = {
+	[4] = KEY_VOLUMEUP,
+	[5] = KEY_VOLUMEDOWN,
+	[6] = KEY_INFO,
+};
+
+static const unsigned short ims_pcu_keymap_3[] = {
+	[1] = KEY_HOMEPAGE,
+	[2] = KEY_ATTENDANT_TOGGLE,
+	[3] = KEY_LIGHTS_TOGGLE,
+	[4] = KEY_VOLUMEUP,
+	[5] = KEY_VOLUMEDOWN,
+	[6] = KEY_DISPLAYTOGGLE,
+	[18] = KEY_PLAYPAUSE,
+};
+
+static const unsigned short ims_pcu_keymap_4[] = {
+	[1] = KEY_ATTENDANT_OFF,
+	[2] = KEY_ATTENDANT_ON,
+	[3] = KEY_LIGHTS_TOGGLE,
+	[4] = KEY_VOLUMEUP,
+	[5] = KEY_VOLUMEDOWN,
+	[6] = KEY_INFO,
+	[18] = KEY_PLAYPAUSE,
+};
+
+static const unsigned short ims_pcu_keymap_5[] = {
+	[1] = KEY_ATTENDANT_OFF,
+	[2] = KEY_ATTENDANT_ON,
+	[3] = KEY_LIGHTS_TOGGLE,
+};
+
+struct ims_pcu_device_info {
+	const unsigned short *keymap;
+	size_t keymap_len;
+	bool has_gamepad;
+};
+
+#define IMS_PCU_DEVINFO(_n, _gamepad)				\
+	[_n] = {						\
+		.keymap = ims_pcu_keymap_##_n,			\
+		.keymap_len = ARRAY_SIZE(ims_pcu_keymap_##_n),	\
+		.has_gamepad = _gamepad,			\
+	}
+
+static const struct ims_pcu_device_info ims_pcu_device_info[] = {
+	IMS_PCU_DEVINFO(1, true),
+	IMS_PCU_DEVINFO(2, true),
+	IMS_PCU_DEVINFO(3, true),
+	IMS_PCU_DEVINFO(4, true),
+	IMS_PCU_DEVINFO(5, false),
+};
+
+static void ims_pcu_buttons_report(struct ims_pcu *pcu, u32 data)
+{
+	struct ims_pcu_buttons *buttons = &pcu->buttons;
+	struct input_dev *input = buttons->input;
+	int i;
+
+	for (i = 0; i < 32; i++) {
+		unsigned short keycode = buttons->keymap[i];
+
+		if (keycode != KEY_RESERVED)
+			input_report_key(input, keycode, data & (1UL << i));
+	}
+
+	input_sync(input);
+}
+
+static int ims_pcu_setup_buttons(struct ims_pcu *pcu,
+				 const unsigned short *keymap,
+				 size_t keymap_len)
+{
+	struct ims_pcu_buttons *buttons = &pcu->buttons;
+	struct input_dev *input;
+	int i;
+	int error;
+
+	input = input_allocate_device();
+	if (!input) {
+		dev_err(pcu->dev,
+			"Not enough memory for input input device\n");
+		return -ENOMEM;
+	}
+
+	snprintf(buttons->name, sizeof(buttons->name),
+		 "IMS PCU#%d Button Interface", pcu->device_no);
+
+	usb_make_path(pcu->udev, buttons->phys, sizeof(buttons->phys));
+	strlcat(buttons->phys, "/input0", sizeof(buttons->phys));
+
+	memcpy(buttons->keymap, keymap, sizeof(*keymap) * keymap_len);
+
+	input->name = buttons->name;
+	input->phys = buttons->phys;
+	usb_to_input_id(pcu->udev, &input->id);
+	input->dev.parent = &pcu->ctrl_intf->dev;
+
+	input->keycode = buttons->keymap;
+	input->keycodemax = ARRAY_SIZE(buttons->keymap);
+	input->keycodesize = sizeof(buttons->keymap[0]);
+
+	__set_bit(EV_KEY, input->evbit);
+	for (i = 0; i < IMS_PCU_KEYMAP_LEN; i++)
+		__set_bit(buttons->keymap[i], input->keybit);
+	__clear_bit(KEY_RESERVED, input->keybit);
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(pcu->dev,
+			"Failed to register buttons input device: %d\n",
+			error);
+		input_free_device(input);
+		return error;
+	}
+
+	buttons->input = input;
+	return 0;
+}
+
+static void ims_pcu_destroy_buttons(struct ims_pcu *pcu)
+{
+	struct ims_pcu_buttons *buttons = &pcu->buttons;
+
+	input_unregister_device(buttons->input);
+}
+
+
+/*********************************************************************
+ *             Gamepad Input device support                          *
+ *********************************************************************/
+
+static void ims_pcu_gamepad_report(struct ims_pcu *pcu, u32 data)
+{
+	struct ims_pcu_gamepad *gamepad = pcu->gamepad;
+	struct input_dev *input = gamepad->input;
+	int x, y;
+
+	x = !!(data & (1 << 14)) - !!(data & (1 << 13));
+	y = !!(data & (1 << 12)) - !!(data & (1 << 11));
+
+	input_report_abs(input, ABS_X, x);
+	input_report_abs(input, ABS_Y, y);
+
+	input_report_key(input, BTN_A, data & (1 << 7));
+	input_report_key(input, BTN_B, data & (1 << 8));
+	input_report_key(input, BTN_X, data & (1 << 9));
+	input_report_key(input, BTN_Y, data & (1 << 10));
+	input_report_key(input, BTN_START, data & (1 << 15));
+	input_report_key(input, BTN_SELECT, data & (1 << 16));
+
+	input_sync(input);
+}
+
+static int ims_pcu_setup_gamepad(struct ims_pcu *pcu)
+{
+	struct ims_pcu_gamepad *gamepad;
+	struct input_dev *input;
+	int error;
+
+	gamepad = kzalloc(sizeof(struct ims_pcu_gamepad), GFP_KERNEL);
+	input = input_allocate_device();
+	if (!gamepad || !input) {
+		dev_err(pcu->dev,
+			"Not enough memory for gamepad device\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	gamepad->input = input;
+
+	snprintf(gamepad->name, sizeof(gamepad->name),
+		 "IMS PCU#%d Gamepad Interface", pcu->device_no);
+
+	usb_make_path(pcu->udev, gamepad->phys, sizeof(gamepad->phys));
+	strlcat(gamepad->phys, "/input1", sizeof(gamepad->phys));
+
+	input->name = gamepad->name;
+	input->phys = gamepad->phys;
+	usb_to_input_id(pcu->udev, &input->id);
+	input->dev.parent = &pcu->ctrl_intf->dev;
+
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(BTN_A, input->keybit);
+	__set_bit(BTN_B, input->keybit);
+	__set_bit(BTN_X, input->keybit);
+	__set_bit(BTN_Y, input->keybit);
+	__set_bit(BTN_START, input->keybit);
+	__set_bit(BTN_SELECT, input->keybit);
+
+	__set_bit(EV_ABS, input->evbit);
+	input_set_abs_params(input, ABS_X, -1, 1, 0, 0);
+	input_set_abs_params(input, ABS_Y, -1, 1, 0, 0);
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(pcu->dev,
+			"Failed to register gamepad input device: %d\n",
+			error);
+		goto err_free_mem;
+	}
+
+	pcu->gamepad = gamepad;
+	return 0;
+
+err_free_mem:
+	input_free_device(input);
+	kfree(gamepad);
+	return -ENOMEM;
+}
+
+static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu)
+{
+	struct ims_pcu_gamepad *gamepad = pcu->gamepad;
+
+	input_unregister_device(gamepad->input);
+	kfree(gamepad);
+}
+
+
+/*********************************************************************
+ *             PCU Communication protocol handling                   *
+ *********************************************************************/
+
+#define IMS_PCU_PROTOCOL_STX		0x02
+#define IMS_PCU_PROTOCOL_ETX		0x03
+#define IMS_PCU_PROTOCOL_DLE		0x10
+
+/* PCU commands */
+#define IMS_PCU_CMD_STATUS		0xa0
+#define IMS_PCU_CMD_PCU_RESET		0xa1
+#define IMS_PCU_CMD_RESET_REASON	0xa2
+#define IMS_PCU_CMD_SEND_BUTTONS	0xa3
+#define IMS_PCU_CMD_JUMP_TO_BTLDR	0xa4
+#define IMS_PCU_CMD_GET_INFO		0xa5
+#define IMS_PCU_CMD_SET_BRIGHTNESS	0xa6
+#define IMS_PCU_CMD_EEPROM		0xa7
+#define IMS_PCU_CMD_GET_FW_VERSION	0xa8
+#define IMS_PCU_CMD_GET_BL_VERSION	0xa9
+#define IMS_PCU_CMD_SET_INFO		0xab
+#define IMS_PCU_CMD_GET_BRIGHTNESS	0xac
+#define IMS_PCU_CMD_GET_DEVICE_ID	0xae
+#define IMS_PCU_CMD_SPECIAL_INFO	0xb0
+#define IMS_PCU_CMD_BOOTLOADER		0xb1	/* Pass data to bootloader */
+#define IMS_PCU_CMD_OFN_SET_CONFIG	0xb3
+#define IMS_PCU_CMD_OFN_GET_CONFIG	0xb4
+
+/* PCU responses */
+#define IMS_PCU_RSP_STATUS		0xc0
+#define IMS_PCU_RSP_PCU_RESET		0	/* Originally 0xc1 */
+#define IMS_PCU_RSP_RESET_REASON	0xc2
+#define IMS_PCU_RSP_SEND_BUTTONS	0xc3
+#define IMS_PCU_RSP_JUMP_TO_BTLDR	0	/* Originally 0xc4 */
+#define IMS_PCU_RSP_GET_INFO		0xc5
+#define IMS_PCU_RSP_SET_BRIGHTNESS	0xc6
+#define IMS_PCU_RSP_EEPROM		0xc7
+#define IMS_PCU_RSP_GET_FW_VERSION	0xc8
+#define IMS_PCU_RSP_GET_BL_VERSION	0xc9
+#define IMS_PCU_RSP_SET_INFO		0xcb
+#define IMS_PCU_RSP_GET_BRIGHTNESS	0xcc
+#define IMS_PCU_RSP_CMD_INVALID		0xcd
+#define IMS_PCU_RSP_GET_DEVICE_ID	0xce
+#define IMS_PCU_RSP_SPECIAL_INFO	0xd0
+#define IMS_PCU_RSP_BOOTLOADER		0xd1	/* Bootloader response */
+#define IMS_PCU_RSP_OFN_SET_CONFIG	0xd2
+#define IMS_PCU_RSP_OFN_GET_CONFIG	0xd3
+
+
+#define IMS_PCU_RSP_EVNT_BUTTONS	0xe0	/* Unsolicited, button state */
+#define IMS_PCU_GAMEPAD_MASK		0x0001ff80UL	/* Bits 7 through 16 */
+
+
+#define IMS_PCU_MIN_PACKET_LEN		3
+#define IMS_PCU_DATA_OFFSET		2
+
+#define IMS_PCU_CMD_WRITE_TIMEOUT	100 /* msec */
+#define IMS_PCU_CMD_RESPONSE_TIMEOUT	500 /* msec */
+
+static void ims_pcu_report_events(struct ims_pcu *pcu)
+{
+	u32 data = get_unaligned_be32(&pcu->read_buf[3]);
+
+	ims_pcu_buttons_report(pcu, data & ~IMS_PCU_GAMEPAD_MASK);
+	if (pcu->gamepad)
+		ims_pcu_gamepad_report(pcu, data);
+}
+
+static void ims_pcu_handle_response(struct ims_pcu *pcu)
+{
+	switch (pcu->read_buf[0]) {
+	case IMS_PCU_RSP_EVNT_BUTTONS:
+		if (likely(pcu->setup_complete))
+			ims_pcu_report_events(pcu);
+		break;
+
+	default:
+		/*
+		 * See if we got command completion.
+		 * If both the sequence and response code match save
+		 * the data and signal completion.
+		 */
+		if (pcu->read_buf[0] == pcu->expected_response &&
+		    pcu->read_buf[1] == pcu->ack_id - 1) {
+
+			memcpy(pcu->cmd_buf, pcu->read_buf, pcu->read_pos);
+			pcu->cmd_buf_len = pcu->read_pos;
+			complete(&pcu->cmd_done);
+		}
+		break;
+	}
+}
+
+static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+{
+	int i;
+
+	for (i = 0; i < urb->actual_length; i++) {
+		u8 data = pcu->urb_in_buf[i];
+
+		/* Skip everything until we get Start Xmit */
+		if (!pcu->have_stx && data != IMS_PCU_PROTOCOL_STX)
+			continue;
+
+		if (pcu->have_dle) {
+			pcu->have_dle = false;
+			pcu->read_buf[pcu->read_pos++] = data;
+			pcu->check_sum += data;
+			continue;
+		}
+
+		switch (data) {
+		case IMS_PCU_PROTOCOL_STX:
+			if (pcu->have_stx)
+				dev_warn(pcu->dev,
+					 "Unexpected STX at byte %d, discarding old data\n",
+					 pcu->read_pos);
+			pcu->have_stx = true;
+			pcu->have_dle = false;
+			pcu->read_pos = 0;
+			pcu->check_sum = 0;
+			break;
+
+		case IMS_PCU_PROTOCOL_DLE:
+			pcu->have_dle = true;
+			break;
+
+		case IMS_PCU_PROTOCOL_ETX:
+			if (pcu->read_pos < IMS_PCU_MIN_PACKET_LEN) {
+				dev_warn(pcu->dev,
+					 "Short packet received (%d bytes), ignoring\n",
+					 pcu->read_pos);
+			} else if (pcu->check_sum != 0) {
+				dev_warn(pcu->dev,
+					 "Invalid checksum in packet (%d bytes), ignoring\n",
+					 pcu->read_pos);
+			} else {
+				ims_pcu_handle_response(pcu);
+			}
+
+			pcu->have_stx = false;
+			pcu->have_dle = false;
+			pcu->read_pos = 0;
+			break;
+
+		default:
+			pcu->read_buf[pcu->read_pos++] = data;
+			pcu->check_sum += data;
+			break;
+		}
+	}
+}
+
+static bool ims_pcu_byte_needs_escape(u8 byte)
+{
+	return byte == IMS_PCU_PROTOCOL_STX ||
+	       byte == IMS_PCU_PROTOCOL_ETX ||
+	       byte == IMS_PCU_PROTOCOL_DLE;
+}
+
+static int ims_pcu_send_cmd_chunk(struct ims_pcu *pcu,
+				  u8 command, int chunk, int len)
+{
+	int error;
+
+	error = usb_bulk_msg(pcu->udev,
+			     usb_sndbulkpipe(pcu->udev,
+					     pcu->ep_out->bEndpointAddress),
+			     pcu->urb_out_buf, len,
+			     NULL, IMS_PCU_CMD_WRITE_TIMEOUT);
+	if (error < 0) {
+		dev_dbg(pcu->dev,
+			"Sending 0x%02x command failed at chunk %d: %d\n",
+			command, chunk, error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int ims_pcu_send_command(struct ims_pcu *pcu,
+				u8 command, const u8 *data, int len)
+{
+	int count = 0;
+	int chunk = 0;
+	int delta;
+	int i;
+	int error;
+	u8 csum = 0;
+	u8 ack_id;
+
+	pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_STX;
+
+	/* We know the command need not be escaped */
+	pcu->urb_out_buf[count++] = command;
+	csum += command;
+
+	ack_id = pcu->ack_id++;
+	if (ack_id == 0xff)
+		ack_id = pcu->ack_id++;
+
+	if (ims_pcu_byte_needs_escape(ack_id))
+		pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE;
+
+	pcu->urb_out_buf[count++] = ack_id;
+	csum += ack_id;
+
+	for (i = 0; i < len; i++) {
+
+		delta = ims_pcu_byte_needs_escape(data[i]) ? 2 : 1;
+		if (count + delta >= pcu->max_out_size) {
+			error = ims_pcu_send_cmd_chunk(pcu, command,
+						       ++chunk, count);
+			if (error)
+				return error;
+
+			count = 0;
+		}
+
+		if (delta == 2)
+			pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE;
+
+		pcu->urb_out_buf[count++] = data[i];
+		csum += data[i];
+	}
+
+	csum = 1 + ~csum;
+
+	delta = ims_pcu_byte_needs_escape(csum) ? 3 : 2;
+	if (count + delta >= pcu->max_out_size) {
+		error = ims_pcu_send_cmd_chunk(pcu, command, ++chunk, count);
+		if (error)
+			return error;
+
+		count = 0;
+	}
+
+	if (delta == 3)
+		pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE;
+
+	pcu->urb_out_buf[count++] = csum;
+	pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_ETX;
+
+	return ims_pcu_send_cmd_chunk(pcu, command, ++chunk, count);
+}
+
+static int __ims_pcu_execute_command(struct ims_pcu *pcu,
+				     u8 command, const void *data, size_t len,
+				     u8 expected_response, int response_time)
+{
+	int error;
+
+	pcu->expected_response = expected_response;
+	init_completion(&pcu->cmd_done);
+
+	error = ims_pcu_send_command(pcu, command, data, len);
+	if (error)
+		return error;
+
+	if (expected_response &&
+	    !wait_for_completion_timeout(&pcu->cmd_done,
+					 msecs_to_jiffies(response_time))) {
+		dev_dbg(pcu->dev, "Command 0x%02x timed out\n", command);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+#define ims_pcu_execute_command(pcu, code, data, len)			\
+	__ims_pcu_execute_command(pcu,					\
+				  IMS_PCU_CMD_##code, data, len,	\
+				  IMS_PCU_RSP_##code,			\
+				  IMS_PCU_CMD_RESPONSE_TIMEOUT)
+
+#define ims_pcu_execute_query(pcu, code)				\
+	ims_pcu_execute_command(pcu, code, NULL, 0)
+
+/* Bootloader commands */
+#define IMS_PCU_BL_CMD_QUERY_DEVICE	0xa1
+#define IMS_PCU_BL_CMD_UNLOCK_CONFIG	0xa2
+#define IMS_PCU_BL_CMD_ERASE_APP	0xa3
+#define IMS_PCU_BL_CMD_PROGRAM_DEVICE	0xa4
+#define IMS_PCU_BL_CMD_PROGRAM_COMPLETE	0xa5
+#define IMS_PCU_BL_CMD_READ_APP		0xa6
+#define IMS_PCU_BL_CMD_RESET_DEVICE	0xa7
+#define IMS_PCU_BL_CMD_LAUNCH_APP	0xa8
+
+/* Bootloader commands */
+#define IMS_PCU_BL_RSP_QUERY_DEVICE	0xc1
+#define IMS_PCU_BL_RSP_UNLOCK_CONFIG	0xc2
+#define IMS_PCU_BL_RSP_ERASE_APP	0xc3
+#define IMS_PCU_BL_RSP_PROGRAM_DEVICE	0xc4
+#define IMS_PCU_BL_RSP_PROGRAM_COMPLETE	0xc5
+#define IMS_PCU_BL_RSP_READ_APP		0xc6
+#define IMS_PCU_BL_RSP_RESET_DEVICE	0	/* originally 0xa7 */
+#define IMS_PCU_BL_RSP_LAUNCH_APP	0	/* originally 0xa8 */
+
+#define IMS_PCU_BL_DATA_OFFSET		3
+
+static int __ims_pcu_execute_bl_command(struct ims_pcu *pcu,
+				        u8 command, const void *data, size_t len,
+				        u8 expected_response, int response_time)
+{
+	int error;
+
+	pcu->cmd_buf[0] = command;
+	if (data)
+		memcpy(&pcu->cmd_buf[1], data, len);
+
+	error = __ims_pcu_execute_command(pcu,
+				IMS_PCU_CMD_BOOTLOADER, pcu->cmd_buf, len + 1,
+				expected_response ? IMS_PCU_RSP_BOOTLOADER : 0,
+				response_time);
+	if (error) {
+		dev_err(pcu->dev,
+			"Failure when sending 0x%02x command to bootloader, error: %d\n",
+			pcu->cmd_buf[0], error);
+		return error;
+	}
+
+	if (expected_response && pcu->cmd_buf[2] != expected_response) {
+		dev_err(pcu->dev,
+			"Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n",
+			pcu->cmd_buf[2], expected_response);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#define ims_pcu_execute_bl_command(pcu, code, data, len, timeout)	\
+	__ims_pcu_execute_bl_command(pcu,				\
+				     IMS_PCU_BL_CMD_##code, data, len,	\
+				     IMS_PCU_BL_RSP_##code, timeout)	\
+
+#define IMS_PCU_INFO_PART_OFFSET	2
+#define IMS_PCU_INFO_DOM_OFFSET		17
+#define IMS_PCU_INFO_SERIAL_OFFSET	25
+
+#define IMS_PCU_SET_INFO_SIZE		31
+
+static int ims_pcu_get_info(struct ims_pcu *pcu)
+{
+	int error;
+
+	error = ims_pcu_execute_query(pcu, GET_INFO);
+	if (error) {
+		dev_err(pcu->dev,
+			"GET_INFO command failed, error: %d\n", error);
+		return error;
+	}
+
+	memcpy(pcu->part_number,
+	       &pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET],
+	       sizeof(pcu->part_number));
+	memcpy(pcu->date_of_manufacturing,
+	       &pcu->cmd_buf[IMS_PCU_INFO_DOM_OFFSET],
+	       sizeof(pcu->date_of_manufacturing));
+	memcpy(pcu->serial_number,
+	       &pcu->cmd_buf[IMS_PCU_INFO_SERIAL_OFFSET],
+	       sizeof(pcu->serial_number));
+
+	return 0;
+}
+
+static int ims_pcu_set_info(struct ims_pcu *pcu)
+{
+	int error;
+
+	memcpy(&pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET],
+	       pcu->part_number, sizeof(pcu->part_number));
+	memcpy(&pcu->cmd_buf[IMS_PCU_INFO_DOM_OFFSET],
+	       pcu->date_of_manufacturing, sizeof(pcu->date_of_manufacturing));
+	memcpy(&pcu->cmd_buf[IMS_PCU_INFO_SERIAL_OFFSET],
+	       pcu->serial_number, sizeof(pcu->serial_number));
+
+	error = ims_pcu_execute_command(pcu, SET_INFO,
+					&pcu->cmd_buf[IMS_PCU_DATA_OFFSET],
+					IMS_PCU_SET_INFO_SIZE);
+	if (error) {
+		dev_err(pcu->dev,
+			"Failed to update device information, error: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int ims_pcu_switch_to_bootloader(struct ims_pcu *pcu)
+{
+	int error;
+
+	/* Execute jump to the bootoloader */
+	error = ims_pcu_execute_command(pcu, JUMP_TO_BTLDR, NULL, 0);
+	if (error) {
+		dev_err(pcu->dev,
+			"Failure when sending JUMP TO BOOLTLOADER command, error: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+/*********************************************************************
+ *             Firmware Update handling                              *
+ *********************************************************************/
+
+#define IMS_PCU_FIRMWARE_NAME	"imspcu.fw"
+
+struct ims_pcu_flash_fmt {
+	__le32 addr;
+	u8 len;
+	u8 data[];
+};
+
+static unsigned int ims_pcu_count_fw_records(const struct firmware *fw)
+{
+	const struct ihex_binrec *rec = (const struct ihex_binrec *)fw->data;
+	unsigned int count = 0;
+
+	while (rec) {
+		count++;
+		rec = ihex_next_binrec(rec);
+	}
+
+	return count;
+}
+
+static int ims_pcu_verify_block(struct ims_pcu *pcu,
+				u32 addr, u8 len, const u8 *data)
+{
+	struct ims_pcu_flash_fmt *fragment;
+	int error;
+
+	fragment = (void *)&pcu->cmd_buf[1];
+	put_unaligned_le32(addr, &fragment->addr);
+	fragment->len = len;
+
+	error = ims_pcu_execute_bl_command(pcu, READ_APP, NULL, 5,
+					IMS_PCU_CMD_RESPONSE_TIMEOUT);
+	if (error) {
+		dev_err(pcu->dev,
+			"Failed to retrieve block at 0x%08x, len %d, error: %d\n",
+			addr, len, error);
+		return error;
+	}
+
+	fragment = (void *)&pcu->cmd_buf[IMS_PCU_BL_DATA_OFFSET];
+	if (get_unaligned_le32(&fragment->addr) != addr ||
+	    fragment->len != len) {
+		dev_err(pcu->dev,
+			"Wrong block when retrieving 0x%08x (0x%08x), len %d (%d)\n",
+			addr, get_unaligned_le32(&fragment->addr),
+			len, fragment->len);
+		return -EINVAL;
+	}
+
+	if (memcmp(fragment->data, data, len)) {
+		dev_err(pcu->dev,
+			"Mismatch in block at 0x%08x, len %d\n",
+			addr, len);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ims_pcu_flash_firmware(struct ims_pcu *pcu,
+				  const struct firmware *fw,
+				  unsigned int n_fw_records)
+{
+	const struct ihex_binrec *rec = (const struct ihex_binrec *)fw->data;
+	struct ims_pcu_flash_fmt *fragment;
+	unsigned int count = 0;
+	u32 addr;
+	u8 len;
+	int error;
+
+	error = ims_pcu_execute_bl_command(pcu, ERASE_APP, NULL, 0, 2000);
+	if (error) {
+		dev_err(pcu->dev,
+			"Failed to erase application image, error: %d\n",
+			error);
+		return error;
+	}
+
+	while (rec) {
+		/*
+		 * The firmware format is messed up for some reason.
+		 * The address twice that of what is needed for some
+		 * reason and we end up overwriting half of the data
+		 * with the next record.
+		 */
+		addr = be32_to_cpu(rec->addr) / 2;
+		len = be16_to_cpu(rec->len);
+
+		fragment = (void *)&pcu->cmd_buf[1];
+		put_unaligned_le32(addr, &fragment->addr);
+		fragment->len = len;
+		memcpy(fragment->data, rec->data, len);
+
+		error = ims_pcu_execute_bl_command(pcu, PROGRAM_DEVICE,
+						NULL, len + 5,
+						IMS_PCU_CMD_RESPONSE_TIMEOUT);
+		if (error) {
+			dev_err(pcu->dev,
+				"Failed to write block at 0x%08x, len %d, error: %d\n",
+				addr, len, error);
+			return error;
+		}
+
+		if (addr >= pcu->fw_start_addr && addr < pcu->fw_end_addr) {
+			error = ims_pcu_verify_block(pcu, addr, len, rec->data);
+			if (error)
+				return error;
+		}
+
+		count++;
+		pcu->update_firmware_status = (count * 100) / n_fw_records;
+
+		rec = ihex_next_binrec(rec);
+	}
+
+	error = ims_pcu_execute_bl_command(pcu, PROGRAM_COMPLETE,
+					    NULL, 0, 2000);
+	if (error)
+		dev_err(pcu->dev,
+			"Failed to send PROGRAM_COMPLETE, error: %d\n",
+			error);
+
+	return 0;
+}
+
+static int ims_pcu_handle_firmware_update(struct ims_pcu *pcu,
+					  const struct firmware *fw)
+{
+	unsigned int n_fw_records;
+	int retval;
+
+	dev_info(pcu->dev, "Updating firmware %s, size: %zu\n",
+		 IMS_PCU_FIRMWARE_NAME, fw->size);
+
+	n_fw_records = ims_pcu_count_fw_records(fw);
+
+	retval = ims_pcu_flash_firmware(pcu, fw, n_fw_records);
+	if (retval)
+		goto out;
+
+	retval = ims_pcu_execute_bl_command(pcu, LAUNCH_APP, NULL, 0, 0);
+	if (retval)
+		dev_err(pcu->dev,
+			"Failed to start application image, error: %d\n",
+			retval);
+
+out:
+	pcu->update_firmware_status = retval;
+	sysfs_notify(&pcu->dev->kobj, NULL, "update_firmware_status");
+	return retval;
+}
+
+static void ims_pcu_process_async_firmware(const struct firmware *fw,
+					   void *context)
+{
+	struct ims_pcu *pcu = context;
+	int error;
+
+	if (!fw) {
+		dev_err(pcu->dev, "Failed to get firmware %s\n",
+			IMS_PCU_FIRMWARE_NAME);
+		goto out;
+	}
+
+	error = ihex_validate_fw(fw);
+	if (error) {
+		dev_err(pcu->dev, "Firmware %s is invalid\n",
+			IMS_PCU_FIRMWARE_NAME);
+		goto out;
+	}
+
+	mutex_lock(&pcu->cmd_mutex);
+	ims_pcu_handle_firmware_update(pcu, fw);
+	mutex_unlock(&pcu->cmd_mutex);
+
+	release_firmware(fw);
+
+out:
+	complete(&pcu->async_firmware_done);
+}
+
+/*********************************************************************
+ *             Backlight LED device support                          *
+ *********************************************************************/
+
+#define IMS_PCU_MAX_BRIGHTNESS		31998
+
+static void ims_pcu_backlight_work(struct work_struct *work)
+{
+	struct ims_pcu_backlight *backlight =
+			container_of(work, struct ims_pcu_backlight, work);
+	struct ims_pcu *pcu =
+			container_of(backlight, struct ims_pcu, backlight);
+	int desired_brightness = backlight->desired_brightness;
+	__le16 br_val = cpu_to_le16(desired_brightness);
+	int error;
+
+	mutex_lock(&pcu->cmd_mutex);
+
+	error = ims_pcu_execute_command(pcu, SET_BRIGHTNESS,
+					&br_val, sizeof(br_val));
+	if (error && error != -ENODEV)
+		dev_warn(pcu->dev,
+			 "Failed to set desired brightness %u, error: %d\n",
+			 desired_brightness, error);
+
+	mutex_unlock(&pcu->cmd_mutex);
+}
+
+static void ims_pcu_backlight_set_brightness(struct led_classdev *cdev,
+					     enum led_brightness value)
+{
+	struct ims_pcu_backlight *backlight =
+			container_of(cdev, struct ims_pcu_backlight, cdev);
+
+	backlight->desired_brightness = value;
+	schedule_work(&backlight->work);
+}
+
+static enum led_brightness
+ims_pcu_backlight_get_brightness(struct led_classdev *cdev)
+{
+	struct ims_pcu_backlight *backlight =
+			container_of(cdev, struct ims_pcu_backlight, cdev);
+	struct ims_pcu *pcu =
+			container_of(backlight, struct ims_pcu, backlight);
+	int brightness;
+	int error;
+
+	mutex_lock(&pcu->cmd_mutex);
+
+	error = ims_pcu_execute_query(pcu, GET_BRIGHTNESS);
+	if (error) {
+		dev_warn(pcu->dev,
+			 "Failed to get current brightness, error: %d\n",
+			 error);
+		/* Assume the LED is OFF */
+		brightness = LED_OFF;
+	} else {
+		brightness =
+			get_unaligned_le16(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET]);
+	}
+
+	mutex_unlock(&pcu->cmd_mutex);
+
+	return brightness;
+}
+
+static int ims_pcu_setup_backlight(struct ims_pcu *pcu)
+{
+	struct ims_pcu_backlight *backlight = &pcu->backlight;
+	int error;
+
+	INIT_WORK(&backlight->work, ims_pcu_backlight_work);
+	snprintf(backlight->name, sizeof(backlight->name),
+		 "pcu%d::kbd_backlight", pcu->device_no);
+
+	backlight->cdev.name = backlight->name;
+	backlight->cdev.max_brightness = IMS_PCU_MAX_BRIGHTNESS;
+	backlight->cdev.brightness_get = ims_pcu_backlight_get_brightness;
+	backlight->cdev.brightness_set = ims_pcu_backlight_set_brightness;
+
+	error = led_classdev_register(pcu->dev, &backlight->cdev);
+	if (error) {
+		dev_err(pcu->dev,
+			"Failed to register backlight LED device, error: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static void ims_pcu_destroy_backlight(struct ims_pcu *pcu)
+{
+	struct ims_pcu_backlight *backlight = &pcu->backlight;
+
+	led_classdev_unregister(&backlight->cdev);
+	cancel_work_sync(&backlight->work);
+}
+
+
+/*********************************************************************
+ *             Sysfs attributes handling                             *
+ *********************************************************************/
+
+struct ims_pcu_attribute {
+	struct device_attribute dattr;
+	size_t field_offset;
+	int field_length;
+};
+
+static ssize_t ims_pcu_attribute_show(struct device *dev,
+				      struct device_attribute *dattr,
+				      char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	struct ims_pcu_attribute *attr =
+			container_of(dattr, struct ims_pcu_attribute, dattr);
+	char *field = (char *)pcu + attr->field_offset;
+
+	return scnprintf(buf, PAGE_SIZE, "%.*s\n", attr->field_length, field);
+}
+
+static ssize_t ims_pcu_attribute_store(struct device *dev,
+				       struct device_attribute *dattr,
+				       const char *buf, size_t count)
+{
+
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	struct ims_pcu_attribute *attr =
+			container_of(dattr, struct ims_pcu_attribute, dattr);
+	char *field = (char *)pcu + attr->field_offset;
+	size_t data_len;
+	int error;
+
+	if (count > attr->field_length)
+		return -EINVAL;
+
+	data_len = strnlen(buf, attr->field_length);
+	if (data_len > attr->field_length)
+		return -EINVAL;
+
+	error = mutex_lock_interruptible(&pcu->cmd_mutex);
+	if (error)
+		return error;
+
+	memset(field, 0, attr->field_length);
+	memcpy(field, buf, data_len);
+
+	error = ims_pcu_set_info(pcu);
+
+	/*
+	 * Even if update failed, let's fetch the info again as we just
+	 * clobbered one of the fields.
+	 */
+	ims_pcu_get_info(pcu);
+
+	mutex_unlock(&pcu->cmd_mutex);
+
+	return error < 0 ? error : count;
+}
+
+#define IMS_PCU_ATTR(_field, _mode)					\
+struct ims_pcu_attribute ims_pcu_attr_##_field = {			\
+	.dattr = __ATTR(_field, _mode,					\
+			ims_pcu_attribute_show,				\
+			ims_pcu_attribute_store),			\
+	.field_offset = offsetof(struct ims_pcu, _field),		\
+	.field_length = sizeof(((struct ims_pcu *)NULL)->_field),	\
+}
+
+#define IMS_PCU_RO_ATTR(_field)						\
+		IMS_PCU_ATTR(_field, S_IRUGO)
+#define IMS_PCU_RW_ATTR(_field)						\
+		IMS_PCU_ATTR(_field, S_IRUGO | S_IWUSR)
+
+static IMS_PCU_RW_ATTR(part_number);
+static IMS_PCU_RW_ATTR(serial_number);
+static IMS_PCU_RW_ATTR(date_of_manufacturing);
+
+static IMS_PCU_RO_ATTR(fw_version);
+static IMS_PCU_RO_ATTR(bl_version);
+static IMS_PCU_RO_ATTR(reset_reason);
+
+static ssize_t ims_pcu_reset_device(struct device *dev,
+				    struct device_attribute *dattr,
+				    const char *buf, size_t count)
+{
+	static const u8 reset_byte = 1;
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	int value;
+	int error;
+
+	error = kstrtoint(buf, 0, &value);
+	if (error)
+		return error;
+
+	if (value != 1)
+		return -EINVAL;
+
+	dev_info(pcu->dev, "Attempting to reset device\n");
+
+	error = ims_pcu_execute_command(pcu, PCU_RESET, &reset_byte, 1);
+	if (error) {
+		dev_info(pcu->dev,
+			 "Failed to reset device, error: %d\n",
+			 error);
+		return error;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(reset_device, S_IWUSR, NULL, ims_pcu_reset_device);
+
+static ssize_t ims_pcu_update_firmware_store(struct device *dev,
+					     struct device_attribute *dattr,
+					     const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	const struct firmware *fw = NULL;
+	int value;
+	int error;
+
+	error = kstrtoint(buf, 0, &value);
+	if (error)
+		return error;
+
+	if (value != 1)
+		return -EINVAL;
+
+	error = mutex_lock_interruptible(&pcu->cmd_mutex);
+	if (error)
+		return error;
+
+	error = request_ihex_firmware(&fw, IMS_PCU_FIRMWARE_NAME, pcu->dev);
+	if (error) {
+		dev_err(pcu->dev, "Failed to request firmware %s, error: %d\n",
+			IMS_PCU_FIRMWARE_NAME, error);
+		goto out;
+	}
+
+	/*
+	 * If we are already in bootloader mode we can proceed with
+	 * flashing the firmware.
+	 *
+	 * If we are in application mode, then we need to switch into
+	 * bootloader mode, which will cause the device to disconnect
+	 * and reconnect as different device.
+	 */
+	if (pcu->bootloader_mode)
+		error = ims_pcu_handle_firmware_update(pcu, fw);
+	else
+		error = ims_pcu_switch_to_bootloader(pcu);
+
+	release_firmware(fw);
+
+out:
+	mutex_unlock(&pcu->cmd_mutex);
+	return error ?: count;
+}
+
+static DEVICE_ATTR(update_firmware, S_IWUSR,
+		   NULL, ims_pcu_update_firmware_store);
+
+static ssize_t
+ims_pcu_update_firmware_status_show(struct device *dev,
+				    struct device_attribute *dattr,
+				    char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", pcu->update_firmware_status);
+}
+
+static DEVICE_ATTR(update_firmware_status, S_IRUGO,
+		   ims_pcu_update_firmware_status_show, NULL);
+
+static struct attribute *ims_pcu_attrs[] = {
+	&ims_pcu_attr_part_number.dattr.attr,
+	&ims_pcu_attr_serial_number.dattr.attr,
+	&ims_pcu_attr_date_of_manufacturing.dattr.attr,
+	&ims_pcu_attr_fw_version.dattr.attr,
+	&ims_pcu_attr_bl_version.dattr.attr,
+	&ims_pcu_attr_reset_reason.dattr.attr,
+	&dev_attr_reset_device.attr,
+	&dev_attr_update_firmware.attr,
+	&dev_attr_update_firmware_status.attr,
+	NULL
+};
+
+static umode_t ims_pcu_is_attr_visible(struct kobject *kobj,
+				       struct attribute *attr, int n)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	umode_t mode = attr->mode;
+
+	if (pcu->bootloader_mode) {
+		if (attr != &dev_attr_update_firmware_status.attr &&
+		    attr != &dev_attr_update_firmware.attr &&
+		    attr != &dev_attr_reset_device.attr) {
+			mode = 0;
+		}
+	} else {
+		if (attr == &dev_attr_update_firmware_status.attr)
+			mode = 0;
+	}
+
+	return mode;
+}
+
+static struct attribute_group ims_pcu_attr_group = {
+	.is_visible	= ims_pcu_is_attr_visible,
+	.attrs		= ims_pcu_attrs,
+};
+
+/* Support for a separate OFN attribute group */
+
+#define OFN_REG_RESULT_OFFSET	2
+
+static int ims_pcu_read_ofn_config(struct ims_pcu *pcu, u8 addr, u8 *data)
+{
+	int error;
+	s16 result;
+
+	error = ims_pcu_execute_command(pcu, OFN_GET_CONFIG,
+					&addr, sizeof(addr));
+	if (error)
+		return error;
+
+	result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
+	if (result < 0)
+		return -EIO;
+
+	/* We only need LSB */
+	*data = pcu->cmd_buf[OFN_REG_RESULT_OFFSET];
+	return 0;
+}
+
+static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data)
+{
+	u8 buffer[] = { addr, data };
+	int error;
+	s16 result;
+
+	error = ims_pcu_execute_command(pcu, OFN_SET_CONFIG,
+					&buffer, sizeof(buffer));
+	if (error)
+		return error;
+
+	result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
+	if (result < 0)
+		return -EIO;
+
+	return 0;
+}
+
+static ssize_t ims_pcu_ofn_reg_data_show(struct device *dev,
+					 struct device_attribute *dattr,
+					 char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	int error;
+	u8 data;
+
+	mutex_lock(&pcu->cmd_mutex);
+	error = ims_pcu_read_ofn_config(pcu, pcu->ofn_reg_addr, &data);
+	mutex_unlock(&pcu->cmd_mutex);
+
+	if (error)
+		return error;
+
+	return scnprintf(buf, PAGE_SIZE, "%x\n", data);
+}
+
+static ssize_t ims_pcu_ofn_reg_data_store(struct device *dev,
+					  struct device_attribute *dattr,
+					  const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	int error;
+	u8 value;
+
+	error = kstrtou8(buf, 0, &value);
+	if (error)
+		return error;
+
+	mutex_lock(&pcu->cmd_mutex);
+	error = ims_pcu_write_ofn_config(pcu, pcu->ofn_reg_addr, value);
+	mutex_unlock(&pcu->cmd_mutex);
+
+	return error ?: count;
+}
+
+static DEVICE_ATTR(reg_data, S_IRUGO | S_IWUSR,
+		   ims_pcu_ofn_reg_data_show, ims_pcu_ofn_reg_data_store);
+
+static ssize_t ims_pcu_ofn_reg_addr_show(struct device *dev,
+					 struct device_attribute *dattr,
+					 char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	int error;
+
+	mutex_lock(&pcu->cmd_mutex);
+	error = scnprintf(buf, PAGE_SIZE, "%x\n", pcu->ofn_reg_addr);
+	mutex_unlock(&pcu->cmd_mutex);
+
+	return error;
+}
+
+static ssize_t ims_pcu_ofn_reg_addr_store(struct device *dev,
+					  struct device_attribute *dattr,
+					  const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	int error;
+	u8 value;
+
+	error = kstrtou8(buf, 0, &value);
+	if (error)
+		return error;
+
+	mutex_lock(&pcu->cmd_mutex);
+	pcu->ofn_reg_addr = value;
+	mutex_unlock(&pcu->cmd_mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(reg_addr, S_IRUGO | S_IWUSR,
+		   ims_pcu_ofn_reg_addr_show, ims_pcu_ofn_reg_addr_store);
+
+struct ims_pcu_ofn_bit_attribute {
+	struct device_attribute dattr;
+	u8 addr;
+	u8 nr;
+};
+
+static ssize_t ims_pcu_ofn_bit_show(struct device *dev,
+				    struct device_attribute *dattr,
+				    char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	struct ims_pcu_ofn_bit_attribute *attr =
+		container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr);
+	int error;
+	u8 data;
+
+	mutex_lock(&pcu->cmd_mutex);
+	error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
+	mutex_unlock(&pcu->cmd_mutex);
+
+	if (error)
+		return error;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", !!(data & (1 << attr->nr)));
+}
+
+static ssize_t ims_pcu_ofn_bit_store(struct device *dev,
+				     struct device_attribute *dattr,
+				     const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	struct ims_pcu_ofn_bit_attribute *attr =
+		container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr);
+	int error;
+	int value;
+	u8 data;
+
+	error = kstrtoint(buf, 0, &value);
+	if (error)
+		return error;
+
+	if (value > 1)
+		return -EINVAL;
+
+	mutex_lock(&pcu->cmd_mutex);
+
+	error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
+	if (!error) {
+		if (value)
+			data |= 1U << attr->nr;
+		else
+			data &= ~(1U << attr->nr);
+
+		error = ims_pcu_write_ofn_config(pcu, attr->addr, data);
+	}
+
+	mutex_unlock(&pcu->cmd_mutex);
+
+	return error ?: count;
+}
+
+#define IMS_PCU_OFN_BIT_ATTR(_field, _addr, _nr)			\
+struct ims_pcu_ofn_bit_attribute ims_pcu_ofn_attr_##_field = {		\
+	.dattr = __ATTR(_field, S_IWUSR | S_IRUGO,			\
+			ims_pcu_ofn_bit_show, ims_pcu_ofn_bit_store),	\
+	.addr = _addr,							\
+	.nr = _nr,							\
+}
+
+static IMS_PCU_OFN_BIT_ATTR(engine_enable,   0x60, 7);
+static IMS_PCU_OFN_BIT_ATTR(speed_enable,    0x60, 6);
+static IMS_PCU_OFN_BIT_ATTR(assert_enable,   0x60, 5);
+static IMS_PCU_OFN_BIT_ATTR(xyquant_enable,  0x60, 4);
+static IMS_PCU_OFN_BIT_ATTR(xyscale_enable,  0x60, 1);
+
+static IMS_PCU_OFN_BIT_ATTR(scale_x2,        0x63, 6);
+static IMS_PCU_OFN_BIT_ATTR(scale_y2,        0x63, 7);
+
+static struct attribute *ims_pcu_ofn_attrs[] = {
+	&dev_attr_reg_data.attr,
+	&dev_attr_reg_addr.attr,
+	&ims_pcu_ofn_attr_engine_enable.dattr.attr,
+	&ims_pcu_ofn_attr_speed_enable.dattr.attr,
+	&ims_pcu_ofn_attr_assert_enable.dattr.attr,
+	&ims_pcu_ofn_attr_xyquant_enable.dattr.attr,
+	&ims_pcu_ofn_attr_xyscale_enable.dattr.attr,
+	&ims_pcu_ofn_attr_scale_x2.dattr.attr,
+	&ims_pcu_ofn_attr_scale_y2.dattr.attr,
+	NULL
+};
+
+static struct attribute_group ims_pcu_ofn_attr_group = {
+	.name	= "ofn",
+	.attrs	= ims_pcu_ofn_attrs,
+};
+
+static void ims_pcu_irq(struct urb *urb)
+{
+	struct ims_pcu *pcu = urb->context;
+	int retval, status;
+
+	status = urb->status;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(pcu->dev, "%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(pcu->dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto exit;
+	}
+
+	dev_dbg(pcu->dev, "%s: received %d: %*ph\n", __func__,
+		urb->actual_length, urb->actual_length, pcu->urb_in_buf);
+
+	if (urb == pcu->urb_in)
+		ims_pcu_process_data(pcu, urb);
+
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval && retval != -ENODEV)
+		dev_err(pcu->dev, "%s - usb_submit_urb failed with result %d\n",
+			__func__, retval);
+}
+
+static int ims_pcu_buffers_alloc(struct ims_pcu *pcu)
+{
+	int error;
+
+	pcu->urb_in_buf = usb_alloc_coherent(pcu->udev, pcu->max_in_size,
+					     GFP_KERNEL, &pcu->read_dma);
+	if (!pcu->urb_in_buf) {
+		dev_err(pcu->dev,
+			"Failed to allocate memory for read buffer\n");
+		return -ENOMEM;
+	}
+
+	pcu->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+	if (!pcu->urb_in) {
+		dev_err(pcu->dev, "Failed to allocate input URB\n");
+		error = -ENOMEM;
+		goto err_free_urb_in_buf;
+	}
+
+	pcu->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	pcu->urb_in->transfer_dma = pcu->read_dma;
+
+	usb_fill_bulk_urb(pcu->urb_in, pcu->udev,
+			  usb_rcvbulkpipe(pcu->udev,
+					  pcu->ep_in->bEndpointAddress),
+			  pcu->urb_in_buf, pcu->max_in_size,
+			  ims_pcu_irq, pcu);
+
+	/*
+	 * We are using usb_bulk_msg() for sending so there is no point
+	 * in allocating memory with usb_alloc_coherent().
+	 */
+	pcu->urb_out_buf = kmalloc(pcu->max_out_size, GFP_KERNEL);
+	if (!pcu->urb_out_buf) {
+		dev_err(pcu->dev, "Failed to allocate memory for write buffer\n");
+		error = -ENOMEM;
+		goto err_free_in_urb;
+	}
+
+	pcu->urb_ctrl_buf = usb_alloc_coherent(pcu->udev, pcu->max_ctrl_size,
+					       GFP_KERNEL, &pcu->ctrl_dma);
+	if (!pcu->urb_ctrl_buf) {
+		dev_err(pcu->dev,
+			"Failed to allocate memory for read buffer\n");
+		error = -ENOMEM;
+		goto err_free_urb_out_buf;
+	}
+
+	pcu->urb_ctrl = usb_alloc_urb(0, GFP_KERNEL);
+	if (!pcu->urb_ctrl) {
+		dev_err(pcu->dev, "Failed to allocate input URB\n");
+		error = -ENOMEM;
+		goto err_free_urb_ctrl_buf;
+	}
+
+	pcu->urb_ctrl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	pcu->urb_ctrl->transfer_dma = pcu->ctrl_dma;
+
+	usb_fill_int_urb(pcu->urb_ctrl, pcu->udev,
+			  usb_rcvintpipe(pcu->udev,
+					 pcu->ep_ctrl->bEndpointAddress),
+			  pcu->urb_ctrl_buf, pcu->max_ctrl_size,
+			  ims_pcu_irq, pcu, pcu->ep_ctrl->bInterval);
+
+	return 0;
+
+err_free_urb_ctrl_buf:
+	usb_free_coherent(pcu->udev, pcu->max_ctrl_size,
+			  pcu->urb_ctrl_buf, pcu->ctrl_dma);
+err_free_urb_out_buf:
+	kfree(pcu->urb_out_buf);
+err_free_in_urb:
+	usb_free_urb(pcu->urb_in);
+err_free_urb_in_buf:
+	usb_free_coherent(pcu->udev, pcu->max_in_size,
+			  pcu->urb_in_buf, pcu->read_dma);
+	return error;
+}
+
+static void ims_pcu_buffers_free(struct ims_pcu *pcu)
+{
+	usb_kill_urb(pcu->urb_in);
+	usb_free_urb(pcu->urb_in);
+
+	usb_free_coherent(pcu->udev, pcu->max_out_size,
+			  pcu->urb_in_buf, pcu->read_dma);
+
+	kfree(pcu->urb_out_buf);
+
+	usb_kill_urb(pcu->urb_ctrl);
+	usb_free_urb(pcu->urb_ctrl);
+
+	usb_free_coherent(pcu->udev, pcu->max_ctrl_size,
+			  pcu->urb_ctrl_buf, pcu->ctrl_dma);
+}
+
+static const struct usb_cdc_union_desc *
+ims_pcu_get_cdc_union_desc(struct usb_interface *intf)
+{
+	const void *buf = intf->altsetting->extra;
+	size_t buflen = intf->altsetting->extralen;
+	struct usb_cdc_union_desc *union_desc;
+
+	if (!buf) {
+		dev_err(&intf->dev, "Missing descriptor data\n");
+		return NULL;
+	}
+
+	if (!buflen) {
+		dev_err(&intf->dev, "Zero length descriptor\n");
+		return NULL;
+	}
+
+	while (buflen >= sizeof(*union_desc)) {
+		union_desc = (struct usb_cdc_union_desc *)buf;
+
+		if (union_desc->bLength > buflen) {
+			dev_err(&intf->dev, "Too large descriptor\n");
+			return NULL;
+		}
+
+		if (union_desc->bDescriptorType == USB_DT_CS_INTERFACE &&
+		    union_desc->bDescriptorSubType == USB_CDC_UNION_TYPE) {
+			dev_dbg(&intf->dev, "Found union header\n");
+
+			if (union_desc->bLength >= sizeof(*union_desc))
+				return union_desc;
+
+			dev_err(&intf->dev,
+				"Union descriptor to short (%d vs %zd\n)",
+				union_desc->bLength, sizeof(*union_desc));
+			return NULL;
+		}
+
+		buflen -= union_desc->bLength;
+		buf += union_desc->bLength;
+	}
+
+	dev_err(&intf->dev, "Missing CDC union descriptor\n");
+	return NULL;
+}
+
+static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pcu)
+{
+	const struct usb_cdc_union_desc *union_desc;
+	struct usb_host_interface *alt;
+
+	union_desc = ims_pcu_get_cdc_union_desc(intf);
+	if (!union_desc)
+		return -EINVAL;
+
+	pcu->ctrl_intf = usb_ifnum_to_if(pcu->udev,
+					 union_desc->bMasterInterface0);
+	if (!pcu->ctrl_intf)
+		return -EINVAL;
+
+	alt = pcu->ctrl_intf->cur_altsetting;
+
+	if (alt->desc.bNumEndpoints < 1)
+		return -ENODEV;
+
+	pcu->ep_ctrl = &alt->endpoint[0].desc;
+	pcu->max_ctrl_size = usb_endpoint_maxp(pcu->ep_ctrl);
+
+	pcu->data_intf = usb_ifnum_to_if(pcu->udev,
+					 union_desc->bSlaveInterface0);
+	if (!pcu->data_intf)
+		return -EINVAL;
+
+	alt = pcu->data_intf->cur_altsetting;
+	if (alt->desc.bNumEndpoints != 2) {
+		dev_err(pcu->dev,
+			"Incorrect number of endpoints on data interface (%d)\n",
+			alt->desc.bNumEndpoints);
+		return -EINVAL;
+	}
+
+	pcu->ep_out = &alt->endpoint[0].desc;
+	if (!usb_endpoint_is_bulk_out(pcu->ep_out)) {
+		dev_err(pcu->dev,
+			"First endpoint on data interface is not BULK OUT\n");
+		return -EINVAL;
+	}
+
+	pcu->max_out_size = usb_endpoint_maxp(pcu->ep_out);
+	if (pcu->max_out_size < 8) {
+		dev_err(pcu->dev,
+			"Max OUT packet size is too small (%zd)\n",
+			pcu->max_out_size);
+		return -EINVAL;
+	}
+
+	pcu->ep_in = &alt->endpoint[1].desc;
+	if (!usb_endpoint_is_bulk_in(pcu->ep_in)) {
+		dev_err(pcu->dev,
+			"Second endpoint on data interface is not BULK IN\n");
+		return -EINVAL;
+	}
+
+	pcu->max_in_size = usb_endpoint_maxp(pcu->ep_in);
+	if (pcu->max_in_size < 8) {
+		dev_err(pcu->dev,
+			"Max IN packet size is too small (%zd)\n",
+			pcu->max_in_size);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ims_pcu_start_io(struct ims_pcu *pcu)
+{
+	int error;
+
+	error = usb_submit_urb(pcu->urb_ctrl, GFP_KERNEL);
+	if (error) {
+		dev_err(pcu->dev,
+			"Failed to start control IO - usb_submit_urb failed with result: %d\n",
+			error);
+		return -EIO;
+	}
+
+	error = usb_submit_urb(pcu->urb_in, GFP_KERNEL);
+	if (error) {
+		dev_err(pcu->dev,
+			"Failed to start IO - usb_submit_urb failed with result: %d\n",
+			error);
+		usb_kill_urb(pcu->urb_ctrl);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void ims_pcu_stop_io(struct ims_pcu *pcu)
+{
+	usb_kill_urb(pcu->urb_in);
+	usb_kill_urb(pcu->urb_ctrl);
+}
+
+static int ims_pcu_line_setup(struct ims_pcu *pcu)
+{
+	struct usb_host_interface *interface = pcu->ctrl_intf->cur_altsetting;
+	struct usb_cdc_line_coding *line = (void *)pcu->cmd_buf;
+	int error;
+
+	memset(line, 0, sizeof(*line));
+	line->dwDTERate = cpu_to_le32(57600);
+	line->bDataBits = 8;
+
+	error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0),
+				USB_CDC_REQ_SET_LINE_CODING,
+				USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+				0, interface->desc.bInterfaceNumber,
+				line, sizeof(struct usb_cdc_line_coding),
+				5000);
+	if (error < 0) {
+		dev_err(pcu->dev, "Failed to set line coding, error: %d\n",
+			error);
+		return error;
+	}
+
+	error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0),
+				USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+				USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+				0x03, interface->desc.bInterfaceNumber,
+				NULL, 0, 5000);
+	if (error < 0) {
+		dev_err(pcu->dev, "Failed to set line state, error: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int ims_pcu_get_device_info(struct ims_pcu *pcu)
+{
+	int error;
+
+	error = ims_pcu_get_info(pcu);
+	if (error)
+		return error;
+
+	error = ims_pcu_execute_query(pcu, GET_FW_VERSION);
+	if (error) {
+		dev_err(pcu->dev,
+			"GET_FW_VERSION command failed, error: %d\n", error);
+		return error;
+	}
+
+	snprintf(pcu->fw_version, sizeof(pcu->fw_version),
+		 "%02d%02d%02d%02d.%c%c",
+		 pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5],
+		 pcu->cmd_buf[6], pcu->cmd_buf[7]);
+
+	error = ims_pcu_execute_query(pcu, GET_BL_VERSION);
+	if (error) {
+		dev_err(pcu->dev,
+			"GET_BL_VERSION command failed, error: %d\n", error);
+		return error;
+	}
+
+	snprintf(pcu->bl_version, sizeof(pcu->bl_version),
+		 "%02d%02d%02d%02d.%c%c",
+		 pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5],
+		 pcu->cmd_buf[6], pcu->cmd_buf[7]);
+
+	error = ims_pcu_execute_query(pcu, RESET_REASON);
+	if (error) {
+		dev_err(pcu->dev,
+			"RESET_REASON command failed, error: %d\n", error);
+		return error;
+	}
+
+	snprintf(pcu->reset_reason, sizeof(pcu->reset_reason),
+		 "%02x", pcu->cmd_buf[IMS_PCU_DATA_OFFSET]);
+
+	dev_dbg(pcu->dev,
+		"P/N: %s, MD: %s, S/N: %s, FW: %s, BL: %s, RR: %s\n",
+		pcu->part_number,
+		pcu->date_of_manufacturing,
+		pcu->serial_number,
+		pcu->fw_version,
+		pcu->bl_version,
+		pcu->reset_reason);
+
+	return 0;
+}
+
+static int ims_pcu_identify_type(struct ims_pcu *pcu, u8 *device_id)
+{
+	int error;
+
+	error = ims_pcu_execute_query(pcu, GET_DEVICE_ID);
+	if (error) {
+		dev_err(pcu->dev,
+			"GET_DEVICE_ID command failed, error: %d\n", error);
+		return error;
+	}
+
+	*device_id = pcu->cmd_buf[IMS_PCU_DATA_OFFSET];
+	dev_dbg(pcu->dev, "Detected device ID: %d\n", *device_id);
+
+	return 0;
+}
+
+static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
+{
+	static atomic_t device_no = ATOMIC_INIT(-1);
+
+	const struct ims_pcu_device_info *info;
+	int error;
+
+	error = ims_pcu_get_device_info(pcu);
+	if (error) {
+		/* Device does not respond to basic queries, hopeless */
+		return error;
+	}
+
+	error = ims_pcu_identify_type(pcu, &pcu->device_id);
+	if (error) {
+		dev_err(pcu->dev,
+			"Failed to identify device, error: %d\n", error);
+		/*
+		 * Do not signal error, but do not create input nor
+		 * backlight devices either, let userspace figure this
+		 * out (flash a new firmware?).
+		 */
+		return 0;
+	}
+
+	if (pcu->device_id >= ARRAY_SIZE(ims_pcu_device_info) ||
+	    !ims_pcu_device_info[pcu->device_id].keymap) {
+		dev_err(pcu->dev, "Device ID %d is not valid\n", pcu->device_id);
+		/* Same as above, punt to userspace */
+		return 0;
+	}
+
+	/* Device appears to be operable, complete initialization */
+	pcu->device_no = atomic_inc_return(&device_no);
+
+	/*
+	 * PCU-B devices, both GEN_1 and GEN_2 do not have OFN sensor
+	 */
+	if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID) {
+		error = sysfs_create_group(&pcu->dev->kobj,
+					   &ims_pcu_ofn_attr_group);
+		if (error)
+			return error;
+	}
+
+	error = ims_pcu_setup_backlight(pcu);
+	if (error)
+		return error;
+
+	info = &ims_pcu_device_info[pcu->device_id];
+	error = ims_pcu_setup_buttons(pcu, info->keymap, info->keymap_len);
+	if (error)
+		goto err_destroy_backlight;
+
+	if (info->has_gamepad) {
+		error = ims_pcu_setup_gamepad(pcu);
+		if (error)
+			goto err_destroy_buttons;
+	}
+
+	pcu->setup_complete = true;
+
+	return 0;
+
+err_destroy_buttons:
+	ims_pcu_destroy_buttons(pcu);
+err_destroy_backlight:
+	ims_pcu_destroy_backlight(pcu);
+	return error;
+}
+
+static void ims_pcu_destroy_application_mode(struct ims_pcu *pcu)
+{
+	if (pcu->setup_complete) {
+		pcu->setup_complete = false;
+		mb(); /* make sure flag setting is not reordered */
+
+		if (pcu->gamepad)
+			ims_pcu_destroy_gamepad(pcu);
+		ims_pcu_destroy_buttons(pcu);
+		ims_pcu_destroy_backlight(pcu);
+
+		if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID)
+			sysfs_remove_group(&pcu->dev->kobj,
+					   &ims_pcu_ofn_attr_group);
+	}
+}
+
+static int ims_pcu_init_bootloader_mode(struct ims_pcu *pcu)
+{
+	int error;
+
+	error = ims_pcu_execute_bl_command(pcu, QUERY_DEVICE, NULL, 0,
+					   IMS_PCU_CMD_RESPONSE_TIMEOUT);
+	if (error) {
+		dev_err(pcu->dev, "Bootloader does not respond, aborting\n");
+		return error;
+	}
+
+	pcu->fw_start_addr =
+		get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 11]);
+	pcu->fw_end_addr =
+		get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 15]);
+
+	dev_info(pcu->dev,
+		 "Device is in bootloader mode (addr 0x%08x-0x%08x), requesting firmware\n",
+		 pcu->fw_start_addr, pcu->fw_end_addr);
+
+	error = request_firmware_nowait(THIS_MODULE, true,
+					IMS_PCU_FIRMWARE_NAME,
+					pcu->dev, GFP_KERNEL, pcu,
+					ims_pcu_process_async_firmware);
+	if (error) {
+		/* This error is not fatal, let userspace have another chance */
+		complete(&pcu->async_firmware_done);
+	}
+
+	return 0;
+}
+
+static void ims_pcu_destroy_bootloader_mode(struct ims_pcu *pcu)
+{
+	/* Make sure our initial firmware request has completed */
+	wait_for_completion(&pcu->async_firmware_done);
+}
+
+#define IMS_PCU_APPLICATION_MODE	0
+#define IMS_PCU_BOOTLOADER_MODE		1
+
+static struct usb_driver ims_pcu_driver;
+
+static int ims_pcu_probe(struct usb_interface *intf,
+			 const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct ims_pcu *pcu;
+	int error;
+
+	pcu = kzalloc(sizeof(struct ims_pcu), GFP_KERNEL);
+	if (!pcu)
+		return -ENOMEM;
+
+	pcu->dev = &intf->dev;
+	pcu->udev = udev;
+	pcu->bootloader_mode = id->driver_info == IMS_PCU_BOOTLOADER_MODE;
+	mutex_init(&pcu->cmd_mutex);
+	init_completion(&pcu->cmd_done);
+	init_completion(&pcu->async_firmware_done);
+
+	error = ims_pcu_parse_cdc_data(intf, pcu);
+	if (error)
+		goto err_free_mem;
+
+	error = usb_driver_claim_interface(&ims_pcu_driver,
+					   pcu->data_intf, pcu);
+	if (error) {
+		dev_err(&intf->dev,
+			"Unable to claim corresponding data interface: %d\n",
+			error);
+		goto err_free_mem;
+	}
+
+	usb_set_intfdata(pcu->ctrl_intf, pcu);
+	usb_set_intfdata(pcu->data_intf, pcu);
+
+	error = ims_pcu_buffers_alloc(pcu);
+	if (error)
+		goto err_unclaim_intf;
+
+	error = ims_pcu_start_io(pcu);
+	if (error)
+		goto err_free_buffers;
+
+	error = ims_pcu_line_setup(pcu);
+	if (error)
+		goto err_stop_io;
+
+	error = sysfs_create_group(&intf->dev.kobj, &ims_pcu_attr_group);
+	if (error)
+		goto err_stop_io;
+
+	error = pcu->bootloader_mode ?
+			ims_pcu_init_bootloader_mode(pcu) :
+			ims_pcu_init_application_mode(pcu);
+	if (error)
+		goto err_remove_sysfs;
+
+	return 0;
+
+err_remove_sysfs:
+	sysfs_remove_group(&intf->dev.kobj, &ims_pcu_attr_group);
+err_stop_io:
+	ims_pcu_stop_io(pcu);
+err_free_buffers:
+	ims_pcu_buffers_free(pcu);
+err_unclaim_intf:
+	usb_driver_release_interface(&ims_pcu_driver, pcu->data_intf);
+err_free_mem:
+	kfree(pcu);
+	return error;
+}
+
+static void ims_pcu_disconnect(struct usb_interface *intf)
+{
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	struct usb_host_interface *alt = intf->cur_altsetting;
+
+	usb_set_intfdata(intf, NULL);
+
+	/*
+	 * See if we are dealing with control or data interface. The cleanup
+	 * happens when we unbind primary (control) interface.
+	 */
+	if (alt->desc.bInterfaceClass != USB_CLASS_COMM)
+		return;
+
+	sysfs_remove_group(&intf->dev.kobj, &ims_pcu_attr_group);
+
+	ims_pcu_stop_io(pcu);
+
+	if (pcu->bootloader_mode)
+		ims_pcu_destroy_bootloader_mode(pcu);
+	else
+		ims_pcu_destroy_application_mode(pcu);
+
+	ims_pcu_buffers_free(pcu);
+	kfree(pcu);
+}
+
+#ifdef CONFIG_PM
+static int ims_pcu_suspend(struct usb_interface *intf,
+			   pm_message_t message)
+{
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	struct usb_host_interface *alt = intf->cur_altsetting;
+
+	if (alt->desc.bInterfaceClass == USB_CLASS_COMM)
+		ims_pcu_stop_io(pcu);
+
+	return 0;
+}
+
+static int ims_pcu_resume(struct usb_interface *intf)
+{
+	struct ims_pcu *pcu = usb_get_intfdata(intf);
+	struct usb_host_interface *alt = intf->cur_altsetting;
+	int retval = 0;
+
+	if (alt->desc.bInterfaceClass == USB_CLASS_COMM) {
+		retval = ims_pcu_start_io(pcu);
+		if (retval == 0)
+			retval = ims_pcu_line_setup(pcu);
+	}
+
+	return retval;
+}
+#endif
+
+static const struct usb_device_id ims_pcu_id_table[] = {
+	{
+		USB_DEVICE_AND_INTERFACE_INFO(0x04d8, 0x0082,
+					USB_CLASS_COMM,
+					USB_CDC_SUBCLASS_ACM,
+					USB_CDC_ACM_PROTO_AT_V25TER),
+		.driver_info = IMS_PCU_APPLICATION_MODE,
+	},
+	{
+		USB_DEVICE_AND_INTERFACE_INFO(0x04d8, 0x0083,
+					USB_CLASS_COMM,
+					USB_CDC_SUBCLASS_ACM,
+					USB_CDC_ACM_PROTO_AT_V25TER),
+		.driver_info = IMS_PCU_BOOTLOADER_MODE,
+	},
+	{ }
+};
+
+static struct usb_driver ims_pcu_driver = {
+	.name			= "ims_pcu",
+	.id_table		= ims_pcu_id_table,
+	.probe			= ims_pcu_probe,
+	.disconnect		= ims_pcu_disconnect,
+#ifdef CONFIG_PM
+	.suspend		= ims_pcu_suspend,
+	.resume			= ims_pcu_resume,
+	.reset_resume		= ims_pcu_resume,
+#endif
+};
+
+module_usb_driver(ims_pcu_driver);
+
+MODULE_DESCRIPTION("IMS Passenger Control Unit driver");
+MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c
new file mode 100644
index 0000000..1fe149f
--- /dev/null
+++ b/drivers/input/misc/ixp4xx-beeper.c
@@ -0,0 +1,177 @@
+/*
+ * Generic IXP4xx beeper driver
+ *
+ * Copyright (C) 2005 Tower Technologies
+ *
+ * based on nslu2-io.c
+ *  Copyright (C) 2004 Karen Spearel
+ *
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ * Maintainers: http://www.nslu2-linux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <mach/hardware.h>
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("ixp4xx beeper driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ixp4xx-beeper");
+
+static DEFINE_SPINLOCK(beep_lock);
+
+static void ixp4xx_spkr_control(unsigned int pin, unsigned int count)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&beep_lock, flags);
+
+	if (count) {
+		gpio_direction_output(pin, 0);
+		*IXP4XX_OSRT2 = (count & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE;
+	} else {
+		gpio_direction_output(pin, 1);
+		gpio_direction_input(pin);
+		*IXP4XX_OSRT2 = 0;
+	}
+
+	spin_unlock_irqrestore(&beep_lock, flags);
+}
+
+static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	unsigned int pin = (unsigned int) input_get_drvdata(dev);
+	unsigned int count = 0;
+
+	if (type != EV_SND)
+		return -1;
+
+	switch (code) {
+		case SND_BELL:
+			if (value)
+				value = 1000;
+		case SND_TONE:
+			break;
+		default:
+			return -1;
+	}
+
+	if (value > 20 && value < 32767)
+		count = (ixp4xx_timer_freq / (value * 4)) - 1;
+
+	ixp4xx_spkr_control(pin, count);
+
+	return 0;
+}
+
+static irqreturn_t ixp4xx_spkr_interrupt(int irq, void *dev_id)
+{
+	unsigned int pin = (unsigned int) dev_id;
+
+	/* clear interrupt */
+	*IXP4XX_OSST = IXP4XX_OSST_TIMER_2_PEND;
+
+	/* flip the beeper output */
+	gpio_set_value(pin, !gpio_get_value(pin));
+
+	return IRQ_HANDLED;
+}
+
+static int ixp4xx_spkr_probe(struct platform_device *dev)
+{
+	struct input_dev *input_dev;
+	int err;
+
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	input_set_drvdata(input_dev, (void *) dev->id);
+
+	input_dev->name = "ixp4xx beeper",
+	input_dev->phys = "ixp4xx/gpio";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor  = 0x001f;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &dev->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_SND);
+	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+	input_dev->event = ixp4xx_spkr_event;
+
+	err = gpio_request(dev->id, "ixp4-beeper");
+	if (err)
+		goto err_free_device;
+
+	err = request_irq(IRQ_IXP4XX_TIMER2, &ixp4xx_spkr_interrupt,
+			  IRQF_NO_SUSPEND, "ixp4xx-beeper",
+			  (void *) dev->id);
+	if (err)
+		goto err_free_gpio;
+
+	err = input_register_device(input_dev);
+	if (err)
+		goto err_free_irq;
+
+	platform_set_drvdata(dev, input_dev);
+
+	return 0;
+
+ err_free_irq:
+	free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id);
+ err_free_gpio:
+	gpio_free(dev->id);
+ err_free_device:
+	input_free_device(input_dev);
+
+	return err;
+}
+
+static int ixp4xx_spkr_remove(struct platform_device *dev)
+{
+	struct input_dev *input_dev = platform_get_drvdata(dev);
+	unsigned int pin = (unsigned int) input_get_drvdata(input_dev);
+
+	input_unregister_device(input_dev);
+
+	/* turn the speaker off */
+	disable_irq(IRQ_IXP4XX_TIMER2);
+	ixp4xx_spkr_control(pin, 0);
+
+	free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id);
+	gpio_free(dev->id);
+
+	return 0;
+}
+
+static void ixp4xx_spkr_shutdown(struct platform_device *dev)
+{
+	struct input_dev *input_dev = platform_get_drvdata(dev);
+	unsigned int pin = (unsigned int) input_get_drvdata(input_dev);
+
+	/* turn off the speaker */
+	disable_irq(IRQ_IXP4XX_TIMER2);
+	ixp4xx_spkr_control(pin, 0);
+}
+
+static struct platform_driver ixp4xx_spkr_platform_driver = {
+	.driver		= {
+		.name	= "ixp4xx-beeper",
+	},
+	.probe		= ixp4xx_spkr_probe,
+	.remove		= ixp4xx_spkr_remove,
+	.shutdown	= ixp4xx_spkr_shutdown,
+};
+module_platform_driver(ixp4xx_spkr_platform_driver);
+
diff --git a/drivers/input/misc/keyspan_remote.c b/drivers/input/misc/keyspan_remote.c
new file mode 100644
index 0000000..a3fe4a9
--- /dev/null
+++ b/drivers/input/misc/keyspan_remote.c
@@ -0,0 +1,595 @@
+/*
+ * keyspan_remote: USB driver for the Keyspan DMR
+ *
+ * Copyright (C) 2005 Zymeta Corporation - Michael Downey (downey@zymeta.com)
+ *
+ *	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, version 2.
+ *
+ * This driver has been put together with the support of Innosys, Inc.
+ * and Keyspan, Inc the manufacturers of the Keyspan USB DMR product.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+
+#define DRIVER_VERSION	"v0.1"
+#define DRIVER_AUTHOR	"Michael Downey <downey@zymeta.com>"
+#define DRIVER_DESC	"Driver for the USB Keyspan remote control."
+#define DRIVER_LICENSE	"GPL"
+
+/* Parameters that can be passed to the driver. */
+static int debug;
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+/* Vendor and product ids */
+#define USB_KEYSPAN_VENDOR_ID		0x06CD
+#define USB_KEYSPAN_PRODUCT_UIA11	0x0202
+
+/* Defines for converting the data from the remote. */
+#define ZERO		0x18
+#define ZERO_MASK	0x1F	/* 5 bits for a 0 */
+#define ONE		0x3C
+#define ONE_MASK	0x3F	/* 6 bits for a 1 */
+#define SYNC		0x3F80
+#define SYNC_MASK	0x3FFF	/* 14 bits for a SYNC sequence */
+#define STOP		0x00
+#define STOP_MASK	0x1F	/* 5 bits for the STOP sequence */
+#define GAP		0xFF
+
+#define RECV_SIZE	8	/* The UIA-11 type have a 8 byte limit. */
+
+/*
+ * Table that maps the 31 possible keycodes to input keys.
+ * Currently there are 15 and 17 button models so RESERVED codes
+ * are blank areas in the mapping.
+ */
+static const unsigned short keyspan_key_table[] = {
+	KEY_RESERVED,		/* 0 is just a place holder. */
+	KEY_RESERVED,
+	KEY_STOP,
+	KEY_PLAYCD,
+	KEY_RESERVED,
+	KEY_PREVIOUSSONG,
+	KEY_REWIND,
+	KEY_FORWARD,
+	KEY_NEXTSONG,
+	KEY_RESERVED,
+	KEY_RESERVED,
+	KEY_RESERVED,
+	KEY_PAUSE,
+	KEY_VOLUMEUP,
+	KEY_RESERVED,
+	KEY_RESERVED,
+	KEY_RESERVED,
+	KEY_VOLUMEDOWN,
+	KEY_RESERVED,
+	KEY_UP,
+	KEY_RESERVED,
+	KEY_MUTE,
+	KEY_LEFT,
+	KEY_ENTER,
+	KEY_RIGHT,
+	KEY_RESERVED,
+	KEY_RESERVED,
+	KEY_DOWN,
+	KEY_RESERVED,
+	KEY_KPASTERISK,
+	KEY_RESERVED,
+	KEY_MENU
+};
+
+/* table of devices that work with this driver */
+static struct usb_device_id keyspan_table[] = {
+	{ USB_DEVICE(USB_KEYSPAN_VENDOR_ID, USB_KEYSPAN_PRODUCT_UIA11) },
+	{ }					/* Terminating entry */
+};
+
+/* Structure to store all the real stuff that a remote sends to us. */
+struct keyspan_message {
+	u16	system;
+	u8	button;
+	u8	toggle;
+};
+
+/* Structure used for all the bit testing magic needed to be done. */
+struct bit_tester {
+	u32	tester;
+	int	len;
+	int	pos;
+	int	bits_left;
+	u8	buffer[32];
+};
+
+/* Structure to hold all of our driver specific stuff */
+struct usb_keyspan {
+	char				name[128];
+	char				phys[64];
+	unsigned short			keymap[ARRAY_SIZE(keyspan_key_table)];
+	struct usb_device		*udev;
+	struct input_dev		*input;
+	struct usb_interface		*interface;
+	struct usb_endpoint_descriptor	*in_endpoint;
+	struct urb*			irq_urb;
+	int				open;
+	dma_addr_t			in_dma;
+	unsigned char			*in_buffer;
+
+	/* variables used to parse messages from remote. */
+	struct bit_tester		data;
+	int				stage;
+	int				toggle;
+};
+
+static struct usb_driver keyspan_driver;
+
+/*
+ * Debug routine that prints out what we've received from the remote.
+ */
+static void keyspan_print(struct usb_keyspan* dev) /*unsigned char* data)*/
+{
+	char codes[4 * RECV_SIZE];
+	int i;
+
+	for (i = 0; i < RECV_SIZE; i++)
+		snprintf(codes + i * 3, 4, "%02x ", dev->in_buffer[i]);
+
+	dev_info(&dev->udev->dev, "%s\n", codes);
+}
+
+/*
+ * Routine that manages the bit_tester structure.  It makes sure that there are
+ * at least bits_needed bits loaded into the tester.
+ */
+static int keyspan_load_tester(struct usb_keyspan* dev, int bits_needed)
+{
+	if (dev->data.bits_left >= bits_needed)
+		return 0;
+
+	/*
+	 * Somehow we've missed the last message. The message will be repeated
+	 * though so it's not too big a deal
+	 */
+	if (dev->data.pos >= dev->data.len) {
+		dev_dbg(&dev->interface->dev,
+			"%s - Error ran out of data. pos: %d, len: %d\n",
+			__func__, dev->data.pos, dev->data.len);
+		return -1;
+	}
+
+	/* Load as much as we can into the tester. */
+	while ((dev->data.bits_left + 7 < (sizeof(dev->data.tester) * 8)) &&
+	       (dev->data.pos < dev->data.len)) {
+		dev->data.tester += (dev->data.buffer[dev->data.pos++] << dev->data.bits_left);
+		dev->data.bits_left += 8;
+	}
+
+	return 0;
+}
+
+static void keyspan_report_button(struct usb_keyspan *remote, int button, int press)
+{
+	struct input_dev *input = remote->input;
+
+	input_event(input, EV_MSC, MSC_SCAN, button);
+	input_report_key(input, remote->keymap[button], press);
+	input_sync(input);
+}
+
+/*
+ * Routine that handles all the logic needed to parse out the message from the remote.
+ */
+static void keyspan_check_data(struct usb_keyspan *remote)
+{
+	int i;
+	int found = 0;
+	struct keyspan_message message;
+
+	switch(remote->stage) {
+	case 0:
+		/*
+		 * In stage 0 we want to find the start of a message.  The remote sends a 0xFF as filler.
+		 * So the first byte that isn't a FF should be the start of a new message.
+		 */
+		for (i = 0; i < RECV_SIZE && remote->in_buffer[i] == GAP; ++i);
+
+		if (i < RECV_SIZE) {
+			memcpy(remote->data.buffer, remote->in_buffer, RECV_SIZE);
+			remote->data.len = RECV_SIZE;
+			remote->data.pos = 0;
+			remote->data.tester = 0;
+			remote->data.bits_left = 0;
+			remote->stage = 1;
+		}
+		break;
+
+	case 1:
+		/*
+		 * Stage 1 we should have 16 bytes and should be able to detect a
+		 * SYNC.  The SYNC is 14 bits, 7 0's and then 7 1's.
+		 */
+		memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
+		remote->data.len += RECV_SIZE;
+
+		found = 0;
+		while ((remote->data.bits_left >= 14 || remote->data.pos < remote->data.len) && !found) {
+			for (i = 0; i < 8; ++i) {
+				if (keyspan_load_tester(remote, 14) != 0) {
+					remote->stage = 0;
+					return;
+				}
+
+				if ((remote->data.tester & SYNC_MASK) == SYNC) {
+					remote->data.tester = remote->data.tester >> 14;
+					remote->data.bits_left -= 14;
+					found = 1;
+					break;
+				} else {
+					remote->data.tester = remote->data.tester >> 1;
+					--remote->data.bits_left;
+				}
+			}
+		}
+
+		if (!found) {
+			remote->stage = 0;
+			remote->data.len = 0;
+		} else {
+			remote->stage = 2;
+		}
+		break;
+
+	case 2:
+		/*
+		 * Stage 2 we should have 24 bytes which will be enough for a full
+		 * message.  We need to parse out the system code, button code,
+		 * toggle code, and stop.
+		 */
+		memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
+		remote->data.len += RECV_SIZE;
+
+		message.system = 0;
+		for (i = 0; i < 9; i++) {
+			keyspan_load_tester(remote, 6);
+
+			if ((remote->data.tester & ZERO_MASK) == ZERO) {
+				message.system = message.system << 1;
+				remote->data.tester = remote->data.tester >> 5;
+				remote->data.bits_left -= 5;
+			} else if ((remote->data.tester & ONE_MASK) == ONE) {
+				message.system = (message.system << 1) + 1;
+				remote->data.tester = remote->data.tester >> 6;
+				remote->data.bits_left -= 6;
+			} else {
+				dev_err(&remote->interface->dev,
+					"%s - Unknown sequence found in system data.\n",
+					__func__);
+				remote->stage = 0;
+				return;
+			}
+		}
+
+		message.button = 0;
+		for (i = 0; i < 5; i++) {
+			keyspan_load_tester(remote, 6);
+
+			if ((remote->data.tester & ZERO_MASK) == ZERO) {
+				message.button = message.button << 1;
+				remote->data.tester = remote->data.tester >> 5;
+				remote->data.bits_left -= 5;
+			} else if ((remote->data.tester & ONE_MASK) == ONE) {
+				message.button = (message.button << 1) + 1;
+				remote->data.tester = remote->data.tester >> 6;
+				remote->data.bits_left -= 6;
+			} else {
+				dev_err(&remote->interface->dev,
+					"%s - Unknown sequence found in button data.\n",
+					__func__);
+				remote->stage = 0;
+				return;
+			}
+		}
+
+		keyspan_load_tester(remote, 6);
+		if ((remote->data.tester & ZERO_MASK) == ZERO) {
+			message.toggle = 0;
+			remote->data.tester = remote->data.tester >> 5;
+			remote->data.bits_left -= 5;
+		} else if ((remote->data.tester & ONE_MASK) == ONE) {
+			message.toggle = 1;
+			remote->data.tester = remote->data.tester >> 6;
+			remote->data.bits_left -= 6;
+		} else {
+			dev_err(&remote->interface->dev,
+				"%s - Error in message, invalid toggle.\n",
+				__func__);
+			remote->stage = 0;
+			return;
+		}
+
+		keyspan_load_tester(remote, 5);
+		if ((remote->data.tester & STOP_MASK) == STOP) {
+			remote->data.tester = remote->data.tester >> 5;
+			remote->data.bits_left -= 5;
+		} else {
+			dev_err(&remote->interface->dev,
+				"Bad message received, no stop bit found.\n");
+		}
+
+		dev_dbg(&remote->interface->dev,
+			"%s found valid message: system: %d, button: %d, toggle: %d\n",
+			__func__, message.system, message.button, message.toggle);
+
+		if (message.toggle != remote->toggle) {
+			keyspan_report_button(remote, message.button, 1);
+			keyspan_report_button(remote, message.button, 0);
+			remote->toggle = message.toggle;
+		}
+
+		remote->stage = 0;
+		break;
+	}
+}
+
+/*
+ * Routine for sending all the initialization messages to the remote.
+ */
+static int keyspan_setup(struct usb_device* dev)
+{
+	int retval = 0;
+
+	retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+				 0x11, 0x40, 0x5601, 0x0, NULL, 0, 0);
+	if (retval) {
+		dev_dbg(&dev->dev, "%s - failed to set bit rate due to error: %d\n",
+			__func__, retval);
+		return(retval);
+	}
+
+	retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+				 0x44, 0x40, 0x0, 0x0, NULL, 0, 0);
+	if (retval) {
+		dev_dbg(&dev->dev, "%s - failed to set resume sensitivity due to error: %d\n",
+			__func__, retval);
+		return(retval);
+	}
+
+	retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+				 0x22, 0x40, 0x0, 0x0, NULL, 0, 0);
+	if (retval) {
+		dev_dbg(&dev->dev, "%s - failed to turn receive on due to error: %d\n",
+			__func__, retval);
+		return(retval);
+	}
+
+	dev_dbg(&dev->dev, "%s - Setup complete.\n", __func__);
+	return(retval);
+}
+
+/*
+ * Routine used to handle a new message that has come in.
+ */
+static void keyspan_irq_recv(struct urb *urb)
+{
+	struct usb_keyspan *dev = urb->context;
+	int retval;
+
+	/* Check our status in case we need to bail out early. */
+	switch (urb->status) {
+	case 0:
+		break;
+
+	/* Device went away so don't keep trying to read from it. */
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+
+	default:
+		goto resubmit;
+	}
+
+	if (debug)
+		keyspan_print(dev);
+
+	keyspan_check_data(dev);
+
+resubmit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&dev->interface->dev,
+			"%s - usb_submit_urb failed with result: %d\n",
+			__func__, retval);
+}
+
+static int keyspan_open(struct input_dev *dev)
+{
+	struct usb_keyspan *remote = input_get_drvdata(dev);
+
+	remote->irq_urb->dev = remote->udev;
+	if (usb_submit_urb(remote->irq_urb, GFP_KERNEL))
+		return -EIO;
+
+	return 0;
+}
+
+static void keyspan_close(struct input_dev *dev)
+{
+	struct usb_keyspan *remote = input_get_drvdata(dev);
+
+	usb_kill_urb(remote->irq_urb);
+}
+
+static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_interface *iface)
+{
+
+	struct usb_endpoint_descriptor *endpoint;
+	int i;
+
+	for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
+		endpoint = &iface->endpoint[i].desc;
+
+		if (usb_endpoint_is_int_in(endpoint)) {
+			/* we found our interrupt in endpoint */
+			return endpoint;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Routine that sets up the driver to handle a specific USB device detected on the bus.
+ */
+static int keyspan_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_keyspan *remote;
+	struct input_dev *input_dev;
+	int i, error;
+
+	endpoint = keyspan_get_in_endpoint(interface->cur_altsetting);
+	if (!endpoint)
+		return -ENODEV;
+
+	remote = kzalloc(sizeof(*remote), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!remote || !input_dev) {
+		error = -ENOMEM;
+		goto fail1;
+	}
+
+	remote->udev = udev;
+	remote->input = input_dev;
+	remote->interface = interface;
+	remote->in_endpoint = endpoint;
+	remote->toggle = -1;	/* Set to -1 so we will always not match the toggle from the first remote message. */
+
+	remote->in_buffer = usb_alloc_coherent(udev, RECV_SIZE, GFP_ATOMIC, &remote->in_dma);
+	if (!remote->in_buffer) {
+		error = -ENOMEM;
+		goto fail1;
+	}
+
+	remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!remote->irq_urb) {
+		error = -ENOMEM;
+		goto fail2;
+	}
+
+	error = keyspan_setup(udev);
+	if (error) {
+		error = -ENODEV;
+		goto fail3;
+	}
+
+	if (udev->manufacturer)
+		strlcpy(remote->name, udev->manufacturer, sizeof(remote->name));
+
+	if (udev->product) {
+		if (udev->manufacturer)
+			strlcat(remote->name, " ", sizeof(remote->name));
+		strlcat(remote->name, udev->product, sizeof(remote->name));
+	}
+
+	if (!strlen(remote->name))
+		snprintf(remote->name, sizeof(remote->name),
+			 "USB Keyspan Remote %04x:%04x",
+			 le16_to_cpu(udev->descriptor.idVendor),
+			 le16_to_cpu(udev->descriptor.idProduct));
+
+	usb_make_path(udev, remote->phys, sizeof(remote->phys));
+	strlcat(remote->phys, "/input0", sizeof(remote->phys));
+	memcpy(remote->keymap, keyspan_key_table, sizeof(remote->keymap));
+
+	input_dev->name = remote->name;
+	input_dev->phys = remote->phys;
+	usb_to_input_id(udev, &input_dev->id);
+	input_dev->dev.parent = &interface->dev;
+	input_dev->keycode = remote->keymap;
+	input_dev->keycodesize = sizeof(unsigned short);
+	input_dev->keycodemax = ARRAY_SIZE(remote->keymap);
+
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+	__set_bit(EV_KEY, input_dev->evbit);
+	for (i = 0; i < ARRAY_SIZE(keyspan_key_table); i++)
+		__set_bit(keyspan_key_table[i], input_dev->keybit);
+	__clear_bit(KEY_RESERVED, input_dev->keybit);
+
+	input_set_drvdata(input_dev, remote);
+
+	input_dev->open = keyspan_open;
+	input_dev->close = keyspan_close;
+
+	/*
+	 * Initialize the URB to access the device.
+	 * The urb gets sent to the device in keyspan_open()
+	 */
+	usb_fill_int_urb(remote->irq_urb,
+			 remote->udev,
+			 usb_rcvintpipe(remote->udev, endpoint->bEndpointAddress),
+			 remote->in_buffer, RECV_SIZE, keyspan_irq_recv, remote,
+			 endpoint->bInterval);
+	remote->irq_urb->transfer_dma = remote->in_dma;
+	remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* we can register the device now, as it is ready */
+	error = input_register_device(remote->input);
+	if (error)
+		goto fail3;
+
+	/* save our data pointer in this interface device */
+	usb_set_intfdata(interface, remote);
+
+	return 0;
+
+ fail3:	usb_free_urb(remote->irq_urb);
+ fail2:	usb_free_coherent(udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
+ fail1:	kfree(remote);
+	input_free_device(input_dev);
+
+	return error;
+}
+
+/*
+ * Routine called when a device is disconnected from the USB.
+ */
+static void keyspan_disconnect(struct usb_interface *interface)
+{
+	struct usb_keyspan *remote;
+
+	remote = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+
+	if (remote) {	/* We have a valid driver structure so clean up everything we allocated. */
+		input_unregister_device(remote->input);
+		usb_kill_urb(remote->irq_urb);
+		usb_free_urb(remote->irq_urb);
+		usb_free_coherent(remote->udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
+		kfree(remote);
+	}
+}
+
+/*
+ * Standard driver set up sections
+ */
+static struct usb_driver keyspan_driver =
+{
+	.name =		"keyspan_remote",
+	.probe =	keyspan_probe,
+	.disconnect =	keyspan_disconnect,
+	.id_table =	keyspan_table
+};
+
+module_usb_driver(keyspan_driver);
+
+MODULE_DEVICE_TABLE(usb, keyspan_table);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
new file mode 100644
index 0000000..efaffcc
--- /dev/null
+++ b/drivers/input/misc/kxtj9.c
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2011 Kionix, Inc.
+ * Written by Chris Hudson <chudson@kionix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input/kxtj9.h>
+#include <linux/input-polldev.h>
+
+#define NAME			"kxtj9"
+#define G_MAX			8000
+/* OUTPUT REGISTERS */
+#define XOUT_L			0x06
+#define WHO_AM_I		0x0F
+/* CONTROL REGISTERS */
+#define INT_REL			0x1A
+#define CTRL_REG1		0x1B
+#define INT_CTRL1		0x1E
+#define DATA_CTRL		0x21
+/* CONTROL REGISTER 1 BITS */
+#define PC1_OFF			0x7F
+#define PC1_ON			(1 << 7)
+/* Data ready funtion enable bit: set during probe if using irq mode */
+#define DRDYE			(1 << 5)
+/* DATA CONTROL REGISTER BITS */
+#define ODR12_5F		0
+#define ODR25F			1
+#define ODR50F			2
+#define ODR100F		3
+#define ODR200F		4
+#define ODR400F		5
+#define ODR800F		6
+/* INTERRUPT CONTROL REGISTER 1 BITS */
+/* Set these during probe if using irq mode */
+#define KXTJ9_IEL		(1 << 3)
+#define KXTJ9_IEA		(1 << 4)
+#define KXTJ9_IEN		(1 << 5)
+/* INPUT_ABS CONSTANTS */
+#define FUZZ			3
+#define FLAT			3
+/* RESUME STATE INDICES */
+#define RES_DATA_CTRL		0
+#define RES_CTRL_REG1		1
+#define RES_INT_CTRL1		2
+#define RESUME_ENTRIES		3
+
+/*
+ * The following table lists the maximum appropriate poll interval for each
+ * available output data rate.
+ */
+static const struct {
+	unsigned int cutoff;
+	u8 mask;
+} kxtj9_odr_table[] = {
+	{ 3,	ODR800F },
+	{ 5,	ODR400F },
+	{ 10,	ODR200F },
+	{ 20,	ODR100F },
+	{ 40,	ODR50F  },
+	{ 80,	ODR25F  },
+	{ 0,	ODR12_5F},
+};
+
+struct kxtj9_data {
+	struct i2c_client *client;
+	struct kxtj9_platform_data pdata;
+	struct input_dev *input_dev;
+#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
+	struct input_polled_dev *poll_dev;
+#endif
+	unsigned int last_poll_interval;
+	u8 shift;
+	u8 ctrl_reg1;
+	u8 data_ctrl;
+	u8 int_ctrl;
+};
+
+static int kxtj9_i2c_read(struct kxtj9_data *tj9, u8 addr, u8 *data, int len)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr = tj9->client->addr,
+			.flags = tj9->client->flags,
+			.len = 1,
+			.buf = &addr,
+		},
+		{
+			.addr = tj9->client->addr,
+			.flags = tj9->client->flags | I2C_M_RD,
+			.len = len,
+			.buf = data,
+		},
+	};
+
+	return i2c_transfer(tj9->client->adapter, msgs, 2);
+}
+
+static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9)
+{
+	s16 acc_data[3]; /* Data bytes from hardware xL, xH, yL, yH, zL, zH */
+	s16 x, y, z;
+	int err;
+
+	err = kxtj9_i2c_read(tj9, XOUT_L, (u8 *)acc_data, 6);
+	if (err < 0)
+		dev_err(&tj9->client->dev, "accelerometer data read failed\n");
+
+	x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]);
+	y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]);
+	z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]);
+
+	x >>= tj9->shift;
+	y >>= tj9->shift;
+	z >>= tj9->shift;
+
+	input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x);
+	input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y);
+	input_report_abs(tj9->input_dev, ABS_Z, tj9->pdata.negate_z ? -z : z);
+	input_sync(tj9->input_dev);
+}
+
+static irqreturn_t kxtj9_isr(int irq, void *dev)
+{
+	struct kxtj9_data *tj9 = dev;
+	int err;
+
+	/* data ready is the only possible interrupt type */
+	kxtj9_report_acceleration_data(tj9);
+
+	err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
+	if (err < 0)
+		dev_err(&tj9->client->dev,
+			"error clearing interrupt status: %d\n", err);
+
+	return IRQ_HANDLED;
+}
+
+static int kxtj9_update_g_range(struct kxtj9_data *tj9, u8 new_g_range)
+{
+	switch (new_g_range) {
+	case KXTJ9_G_2G:
+		tj9->shift = 4;
+		break;
+	case KXTJ9_G_4G:
+		tj9->shift = 3;
+		break;
+	case KXTJ9_G_8G:
+		tj9->shift = 2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	tj9->ctrl_reg1 &= 0xe7;
+	tj9->ctrl_reg1 |= new_g_range;
+
+	return 0;
+}
+
+static int kxtj9_update_odr(struct kxtj9_data *tj9, unsigned int poll_interval)
+{
+	int err;
+	int i;
+
+	/* Use the lowest ODR that can support the requested poll interval */
+	for (i = 0; i < ARRAY_SIZE(kxtj9_odr_table); i++) {
+		tj9->data_ctrl = kxtj9_odr_table[i].mask;
+		if (poll_interval < kxtj9_odr_table[i].cutoff)
+			break;
+	}
+
+	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
+	if (err < 0)
+		return err;
+
+	err = i2c_smbus_write_byte_data(tj9->client, DATA_CTRL, tj9->data_ctrl);
+	if (err < 0)
+		return err;
+
+	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int kxtj9_device_power_on(struct kxtj9_data *tj9)
+{
+	if (tj9->pdata.power_on)
+		return tj9->pdata.power_on();
+
+	return 0;
+}
+
+static void kxtj9_device_power_off(struct kxtj9_data *tj9)
+{
+	int err;
+
+	tj9->ctrl_reg1 &= PC1_OFF;
+	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+	if (err < 0)
+		dev_err(&tj9->client->dev, "soft power off failed\n");
+
+	if (tj9->pdata.power_off)
+		tj9->pdata.power_off();
+}
+
+static int kxtj9_enable(struct kxtj9_data *tj9)
+{
+	int err;
+
+	err = kxtj9_device_power_on(tj9);
+	if (err < 0)
+		return err;
+
+	/* ensure that PC1 is cleared before updating control registers */
+	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
+	if (err < 0)
+		return err;
+
+	/* only write INT_CTRL_REG1 if in irq mode */
+	if (tj9->client->irq) {
+		err = i2c_smbus_write_byte_data(tj9->client,
+						INT_CTRL1, tj9->int_ctrl);
+		if (err < 0)
+			return err;
+	}
+
+	err = kxtj9_update_g_range(tj9, tj9->pdata.g_range);
+	if (err < 0)
+		return err;
+
+	/* turn on outputs */
+	tj9->ctrl_reg1 |= PC1_ON;
+	err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+	if (err < 0)
+		return err;
+
+	err = kxtj9_update_odr(tj9, tj9->last_poll_interval);
+	if (err < 0)
+		return err;
+
+	/* clear initial interrupt if in irq mode */
+	if (tj9->client->irq) {
+		err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
+		if (err < 0) {
+			dev_err(&tj9->client->dev,
+				"error clearing interrupt: %d\n", err);
+			goto fail;
+		}
+	}
+
+	return 0;
+
+fail:
+	kxtj9_device_power_off(tj9);
+	return err;
+}
+
+static void kxtj9_disable(struct kxtj9_data *tj9)
+{
+	kxtj9_device_power_off(tj9);
+}
+
+static int kxtj9_input_open(struct input_dev *input)
+{
+	struct kxtj9_data *tj9 = input_get_drvdata(input);
+
+	return kxtj9_enable(tj9);
+}
+
+static void kxtj9_input_close(struct input_dev *dev)
+{
+	struct kxtj9_data *tj9 = input_get_drvdata(dev);
+
+	kxtj9_disable(tj9);
+}
+
+static void kxtj9_init_input_device(struct kxtj9_data *tj9,
+					      struct input_dev *input_dev)
+{
+	__set_bit(EV_ABS, input_dev->evbit);
+	input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT);
+	input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
+	input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
+
+	input_dev->name = "kxtj9_accel";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &tj9->client->dev;
+}
+
+static int kxtj9_setup_input_device(struct kxtj9_data *tj9)
+{
+	struct input_dev *input_dev;
+	int err;
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&tj9->client->dev, "input device allocate failed\n");
+		return -ENOMEM;
+	}
+
+	tj9->input_dev = input_dev;
+
+	input_dev->open = kxtj9_input_open;
+	input_dev->close = kxtj9_input_close;
+	input_set_drvdata(input_dev, tj9);
+
+	kxtj9_init_input_device(tj9, input_dev);
+
+	err = input_register_device(tj9->input_dev);
+	if (err) {
+		dev_err(&tj9->client->dev,
+			"unable to register input polled device %s: %d\n",
+			tj9->input_dev->name, err);
+		input_free_device(tj9->input_dev);
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * When IRQ mode is selected, we need to provide an interface to allow the user
+ * to change the output data rate of the part.  For consistency, we are using
+ * the set_poll method, which accepts a poll interval in milliseconds, and then
+ * calls update_odr() while passing this value as an argument.  In IRQ mode, the
+ * data outputs will not be read AT the requested poll interval, rather, the
+ * lowest ODR that can support the requested interval.  The client application
+ * will be responsible for retrieving data from the input node at the desired
+ * interval.
+ */
+
+/* Returns currently selected poll interval (in ms) */
+static ssize_t kxtj9_get_poll(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%d\n", tj9->last_poll_interval);
+}
+
+/* Allow users to select a new poll interval (in ms) */
+static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
+						const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+	struct input_dev *input_dev = tj9->input_dev;
+	unsigned int interval;
+	int error;
+
+	error = kstrtouint(buf, 10, &interval);
+	if (error < 0)
+		return error;
+
+	/* Lock the device to prevent races with open/close (and itself) */
+	mutex_lock(&input_dev->mutex);
+
+	disable_irq(client->irq);
+
+	/*
+	 * Set current interval to the greater of the minimum interval or
+	 * the requested interval
+	 */
+	tj9->last_poll_interval = max(interval, tj9->pdata.min_interval);
+
+	kxtj9_update_odr(tj9, tj9->last_poll_interval);
+
+	enable_irq(client->irq);
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
+
+static struct attribute *kxtj9_attributes[] = {
+	&dev_attr_poll.attr,
+	NULL
+};
+
+static struct attribute_group kxtj9_attribute_group = {
+	.attrs = kxtj9_attributes
+};
+
+
+#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
+static void kxtj9_poll(struct input_polled_dev *dev)
+{
+	struct kxtj9_data *tj9 = dev->private;
+	unsigned int poll_interval = dev->poll_interval;
+
+	kxtj9_report_acceleration_data(tj9);
+
+	if (poll_interval != tj9->last_poll_interval) {
+		kxtj9_update_odr(tj9, poll_interval);
+		tj9->last_poll_interval = poll_interval;
+	}
+}
+
+static void kxtj9_polled_input_open(struct input_polled_dev *dev)
+{
+	struct kxtj9_data *tj9 = dev->private;
+
+	kxtj9_enable(tj9);
+}
+
+static void kxtj9_polled_input_close(struct input_polled_dev *dev)
+{
+	struct kxtj9_data *tj9 = dev->private;
+
+	kxtj9_disable(tj9);
+}
+
+static int kxtj9_setup_polled_device(struct kxtj9_data *tj9)
+{
+	int err;
+	struct input_polled_dev *poll_dev;
+	poll_dev = input_allocate_polled_device();
+
+	if (!poll_dev) {
+		dev_err(&tj9->client->dev,
+			"Failed to allocate polled device\n");
+		return -ENOMEM;
+	}
+
+	tj9->poll_dev = poll_dev;
+	tj9->input_dev = poll_dev->input;
+
+	poll_dev->private = tj9;
+	poll_dev->poll = kxtj9_poll;
+	poll_dev->open = kxtj9_polled_input_open;
+	poll_dev->close = kxtj9_polled_input_close;
+
+	kxtj9_init_input_device(tj9, poll_dev->input);
+
+	err = input_register_polled_device(poll_dev);
+	if (err) {
+		dev_err(&tj9->client->dev,
+			"Unable to register polled device, err=%d\n", err);
+		input_free_polled_device(poll_dev);
+		return err;
+	}
+
+	return 0;
+}
+
+static void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
+{
+	input_unregister_polled_device(tj9->poll_dev);
+	input_free_polled_device(tj9->poll_dev);
+}
+
+#else
+
+static inline int kxtj9_setup_polled_device(struct kxtj9_data *tj9)
+{
+	return -ENOSYS;
+}
+
+static inline void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
+{
+}
+
+#endif
+
+static int kxtj9_verify(struct kxtj9_data *tj9)
+{
+	int retval;
+
+	retval = kxtj9_device_power_on(tj9);
+	if (retval < 0)
+		return retval;
+
+	retval = i2c_smbus_read_byte_data(tj9->client, WHO_AM_I);
+	if (retval < 0) {
+		dev_err(&tj9->client->dev, "read err int source\n");
+		goto out;
+	}
+
+	retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0;
+
+out:
+	kxtj9_device_power_off(tj9);
+	return retval;
+}
+
+static int kxtj9_probe(struct i2c_client *client,
+				 const struct i2c_device_id *id)
+{
+	const struct kxtj9_platform_data *pdata =
+			dev_get_platdata(&client->dev);
+	struct kxtj9_data *tj9;
+	int err;
+
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&client->dev, "client is not i2c capable\n");
+		return -ENXIO;
+	}
+
+	if (!pdata) {
+		dev_err(&client->dev, "platform data is NULL; exiting\n");
+		return -EINVAL;
+	}
+
+	tj9 = kzalloc(sizeof(*tj9), GFP_KERNEL);
+	if (!tj9) {
+		dev_err(&client->dev,
+			"failed to allocate memory for module data\n");
+		return -ENOMEM;
+	}
+
+	tj9->client = client;
+	tj9->pdata = *pdata;
+
+	if (pdata->init) {
+		err = pdata->init();
+		if (err < 0)
+			goto err_free_mem;
+	}
+
+	err = kxtj9_verify(tj9);
+	if (err < 0) {
+		dev_err(&client->dev, "device not recognized\n");
+		goto err_pdata_exit;
+	}
+
+	i2c_set_clientdata(client, tj9);
+
+	tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range;
+	tj9->last_poll_interval = tj9->pdata.init_interval;
+
+	if (client->irq) {
+		/* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
+		tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
+		tj9->ctrl_reg1 |= DRDYE;
+
+		err = kxtj9_setup_input_device(tj9);
+		if (err)
+			goto err_pdata_exit;
+
+		err = request_threaded_irq(client->irq, NULL, kxtj9_isr,
+					   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					   "kxtj9-irq", tj9);
+		if (err) {
+			dev_err(&client->dev, "request irq failed: %d\n", err);
+			goto err_destroy_input;
+		}
+
+		err = sysfs_create_group(&client->dev.kobj, &kxtj9_attribute_group);
+		if (err) {
+			dev_err(&client->dev, "sysfs create failed: %d\n", err);
+			goto err_free_irq;
+		}
+
+	} else {
+		err = kxtj9_setup_polled_device(tj9);
+		if (err)
+			goto err_pdata_exit;
+	}
+
+	return 0;
+
+err_free_irq:
+	free_irq(client->irq, tj9);
+err_destroy_input:
+	input_unregister_device(tj9->input_dev);
+err_pdata_exit:
+	if (tj9->pdata.exit)
+		tj9->pdata.exit();
+err_free_mem:
+	kfree(tj9);
+	return err;
+}
+
+static int kxtj9_remove(struct i2c_client *client)
+{
+	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+
+	if (client->irq) {
+		sysfs_remove_group(&client->dev.kobj, &kxtj9_attribute_group);
+		free_irq(client->irq, tj9);
+		input_unregister_device(tj9->input_dev);
+	} else {
+		kxtj9_teardown_polled_device(tj9);
+	}
+
+	if (tj9->pdata.exit)
+		tj9->pdata.exit();
+
+	kfree(tj9);
+
+	return 0;
+}
+
+static int __maybe_unused kxtj9_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+	struct input_dev *input_dev = tj9->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users)
+		kxtj9_disable(tj9);
+
+	mutex_unlock(&input_dev->mutex);
+	return 0;
+}
+
+static int __maybe_unused kxtj9_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+	struct input_dev *input_dev = tj9->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users)
+		kxtj9_enable(tj9);
+
+	mutex_unlock(&input_dev->mutex);
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
+
+static const struct i2c_device_id kxtj9_id[] = {
+	{ NAME, 0 },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(i2c, kxtj9_id);
+
+static struct i2c_driver kxtj9_driver = {
+	.driver = {
+		.name	= NAME,
+		.pm	= &kxtj9_pm_ops,
+	},
+	.probe		= kxtj9_probe,
+	.remove		= kxtj9_remove,
+	.id_table	= kxtj9_id,
+};
+
+module_i2c_driver(kxtj9_driver);
+
+MODULE_DESCRIPTION("KXTJ9 accelerometer driver");
+MODULE_AUTHOR("Chris Hudson <chudson@kionix.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/m68kspkr.c b/drivers/input/misc/m68kspkr.c
new file mode 100644
index 0000000..312d636
--- /dev/null
+++ b/drivers/input/misc/m68kspkr.c
@@ -0,0 +1,149 @@
+/*
+ *  m68k beeper driver for Linux
+ *
+ *  Copyright (c) 2002 Richard Zidlicky
+ *  Copyright (c) 2002 Vojtech Pavlik
+ *  Copyright (c) 1992 Orest Zborowski
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Richard Zidlicky <rz@linux-m68k.org>");
+MODULE_DESCRIPTION("m68k beeper driver");
+MODULE_LICENSE("GPL");
+
+static struct platform_device *m68kspkr_platform_device;
+
+static int m68kspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	unsigned int count = 0;
+
+	if (type != EV_SND)
+		return -1;
+
+	switch (code) {
+		case SND_BELL: if (value) value = 1000;
+		case SND_TONE: break;
+		default: return -1;
+	}
+
+	if (value > 20 && value < 32767)
+		count = 1193182 / value;
+
+	mach_beep(count, -1);
+
+	return 0;
+}
+
+static int m68kspkr_probe(struct platform_device *dev)
+{
+	struct input_dev *input_dev;
+	int err;
+
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	input_dev->name = "m68k beeper";
+	input_dev->phys = "m68k/generic";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor  = 0x001f;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &dev->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_SND);
+	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+	input_dev->event = m68kspkr_event;
+
+	err = input_register_device(input_dev);
+	if (err) {
+		input_free_device(input_dev);
+		return err;
+	}
+
+	platform_set_drvdata(dev, input_dev);
+
+	return 0;
+}
+
+static int m68kspkr_remove(struct platform_device *dev)
+{
+	struct input_dev *input_dev = platform_get_drvdata(dev);
+
+	input_unregister_device(input_dev);
+	/* turn off the speaker */
+	m68kspkr_event(NULL, EV_SND, SND_BELL, 0);
+
+	return 0;
+}
+
+static void m68kspkr_shutdown(struct platform_device *dev)
+{
+	/* turn off the speaker */
+	m68kspkr_event(NULL, EV_SND, SND_BELL, 0);
+}
+
+static struct platform_driver m68kspkr_platform_driver = {
+	.driver		= {
+		.name	= "m68kspkr",
+	},
+	.probe		= m68kspkr_probe,
+	.remove		= m68kspkr_remove,
+	.shutdown	= m68kspkr_shutdown,
+};
+
+static int __init m68kspkr_init(void)
+{
+	int err;
+
+	if (!mach_beep) {
+		printk(KERN_INFO "m68kspkr: no lowlevel beep support\n");
+		return -ENODEV;
+        }
+
+	err = platform_driver_register(&m68kspkr_platform_driver);
+	if (err)
+		return err;
+
+	m68kspkr_platform_device = platform_device_alloc("m68kspkr", -1);
+	if (!m68kspkr_platform_device) {
+		err = -ENOMEM;
+		goto err_unregister_driver;
+	}
+
+	err = platform_device_add(m68kspkr_platform_device);
+	if (err)
+		goto err_free_device;
+
+	return 0;
+
+ err_free_device:
+	platform_device_put(m68kspkr_platform_device);
+ err_unregister_driver:
+	platform_driver_unregister(&m68kspkr_platform_driver);
+
+	return err;
+}
+
+static void __exit m68kspkr_exit(void)
+{
+	platform_device_unregister(m68kspkr_platform_device);
+	platform_driver_unregister(&m68kspkr_platform_driver);
+}
+
+module_init(m68kspkr_init);
+module_exit(m68kspkr_exit);
diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c
new file mode 100644
index 0000000..6d96bff
--- /dev/null
+++ b/drivers/input/misc/max77693-haptic.c
@@ -0,0 +1,421 @@
+/*
+ * MAXIM MAX77693/MAX77843 Haptic device driver
+ *
+ * Copyright (C) 2014,2015 Samsung Electronics
+ * Jaewon Kim <jaewon02.kim@samsung.com>
+ * Krzysztof Kozlowski <k.kozlowski@samsung.com>
+ *
+ * This program is not provided / owned by Maxim Integrated Products.
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-common.h>
+#include <linux/mfd/max77693-private.h>
+#include <linux/mfd/max77843-private.h>
+
+#define MAX_MAGNITUDE_SHIFT	16
+
+enum max77693_haptic_motor_type {
+	MAX77693_HAPTIC_ERM = 0,
+	MAX77693_HAPTIC_LRA,
+};
+
+enum max77693_haptic_pulse_mode {
+	MAX77693_HAPTIC_EXTERNAL_MODE = 0,
+	MAX77693_HAPTIC_INTERNAL_MODE,
+};
+
+enum max77693_haptic_pwm_divisor {
+	MAX77693_HAPTIC_PWM_DIVISOR_32 = 0,
+	MAX77693_HAPTIC_PWM_DIVISOR_64,
+	MAX77693_HAPTIC_PWM_DIVISOR_128,
+	MAX77693_HAPTIC_PWM_DIVISOR_256,
+};
+
+struct max77693_haptic {
+	enum max77693_types dev_type;
+
+	struct regmap *regmap_pmic;
+	struct regmap *regmap_haptic;
+	struct device *dev;
+	struct input_dev *input_dev;
+	struct pwm_device *pwm_dev;
+	struct regulator *motor_reg;
+
+	bool enabled;
+	bool suspend_state;
+	unsigned int magnitude;
+	unsigned int pwm_duty;
+	enum max77693_haptic_motor_type type;
+	enum max77693_haptic_pulse_mode mode;
+
+	struct work_struct work;
+};
+
+static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic)
+{
+	int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2;
+	int error;
+
+	error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period);
+	if (error) {
+		dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int max77843_haptic_bias(struct max77693_haptic *haptic, bool on)
+{
+	int error;
+
+	if (haptic->dev_type != TYPE_MAX77843)
+		return 0;
+
+	error = regmap_update_bits(haptic->regmap_haptic,
+				   MAX77843_SYS_REG_MAINCTRL1,
+				   MAX77843_MAINCTRL1_BIASEN_MASK,
+				   on << MAINCTRL1_BIASEN_SHIFT);
+	if (error) {
+		dev_err(haptic->dev, "failed to %s bias: %d\n",
+			on ? "enable" : "disable", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int max77693_haptic_configure(struct max77693_haptic *haptic,
+				     bool enable)
+{
+	unsigned int value, config_reg;
+	int error;
+
+	switch (haptic->dev_type) {
+	case TYPE_MAX77693:
+		value = ((haptic->type << MAX77693_CONFIG2_MODE) |
+			(enable << MAX77693_CONFIG2_MEN) |
+			(haptic->mode << MAX77693_CONFIG2_HTYP) |
+			MAX77693_HAPTIC_PWM_DIVISOR_128);
+		config_reg = MAX77693_HAPTIC_REG_CONFIG2;
+		break;
+	case TYPE_MAX77843:
+		value = (haptic->type << MCONFIG_MODE_SHIFT) |
+			(enable << MCONFIG_MEN_SHIFT) |
+			MAX77693_HAPTIC_PWM_DIVISOR_128;
+		config_reg = MAX77843_HAP_REG_MCONFIG;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	error = regmap_write(haptic->regmap_haptic,
+			     config_reg, value);
+	if (error) {
+		dev_err(haptic->dev,
+			"failed to update haptic config: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int max77693_haptic_lowsys(struct max77693_haptic *haptic, bool enable)
+{
+	int error;
+
+	if (haptic->dev_type != TYPE_MAX77693)
+		return 0;
+
+	error = regmap_update_bits(haptic->regmap_pmic,
+				   MAX77693_PMIC_REG_LSCNFG,
+				   MAX77693_PMIC_LOW_SYS_MASK,
+				   enable << MAX77693_PMIC_LOW_SYS_SHIFT);
+	if (error) {
+		dev_err(haptic->dev, "cannot update pmic regmap: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static void max77693_haptic_enable(struct max77693_haptic *haptic)
+{
+	int error;
+
+	if (haptic->enabled)
+		return;
+
+	error = pwm_enable(haptic->pwm_dev);
+	if (error) {
+		dev_err(haptic->dev,
+			"failed to enable haptic pwm device: %d\n", error);
+		return;
+	}
+
+	error = max77693_haptic_lowsys(haptic, true);
+	if (error)
+		goto err_enable_lowsys;
+
+	error = max77693_haptic_configure(haptic, true);
+	if (error)
+		goto err_enable_config;
+
+	haptic->enabled = true;
+
+	return;
+
+err_enable_config:
+	max77693_haptic_lowsys(haptic, false);
+err_enable_lowsys:
+	pwm_disable(haptic->pwm_dev);
+}
+
+static void max77693_haptic_disable(struct max77693_haptic *haptic)
+{
+	int error;
+
+	if (!haptic->enabled)
+		return;
+
+	error = max77693_haptic_configure(haptic, false);
+	if (error)
+		return;
+
+	error = max77693_haptic_lowsys(haptic, false);
+	if (error)
+		goto err_disable_lowsys;
+
+	pwm_disable(haptic->pwm_dev);
+	haptic->enabled = false;
+
+	return;
+
+err_disable_lowsys:
+	max77693_haptic_configure(haptic, true);
+}
+
+static void max77693_haptic_play_work(struct work_struct *work)
+{
+	struct max77693_haptic *haptic =
+			container_of(work, struct max77693_haptic, work);
+	int error;
+
+	error = max77693_haptic_set_duty_cycle(haptic);
+	if (error) {
+		dev_err(haptic->dev, "failed to set duty cycle: %d\n", error);
+		return;
+	}
+
+	if (haptic->magnitude)
+		max77693_haptic_enable(haptic);
+	else
+		max77693_haptic_disable(haptic);
+}
+
+static int max77693_haptic_play_effect(struct input_dev *dev, void *data,
+				       struct ff_effect *effect)
+{
+	struct max77693_haptic *haptic = input_get_drvdata(dev);
+	u64 period_mag_multi;
+
+	haptic->magnitude = effect->u.rumble.strong_magnitude;
+	if (!haptic->magnitude)
+		haptic->magnitude = effect->u.rumble.weak_magnitude;
+
+	/*
+	 * The magnitude comes from force-feedback interface.
+	 * The formula to convert magnitude to pwm_duty as follows:
+	 * - pwm_duty = (magnitude * pwm_period) / MAX_MAGNITUDE(0xFFFF)
+	 */
+	period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude;
+	haptic->pwm_duty = (unsigned int)(period_mag_multi >>
+						MAX_MAGNITUDE_SHIFT);
+
+	schedule_work(&haptic->work);
+
+	return 0;
+}
+
+static int max77693_haptic_open(struct input_dev *dev)
+{
+	struct max77693_haptic *haptic = input_get_drvdata(dev);
+	int error;
+
+	error = max77843_haptic_bias(haptic, true);
+	if (error)
+		return error;
+
+	error = regulator_enable(haptic->motor_reg);
+	if (error) {
+		dev_err(haptic->dev,
+			"failed to enable regulator: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static void max77693_haptic_close(struct input_dev *dev)
+{
+	struct max77693_haptic *haptic = input_get_drvdata(dev);
+	int error;
+
+	cancel_work_sync(&haptic->work);
+	max77693_haptic_disable(haptic);
+
+	error = regulator_disable(haptic->motor_reg);
+	if (error)
+		dev_err(haptic->dev,
+			"failed to disable regulator: %d\n", error);
+
+	max77843_haptic_bias(haptic, false);
+}
+
+static int max77693_haptic_probe(struct platform_device *pdev)
+{
+	struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent);
+	struct max77693_haptic *haptic;
+	int error;
+
+	haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL);
+	if (!haptic)
+		return -ENOMEM;
+
+	haptic->regmap_pmic = max77693->regmap;
+	haptic->dev = &pdev->dev;
+	haptic->type = MAX77693_HAPTIC_LRA;
+	haptic->mode = MAX77693_HAPTIC_EXTERNAL_MODE;
+	haptic->suspend_state = false;
+
+	/* Variant-specific init */
+	haptic->dev_type = platform_get_device_id(pdev)->driver_data;
+	switch (haptic->dev_type) {
+	case TYPE_MAX77693:
+		haptic->regmap_haptic = max77693->regmap_haptic;
+		break;
+	case TYPE_MAX77843:
+		haptic->regmap_haptic = max77693->regmap;
+		break;
+	default:
+		dev_err(&pdev->dev, "unsupported device type: %u\n",
+			haptic->dev_type);
+		return -EINVAL;
+	}
+
+	INIT_WORK(&haptic->work, max77693_haptic_play_work);
+
+	/* Get pwm and regulatot for haptic device */
+	haptic->pwm_dev = devm_pwm_get(&pdev->dev, NULL);
+	if (IS_ERR(haptic->pwm_dev)) {
+		dev_err(&pdev->dev, "failed to get pwm device\n");
+		return PTR_ERR(haptic->pwm_dev);
+	}
+
+	haptic->motor_reg = devm_regulator_get(&pdev->dev, "haptic");
+	if (IS_ERR(haptic->motor_reg)) {
+		dev_err(&pdev->dev, "failed to get regulator\n");
+		return PTR_ERR(haptic->motor_reg);
+	}
+
+	/* Initialize input device for haptic device */
+	haptic->input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!haptic->input_dev) {
+		dev_err(&pdev->dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	haptic->input_dev->name = "max77693-haptic";
+	haptic->input_dev->id.version = 1;
+	haptic->input_dev->dev.parent = &pdev->dev;
+	haptic->input_dev->open = max77693_haptic_open;
+	haptic->input_dev->close = max77693_haptic_close;
+	input_set_drvdata(haptic->input_dev, haptic);
+	input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE);
+
+	error = input_ff_create_memless(haptic->input_dev, NULL,
+				max77693_haptic_play_effect);
+	if (error) {
+		dev_err(&pdev->dev, "failed to create force-feedback\n");
+		return error;
+	}
+
+	error = input_register_device(haptic->input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		return error;
+	}
+
+	platform_set_drvdata(pdev, haptic);
+
+	return 0;
+}
+
+static int __maybe_unused max77693_haptic_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct max77693_haptic *haptic = platform_get_drvdata(pdev);
+
+	if (haptic->enabled) {
+		max77693_haptic_disable(haptic);
+		haptic->suspend_state = true;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused max77693_haptic_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct max77693_haptic *haptic = platform_get_drvdata(pdev);
+
+	if (haptic->suspend_state) {
+		max77693_haptic_enable(haptic);
+		haptic->suspend_state = false;
+	}
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(max77693_haptic_pm_ops,
+			 max77693_haptic_suspend, max77693_haptic_resume);
+
+static const struct platform_device_id max77693_haptic_id[] = {
+	{ "max77693-haptic", TYPE_MAX77693 },
+	{ "max77843-haptic", TYPE_MAX77843 },
+	{},
+};
+MODULE_DEVICE_TABLE(platform, max77693_haptic_id);
+
+static struct platform_driver max77693_haptic_driver = {
+	.driver		= {
+		.name	= "max77693-haptic",
+		.pm	= &max77693_haptic_pm_ops,
+	},
+	.probe		= max77693_haptic_probe,
+	.id_table	= max77693_haptic_id,
+};
+module_platform_driver(max77693_haptic_driver);
+
+MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
+MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
+MODULE_DESCRIPTION("MAXIM 77693/77843 Haptic driver");
+MODULE_ALIAS("platform:max77693-haptic");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c
new file mode 100644
index 0000000..7c49b8d
--- /dev/null
+++ b/drivers/input/misc/max8925_onkey.c
@@ -0,0 +1,177 @@
+/**
+ * MAX8925 ONKEY driver
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ *      Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max8925.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#define SW_INPUT		(1 << 7)	/* 0/1 -- up/down */
+#define HARDRESET_EN		(1 << 7)
+#define PWREN_EN		(1 << 7)
+
+struct max8925_onkey_info {
+	struct input_dev	*idev;
+	struct i2c_client	*i2c;
+	struct device		*dev;
+	unsigned int		irq[2];
+};
+
+/*
+ * MAX8925 gives us an interrupt when ONKEY is pressed or released.
+ * max8925_set_bits() operates I2C bus and may sleep. So implement
+ * it in thread IRQ handler.
+ */
+static irqreturn_t max8925_onkey_handler(int irq, void *data)
+{
+	struct max8925_onkey_info *info = data;
+	int state;
+
+	state = max8925_reg_read(info->i2c, MAX8925_ON_OFF_STATUS);
+
+	input_report_key(info->idev, KEY_POWER, state & SW_INPUT);
+	input_sync(info->idev);
+
+	dev_dbg(info->dev, "onkey state:%d\n", state);
+
+	/* Enable hardreset to halt if system isn't shutdown on time */
+	max8925_set_bits(info->i2c, MAX8925_SYSENSEL,
+			 HARDRESET_EN, HARDRESET_EN);
+
+	return IRQ_HANDLED;
+}
+
+static int max8925_onkey_probe(struct platform_device *pdev)
+{
+	struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+	struct max8925_onkey_info *info;
+	struct input_dev *input;
+	int irq[2], error;
+
+	irq[0] = platform_get_irq(pdev, 0);
+	if (irq[0] < 0) {
+		dev_err(&pdev->dev, "No IRQ resource!\n");
+		return -EINVAL;
+	}
+
+	irq[1] = platform_get_irq(pdev, 1);
+	if (irq[1] < 0) {
+		dev_err(&pdev->dev, "No IRQ resource!\n");
+		return -EINVAL;
+	}
+
+	info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_onkey_info),
+			    GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input)
+		return -ENOMEM;
+
+	info->idev = input;
+	info->i2c = chip->i2c;
+	info->dev = &pdev->dev;
+	info->irq[0] = irq[0];
+	info->irq[1] = irq[1];
+
+	input->name = "max8925_on";
+	input->phys = "max8925_on/input0";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &pdev->dev;
+	input_set_capability(input, EV_KEY, KEY_POWER);
+
+	error = devm_request_threaded_irq(&pdev->dev, irq[0], NULL,
+					  max8925_onkey_handler, IRQF_ONESHOT,
+					  "onkey-down", info);
+	if (error < 0) {
+		dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+			irq[0], error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&pdev->dev, irq[1], NULL,
+					  max8925_onkey_handler, IRQF_ONESHOT,
+					  "onkey-up", info);
+	if (error < 0) {
+		dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+			irq[1], error);
+		return error;
+	}
+
+	error = input_register_device(info->idev);
+	if (error) {
+		dev_err(chip->dev, "Can't register input device: %d\n", error);
+		return error;
+	}
+
+	platform_set_drvdata(pdev, info);
+	device_init_wakeup(&pdev->dev, 1);
+
+	return 0;
+}
+
+static int __maybe_unused max8925_onkey_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct max8925_onkey_info *info = platform_get_drvdata(pdev);
+	struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+	if (device_may_wakeup(dev)) {
+		chip->wakeup_flag |= 1 << info->irq[0];
+		chip->wakeup_flag |= 1 << info->irq[1];
+	}
+
+	return 0;
+}
+
+static int __maybe_unused max8925_onkey_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct max8925_onkey_info *info = platform_get_drvdata(pdev);
+	struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+	if (device_may_wakeup(dev)) {
+		chip->wakeup_flag &= ~(1 << info->irq[0]);
+		chip->wakeup_flag &= ~(1 << info->irq[1]);
+	}
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(max8925_onkey_pm_ops, max8925_onkey_suspend, max8925_onkey_resume);
+
+static struct platform_driver max8925_onkey_driver = {
+	.driver		= {
+		.name	= "max8925-onkey",
+		.pm	= &max8925_onkey_pm_ops,
+	},
+	.probe		= max8925_onkey_probe,
+};
+module_platform_driver(max8925_onkey_driver);
+
+MODULE_DESCRIPTION("Maxim MAX8925 ONKEY driver");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c
new file mode 100644
index 0000000..8d6326d
--- /dev/null
+++ b/drivers/input/misc/max8997_haptic.c
@@ -0,0 +1,414 @@
+/*
+ * MAX8997-haptic controller driver
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Donggeun Kim <dg77.kim@samsung.com>
+ *
+ * This program is not provided / owned by Maxim Integrated Products.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/pwm.h>
+#include <linux/input.h>
+#include <linux/mfd/max8997-private.h>
+#include <linux/mfd/max8997.h>
+#include <linux/regulator/consumer.h>
+
+/* Haptic configuration 2 register */
+#define MAX8997_MOTOR_TYPE_SHIFT	7
+#define MAX8997_ENABLE_SHIFT		6
+#define MAX8997_MODE_SHIFT		5
+
+/* Haptic driver configuration register */
+#define MAX8997_CYCLE_SHIFT		6
+#define MAX8997_SIG_PERIOD_SHIFT	4
+#define MAX8997_SIG_DUTY_SHIFT		2
+#define MAX8997_PWM_DUTY_SHIFT		0
+
+struct max8997_haptic {
+	struct device *dev;
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	struct regulator *regulator;
+
+	struct work_struct work;
+	struct mutex mutex;
+
+	bool enabled;
+	unsigned int level;
+
+	struct pwm_device *pwm;
+	unsigned int pwm_period;
+	enum max8997_haptic_pwm_divisor pwm_divisor;
+
+	enum max8997_haptic_motor_type type;
+	enum max8997_haptic_pulse_mode mode;
+
+	unsigned int internal_mode_pattern;
+	unsigned int pattern_cycle;
+	unsigned int pattern_signal_period;
+};
+
+static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip)
+{
+	int ret = 0;
+
+	if (chip->mode == MAX8997_EXTERNAL_MODE) {
+		unsigned int duty = chip->pwm_period * chip->level / 100;
+		ret = pwm_config(chip->pwm, duty, chip->pwm_period);
+	} else {
+		int i;
+		u8 duty_index = 0;
+
+		for (i = 0; i <= 64; i++) {
+			if (chip->level <= i * 100 / 64) {
+				duty_index = i;
+				break;
+			}
+		}
+		switch (chip->internal_mode_pattern) {
+		case 0:
+			max8997_write_reg(chip->client,
+				MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index);
+			break;
+		case 1:
+			max8997_write_reg(chip->client,
+				MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index);
+			break;
+		case 2:
+			max8997_write_reg(chip->client,
+				MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index);
+			break;
+		case 3:
+			max8997_write_reg(chip->client,
+				MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index);
+			break;
+		default:
+			break;
+		}
+	}
+	return ret;
+}
+
+static void max8997_haptic_configure(struct max8997_haptic *chip)
+{
+	u8 value;
+
+	value = chip->type << MAX8997_MOTOR_TYPE_SHIFT |
+		chip->enabled << MAX8997_ENABLE_SHIFT |
+		chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor;
+	max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CONF2, value);
+
+	if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) {
+		value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT |
+			chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT |
+			chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT |
+			chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT;
+		max8997_write_reg(chip->client,
+			MAX8997_HAPTIC_REG_DRVCONF, value);
+
+		switch (chip->internal_mode_pattern) {
+		case 0:
+			value = chip->pattern_cycle << 4;
+			max8997_write_reg(chip->client,
+				MAX8997_HAPTIC_REG_CYCLECONF1, value);
+			value = chip->pattern_signal_period;
+			max8997_write_reg(chip->client,
+				MAX8997_HAPTIC_REG_SIGCONF1, value);
+			break;
+
+		case 1:
+			value = chip->pattern_cycle;
+			max8997_write_reg(chip->client,
+				MAX8997_HAPTIC_REG_CYCLECONF1, value);
+			value = chip->pattern_signal_period;
+			max8997_write_reg(chip->client,
+				MAX8997_HAPTIC_REG_SIGCONF2, value);
+			break;
+
+		case 2:
+			value = chip->pattern_cycle << 4;
+			max8997_write_reg(chip->client,
+				MAX8997_HAPTIC_REG_CYCLECONF2, value);
+			value = chip->pattern_signal_period;
+			max8997_write_reg(chip->client,
+				MAX8997_HAPTIC_REG_SIGCONF3, value);
+			break;
+
+		case 3:
+			value = chip->pattern_cycle;
+			max8997_write_reg(chip->client,
+				MAX8997_HAPTIC_REG_CYCLECONF2, value);
+			value = chip->pattern_signal_period;
+			max8997_write_reg(chip->client,
+				MAX8997_HAPTIC_REG_SIGCONF4, value);
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+static void max8997_haptic_enable(struct max8997_haptic *chip)
+{
+	int error;
+
+	mutex_lock(&chip->mutex);
+
+	error = max8997_haptic_set_duty_cycle(chip);
+	if (error) {
+		dev_err(chip->dev, "set_pwm_cycle failed, error: %d\n", error);
+		goto out;
+	}
+
+	if (!chip->enabled) {
+		error = regulator_enable(chip->regulator);
+		if (error) {
+			dev_err(chip->dev, "Failed to enable regulator\n");
+			goto out;
+		}
+		max8997_haptic_configure(chip);
+		if (chip->mode == MAX8997_EXTERNAL_MODE) {
+			error = pwm_enable(chip->pwm);
+			if (error) {
+				dev_err(chip->dev, "Failed to enable PWM\n");
+				regulator_disable(chip->regulator);
+				goto out;
+			}
+		}
+		chip->enabled = true;
+	}
+
+out:
+	mutex_unlock(&chip->mutex);
+}
+
+static void max8997_haptic_disable(struct max8997_haptic *chip)
+{
+	mutex_lock(&chip->mutex);
+
+	if (chip->enabled) {
+		chip->enabled = false;
+		max8997_haptic_configure(chip);
+		if (chip->mode == MAX8997_EXTERNAL_MODE)
+			pwm_disable(chip->pwm);
+		regulator_disable(chip->regulator);
+	}
+
+	mutex_unlock(&chip->mutex);
+}
+
+static void max8997_haptic_play_effect_work(struct work_struct *work)
+{
+	struct max8997_haptic *chip =
+			container_of(work, struct max8997_haptic, work);
+
+	if (chip->level)
+		max8997_haptic_enable(chip);
+	else
+		max8997_haptic_disable(chip);
+}
+
+static int max8997_haptic_play_effect(struct input_dev *dev, void *data,
+				  struct ff_effect *effect)
+{
+	struct max8997_haptic *chip = input_get_drvdata(dev);
+
+	chip->level = effect->u.rumble.strong_magnitude;
+	if (!chip->level)
+		chip->level = effect->u.rumble.weak_magnitude;
+
+	schedule_work(&chip->work);
+
+	return 0;
+}
+
+static void max8997_haptic_close(struct input_dev *dev)
+{
+	struct max8997_haptic *chip = input_get_drvdata(dev);
+
+	cancel_work_sync(&chip->work);
+	max8997_haptic_disable(chip);
+}
+
+static int max8997_haptic_probe(struct platform_device *pdev)
+{
+	struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	const struct max8997_platform_data *pdata =
+					dev_get_platdata(iodev->dev);
+	const struct max8997_haptic_platform_data *haptic_pdata = NULL;
+	struct max8997_haptic *chip;
+	struct input_dev *input_dev;
+	int error;
+
+	if (pdata)
+		haptic_pdata = pdata->haptic_pdata;
+
+	if (!haptic_pdata) {
+		dev_err(&pdev->dev, "no haptic platform data\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct max8997_haptic), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!chip || !input_dev) {
+		dev_err(&pdev->dev, "unable to allocate memory\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	INIT_WORK(&chip->work, max8997_haptic_play_effect_work);
+	mutex_init(&chip->mutex);
+
+	chip->client = iodev->haptic;
+	chip->dev = &pdev->dev;
+	chip->input_dev = input_dev;
+	chip->pwm_period = haptic_pdata->pwm_period;
+	chip->type = haptic_pdata->type;
+	chip->mode = haptic_pdata->mode;
+	chip->pwm_divisor = haptic_pdata->pwm_divisor;
+
+	switch (chip->mode) {
+	case MAX8997_INTERNAL_MODE:
+		chip->internal_mode_pattern =
+				haptic_pdata->internal_mode_pattern;
+		chip->pattern_cycle = haptic_pdata->pattern_cycle;
+		chip->pattern_signal_period =
+				haptic_pdata->pattern_signal_period;
+		break;
+
+	case MAX8997_EXTERNAL_MODE:
+		chip->pwm = pwm_request(haptic_pdata->pwm_channel_id,
+					"max8997-haptic");
+		if (IS_ERR(chip->pwm)) {
+			error = PTR_ERR(chip->pwm);
+			dev_err(&pdev->dev,
+				"unable to request PWM for haptic, error: %d\n",
+				error);
+			goto err_free_mem;
+		}
+		break;
+
+	default:
+		dev_err(&pdev->dev,
+			"Invalid chip mode specified (%d)\n", chip->mode);
+		error = -EINVAL;
+		goto err_free_mem;
+	}
+
+	chip->regulator = regulator_get(&pdev->dev, "inmotor");
+	if (IS_ERR(chip->regulator)) {
+		error = PTR_ERR(chip->regulator);
+		dev_err(&pdev->dev,
+			"unable to get regulator, error: %d\n",
+			error);
+		goto err_free_pwm;
+	}
+
+	input_dev->name = "max8997-haptic";
+	input_dev->id.version = 1;
+	input_dev->dev.parent = &pdev->dev;
+	input_dev->close = max8997_haptic_close;
+	input_set_drvdata(input_dev, chip);
+	input_set_capability(input_dev, EV_FF, FF_RUMBLE);
+
+	error = input_ff_create_memless(input_dev, NULL,
+				max8997_haptic_play_effect);
+	if (error) {
+		dev_err(&pdev->dev,
+			"unable to create FF device, error: %d\n",
+			error);
+		goto err_put_regulator;
+	}
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(&pdev->dev,
+			"unable to register input device, error: %d\n",
+			error);
+		goto err_destroy_ff;
+	}
+
+	platform_set_drvdata(pdev, chip);
+	return 0;
+
+err_destroy_ff:
+	input_ff_destroy(input_dev);
+err_put_regulator:
+	regulator_put(chip->regulator);
+err_free_pwm:
+	if (chip->mode == MAX8997_EXTERNAL_MODE)
+		pwm_free(chip->pwm);
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(chip);
+
+	return error;
+}
+
+static int max8997_haptic_remove(struct platform_device *pdev)
+{
+	struct max8997_haptic *chip = platform_get_drvdata(pdev);
+
+	input_unregister_device(chip->input_dev);
+	regulator_put(chip->regulator);
+
+	if (chip->mode == MAX8997_EXTERNAL_MODE)
+		pwm_free(chip->pwm);
+
+	kfree(chip);
+
+	return 0;
+}
+
+static int __maybe_unused max8997_haptic_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct max8997_haptic *chip = platform_get_drvdata(pdev);
+
+	max8997_haptic_disable(chip);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, max8997_haptic_suspend, NULL);
+
+static const struct platform_device_id max8997_haptic_id[] = {
+	{ "max8997-haptic", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, max8997_haptic_id);
+
+static struct platform_driver max8997_haptic_driver = {
+	.driver	= {
+		.name	= "max8997-haptic",
+		.pm	= &max8997_haptic_pm_ops,
+	},
+	.probe		= max8997_haptic_probe,
+	.remove		= max8997_haptic_remove,
+	.id_table	= max8997_haptic_id,
+};
+module_platform_driver(max8997_haptic_driver);
+
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_DESCRIPTION("max8997_haptic driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/mc13783-pwrbutton.c b/drivers/input/misc/mc13783-pwrbutton.c
new file mode 100644
index 0000000..afdf8ef
--- /dev/null
+++ b/drivers/input/misc/mc13783-pwrbutton.c
@@ -0,0 +1,269 @@
+/**
+ * Copyright (C) 2011 Philippe Rétornaz
+ *
+ * Based on twl4030-pwrbutton driver by:
+ *     Peter De Schrijver <peter.de-schrijver@nokia.com>
+ *     Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335  USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/mc13783.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+struct mc13783_pwrb {
+	struct input_dev *pwr;
+	struct mc13xxx *mc13783;
+#define MC13783_PWRB_B1_POL_INVERT	(1 << 0)
+#define MC13783_PWRB_B2_POL_INVERT	(1 << 1)
+#define MC13783_PWRB_B3_POL_INVERT	(1 << 2)
+	int flags;
+	unsigned short keymap[3];
+};
+
+#define MC13783_REG_INTERRUPT_SENSE_1		5
+#define MC13783_IRQSENSE1_ONOFD1S		(1 << 3)
+#define MC13783_IRQSENSE1_ONOFD2S		(1 << 4)
+#define MC13783_IRQSENSE1_ONOFD3S		(1 << 5)
+
+#define MC13783_REG_POWER_CONTROL_2		15
+#define MC13783_POWER_CONTROL_2_ON1BDBNC	4
+#define MC13783_POWER_CONTROL_2_ON2BDBNC	6
+#define MC13783_POWER_CONTROL_2_ON3BDBNC	8
+#define MC13783_POWER_CONTROL_2_ON1BRSTEN	(1 << 1)
+#define MC13783_POWER_CONTROL_2_ON2BRSTEN	(1 << 2)
+#define MC13783_POWER_CONTROL_2_ON3BRSTEN	(1 << 3)
+
+static irqreturn_t button_irq(int irq, void *_priv)
+{
+	struct mc13783_pwrb *priv = _priv;
+	int val;
+
+	mc13xxx_irq_ack(priv->mc13783, irq);
+	mc13xxx_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_SENSE_1, &val);
+
+	switch (irq) {
+	case MC13783_IRQ_ONOFD1:
+		val = val & MC13783_IRQSENSE1_ONOFD1S ? 1 : 0;
+		if (priv->flags & MC13783_PWRB_B1_POL_INVERT)
+			val ^= 1;
+		input_report_key(priv->pwr, priv->keymap[0], val);
+		break;
+
+	case MC13783_IRQ_ONOFD2:
+		val = val & MC13783_IRQSENSE1_ONOFD2S ? 1 : 0;
+		if (priv->flags & MC13783_PWRB_B2_POL_INVERT)
+			val ^= 1;
+		input_report_key(priv->pwr, priv->keymap[1], val);
+		break;
+
+	case MC13783_IRQ_ONOFD3:
+		val = val & MC13783_IRQSENSE1_ONOFD3S ? 1 : 0;
+		if (priv->flags & MC13783_PWRB_B3_POL_INVERT)
+			val ^= 1;
+		input_report_key(priv->pwr, priv->keymap[2], val);
+		break;
+	}
+
+	input_sync(priv->pwr);
+
+	return IRQ_HANDLED;
+}
+
+static int mc13783_pwrbutton_probe(struct platform_device *pdev)
+{
+	const struct mc13xxx_buttons_platform_data *pdata;
+	struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent);
+	struct input_dev *pwr;
+	struct mc13783_pwrb *priv;
+	int err = 0;
+	int reg = 0;
+
+	pdata = dev_get_platdata(&pdev->dev);
+	if (!pdata) {
+		dev_err(&pdev->dev, "missing platform data\n");
+		return -ENODEV;
+	}
+
+	pwr = input_allocate_device();
+	if (!pwr) {
+		dev_dbg(&pdev->dev, "Can't allocate power button\n");
+		return -ENOMEM;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		err = -ENOMEM;
+		dev_dbg(&pdev->dev, "Can't allocate power button\n");
+		goto free_input_dev;
+	}
+
+	reg |= (pdata->b1on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON1BDBNC;
+	reg |= (pdata->b2on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON2BDBNC;
+	reg |= (pdata->b3on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON3BDBNC;
+
+	priv->pwr = pwr;
+	priv->mc13783 = mc13783;
+
+	mc13xxx_lock(mc13783);
+
+	if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) {
+		priv->keymap[0] = pdata->b1on_key;
+		if (pdata->b1on_key != KEY_RESERVED)
+			__set_bit(pdata->b1on_key, pwr->keybit);
+
+		if (pdata->b1on_flags & MC13783_BUTTON_POL_INVERT)
+			priv->flags |= MC13783_PWRB_B1_POL_INVERT;
+
+		if (pdata->b1on_flags & MC13783_BUTTON_RESET_EN)
+			reg |= MC13783_POWER_CONTROL_2_ON1BRSTEN;
+
+		err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD1,
+					  button_irq, "b1on", priv);
+		if (err) {
+			dev_dbg(&pdev->dev, "Can't request irq\n");
+			goto free_priv;
+		}
+	}
+
+	if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) {
+		priv->keymap[1] = pdata->b2on_key;
+		if (pdata->b2on_key != KEY_RESERVED)
+			__set_bit(pdata->b2on_key, pwr->keybit);
+
+		if (pdata->b2on_flags & MC13783_BUTTON_POL_INVERT)
+			priv->flags |= MC13783_PWRB_B2_POL_INVERT;
+
+		if (pdata->b2on_flags & MC13783_BUTTON_RESET_EN)
+			reg |= MC13783_POWER_CONTROL_2_ON2BRSTEN;
+
+		err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD2,
+					  button_irq, "b2on", priv);
+		if (err) {
+			dev_dbg(&pdev->dev, "Can't request irq\n");
+			goto free_irq_b1;
+		}
+	}
+
+	if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) {
+		priv->keymap[2] = pdata->b3on_key;
+		if (pdata->b3on_key != KEY_RESERVED)
+			__set_bit(pdata->b3on_key, pwr->keybit);
+
+		if (pdata->b3on_flags & MC13783_BUTTON_POL_INVERT)
+			priv->flags |= MC13783_PWRB_B3_POL_INVERT;
+
+		if (pdata->b3on_flags & MC13783_BUTTON_RESET_EN)
+			reg |= MC13783_POWER_CONTROL_2_ON3BRSTEN;
+
+		err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD3,
+					  button_irq, "b3on", priv);
+		if (err) {
+			dev_dbg(&pdev->dev, "Can't request irq: %d\n", err);
+			goto free_irq_b2;
+		}
+	}
+
+	mc13xxx_reg_rmw(mc13783, MC13783_REG_POWER_CONTROL_2, 0x3FE, reg);
+
+	mc13xxx_unlock(mc13783);
+
+	pwr->name = "mc13783_pwrbutton";
+	pwr->phys = "mc13783_pwrbutton/input0";
+	pwr->dev.parent = &pdev->dev;
+
+	pwr->keycode = priv->keymap;
+	pwr->keycodemax = ARRAY_SIZE(priv->keymap);
+	pwr->keycodesize = sizeof(priv->keymap[0]);
+	__set_bit(EV_KEY, pwr->evbit);
+
+	err = input_register_device(pwr);
+	if (err) {
+		dev_dbg(&pdev->dev, "Can't register power button: %d\n", err);
+		goto free_irq;
+	}
+
+	platform_set_drvdata(pdev, priv);
+
+	return 0;
+
+free_irq:
+	mc13xxx_lock(mc13783);
+
+	if (pdata->b3on_flags & MC13783_BUTTON_ENABLE)
+		mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD3, priv);
+
+free_irq_b2:
+	if (pdata->b2on_flags & MC13783_BUTTON_ENABLE)
+		mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD2, priv);
+
+free_irq_b1:
+	if (pdata->b1on_flags & MC13783_BUTTON_ENABLE)
+		mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD1, priv);
+
+free_priv:
+	mc13xxx_unlock(mc13783);
+	kfree(priv);
+
+free_input_dev:
+	input_free_device(pwr);
+
+	return err;
+}
+
+static int mc13783_pwrbutton_remove(struct platform_device *pdev)
+{
+	struct mc13783_pwrb *priv = platform_get_drvdata(pdev);
+	const struct mc13xxx_buttons_platform_data *pdata;
+
+	pdata = dev_get_platdata(&pdev->dev);
+
+	mc13xxx_lock(priv->mc13783);
+
+	if (pdata->b3on_flags & MC13783_BUTTON_ENABLE)
+		mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD3, priv);
+	if (pdata->b2on_flags & MC13783_BUTTON_ENABLE)
+		mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD2, priv);
+	if (pdata->b1on_flags & MC13783_BUTTON_ENABLE)
+		mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD1, priv);
+
+	mc13xxx_unlock(priv->mc13783);
+
+	input_unregister_device(priv->pwr);
+	kfree(priv);
+
+	return 0;
+}
+
+static struct platform_driver mc13783_pwrbutton_driver = {
+	.probe		= mc13783_pwrbutton_probe,
+	.remove		= mc13783_pwrbutton_remove,
+	.driver		= {
+		.name	= "mc13783-pwrbutton",
+	},
+};
+
+module_platform_driver(mc13783_pwrbutton_driver);
+
+MODULE_ALIAS("platform:mc13783-pwrbutton");
+MODULE_DESCRIPTION("MC13783 Power Button");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Philippe Retornaz");
diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c
new file mode 100644
index 0000000..19c7357
--- /dev/null
+++ b/drivers/input/misc/mma8450.c
@@ -0,0 +1,238 @@
+/*
+ *  Driver for Freescale's 3-Axis Accelerometer MMA8450
+ *
+ *  Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input-polldev.h>
+#include <linux/of_device.h>
+
+#define MMA8450_DRV_NAME	"mma8450"
+
+#define MODE_CHANGE_DELAY_MS	100
+#define POLL_INTERVAL		100
+#define POLL_INTERVAL_MAX	500
+
+/* register definitions */
+#define MMA8450_STATUS		0x00
+#define MMA8450_STATUS_ZXYDR	0x08
+
+#define MMA8450_OUT_X8		0x01
+#define MMA8450_OUT_Y8		0x02
+#define MMA8450_OUT_Z8		0x03
+
+#define MMA8450_OUT_X_LSB	0x05
+#define MMA8450_OUT_X_MSB	0x06
+#define MMA8450_OUT_Y_LSB	0x07
+#define MMA8450_OUT_Y_MSB	0x08
+#define MMA8450_OUT_Z_LSB	0x09
+#define MMA8450_OUT_Z_MSB	0x0a
+
+#define MMA8450_XYZ_DATA_CFG	0x16
+
+#define MMA8450_CTRL_REG1	0x38
+#define MMA8450_CTRL_REG2	0x39
+
+/* mma8450 status */
+struct mma8450 {
+	struct i2c_client	*client;
+	struct input_polled_dev	*idev;
+};
+
+static int mma8450_read(struct mma8450 *m, unsigned off)
+{
+	struct i2c_client *c = m->client;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(c, off);
+	if (ret < 0)
+		dev_err(&c->dev,
+			"failed to read register 0x%02x, error %d\n",
+			off, ret);
+
+	return ret;
+}
+
+static int mma8450_write(struct mma8450 *m, unsigned off, u8 v)
+{
+	struct i2c_client *c = m->client;
+	int error;
+
+	error = i2c_smbus_write_byte_data(c, off, v);
+	if (error < 0) {
+		dev_err(&c->dev,
+			"failed to write to register 0x%02x, error %d\n",
+			off, error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int mma8450_read_block(struct mma8450 *m, unsigned off,
+			      u8 *buf, size_t size)
+{
+	struct i2c_client *c = m->client;
+	int err;
+
+	err = i2c_smbus_read_i2c_block_data(c, off, size, buf);
+	if (err < 0) {
+		dev_err(&c->dev,
+			"failed to read block data at 0x%02x, error %d\n",
+			MMA8450_OUT_X_LSB, err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void mma8450_poll(struct input_polled_dev *dev)
+{
+	struct mma8450 *m = dev->private;
+	int x, y, z;
+	int ret;
+	u8 buf[6];
+
+	ret = mma8450_read(m, MMA8450_STATUS);
+	if (ret < 0)
+		return;
+
+	if (!(ret & MMA8450_STATUS_ZXYDR))
+		return;
+
+	ret = mma8450_read_block(m, MMA8450_OUT_X_LSB, buf, sizeof(buf));
+	if (ret < 0)
+		return;
+
+	x = ((int)(s8)buf[1] << 4) | (buf[0] & 0xf);
+	y = ((int)(s8)buf[3] << 4) | (buf[2] & 0xf);
+	z = ((int)(s8)buf[5] << 4) | (buf[4] & 0xf);
+
+	input_report_abs(dev->input, ABS_X, x);
+	input_report_abs(dev->input, ABS_Y, y);
+	input_report_abs(dev->input, ABS_Z, z);
+	input_sync(dev->input);
+}
+
+/* Initialize the MMA8450 chip */
+static void mma8450_open(struct input_polled_dev *dev)
+{
+	struct mma8450 *m = dev->private;
+	int err;
+
+	/* enable all events from X/Y/Z, no FIFO */
+	err = mma8450_write(m, MMA8450_XYZ_DATA_CFG, 0x07);
+	if (err)
+		return;
+
+	/*
+	 * Sleep mode poll rate - 50Hz
+	 * System output data rate - 400Hz
+	 * Full scale selection - Active, +/- 2G
+	 */
+	err = mma8450_write(m, MMA8450_CTRL_REG1, 0x01);
+	if (err < 0)
+		return;
+
+	msleep(MODE_CHANGE_DELAY_MS);
+}
+
+static void mma8450_close(struct input_polled_dev *dev)
+{
+	struct mma8450 *m = dev->private;
+
+	mma8450_write(m, MMA8450_CTRL_REG1, 0x00);
+	mma8450_write(m, MMA8450_CTRL_REG2, 0x01);
+}
+
+/*
+ * I2C init/probing/exit functions
+ */
+static int mma8450_probe(struct i2c_client *c,
+			 const struct i2c_device_id *id)
+{
+	struct input_polled_dev *idev;
+	struct mma8450 *m;
+	int err;
+
+	m = devm_kzalloc(&c->dev, sizeof(*m), GFP_KERNEL);
+	if (!m)
+		return -ENOMEM;
+
+	idev = devm_input_allocate_polled_device(&c->dev);
+	if (!idev)
+		return -ENOMEM;
+
+	m->client = c;
+	m->idev = idev;
+
+	idev->private		= m;
+	idev->input->name	= MMA8450_DRV_NAME;
+	idev->input->id.bustype	= BUS_I2C;
+	idev->poll		= mma8450_poll;
+	idev->poll_interval	= POLL_INTERVAL;
+	idev->poll_interval_max	= POLL_INTERVAL_MAX;
+	idev->open		= mma8450_open;
+	idev->close		= mma8450_close;
+
+	__set_bit(EV_ABS, idev->input->evbit);
+	input_set_abs_params(idev->input, ABS_X, -2048, 2047, 32, 32);
+	input_set_abs_params(idev->input, ABS_Y, -2048, 2047, 32, 32);
+	input_set_abs_params(idev->input, ABS_Z, -2048, 2047, 32, 32);
+
+	err = input_register_polled_device(idev);
+	if (err) {
+		dev_err(&c->dev, "failed to register polled input device\n");
+		return err;
+	}
+
+	i2c_set_clientdata(c, m);
+
+	return 0;
+}
+
+static const struct i2c_device_id mma8450_id[] = {
+	{ MMA8450_DRV_NAME, 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, mma8450_id);
+
+static const struct of_device_id mma8450_dt_ids[] = {
+	{ .compatible = "fsl,mma8450", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mma8450_dt_ids);
+
+static struct i2c_driver mma8450_driver = {
+	.driver = {
+		.name	= MMA8450_DRV_NAME,
+		.of_match_table = mma8450_dt_ids,
+	},
+	.probe		= mma8450_probe,
+	.id_table	= mma8450_id,
+};
+
+module_i2c_driver(mma8450_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MMA8450 3-Axis Accelerometer Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
new file mode 100644
index 0000000..f088db3
--- /dev/null
+++ b/drivers/input/misc/mpu3050.c
@@ -0,0 +1,481 @@
+/*
+ * MPU3050 Tri-axis gyroscope driver
+ *
+ * Copyright (C) 2011 Wistron Co.Ltd
+ * Joseph Lai <joseph_lai@wistron.com>
+ *
+ * Trimmed down by Alan Cox <alan@linux.intel.com> to produce this version
+ *
+ * This is a 'lite' version of the driver, while we consider the right way
+ * to present the other features to user space. In particular it requires the
+ * device has an IRQ, and it only provides an input interface, so is not much
+ * use for device orientation. A fuller version is available from the Meego
+ * tree.
+ *
+ * This program is based on bma023.c.
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+
+#define MPU3050_CHIP_ID		0x69
+
+#define MPU3050_AUTO_DELAY	1000
+
+#define MPU3050_MIN_VALUE	-32768
+#define MPU3050_MAX_VALUE	32767
+
+#define MPU3050_DEFAULT_POLL_INTERVAL	200
+#define MPU3050_DEFAULT_FS_RANGE	3
+
+/* Register map */
+#define MPU3050_CHIP_ID_REG	0x00
+#define MPU3050_SMPLRT_DIV	0x15
+#define MPU3050_DLPF_FS_SYNC	0x16
+#define MPU3050_INT_CFG		0x17
+#define MPU3050_XOUT_H		0x1D
+#define MPU3050_PWR_MGM		0x3E
+#define MPU3050_PWR_MGM_POS	6
+
+/* Register bits */
+
+/* DLPF_FS_SYNC */
+#define MPU3050_EXT_SYNC_NONE		0x00
+#define MPU3050_EXT_SYNC_TEMP		0x20
+#define MPU3050_EXT_SYNC_GYROX		0x40
+#define MPU3050_EXT_SYNC_GYROY		0x60
+#define MPU3050_EXT_SYNC_GYROZ		0x80
+#define MPU3050_EXT_SYNC_ACCELX	0xA0
+#define MPU3050_EXT_SYNC_ACCELY	0xC0
+#define MPU3050_EXT_SYNC_ACCELZ	0xE0
+#define MPU3050_EXT_SYNC_MASK		0xE0
+#define MPU3050_FS_250DPS		0x00
+#define MPU3050_FS_500DPS		0x08
+#define MPU3050_FS_1000DPS		0x10
+#define MPU3050_FS_2000DPS		0x18
+#define MPU3050_FS_MASK		0x18
+#define MPU3050_DLPF_CFG_256HZ_NOLPF2	0x00
+#define MPU3050_DLPF_CFG_188HZ		0x01
+#define MPU3050_DLPF_CFG_98HZ		0x02
+#define MPU3050_DLPF_CFG_42HZ		0x03
+#define MPU3050_DLPF_CFG_20HZ		0x04
+#define MPU3050_DLPF_CFG_10HZ		0x05
+#define MPU3050_DLPF_CFG_5HZ		0x06
+#define MPU3050_DLPF_CFG_2100HZ_NOLPF	0x07
+#define MPU3050_DLPF_CFG_MASK		0x07
+/* INT_CFG */
+#define MPU3050_RAW_RDY_EN		0x01
+#define MPU3050_MPU_RDY_EN		0x02
+#define MPU3050_LATCH_INT_EN		0x04
+/* PWR_MGM */
+#define MPU3050_PWR_MGM_PLL_X		0x01
+#define MPU3050_PWR_MGM_PLL_Y		0x02
+#define MPU3050_PWR_MGM_PLL_Z		0x03
+#define MPU3050_PWR_MGM_CLKSEL		0x07
+#define MPU3050_PWR_MGM_STBY_ZG	0x08
+#define MPU3050_PWR_MGM_STBY_YG	0x10
+#define MPU3050_PWR_MGM_STBY_XG	0x20
+#define MPU3050_PWR_MGM_SLEEP		0x40
+#define MPU3050_PWR_MGM_RESET		0x80
+#define MPU3050_PWR_MGM_MASK		0x40
+
+struct axis_data {
+	s16 x;
+	s16 y;
+	s16 z;
+};
+
+struct mpu3050_sensor {
+	struct i2c_client *client;
+	struct device *dev;
+	struct input_dev *idev;
+};
+
+/**
+ *	mpu3050_xyz_read_reg	-	read the axes values
+ *	@buffer: provide register addr and get register
+ *	@length: length of register
+ *
+ *	Reads the register values in one transaction or returns a negative
+ *	error code on failure.
+ */
+static int mpu3050_xyz_read_reg(struct i2c_client *client,
+			       u8 *buffer, int length)
+{
+	/*
+	 * Annoying we can't make this const because the i2c layer doesn't
+	 * declare input buffers const.
+	 */
+	char cmd = MPU3050_XOUT_H;
+	struct i2c_msg msg[] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &cmd,
+		},
+		{
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = length,
+			.buf = buffer,
+		},
+	};
+
+	return i2c_transfer(client->adapter, msg, 2);
+}
+
+/**
+ *	mpu3050_read_xyz	-	get co-ordinates from device
+ *	@client: i2c address of sensor
+ *	@coords: co-ordinates to update
+ *
+ *	Return the converted X Y and Z co-ordinates from the sensor device
+ */
+static void mpu3050_read_xyz(struct i2c_client *client,
+			     struct axis_data *coords)
+{
+	u16 buffer[3];
+
+	mpu3050_xyz_read_reg(client, (u8 *)buffer, 6);
+	coords->x = be16_to_cpu(buffer[0]);
+	coords->y = be16_to_cpu(buffer[1]);
+	coords->z = be16_to_cpu(buffer[2]);
+	dev_dbg(&client->dev, "%s: x %d, y %d, z %d\n", __func__,
+					coords->x, coords->y, coords->z);
+}
+
+/**
+ *	mpu3050_set_power_mode	-	set the power mode
+ *	@client: i2c client for the sensor
+ *	@val: value to switch on/off of power, 1: normal power, 0: low power
+ *
+ *	Put device to normal-power mode or low-power mode.
+ */
+static void mpu3050_set_power_mode(struct i2c_client *client, u8 val)
+{
+	u8 value;
+
+	value = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);
+	value = (value & ~MPU3050_PWR_MGM_MASK) |
+		(((val << MPU3050_PWR_MGM_POS) & MPU3050_PWR_MGM_MASK) ^
+		 MPU3050_PWR_MGM_MASK);
+	i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, value);
+}
+
+/**
+ *	mpu3050_input_open	-	called on input event open
+ *	@input: input dev of opened device
+ *
+ *	The input layer calls this function when input event is opened. The
+ *	function will push the device to resume. Then, the device is ready
+ *	to provide data.
+ */
+static int mpu3050_input_open(struct input_dev *input)
+{
+	struct mpu3050_sensor *sensor = input_get_drvdata(input);
+	int error;
+
+	pm_runtime_get(sensor->dev);
+
+	/* Enable interrupts */
+	error = i2c_smbus_write_byte_data(sensor->client, MPU3050_INT_CFG,
+					  MPU3050_LATCH_INT_EN |
+					  MPU3050_RAW_RDY_EN |
+					  MPU3050_MPU_RDY_EN);
+	if (error < 0) {
+		pm_runtime_put(sensor->dev);
+		return error;
+	}
+
+	return 0;
+}
+
+/**
+ *	mpu3050_input_close	-	called on input event close
+ *	@input: input dev of closed device
+ *
+ *	The input layer calls this function when input event is closed. The
+ *	function will push the device to suspend.
+ */
+static void mpu3050_input_close(struct input_dev *input)
+{
+	struct mpu3050_sensor *sensor = input_get_drvdata(input);
+
+	pm_runtime_put(sensor->dev);
+}
+
+/**
+ *	mpu3050_interrupt_thread	-	handle an IRQ
+ *	@irq: interrupt numner
+ *	@data: the sensor
+ *
+ *	Called by the kernel single threaded after an interrupt occurs. Read
+ *	the sensor data and generate an input event for it.
+ */
+static irqreturn_t mpu3050_interrupt_thread(int irq, void *data)
+{
+	struct mpu3050_sensor *sensor = data;
+	struct axis_data axis;
+
+	mpu3050_read_xyz(sensor->client, &axis);
+
+	input_report_abs(sensor->idev, ABS_X, axis.x);
+	input_report_abs(sensor->idev, ABS_Y, axis.y);
+	input_report_abs(sensor->idev, ABS_Z, axis.z);
+	input_sync(sensor->idev);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ *	mpu3050_hw_init	-	initialize hardware
+ *	@sensor: the sensor
+ *
+ *	Called during device probe; configures the sampling method.
+ */
+static int mpu3050_hw_init(struct mpu3050_sensor *sensor)
+{
+	struct i2c_client *client = sensor->client;
+	int ret;
+	u8 reg;
+
+	/* Reset */
+	ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM,
+					MPU3050_PWR_MGM_RESET);
+	if (ret < 0)
+		return ret;
+
+	ret = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);
+	if (ret < 0)
+		return ret;
+
+	ret &= ~MPU3050_PWR_MGM_CLKSEL;
+	ret |= MPU3050_PWR_MGM_PLL_Z;
+	ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, ret);
+	if (ret < 0)
+		return ret;
+
+	/* Output frequency divider. The poll interval */
+	ret = i2c_smbus_write_byte_data(client, MPU3050_SMPLRT_DIV,
+					MPU3050_DEFAULT_POLL_INTERVAL - 1);
+	if (ret < 0)
+		return ret;
+
+	/* Set low pass filter and full scale */
+	reg = MPU3050_DEFAULT_FS_RANGE;
+	reg |= MPU3050_DLPF_CFG_42HZ << 3;
+	reg |= MPU3050_EXT_SYNC_NONE << 5;
+	ret = i2c_smbus_write_byte_data(client, MPU3050_DLPF_FS_SYNC, reg);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/**
+ *	mpu3050_probe	-	device detection callback
+ *	@client: i2c client of found device
+ *	@id: id match information
+ *
+ *	The I2C layer calls us when it believes a sensor is present at this
+ *	address. Probe to see if this is correct and to validate the device.
+ *
+ *	If present install the relevant sysfs interfaces and input device.
+ */
+static int mpu3050_probe(struct i2c_client *client,
+				   const struct i2c_device_id *id)
+{
+	struct mpu3050_sensor *sensor;
+	struct input_dev *idev;
+	int ret;
+	int error;
+
+	sensor = kzalloc(sizeof(struct mpu3050_sensor), GFP_KERNEL);
+	idev = input_allocate_device();
+	if (!sensor || !idev) {
+		dev_err(&client->dev, "failed to allocate driver data\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	sensor->client = client;
+	sensor->dev = &client->dev;
+	sensor->idev = idev;
+
+	mpu3050_set_power_mode(client, 1);
+	msleep(10);
+
+	ret = i2c_smbus_read_byte_data(client, MPU3050_CHIP_ID_REG);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to detect device\n");
+		error = -ENXIO;
+		goto err_free_mem;
+	}
+
+	if (ret != MPU3050_CHIP_ID) {
+		dev_err(&client->dev, "unsupported chip id\n");
+		error = -ENXIO;
+		goto err_free_mem;
+	}
+
+	idev->name = "MPU3050";
+	idev->id.bustype = BUS_I2C;
+	idev->dev.parent = &client->dev;
+
+	idev->open = mpu3050_input_open;
+	idev->close = mpu3050_input_close;
+
+	__set_bit(EV_ABS, idev->evbit);
+	input_set_abs_params(idev, ABS_X,
+			     MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+	input_set_abs_params(idev, ABS_Y,
+			     MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+	input_set_abs_params(idev, ABS_Z,
+			     MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+
+	input_set_drvdata(idev, sensor);
+
+	pm_runtime_set_active(&client->dev);
+
+	error = mpu3050_hw_init(sensor);
+	if (error)
+		goto err_pm_set_suspended;
+
+	error = request_threaded_irq(client->irq,
+				     NULL, mpu3050_interrupt_thread,
+				     IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+				     "mpu3050", sensor);
+	if (error) {
+		dev_err(&client->dev,
+			"can't get IRQ %d, error %d\n", client->irq, error);
+		goto err_pm_set_suspended;
+	}
+
+	error = input_register_device(idev);
+	if (error) {
+		dev_err(&client->dev, "failed to register input device\n");
+		goto err_free_irq;
+	}
+
+	pm_runtime_enable(&client->dev);
+	pm_runtime_set_autosuspend_delay(&client->dev, MPU3050_AUTO_DELAY);
+	i2c_set_clientdata(client, sensor);
+
+	return 0;
+
+err_free_irq:
+	free_irq(client->irq, sensor);
+err_pm_set_suspended:
+	pm_runtime_set_suspended(&client->dev);
+err_free_mem:
+	input_free_device(idev);
+	kfree(sensor);
+	return error;
+}
+
+/**
+ *	mpu3050_remove	-	remove a sensor
+ *	@client: i2c client of sensor being removed
+ *
+ *	Our sensor is going away, clean up the resources.
+ */
+static int mpu3050_remove(struct i2c_client *client)
+{
+	struct mpu3050_sensor *sensor = i2c_get_clientdata(client);
+
+	pm_runtime_disable(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+
+	free_irq(client->irq, sensor);
+	input_unregister_device(sensor->idev);
+	kfree(sensor);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ *	mpu3050_suspend		-	called on device suspend
+ *	@dev: device being suspended
+ *
+ *	Put the device into sleep mode before we suspend the machine.
+ */
+static int mpu3050_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	mpu3050_set_power_mode(client, 0);
+
+	return 0;
+}
+
+/**
+ *	mpu3050_resume		-	called on device resume
+ *	@dev: device being resumed
+ *
+ *	Put the device into powered mode on resume.
+ */
+static int mpu3050_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	mpu3050_set_power_mode(client, 1);
+	msleep(100);  /* wait for gyro chip resume */
+
+	return 0;
+}
+#endif
+
+static UNIVERSAL_DEV_PM_OPS(mpu3050_pm, mpu3050_suspend, mpu3050_resume, NULL);
+
+static const struct i2c_device_id mpu3050_ids[] = {
+	{ "mpu3050", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, mpu3050_ids);
+
+static const struct of_device_id mpu3050_of_match[] = {
+	{ .compatible = "invn,mpu3050", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, mpu3050_of_match);
+
+static struct i2c_driver mpu3050_i2c_driver = {
+	.driver	= {
+		.name	= "mpu3050",
+		.pm	= &mpu3050_pm,
+		.of_match_table = mpu3050_of_match,
+	},
+	.probe		= mpu3050_probe,
+	.remove		= mpu3050_remove,
+	.id_table	= mpu3050_ids,
+};
+
+module_i2c_driver(mpu3050_i2c_driver);
+
+MODULE_AUTHOR("Wistron Corp.");
+MODULE_DESCRIPTION("MPU3050 Tri-axis gyroscope driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c
new file mode 100644
index 0000000..1e1baed
--- /dev/null
+++ b/drivers/input/misc/palmas-pwrbutton.c
@@ -0,0 +1,329 @@
+/*
+ * Texas Instruments' Palmas Power Button Input Driver
+ *
+ * Copyright (C) 2012-2014 Texas Instruments Incorporated - http://www.ti.com/
+ *	Girish S Ghongdemath
+ *	Nishanth Menon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/palmas.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define PALMAS_LPK_TIME_MASK		0x0c
+#define PALMAS_PWRON_DEBOUNCE_MASK	0x03
+#define PALMAS_PWR_KEY_Q_TIME_MS	20
+
+/**
+ * struct palmas_pwron - Palmas power on data
+ * @palmas:		pointer to palmas device
+ * @input_dev:		pointer to input device
+ * @input_work:		work for detecting release of key
+ * @irq:		irq that we are hooked on to
+ */
+struct palmas_pwron {
+	struct palmas *palmas;
+	struct input_dev *input_dev;
+	struct delayed_work input_work;
+	int irq;
+};
+
+/**
+ * struct palmas_pwron_config - configuration of palmas power on
+ * @long_press_time_val:	value for long press h/w shutdown event
+ * @pwron_debounce_val:		value for debounce of power button
+ */
+struct palmas_pwron_config {
+	u8 long_press_time_val;
+	u8 pwron_debounce_val;
+};
+
+/**
+ * palmas_power_button_work() - Detects the button release event
+ * @work:	work item to detect button release
+ */
+static void palmas_power_button_work(struct work_struct *work)
+{
+	struct palmas_pwron *pwron = container_of(work,
+						  struct palmas_pwron,
+						  input_work.work);
+	struct input_dev *input_dev = pwron->input_dev;
+	unsigned int reg;
+	int error;
+
+	error = palmas_read(pwron->palmas, PALMAS_INTERRUPT_BASE,
+			    PALMAS_INT1_LINE_STATE, &reg);
+	if (error) {
+		dev_err(input_dev->dev.parent,
+			"Cannot read palmas PWRON status: %d\n", error);
+	} else if (reg & BIT(1)) {
+		/* The button is released, report event. */
+		input_report_key(input_dev, KEY_POWER, 0);
+		input_sync(input_dev);
+	} else {
+		/* The button is still depressed, keep checking. */
+		schedule_delayed_work(&pwron->input_work,
+				msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS));
+	}
+}
+
+/**
+ * pwron_irq() - button press isr
+ * @irq:		irq
+ * @palmas_pwron:	pwron struct
+ *
+ * Return: IRQ_HANDLED
+ */
+static irqreturn_t pwron_irq(int irq, void *palmas_pwron)
+{
+	struct palmas_pwron *pwron = palmas_pwron;
+	struct input_dev *input_dev = pwron->input_dev;
+
+	input_report_key(input_dev, KEY_POWER, 1);
+	pm_wakeup_event(input_dev->dev.parent, 0);
+	input_sync(input_dev);
+
+	mod_delayed_work(system_wq, &pwron->input_work,
+			 msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS));
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * palmas_pwron_params_ofinit() - device tree parameter parser
+ * @dev:	palmas button device
+ * @config:	configuration params that this fills up
+ */
+static void palmas_pwron_params_ofinit(struct device *dev,
+				       struct palmas_pwron_config *config)
+{
+	struct device_node *np;
+	u32 val;
+	int i, error;
+	u8 lpk_times[] = { 6, 8, 10, 12 };
+	int pwr_on_deb_ms[] = { 15, 100, 500, 1000 };
+
+	memset(config, 0, sizeof(*config));
+
+	/* Default config parameters */
+	config->long_press_time_val = ARRAY_SIZE(lpk_times) - 1;
+
+	np = dev->of_node;
+	if (!np)
+		return;
+
+	error = of_property_read_u32(np, "ti,palmas-long-press-seconds", &val);
+	if (!error) {
+		for (i = 0; i < ARRAY_SIZE(lpk_times); i++) {
+			if (val <= lpk_times[i]) {
+				config->long_press_time_val = i;
+				break;
+			}
+		}
+	}
+
+	error = of_property_read_u32(np,
+				     "ti,palmas-pwron-debounce-milli-seconds",
+				     &val);
+	if (!error) {
+		for (i = 0; i < ARRAY_SIZE(pwr_on_deb_ms); i++) {
+			if (val <= pwr_on_deb_ms[i]) {
+				config->pwron_debounce_val = i;
+				break;
+			}
+		}
+	}
+
+	dev_info(dev, "h/w controlled shutdown duration=%d seconds\n",
+		 lpk_times[config->long_press_time_val]);
+}
+
+/**
+ * palmas_pwron_probe() - probe
+ * @pdev:	platform device for the button
+ *
+ * Return: 0 for successful probe else appropriate error
+ */
+static int palmas_pwron_probe(struct platform_device *pdev)
+{
+	struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct input_dev *input_dev;
+	struct palmas_pwron *pwron;
+	struct palmas_pwron_config config;
+	int val;
+	int error;
+
+	palmas_pwron_params_ofinit(dev, &config);
+
+	pwron = kzalloc(sizeof(*pwron), GFP_KERNEL);
+	if (!pwron)
+		return -ENOMEM;
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(dev, "Can't allocate power button\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	input_dev->name = "palmas_pwron";
+	input_dev->phys = "palmas_pwron/input0";
+	input_dev->dev.parent = dev;
+
+	input_set_capability(input_dev, EV_KEY, KEY_POWER);
+
+	/*
+	 * Setup default hardware shutdown option (long key press)
+	 * and debounce.
+	 */
+	val = config.long_press_time_val << __ffs(PALMAS_LPK_TIME_MASK);
+	val |= config.pwron_debounce_val << __ffs(PALMAS_PWRON_DEBOUNCE_MASK);
+	error = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
+				   PALMAS_LONG_PRESS_KEY,
+				   PALMAS_LPK_TIME_MASK |
+					PALMAS_PWRON_DEBOUNCE_MASK,
+				   val);
+	if (error) {
+		dev_err(dev, "LONG_PRESS_KEY_UPDATE failed: %d\n", error);
+		goto err_free_input;
+	}
+
+	pwron->palmas = palmas;
+	pwron->input_dev = input_dev;
+
+	INIT_DELAYED_WORK(&pwron->input_work, palmas_power_button_work);
+
+	pwron->irq = platform_get_irq(pdev, 0);
+	error = request_threaded_irq(pwron->irq, NULL, pwron_irq,
+				     IRQF_TRIGGER_HIGH |
+					IRQF_TRIGGER_LOW |
+					IRQF_ONESHOT,
+				     dev_name(dev), pwron);
+	if (error) {
+		dev_err(dev, "Can't get IRQ for pwron: %d\n", error);
+		goto err_free_input;
+	}
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(dev, "Can't register power button: %d\n", error);
+		goto err_free_irq;
+	}
+
+	platform_set_drvdata(pdev, pwron);
+	device_init_wakeup(dev, true);
+
+	return 0;
+
+err_free_irq:
+	cancel_delayed_work_sync(&pwron->input_work);
+	free_irq(pwron->irq, pwron);
+err_free_input:
+	input_free_device(input_dev);
+err_free_mem:
+	kfree(pwron);
+	return error;
+}
+
+/**
+ * palmas_pwron_remove() - Cleanup on removal
+ * @pdev:	platform device for the button
+ *
+ * Return: 0
+ */
+static int palmas_pwron_remove(struct platform_device *pdev)
+{
+	struct palmas_pwron *pwron = platform_get_drvdata(pdev);
+
+	free_irq(pwron->irq, pwron);
+	cancel_delayed_work_sync(&pwron->input_work);
+
+	input_unregister_device(pwron->input_dev);
+	kfree(pwron);
+
+	return 0;
+}
+
+/**
+ * palmas_pwron_suspend() - suspend handler
+ * @dev:	power button device
+ *
+ * Cancel all pending work items for the power button, setup irq for wakeup
+ *
+ * Return: 0
+ */
+static int __maybe_unused palmas_pwron_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct palmas_pwron *pwron = platform_get_drvdata(pdev);
+
+	cancel_delayed_work_sync(&pwron->input_work);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(pwron->irq);
+
+	return 0;
+}
+
+/**
+ * palmas_pwron_resume() - resume handler
+ * @dev:	power button device
+ *
+ * Just disable the wakeup capability of irq here.
+ *
+ * Return: 0
+ */
+static int __maybe_unused palmas_pwron_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct palmas_pwron *pwron = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(pwron->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(palmas_pwron_pm,
+			 palmas_pwron_suspend, palmas_pwron_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_palmas_pwr_match[] = {
+	{ .compatible = "ti,palmas-pwrbutton" },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, of_palmas_pwr_match);
+#endif
+
+static struct platform_driver palmas_pwron_driver = {
+	.probe	= palmas_pwron_probe,
+	.remove	= palmas_pwron_remove,
+	.driver	= {
+		.name	= "palmas_pwrbutton",
+		.of_match_table = of_match_ptr(of_palmas_pwr_match),
+		.pm	= &palmas_pwron_pm,
+	},
+};
+module_platform_driver(palmas_pwron_driver);
+
+MODULE_ALIAS("platform:palmas-pwrbutton");
+MODULE_DESCRIPTION("Palmas Power Button");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Texas Instruments Inc.");
diff --git a/drivers/input/misc/pcap_keys.c b/drivers/input/misc/pcap_keys.c
new file mode 100644
index 0000000..3b81daf
--- /dev/null
+++ b/drivers/input/misc/pcap_keys.c
@@ -0,0 +1,131 @@
+/*
+ *  Input driver for PCAP events:
+ *   * Power key
+ *   * Headphone button
+ *
+ *  Copyright (c) 2008,2009 Ilya Petrov <ilya.muromec@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/mfd/ezx-pcap.h>
+#include <linux/slab.h>
+
+struct pcap_keys {
+	struct pcap_chip *pcap;
+	struct input_dev *input;
+};
+
+/* PCAP2 interrupts us on keypress */
+static irqreturn_t pcap_keys_handler(int irq, void *_pcap_keys)
+{
+	struct pcap_keys *pcap_keys = _pcap_keys;
+	int pirq = irq_to_pcap(pcap_keys->pcap, irq);
+	u32 pstat;
+
+	ezx_pcap_read(pcap_keys->pcap, PCAP_REG_PSTAT, &pstat);
+	pstat &= 1 << pirq;
+
+	switch (pirq) {
+	case PCAP_IRQ_ONOFF:
+		input_report_key(pcap_keys->input, KEY_POWER, !pstat);
+		break;
+	case PCAP_IRQ_MIC:
+		input_report_key(pcap_keys->input, KEY_HP, !pstat);
+		break;
+	}
+
+	input_sync(pcap_keys->input);
+
+	return IRQ_HANDLED;
+}
+
+static int pcap_keys_probe(struct platform_device *pdev)
+{
+	int err = -ENOMEM;
+	struct pcap_keys *pcap_keys;
+	struct input_dev *input_dev;
+
+	pcap_keys = kmalloc(sizeof(struct pcap_keys), GFP_KERNEL);
+	if (!pcap_keys)
+		return err;
+
+	pcap_keys->pcap = dev_get_drvdata(pdev->dev.parent);
+
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		goto fail;
+
+	pcap_keys->input = input_dev;
+
+	platform_set_drvdata(pdev, pcap_keys);
+	input_dev->name = pdev->name;
+	input_dev->phys = "pcap-keys/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->dev.parent = &pdev->dev;
+
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(KEY_POWER, input_dev->keybit);
+	__set_bit(KEY_HP, input_dev->keybit);
+
+	err = input_register_device(input_dev);
+	if (err)
+		goto fail_allocate;
+
+	err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF),
+			pcap_keys_handler, 0, "Power key", pcap_keys);
+	if (err)
+		goto fail_register;
+
+	err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC),
+			pcap_keys_handler, 0, "Headphone button", pcap_keys);
+	if (err)
+		goto fail_pwrkey;
+
+	return 0;
+
+fail_pwrkey:
+	free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys);
+fail_register:
+	input_unregister_device(input_dev);
+	goto fail;
+fail_allocate:
+	input_free_device(input_dev);
+fail:
+	kfree(pcap_keys);
+	return err;
+}
+
+static int pcap_keys_remove(struct platform_device *pdev)
+{
+	struct pcap_keys *pcap_keys = platform_get_drvdata(pdev);
+
+	free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys);
+	free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC), pcap_keys);
+
+	input_unregister_device(pcap_keys->input);
+	kfree(pcap_keys);
+
+	return 0;
+}
+
+static struct platform_driver pcap_keys_device_driver = {
+	.probe		= pcap_keys_probe,
+	.remove		= pcap_keys_remove,
+	.driver		= {
+		.name	= "pcap-keys",
+	}
+};
+module_platform_driver(pcap_keys_device_driver);
+
+MODULE_DESCRIPTION("Motorola PCAP2 input events driver");
+MODULE_AUTHOR("Ilya Petrov <ilya.muromec@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcap_keys");
diff --git a/drivers/input/misc/pcf50633-input.c b/drivers/input/misc/pcf50633-input.c
new file mode 100644
index 0000000..db92f4f
--- /dev/null
+++ b/drivers/input/misc/pcf50633-input.c
@@ -0,0 +1,120 @@
+/* NXP PCF50633 Input Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ * Broken down from monstrous PCF50633 driver mainly by
+ * Harald Welte, Andy Green and Werner Almesberger
+ *
+ *  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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/pcf50633/core.h>
+
+#define PCF50633_OOCSTAT_ONKEY	0x01
+#define PCF50633_REG_OOCSTAT	0x12
+#define PCF50633_REG_OOCMODE	0x10
+
+struct pcf50633_input {
+	struct pcf50633 *pcf;
+	struct input_dev *input_dev;
+};
+
+static void
+pcf50633_input_irq(int irq, void *data)
+{
+	struct pcf50633_input *input;
+	int onkey_released;
+
+	input = data;
+
+	/* We report only one event depending on the key press status */
+	onkey_released = pcf50633_reg_read(input->pcf, PCF50633_REG_OOCSTAT)
+						& PCF50633_OOCSTAT_ONKEY;
+
+	if (irq == PCF50633_IRQ_ONKEYF && !onkey_released)
+		input_report_key(input->input_dev, KEY_POWER, 1);
+	else if (irq == PCF50633_IRQ_ONKEYR && onkey_released)
+		input_report_key(input->input_dev, KEY_POWER, 0);
+
+	input_sync(input->input_dev);
+}
+
+static int pcf50633_input_probe(struct platform_device *pdev)
+{
+	struct pcf50633_input *input;
+	struct input_dev *input_dev;
+	int ret;
+
+
+	input = kzalloc(sizeof(*input), GFP_KERNEL);
+	if (!input)
+		return -ENOMEM;
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		kfree(input);
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, input);
+	input->pcf = dev_to_pcf50633(pdev->dev.parent);
+	input->input_dev = input_dev;
+
+	input_dev->name = "PCF50633 PMU events";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_PWR);
+	set_bit(KEY_POWER, input_dev->keybit);
+
+	ret = input_register_device(input_dev);
+	if (ret) {
+		input_free_device(input_dev);
+		kfree(input);
+		return ret;
+	}
+	pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYR,
+				pcf50633_input_irq, input);
+	pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYF,
+				pcf50633_input_irq, input);
+
+	return 0;
+}
+
+static int pcf50633_input_remove(struct platform_device *pdev)
+{
+	struct pcf50633_input *input  = platform_get_drvdata(pdev);
+
+	pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYR);
+	pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYF);
+
+	input_unregister_device(input->input_dev);
+	kfree(input);
+
+	return 0;
+}
+
+static struct platform_driver pcf50633_input_driver = {
+	.driver = {
+		.name = "pcf50633-input",
+	},
+	.probe = pcf50633_input_probe,
+	.remove = pcf50633_input_remove,
+};
+module_platform_driver(pcf50633_input_driver);
+
+MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
+MODULE_DESCRIPTION("PCF50633 input driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcf50633-input");
diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c
new file mode 100644
index 0000000..4abdf1e
--- /dev/null
+++ b/drivers/input/misc/pcf8574_keypad.c
@@ -0,0 +1,224 @@
+/*
+ * Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander
+ *
+ * Copyright 2005-2008 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#define DRV_NAME "pcf8574_keypad"
+
+static const unsigned char pcf8574_kp_btncode[] = {
+	[0] = KEY_RESERVED,
+	[1] = KEY_ENTER,
+	[2] = KEY_BACKSLASH,
+	[3] = KEY_0,
+	[4] = KEY_RIGHTBRACE,
+	[5] = KEY_C,
+	[6] = KEY_9,
+	[7] = KEY_8,
+	[8] = KEY_7,
+	[9] = KEY_B,
+	[10] = KEY_6,
+	[11] = KEY_5,
+	[12] = KEY_4,
+	[13] = KEY_A,
+	[14] = KEY_3,
+	[15] = KEY_2,
+	[16] = KEY_1
+};
+
+struct kp_data {
+	unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)];
+	struct input_dev *idev;
+	struct i2c_client *client;
+	char name[64];
+	char phys[32];
+	unsigned char laststate;
+};
+
+static short read_state(struct kp_data *lp)
+{
+	unsigned char x, y, a, b;
+
+	i2c_smbus_write_byte(lp->client, 240);
+	x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4));
+
+	i2c_smbus_write_byte(lp->client, 15);
+	y = 0xF & (~i2c_smbus_read_byte(lp->client));
+
+	for (a = 0; x > 0; a++)
+		x = x >> 1;
+	for (b = 0; y > 0; b++)
+		y = y >> 1;
+
+	return ((a - 1) * 4) + b;
+}
+
+static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id)
+{
+	struct kp_data *lp = dev_id;
+	unsigned char nextstate = read_state(lp);
+
+	if (lp->laststate != nextstate) {
+		int key_down = nextstate < ARRAY_SIZE(lp->btncode);
+		unsigned short keycode = key_down ?
+			lp->btncode[nextstate] : lp->btncode[lp->laststate];
+
+		input_report_key(lp->idev, keycode, key_down);
+		input_sync(lp->idev);
+
+		lp->laststate = nextstate;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	int i, ret;
+	struct input_dev *idev;
+	struct kp_data *lp;
+
+	if (i2c_smbus_write_byte(client, 240) < 0) {
+		dev_err(&client->dev, "probe: write fail\n");
+		return -ENODEV;
+	}
+
+	lp = kzalloc(sizeof(*lp), GFP_KERNEL);
+	if (!lp)
+		return -ENOMEM;
+
+	idev = input_allocate_device();
+	if (!idev) {
+		dev_err(&client->dev, "Can't allocate input device\n");
+		ret = -ENOMEM;
+		goto fail_allocate;
+	}
+
+	lp->idev = idev;
+	lp->client = client;
+
+	idev->evbit[0] = BIT_MASK(EV_KEY);
+	idev->keycode = lp->btncode;
+	idev->keycodesize = sizeof(lp->btncode[0]);
+	idev->keycodemax = ARRAY_SIZE(lp->btncode);
+
+	for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {
+		if (lp->btncode[i] <= KEY_MAX) {
+			lp->btncode[i] = pcf8574_kp_btncode[i];
+			__set_bit(lp->btncode[i], idev->keybit);
+		}
+	}
+	__clear_bit(KEY_RESERVED, idev->keybit);
+
+	sprintf(lp->name, DRV_NAME);
+	sprintf(lp->phys, "kp_data/input0");
+
+	idev->name = lp->name;
+	idev->phys = lp->phys;
+	idev->id.bustype = BUS_I2C;
+	idev->id.vendor = 0x0001;
+	idev->id.product = 0x0001;
+	idev->id.version = 0x0100;
+
+	lp->laststate = read_state(lp);
+
+	ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler,
+				   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+				   DRV_NAME, lp);
+	if (ret) {
+		dev_err(&client->dev, "IRQ %d is not free\n", client->irq);
+		goto fail_free_device;
+	}
+
+	ret = input_register_device(idev);
+	if (ret) {
+		dev_err(&client->dev, "input_register_device() failed\n");
+		goto fail_free_irq;
+	}
+
+	i2c_set_clientdata(client, lp);
+	return 0;
+
+ fail_free_irq:
+	free_irq(client->irq, lp);
+ fail_free_device:
+	input_free_device(idev);
+ fail_allocate:
+	kfree(lp);
+
+	return ret;
+}
+
+static int pcf8574_kp_remove(struct i2c_client *client)
+{
+	struct kp_data *lp = i2c_get_clientdata(client);
+
+	free_irq(client->irq, lp);
+
+	input_unregister_device(lp->idev);
+	kfree(lp);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int pcf8574_kp_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static int pcf8574_kp_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	disable_irq(client->irq);
+
+	return 0;
+}
+
+static const struct dev_pm_ops pcf8574_kp_pm_ops = {
+	.suspend	= pcf8574_kp_suspend,
+	.resume		= pcf8574_kp_resume,
+};
+
+#else
+# define pcf8574_kp_resume  NULL
+# define pcf8574_kp_suspend NULL
+#endif
+
+static const struct i2c_device_id pcf8574_kp_id[] = {
+	{ DRV_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
+
+static struct i2c_driver pcf8574_kp_driver = {
+	.driver = {
+		.name  = DRV_NAME,
+#ifdef CONFIG_PM
+		.pm = &pcf8574_kp_pm_ops,
+#endif
+	},
+	.probe    = pcf8574_kp_probe,
+	.remove   = pcf8574_kp_remove,
+	.id_table = pcf8574_kp_id,
+};
+
+module_i2c_driver(pcf8574_kp_driver);
+
+MODULE_AUTHOR("Michael Hennerich");
+MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c
new file mode 100644
index 0000000..72b1fc3
--- /dev/null
+++ b/drivers/input/misc/pcspkr.c
@@ -0,0 +1,135 @@
+/*
+ *  PC Speaker beeper driver for Linux
+ *
+ *  Copyright (c) 2002 Vojtech Pavlik
+ *  Copyright (c) 1992 Orest Zborowski
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i8253.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/timex.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("PC Speaker beeper driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcspkr");
+
+static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	unsigned int count = 0;
+	unsigned long flags;
+
+	if (type != EV_SND)
+		return -1;
+
+	switch (code) {
+		case SND_BELL: if (value) value = 1000;
+		case SND_TONE: break;
+		default: return -1;
+	}
+
+	if (value > 20 && value < 32767)
+		count = PIT_TICK_RATE / value;
+
+	raw_spin_lock_irqsave(&i8253_lock, flags);
+
+	if (count) {
+		/* set command for counter 2, 2 byte write */
+		outb_p(0xB6, 0x43);
+		/* select desired HZ */
+		outb_p(count & 0xff, 0x42);
+		outb((count >> 8) & 0xff, 0x42);
+		/* enable counter 2 */
+		outb_p(inb_p(0x61) | 3, 0x61);
+	} else {
+		/* disable counter 2 */
+		outb(inb_p(0x61) & 0xFC, 0x61);
+	}
+
+	raw_spin_unlock_irqrestore(&i8253_lock, flags);
+
+	return 0;
+}
+
+static int pcspkr_probe(struct platform_device *dev)
+{
+	struct input_dev *pcspkr_dev;
+	int err;
+
+	pcspkr_dev = input_allocate_device();
+	if (!pcspkr_dev)
+		return -ENOMEM;
+
+	pcspkr_dev->name = "PC Speaker";
+	pcspkr_dev->phys = "isa0061/input0";
+	pcspkr_dev->id.bustype = BUS_ISA;
+	pcspkr_dev->id.vendor = 0x001f;
+	pcspkr_dev->id.product = 0x0001;
+	pcspkr_dev->id.version = 0x0100;
+	pcspkr_dev->dev.parent = &dev->dev;
+
+	pcspkr_dev->evbit[0] = BIT_MASK(EV_SND);
+	pcspkr_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+	pcspkr_dev->event = pcspkr_event;
+
+	err = input_register_device(pcspkr_dev);
+	if (err) {
+		input_free_device(pcspkr_dev);
+		return err;
+	}
+
+	platform_set_drvdata(dev, pcspkr_dev);
+
+	return 0;
+}
+
+static int pcspkr_remove(struct platform_device *dev)
+{
+	struct input_dev *pcspkr_dev = platform_get_drvdata(dev);
+
+	input_unregister_device(pcspkr_dev);
+	/* turn off the speaker */
+	pcspkr_event(NULL, EV_SND, SND_BELL, 0);
+
+	return 0;
+}
+
+static int pcspkr_suspend(struct device *dev)
+{
+	pcspkr_event(NULL, EV_SND, SND_BELL, 0);
+
+	return 0;
+}
+
+static void pcspkr_shutdown(struct platform_device *dev)
+{
+	/* turn off the speaker */
+	pcspkr_event(NULL, EV_SND, SND_BELL, 0);
+}
+
+static const struct dev_pm_ops pcspkr_pm_ops = {
+	.suspend = pcspkr_suspend,
+};
+
+static struct platform_driver pcspkr_platform_driver = {
+	.driver		= {
+		.name	= "pcspkr",
+		.pm	= &pcspkr_pm_ops,
+	},
+	.probe		= pcspkr_probe,
+	.remove		= pcspkr_remove,
+	.shutdown	= pcspkr_shutdown,
+};
+module_platform_driver(pcspkr_platform_driver);
+
diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c
new file mode 100644
index 0000000..e317b75
--- /dev/null
+++ b/drivers/input/misc/pm8941-pwrkey.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2014, Sony Mobile Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+
+#define PON_REV2			0x01
+
+#define PON_RT_STS			0x10
+#define  PON_KPDPWR_N_SET		BIT(0)
+
+#define PON_PS_HOLD_RST_CTL		0x5a
+#define PON_PS_HOLD_RST_CTL2		0x5b
+#define  PON_PS_HOLD_ENABLE		BIT(7)
+#define  PON_PS_HOLD_TYPE_MASK		0x0f
+#define  PON_PS_HOLD_TYPE_SHUTDOWN	4
+#define  PON_PS_HOLD_TYPE_HARD_RESET	7
+
+#define PON_PULL_CTL			0x70
+#define  PON_KPDPWR_PULL_UP		BIT(1)
+
+#define PON_DBC_CTL			0x71
+#define  PON_DBC_DELAY_MASK		0x7
+
+
+struct pm8941_pwrkey {
+	struct device *dev;
+	int irq;
+	u32 baseaddr;
+	struct regmap *regmap;
+	struct input_dev *input;
+
+	unsigned int revision;
+	struct notifier_block reboot_notifier;
+};
+
+static int pm8941_reboot_notify(struct notifier_block *nb,
+				unsigned long code, void *unused)
+{
+	struct pm8941_pwrkey *pwrkey = container_of(nb, struct pm8941_pwrkey,
+						    reboot_notifier);
+	unsigned int enable_reg;
+	unsigned int reset_type;
+	int error;
+
+	/* PMICs with revision 0 have the enable bit in same register as ctrl */
+	if (pwrkey->revision == 0)
+		enable_reg = PON_PS_HOLD_RST_CTL;
+	else
+		enable_reg = PON_PS_HOLD_RST_CTL2;
+
+	error = regmap_update_bits(pwrkey->regmap,
+				   pwrkey->baseaddr + enable_reg,
+				   PON_PS_HOLD_ENABLE,
+				   0);
+	if (error)
+		dev_err(pwrkey->dev,
+			"unable to clear ps hold reset enable: %d\n",
+			error);
+
+	/*
+	 * Updates of PON_PS_HOLD_ENABLE requires 3 sleep cycles between
+	 * writes.
+	 */
+	usleep_range(100, 1000);
+
+	switch (code) {
+	case SYS_HALT:
+	case SYS_POWER_OFF:
+		reset_type = PON_PS_HOLD_TYPE_SHUTDOWN;
+		break;
+	case SYS_RESTART:
+	default:
+		reset_type = PON_PS_HOLD_TYPE_HARD_RESET;
+		break;
+	}
+
+	error = regmap_update_bits(pwrkey->regmap,
+				   pwrkey->baseaddr + PON_PS_HOLD_RST_CTL,
+				   PON_PS_HOLD_TYPE_MASK,
+				   reset_type);
+	if (error)
+		dev_err(pwrkey->dev, "unable to set ps hold reset type: %d\n",
+			error);
+
+	error = regmap_update_bits(pwrkey->regmap,
+				   pwrkey->baseaddr + enable_reg,
+				   PON_PS_HOLD_ENABLE,
+				   PON_PS_HOLD_ENABLE);
+	if (error)
+		dev_err(pwrkey->dev, "unable to re-set enable: %d\n", error);
+
+	return NOTIFY_DONE;
+}
+
+static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
+{
+	struct pm8941_pwrkey *pwrkey = _data;
+	unsigned int sts;
+	int error;
+
+	error = regmap_read(pwrkey->regmap,
+			    pwrkey->baseaddr + PON_RT_STS, &sts);
+	if (error)
+		return IRQ_HANDLED;
+
+	input_report_key(pwrkey->input, KEY_POWER, !!(sts & PON_KPDPWR_N_SET));
+	input_sync(pwrkey->input);
+
+	return IRQ_HANDLED;
+}
+
+static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev)
+{
+	struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(pwrkey->irq);
+
+	return 0;
+}
+
+static int __maybe_unused pm8941_pwrkey_resume(struct device *dev)
+{
+	struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(pwrkey->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops,
+			 pm8941_pwrkey_suspend, pm8941_pwrkey_resume);
+
+static int pm8941_pwrkey_probe(struct platform_device *pdev)
+{
+	struct pm8941_pwrkey *pwrkey;
+	bool pull_up;
+	u32 req_delay;
+	int error;
+
+	if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay))
+		req_delay = 15625;
+
+	if (req_delay > 2000000 || req_delay == 0) {
+		dev_err(&pdev->dev, "invalid debounce time: %u\n", req_delay);
+		return -EINVAL;
+	}
+
+	pull_up = of_property_read_bool(pdev->dev.of_node, "bias-pull-up");
+
+	pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL);
+	if (!pwrkey)
+		return -ENOMEM;
+
+	pwrkey->dev = &pdev->dev;
+
+	pwrkey->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!pwrkey->regmap) {
+		dev_err(&pdev->dev, "failed to locate regmap\n");
+		return -ENODEV;
+	}
+
+	pwrkey->irq = platform_get_irq(pdev, 0);
+	if (pwrkey->irq < 0) {
+		dev_err(&pdev->dev, "failed to get irq\n");
+		return pwrkey->irq;
+	}
+
+	error = of_property_read_u32(pdev->dev.of_node, "reg",
+				     &pwrkey->baseaddr);
+	if (error)
+		return error;
+
+	error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
+			    &pwrkey->revision);
+	if (error) {
+		dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
+		return error;
+	}
+
+	pwrkey->input = devm_input_allocate_device(&pdev->dev);
+	if (!pwrkey->input) {
+		dev_dbg(&pdev->dev, "unable to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	input_set_capability(pwrkey->input, EV_KEY, KEY_POWER);
+
+	pwrkey->input->name = "pm8941_pwrkey";
+	pwrkey->input->phys = "pm8941_pwrkey/input0";
+
+	req_delay = (req_delay << 6) / USEC_PER_SEC;
+	req_delay = ilog2(req_delay);
+
+	error = regmap_update_bits(pwrkey->regmap,
+				   pwrkey->baseaddr + PON_DBC_CTL,
+				   PON_DBC_DELAY_MASK,
+				   req_delay);
+	if (error) {
+		dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
+		return error;
+	}
+
+	error = regmap_update_bits(pwrkey->regmap,
+				   pwrkey->baseaddr + PON_PULL_CTL,
+				   PON_KPDPWR_PULL_UP,
+				   pull_up ? PON_KPDPWR_PULL_UP : 0);
+	if (error) {
+		dev_err(&pdev->dev, "failed to set pull: %d\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&pdev->dev, pwrkey->irq,
+					  NULL, pm8941_pwrkey_irq,
+					  IRQF_ONESHOT,
+					  "pm8941_pwrkey", pwrkey);
+	if (error) {
+		dev_err(&pdev->dev, "failed requesting IRQ: %d\n", error);
+		return error;
+	}
+
+	error = input_register_device(pwrkey->input);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register input device: %d\n",
+			error);
+		return error;
+	}
+
+	pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify,
+	error = register_reboot_notifier(&pwrkey->reboot_notifier);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register reboot notifier: %d\n",
+			error);
+		return error;
+	}
+
+	platform_set_drvdata(pdev, pwrkey);
+	device_init_wakeup(&pdev->dev, 1);
+
+	return 0;
+}
+
+static int pm8941_pwrkey_remove(struct platform_device *pdev)
+{
+	struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	unregister_reboot_notifier(&pwrkey->reboot_notifier);
+
+	return 0;
+}
+
+static const struct of_device_id pm8941_pwr_key_id_table[] = {
+	{ .compatible = "qcom,pm8941-pwrkey" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table);
+
+static struct platform_driver pm8941_pwrkey_driver = {
+	.probe = pm8941_pwrkey_probe,
+	.remove = pm8941_pwrkey_remove,
+	.driver = {
+		.name = "pm8941-pwrkey",
+		.pm = &pm8941_pwr_key_pm_ops,
+		.of_match_table = of_match_ptr(pm8941_pwr_key_id_table),
+	},
+};
+module_platform_driver(pm8941_pwrkey_driver);
+
+MODULE_DESCRIPTION("PM8941 Power Key driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c
new file mode 100644
index 0000000..5113877
--- /dev/null
+++ b/drivers/input/misc/pm8xxx-vibrator.c
@@ -0,0 +1,234 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+
+#define VIB_DRV			0x4A
+
+#define VIB_DRV_SEL_MASK	0xf8
+#define VIB_DRV_SEL_SHIFT	0x03
+#define VIB_DRV_EN_MANUAL_MASK	0xfc
+
+#define VIB_MAX_LEVEL_mV	(3100)
+#define VIB_MIN_LEVEL_mV	(1200)
+#define VIB_MAX_LEVELS		(VIB_MAX_LEVEL_mV - VIB_MIN_LEVEL_mV)
+
+#define MAX_FF_SPEED		0xff
+
+/**
+ * struct pm8xxx_vib - structure to hold vibrator data
+ * @vib_input_dev: input device supporting force feedback
+ * @work: work structure to set the vibration parameters
+ * @regmap: regmap for register read/write
+ * @speed: speed of vibration set from userland
+ * @active: state of vibrator
+ * @level: level of vibration to set in the chip
+ * @reg_vib_drv: VIB_DRV register value
+ */
+struct pm8xxx_vib {
+	struct input_dev *vib_input_dev;
+	struct work_struct work;
+	struct regmap *regmap;
+	int speed;
+	int level;
+	bool active;
+	u8  reg_vib_drv;
+};
+
+/**
+ * pm8xxx_vib_set - handler to start/stop vibration
+ * @vib: pointer to vibrator structure
+ * @on: state to set
+ */
+static int pm8xxx_vib_set(struct pm8xxx_vib *vib, bool on)
+{
+	int rc;
+	unsigned int val = vib->reg_vib_drv;
+
+	if (on)
+		val |= ((vib->level << VIB_DRV_SEL_SHIFT) & VIB_DRV_SEL_MASK);
+	else
+		val &= ~VIB_DRV_SEL_MASK;
+
+	rc = regmap_write(vib->regmap, VIB_DRV, val);
+	if (rc < 0)
+		return rc;
+
+	vib->reg_vib_drv = val;
+	return 0;
+}
+
+/**
+ * pm8xxx_work_handler - worker to set vibration level
+ * @work: pointer to work_struct
+ */
+static void pm8xxx_work_handler(struct work_struct *work)
+{
+	struct pm8xxx_vib *vib = container_of(work, struct pm8xxx_vib, work);
+	int rc;
+	unsigned int val;
+
+	rc = regmap_read(vib->regmap, VIB_DRV, &val);
+	if (rc < 0)
+		return;
+
+	/*
+	 * pmic vibrator supports voltage ranges from 1.2 to 3.1V, so
+	 * scale the level to fit into these ranges.
+	 */
+	if (vib->speed) {
+		vib->active = true;
+		vib->level = ((VIB_MAX_LEVELS * vib->speed) / MAX_FF_SPEED) +
+						VIB_MIN_LEVEL_mV;
+		vib->level /= 100;
+	} else {
+		vib->active = false;
+		vib->level = VIB_MIN_LEVEL_mV / 100;
+	}
+
+	pm8xxx_vib_set(vib, vib->active);
+}
+
+/**
+ * pm8xxx_vib_close - callback of input close callback
+ * @dev: input device pointer
+ *
+ * Turns off the vibrator.
+ */
+static void pm8xxx_vib_close(struct input_dev *dev)
+{
+	struct pm8xxx_vib *vib = input_get_drvdata(dev);
+
+	cancel_work_sync(&vib->work);
+	if (vib->active)
+		pm8xxx_vib_set(vib, false);
+}
+
+/**
+ * pm8xxx_vib_play_effect - function to handle vib effects.
+ * @dev: input device pointer
+ * @data: data of effect
+ * @effect: effect to play
+ *
+ * Currently this driver supports only rumble effects.
+ */
+static int pm8xxx_vib_play_effect(struct input_dev *dev, void *data,
+				  struct ff_effect *effect)
+{
+	struct pm8xxx_vib *vib = input_get_drvdata(dev);
+
+	vib->speed = effect->u.rumble.strong_magnitude >> 8;
+	if (!vib->speed)
+		vib->speed = effect->u.rumble.weak_magnitude >> 9;
+
+	schedule_work(&vib->work);
+
+	return 0;
+}
+
+static int pm8xxx_vib_probe(struct platform_device *pdev)
+{
+	struct pm8xxx_vib *vib;
+	struct input_dev *input_dev;
+	int error;
+	unsigned int val;
+
+	vib = devm_kzalloc(&pdev->dev, sizeof(*vib), GFP_KERNEL);
+	if (!vib)
+		return -ENOMEM;
+
+	vib->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!vib->regmap)
+		return -ENODEV;
+
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!input_dev)
+		return -ENOMEM;
+
+	INIT_WORK(&vib->work, pm8xxx_work_handler);
+	vib->vib_input_dev = input_dev;
+
+	/* operate in manual mode */
+	error = regmap_read(vib->regmap, VIB_DRV, &val);
+	if (error < 0)
+		return error;
+
+	val &= ~VIB_DRV_EN_MANUAL_MASK;
+	error = regmap_write(vib->regmap, VIB_DRV, val);
+	if (error < 0)
+		return error;
+
+	vib->reg_vib_drv = val;
+
+	input_dev->name = "pm8xxx_vib_ffmemless";
+	input_dev->id.version = 1;
+	input_dev->close = pm8xxx_vib_close;
+	input_set_drvdata(input_dev, vib);
+	input_set_capability(vib->vib_input_dev, EV_FF, FF_RUMBLE);
+
+	error = input_ff_create_memless(input_dev, NULL,
+					pm8xxx_vib_play_effect);
+	if (error) {
+		dev_err(&pdev->dev,
+			"couldn't register vibrator as FF device\n");
+		return error;
+	}
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "couldn't register input device\n");
+		return error;
+	}
+
+	platform_set_drvdata(pdev, vib);
+	return 0;
+}
+
+static int __maybe_unused pm8xxx_vib_suspend(struct device *dev)
+{
+	struct pm8xxx_vib *vib = dev_get_drvdata(dev);
+
+	/* Turn off the vibrator */
+	pm8xxx_vib_set(vib, false);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pm8xxx_vib_pm_ops, pm8xxx_vib_suspend, NULL);
+
+static const struct of_device_id pm8xxx_vib_id_table[] = {
+	{ .compatible = "qcom,pm8058-vib" },
+	{ .compatible = "qcom,pm8921-vib" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_vib_id_table);
+
+static struct platform_driver pm8xxx_vib_driver = {
+	.probe		= pm8xxx_vib_probe,
+	.driver		= {
+		.name	= "pm8xxx-vib",
+		.pm	= &pm8xxx_vib_pm_ops,
+		.of_match_table = pm8xxx_vib_id_table,
+	},
+};
+module_platform_driver(pm8xxx_vib_driver);
+
+MODULE_ALIAS("platform:pm8xxx_vib");
+MODULE_DESCRIPTION("PMIC8xxx vibrator driver based on ff-memless framework");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Amy Maloche <amaloche@codeaurora.org>");
diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
new file mode 100644
index 0000000..67aab86
--- /dev/null
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -0,0 +1,470 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/log2.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#define PON_CNTL_1 0x1C
+#define PON_CNTL_PULL_UP BIT(7)
+#define PON_CNTL_TRIG_DELAY_MASK (0x7)
+#define PON_CNTL_1_PULL_UP_EN			0xe0
+#define PON_CNTL_1_USB_PWR_EN			0x10
+#define PON_CNTL_1_WD_EN_RESET			0x08
+
+#define PM8058_SLEEP_CTRL			0x02b
+#define PM8921_SLEEP_CTRL			0x10a
+
+#define SLEEP_CTRL_SMPL_EN_RESET		0x04
+
+/* Regulator master enable addresses */
+#define REG_PM8058_VREG_EN_MSM			0x018
+#define REG_PM8058_VREG_EN_GRP_5_4		0x1c8
+
+/* Regulator control registers for shutdown/reset */
+#define PM8058_S0_CTRL				0x004
+#define PM8058_S1_CTRL				0x005
+#define PM8058_S3_CTRL				0x111
+#define PM8058_L21_CTRL				0x120
+#define PM8058_L22_CTRL				0x121
+
+#define PM8058_REGULATOR_ENABLE_MASK		0x80
+#define PM8058_REGULATOR_ENABLE			0x80
+#define PM8058_REGULATOR_DISABLE		0x00
+#define PM8058_REGULATOR_PULL_DOWN_MASK		0x40
+#define PM8058_REGULATOR_PULL_DOWN_EN		0x40
+
+/* Buck CTRL register */
+#define PM8058_SMPS_LEGACY_VREF_SEL		0x20
+#define PM8058_SMPS_LEGACY_VPROG_MASK		0x1f
+#define PM8058_SMPS_ADVANCED_BAND_MASK		0xC0
+#define PM8058_SMPS_ADVANCED_BAND_SHIFT		6
+#define PM8058_SMPS_ADVANCED_VPROG_MASK		0x3f
+
+/* Buck TEST2 registers for shutdown/reset */
+#define PM8058_S0_TEST2				0x084
+#define PM8058_S1_TEST2				0x085
+#define PM8058_S3_TEST2				0x11a
+
+#define PM8058_REGULATOR_BANK_WRITE		0x80
+#define PM8058_REGULATOR_BANK_MASK		0x70
+#define PM8058_REGULATOR_BANK_SHIFT		4
+#define PM8058_REGULATOR_BANK_SEL(n)	((n) << PM8058_REGULATOR_BANK_SHIFT)
+
+/* Buck TEST2 register bank 1 */
+#define PM8058_SMPS_LEGACY_VLOW_SEL		0x01
+
+/* Buck TEST2 register bank 7 */
+#define PM8058_SMPS_ADVANCED_MODE_MASK		0x02
+#define PM8058_SMPS_ADVANCED_MODE		0x02
+#define PM8058_SMPS_LEGACY_MODE			0x00
+
+/**
+ * struct pmic8xxx_pwrkey - pmic8xxx pwrkey information
+ * @key_press_irq: key press irq number
+ * @regmap: device regmap
+ * @shutdown_fn: shutdown configuration function
+ */
+struct pmic8xxx_pwrkey {
+	int key_press_irq;
+	struct regmap *regmap;
+	int (*shutdown_fn)(struct pmic8xxx_pwrkey *, bool);
+};
+
+static irqreturn_t pwrkey_press_irq(int irq, void *_pwr)
+{
+	struct input_dev *pwr = _pwr;
+
+	input_report_key(pwr, KEY_POWER, 1);
+	input_sync(pwr);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pwrkey_release_irq(int irq, void *_pwr)
+{
+	struct input_dev *pwr = _pwr;
+
+	input_report_key(pwr, KEY_POWER, 0);
+	input_sync(pwr);
+
+	return IRQ_HANDLED;
+}
+
+static int __maybe_unused pmic8xxx_pwrkey_suspend(struct device *dev)
+{
+	struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(pwrkey->key_press_irq);
+
+	return 0;
+}
+
+static int __maybe_unused pmic8xxx_pwrkey_resume(struct device *dev)
+{
+	struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(pwrkey->key_press_irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops,
+		pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume);
+
+static void pmic8xxx_pwrkey_shutdown(struct platform_device *pdev)
+{
+	struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev);
+	int error;
+	u8 mask, val;
+	bool reset = system_state == SYSTEM_RESTART;
+
+	if (pwrkey->shutdown_fn) {
+		error = pwrkey->shutdown_fn(pwrkey, reset);
+		if (error)
+			return;
+	}
+
+	/*
+	 * Select action to perform (reset or shutdown) when PS_HOLD goes low.
+	 * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that
+	 * USB charging is enabled.
+	 */
+	mask = PON_CNTL_1_PULL_UP_EN | PON_CNTL_1_USB_PWR_EN;
+	mask |= PON_CNTL_1_WD_EN_RESET;
+	val = mask;
+	if (!reset)
+		val &= ~PON_CNTL_1_WD_EN_RESET;
+
+	regmap_update_bits(pwrkey->regmap, PON_CNTL_1, mask, val);
+}
+
+/*
+ * Set an SMPS regulator to be disabled in its CTRL register, but enabled
+ * in the master enable register.  Also set it's pull down enable bit.
+ * Take care to make sure that the output voltage doesn't change if switching
+ * from advanced mode to legacy mode.
+ */
+static int pm8058_disable_smps_locally_set_pull_down(struct regmap *regmap,
+	u16 ctrl_addr, u16 test2_addr, u16 master_enable_addr,
+	u8 master_enable_bit)
+{
+	int error;
+	u8 vref_sel, vlow_sel, band, vprog, bank;
+	unsigned int reg;
+
+	bank = PM8058_REGULATOR_BANK_SEL(7);
+	error = regmap_write(regmap, test2_addr, bank);
+	if (error)
+		return error;
+
+	error = regmap_read(regmap, test2_addr, &reg);
+	if (error)
+		return error;
+
+	reg &= PM8058_SMPS_ADVANCED_MODE_MASK;
+	/* Check if in advanced mode. */
+	if (reg == PM8058_SMPS_ADVANCED_MODE) {
+		/* Determine current output voltage. */
+		error = regmap_read(regmap, ctrl_addr, &reg);
+		if (error)
+			return error;
+
+		band = reg & PM8058_SMPS_ADVANCED_BAND_MASK;
+		band >>= PM8058_SMPS_ADVANCED_BAND_SHIFT;
+		switch (band) {
+		case 3:
+			vref_sel = 0;
+			vlow_sel = 0;
+			break;
+		case 2:
+			vref_sel = PM8058_SMPS_LEGACY_VREF_SEL;
+			vlow_sel = 0;
+			break;
+		case 1:
+			vref_sel = PM8058_SMPS_LEGACY_VREF_SEL;
+			vlow_sel = PM8058_SMPS_LEGACY_VLOW_SEL;
+			break;
+		default:
+			pr_err("%s: regulator already disabled\n", __func__);
+			return -EPERM;
+		}
+		vprog = reg & PM8058_SMPS_ADVANCED_VPROG_MASK;
+		/* Round up if fine step is in use. */
+		vprog = (vprog + 1) >> 1;
+		if (vprog > PM8058_SMPS_LEGACY_VPROG_MASK)
+			vprog = PM8058_SMPS_LEGACY_VPROG_MASK;
+
+		/* Set VLOW_SEL bit. */
+		bank = PM8058_REGULATOR_BANK_SEL(1);
+		error = regmap_write(regmap, test2_addr, bank);
+		if (error)
+			return error;
+
+		error = regmap_update_bits(regmap, test2_addr,
+			PM8058_REGULATOR_BANK_WRITE | PM8058_REGULATOR_BANK_MASK
+				| PM8058_SMPS_LEGACY_VLOW_SEL,
+			PM8058_REGULATOR_BANK_WRITE |
+			PM8058_REGULATOR_BANK_SEL(1) | vlow_sel);
+		if (error)
+			return error;
+
+		/* Switch to legacy mode */
+		bank = PM8058_REGULATOR_BANK_SEL(7);
+		error = regmap_write(regmap, test2_addr, bank);
+		if (error)
+			return error;
+
+		error = regmap_update_bits(regmap, test2_addr,
+				PM8058_REGULATOR_BANK_WRITE |
+				PM8058_REGULATOR_BANK_MASK |
+				PM8058_SMPS_ADVANCED_MODE_MASK,
+				PM8058_REGULATOR_BANK_WRITE |
+				PM8058_REGULATOR_BANK_SEL(7) |
+				PM8058_SMPS_LEGACY_MODE);
+		if (error)
+			return error;
+
+		/* Enable locally, enable pull down, keep voltage the same. */
+		error = regmap_update_bits(regmap, ctrl_addr,
+			PM8058_REGULATOR_ENABLE_MASK |
+			PM8058_REGULATOR_PULL_DOWN_MASK |
+			PM8058_SMPS_LEGACY_VREF_SEL |
+			PM8058_SMPS_LEGACY_VPROG_MASK,
+			PM8058_REGULATOR_ENABLE | PM8058_REGULATOR_PULL_DOWN_EN
+				| vref_sel | vprog);
+		if (error)
+			return error;
+	}
+
+	/* Enable in master control register. */
+	error = regmap_update_bits(regmap, master_enable_addr,
+			master_enable_bit, master_enable_bit);
+	if (error)
+		return error;
+
+	/* Disable locally and enable pull down. */
+	return regmap_update_bits(regmap, ctrl_addr,
+		PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK,
+		PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN);
+}
+
+static int pm8058_disable_ldo_locally_set_pull_down(struct regmap *regmap,
+		u16 ctrl_addr, u16 master_enable_addr, u8 master_enable_bit)
+{
+	int error;
+
+	/* Enable LDO in master control register. */
+	error = regmap_update_bits(regmap, master_enable_addr,
+			master_enable_bit, master_enable_bit);
+	if (error)
+		return error;
+
+	/* Disable LDO in CTRL register and set pull down */
+	return regmap_update_bits(regmap, ctrl_addr,
+		PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK,
+		PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN);
+}
+
+static int pm8058_pwrkey_shutdown(struct pmic8xxx_pwrkey *pwrkey, bool reset)
+{
+	int error;
+	struct regmap *regmap = pwrkey->regmap;
+	u8 mask, val;
+
+	/* When shutting down, enable active pulldowns on important rails. */
+	if (!reset) {
+		/* Disable SMPS's 0,1,3 locally and set pulldown enable bits. */
+		pm8058_disable_smps_locally_set_pull_down(regmap,
+			PM8058_S0_CTRL, PM8058_S0_TEST2,
+			REG_PM8058_VREG_EN_MSM, BIT(7));
+		pm8058_disable_smps_locally_set_pull_down(regmap,
+			PM8058_S1_CTRL, PM8058_S1_TEST2,
+			REG_PM8058_VREG_EN_MSM, BIT(6));
+		pm8058_disable_smps_locally_set_pull_down(regmap,
+			PM8058_S3_CTRL, PM8058_S3_TEST2,
+			REG_PM8058_VREG_EN_GRP_5_4, BIT(7) | BIT(4));
+		/* Disable LDO 21 locally and set pulldown enable bit. */
+		pm8058_disable_ldo_locally_set_pull_down(regmap,
+			PM8058_L21_CTRL, REG_PM8058_VREG_EN_GRP_5_4,
+			BIT(1));
+	}
+
+	/*
+	 * Fix-up: Set regulator LDO22 to 1.225 V in high power mode. Leave its
+	 * pull-down state intact. This ensures a safe shutdown.
+	 */
+	error = regmap_update_bits(regmap, PM8058_L22_CTRL, 0xbf, 0x93);
+	if (error)
+		return error;
+
+	/* Enable SMPL if resetting is desired */
+	mask = SLEEP_CTRL_SMPL_EN_RESET;
+	val = 0;
+	if (reset)
+		val = mask;
+	return regmap_update_bits(regmap, PM8058_SLEEP_CTRL, mask, val);
+}
+
+static int pm8921_pwrkey_shutdown(struct pmic8xxx_pwrkey *pwrkey, bool reset)
+{
+	struct regmap *regmap = pwrkey->regmap;
+	u8 mask = SLEEP_CTRL_SMPL_EN_RESET;
+	u8 val = 0;
+
+	/* Enable SMPL if resetting is desired */
+	if (reset)
+		val = mask;
+	return regmap_update_bits(regmap, PM8921_SLEEP_CTRL, mask, val);
+}
+
+static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
+{
+	struct input_dev *pwr;
+	int key_release_irq = platform_get_irq(pdev, 0);
+	int key_press_irq = platform_get_irq(pdev, 1);
+	int err;
+	unsigned int delay;
+	unsigned int pon_cntl;
+	struct regmap *regmap;
+	struct pmic8xxx_pwrkey *pwrkey;
+	u32 kpd_delay;
+	bool pull_up;
+
+	if (of_property_read_u32(pdev->dev.of_node, "debounce", &kpd_delay))
+		kpd_delay = 15625;
+
+	/* Valid range of pwr key trigger delay is 1/64 sec to 2 seconds. */
+	if (kpd_delay > USEC_PER_SEC * 2 || kpd_delay < USEC_PER_SEC / 64) {
+		dev_err(&pdev->dev, "invalid power key trigger delay\n");
+		return -EINVAL;
+	}
+
+	pull_up = of_property_read_bool(pdev->dev.of_node, "pull-up");
+
+	regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!regmap) {
+		dev_err(&pdev->dev, "failed to locate regmap for the device\n");
+		return -ENODEV;
+	}
+
+	pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL);
+	if (!pwrkey)
+		return -ENOMEM;
+
+	pwrkey->shutdown_fn = of_device_get_match_data(&pdev->dev);
+	pwrkey->regmap = regmap;
+	pwrkey->key_press_irq = key_press_irq;
+
+	pwr = devm_input_allocate_device(&pdev->dev);
+	if (!pwr) {
+		dev_dbg(&pdev->dev, "Can't allocate power button\n");
+		return -ENOMEM;
+	}
+
+	input_set_capability(pwr, EV_KEY, KEY_POWER);
+
+	pwr->name = "pmic8xxx_pwrkey";
+	pwr->phys = "pmic8xxx_pwrkey/input0";
+
+	delay = (kpd_delay << 6) / USEC_PER_SEC;
+	delay = ilog2(delay);
+
+	err = regmap_read(regmap, PON_CNTL_1, &pon_cntl);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err);
+		return err;
+	}
+
+	pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK;
+	pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK);
+	if (pull_up)
+		pon_cntl |= PON_CNTL_PULL_UP;
+	else
+		pon_cntl &= ~PON_CNTL_PULL_UP;
+
+	err = regmap_write(regmap, PON_CNTL_1, pon_cntl);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err);
+		return err;
+	}
+
+	err = devm_request_irq(&pdev->dev, key_press_irq, pwrkey_press_irq,
+			       IRQF_TRIGGER_RISING,
+			       "pmic8xxx_pwrkey_press", pwr);
+	if (err) {
+		dev_err(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
+			key_press_irq, err);
+		return err;
+	}
+
+	err = devm_request_irq(&pdev->dev, key_release_irq, pwrkey_release_irq,
+			       IRQF_TRIGGER_RISING,
+			       "pmic8xxx_pwrkey_release", pwr);
+	if (err) {
+		dev_err(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
+			key_release_irq, err);
+		return err;
+	}
+
+	err = input_register_device(pwr);
+	if (err) {
+		dev_err(&pdev->dev, "Can't register power key: %d\n", err);
+		return err;
+	}
+
+	platform_set_drvdata(pdev, pwrkey);
+	device_init_wakeup(&pdev->dev, 1);
+
+	return 0;
+}
+
+static int pmic8xxx_pwrkey_remove(struct platform_device *pdev)
+{
+	device_init_wakeup(&pdev->dev, 0);
+
+	return 0;
+}
+
+static const struct of_device_id pm8xxx_pwr_key_id_table[] = {
+	{ .compatible = "qcom,pm8058-pwrkey", .data = &pm8058_pwrkey_shutdown },
+	{ .compatible = "qcom,pm8921-pwrkey", .data = &pm8921_pwrkey_shutdown },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_pwr_key_id_table);
+
+static struct platform_driver pmic8xxx_pwrkey_driver = {
+	.probe		= pmic8xxx_pwrkey_probe,
+	.remove		= pmic8xxx_pwrkey_remove,
+	.shutdown	= pmic8xxx_pwrkey_shutdown,
+	.driver		= {
+		.name	= "pm8xxx-pwrkey",
+		.pm	= &pm8xxx_pwr_key_pm_ops,
+		.of_match_table = pm8xxx_pwr_key_id_table,
+	},
+};
+module_platform_driver(pmic8xxx_pwrkey_driver);
+
+MODULE_ALIAS("platform:pmic8xxx_pwrkey");
+MODULE_DESCRIPTION("PMIC8XXX Power Key driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Trilok Soni <tsoni@codeaurora.org>");
diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c
new file mode 100644
index 0000000..84909a1
--- /dev/null
+++ b/drivers/input/misc/powermate.c
@@ -0,0 +1,455 @@
+/*
+ * A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial.
+ *
+ * v1.1, (c)2002 William R Sowerbutts <will@sowerbutts.com>
+ *
+ * This device is a anodised aluminium knob which connects over USB. It can measure
+ * clockwise and anticlockwise rotation. The dial also acts as a pushbutton with
+ * a spring for automatic release. The base contains a pair of LEDs which illuminate
+ * the translucent base. It rotates without limit and reports its relative rotation
+ * back to the host when polled by the USB controller.
+ *
+ * Testing with the knob I have has shown that it measures approximately 94 "clicks"
+ * for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was
+ * a variable speed cordless electric drill) has shown that the device can measure
+ * speeds of up to 7 clicks either clockwise or anticlockwise between pollings from
+ * the host. If it counts more than 7 clicks before it is polled, it will wrap back
+ * to zero and start counting again. This was at quite high speed, however, almost
+ * certainly faster than the human hand could turn it. Griffin say that it loses a
+ * pulse or two on a direction change; the granularity is so fine that I never
+ * noticed this in practice.
+ *
+ * The device's microcontroller can be programmed to set the LED to either a constant
+ * intensity, or to a rhythmic pulsing. Several patterns and speeds are available.
+ *
+ * Griffin were very happy to provide documentation and free hardware for development.
+ *
+ * Some userspace tools are available on the web: http://sowerbutts.com/powermate/
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/usb/input.h>
+
+#define POWERMATE_VENDOR	0x077d	/* Griffin Technology, Inc. */
+#define POWERMATE_PRODUCT_NEW	0x0410	/* Griffin PowerMate */
+#define POWERMATE_PRODUCT_OLD	0x04AA	/* Griffin soundKnob */
+
+#define CONTOUR_VENDOR		0x05f3	/* Contour Design, Inc. */
+#define CONTOUR_JOG		0x0240	/* Jog and Shuttle */
+
+/* these are the command codes we send to the device */
+#define SET_STATIC_BRIGHTNESS  0x01
+#define SET_PULSE_ASLEEP       0x02
+#define SET_PULSE_AWAKE        0x03
+#define SET_PULSE_MODE         0x04
+
+/* these refer to bits in the powermate_device's requires_update field. */
+#define UPDATE_STATIC_BRIGHTNESS (1<<0)
+#define UPDATE_PULSE_ASLEEP      (1<<1)
+#define UPDATE_PULSE_AWAKE       (1<<2)
+#define UPDATE_PULSE_MODE        (1<<3)
+
+/* at least two versions of the hardware exist, with differing payload
+   sizes. the first three bytes always contain the "interesting" data in
+   the relevant format. */
+#define POWERMATE_PAYLOAD_SIZE_MAX 6
+#define POWERMATE_PAYLOAD_SIZE_MIN 3
+struct powermate_device {
+	signed char *data;
+	dma_addr_t data_dma;
+	struct urb *irq, *config;
+	struct usb_ctrlrequest *configcr;
+	struct usb_device *udev;
+	struct usb_interface *intf;
+	struct input_dev *input;
+	spinlock_t lock;
+	int static_brightness;
+	int pulse_speed;
+	int pulse_table;
+	int pulse_asleep;
+	int pulse_awake;
+	int requires_update; // physical settings which are out of sync
+	char phys[64];
+};
+
+static char pm_name_powermate[] = "Griffin PowerMate";
+static char pm_name_soundknob[] = "Griffin SoundKnob";
+
+static void powermate_config_complete(struct urb *urb);
+
+/* Callback for data arriving from the PowerMate over the USB interrupt pipe */
+static void powermate_irq(struct urb *urb)
+{
+	struct powermate_device *pm = urb->context;
+	struct device *dev = &pm->intf->dev;
+	int retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
+			__func__, urb->status);
+		return;
+	default:
+		dev_dbg(dev, "%s - nonzero urb status received: %d\n",
+			__func__, urb->status);
+		goto exit;
+	}
+
+	/* handle updates to device state */
+	input_report_key(pm->input, BTN_0, pm->data[0] & 0x01);
+	input_report_rel(pm->input, REL_DIAL, pm->data[1]);
+	input_sync(pm->input);
+
+exit:
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(dev, "%s - usb_submit_urb failed with result: %d\n",
+			__func__, retval);
+}
+
+/* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */
+static void powermate_sync_state(struct powermate_device *pm)
+{
+	if (pm->requires_update == 0)
+		return; /* no updates are required */
+	if (pm->config->status == -EINPROGRESS)
+		return; /* an update is already in progress; it'll issue this update when it completes */
+
+	if (pm->requires_update & UPDATE_PULSE_ASLEEP){
+		pm->configcr->wValue = cpu_to_le16( SET_PULSE_ASLEEP );
+		pm->configcr->wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 );
+		pm->requires_update &= ~UPDATE_PULSE_ASLEEP;
+	}else if (pm->requires_update & UPDATE_PULSE_AWAKE){
+		pm->configcr->wValue = cpu_to_le16( SET_PULSE_AWAKE );
+		pm->configcr->wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 );
+		pm->requires_update &= ~UPDATE_PULSE_AWAKE;
+	}else if (pm->requires_update & UPDATE_PULSE_MODE){
+		int op, arg;
+		/* the powermate takes an operation and an argument for its pulse algorithm.
+		   the operation can be:
+		   0: divide the speed
+		   1: pulse at normal speed
+		   2: multiply the speed
+		   the argument only has an effect for operations 0 and 2, and ranges between
+		   1 (least effect) to 255 (maximum effect).
+
+		   thus, several states are equivalent and are coalesced into one state.
+
+		   we map this onto a range from 0 to 510, with:
+		   0 -- 254    -- use divide (0 = slowest)
+		   255         -- use normal speed
+		   256 -- 510  -- use multiple (510 = fastest).
+
+		   Only values of 'arg' quite close to 255 are particularly useful/spectacular.
+		*/
+		if (pm->pulse_speed < 255) {
+			op = 0;                   // divide
+			arg = 255 - pm->pulse_speed;
+		} else if (pm->pulse_speed > 255) {
+			op = 2;                   // multiply
+			arg = pm->pulse_speed - 255;
+		} else {
+			op = 1;                   // normal speed
+			arg = 0;                  // can be any value
+		}
+		pm->configcr->wValue = cpu_to_le16( (pm->pulse_table << 8) | SET_PULSE_MODE );
+		pm->configcr->wIndex = cpu_to_le16( (arg << 8) | op );
+		pm->requires_update &= ~UPDATE_PULSE_MODE;
+	} else if (pm->requires_update & UPDATE_STATIC_BRIGHTNESS) {
+		pm->configcr->wValue = cpu_to_le16( SET_STATIC_BRIGHTNESS );
+		pm->configcr->wIndex = cpu_to_le16( pm->static_brightness );
+		pm->requires_update &= ~UPDATE_STATIC_BRIGHTNESS;
+	} else {
+		printk(KERN_ERR "powermate: unknown update required");
+		pm->requires_update = 0; /* fudge the bug */
+		return;
+	}
+
+/*	printk("powermate: %04x %04x\n", pm->configcr->wValue, pm->configcr->wIndex); */
+
+	pm->configcr->bRequestType = 0x41; /* vendor request */
+	pm->configcr->bRequest = 0x01;
+	pm->configcr->wLength = 0;
+
+	usb_fill_control_urb(pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0),
+			     (void *) pm->configcr, NULL, 0,
+			     powermate_config_complete, pm);
+
+	if (usb_submit_urb(pm->config, GFP_ATOMIC))
+		printk(KERN_ERR "powermate: usb_submit_urb(config) failed");
+}
+
+/* Called when our asynchronous control message completes. We may need to issue another immediately */
+static void powermate_config_complete(struct urb *urb)
+{
+	struct powermate_device *pm = urb->context;
+	unsigned long flags;
+
+	if (urb->status)
+		printk(KERN_ERR "powermate: config urb returned %d\n", urb->status);
+
+	spin_lock_irqsave(&pm->lock, flags);
+	powermate_sync_state(pm);
+	spin_unlock_irqrestore(&pm->lock, flags);
+}
+
+/* Set the LED up as described and begin the sync with the hardware if required */
+static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed,
+				int pulse_table, int pulse_asleep, int pulse_awake)
+{
+	unsigned long flags;
+
+	if (pulse_speed < 0)
+		pulse_speed = 0;
+	if (pulse_table < 0)
+		pulse_table = 0;
+	if (pulse_speed > 510)
+		pulse_speed = 510;
+	if (pulse_table > 2)
+		pulse_table = 2;
+
+	pulse_asleep = !!pulse_asleep;
+	pulse_awake = !!pulse_awake;
+
+
+	spin_lock_irqsave(&pm->lock, flags);
+
+	/* mark state updates which are required */
+	if (static_brightness != pm->static_brightness) {
+		pm->static_brightness = static_brightness;
+		pm->requires_update |= UPDATE_STATIC_BRIGHTNESS;
+	}
+	if (pulse_asleep != pm->pulse_asleep) {
+		pm->pulse_asleep = pulse_asleep;
+		pm->requires_update |= (UPDATE_PULSE_ASLEEP | UPDATE_STATIC_BRIGHTNESS);
+	}
+	if (pulse_awake != pm->pulse_awake) {
+		pm->pulse_awake = pulse_awake;
+		pm->requires_update |= (UPDATE_PULSE_AWAKE | UPDATE_STATIC_BRIGHTNESS);
+	}
+	if (pulse_speed != pm->pulse_speed || pulse_table != pm->pulse_table) {
+		pm->pulse_speed = pulse_speed;
+		pm->pulse_table = pulse_table;
+		pm->requires_update |= UPDATE_PULSE_MODE;
+	}
+
+	powermate_sync_state(pm);
+
+	spin_unlock_irqrestore(&pm->lock, flags);
+}
+
+/* Callback from the Input layer when an event arrives from userspace to configure the LED */
+static int powermate_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int _value)
+{
+	unsigned int command = (unsigned int)_value;
+	struct powermate_device *pm = input_get_drvdata(dev);
+
+	if (type == EV_MSC && code == MSC_PULSELED){
+		/*
+		    bits  0- 7: 8 bits: LED brightness
+		    bits  8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster.
+		    bits 17-18: 2 bits: pulse table (0, 1, 2 valid)
+		    bit     19: 1 bit : pulse whilst asleep?
+		    bit     20: 1 bit : pulse constantly?
+		*/
+		int static_brightness = command & 0xFF;   // bits 0-7
+		int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16
+		int pulse_table = (command >> 17) & 0x3;  // bits 17-18
+		int pulse_asleep = (command >> 19) & 0x1; // bit 19
+		int pulse_awake  = (command >> 20) & 0x1; // bit 20
+
+		powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake);
+	}
+
+	return 0;
+}
+
+static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm)
+{
+	pm->data = usb_alloc_coherent(udev, POWERMATE_PAYLOAD_SIZE_MAX,
+				      GFP_ATOMIC, &pm->data_dma);
+	if (!pm->data)
+		return -1;
+
+	pm->configcr = kmalloc(sizeof(*(pm->configcr)), GFP_KERNEL);
+	if (!pm->configcr)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void powermate_free_buffers(struct usb_device *udev, struct powermate_device *pm)
+{
+	usb_free_coherent(udev, POWERMATE_PAYLOAD_SIZE_MAX,
+			  pm->data, pm->data_dma);
+	kfree(pm->configcr);
+}
+
+/* Called whenever a USB device matching one in our supported devices table is connected */
+static int powermate_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev (intf);
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct powermate_device *pm;
+	struct input_dev *input_dev;
+	int pipe, maxp;
+	int error = -ENOMEM;
+
+	interface = intf->cur_altsetting;
+	if (interface->desc.bNumEndpoints < 1)
+		return -EINVAL;
+
+	endpoint = &interface->endpoint[0].desc;
+	if (!usb_endpoint_is_int_in(endpoint))
+		return -EIO;
+
+	usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+		0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		0, interface->desc.bInterfaceNumber, NULL, 0,
+		USB_CTRL_SET_TIMEOUT);
+
+	pm = kzalloc(sizeof(struct powermate_device), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!pm || !input_dev)
+		goto fail1;
+
+	if (powermate_alloc_buffers(udev, pm))
+		goto fail2;
+
+	pm->irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!pm->irq)
+		goto fail2;
+
+	pm->config = usb_alloc_urb(0, GFP_KERNEL);
+	if (!pm->config)
+		goto fail3;
+
+	pm->udev = udev;
+	pm->intf = intf;
+	pm->input = input_dev;
+
+	usb_make_path(udev, pm->phys, sizeof(pm->phys));
+	strlcat(pm->phys, "/input0", sizeof(pm->phys));
+
+	spin_lock_init(&pm->lock);
+
+	switch (le16_to_cpu(udev->descriptor.idProduct)) {
+	case POWERMATE_PRODUCT_NEW:
+		input_dev->name = pm_name_powermate;
+		break;
+	case POWERMATE_PRODUCT_OLD:
+		input_dev->name = pm_name_soundknob;
+		break;
+	default:
+		input_dev->name = pm_name_soundknob;
+		printk(KERN_WARNING "powermate: unknown product id %04x\n",
+		       le16_to_cpu(udev->descriptor.idProduct));
+	}
+
+	input_dev->phys = pm->phys;
+	usb_to_input_id(udev, &input_dev->id);
+	input_dev->dev.parent = &intf->dev;
+
+	input_set_drvdata(input_dev, pm);
+
+	input_dev->event = powermate_input_event;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) |
+		BIT_MASK(EV_MSC);
+	input_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
+	input_dev->relbit[BIT_WORD(REL_DIAL)] = BIT_MASK(REL_DIAL);
+	input_dev->mscbit[BIT_WORD(MSC_PULSELED)] = BIT_MASK(MSC_PULSELED);
+
+	/* get a handle to the interrupt data pipe */
+	pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+	maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+
+	if (maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX) {
+		printk(KERN_WARNING "powermate: Expected payload of %d--%d bytes, found %d bytes!\n",
+			POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp);
+		maxp = POWERMATE_PAYLOAD_SIZE_MAX;
+	}
+
+	usb_fill_int_urb(pm->irq, udev, pipe, pm->data,
+			 maxp, powermate_irq,
+			 pm, endpoint->bInterval);
+	pm->irq->transfer_dma = pm->data_dma;
+	pm->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* register our interrupt URB with the USB system */
+	if (usb_submit_urb(pm->irq, GFP_KERNEL)) {
+		error = -EIO;
+		goto fail4;
+	}
+
+	error = input_register_device(pm->input);
+	if (error)
+		goto fail5;
+
+
+	/* force an update of everything */
+	pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS;
+	powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters
+
+	usb_set_intfdata(intf, pm);
+	return 0;
+
+ fail5:	usb_kill_urb(pm->irq);
+ fail4:	usb_free_urb(pm->config);
+ fail3:	usb_free_urb(pm->irq);
+ fail2:	powermate_free_buffers(udev, pm);
+ fail1:	input_free_device(input_dev);
+	kfree(pm);
+	return error;
+}
+
+/* Called when a USB device we've accepted ownership of is removed */
+static void powermate_disconnect(struct usb_interface *intf)
+{
+	struct powermate_device *pm = usb_get_intfdata (intf);
+
+	usb_set_intfdata(intf, NULL);
+	if (pm) {
+		pm->requires_update = 0;
+		usb_kill_urb(pm->irq);
+		input_unregister_device(pm->input);
+		usb_free_urb(pm->irq);
+		usb_free_urb(pm->config);
+		powermate_free_buffers(interface_to_usbdev(intf), pm);
+
+		kfree(pm);
+	}
+}
+
+static struct usb_device_id powermate_devices [] = {
+	{ USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_NEW) },
+	{ USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_OLD) },
+	{ USB_DEVICE(CONTOUR_VENDOR, CONTOUR_JOG) },
+	{ } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, powermate_devices);
+
+static struct usb_driver powermate_driver = {
+        .name =         "powermate",
+        .probe =        powermate_probe,
+        .disconnect =   powermate_disconnect,
+        .id_table =     powermate_devices,
+};
+
+module_usb_driver(powermate_driver);
+
+MODULE_AUTHOR( "William R Sowerbutts" );
+MODULE_DESCRIPTION( "Griffin Technology, Inc PowerMate driver" );
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c
new file mode 100644
index 0000000..18663d4
--- /dev/null
+++ b/drivers/input/misc/pwm-beeper.c
@@ -0,0 +1,220 @@
+/*
+ *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ *  PWM beeper driver
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+struct pwm_beeper {
+	struct input_dev *input;
+	struct pwm_device *pwm;
+	struct work_struct work;
+	unsigned long period;
+};
+
+#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
+
+static void __pwm_beeper_set(struct pwm_beeper *beeper)
+{
+	unsigned long period = beeper->period;
+
+	if (period) {
+		pwm_config(beeper->pwm, period / 2, period);
+		pwm_enable(beeper->pwm);
+	} else
+		pwm_disable(beeper->pwm);
+}
+
+static void pwm_beeper_work(struct work_struct *work)
+{
+	struct pwm_beeper *beeper =
+		container_of(work, struct pwm_beeper, work);
+
+	__pwm_beeper_set(beeper);
+}
+
+static int pwm_beeper_event(struct input_dev *input,
+			    unsigned int type, unsigned int code, int value)
+{
+	struct pwm_beeper *beeper = input_get_drvdata(input);
+
+	if (type != EV_SND || value < 0)
+		return -EINVAL;
+
+	switch (code) {
+	case SND_BELL:
+		value = value ? 1000 : 0;
+		break;
+	case SND_TONE:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (value == 0)
+		beeper->period = 0;
+	else
+		beeper->period = HZ_TO_NANOSECONDS(value);
+
+	schedule_work(&beeper->work);
+
+	return 0;
+}
+
+static void pwm_beeper_stop(struct pwm_beeper *beeper)
+{
+	cancel_work_sync(&beeper->work);
+
+	if (beeper->period)
+		pwm_disable(beeper->pwm);
+}
+
+static void pwm_beeper_close(struct input_dev *input)
+{
+	struct pwm_beeper *beeper = input_get_drvdata(input);
+
+	pwm_beeper_stop(beeper);
+}
+
+static int pwm_beeper_probe(struct platform_device *pdev)
+{
+	unsigned long pwm_id = (unsigned long)dev_get_platdata(&pdev->dev);
+	struct pwm_beeper *beeper;
+	int error;
+
+	beeper = kzalloc(sizeof(*beeper), GFP_KERNEL);
+	if (!beeper)
+		return -ENOMEM;
+
+	beeper->pwm = pwm_get(&pdev->dev, NULL);
+	if (IS_ERR(beeper->pwm)) {
+		dev_dbg(&pdev->dev, "unable to request PWM, trying legacy API\n");
+		beeper->pwm = pwm_request(pwm_id, "pwm beeper");
+	}
+
+	if (IS_ERR(beeper->pwm)) {
+		error = PTR_ERR(beeper->pwm);
+		dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error);
+		goto err_free;
+	}
+
+	INIT_WORK(&beeper->work, pwm_beeper_work);
+
+	beeper->input = input_allocate_device();
+	if (!beeper->input) {
+		dev_err(&pdev->dev, "Failed to allocate input device\n");
+		error = -ENOMEM;
+		goto err_pwm_free;
+	}
+	beeper->input->dev.parent = &pdev->dev;
+
+	beeper->input->name = "pwm-beeper";
+	beeper->input->phys = "pwm/input0";
+	beeper->input->id.bustype = BUS_HOST;
+	beeper->input->id.vendor = 0x001f;
+	beeper->input->id.product = 0x0001;
+	beeper->input->id.version = 0x0100;
+
+	beeper->input->evbit[0] = BIT(EV_SND);
+	beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL);
+
+	beeper->input->event = pwm_beeper_event;
+	beeper->input->close = pwm_beeper_close;
+
+	input_set_drvdata(beeper->input, beeper);
+
+	error = input_register_device(beeper->input);
+	if (error) {
+		dev_err(&pdev->dev, "Failed to register input device: %d\n", error);
+		goto err_input_free;
+	}
+
+	platform_set_drvdata(pdev, beeper);
+
+	return 0;
+
+err_input_free:
+	input_free_device(beeper->input);
+err_pwm_free:
+	pwm_free(beeper->pwm);
+err_free:
+	kfree(beeper);
+
+	return error;
+}
+
+static int pwm_beeper_remove(struct platform_device *pdev)
+{
+	struct pwm_beeper *beeper = platform_get_drvdata(pdev);
+
+	input_unregister_device(beeper->input);
+
+	pwm_free(beeper->pwm);
+
+	kfree(beeper);
+
+	return 0;
+}
+
+static int __maybe_unused pwm_beeper_suspend(struct device *dev)
+{
+	struct pwm_beeper *beeper = dev_get_drvdata(dev);
+
+	pwm_beeper_stop(beeper);
+
+	return 0;
+}
+
+static int __maybe_unused pwm_beeper_resume(struct device *dev)
+{
+	struct pwm_beeper *beeper = dev_get_drvdata(dev);
+
+	if (beeper->period)
+		__pwm_beeper_set(beeper);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops,
+			 pwm_beeper_suspend, pwm_beeper_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pwm_beeper_match[] = {
+	{ .compatible = "pwm-beeper", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, pwm_beeper_match);
+#endif
+
+static struct platform_driver pwm_beeper_driver = {
+	.probe	= pwm_beeper_probe,
+	.remove = pwm_beeper_remove,
+	.driver = {
+		.name	= "pwm-beeper",
+		.pm	= &pwm_beeper_pm_ops,
+		.of_match_table = of_match_ptr(pwm_beeper_match),
+	},
+};
+module_platform_driver(pwm_beeper_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("PWM beeper driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pwm-beeper");
diff --git a/drivers/input/misc/rb532_button.c b/drivers/input/misc/rb532_button.c
new file mode 100644
index 0000000..62c5814
--- /dev/null
+++ b/drivers/input/misc/rb532_button.c
@@ -0,0 +1,107 @@
+/*
+ * Support for the S1 button on Routerboard 532
+ *
+ * Copyright (C) 2009  Phil Sutter <n0-1@freewrt.org>
+ */
+
+#include <linux/input-polldev.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <asm/mach-rc32434/gpio.h>
+#include <asm/mach-rc32434/rb.h>
+
+#define DRV_NAME "rb532-button"
+
+#define RB532_BTN_RATE 100 /* msec */
+#define RB532_BTN_KSYM BTN_0
+
+/* The S1 button state is provided by GPIO pin 1. But as this
+ * pin is also used for uart input as alternate function, the
+ * operational modes must be switched first:
+ * 1) disable uart using set_latch_u5()
+ * 2) turn off alternate function implicitly through
+ *    gpio_direction_input()
+ * 3) read the GPIO's current value
+ * 4) undo step 2 by enabling alternate function (in this
+ *    mode the GPIO direction is fixed, so no change needed)
+ * 5) turn on uart again
+ * The GPIO value occurs to be inverted, so pin high means
+ * button is not pressed.
+ */
+static bool rb532_button_pressed(void)
+{
+	int val;
+
+	set_latch_u5(0, LO_FOFF);
+	gpio_direction_input(GPIO_BTN_S1);
+
+	val = gpio_get_value(GPIO_BTN_S1);
+
+	rb532_gpio_set_func(GPIO_BTN_S1);
+	set_latch_u5(LO_FOFF, 0);
+
+	return !val;
+}
+
+static void rb532_button_poll(struct input_polled_dev *poll_dev)
+{
+	input_report_key(poll_dev->input, RB532_BTN_KSYM,
+			 rb532_button_pressed());
+	input_sync(poll_dev->input);
+}
+
+static int rb532_button_probe(struct platform_device *pdev)
+{
+	struct input_polled_dev *poll_dev;
+	int error;
+
+	poll_dev = input_allocate_polled_device();
+	if (!poll_dev)
+		return -ENOMEM;
+
+	poll_dev->poll = rb532_button_poll;
+	poll_dev->poll_interval = RB532_BTN_RATE;
+
+	poll_dev->input->name = "rb532 button";
+	poll_dev->input->phys = "rb532/button0";
+	poll_dev->input->id.bustype = BUS_HOST;
+	poll_dev->input->dev.parent = &pdev->dev;
+
+	dev_set_drvdata(&pdev->dev, poll_dev);
+
+	input_set_capability(poll_dev->input, EV_KEY, RB532_BTN_KSYM);
+
+	error = input_register_polled_device(poll_dev);
+	if (error) {
+		input_free_polled_device(poll_dev);
+		return error;
+	}
+
+	return 0;
+}
+
+static int rb532_button_remove(struct platform_device *pdev)
+{
+	struct input_polled_dev *poll_dev = dev_get_drvdata(&pdev->dev);
+
+	input_unregister_polled_device(poll_dev);
+	input_free_polled_device(poll_dev);
+
+	return 0;
+}
+
+static struct platform_driver rb532_button_driver = {
+	.probe = rb532_button_probe,
+	.remove = rb532_button_remove,
+	.driver = {
+		.name = DRV_NAME,
+	},
+};
+module_platform_driver(rb532_button_driver);
+
+MODULE_AUTHOR("Phil Sutter <n0-1@freewrt.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Support for S1 button on Routerboard 532");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c
new file mode 100644
index 0000000..a804705
--- /dev/null
+++ b/drivers/input/misc/regulator-haptic.c
@@ -0,0 +1,267 @@
+/*
+ * Regulator haptic driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Jaewon Kim <jaewon02.kim@samsung.com>
+ * Author: Hyunhee Kim <hyunhee.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_data/regulator-haptic.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#define MAX_MAGNITUDE_SHIFT	16
+
+struct regulator_haptic {
+	struct device *dev;
+	struct input_dev *input_dev;
+	struct regulator *regulator;
+
+	struct work_struct work;
+	struct mutex mutex;
+
+	bool active;
+	bool suspended;
+
+	unsigned int max_volt;
+	unsigned int min_volt;
+	unsigned int magnitude;
+};
+
+static int regulator_haptic_toggle(struct regulator_haptic *haptic, bool on)
+{
+	int error;
+
+	if (haptic->active != on) {
+
+		error = on ? regulator_enable(haptic->regulator) :
+			     regulator_disable(haptic->regulator);
+		if (error) {
+			dev_err(haptic->dev,
+				"failed to switch regulator %s: %d\n",
+				on ? "on" : "off", error);
+			return error;
+		}
+
+		haptic->active = on;
+	}
+
+	return 0;
+}
+
+static int regulator_haptic_set_voltage(struct regulator_haptic *haptic,
+					 unsigned int magnitude)
+{
+	u64 volt_mag_multi;
+	unsigned int intensity;
+	int error;
+
+	volt_mag_multi = (u64)(haptic->max_volt - haptic->min_volt) * magnitude;
+	intensity = (unsigned int)(volt_mag_multi >> MAX_MAGNITUDE_SHIFT);
+
+	error = regulator_set_voltage(haptic->regulator,
+				      intensity + haptic->min_volt,
+				      haptic->max_volt);
+	if (error) {
+		dev_err(haptic->dev, "cannot set regulator voltage to %d: %d\n",
+			intensity + haptic->min_volt, error);
+		return error;
+	}
+
+	regulator_haptic_toggle(haptic, !!magnitude);
+
+	return 0;
+}
+
+static void regulator_haptic_work(struct work_struct *work)
+{
+	struct regulator_haptic *haptic = container_of(work,
+					struct regulator_haptic, work);
+
+	mutex_lock(&haptic->mutex);
+
+	if (!haptic->suspended)
+		regulator_haptic_set_voltage(haptic, haptic->magnitude);
+
+	mutex_unlock(&haptic->mutex);
+}
+
+static int regulator_haptic_play_effect(struct input_dev *input, void *data,
+					struct ff_effect *effect)
+{
+	struct regulator_haptic *haptic = input_get_drvdata(input);
+
+	haptic->magnitude = effect->u.rumble.strong_magnitude;
+	if (!haptic->magnitude)
+		haptic->magnitude = effect->u.rumble.weak_magnitude;
+
+	schedule_work(&haptic->work);
+
+	return 0;
+}
+
+static void regulator_haptic_close(struct input_dev *input)
+{
+	struct regulator_haptic *haptic = input_get_drvdata(input);
+
+	cancel_work_sync(&haptic->work);
+	regulator_haptic_set_voltage(haptic, 0);
+}
+
+static int __maybe_unused
+regulator_haptic_parse_dt(struct device *dev, struct regulator_haptic *haptic)
+{
+	struct device_node *node;
+	int error;
+
+	node = dev->of_node;
+	if(!node) {
+		dev_err(dev, "Missing dveice tree data\n");
+		return -EINVAL;
+	}
+
+	error = of_property_read_u32(node, "max-microvolt", &haptic->max_volt);
+	if (error) {
+		dev_err(dev, "cannot parse max-microvolt\n");
+		return error;
+	}
+
+	error = of_property_read_u32(node, "min-microvolt", &haptic->min_volt);
+	if (error) {
+		dev_err(dev, "cannot parse min-microvolt\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static int regulator_haptic_probe(struct platform_device *pdev)
+{
+	const struct regulator_haptic_data *pdata = dev_get_platdata(&pdev->dev);
+	struct regulator_haptic *haptic;
+	struct input_dev *input_dev;
+	int error;
+
+	haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL);
+	if (!haptic)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, haptic);
+	haptic->dev = &pdev->dev;
+	mutex_init(&haptic->mutex);
+	INIT_WORK(&haptic->work, regulator_haptic_work);
+
+	if (pdata) {
+		haptic->max_volt = pdata->max_volt;
+		haptic->min_volt = pdata->min_volt;
+	} else if (IS_ENABLED(CONFIG_OF)) {
+		error = regulator_haptic_parse_dt(&pdev->dev, haptic);
+		if (error)
+			return error;
+	} else {
+		dev_err(&pdev->dev, "Missing platform data\n");
+		return -EINVAL;
+	}
+
+	haptic->regulator = devm_regulator_get_exclusive(&pdev->dev, "haptic");
+	if (IS_ERR(haptic->regulator)) {
+		dev_err(&pdev->dev, "failed to get regulator\n");
+		return PTR_ERR(haptic->regulator);
+	}
+
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!input_dev)
+		return	-ENOMEM;
+
+	haptic->input_dev = input_dev;
+	haptic->input_dev->name = "regulator-haptic";
+	haptic->input_dev->dev.parent = &pdev->dev;
+	haptic->input_dev->close = regulator_haptic_close;
+	input_set_drvdata(haptic->input_dev, haptic);
+	input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE);
+
+	error = input_ff_create_memless(input_dev, NULL,
+					regulator_haptic_play_effect);
+	if (error) {
+		dev_err(&pdev->dev, "failed to create force-feedback\n");
+		return error;
+	}
+
+	error = input_register_device(haptic->input_dev);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused regulator_haptic_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct regulator_haptic *haptic = platform_get_drvdata(pdev);
+	int error;
+
+	error = mutex_lock_interruptible(&haptic->mutex);
+	if (error)
+		return error;
+
+	regulator_haptic_set_voltage(haptic, 0);
+
+	haptic->suspended = true;
+
+	mutex_unlock(&haptic->mutex);
+
+	return 0;
+}
+
+static int __maybe_unused regulator_haptic_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct regulator_haptic *haptic = platform_get_drvdata(pdev);
+	unsigned int magnitude;
+
+	mutex_lock(&haptic->mutex);
+
+	haptic->suspended = false;
+
+	magnitude = ACCESS_ONCE(haptic->magnitude);
+	if (magnitude)
+		regulator_haptic_set_voltage(haptic, magnitude);
+
+	mutex_unlock(&haptic->mutex);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops,
+		regulator_haptic_suspend, regulator_haptic_resume);
+
+static const struct of_device_id regulator_haptic_dt_match[] = {
+	{ .compatible = "regulator-haptic" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, regulator_haptic_dt_match);
+
+static struct platform_driver regulator_haptic_driver = {
+	.probe		= regulator_haptic_probe,
+	.driver		= {
+		.name		= "regulator-haptic",
+		.of_match_table = regulator_haptic_dt_match,
+		.pm		= &regulator_haptic_pm_ops,
+	},
+};
+module_platform_driver(regulator_haptic_driver);
+
+MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
+MODULE_AUTHOR("Hyunhee Kim <hyunhee.kim@samsung.com>");
+MODULE_DESCRIPTION("Regulator haptic driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/retu-pwrbutton.c b/drivers/input/misc/retu-pwrbutton.c
new file mode 100644
index 0000000..30b459b
--- /dev/null
+++ b/drivers/input/misc/retu-pwrbutton.c
@@ -0,0 +1,98 @@
+/*
+ * Retu power button driver.
+ *
+ * Copyright (C) 2004-2010 Nokia Corporation
+ *
+ * Original code written by Ari Saastamoinen, Juha Yrjölä and Felipe Balbi.
+ * Rewritten by Aaro Koskinen.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/retu.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#define RETU_STATUS_PWRONX (1 << 5)
+
+static irqreturn_t retu_pwrbutton_irq(int irq, void *_pwr)
+{
+	struct input_dev *idev = _pwr;
+	struct retu_dev *rdev = input_get_drvdata(idev);
+	bool state;
+
+	state = !(retu_read(rdev, RETU_REG_STATUS) & RETU_STATUS_PWRONX);
+	input_report_key(idev, KEY_POWER, state);
+	input_sync(idev);
+
+	return IRQ_HANDLED;
+}
+
+static int retu_pwrbutton_probe(struct platform_device *pdev)
+{
+	struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
+	struct input_dev *idev;
+	int irq;
+	int error;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	idev = devm_input_allocate_device(&pdev->dev);
+	if (!idev)
+		return -ENOMEM;
+
+	idev->name = "retu-pwrbutton";
+	idev->dev.parent = &pdev->dev;
+
+	input_set_capability(idev, EV_KEY, KEY_POWER);
+	input_set_drvdata(idev, rdev);
+
+	error = devm_request_threaded_irq(&pdev->dev, irq,
+					  NULL, retu_pwrbutton_irq,
+					  IRQF_ONESHOT,
+					  "retu-pwrbutton", idev);
+	if (error)
+		return error;
+
+	error = input_register_device(idev);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static int retu_pwrbutton_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver retu_pwrbutton_driver = {
+	.probe		= retu_pwrbutton_probe,
+	.remove		= retu_pwrbutton_remove,
+	.driver		= {
+		.name	= "retu-pwrbutton",
+	},
+};
+module_platform_driver(retu_pwrbutton_driver);
+
+MODULE_ALIAS("platform:retu-pwrbutton");
+MODULE_DESCRIPTION("Retu Power Button");
+MODULE_AUTHOR("Ari Saastamoinen");
+MODULE_AUTHOR("Felipe Balbi");
+MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
new file mode 100644
index 0000000..8aee719
--- /dev/null
+++ b/drivers/input/misc/rotary_encoder.c
@@ -0,0 +1,447 @@
+/*
+ * rotary_encoder.c
+ *
+ * (c) 2009 Daniel Mack <daniel@caiaq.de>
+ * Copyright (C) 2011 Johan Hovold <jhovold@gmail.com>
+ *
+ * state machine code inspired by code from Tim Ruetz
+ *
+ * A generic driver for rotary encoders connected to GPIO lines.
+ * See file:Documentation/input/rotary-encoder.txt for more information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/rotary_encoder.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/pm.h>
+
+#define DRV_NAME "rotary-encoder"
+
+struct rotary_encoder {
+	struct input_dev *input;
+	const struct rotary_encoder_platform_data *pdata;
+
+	unsigned int axis;
+	unsigned int pos;
+
+	unsigned int irq_a;
+	unsigned int irq_b;
+
+	bool armed;
+	unsigned char dir;	/* 0 - clockwise, 1 - CCW */
+
+	char last_stable;
+};
+
+static int rotary_encoder_get_state(const struct rotary_encoder_platform_data *pdata)
+{
+	int a = !!gpio_get_value(pdata->gpio_a);
+	int b = !!gpio_get_value(pdata->gpio_b);
+
+	a ^= pdata->inverted_a;
+	b ^= pdata->inverted_b;
+
+	return ((a << 1) | b);
+}
+
+static void rotary_encoder_report_event(struct rotary_encoder *encoder)
+{
+	const struct rotary_encoder_platform_data *pdata = encoder->pdata;
+
+	if (pdata->relative_axis) {
+		input_report_rel(encoder->input,
+				 pdata->axis, encoder->dir ? -1 : 1);
+	} else {
+		unsigned int pos = encoder->pos;
+
+		if (encoder->dir) {
+			/* turning counter-clockwise */
+			if (pdata->rollover)
+				pos += pdata->steps;
+			if (pos)
+				pos--;
+		} else {
+			/* turning clockwise */
+			if (pdata->rollover || pos < pdata->steps)
+				pos++;
+		}
+
+		if (pdata->rollover)
+			pos %= pdata->steps;
+
+		encoder->pos = pos;
+		input_report_abs(encoder->input, pdata->axis, encoder->pos);
+	}
+
+	input_sync(encoder->input);
+}
+
+static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
+{
+	struct rotary_encoder *encoder = dev_id;
+	int state;
+
+	state = rotary_encoder_get_state(encoder->pdata);
+
+	switch (state) {
+	case 0x0:
+		if (encoder->armed) {
+			rotary_encoder_report_event(encoder);
+			encoder->armed = false;
+		}
+		break;
+
+	case 0x1:
+	case 0x2:
+		if (encoder->armed)
+			encoder->dir = state - 1;
+		break;
+
+	case 0x3:
+		encoder->armed = true;
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
+{
+	struct rotary_encoder *encoder = dev_id;
+	int state;
+
+	state = rotary_encoder_get_state(encoder->pdata);
+
+	switch (state) {
+	case 0x00:
+	case 0x03:
+		if (state != encoder->last_stable) {
+			rotary_encoder_report_event(encoder);
+			encoder->last_stable = state;
+		}
+		break;
+
+	case 0x01:
+	case 0x02:
+		encoder->dir = (encoder->last_stable + state) & 0x01;
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id)
+{
+	struct rotary_encoder *encoder = dev_id;
+	unsigned char sum;
+	int state;
+
+	state = rotary_encoder_get_state(encoder->pdata);
+
+	/*
+	 * We encode the previous and the current state using a byte.
+	 * The previous state in the MSB nibble, the current state in the LSB
+	 * nibble. Then use a table to decide the direction of the turn.
+	 */
+	sum = (encoder->last_stable << 4) + state;
+	switch (sum) {
+	case 0x31:
+	case 0x10:
+	case 0x02:
+	case 0x23:
+		encoder->dir = 0; /* clockwise */
+		break;
+
+	case 0x13:
+	case 0x01:
+	case 0x20:
+	case 0x32:
+		encoder->dir = 1; /* counter-clockwise */
+		break;
+
+	default:
+		/*
+		 * Ignore all other values. This covers the case when the
+		 * state didn't change (a spurious interrupt) and the
+		 * cases where the state changed by two steps, making it
+		 * impossible to tell the direction.
+		 *
+		 * In either case, don't report any event and save the
+		 * state for later.
+		 */
+		goto out;
+	}
+
+	rotary_encoder_report_event(encoder);
+
+out:
+	encoder->last_stable = state;
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rotary_encoder_of_match[] = {
+	{ .compatible = "rotary-encoder", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, rotary_encoder_of_match);
+
+static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct device *dev)
+{
+	const struct of_device_id *of_id =
+				of_match_device(rotary_encoder_of_match, dev);
+	struct device_node *np = dev->of_node;
+	struct rotary_encoder_platform_data *pdata;
+	enum of_gpio_flags flags;
+	int error;
+
+	if (!of_id || !np)
+		return NULL;
+
+	pdata = kzalloc(sizeof(struct rotary_encoder_platform_data),
+			GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	of_property_read_u32(np, "rotary-encoder,steps", &pdata->steps);
+	of_property_read_u32(np, "linux,axis", &pdata->axis);
+
+	pdata->gpio_a = of_get_gpio_flags(np, 0, &flags);
+	pdata->inverted_a = flags & OF_GPIO_ACTIVE_LOW;
+
+	pdata->gpio_b = of_get_gpio_flags(np, 1, &flags);
+	pdata->inverted_b = flags & OF_GPIO_ACTIVE_LOW;
+
+	pdata->relative_axis =
+		of_property_read_bool(np, "rotary-encoder,relative-axis");
+	pdata->rollover = of_property_read_bool(np, "rotary-encoder,rollover");
+
+	error = of_property_read_u32(np, "rotary-encoder,steps-per-period",
+				     &pdata->steps_per_period);
+	if (error) {
+		/*
+		 * The 'half-period' property has been deprecated, you must use
+		 * 'steps-per-period' and set an appropriate value, but we still
+		 * need to parse it to maintain compatibility.
+		 */
+		if (of_property_read_bool(np, "rotary-encoder,half-period")) {
+			pdata->steps_per_period = 2;
+		} else {
+			/* Fallback to one step per period behavior */
+			pdata->steps_per_period = 1;
+		}
+	}
+
+	pdata->wakeup_source = of_property_read_bool(np, "wakeup-source");
+
+	return pdata;
+}
+#else
+static inline struct rotary_encoder_platform_data *
+rotary_encoder_parse_dt(struct device *dev)
+{
+	return NULL;
+}
+#endif
+
+static int rotary_encoder_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct rotary_encoder_platform_data *pdata = dev_get_platdata(dev);
+	struct rotary_encoder *encoder;
+	struct input_dev *input;
+	irq_handler_t handler;
+	int err;
+
+	if (!pdata) {
+		pdata = rotary_encoder_parse_dt(dev);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+
+		if (!pdata) {
+			dev_err(dev, "missing platform data\n");
+			return -EINVAL;
+		}
+	}
+
+	encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL);
+	input = input_allocate_device();
+	if (!encoder || !input) {
+		err = -ENOMEM;
+		goto exit_free_mem;
+	}
+
+	encoder->input = input;
+	encoder->pdata = pdata;
+
+	input->name = pdev->name;
+	input->id.bustype = BUS_HOST;
+	input->dev.parent = dev;
+
+	if (pdata->relative_axis) {
+		input->evbit[0] = BIT_MASK(EV_REL);
+		input->relbit[0] = BIT_MASK(pdata->axis);
+	} else {
+		input->evbit[0] = BIT_MASK(EV_ABS);
+		input_set_abs_params(encoder->input,
+				     pdata->axis, 0, pdata->steps, 0, 1);
+	}
+
+	/* request the GPIOs */
+	err = gpio_request_one(pdata->gpio_a, GPIOF_IN, dev_name(dev));
+	if (err) {
+		dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_a);
+		goto exit_free_mem;
+	}
+
+	err = gpio_request_one(pdata->gpio_b, GPIOF_IN, dev_name(dev));
+	if (err) {
+		dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_b);
+		goto exit_free_gpio_a;
+	}
+
+	encoder->irq_a = gpio_to_irq(pdata->gpio_a);
+	encoder->irq_b = gpio_to_irq(pdata->gpio_b);
+
+	switch (pdata->steps_per_period) {
+	case 4:
+		handler = &rotary_encoder_quarter_period_irq;
+		encoder->last_stable = rotary_encoder_get_state(pdata);
+		break;
+	case 2:
+		handler = &rotary_encoder_half_period_irq;
+		encoder->last_stable = rotary_encoder_get_state(pdata);
+		break;
+	case 1:
+		handler = &rotary_encoder_irq;
+		break;
+	default:
+		dev_err(dev, "'%d' is not a valid steps-per-period value\n",
+			pdata->steps_per_period);
+		err = -EINVAL;
+		goto exit_free_gpio_b;
+	}
+
+	err = request_irq(encoder->irq_a, handler,
+			  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+			  DRV_NAME, encoder);
+	if (err) {
+		dev_err(dev, "unable to request IRQ %d\n", encoder->irq_a);
+		goto exit_free_gpio_b;
+	}
+
+	err = request_irq(encoder->irq_b, handler,
+			  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+			  DRV_NAME, encoder);
+	if (err) {
+		dev_err(dev, "unable to request IRQ %d\n", encoder->irq_b);
+		goto exit_free_irq_a;
+	}
+
+	err = input_register_device(input);
+	if (err) {
+		dev_err(dev, "failed to register input device\n");
+		goto exit_free_irq_b;
+	}
+
+	device_init_wakeup(&pdev->dev, pdata->wakeup_source);
+
+	platform_set_drvdata(pdev, encoder);
+
+	return 0;
+
+exit_free_irq_b:
+	free_irq(encoder->irq_b, encoder);
+exit_free_irq_a:
+	free_irq(encoder->irq_a, encoder);
+exit_free_gpio_b:
+	gpio_free(pdata->gpio_b);
+exit_free_gpio_a:
+	gpio_free(pdata->gpio_a);
+exit_free_mem:
+	input_free_device(input);
+	kfree(encoder);
+	if (!dev_get_platdata(&pdev->dev))
+		kfree(pdata);
+
+	return err;
+}
+
+static int rotary_encoder_remove(struct platform_device *pdev)
+{
+	struct rotary_encoder *encoder = platform_get_drvdata(pdev);
+	const struct rotary_encoder_platform_data *pdata = encoder->pdata;
+
+	device_init_wakeup(&pdev->dev, false);
+
+	free_irq(encoder->irq_a, encoder);
+	free_irq(encoder->irq_b, encoder);
+	gpio_free(pdata->gpio_a);
+	gpio_free(pdata->gpio_b);
+
+	input_unregister_device(encoder->input);
+	kfree(encoder);
+
+	if (!dev_get_platdata(&pdev->dev))
+		kfree(pdata);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rotary_encoder_suspend(struct device *dev)
+{
+	struct rotary_encoder *encoder = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev)) {
+		enable_irq_wake(encoder->irq_a);
+		enable_irq_wake(encoder->irq_b);
+	}
+
+	return 0;
+}
+
+static int rotary_encoder_resume(struct device *dev)
+{
+	struct rotary_encoder *encoder = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev)) {
+		disable_irq_wake(encoder->irq_a);
+		disable_irq_wake(encoder->irq_b);
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops,
+		 rotary_encoder_suspend, rotary_encoder_resume);
+
+static struct platform_driver rotary_encoder_driver = {
+	.probe		= rotary_encoder_probe,
+	.remove		= rotary_encoder_remove,
+	.driver		= {
+		.name	= DRV_NAME,
+		.pm	= &rotary_encoder_pm_ops,
+		.of_match_table = of_match_ptr(rotary_encoder_of_match),
+	}
+};
+module_platform_driver(rotary_encoder_driver);
+
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DESCRIPTION("GPIO rotary encoder driver");
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>, Johan Hovold");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/sgi_btns.c b/drivers/input/misc/sgi_btns.c
new file mode 100644
index 0000000..7bbe79d
--- /dev/null
+++ b/drivers/input/misc/sgi_btns.c
@@ -0,0 +1,164 @@
+/*
+ *  SGI Volume Button interface driver
+ *
+ *  Copyright (C) 2008  Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_SGI_IP22
+#include <asm/sgi/ioc.h>
+
+static inline u8 button_status(void)
+{
+	u8 status;
+
+	status = readb(&sgioc->panel) ^ 0xa0;
+	return ((status & 0x80) >> 6) | ((status & 0x20) >> 5);
+}
+#endif
+
+#ifdef CONFIG_SGI_IP32
+#include <asm/ip32/mace.h>
+
+static inline u8 button_status(void)
+{
+	u64 status;
+
+	status = readq(&mace->perif.audio.control);
+	writeq(status & ~(3U << 23), &mace->perif.audio.control);
+
+	return (status >> 23) & 3;
+}
+#endif
+
+#define BUTTONS_POLL_INTERVAL	30	/* msec */
+#define BUTTONS_COUNT_THRESHOLD	3
+
+static const unsigned short sgi_map[] = {
+	KEY_VOLUMEDOWN,
+	KEY_VOLUMEUP
+};
+
+struct buttons_dev {
+	struct input_polled_dev *poll_dev;
+	unsigned short keymap[ARRAY_SIZE(sgi_map)];
+	int count[ARRAY_SIZE(sgi_map)];
+};
+
+static void handle_buttons(struct input_polled_dev *dev)
+{
+	struct buttons_dev *bdev = dev->private;
+	struct input_dev *input = dev->input;
+	u8 status;
+	int i;
+
+	status = button_status();
+
+	for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
+		if (status & (1U << i)) {
+			if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
+				input_event(input, EV_MSC, MSC_SCAN, i);
+				input_report_key(input, bdev->keymap[i], 1);
+				input_sync(input);
+			}
+		} else {
+			if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
+				input_event(input, EV_MSC, MSC_SCAN, i);
+				input_report_key(input, bdev->keymap[i], 0);
+				input_sync(input);
+			}
+			bdev->count[i] = 0;
+		}
+	}
+}
+
+static int sgi_buttons_probe(struct platform_device *pdev)
+{
+	struct buttons_dev *bdev;
+	struct input_polled_dev *poll_dev;
+	struct input_dev *input;
+	int error, i;
+
+	bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
+	poll_dev = input_allocate_polled_device();
+	if (!bdev || !poll_dev) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	memcpy(bdev->keymap, sgi_map, sizeof(bdev->keymap));
+
+	poll_dev->private = bdev;
+	poll_dev->poll = handle_buttons;
+	poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
+
+	input = poll_dev->input;
+	input->name = "SGI buttons";
+	input->phys = "sgi/input0";
+	input->id.bustype = BUS_HOST;
+	input->dev.parent = &pdev->dev;
+
+	input->keycode = bdev->keymap;
+	input->keycodemax = ARRAY_SIZE(bdev->keymap);
+	input->keycodesize = sizeof(unsigned short);
+
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+	__set_bit(EV_KEY, input->evbit);
+	for (i = 0; i < ARRAY_SIZE(sgi_map); i++)
+		__set_bit(bdev->keymap[i], input->keybit);
+	__clear_bit(KEY_RESERVED, input->keybit);
+
+	bdev->poll_dev = poll_dev;
+	platform_set_drvdata(pdev, bdev);
+
+	error = input_register_polled_device(poll_dev);
+	if (error)
+		goto err_free_mem;
+
+	return 0;
+
+ err_free_mem:
+	input_free_polled_device(poll_dev);
+	kfree(bdev);
+	return error;
+}
+
+static int sgi_buttons_remove(struct platform_device *pdev)
+{
+	struct buttons_dev *bdev = platform_get_drvdata(pdev);
+
+	input_unregister_polled_device(bdev->poll_dev);
+	input_free_polled_device(bdev->poll_dev);
+	kfree(bdev);
+
+	return 0;
+}
+
+static struct platform_driver sgi_buttons_driver = {
+	.probe	= sgi_buttons_probe,
+	.remove	= sgi_buttons_remove,
+	.driver	= {
+		.name	= "sgibtns",
+	},
+};
+module_platform_driver(sgi_buttons_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c
new file mode 100644
index 0000000..9d5b89b
--- /dev/null
+++ b/drivers/input/misc/sirfsoc-onkey.c
@@ -0,0 +1,216 @@
+/*
+ * Power key driver for SiRF PrimaII
+ *
+ * Copyright (c) 2013 - 2014 Cambridge Silicon Radio Limited, a CSR plc group
+ * company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/rtc/sirfsoc_rtciobrg.h>
+#include <linux/of.h>
+#include <linux/workqueue.h>
+
+struct sirfsoc_pwrc_drvdata {
+	u32			pwrc_base;
+	struct input_dev	*input;
+	struct delayed_work	work;
+};
+
+#define PWRC_ON_KEY_BIT			(1 << 0)
+
+#define PWRC_INT_STATUS			0xc
+#define PWRC_INT_MASK			0x10
+#define PWRC_PIN_STATUS			0x14
+#define PWRC_KEY_DETECT_UP_TIME		20	/* ms*/
+
+static int sirfsoc_pwrc_is_on_key_down(struct sirfsoc_pwrc_drvdata *pwrcdrv)
+{
+	u32 state = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base +
+							PWRC_PIN_STATUS);
+	return !(state & PWRC_ON_KEY_BIT); /* ON_KEY is active low */
+}
+
+static void sirfsoc_pwrc_report_event(struct work_struct *work)
+{
+	struct sirfsoc_pwrc_drvdata *pwrcdrv =
+		container_of(work, struct sirfsoc_pwrc_drvdata, work.work);
+
+	if (sirfsoc_pwrc_is_on_key_down(pwrcdrv)) {
+		schedule_delayed_work(&pwrcdrv->work,
+			msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME));
+	} else {
+		input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 0);
+		input_sync(pwrcdrv->input);
+	}
+}
+
+static irqreturn_t sirfsoc_pwrc_isr(int irq, void *dev_id)
+{
+	struct sirfsoc_pwrc_drvdata *pwrcdrv = dev_id;
+	u32 int_status;
+
+	int_status = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base +
+							PWRC_INT_STATUS);
+	sirfsoc_rtc_iobrg_writel(int_status & ~PWRC_ON_KEY_BIT,
+				 pwrcdrv->pwrc_base + PWRC_INT_STATUS);
+
+	input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 1);
+	input_sync(pwrcdrv->input);
+	schedule_delayed_work(&pwrcdrv->work,
+			      msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME));
+
+	return IRQ_HANDLED;
+}
+
+static void sirfsoc_pwrc_toggle_interrupts(struct sirfsoc_pwrc_drvdata *pwrcdrv,
+					   bool enable)
+{
+	u32 int_mask;
+
+	int_mask = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + PWRC_INT_MASK);
+	if (enable)
+		int_mask |= PWRC_ON_KEY_BIT;
+	else
+		int_mask &= ~PWRC_ON_KEY_BIT;
+	sirfsoc_rtc_iobrg_writel(int_mask, pwrcdrv->pwrc_base + PWRC_INT_MASK);
+}
+
+static int sirfsoc_pwrc_open(struct input_dev *input)
+{
+	struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input);
+
+	sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true);
+
+	return 0;
+}
+
+static void sirfsoc_pwrc_close(struct input_dev *input)
+{
+	struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input);
+
+	sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false);
+	cancel_delayed_work_sync(&pwrcdrv->work);
+}
+
+static const struct of_device_id sirfsoc_pwrc_of_match[] = {
+	{ .compatible = "sirf,prima2-pwrc" },
+	{},
+}
+MODULE_DEVICE_TABLE(of, sirfsoc_pwrc_of_match);
+
+static int sirfsoc_pwrc_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct sirfsoc_pwrc_drvdata *pwrcdrv;
+	int irq;
+	int error;
+
+	pwrcdrv = devm_kzalloc(&pdev->dev, sizeof(struct sirfsoc_pwrc_drvdata),
+			       GFP_KERNEL);
+	if (!pwrcdrv) {
+		dev_info(&pdev->dev, "Not enough memory for the device data\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * We can't use of_iomap because pwrc is not mapped in memory,
+	 * the so-called base address is only offset in rtciobrg
+	 */
+	error = of_property_read_u32(np, "reg", &pwrcdrv->pwrc_base);
+	if (error) {
+		dev_err(&pdev->dev,
+			"unable to find base address of pwrc node in dtb\n");
+		return error;
+	}
+
+	pwrcdrv->input = devm_input_allocate_device(&pdev->dev);
+	if (!pwrcdrv->input)
+		return -ENOMEM;
+
+	pwrcdrv->input->name = "sirfsoc pwrckey";
+	pwrcdrv->input->phys = "pwrc/input0";
+	pwrcdrv->input->evbit[0] = BIT_MASK(EV_KEY);
+	input_set_capability(pwrcdrv->input, EV_KEY, KEY_POWER);
+
+	INIT_DELAYED_WORK(&pwrcdrv->work, sirfsoc_pwrc_report_event);
+
+	pwrcdrv->input->open = sirfsoc_pwrc_open;
+	pwrcdrv->input->close = sirfsoc_pwrc_close;
+
+	input_set_drvdata(pwrcdrv->input, pwrcdrv);
+
+	/* Make sure the device is quiesced */
+	sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false);
+
+	irq = platform_get_irq(pdev, 0);
+	error = devm_request_irq(&pdev->dev, irq,
+				 sirfsoc_pwrc_isr, 0,
+				 "sirfsoc_pwrc_int", pwrcdrv);
+	if (error) {
+		dev_err(&pdev->dev, "unable to claim irq %d, error: %d\n",
+			irq, error);
+		return error;
+	}
+
+	error = input_register_device(pwrcdrv->input);
+	if (error) {
+		dev_err(&pdev->dev,
+			"unable to register input device, error: %d\n",
+			error);
+		return error;
+	}
+
+	dev_set_drvdata(&pdev->dev, pwrcdrv);
+	device_init_wakeup(&pdev->dev, 1);
+
+	return 0;
+}
+
+static int sirfsoc_pwrc_remove(struct platform_device *pdev)
+{
+	device_init_wakeup(&pdev->dev, 0);
+
+	return 0;
+}
+
+static int __maybe_unused sirfsoc_pwrc_resume(struct device *dev)
+{
+	struct sirfsoc_pwrc_drvdata *pwrcdrv = dev_get_drvdata(dev);
+	struct input_dev *input = pwrcdrv->input;
+
+	/*
+	 * Do not mask pwrc interrupt as we want pwrc work as a wakeup source
+	 * if users touch X_ONKEY_B, see arch/arm/mach-prima2/pm.c
+	 */
+	mutex_lock(&input->mutex);
+	if (input->users)
+		sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true);
+	mutex_unlock(&input->mutex);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(sirfsoc_pwrc_pm_ops, NULL, sirfsoc_pwrc_resume);
+
+static struct platform_driver sirfsoc_pwrc_driver = {
+	.probe		= sirfsoc_pwrc_probe,
+	.remove		= sirfsoc_pwrc_remove,
+	.driver		= {
+		.name	= "sirfsoc-pwrc",
+		.pm	= &sirfsoc_pwrc_pm_ops,
+		.of_match_table = sirfsoc_pwrc_of_match,
+	}
+};
+
+module_platform_driver(sirfsoc_pwrc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Binghua Duan <Binghua.Duan@csr.com>, Xianglong Du <Xianglong.Du@csr.com>");
+MODULE_DESCRIPTION("CSR Prima2 PWRC Driver");
+MODULE_ALIAS("platform:sirfsoc-pwrc");
diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c
new file mode 100644
index 0000000..c14b827
--- /dev/null
+++ b/drivers/input/misc/soc_button_array.c
@@ -0,0 +1,221 @@
+/*
+ * Supports for the button array on SoC tablets originally running
+ * Windows 8.
+ *
+ * (C) Copyright 2014 Intel Corporation
+ *
+ * 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; version 2
+ * of the License.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio_keys.h>
+#include <linux/platform_device.h>
+
+/*
+ * Definition of buttons on the tablet. The ACPI index of each button
+ * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
+ * Platforms"
+ */
+#define MAX_NBUTTONS	5
+
+struct soc_button_info {
+	const char *name;
+	int acpi_index;
+	unsigned int event_type;
+	unsigned int event_code;
+	bool autorepeat;
+	bool wakeup;
+};
+
+/*
+ * Some of the buttons like volume up/down are auto repeat, while others
+ * are not. To support both, we register two platform devices, and put
+ * buttons into them based on whether the key should be auto repeat.
+ */
+#define BUTTON_TYPES	2
+
+struct soc_button_data {
+	struct platform_device *children[BUTTON_TYPES];
+};
+
+/*
+ * Get the Nth GPIO number from the ACPI object.
+ */
+static int soc_button_lookup_gpio(struct device *dev, int acpi_index)
+{
+	struct gpio_desc *desc;
+	int gpio;
+
+	desc = gpiod_get_index(dev, KBUILD_MODNAME, acpi_index, GPIOD_ASIS);
+	if (IS_ERR(desc))
+		return PTR_ERR(desc);
+
+	gpio = desc_to_gpio(desc);
+
+	gpiod_put(desc);
+
+	return gpio;
+}
+
+static struct platform_device *
+soc_button_device_create(struct platform_device *pdev,
+			 const struct soc_button_info *button_info,
+			 bool autorepeat)
+{
+	const struct soc_button_info *info;
+	struct platform_device *pd;
+	struct gpio_keys_button *gpio_keys;
+	struct gpio_keys_platform_data *gpio_keys_pdata;
+	int n_buttons = 0;
+	int gpio;
+	int error;
+
+	gpio_keys_pdata = devm_kzalloc(&pdev->dev,
+				       sizeof(*gpio_keys_pdata) +
+					sizeof(*gpio_keys) * MAX_NBUTTONS,
+				       GFP_KERNEL);
+	if (!gpio_keys_pdata)
+		return ERR_PTR(-ENOMEM);
+
+	gpio_keys = (void *)(gpio_keys_pdata + 1);
+
+	for (info = button_info; info->name; info++) {
+		if (info->autorepeat != autorepeat)
+			continue;
+
+		gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index);
+		if (gpio < 0)
+			continue;
+
+		gpio_keys[n_buttons].type = info->event_type;
+		gpio_keys[n_buttons].code = info->event_code;
+		gpio_keys[n_buttons].gpio = gpio;
+		gpio_keys[n_buttons].active_low = 1;
+		gpio_keys[n_buttons].desc = info->name;
+		gpio_keys[n_buttons].wakeup = info->wakeup;
+		n_buttons++;
+	}
+
+	if (n_buttons == 0) {
+		error = -ENODEV;
+		goto err_free_mem;
+	}
+
+	gpio_keys_pdata->buttons = gpio_keys;
+	gpio_keys_pdata->nbuttons = n_buttons;
+	gpio_keys_pdata->rep = autorepeat;
+
+	pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO);
+	if (!pd) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	error = platform_device_add_data(pd, gpio_keys_pdata,
+					 sizeof(*gpio_keys_pdata));
+	if (error)
+		goto err_free_pdev;
+
+	error = platform_device_add(pd);
+	if (error)
+		goto err_free_pdev;
+
+	return pd;
+
+err_free_pdev:
+	platform_device_put(pd);
+err_free_mem:
+	devm_kfree(&pdev->dev, gpio_keys_pdata);
+	return ERR_PTR(error);
+}
+
+static int soc_button_remove(struct platform_device *pdev)
+{
+	struct soc_button_data *priv = platform_get_drvdata(pdev);
+
+	int i;
+
+	for (i = 0; i < BUTTON_TYPES; i++)
+		if (priv->children[i])
+			platform_device_unregister(priv->children[i]);
+
+	return 0;
+}
+
+static int soc_button_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct acpi_device_id *id;
+	struct soc_button_info *button_info;
+	struct soc_button_data *priv;
+	struct platform_device *pd;
+	int i;
+	int error;
+
+	id = acpi_match_device(dev->driver->acpi_match_table, dev);
+	if (!id)
+		return -ENODEV;
+
+	button_info = (struct soc_button_info *)id->driver_data;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, priv);
+
+	for (i = 0; i < BUTTON_TYPES; i++) {
+		pd = soc_button_device_create(pdev, button_info, i == 0);
+		if (IS_ERR(pd)) {
+			error = PTR_ERR(pd);
+			if (error != -ENODEV) {
+				soc_button_remove(pdev);
+				return error;
+			}
+			continue;
+		}
+
+		priv->children[i] = pd;
+	}
+
+	if (!priv->children[0] && !priv->children[1])
+		return -ENODEV;
+
+	return 0;
+}
+
+static struct soc_button_info soc_button_PNP0C40[] = {
+	{ "power", 0, EV_KEY, KEY_POWER, false, true },
+	{ "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
+	{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
+	{ "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false },
+	{ "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false },
+	{ }
+};
+
+static const struct acpi_device_id soc_button_acpi_match[] = {
+	{ "PNP0C40", (unsigned long)soc_button_PNP0C40 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(acpi, soc_button_acpi_match);
+
+static struct platform_driver soc_button_driver = {
+	.probe          = soc_button_probe,
+	.remove		= soc_button_remove,
+	.driver		= {
+		.name = KBUILD_MODNAME,
+		.acpi_match_table = ACPI_PTR(soc_button_acpi_match),
+	},
+};
+module_platform_driver(soc_button_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c
new file mode 100644
index 0000000..6f997aa
--- /dev/null
+++ b/drivers/input/misc/sparcspkr.c
@@ -0,0 +1,368 @@
+/*
+ *  Driver for PC-speaker like devices found on various Sparc systems.
+ *
+ *  Copyright (c) 2002 Vojtech Pavlik
+ *  Copyright (c) 2002, 2006, 2008 David S. Miller (davem@davemloft.net)
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
+MODULE_DESCRIPTION("Sparc Speaker beeper driver");
+MODULE_LICENSE("GPL");
+
+struct grover_beep_info {
+	void __iomem	*freq_regs;
+	void __iomem	*enable_reg;
+};
+
+struct bbc_beep_info {
+	u32		clock_freq;
+	void __iomem	*regs;
+};
+
+struct sparcspkr_state {
+	const char		*name;
+	int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
+	spinlock_t		lock;
+	struct input_dev	*input_dev;
+	union {
+		struct grover_beep_info grover;
+		struct bbc_beep_info bbc;
+	} u;
+};
+
+static u32 bbc_count_to_reg(struct bbc_beep_info *info, unsigned int count)
+{
+	u32 val, clock_freq = info->clock_freq;
+	int i;
+
+	if (!count)
+		return 0;
+
+	if (count <= clock_freq >> 20)
+		return 1 << 18;
+
+	if (count >= clock_freq >> 12)
+		return 1 << 10;
+
+	val = 1 << 18;
+	for (i = 19; i >= 11; i--) {
+		val >>= 1;
+		if (count <= clock_freq >> i)
+			break;
+	}
+
+	return val;
+}
+
+static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
+	struct bbc_beep_info *info = &state->u.bbc;
+	unsigned int count = 0;
+	unsigned long flags;
+
+	if (type != EV_SND)
+		return -1;
+
+	switch (code) {
+		case SND_BELL: if (value) value = 1000;
+		case SND_TONE: break;
+		default: return -1;
+	}
+
+	if (value > 20 && value < 32767)
+		count = 1193182 / value;
+
+	count = bbc_count_to_reg(info, count);
+
+	spin_lock_irqsave(&state->lock, flags);
+
+	if (count) {
+		sbus_writeb(0x01,                 info->regs + 0);
+		sbus_writeb(0x00,                 info->regs + 2);
+		sbus_writeb((count >> 16) & 0xff, info->regs + 3);
+		sbus_writeb((count >>  8) & 0xff, info->regs + 4);
+		sbus_writeb(0x00,                 info->regs + 5);
+	} else {
+		sbus_writeb(0x00,                 info->regs + 0);
+	}
+
+	spin_unlock_irqrestore(&state->lock, flags);
+
+	return 0;
+}
+
+static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
+	struct grover_beep_info *info = &state->u.grover;
+	unsigned int count = 0;
+	unsigned long flags;
+
+	if (type != EV_SND)
+		return -1;
+
+	switch (code) {
+		case SND_BELL: if (value) value = 1000;
+		case SND_TONE: break;
+		default: return -1;
+	}
+
+	if (value > 20 && value < 32767)
+		count = 1193182 / value;
+
+	spin_lock_irqsave(&state->lock, flags);
+
+	if (count) {
+		/* enable counter 2 */
+		sbus_writeb(sbus_readb(info->enable_reg) | 3, info->enable_reg);
+		/* set command for counter 2, 2 byte write */
+		sbus_writeb(0xB6, info->freq_regs + 1);
+		/* select desired HZ */
+		sbus_writeb(count & 0xff, info->freq_regs + 0);
+		sbus_writeb((count >> 8) & 0xff, info->freq_regs + 0);
+	} else {
+		/* disable counter 2 */
+		sbus_writeb(sbus_readb(info->enable_reg) & 0xFC, info->enable_reg);
+	}
+
+	spin_unlock_irqrestore(&state->lock, flags);
+
+	return 0;
+}
+
+static int sparcspkr_probe(struct device *dev)
+{
+	struct sparcspkr_state *state = dev_get_drvdata(dev);
+	struct input_dev *input_dev;
+	int error;
+
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	input_dev->name = state->name;
+	input_dev->phys = "sparc/input0";
+	input_dev->id.bustype = BUS_ISA;
+	input_dev->id.vendor = 0x001f;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_SND);
+	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+
+	input_dev->event = state->event;
+
+	error = input_register_device(input_dev);
+	if (error) {
+		input_free_device(input_dev);
+		return error;
+	}
+
+	state->input_dev = input_dev;
+
+	return 0;
+}
+
+static void sparcspkr_shutdown(struct platform_device *dev)
+{
+	struct sparcspkr_state *state = platform_get_drvdata(dev);
+	struct input_dev *input_dev = state->input_dev;
+
+	/* turn off the speaker */
+	state->event(input_dev, EV_SND, SND_BELL, 0);
+}
+
+static int bbc_beep_probe(struct platform_device *op)
+{
+	struct sparcspkr_state *state;
+	struct bbc_beep_info *info;
+	struct device_node *dp;
+	int err = -ENOMEM;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		goto out_err;
+
+	state->name = "Sparc BBC Speaker";
+	state->event = bbc_spkr_event;
+	spin_lock_init(&state->lock);
+
+	dp = of_find_node_by_path("/");
+	err = -ENODEV;
+	if (!dp)
+		goto out_free;
+
+	info = &state->u.bbc;
+	info->clock_freq = of_getintprop_default(dp, "clock-frequency", 0);
+	if (!info->clock_freq)
+		goto out_free;
+
+	info->regs = of_ioremap(&op->resource[0], 0, 6, "bbc beep");
+	if (!info->regs)
+		goto out_free;
+
+	platform_set_drvdata(op, state);
+
+	err = sparcspkr_probe(&op->dev);
+	if (err)
+		goto out_clear_drvdata;
+
+	return 0;
+
+out_clear_drvdata:
+	of_iounmap(&op->resource[0], info->regs, 6);
+
+out_free:
+	kfree(state);
+out_err:
+	return err;
+}
+
+static int bbc_remove(struct platform_device *op)
+{
+	struct sparcspkr_state *state = platform_get_drvdata(op);
+	struct input_dev *input_dev = state->input_dev;
+	struct bbc_beep_info *info = &state->u.bbc;
+
+	/* turn off the speaker */
+	state->event(input_dev, EV_SND, SND_BELL, 0);
+
+	input_unregister_device(input_dev);
+
+	of_iounmap(&op->resource[0], info->regs, 6);
+
+	kfree(state);
+
+	return 0;
+}
+
+static const struct of_device_id bbc_beep_match[] = {
+	{
+		.name = "beep",
+		.compatible = "SUNW,bbc-beep",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, bbc_beep_match);
+
+static struct platform_driver bbc_beep_driver = {
+	.driver = {
+		.name = "bbcbeep",
+		.of_match_table = bbc_beep_match,
+	},
+	.probe		= bbc_beep_probe,
+	.remove		= bbc_remove,
+	.shutdown	= sparcspkr_shutdown,
+};
+
+static int grover_beep_probe(struct platform_device *op)
+{
+	struct sparcspkr_state *state;
+	struct grover_beep_info *info;
+	int err = -ENOMEM;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		goto out_err;
+
+	state->name = "Sparc Grover Speaker";
+	state->event = grover_spkr_event;
+	spin_lock_init(&state->lock);
+
+	info = &state->u.grover;
+	info->freq_regs = of_ioremap(&op->resource[2], 0, 2, "grover beep freq");
+	if (!info->freq_regs)
+		goto out_free;
+
+	info->enable_reg = of_ioremap(&op->resource[3], 0, 1, "grover beep enable");
+	if (!info->enable_reg)
+		goto out_unmap_freq_regs;
+
+	platform_set_drvdata(op, state);
+
+	err = sparcspkr_probe(&op->dev);
+	if (err)
+		goto out_clear_drvdata;
+
+	return 0;
+
+out_clear_drvdata:
+	of_iounmap(&op->resource[3], info->enable_reg, 1);
+
+out_unmap_freq_regs:
+	of_iounmap(&op->resource[2], info->freq_regs, 2);
+out_free:
+	kfree(state);
+out_err:
+	return err;
+}
+
+static int grover_remove(struct platform_device *op)
+{
+	struct sparcspkr_state *state = platform_get_drvdata(op);
+	struct grover_beep_info *info = &state->u.grover;
+	struct input_dev *input_dev = state->input_dev;
+
+	/* turn off the speaker */
+	state->event(input_dev, EV_SND, SND_BELL, 0);
+
+	input_unregister_device(input_dev);
+
+	of_iounmap(&op->resource[3], info->enable_reg, 1);
+	of_iounmap(&op->resource[2], info->freq_regs, 2);
+
+	kfree(state);
+
+	return 0;
+}
+
+static const struct of_device_id grover_beep_match[] = {
+	{
+		.name = "beep",
+		.compatible = "SUNW,smbus-beep",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, grover_beep_match);
+
+static struct platform_driver grover_beep_driver = {
+	.driver = {
+		.name = "groverbeep",
+		.of_match_table = grover_beep_match,
+	},
+	.probe		= grover_beep_probe,
+	.remove		= grover_remove,
+	.shutdown	= sparcspkr_shutdown,
+};
+
+static int __init sparcspkr_init(void)
+{
+	int err = platform_driver_register(&bbc_beep_driver);
+
+	if (!err) {
+		err = platform_driver_register(&grover_beep_driver);
+		if (err)
+			platform_driver_unregister(&bbc_beep_driver);
+	}
+
+	return err;
+}
+
+static void __exit sparcspkr_exit(void)
+{
+	platform_driver_unregister(&bbc_beep_driver);
+	platform_driver_unregister(&grover_beep_driver);
+}
+
+module_init(sparcspkr_init);
+module_exit(sparcspkr_exit);
diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c
new file mode 100644
index 0000000..a39b626
--- /dev/null
+++ b/drivers/input/misc/tps65218-pwrbutton.c
@@ -0,0 +1,126 @@
+/*
+ * Texas Instruments' TPS65218 Power Button Input Driver
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Felipe Balbi <balbi@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/tps65218.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct tps65218_pwrbutton {
+	struct device *dev;
+	struct tps65218 *tps;
+	struct input_dev *idev;
+};
+
+static irqreturn_t tps65218_pwr_irq(int irq, void *_pwr)
+{
+	struct tps65218_pwrbutton *pwr = _pwr;
+	unsigned int reg;
+	int error;
+
+	error = tps65218_reg_read(pwr->tps, TPS65218_REG_STATUS, &reg);
+	if (error) {
+		dev_err(pwr->dev, "can't read register: %d\n", error);
+		goto out;
+	}
+
+	if (reg & TPS65218_STATUS_PB_STATE) {
+		input_report_key(pwr->idev, KEY_POWER, 1);
+		pm_wakeup_event(pwr->dev, 0);
+	} else {
+		input_report_key(pwr->idev, KEY_POWER, 0);
+	}
+
+	input_sync(pwr->idev);
+
+out:
+	return IRQ_HANDLED;
+}
+
+static int tps65218_pwron_probe(struct platform_device *pdev)
+{
+	struct tps65218 *tps = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct tps65218_pwrbutton *pwr;
+	struct input_dev *idev;
+	int error;
+	int irq;
+
+	pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL);
+	if (!pwr)
+		return -ENOMEM;
+
+	idev = devm_input_allocate_device(dev);
+	if (!idev)
+		return -ENOMEM;
+
+	idev->name = "tps65218_pwrbutton";
+	idev->phys = "tps65218_pwrbutton/input0";
+	idev->dev.parent = dev;
+	idev->id.bustype = BUS_I2C;
+
+	input_set_capability(idev, EV_KEY, KEY_POWER);
+
+	pwr->tps = tps;
+	pwr->dev = dev;
+	pwr->idev = idev;
+	platform_set_drvdata(pdev, pwr);
+	device_init_wakeup(dev, true);
+
+	irq = platform_get_irq(pdev, 0);
+	error = devm_request_threaded_irq(dev, irq, NULL, tps65218_pwr_irq,
+					  IRQF_TRIGGER_RISING |
+						IRQF_TRIGGER_FALLING |
+						IRQF_ONESHOT,
+					  "tps65218-pwrbutton", pwr);
+	if (error) {
+		dev_err(dev, "failed to request IRQ #%d: %d\n",
+			irq, error);
+		return error;
+	}
+
+	error= input_register_device(idev);
+	if (error) {
+		dev_err(dev, "Can't register power button: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id of_tps65218_pwr_match[] = {
+	{ .compatible = "ti,tps65218-pwrbutton" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, of_tps65218_pwr_match);
+
+static struct platform_driver tps65218_pwron_driver = {
+	.probe	= tps65218_pwron_probe,
+	.driver	= {
+		.name	= "tps65218_pwrbutton",
+		.of_match_table = of_tps65218_pwr_match,
+	},
+};
+module_platform_driver(tps65218_pwron_driver);
+
+MODULE_DESCRIPTION("TPS65218 Power Button");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
new file mode 100644
index 0000000..603fc2f
--- /dev/null
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -0,0 +1,116 @@
+/**
+ * twl4030-pwrbutton.c - TWL4030 Power Button Input Driver
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Written by Peter De Schrijver <peter.de-schrijver@nokia.com>
+ * Several fixes by Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl.h>
+
+#define PWR_PWRON_IRQ (1 << 0)
+
+#define STS_HW_CONDITIONS 0xf
+
+static irqreturn_t powerbutton_irq(int irq, void *_pwr)
+{
+	struct input_dev *pwr = _pwr;
+	int err;
+	u8 value;
+
+	err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &value, STS_HW_CONDITIONS);
+	if (!err)  {
+		pm_wakeup_event(pwr->dev.parent, 0);
+		input_report_key(pwr, KEY_POWER, value & PWR_PWRON_IRQ);
+		input_sync(pwr);
+	} else {
+		dev_err(pwr->dev.parent, "twl4030: i2c error %d while reading"
+			" TWL4030 PM_MASTER STS_HW_CONDITIONS register\n", err);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int twl4030_pwrbutton_probe(struct platform_device *pdev)
+{
+	struct input_dev *pwr;
+	int irq = platform_get_irq(pdev, 0);
+	int err;
+
+	pwr = devm_input_allocate_device(&pdev->dev);
+	if (!pwr) {
+		dev_err(&pdev->dev, "Can't allocate power button\n");
+		return -ENOMEM;
+	}
+
+	pwr->evbit[0] = BIT_MASK(EV_KEY);
+	pwr->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+	pwr->name = "twl4030_pwrbutton";
+	pwr->phys = "twl4030_pwrbutton/input0";
+	pwr->dev.parent = &pdev->dev;
+
+	err = devm_request_threaded_irq(&pwr->dev, irq, NULL, powerbutton_irq,
+			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+			IRQF_ONESHOT,
+			"twl4030_pwrbutton", pwr);
+	if (err < 0) {
+		dev_err(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err);
+		return err;
+	}
+
+	err = input_register_device(pwr);
+	if (err) {
+		dev_err(&pdev->dev, "Can't register power button: %d\n", err);
+		return err;
+	}
+
+	platform_set_drvdata(pdev, pwr);
+	device_init_wakeup(&pdev->dev, true);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id twl4030_pwrbutton_dt_match_table[] = {
+       { .compatible = "ti,twl4030-pwrbutton" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, twl4030_pwrbutton_dt_match_table);
+#endif
+
+static struct platform_driver twl4030_pwrbutton_driver = {
+	.probe		= twl4030_pwrbutton_probe,
+	.driver		= {
+		.name	= "twl4030_pwrbutton",
+		.of_match_table = of_match_ptr(twl4030_pwrbutton_dt_match_table),
+	},
+};
+module_platform_driver(twl4030_pwrbutton_driver);
+
+MODULE_ALIAS("platform:twl4030_pwrbutton");
+MODULE_DESCRIPTION("Triton2 Power Button");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter De Schrijver <peter.de-schrijver@nokia.com>");
+MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");
+
diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c
new file mode 100644
index 0000000..10c4e3d
--- /dev/null
+++ b/drivers/input/misc/twl4030-vibra.c
@@ -0,0 +1,263 @@
+/*
+ * twl4030-vibra.c - TWL4030 Vibrator driver
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ *
+ * Written by Henrik Saari <henrik.saari@nokia.com>
+ * Updates by Felipe Balbi <felipe.balbi@nokia.com>
+ * Input by Jari Vanhala <ext-jari.vanhala@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/workqueue.h>
+#include <linux/i2c/twl.h>
+#include <linux/mfd/twl4030-audio.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+/* MODULE ID2 */
+#define LEDEN		0x00
+
+/* ForceFeedback */
+#define EFFECT_DIR_180_DEG	0x8000 /* range is 0 - 0xFFFF */
+
+struct vibra_info {
+	struct device		*dev;
+	struct input_dev	*input_dev;
+
+	struct work_struct	play_work;
+
+	bool			enabled;
+	int			speed;
+	int			direction;
+
+	bool			coexist;
+};
+
+static void vibra_disable_leds(void)
+{
+	u8 reg;
+
+	/* Disable LEDA & LEDB, cannot be used with vibra (PWM) */
+	twl_i2c_read_u8(TWL4030_MODULE_LED, &reg, LEDEN);
+	reg &= ~0x03;
+	twl_i2c_write_u8(TWL4030_MODULE_LED, LEDEN, reg);
+}
+
+/* Powers H-Bridge and enables audio clk */
+static void vibra_enable(struct vibra_info *info)
+{
+	u8 reg;
+
+	twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER);
+
+	/* turn H-Bridge on */
+	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
+			&reg, TWL4030_REG_VIBRA_CTL);
+	twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+			 (reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
+
+	twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL);
+
+	info->enabled = true;
+}
+
+static void vibra_disable(struct vibra_info *info)
+{
+	u8 reg;
+
+	/* Power down H-Bridge */
+	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
+			&reg, TWL4030_REG_VIBRA_CTL);
+	twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+			 (reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
+
+	twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL);
+	twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
+
+	info->enabled = false;
+}
+
+static void vibra_play_work(struct work_struct *work)
+{
+	struct vibra_info *info = container_of(work,
+			struct vibra_info, play_work);
+	int dir;
+	int pwm;
+	u8 reg;
+
+	dir = info->direction;
+	pwm = info->speed;
+
+	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
+			&reg, TWL4030_REG_VIBRA_CTL);
+	if (pwm && (!info->coexist || !(reg & TWL4030_VIBRA_SEL))) {
+
+		if (!info->enabled)
+			vibra_enable(info);
+
+		/* set vibra rotation direction */
+		twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
+				&reg, TWL4030_REG_VIBRA_CTL);
+		reg = (dir) ? (reg | TWL4030_VIBRA_DIR) :
+			(reg & ~TWL4030_VIBRA_DIR);
+		twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+				 reg, TWL4030_REG_VIBRA_CTL);
+
+		/* set PWM, 1 = max, 255 = min */
+		twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+				 256 - pwm, TWL4030_REG_VIBRA_SET);
+	} else {
+		if (info->enabled)
+			vibra_disable(info);
+	}
+}
+
+/*** Input/ForceFeedback ***/
+
+static int vibra_play(struct input_dev *input, void *data,
+		      struct ff_effect *effect)
+{
+	struct vibra_info *info = input_get_drvdata(input);
+
+	info->speed = effect->u.rumble.strong_magnitude >> 8;
+	if (!info->speed)
+		info->speed = effect->u.rumble.weak_magnitude >> 9;
+	info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1;
+	schedule_work(&info->play_work);
+	return 0;
+}
+
+static void twl4030_vibra_close(struct input_dev *input)
+{
+	struct vibra_info *info = input_get_drvdata(input);
+
+	cancel_work_sync(&info->play_work);
+
+	if (info->enabled)
+		vibra_disable(info);
+}
+
+/*** Module ***/
+static int __maybe_unused twl4030_vibra_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vibra_info *info = platform_get_drvdata(pdev);
+
+	if (info->enabled)
+		vibra_disable(info);
+
+	return 0;
+}
+
+static int __maybe_unused twl4030_vibra_resume(struct device *dev)
+{
+	vibra_disable_leds();
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
+			 twl4030_vibra_suspend, twl4030_vibra_resume);
+
+static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata,
+			      struct device_node *node)
+{
+	if (pdata && pdata->coexist)
+		return true;
+
+	node = of_find_node_by_name(node, "codec");
+	if (node) {
+		of_node_put(node);
+		return true;
+	}
+
+	return false;
+}
+
+static int twl4030_vibra_probe(struct platform_device *pdev)
+{
+	struct twl4030_vibra_data *pdata = dev_get_platdata(&pdev->dev);
+	struct device_node *twl4030_core_node = pdev->dev.parent->of_node;
+	struct vibra_info *info;
+	int ret;
+
+	if (!pdata && !twl4030_core_node) {
+		dev_dbg(&pdev->dev, "platform_data not available\n");
+		return -EINVAL;
+	}
+
+	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->dev = &pdev->dev;
+	info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node);
+	INIT_WORK(&info->play_work, vibra_play_work);
+
+	info->input_dev = devm_input_allocate_device(&pdev->dev);
+	if (info->input_dev == NULL) {
+		dev_err(&pdev->dev, "couldn't allocate input device\n");
+		return -ENOMEM;
+	}
+
+	input_set_drvdata(info->input_dev, info);
+
+	info->input_dev->name = "twl4030:vibrator";
+	info->input_dev->id.version = 1;
+	info->input_dev->dev.parent = pdev->dev.parent;
+	info->input_dev->close = twl4030_vibra_close;
+	__set_bit(FF_RUMBLE, info->input_dev->ffbit);
+
+	ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
+	if (ret < 0) {
+		dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
+		return ret;
+	}
+
+	ret = input_register_device(info->input_dev);
+	if (ret < 0) {
+		dev_dbg(&pdev->dev, "couldn't register input device\n");
+		goto err_iff;
+	}
+
+	vibra_disable_leds();
+
+	platform_set_drvdata(pdev, info);
+	return 0;
+
+err_iff:
+	input_ff_destroy(info->input_dev);
+	return ret;
+}
+
+static struct platform_driver twl4030_vibra_driver = {
+	.probe		= twl4030_vibra_probe,
+	.driver		= {
+		.name	= "twl4030-vibra",
+		.pm	= &twl4030_vibra_pm_ops,
+	},
+};
+module_platform_driver(twl4030_vibra_driver);
+
+MODULE_ALIAS("platform:twl4030-vibra");
+MODULE_DESCRIPTION("TWL4030 Vibra driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nokia Corporation");
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c
new file mode 100644
index 0000000..ea63fad
--- /dev/null
+++ b/drivers/input/misc/twl6040-vibra.c
@@ -0,0 +1,399 @@
+/*
+ * twl6040-vibra.c - TWL6040 Vibrator driver
+ *
+ * Author:      Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
+ * Author:      Misael Lopez Cruz <misael.lopez@ti.com>
+ *
+ * Copyright:   (C) 2011 Texas Instruments, Inc.
+ *
+ * Based on twl4030-vibra.c by Henrik Saari <henrik.saari@nokia.com>
+ *				Felipe Balbi <felipe.balbi@nokia.com>
+ *				Jari Vanhala <ext-javi.vanhala@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/workqueue.h>
+#include <linux/input.h>
+#include <linux/mfd/twl6040.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+#define EFFECT_DIR_180_DEG	0x8000
+
+/* Recommended modulation index 85% */
+#define TWL6040_VIBRA_MOD	85
+
+#define TWL6040_NUM_SUPPLIES 2
+
+struct vibra_info {
+	struct device *dev;
+	struct input_dev *input_dev;
+	struct workqueue_struct *workqueue;
+	struct work_struct play_work;
+	struct mutex mutex;
+	int irq;
+
+	bool enabled;
+	int weak_speed;
+	int strong_speed;
+	int direction;
+
+	unsigned int vibldrv_res;
+	unsigned int vibrdrv_res;
+	unsigned int viblmotor_res;
+	unsigned int vibrmotor_res;
+
+	struct regulator_bulk_data supplies[TWL6040_NUM_SUPPLIES];
+
+	struct twl6040 *twl6040;
+};
+
+static irqreturn_t twl6040_vib_irq_handler(int irq, void *data)
+{
+	struct vibra_info *info = data;
+	struct twl6040 *twl6040 = info->twl6040;
+	u8 status;
+
+	status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
+	if (status & TWL6040_VIBLOCDET) {
+		dev_warn(info->dev, "Left Vibrator overcurrent detected\n");
+		twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLL,
+				   TWL6040_VIBENA);
+	}
+	if (status & TWL6040_VIBROCDET) {
+		dev_warn(info->dev, "Right Vibrator overcurrent detected\n");
+		twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLR,
+				   TWL6040_VIBENA);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void twl6040_vibra_enable(struct vibra_info *info)
+{
+	struct twl6040 *twl6040 = info->twl6040;
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(info->supplies), info->supplies);
+	if (ret) {
+		dev_err(info->dev, "failed to enable regulators %d\n", ret);
+		return;
+	}
+
+	twl6040_power(info->twl6040, 1);
+	if (twl6040_get_revid(twl6040) <= TWL6040_REV_ES1_1) {
+		/*
+		 * ERRATA: Disable overcurrent protection for at least
+		 * 3ms when enabling vibrator drivers to avoid false
+		 * overcurrent detection
+		 */
+		twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL,
+				  TWL6040_VIBENA | TWL6040_VIBCTRL);
+		twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR,
+				  TWL6040_VIBENA | TWL6040_VIBCTRL);
+		usleep_range(3000, 3500);
+	}
+
+	twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL,
+			  TWL6040_VIBENA);
+	twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR,
+			  TWL6040_VIBENA);
+
+	info->enabled = true;
+}
+
+static void twl6040_vibra_disable(struct vibra_info *info)
+{
+	struct twl6040 *twl6040 = info->twl6040;
+
+	twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL, 0x00);
+	twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR, 0x00);
+	twl6040_power(info->twl6040, 0);
+
+	regulator_bulk_disable(ARRAY_SIZE(info->supplies), info->supplies);
+
+	info->enabled = false;
+}
+
+static u8 twl6040_vibra_code(int vddvib, int vibdrv_res, int motor_res,
+			     int speed, int direction)
+{
+	int vpk, max_code;
+	u8 vibdat;
+
+	/* output swing */
+	vpk = (vddvib * motor_res * TWL6040_VIBRA_MOD) /
+		(100 * (vibdrv_res + motor_res));
+
+	/* 50mV per VIBDAT code step */
+	max_code = vpk / 50;
+	if (max_code > TWL6040_VIBDAT_MAX)
+		max_code = TWL6040_VIBDAT_MAX;
+
+	/* scale speed to max allowed code */
+	vibdat = (u8)((speed * max_code) / USHRT_MAX);
+
+	/* 2's complement for direction > 180 degrees */
+	vibdat *= direction;
+
+	return vibdat;
+}
+
+static void twl6040_vibra_set_effect(struct vibra_info *info)
+{
+	struct twl6040 *twl6040 = info->twl6040;
+	u8 vibdatl, vibdatr;
+	int volt;
+
+	/* weak motor */
+	volt = regulator_get_voltage(info->supplies[0].consumer) / 1000;
+	vibdatl = twl6040_vibra_code(volt, info->vibldrv_res,
+				     info->viblmotor_res,
+				     info->weak_speed, info->direction);
+
+	/* strong motor */
+	volt = regulator_get_voltage(info->supplies[1].consumer) / 1000;
+	vibdatr = twl6040_vibra_code(volt, info->vibrdrv_res,
+				     info->vibrmotor_res,
+				     info->strong_speed, info->direction);
+
+	twl6040_reg_write(twl6040, TWL6040_REG_VIBDATL, vibdatl);
+	twl6040_reg_write(twl6040, TWL6040_REG_VIBDATR, vibdatr);
+}
+
+static void vibra_play_work(struct work_struct *work)
+{
+	struct vibra_info *info = container_of(work,
+				struct vibra_info, play_work);
+
+	mutex_lock(&info->mutex);
+
+	if (info->weak_speed || info->strong_speed) {
+		if (!info->enabled)
+			twl6040_vibra_enable(info);
+
+		twl6040_vibra_set_effect(info);
+	} else if (info->enabled)
+		twl6040_vibra_disable(info);
+
+	mutex_unlock(&info->mutex);
+}
+
+static int vibra_play(struct input_dev *input, void *data,
+		      struct ff_effect *effect)
+{
+	struct vibra_info *info = input_get_drvdata(input);
+	int ret;
+
+	/* Do not allow effect, while the routing is set to use audio */
+	ret = twl6040_get_vibralr_status(info->twl6040);
+	if (ret & TWL6040_VIBSEL) {
+		dev_info(&input->dev, "Vibra is configured for audio\n");
+		return -EBUSY;
+	}
+
+	info->weak_speed = effect->u.rumble.weak_magnitude;
+	info->strong_speed = effect->u.rumble.strong_magnitude;
+	info->direction = effect->direction < EFFECT_DIR_180_DEG ? 1 : -1;
+
+	ret = queue_work(info->workqueue, &info->play_work);
+	if (!ret) {
+		dev_info(&input->dev, "work is already on queue\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void twl6040_vibra_close(struct input_dev *input)
+{
+	struct vibra_info *info = input_get_drvdata(input);
+
+	cancel_work_sync(&info->play_work);
+
+	mutex_lock(&info->mutex);
+
+	if (info->enabled)
+		twl6040_vibra_disable(info);
+
+	mutex_unlock(&info->mutex);
+}
+
+static int __maybe_unused twl6040_vibra_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vibra_info *info = platform_get_drvdata(pdev);
+
+	mutex_lock(&info->mutex);
+
+	if (info->enabled)
+		twl6040_vibra_disable(info);
+
+	mutex_unlock(&info->mutex);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, twl6040_vibra_suspend, NULL);
+
+static int twl6040_vibra_probe(struct platform_device *pdev)
+{
+	struct device *twl6040_core_dev = pdev->dev.parent;
+	struct device_node *twl6040_core_node;
+	struct vibra_info *info;
+	int vddvibl_uV = 0;
+	int vddvibr_uV = 0;
+	int error;
+
+	twl6040_core_node = of_find_node_by_name(twl6040_core_dev->of_node,
+						 "vibra");
+	if (!twl6040_core_node) {
+		dev_err(&pdev->dev, "parent of node is missing?\n");
+		return -EINVAL;
+	}
+
+	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		of_node_put(twl6040_core_node);
+		dev_err(&pdev->dev, "couldn't allocate memory\n");
+		return -ENOMEM;
+	}
+
+	info->dev = &pdev->dev;
+
+	info->twl6040 = dev_get_drvdata(pdev->dev.parent);
+
+	of_property_read_u32(twl6040_core_node, "ti,vibldrv-res",
+			     &info->vibldrv_res);
+	of_property_read_u32(twl6040_core_node, "ti,vibrdrv-res",
+			     &info->vibrdrv_res);
+	of_property_read_u32(twl6040_core_node, "ti,viblmotor-res",
+			     &info->viblmotor_res);
+	of_property_read_u32(twl6040_core_node, "ti,vibrmotor-res",
+			     &info->vibrmotor_res);
+	of_property_read_u32(twl6040_core_node, "ti,vddvibl-uV", &vddvibl_uV);
+	of_property_read_u32(twl6040_core_node, "ti,vddvibr-uV", &vddvibr_uV);
+
+	of_node_put(twl6040_core_node);
+
+	if ((!info->vibldrv_res && !info->viblmotor_res) ||
+	    (!info->vibrdrv_res && !info->vibrmotor_res)) {
+		dev_err(info->dev, "invalid vibra driver/motor resistance\n");
+		return -EINVAL;
+	}
+
+	info->irq = platform_get_irq(pdev, 0);
+	if (info->irq < 0) {
+		dev_err(info->dev, "invalid irq\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&info->mutex);
+
+	error = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
+					  twl6040_vib_irq_handler,
+					  IRQF_ONESHOT,
+					  "twl6040_irq_vib", info);
+	if (error) {
+		dev_err(info->dev, "VIB IRQ request failed: %d\n", error);
+		return error;
+	}
+
+	info->supplies[0].supply = "vddvibl";
+	info->supplies[1].supply = "vddvibr";
+	/*
+	 * When booted with Device tree the regulators are attached to the
+	 * parent device (twl6040 MFD core)
+	 */
+	error = devm_regulator_bulk_get(twl6040_core_dev,
+					ARRAY_SIZE(info->supplies),
+					info->supplies);
+	if (error) {
+		dev_err(info->dev, "couldn't get regulators %d\n", error);
+		return error;
+	}
+
+	if (vddvibl_uV) {
+		error = regulator_set_voltage(info->supplies[0].consumer,
+					      vddvibl_uV, vddvibl_uV);
+		if (error) {
+			dev_err(info->dev, "failed to set VDDVIBL volt %d\n",
+				error);
+			return error;
+		}
+	}
+
+	if (vddvibr_uV) {
+		error = regulator_set_voltage(info->supplies[1].consumer,
+					      vddvibr_uV, vddvibr_uV);
+		if (error) {
+			dev_err(info->dev, "failed to set VDDVIBR volt %d\n",
+				error);
+			return error;
+		}
+	}
+
+	INIT_WORK(&info->play_work, vibra_play_work);
+
+	info->input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!info->input_dev) {
+		dev_err(info->dev, "couldn't allocate input device\n");
+		return -ENOMEM;
+	}
+
+	input_set_drvdata(info->input_dev, info);
+
+	info->input_dev->name = "twl6040:vibrator";
+	info->input_dev->id.version = 1;
+	info->input_dev->dev.parent = pdev->dev.parent;
+	info->input_dev->close = twl6040_vibra_close;
+	__set_bit(FF_RUMBLE, info->input_dev->ffbit);
+
+	error = input_ff_create_memless(info->input_dev, NULL, vibra_play);
+	if (error) {
+		dev_err(info->dev, "couldn't register vibrator to FF\n");
+		return error;
+	}
+
+	error = input_register_device(info->input_dev);
+	if (error) {
+		dev_err(info->dev, "couldn't register input device\n");
+		return error;
+	}
+
+	platform_set_drvdata(pdev, info);
+
+	return 0;
+}
+
+static struct platform_driver twl6040_vibra_driver = {
+	.probe		= twl6040_vibra_probe,
+	.driver		= {
+		.name	= "twl6040-vibra",
+		.pm	= &twl6040_vibra_pm_ops,
+	},
+};
+module_platform_driver(twl6040_vibra_driver);
+
+MODULE_ALIAS("platform:twl6040-vibra");
+MODULE_DESCRIPTION("TWL6040 Vibra driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
+MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
new file mode 100644
index 0000000..2bb4c86
--- /dev/null
+++ b/drivers/input/misc/uinput.c
@@ -0,0 +1,947 @@
+/*
+ *  User level driver support for input subsystem
+ *
+ * Heavily based on evdev.c by Vojtech Pavlik
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
+ *
+ * Changes/Revisions:
+ *	0.4	01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
+ *		- add UI_GET_SYSNAME ioctl
+ *	0.3	09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>)
+ *		- updated ff support for the changes in kernel interface
+ *		- added MODULE_VERSION
+ *	0.2	16/10/2004 (Micah Dowty <micah@navi.cx>)
+ *		- added force feedback support
+ *              - added UI_SET_PHYS
+ *	0.1	20/06/2002
+ *		- first public version
+ */
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uinput.h>
+#include <linux/input/mt.h>
+#include "../input-compat.h"
+
+static int uinput_dev_event(struct input_dev *dev,
+			    unsigned int type, unsigned int code, int value)
+{
+	struct uinput_device	*udev = input_get_drvdata(dev);
+
+	udev->buff[udev->head].type = type;
+	udev->buff[udev->head].code = code;
+	udev->buff[udev->head].value = value;
+	do_gettimeofday(&udev->buff[udev->head].time);
+	udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
+
+	wake_up_interruptible(&udev->waitq);
+
+	return 0;
+}
+
+/* Atomically allocate an ID for the given request. Returns 0 on success. */
+static bool uinput_request_alloc_id(struct uinput_device *udev,
+				    struct uinput_request *request)
+{
+	unsigned int id;
+	bool reserved = false;
+
+	spin_lock(&udev->requests_lock);
+
+	for (id = 0; id < UINPUT_NUM_REQUESTS; id++) {
+		if (!udev->requests[id]) {
+			request->id = id;
+			udev->requests[id] = request;
+			reserved = true;
+			break;
+		}
+	}
+
+	spin_unlock(&udev->requests_lock);
+	return reserved;
+}
+
+static struct uinput_request *uinput_request_find(struct uinput_device *udev,
+						  unsigned int id)
+{
+	/* Find an input request, by ID. Returns NULL if the ID isn't valid. */
+	if (id >= UINPUT_NUM_REQUESTS)
+		return NULL;
+
+	return udev->requests[id];
+}
+
+static int uinput_request_reserve_slot(struct uinput_device *udev,
+				       struct uinput_request *request)
+{
+	/* Allocate slot. If none are available right away, wait. */
+	return wait_event_interruptible(udev->requests_waitq,
+					uinput_request_alloc_id(udev, request));
+}
+
+static void uinput_request_done(struct uinput_device *udev,
+				struct uinput_request *request)
+{
+	/* Mark slot as available */
+	udev->requests[request->id] = NULL;
+	wake_up(&udev->requests_waitq);
+
+	complete(&request->done);
+}
+
+static int uinput_request_send(struct uinput_device *udev,
+			       struct uinput_request *request)
+{
+	int retval;
+
+	retval = mutex_lock_interruptible(&udev->mutex);
+	if (retval)
+		return retval;
+
+	if (udev->state != UIST_CREATED) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	init_completion(&request->done);
+
+	/*
+	 * Tell our userspace application about this new request
+	 * by queueing an input event.
+	 */
+	uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id);
+
+ out:
+	mutex_unlock(&udev->mutex);
+	return retval;
+}
+
+static int uinput_request_submit(struct uinput_device *udev,
+				 struct uinput_request *request)
+{
+	int error;
+
+	error = uinput_request_reserve_slot(udev, request);
+	if (error)
+		return error;
+
+	error = uinput_request_send(udev, request);
+	if (error) {
+		uinput_request_done(udev, request);
+		return error;
+	}
+
+	wait_for_completion(&request->done);
+	return request->retval;
+}
+
+/*
+ * Fail all outstanding requests so handlers don't wait for the userspace
+ * to finish processing them.
+ */
+static void uinput_flush_requests(struct uinput_device *udev)
+{
+	struct uinput_request *request;
+	int i;
+
+	spin_lock(&udev->requests_lock);
+
+	for (i = 0; i < UINPUT_NUM_REQUESTS; i++) {
+		request = udev->requests[i];
+		if (request) {
+			request->retval = -ENODEV;
+			uinput_request_done(udev, request);
+		}
+	}
+
+	spin_unlock(&udev->requests_lock);
+}
+
+static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
+{
+	uinput_dev_event(dev, EV_FF, FF_GAIN, gain);
+}
+
+static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+	uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude);
+}
+
+static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
+{
+	return uinput_dev_event(dev, EV_FF, effect_id, value);
+}
+
+static int uinput_dev_upload_effect(struct input_dev *dev,
+				    struct ff_effect *effect,
+				    struct ff_effect *old)
+{
+	struct uinput_device *udev = input_get_drvdata(dev);
+	struct uinput_request request;
+
+	/*
+	 * uinput driver does not currently support periodic effects with
+	 * custom waveform since it does not have a way to pass buffer of
+	 * samples (custom_data) to userspace. If ever there is a device
+	 * supporting custom waveforms we would need to define an additional
+	 * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out.
+	 */
+	if (effect->type == FF_PERIODIC &&
+			effect->u.periodic.waveform == FF_CUSTOM)
+		return -EINVAL;
+
+	request.code = UI_FF_UPLOAD;
+	request.u.upload.effect = effect;
+	request.u.upload.old = old;
+
+	return uinput_request_submit(udev, &request);
+}
+
+static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
+{
+	struct uinput_device *udev = input_get_drvdata(dev);
+	struct uinput_request request;
+
+	if (!test_bit(EV_FF, dev->evbit))
+		return -ENOSYS;
+
+	request.code = UI_FF_ERASE;
+	request.u.effect_id = effect_id;
+
+	return uinput_request_submit(udev, &request);
+}
+
+static void uinput_destroy_device(struct uinput_device *udev)
+{
+	const char *name, *phys;
+	struct input_dev *dev = udev->dev;
+	enum uinput_state old_state = udev->state;
+
+	udev->state = UIST_NEW_DEVICE;
+
+	if (dev) {
+		name = dev->name;
+		phys = dev->phys;
+		if (old_state == UIST_CREATED) {
+			uinput_flush_requests(udev);
+			input_unregister_device(dev);
+		} else {
+			input_free_device(dev);
+		}
+		kfree(name);
+		kfree(phys);
+		udev->dev = NULL;
+	}
+}
+
+static int uinput_create_device(struct uinput_device *udev)
+{
+	struct input_dev *dev = udev->dev;
+	int error;
+
+	if (udev->state != UIST_SETUP_COMPLETE) {
+		printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
+		return -EINVAL;
+	}
+
+	if (udev->ff_effects_max) {
+		error = input_ff_create(dev, udev->ff_effects_max);
+		if (error)
+			goto fail1;
+
+		dev->ff->upload = uinput_dev_upload_effect;
+		dev->ff->erase = uinput_dev_erase_effect;
+		dev->ff->playback = uinput_dev_playback;
+		dev->ff->set_gain = uinput_dev_set_gain;
+		dev->ff->set_autocenter = uinput_dev_set_autocenter;
+	}
+
+	error = input_register_device(udev->dev);
+	if (error)
+		goto fail2;
+
+	udev->state = UIST_CREATED;
+
+	return 0;
+
+ fail2:	input_ff_destroy(dev);
+ fail1: uinput_destroy_device(udev);
+	return error;
+}
+
+static int uinput_open(struct inode *inode, struct file *file)
+{
+	struct uinput_device *newdev;
+
+	newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);
+	if (!newdev)
+		return -ENOMEM;
+
+	mutex_init(&newdev->mutex);
+	spin_lock_init(&newdev->requests_lock);
+	init_waitqueue_head(&newdev->requests_waitq);
+	init_waitqueue_head(&newdev->waitq);
+	newdev->state = UIST_NEW_DEVICE;
+
+	file->private_data = newdev;
+	nonseekable_open(inode, file);
+
+	return 0;
+}
+
+static int uinput_validate_absbits(struct input_dev *dev)
+{
+	unsigned int cnt;
+	int nslot;
+
+	if (!test_bit(EV_ABS, dev->evbit))
+		return 0;
+
+	/*
+	 * Check if absmin/absmax/absfuzz/absflat are sane.
+	 */
+
+	for_each_set_bit(cnt, dev->absbit, ABS_CNT) {
+		int min, max;
+
+		min = input_abs_get_min(dev, cnt);
+		max = input_abs_get_max(dev, cnt);
+
+		if ((min != 0 || max != 0) && max <= min) {
+			printk(KERN_DEBUG
+				"%s: invalid abs[%02x] min:%d max:%d\n",
+				UINPUT_NAME, cnt,
+				input_abs_get_min(dev, cnt),
+				input_abs_get_max(dev, cnt));
+			return -EINVAL;
+		}
+
+		if (input_abs_get_flat(dev, cnt) >
+		    input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) {
+			printk(KERN_DEBUG
+				"%s: abs_flat #%02x out of range: %d "
+				"(min:%d/max:%d)\n",
+				UINPUT_NAME, cnt,
+				input_abs_get_flat(dev, cnt),
+				input_abs_get_min(dev, cnt),
+				input_abs_get_max(dev, cnt));
+			return -EINVAL;
+		}
+	}
+
+	if (test_bit(ABS_MT_SLOT, dev->absbit)) {
+		nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
+		input_mt_init_slots(dev, nslot, 0);
+	} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
+		input_set_events_per_packet(dev, 60);
+	}
+
+	return 0;
+}
+
+static int uinput_allocate_device(struct uinput_device *udev)
+{
+	udev->dev = input_allocate_device();
+	if (!udev->dev)
+		return -ENOMEM;
+
+	udev->dev->event = uinput_dev_event;
+	input_set_drvdata(udev->dev, udev);
+
+	return 0;
+}
+
+static int uinput_setup_device(struct uinput_device *udev,
+			       const char __user *buffer, size_t count)
+{
+	struct uinput_user_dev	*user_dev;
+	struct input_dev	*dev;
+	int			i;
+	int			retval;
+
+	if (count != sizeof(struct uinput_user_dev))
+		return -EINVAL;
+
+	if (!udev->dev) {
+		retval = uinput_allocate_device(udev);
+		if (retval)
+			return retval;
+	}
+
+	dev = udev->dev;
+
+	user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev));
+	if (IS_ERR(user_dev))
+		return PTR_ERR(user_dev);
+
+	udev->ff_effects_max = user_dev->ff_effects_max;
+
+	/* Ensure name is filled in */
+	if (!user_dev->name[0]) {
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	kfree(dev->name);
+	dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE,
+			     GFP_KERNEL);
+	if (!dev->name) {
+		retval = -ENOMEM;
+		goto exit;
+	}
+
+	dev->id.bustype	= user_dev->id.bustype;
+	dev->id.vendor	= user_dev->id.vendor;
+	dev->id.product	= user_dev->id.product;
+	dev->id.version	= user_dev->id.version;
+
+	for (i = 0; i < ABS_CNT; i++) {
+		input_abs_set_max(dev, i, user_dev->absmax[i]);
+		input_abs_set_min(dev, i, user_dev->absmin[i]);
+		input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]);
+		input_abs_set_flat(dev, i, user_dev->absflat[i]);
+	}
+
+	retval = uinput_validate_absbits(dev);
+	if (retval < 0)
+		goto exit;
+
+	udev->state = UIST_SETUP_COMPLETE;
+	retval = count;
+
+ exit:
+	kfree(user_dev);
+	return retval;
+}
+
+static ssize_t uinput_inject_events(struct uinput_device *udev,
+				    const char __user *buffer, size_t count)
+{
+	struct input_event ev;
+	size_t bytes = 0;
+
+	if (count != 0 && count < input_event_size())
+		return -EINVAL;
+
+	while (bytes + input_event_size() <= count) {
+		/*
+		 * Note that even if some events were fetched successfully
+		 * we are still going to return EFAULT instead of partial
+		 * count to let userspace know that it got it's buffers
+		 * all wrong.
+		 */
+		if (input_event_from_user(buffer + bytes, &ev))
+			return -EFAULT;
+
+		input_event(udev->dev, ev.type, ev.code, ev.value);
+		bytes += input_event_size();
+	}
+
+	return bytes;
+}
+
+static ssize_t uinput_write(struct file *file, const char __user *buffer,
+			    size_t count, loff_t *ppos)
+{
+	struct uinput_device *udev = file->private_data;
+	int retval;
+
+	if (count == 0)
+		return 0;
+
+	retval = mutex_lock_interruptible(&udev->mutex);
+	if (retval)
+		return retval;
+
+	retval = udev->state == UIST_CREATED ?
+			uinput_inject_events(udev, buffer, count) :
+			uinput_setup_device(udev, buffer, count);
+
+	mutex_unlock(&udev->mutex);
+
+	return retval;
+}
+
+static bool uinput_fetch_next_event(struct uinput_device *udev,
+				    struct input_event *event)
+{
+	bool have_event;
+
+	spin_lock_irq(&udev->dev->event_lock);
+
+	have_event = udev->head != udev->tail;
+	if (have_event) {
+		*event = udev->buff[udev->tail];
+		udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
+	}
+
+	spin_unlock_irq(&udev->dev->event_lock);
+
+	return have_event;
+}
+
+static ssize_t uinput_events_to_user(struct uinput_device *udev,
+				     char __user *buffer, size_t count)
+{
+	struct input_event event;
+	size_t read = 0;
+
+	while (read + input_event_size() <= count &&
+	       uinput_fetch_next_event(udev, &event)) {
+
+		if (input_event_to_user(buffer + read, &event))
+			return -EFAULT;
+
+		read += input_event_size();
+	}
+
+	return read;
+}
+
+static ssize_t uinput_read(struct file *file, char __user *buffer,
+			   size_t count, loff_t *ppos)
+{
+	struct uinput_device *udev = file->private_data;
+	ssize_t retval;
+
+	if (count != 0 && count < input_event_size())
+		return -EINVAL;
+
+	do {
+		retval = mutex_lock_interruptible(&udev->mutex);
+		if (retval)
+			return retval;
+
+		if (udev->state != UIST_CREATED)
+			retval = -ENODEV;
+		else if (udev->head == udev->tail &&
+			 (file->f_flags & O_NONBLOCK))
+			retval = -EAGAIN;
+		else
+			retval = uinput_events_to_user(udev, buffer, count);
+
+		mutex_unlock(&udev->mutex);
+
+		if (retval || count == 0)
+			break;
+
+		if (!(file->f_flags & O_NONBLOCK))
+			retval = wait_event_interruptible(udev->waitq,
+						  udev->head != udev->tail ||
+						  udev->state != UIST_CREATED);
+	} while (retval == 0);
+
+	return retval;
+}
+
+static unsigned int uinput_poll(struct file *file, poll_table *wait)
+{
+	struct uinput_device *udev = file->private_data;
+
+	poll_wait(file, &udev->waitq, wait);
+
+	if (udev->head != udev->tail)
+		return POLLIN | POLLRDNORM;
+
+	return 0;
+}
+
+static int uinput_release(struct inode *inode, struct file *file)
+{
+	struct uinput_device *udev = file->private_data;
+
+	uinput_destroy_device(udev);
+	kfree(udev);
+
+	return 0;
+}
+
+#ifdef CONFIG_COMPAT
+struct uinput_ff_upload_compat {
+	__u32			request_id;
+	__s32			retval;
+	struct ff_effect_compat	effect;
+	struct ff_effect_compat	old;
+};
+
+static int uinput_ff_upload_to_user(char __user *buffer,
+				    const struct uinput_ff_upload *ff_up)
+{
+	if (INPUT_COMPAT_TEST) {
+		struct uinput_ff_upload_compat ff_up_compat;
+
+		ff_up_compat.request_id = ff_up->request_id;
+		ff_up_compat.retval = ff_up->retval;
+		/*
+		 * It so happens that the pointer that gives us the trouble
+		 * is the last field in the structure. Since we don't support
+		 * custom waveforms in uinput anyway we can just copy the whole
+		 * thing (to the compat size) and ignore the pointer.
+		 */
+		memcpy(&ff_up_compat.effect, &ff_up->effect,
+			sizeof(struct ff_effect_compat));
+		memcpy(&ff_up_compat.old, &ff_up->old,
+			sizeof(struct ff_effect_compat));
+
+		if (copy_to_user(buffer, &ff_up_compat,
+				 sizeof(struct uinput_ff_upload_compat)))
+			return -EFAULT;
+	} else {
+		if (copy_to_user(buffer, ff_up,
+				 sizeof(struct uinput_ff_upload)))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int uinput_ff_upload_from_user(const char __user *buffer,
+				      struct uinput_ff_upload *ff_up)
+{
+	if (INPUT_COMPAT_TEST) {
+		struct uinput_ff_upload_compat ff_up_compat;
+
+		if (copy_from_user(&ff_up_compat, buffer,
+				   sizeof(struct uinput_ff_upload_compat)))
+			return -EFAULT;
+
+		ff_up->request_id = ff_up_compat.request_id;
+		ff_up->retval = ff_up_compat.retval;
+		memcpy(&ff_up->effect, &ff_up_compat.effect,
+			sizeof(struct ff_effect_compat));
+		memcpy(&ff_up->old, &ff_up_compat.old,
+			sizeof(struct ff_effect_compat));
+
+	} else {
+		if (copy_from_user(ff_up, buffer,
+				   sizeof(struct uinput_ff_upload)))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+#else
+
+static int uinput_ff_upload_to_user(char __user *buffer,
+				    const struct uinput_ff_upload *ff_up)
+{
+	if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int uinput_ff_upload_from_user(const char __user *buffer,
+				      struct uinput_ff_upload *ff_up)
+{
+	if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload)))
+		return -EFAULT;
+
+	return 0;
+}
+
+#endif
+
+#define uinput_set_bit(_arg, _bit, _max)		\
+({							\
+	int __ret = 0;					\
+	if (udev->state == UIST_CREATED)		\
+		__ret =  -EINVAL;			\
+	else if ((_arg) > (_max))			\
+		__ret = -EINVAL;			\
+	else set_bit((_arg), udev->dev->_bit);		\
+	__ret;						\
+})
+
+static int uinput_str_to_user(void __user *dest, const char *str,
+			      unsigned int maxlen)
+{
+	char __user *p = dest;
+	int len, ret;
+
+	if (!str)
+		return -ENOENT;
+
+	if (maxlen == 0)
+		return -EINVAL;
+
+	len = strlen(str) + 1;
+	if (len > maxlen)
+		len = maxlen;
+
+	ret = copy_to_user(p, str, len);
+	if (ret)
+		return -EFAULT;
+
+	/* force terminating '\0' */
+	ret = put_user(0, p + len - 1);
+	return ret ? -EFAULT : len;
+}
+
+static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
+				 unsigned long arg, void __user *p)
+{
+	int			retval;
+	struct uinput_device	*udev = file->private_data;
+	struct uinput_ff_upload ff_up;
+	struct uinput_ff_erase  ff_erase;
+	struct uinput_request   *req;
+	char			*phys;
+	const char		*name;
+	unsigned int		size;
+
+	retval = mutex_lock_interruptible(&udev->mutex);
+	if (retval)
+		return retval;
+
+	if (!udev->dev) {
+		retval = uinput_allocate_device(udev);
+		if (retval)
+			goto out;
+	}
+
+	switch (cmd) {
+		case UI_GET_VERSION:
+			if (put_user(UINPUT_VERSION,
+				     (unsigned int __user *)p))
+				retval = -EFAULT;
+			goto out;
+
+		case UI_DEV_CREATE:
+			retval = uinput_create_device(udev);
+			goto out;
+
+		case UI_DEV_DESTROY:
+			uinput_destroy_device(udev);
+			goto out;
+
+		case UI_SET_EVBIT:
+			retval = uinput_set_bit(arg, evbit, EV_MAX);
+			goto out;
+
+		case UI_SET_KEYBIT:
+			retval = uinput_set_bit(arg, keybit, KEY_MAX);
+			goto out;
+
+		case UI_SET_RELBIT:
+			retval = uinput_set_bit(arg, relbit, REL_MAX);
+			goto out;
+
+		case UI_SET_ABSBIT:
+			retval = uinput_set_bit(arg, absbit, ABS_MAX);
+			goto out;
+
+		case UI_SET_MSCBIT:
+			retval = uinput_set_bit(arg, mscbit, MSC_MAX);
+			goto out;
+
+		case UI_SET_LEDBIT:
+			retval = uinput_set_bit(arg, ledbit, LED_MAX);
+			goto out;
+
+		case UI_SET_SNDBIT:
+			retval = uinput_set_bit(arg, sndbit, SND_MAX);
+			goto out;
+
+		case UI_SET_FFBIT:
+			retval = uinput_set_bit(arg, ffbit, FF_MAX);
+			goto out;
+
+		case UI_SET_SWBIT:
+			retval = uinput_set_bit(arg, swbit, SW_MAX);
+			goto out;
+
+		case UI_SET_PROPBIT:
+			retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX);
+			goto out;
+
+		case UI_SET_PHYS:
+			if (udev->state == UIST_CREATED) {
+				retval = -EINVAL;
+				goto out;
+			}
+
+			phys = strndup_user(p, 1024);
+			if (IS_ERR(phys)) {
+				retval = PTR_ERR(phys);
+				goto out;
+			}
+
+			kfree(udev->dev->phys);
+			udev->dev->phys = phys;
+			goto out;
+
+		case UI_BEGIN_FF_UPLOAD:
+			retval = uinput_ff_upload_from_user(p, &ff_up);
+			if (retval)
+				goto out;
+
+			req = uinput_request_find(udev, ff_up.request_id);
+			if (!req || req->code != UI_FF_UPLOAD ||
+			    !req->u.upload.effect) {
+				retval = -EINVAL;
+				goto out;
+			}
+
+			ff_up.retval = 0;
+			ff_up.effect = *req->u.upload.effect;
+			if (req->u.upload.old)
+				ff_up.old = *req->u.upload.old;
+			else
+				memset(&ff_up.old, 0, sizeof(struct ff_effect));
+
+			retval = uinput_ff_upload_to_user(p, &ff_up);
+			goto out;
+
+		case UI_BEGIN_FF_ERASE:
+			if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
+				retval = -EFAULT;
+				goto out;
+			}
+
+			req = uinput_request_find(udev, ff_erase.request_id);
+			if (!req || req->code != UI_FF_ERASE) {
+				retval = -EINVAL;
+				goto out;
+			}
+
+			ff_erase.retval = 0;
+			ff_erase.effect_id = req->u.effect_id;
+			if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
+				retval = -EFAULT;
+				goto out;
+			}
+
+			goto out;
+
+		case UI_END_FF_UPLOAD:
+			retval = uinput_ff_upload_from_user(p, &ff_up);
+			if (retval)
+				goto out;
+
+			req = uinput_request_find(udev, ff_up.request_id);
+			if (!req || req->code != UI_FF_UPLOAD ||
+			    !req->u.upload.effect) {
+				retval = -EINVAL;
+				goto out;
+			}
+
+			req->retval = ff_up.retval;
+			uinput_request_done(udev, req);
+			goto out;
+
+		case UI_END_FF_ERASE:
+			if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
+				retval = -EFAULT;
+				goto out;
+			}
+
+			req = uinput_request_find(udev, ff_erase.request_id);
+			if (!req || req->code != UI_FF_ERASE) {
+				retval = -EINVAL;
+				goto out;
+			}
+
+			req->retval = ff_erase.retval;
+			uinput_request_done(udev, req);
+			goto out;
+	}
+
+	size = _IOC_SIZE(cmd);
+
+	/* Now check variable-length commands */
+	switch (cmd & ~IOCSIZE_MASK) {
+	case UI_GET_SYSNAME(0):
+		if (udev->state != UIST_CREATED) {
+			retval = -ENOENT;
+			goto out;
+		}
+		name = dev_name(&udev->dev->dev);
+		retval = uinput_str_to_user(p, name, size);
+		goto out;
+	}
+
+	retval = -EINVAL;
+ out:
+	mutex_unlock(&udev->mutex);
+	return retval;
+}
+
+static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg);
+}
+
+#ifdef CONFIG_COMPAT
+
+#define UI_SET_PHYS_COMPAT	_IOW(UINPUT_IOCTL_BASE, 108, compat_uptr_t)
+
+static long uinput_compat_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	if (cmd == UI_SET_PHYS_COMPAT)
+		cmd = UI_SET_PHYS;
+
+	return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations uinput_fops = {
+	.owner		= THIS_MODULE,
+	.open		= uinput_open,
+	.release	= uinput_release,
+	.read		= uinput_read,
+	.write		= uinput_write,
+	.poll		= uinput_poll,
+	.unlocked_ioctl	= uinput_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= uinput_compat_ioctl,
+#endif
+	.llseek		= no_llseek,
+};
+
+static struct miscdevice uinput_misc = {
+	.fops		= &uinput_fops,
+	.minor		= UINPUT_MINOR,
+	.name		= UINPUT_NAME,
+};
+MODULE_ALIAS_MISCDEV(UINPUT_MINOR);
+MODULE_ALIAS("devname:" UINPUT_NAME);
+
+static int __init uinput_init(void)
+{
+	return misc_register(&uinput_misc);
+}
+
+static void __exit uinput_exit(void)
+{
+	misc_deregister(&uinput_misc);
+}
+
+MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
+MODULE_DESCRIPTION("User level driver support for input subsystem");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.3");
+
+module_init(uinput_init);
+module_exit(uinput_exit);
diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c
new file mode 100644
index 0000000..e25f87b
--- /dev/null
+++ b/drivers/input/misc/wistron_btns.c
@@ -0,0 +1,1407 @@
+/*
+ * Wistron laptop button driver
+ * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
+ * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
+ * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
+ *
+ * You can redistribute and/or modify this program under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <linux/io.h>
+#include <linux/dmi.h>
+#include <linux/init.h>
+#include <linux/input-polldev.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/mc146818rtc.h>
+#include <linux/module.h>
+#include <linux/preempt.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+
+/* How often we poll keys - msecs */
+#define POLL_INTERVAL_DEFAULT	500 /* when idle */
+#define POLL_INTERVAL_BURST	100 /* when a key was recently pressed */
+
+/* BIOS subsystem IDs */
+#define WIFI		0x35
+#define BLUETOOTH	0x34
+#define MAIL_LED	0x31
+
+MODULE_AUTHOR("Miloslav Trmac <mitr@volny.cz>");
+MODULE_DESCRIPTION("Wistron laptop button driver");
+MODULE_LICENSE("GPL v2");
+
+static bool force; /* = 0; */
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Load even if computer is not in database");
+
+static char *keymap_name; /* = NULL; */
+module_param_named(keymap, keymap_name, charp, 0);
+MODULE_PARM_DESC(keymap, "Keymap name, if it can't be autodetected [generic, 1557/MS2141]");
+
+static struct platform_device *wistron_device;
+
+ /* BIOS interface implementation */
+
+static void __iomem *bios_entry_point; /* BIOS routine entry point */
+static void __iomem *bios_code_map_base;
+static void __iomem *bios_data_map_base;
+
+static u8 cmos_address;
+
+struct regs {
+	u32 eax, ebx, ecx;
+};
+
+static void call_bios(struct regs *regs)
+{
+	unsigned long flags;
+
+	preempt_disable();
+	local_irq_save(flags);
+	asm volatile ("pushl %%ebp;"
+		      "movl %7, %%ebp;"
+		      "call *%6;"
+		      "popl %%ebp"
+		      : "=a" (regs->eax), "=b" (regs->ebx), "=c" (regs->ecx)
+		      : "0" (regs->eax), "1" (regs->ebx), "2" (regs->ecx),
+			"m" (bios_entry_point), "m" (bios_data_map_base)
+		      : "edx", "edi", "esi", "memory");
+	local_irq_restore(flags);
+	preempt_enable();
+}
+
+static ssize_t __init locate_wistron_bios(void __iomem *base)
+{
+	static unsigned char __initdata signature[] =
+		{ 0x42, 0x21, 0x55, 0x30 };
+	ssize_t offset;
+
+	for (offset = 0; offset < 0x10000; offset += 0x10) {
+		if (check_signature(base + offset, signature,
+				    sizeof(signature)) != 0)
+			return offset;
+	}
+	return -1;
+}
+
+static int __init map_bios(void)
+{
+	void __iomem *base;
+	ssize_t offset;
+	u32 entry_point;
+
+	base = ioremap(0xF0000, 0x10000); /* Can't fail */
+	offset = locate_wistron_bios(base);
+	if (offset < 0) {
+		printk(KERN_ERR "wistron_btns: BIOS entry point not found\n");
+		iounmap(base);
+		return -ENODEV;
+	}
+
+	entry_point = readl(base + offset + 5);
+	printk(KERN_DEBUG
+		"wistron_btns: BIOS signature found at %p, entry point %08X\n",
+		base + offset, entry_point);
+
+	if (entry_point >= 0xF0000) {
+		bios_code_map_base = base;
+		bios_entry_point = bios_code_map_base + (entry_point & 0xFFFF);
+	} else {
+		iounmap(base);
+		bios_code_map_base = ioremap(entry_point & ~0x3FFF, 0x4000);
+		if (bios_code_map_base == NULL) {
+			printk(KERN_ERR
+				"wistron_btns: Can't map BIOS code at %08X\n",
+				entry_point & ~0x3FFF);
+			goto err;
+		}
+		bios_entry_point = bios_code_map_base + (entry_point & 0x3FFF);
+	}
+	/* The Windows driver maps 0x10000 bytes, we keep only one page... */
+	bios_data_map_base = ioremap(0x400, 0xc00);
+	if (bios_data_map_base == NULL) {
+		printk(KERN_ERR "wistron_btns: Can't map BIOS data\n");
+		goto err_code;
+	}
+	return 0;
+
+err_code:
+	iounmap(bios_code_map_base);
+err:
+	return -ENOMEM;
+}
+
+static inline void unmap_bios(void)
+{
+	iounmap(bios_code_map_base);
+	iounmap(bios_data_map_base);
+}
+
+ /* BIOS calls */
+
+static u16 bios_pop_queue(void)
+{
+	struct regs regs;
+
+	memset(&regs, 0, sizeof (regs));
+	regs.eax = 0x9610;
+	regs.ebx = 0x061C;
+	regs.ecx = 0x0000;
+	call_bios(&regs);
+
+	return regs.eax;
+}
+
+static void bios_attach(void)
+{
+	struct regs regs;
+
+	memset(&regs, 0, sizeof (regs));
+	regs.eax = 0x9610;
+	regs.ebx = 0x012E;
+	call_bios(&regs);
+}
+
+static void bios_detach(void)
+{
+	struct regs regs;
+
+	memset(&regs, 0, sizeof (regs));
+	regs.eax = 0x9610;
+	regs.ebx = 0x002E;
+	call_bios(&regs);
+}
+
+static u8 bios_get_cmos_address(void)
+{
+	struct regs regs;
+
+	memset(&regs, 0, sizeof (regs));
+	regs.eax = 0x9610;
+	regs.ebx = 0x051C;
+	call_bios(&regs);
+
+	return regs.ecx;
+}
+
+static u16 bios_get_default_setting(u8 subsys)
+{
+	struct regs regs;
+
+	memset(&regs, 0, sizeof (regs));
+	regs.eax = 0x9610;
+	regs.ebx = 0x0200 | subsys;
+	call_bios(&regs);
+
+	return regs.eax;
+}
+
+static void bios_set_state(u8 subsys, int enable)
+{
+	struct regs regs;
+
+	memset(&regs, 0, sizeof (regs));
+	regs.eax = 0x9610;
+	regs.ebx = (enable ? 0x0100 : 0x0000) | subsys;
+	call_bios(&regs);
+}
+
+/* Hardware database */
+
+#define KE_WIFI		(KE_LAST + 1)
+#define KE_BLUETOOTH	(KE_LAST + 2)
+
+#define FE_MAIL_LED 0x01
+#define FE_WIFI_LED 0x02
+#define FE_UNTESTED 0x80
+
+static struct key_entry *keymap; /* = NULL; Current key map */
+static bool have_wifi;
+static bool have_bluetooth;
+static int leds_present;	/* bitmask of leds present */
+
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+	const struct key_entry *key;
+
+	keymap = dmi->driver_data;
+	for (key = keymap; key->type != KE_END; key++) {
+		if (key->type == KE_WIFI)
+			have_wifi = true;
+		else if (key->type == KE_BLUETOOTH)
+			have_bluetooth = true;
+	}
+	leds_present = key->code & (FE_MAIL_LED | FE_WIFI_LED);
+
+	return 1;
+}
+
+static struct key_entry keymap_empty[] __initdata = {
+	{ KE_END, 0 }
+};
+
+static struct key_entry keymap_fs_amilo_pro_v2000[] __initdata = {
+	{ KE_KEY,  0x01, {KEY_HELP} },
+	{ KE_KEY,  0x11, {KEY_PROG1} },
+	{ KE_KEY,  0x12, {KEY_PROG2} },
+	{ KE_WIFI, 0x30 },
+	{ KE_KEY,  0x31, {KEY_MAIL} },
+	{ KE_KEY,  0x36, {KEY_WWW} },
+	{ KE_END,  0 }
+};
+
+static struct key_entry keymap_fs_amilo_pro_v3505[] __initdata = {
+	{ KE_KEY,       0x01, {KEY_HELP} },          /* Fn+F1 */
+	{ KE_KEY,       0x06, {KEY_DISPLAYTOGGLE} }, /* Fn+F4 */
+	{ KE_BLUETOOTH, 0x30 },                      /* Fn+F10 */
+	{ KE_KEY,       0x31, {KEY_MAIL} },          /* mail button */
+	{ KE_KEY,       0x36, {KEY_WWW} },           /* www button */
+	{ KE_WIFI,      0x78 },                      /* satellite dish button */
+	{ KE_END,       0 }
+};
+
+static struct key_entry keymap_fs_amilo_pro_v8210[] __initdata = {
+	{ KE_KEY,       0x01, {KEY_HELP} },          /* Fn+F1 */
+	{ KE_KEY,       0x06, {KEY_DISPLAYTOGGLE} }, /* Fn+F4 */
+	{ KE_BLUETOOTH, 0x30 },                      /* Fn+F10 */
+	{ KE_KEY,       0x31, {KEY_MAIL} },          /* mail button */
+	{ KE_KEY,       0x36, {KEY_WWW} },           /* www button */
+	{ KE_WIFI,      0x78 },                      /* satelite dish button */
+	{ KE_END,       FE_WIFI_LED }
+};
+
+static struct key_entry keymap_fujitsu_n3510[] __initdata = {
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x71, {KEY_STOPCD} },
+	{ KE_KEY, 0x72, {KEY_PLAYPAUSE} },
+	{ KE_KEY, 0x74, {KEY_REWIND} },
+	{ KE_KEY, 0x78, {KEY_FORWARD} },
+	{ KE_END, 0 }
+};
+
+static struct key_entry keymap_wistron_ms2111[] __initdata = {
+	{ KE_KEY,  0x11, {KEY_PROG1} },
+	{ KE_KEY,  0x12, {KEY_PROG2} },
+	{ KE_KEY,  0x13, {KEY_PROG3} },
+	{ KE_KEY,  0x31, {KEY_MAIL} },
+	{ KE_KEY,  0x36, {KEY_WWW} },
+	{ KE_END, FE_MAIL_LED }
+};
+
+static struct key_entry keymap_wistron_md40100[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+	{ KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_ms2141[] __initdata = {
+	{ KE_KEY,  0x11, {KEY_PROG1} },
+	{ KE_KEY,  0x12, {KEY_PROG2} },
+	{ KE_WIFI, 0x30 },
+	{ KE_KEY,  0x22, {KEY_REWIND} },
+	{ KE_KEY,  0x23, {KEY_FORWARD} },
+	{ KE_KEY,  0x24, {KEY_PLAYPAUSE} },
+	{ KE_KEY,  0x25, {KEY_STOPCD} },
+	{ KE_KEY,  0x31, {KEY_MAIL} },
+	{ KE_KEY,  0x36, {KEY_WWW} },
+	{ KE_END,  0 }
+};
+
+static struct key_entry keymap_acer_aspire_1500[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x03, {KEY_POWER} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_WIFI, 0x30 },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_KEY, 0x49, {KEY_CONFIG} },
+	{ KE_BLUETOOTH, 0x44 },
+	{ KE_END, FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_aspire_1600[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x03, {KEY_POWER} },
+	{ KE_KEY, 0x08, {KEY_MUTE} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x13, {KEY_PROG3} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_KEY, 0x49, {KEY_CONFIG} },
+	{ KE_WIFI, 0x30 },
+	{ KE_BLUETOOTH, 0x44 },
+	{ KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+/* 3020 has been tested */
+static struct key_entry keymap_acer_aspire_5020[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x03, {KEY_POWER} },
+	{ KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_KEY, 0x6a, {KEY_CONFIG} },
+	{ KE_WIFI, 0x30 },
+	{ KE_BLUETOOTH, 0x44 },
+	{ KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_2410[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x6d, {KEY_POWER} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_KEY, 0x6a, {KEY_CONFIG} },
+	{ KE_WIFI, 0x30 },
+	{ KE_BLUETOOTH, 0x44 },
+	{ KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_110[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x03, {KEY_POWER} },
+	{ KE_KEY, 0x08, {KEY_MUTE} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x20, {KEY_VOLUMEUP} },
+	{ KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */
+	{ KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */
+	{ KE_WIFI, 0x30 },
+	{ KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_300[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x03, {KEY_POWER} },
+	{ KE_KEY, 0x08, {KEY_MUTE} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x20, {KEY_VOLUMEUP} },
+	{ KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_WIFI, 0x30 },
+	{ KE_BLUETOOTH, 0x44 },
+	{ KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_380[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x03, {KEY_POWER} }, /* not 370 */
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x13, {KEY_PROG3} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_WIFI, 0x30 },
+	{ KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+/* unusual map */
+static struct key_entry keymap_acer_travelmate_220[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x11, {KEY_MAIL} },
+	{ KE_KEY, 0x12, {KEY_WWW} },
+	{ KE_KEY, 0x13, {KEY_PROG2} },
+	{ KE_KEY, 0x31, {KEY_PROG1} },
+	{ KE_END, FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_230[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_END, FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_240[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x03, {KEY_POWER} },
+	{ KE_KEY, 0x08, {KEY_MUTE} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_BLUETOOTH, 0x44 },
+	{ KE_WIFI, 0x30 },
+	{ KE_END, FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_350[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x13, {KEY_MAIL} },
+	{ KE_KEY, 0x14, {KEY_PROG3} },
+	{ KE_KEY, 0x15, {KEY_WWW} },
+	{ KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_360[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x13, {KEY_MAIL} },
+	{ KE_KEY, 0x14, {KEY_PROG3} },
+	{ KE_KEY, 0x15, {KEY_WWW} },
+	{ KE_KEY, 0x40, {KEY_WLAN} },
+	{ KE_END, FE_WIFI_LED | FE_UNTESTED } /* no mail led */
+};
+
+/* Wifi subsystem only activates the led. Therefore we need to pass
+ * wifi event as a normal key, then userspace can really change the wifi state.
+ * TODO we need to export led state to userspace (wifi and mail) */
+static struct key_entry keymap_acer_travelmate_610[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x13, {KEY_PROG3} },
+	{ KE_KEY, 0x14, {KEY_MAIL} },
+	{ KE_KEY, 0x15, {KEY_WWW} },
+	{ KE_KEY, 0x40, {KEY_WLAN} },
+	{ KE_END, FE_MAIL_LED | FE_WIFI_LED }
+};
+
+static struct key_entry keymap_acer_travelmate_630[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x03, {KEY_POWER} },
+	{ KE_KEY, 0x08, {KEY_MUTE} }, /* not 620 */
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x13, {KEY_PROG3} },
+	{ KE_KEY, 0x20, {KEY_VOLUMEUP} },
+	{ KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_WIFI, 0x30 },
+	{ KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_aopen_1559as[] __initdata = {
+	{ KE_KEY,  0x01, {KEY_HELP} },
+	{ KE_KEY,  0x06, {KEY_PROG3} },
+	{ KE_KEY,  0x11, {KEY_PROG1} },
+	{ KE_KEY,  0x12, {KEY_PROG2} },
+	{ KE_WIFI, 0x30 },
+	{ KE_KEY,  0x31, {KEY_MAIL} },
+	{ KE_KEY,  0x36, {KEY_WWW} },
+	{ KE_END,  0 },
+};
+
+static struct key_entry keymap_fs_amilo_d88x0[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x08, {KEY_MUTE} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x13, {KEY_PROG3} },
+	{ KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_md2900[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_WIFI, 0x30 },
+	{ KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_md96500[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */
+	{ KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+	{ KE_KEY, 0x08, {KEY_MUTE} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x20, {KEY_VOLUMEUP} },
+	{ KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+	{ KE_KEY, 0x22, {KEY_REWIND} },
+	{ KE_KEY, 0x23, {KEY_FORWARD} },
+	{ KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+	{ KE_KEY, 0x25, {KEY_STOPCD} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_WIFI, 0x30 },
+	{ KE_BLUETOOTH, 0x44 },
+	{ KE_END, 0 }
+};
+
+static struct key_entry keymap_wistron_generic[] __initdata = {
+	{ KE_KEY, 0x01, {KEY_HELP} },
+	{ KE_KEY, 0x02, {KEY_CONFIG} },
+	{ KE_KEY, 0x03, {KEY_POWER} },
+	{ KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */
+	{ KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+	{ KE_KEY, 0x08, {KEY_MUTE} },
+	{ KE_KEY, 0x11, {KEY_PROG1} },
+	{ KE_KEY, 0x12, {KEY_PROG2} },
+	{ KE_KEY, 0x13, {KEY_PROG3} },
+	{ KE_KEY, 0x14, {KEY_MAIL} },
+	{ KE_KEY, 0x15, {KEY_WWW} },
+	{ KE_KEY, 0x20, {KEY_VOLUMEUP} },
+	{ KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+	{ KE_KEY, 0x22, {KEY_REWIND} },
+	{ KE_KEY, 0x23, {KEY_FORWARD} },
+	{ KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+	{ KE_KEY, 0x25, {KEY_STOPCD} },
+	{ KE_KEY, 0x31, {KEY_MAIL} },
+	{ KE_KEY, 0x36, {KEY_WWW} },
+	{ KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+	{ KE_KEY, 0x40, {KEY_WLAN} },
+	{ KE_KEY, 0x49, {KEY_CONFIG} },
+	{ KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */
+	{ KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */
+	{ KE_KEY, 0x6a, {KEY_CONFIG} },
+	{ KE_KEY, 0x6d, {KEY_POWER} },
+	{ KE_KEY, 0x71, {KEY_STOPCD} },
+	{ KE_KEY, 0x72, {KEY_PLAYPAUSE} },
+	{ KE_KEY, 0x74, {KEY_REWIND} },
+	{ KE_KEY, 0x78, {KEY_FORWARD} },
+	{ KE_WIFI, 0x30 },
+	{ KE_BLUETOOTH, 0x44 },
+	{ KE_END, 0 }
+};
+
+static struct key_entry keymap_aopen_1557[] __initdata = {
+	{ KE_KEY,  0x01, {KEY_HELP} },
+	{ KE_KEY,  0x11, {KEY_PROG1} },
+	{ KE_KEY,  0x12, {KEY_PROG2} },
+	{ KE_WIFI, 0x30 },
+	{ KE_KEY,  0x22, {KEY_REWIND} },
+	{ KE_KEY,  0x23, {KEY_FORWARD} },
+	{ KE_KEY,  0x24, {KEY_PLAYPAUSE} },
+	{ KE_KEY,  0x25, {KEY_STOPCD} },
+	{ KE_KEY,  0x31, {KEY_MAIL} },
+	{ KE_KEY,  0x36, {KEY_WWW} },
+	{ KE_END,  0 }
+};
+
+static struct key_entry keymap_prestigio[] __initdata = {
+	{ KE_KEY,  0x11, {KEY_PROG1} },
+	{ KE_KEY,  0x12, {KEY_PROG2} },
+	{ KE_WIFI, 0x30 },
+	{ KE_KEY,  0x22, {KEY_REWIND} },
+	{ KE_KEY,  0x23, {KEY_FORWARD} },
+	{ KE_KEY,  0x24, {KEY_PLAYPAUSE} },
+	{ KE_KEY,  0x25, {KEY_STOPCD} },
+	{ KE_KEY,  0x31, {KEY_MAIL} },
+	{ KE_KEY,  0x36, {KEY_WWW} },
+	{ KE_END,  0 }
+};
+
+
+/*
+ * If your machine is not here (which is currently rather likely), please send
+ * a list of buttons and their key codes (reported when loading this module
+ * with force=1) and the output of dmidecode to $MODULE_AUTHOR.
+ */
+static const struct dmi_system_id dmi_ids[] __initconst = {
+	{
+		/* Fujitsu-Siemens Amilo Pro V2000 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2000"),
+		},
+		.driver_data = keymap_fs_amilo_pro_v2000
+	},
+	{
+		/* Fujitsu-Siemens Amilo Pro Edition V3505 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Edition V3505"),
+		},
+		.driver_data = keymap_fs_amilo_pro_v3505
+	},
+	{
+		/* Fujitsu-Siemens Amilo Pro Edition V8210 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Series V8210"),
+		},
+		.driver_data = keymap_fs_amilo_pro_v8210
+	},
+	{
+		/* Fujitsu-Siemens Amilo M7400 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO M        "),
+		},
+		.driver_data = keymap_fs_amilo_pro_v2000
+	},
+	{
+		/* Maxdata Pro 7000 DX */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "MAXDATA"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Pro 7000"),
+		},
+		.driver_data = keymap_fs_amilo_pro_v2000
+	},
+	{
+		/* Fujitsu N3510 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "N3510"),
+		},
+		.driver_data = keymap_fujitsu_n3510
+	},
+	{
+		/* Acer Aspire 1500 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1500"),
+		},
+		.driver_data = keymap_acer_aspire_1500
+	},
+	{
+		/* Acer Aspire 1600 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1600"),
+		},
+		.driver_data = keymap_acer_aspire_1600
+	},
+	{
+		/* Acer Aspire 3020 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3020"),
+		},
+		.driver_data = keymap_acer_aspire_5020
+	},
+	{
+		/* Acer Aspire 5020 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5020"),
+		},
+		.driver_data = keymap_acer_aspire_5020
+	},
+	{
+		/* Acer TravelMate 2100 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2100"),
+		},
+		.driver_data = keymap_acer_aspire_5020
+	},
+	{
+		/* Acer TravelMate 2410 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2410"),
+		},
+		.driver_data = keymap_acer_travelmate_2410
+	},
+	{
+		/* Acer TravelMate C300 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C300"),
+		},
+		.driver_data = keymap_acer_travelmate_300
+	},
+	{
+		/* Acer TravelMate C100 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C100"),
+		},
+		.driver_data = keymap_acer_travelmate_300
+	},
+	{
+		/* Acer TravelMate C110 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C110"),
+		},
+		.driver_data = keymap_acer_travelmate_110
+	},
+	{
+		/* Acer TravelMate 380 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 380"),
+		},
+		.driver_data = keymap_acer_travelmate_380
+	},
+	{
+		/* Acer TravelMate 370 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 370"),
+		},
+		.driver_data = keymap_acer_travelmate_380 /* keyboard minus 1 key */
+	},
+	{
+		/* Acer TravelMate 220 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 220"),
+		},
+		.driver_data = keymap_acer_travelmate_220
+	},
+	{
+		/* Acer TravelMate 260 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 260"),
+		},
+		.driver_data = keymap_acer_travelmate_220
+	},
+	{
+		/* Acer TravelMate 230 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 230"),
+			/* acerhk looks for "TravelMate F4..." ?! */
+		},
+		.driver_data = keymap_acer_travelmate_230
+	},
+	{
+		/* Acer TravelMate 280 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 280"),
+		},
+		.driver_data = keymap_acer_travelmate_230
+	},
+	{
+		/* Acer TravelMate 240 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 240"),
+		},
+		.driver_data = keymap_acer_travelmate_240
+	},
+	{
+		/* Acer TravelMate 250 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 250"),
+		},
+		.driver_data = keymap_acer_travelmate_240
+	},
+	{
+		/* Acer TravelMate 2424NWXCi */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2420"),
+		},
+		.driver_data = keymap_acer_travelmate_240
+	},
+	{
+		/* Acer TravelMate 350 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 350"),
+		},
+		.driver_data = keymap_acer_travelmate_350
+	},
+	{
+		/* Acer TravelMate 360 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"),
+		},
+		.driver_data = keymap_acer_travelmate_360
+	},
+	{
+		/* Acer TravelMate 610 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ACER"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 610"),
+		},
+		.driver_data = keymap_acer_travelmate_610
+	},
+	{
+		/* Acer TravelMate 620 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 620"),
+		},
+		.driver_data = keymap_acer_travelmate_630
+	},
+	{
+		/* Acer TravelMate 630 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 630"),
+		},
+		.driver_data = keymap_acer_travelmate_630
+	},
+	{
+		/* AOpen 1559AS */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "E2U"),
+			DMI_MATCH(DMI_BOARD_NAME, "E2U"),
+		},
+		.driver_data = keymap_aopen_1559as
+	},
+	{
+		/* Medion MD 9783 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "MD 9783"),
+		},
+		.driver_data = keymap_wistron_ms2111
+	},
+	{
+		/* Medion MD 40100 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "WID2000"),
+		},
+		.driver_data = keymap_wistron_md40100
+	},
+	{
+		/* Medion MD 2900 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2000"),
+		},
+		.driver_data = keymap_wistron_md2900
+	},
+	{
+		/* Medion MD 42200 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Medion"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2030"),
+		},
+		.driver_data = keymap_fs_amilo_pro_v2000
+	},
+	{
+		/* Medion MD 96500 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2040"),
+		},
+		.driver_data = keymap_wistron_md96500
+	},
+	{
+		/* Medion MD 95400 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2050"),
+		},
+		.driver_data = keymap_wistron_md96500
+	},
+	{
+		/* Fujitsu Siemens Amilo D7820 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), /* not sure */
+			DMI_MATCH(DMI_PRODUCT_NAME, "Amilo D"),
+		},
+		.driver_data = keymap_fs_amilo_d88x0
+	},
+	{
+		/* Fujitsu Siemens Amilo D88x0 */
+		.callback = dmi_matched,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO D"),
+		},
+		.driver_data = keymap_fs_amilo_d88x0
+	},
+	{ NULL, }
+};
+MODULE_DEVICE_TABLE(dmi, dmi_ids);
+
+/* Copy the good keymap, as the original ones are free'd */
+static int __init copy_keymap(void)
+{
+	const struct key_entry *key;
+	struct key_entry *new_keymap;
+	unsigned int length = 1;
+
+	for (key = keymap; key->type != KE_END; key++)
+		length++;
+
+	new_keymap = kmemdup(keymap, length * sizeof(struct key_entry),
+			     GFP_KERNEL);
+	if (!new_keymap)
+		return -ENOMEM;
+
+	keymap = new_keymap;
+
+	return 0;
+}
+
+static int __init select_keymap(void)
+{
+	dmi_check_system(dmi_ids);
+	if (keymap_name != NULL) {
+		if (strcmp (keymap_name, "1557/MS2141") == 0)
+			keymap = keymap_wistron_ms2141;
+		else if (strcmp (keymap_name, "aopen1557") == 0)
+			keymap = keymap_aopen_1557;
+		else if (strcmp (keymap_name, "prestigio") == 0)
+			keymap = keymap_prestigio;
+		else if (strcmp (keymap_name, "generic") == 0)
+			keymap = keymap_wistron_generic;
+		else {
+			printk(KERN_ERR "wistron_btns: Keymap unknown\n");
+			return -EINVAL;
+		}
+	}
+	if (keymap == NULL) {
+		if (!force) {
+			printk(KERN_ERR "wistron_btns: System unknown\n");
+			return -ENODEV;
+		}
+		keymap = keymap_empty;
+	}
+
+	return copy_keymap();
+}
+
+ /* Input layer interface */
+
+static struct input_polled_dev *wistron_idev;
+static unsigned long jiffies_last_press;
+static bool wifi_enabled;
+static bool bluetooth_enabled;
+
+ /* led management */
+static void wistron_mail_led_set(struct led_classdev *led_cdev,
+				enum led_brightness value)
+{
+	bios_set_state(MAIL_LED, (value != LED_OFF) ? 1 : 0);
+}
+
+/* same as setting up wifi card, but for laptops on which the led is managed */
+static void wistron_wifi_led_set(struct led_classdev *led_cdev,
+				enum led_brightness value)
+{
+	bios_set_state(WIFI, (value != LED_OFF) ? 1 : 0);
+}
+
+static struct led_classdev wistron_mail_led = {
+	.name			= "wistron:green:mail",
+	.brightness_set		= wistron_mail_led_set,
+};
+
+static struct led_classdev wistron_wifi_led = {
+	.name			= "wistron:red:wifi",
+	.brightness_set		= wistron_wifi_led_set,
+};
+
+static void wistron_led_init(struct device *parent)
+{
+	if (leds_present & FE_WIFI_LED) {
+		u16 wifi = bios_get_default_setting(WIFI);
+		if (wifi & 1) {
+			wistron_wifi_led.brightness = (wifi & 2) ? LED_FULL : LED_OFF;
+			if (led_classdev_register(parent, &wistron_wifi_led))
+				leds_present &= ~FE_WIFI_LED;
+			else
+				bios_set_state(WIFI, wistron_wifi_led.brightness);
+
+		} else
+			leds_present &= ~FE_WIFI_LED;
+	}
+
+	if (leds_present & FE_MAIL_LED) {
+		/* bios_get_default_setting(MAIL) always retuns 0, so just turn the led off */
+		wistron_mail_led.brightness = LED_OFF;
+		if (led_classdev_register(parent, &wistron_mail_led))
+			leds_present &= ~FE_MAIL_LED;
+		else
+			bios_set_state(MAIL_LED, wistron_mail_led.brightness);
+	}
+}
+
+static void wistron_led_remove(void)
+{
+	if (leds_present & FE_MAIL_LED)
+		led_classdev_unregister(&wistron_mail_led);
+
+	if (leds_present & FE_WIFI_LED)
+		led_classdev_unregister(&wistron_wifi_led);
+}
+
+static inline void wistron_led_suspend(void)
+{
+	if (leds_present & FE_MAIL_LED)
+		led_classdev_suspend(&wistron_mail_led);
+
+	if (leds_present & FE_WIFI_LED)
+		led_classdev_suspend(&wistron_wifi_led);
+}
+
+static inline void wistron_led_resume(void)
+{
+	if (leds_present & FE_MAIL_LED)
+		led_classdev_resume(&wistron_mail_led);
+
+	if (leds_present & FE_WIFI_LED)
+		led_classdev_resume(&wistron_wifi_led);
+}
+
+static void handle_key(u8 code)
+{
+	const struct key_entry *key =
+		sparse_keymap_entry_from_scancode(wistron_idev->input, code);
+
+	if (key) {
+		switch (key->type) {
+		case KE_WIFI:
+			if (have_wifi) {
+				wifi_enabled = !wifi_enabled;
+				bios_set_state(WIFI, wifi_enabled);
+			}
+			break;
+
+		case KE_BLUETOOTH:
+			if (have_bluetooth) {
+				bluetooth_enabled = !bluetooth_enabled;
+				bios_set_state(BLUETOOTH, bluetooth_enabled);
+			}
+			break;
+
+		default:
+			sparse_keymap_report_entry(wistron_idev->input,
+						   key, 1, true);
+			break;
+		}
+		jiffies_last_press = jiffies;
+	} else
+		printk(KERN_NOTICE
+			"wistron_btns: Unknown key code %02X\n", code);
+}
+
+static void poll_bios(bool discard)
+{
+	u8 qlen;
+	u16 val;
+
+	for (;;) {
+		qlen = CMOS_READ(cmos_address);
+		if (qlen == 0)
+			break;
+		val = bios_pop_queue();
+		if (val != 0 && !discard)
+			handle_key((u8)val);
+	}
+}
+
+static void wistron_flush(struct input_polled_dev *dev)
+{
+	/* Flush stale event queue */
+	poll_bios(true);
+}
+
+static void wistron_poll(struct input_polled_dev *dev)
+{
+	poll_bios(false);
+
+	/* Increase poll frequency if user is currently pressing keys (< 2s ago) */
+	if (time_before(jiffies, jiffies_last_press + 2 * HZ))
+		dev->poll_interval = POLL_INTERVAL_BURST;
+	else
+		dev->poll_interval = POLL_INTERVAL_DEFAULT;
+}
+
+static int wistron_setup_keymap(struct input_dev *dev,
+					  struct key_entry *entry)
+{
+	switch (entry->type) {
+
+	/* if wifi or bluetooth are not available, create normal keys */
+	case KE_WIFI:
+		if (!have_wifi) {
+			entry->type = KE_KEY;
+			entry->keycode = KEY_WLAN;
+		}
+		break;
+
+	case KE_BLUETOOTH:
+		if (!have_bluetooth) {
+			entry->type = KE_KEY;
+			entry->keycode = KEY_BLUETOOTH;
+		}
+		break;
+
+	case KE_END:
+		if (entry->code & FE_UNTESTED)
+			printk(KERN_WARNING "Untested laptop multimedia keys, "
+				"please report success or failure to "
+				"eric.piel@tremplin-utc.net\n");
+		break;
+	}
+
+	return 0;
+}
+
+static int setup_input_dev(void)
+{
+	struct input_dev *input_dev;
+	int error;
+
+	wistron_idev = input_allocate_polled_device();
+	if (!wistron_idev)
+		return -ENOMEM;
+
+	wistron_idev->open = wistron_flush;
+	wistron_idev->poll = wistron_poll;
+	wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT;
+
+	input_dev = wistron_idev->input;
+	input_dev->name = "Wistron laptop buttons";
+	input_dev->phys = "wistron/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->dev.parent = &wistron_device->dev;
+
+	error = sparse_keymap_setup(input_dev, keymap, wistron_setup_keymap);
+	if (error)
+		goto err_free_dev;
+
+	error = input_register_polled_device(wistron_idev);
+	if (error)
+		goto err_free_keymap;
+
+	return 0;
+
+ err_free_keymap:
+	sparse_keymap_free(input_dev);
+ err_free_dev:
+	input_free_polled_device(wistron_idev);
+	return error;
+}
+
+/* Driver core */
+
+static int wistron_probe(struct platform_device *dev)
+{
+	int err;
+
+	bios_attach();
+	cmos_address = bios_get_cmos_address();
+
+	if (have_wifi) {
+		u16 wifi = bios_get_default_setting(WIFI);
+		if (wifi & 1)
+			wifi_enabled = wifi & 2;
+		else
+			have_wifi = 0;
+
+		if (have_wifi)
+			bios_set_state(WIFI, wifi_enabled);
+	}
+
+	if (have_bluetooth) {
+		u16 bt = bios_get_default_setting(BLUETOOTH);
+		if (bt & 1)
+			bluetooth_enabled = bt & 2;
+		else
+			have_bluetooth = false;
+
+		if (have_bluetooth)
+			bios_set_state(BLUETOOTH, bluetooth_enabled);
+	}
+
+	wistron_led_init(&dev->dev);
+
+	err = setup_input_dev();
+	if (err) {
+		bios_detach();
+		return err;
+	}
+
+	return 0;
+}
+
+static int wistron_remove(struct platform_device *dev)
+{
+	wistron_led_remove();
+	input_unregister_polled_device(wistron_idev);
+	sparse_keymap_free(wistron_idev->input);
+	input_free_polled_device(wistron_idev);
+	bios_detach();
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wistron_suspend(struct device *dev)
+{
+	if (have_wifi)
+		bios_set_state(WIFI, 0);
+
+	if (have_bluetooth)
+		bios_set_state(BLUETOOTH, 0);
+
+	wistron_led_suspend();
+
+	return 0;
+}
+
+static int wistron_resume(struct device *dev)
+{
+	if (have_wifi)
+		bios_set_state(WIFI, wifi_enabled);
+
+	if (have_bluetooth)
+		bios_set_state(BLUETOOTH, bluetooth_enabled);
+
+	wistron_led_resume();
+
+	poll_bios(true);
+
+	return 0;
+}
+
+static const struct dev_pm_ops wistron_pm_ops = {
+	.suspend	= wistron_suspend,
+	.resume		= wistron_resume,
+	.poweroff	= wistron_suspend,
+	.restore	= wistron_resume,
+};
+#endif
+
+static struct platform_driver wistron_driver = {
+	.driver		= {
+		.name	= "wistron-bios",
+#ifdef CONFIG_PM
+		.pm	= &wistron_pm_ops,
+#endif
+	},
+	.probe		= wistron_probe,
+	.remove		= wistron_remove,
+};
+
+static int __init wb_module_init(void)
+{
+	int err;
+
+	err = select_keymap();
+	if (err)
+		return err;
+
+	err = map_bios();
+	if (err)
+		goto err_free_keymap;
+
+	err = platform_driver_register(&wistron_driver);
+	if (err)
+		goto err_unmap_bios;
+
+	wistron_device = platform_device_alloc("wistron-bios", -1);
+	if (!wistron_device) {
+		err = -ENOMEM;
+		goto err_unregister_driver;
+	}
+
+	err = platform_device_add(wistron_device);
+	if (err)
+		goto err_free_device;
+
+	return 0;
+
+ err_free_device:
+	platform_device_put(wistron_device);
+ err_unregister_driver:
+	platform_driver_unregister(&wistron_driver);
+ err_unmap_bios:
+	unmap_bios();
+ err_free_keymap:
+	kfree(keymap);
+
+	return err;
+}
+
+static void __exit wb_module_exit(void)
+{
+	platform_device_unregister(wistron_device);
+	platform_driver_unregister(&wistron_driver);
+	unmap_bios();
+	kfree(keymap);
+}
+
+module_init(wb_module_init);
+module_exit(wb_module_exit);
diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c
new file mode 100644
index 0000000..1b44de2
--- /dev/null
+++ b/drivers/input/misc/wm831x-on.c
@@ -0,0 +1,150 @@
+/**
+ * wm831x-on.c - WM831X ON pin driver
+ *
+ * Copyright (C) 2009 Wolfson Microelectronics plc
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/wm831x/core.h>
+
+struct wm831x_on {
+	struct input_dev *dev;
+	struct delayed_work work;
+	struct wm831x *wm831x;
+};
+
+/*
+ * The chip gives us an interrupt when the ON pin is asserted but we
+ * then need to poll to see when the pin is deasserted.
+ */
+static void wm831x_poll_on(struct work_struct *work)
+{
+	struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on,
+						   work.work);
+	struct wm831x *wm831x = wm831x_on->wm831x;
+	int poll, ret;
+
+	ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL);
+	if (ret >= 0) {
+		poll = !(ret & WM831X_ON_PIN_STS);
+
+		input_report_key(wm831x_on->dev, KEY_POWER, poll);
+		input_sync(wm831x_on->dev);
+	} else {
+		dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret);
+		poll = 1;
+	}
+
+	if (poll)
+		schedule_delayed_work(&wm831x_on->work, 100);
+}
+
+static irqreturn_t wm831x_on_irq(int irq, void *data)
+{
+	struct wm831x_on *wm831x_on = data;
+
+	schedule_delayed_work(&wm831x_on->work, 0);
+
+	return IRQ_HANDLED;
+}
+
+static int wm831x_on_probe(struct platform_device *pdev)
+{
+	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+	struct wm831x_on *wm831x_on;
+	int irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0));
+	int ret;
+
+	wm831x_on = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_on),
+				 GFP_KERNEL);
+	if (!wm831x_on) {
+		dev_err(&pdev->dev, "Can't allocate data\n");
+		return -ENOMEM;
+	}
+
+	wm831x_on->wm831x = wm831x;
+	INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on);
+
+	wm831x_on->dev = devm_input_allocate_device(&pdev->dev);
+	if (!wm831x_on->dev) {
+		dev_err(&pdev->dev, "Can't allocate input dev\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	wm831x_on->dev->evbit[0] = BIT_MASK(EV_KEY);
+	wm831x_on->dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+	wm831x_on->dev->name = "wm831x_on";
+	wm831x_on->dev->phys = "wm831x_on/input0";
+	wm831x_on->dev->dev.parent = &pdev->dev;
+
+	ret = request_threaded_irq(irq, NULL, wm831x_on_irq,
+				   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+				   "wm831x_on",
+				   wm831x_on);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret);
+		goto err_input_dev;
+	}
+	ret = input_register_device(wm831x_on->dev);
+	if (ret) {
+		dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret);
+		goto err_irq;
+	}
+
+	platform_set_drvdata(pdev, wm831x_on);
+
+	return 0;
+
+err_irq:
+	free_irq(irq, wm831x_on);
+err_input_dev:
+err:
+	return ret;
+}
+
+static int wm831x_on_remove(struct platform_device *pdev)
+{
+	struct wm831x_on *wm831x_on = platform_get_drvdata(pdev);
+	int irq = platform_get_irq(pdev, 0);
+
+	free_irq(irq, wm831x_on);
+	cancel_delayed_work_sync(&wm831x_on->work);
+
+	return 0;
+}
+
+static struct platform_driver wm831x_on_driver = {
+	.probe		= wm831x_on_probe,
+	.remove		= wm831x_on_remove,
+	.driver		= {
+		.name	= "wm831x-on",
+	},
+};
+module_platform_driver(wm831x_on_driver);
+
+MODULE_ALIAS("platform:wm831x-on");
+MODULE_DESCRIPTION("WM831x ON pin");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+
diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
new file mode 100644
index 0000000..0a9ad2c
--- /dev/null
+++ b/drivers/input/misc/xen-kbdfront.c
@@ -0,0 +1,407 @@
+/*
+ * Xen para-virtual input device
+ *
+ * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com>
+ * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
+ *
+ *  Based on linux/drivers/input/mouse/sermouse.c
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include <asm/xen/hypervisor.h>
+
+#include <xen/xen.h>
+#include <xen/events.h>
+#include <xen/page.h>
+#include <xen/grant_table.h>
+#include <xen/interface/grant_table.h>
+#include <xen/interface/io/fbif.h>
+#include <xen/interface/io/kbdif.h>
+#include <xen/xenbus.h>
+#include <xen/platform_pci.h>
+
+struct xenkbd_info {
+	struct input_dev *kbd;
+	struct input_dev *ptr;
+	struct xenkbd_page *page;
+	int gref;
+	int irq;
+	struct xenbus_device *xbdev;
+	char phys[32];
+};
+
+static int xenkbd_remove(struct xenbus_device *);
+static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *);
+static void xenkbd_disconnect_backend(struct xenkbd_info *);
+
+/*
+ * Note: if you need to send out events, see xenfb_do_update() for how
+ * to do that.
+ */
+
+static irqreturn_t input_handler(int rq, void *dev_id)
+{
+	struct xenkbd_info *info = dev_id;
+	struct xenkbd_page *page = info->page;
+	__u32 cons, prod;
+
+	prod = page->in_prod;
+	if (prod == page->in_cons)
+		return IRQ_HANDLED;
+	rmb();			/* ensure we see ring contents up to prod */
+	for (cons = page->in_cons; cons != prod; cons++) {
+		union xenkbd_in_event *event;
+		struct input_dev *dev;
+		event = &XENKBD_IN_RING_REF(page, cons);
+
+		dev = info->ptr;
+		switch (event->type) {
+		case XENKBD_TYPE_MOTION:
+			input_report_rel(dev, REL_X, event->motion.rel_x);
+			input_report_rel(dev, REL_Y, event->motion.rel_y);
+			if (event->motion.rel_z)
+				input_report_rel(dev, REL_WHEEL,
+						 -event->motion.rel_z);
+			break;
+		case XENKBD_TYPE_KEY:
+			dev = NULL;
+			if (test_bit(event->key.keycode, info->kbd->keybit))
+				dev = info->kbd;
+			if (test_bit(event->key.keycode, info->ptr->keybit))
+				dev = info->ptr;
+			if (dev)
+				input_report_key(dev, event->key.keycode,
+						 event->key.pressed);
+			else
+				pr_warning("unhandled keycode 0x%x\n",
+					   event->key.keycode);
+			break;
+		case XENKBD_TYPE_POS:
+			input_report_abs(dev, ABS_X, event->pos.abs_x);
+			input_report_abs(dev, ABS_Y, event->pos.abs_y);
+			if (event->pos.rel_z)
+				input_report_rel(dev, REL_WHEEL,
+						 -event->pos.rel_z);
+			break;
+		}
+		if (dev)
+			input_sync(dev);
+	}
+	mb();			/* ensure we got ring contents */
+	page->in_cons = cons;
+	notify_remote_via_irq(info->irq);
+
+	return IRQ_HANDLED;
+}
+
+static int xenkbd_probe(struct xenbus_device *dev,
+				  const struct xenbus_device_id *id)
+{
+	int ret, i, abs;
+	struct xenkbd_info *info;
+	struct input_dev *kbd, *ptr;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
+		return -ENOMEM;
+	}
+	dev_set_drvdata(&dev->dev, info);
+	info->xbdev = dev;
+	info->irq = -1;
+	info->gref = -1;
+	snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename);
+
+	info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
+	if (!info->page)
+		goto error_nomem;
+
+	if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0)
+		abs = 0;
+	if (abs) {
+		ret = xenbus_printf(XBT_NIL, dev->nodename,
+				    "request-abs-pointer", "1");
+		if (ret) {
+			pr_warning("xenkbd: can't request abs-pointer");
+			abs = 0;
+		}
+	}
+
+	/* keyboard */
+	kbd = input_allocate_device();
+	if (!kbd)
+		goto error_nomem;
+	kbd->name = "Xen Virtual Keyboard";
+	kbd->phys = info->phys;
+	kbd->id.bustype = BUS_PCI;
+	kbd->id.vendor = 0x5853;
+	kbd->id.product = 0xffff;
+
+	__set_bit(EV_KEY, kbd->evbit);
+	for (i = KEY_ESC; i < KEY_UNKNOWN; i++)
+		__set_bit(i, kbd->keybit);
+	for (i = KEY_OK; i < KEY_MAX; i++)
+		__set_bit(i, kbd->keybit);
+
+	ret = input_register_device(kbd);
+	if (ret) {
+		input_free_device(kbd);
+		xenbus_dev_fatal(dev, ret, "input_register_device(kbd)");
+		goto error;
+	}
+	info->kbd = kbd;
+
+	/* pointing device */
+	ptr = input_allocate_device();
+	if (!ptr)
+		goto error_nomem;
+	ptr->name = "Xen Virtual Pointer";
+	ptr->phys = info->phys;
+	ptr->id.bustype = BUS_PCI;
+	ptr->id.vendor = 0x5853;
+	ptr->id.product = 0xfffe;
+
+	if (abs) {
+		__set_bit(EV_ABS, ptr->evbit);
+		input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0);
+		input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0);
+	} else {
+		input_set_capability(ptr, EV_REL, REL_X);
+		input_set_capability(ptr, EV_REL, REL_Y);
+	}
+	input_set_capability(ptr, EV_REL, REL_WHEEL);
+
+	__set_bit(EV_KEY, ptr->evbit);
+	for (i = BTN_LEFT; i <= BTN_TASK; i++)
+		__set_bit(i, ptr->keybit);
+
+	ret = input_register_device(ptr);
+	if (ret) {
+		input_free_device(ptr);
+		xenbus_dev_fatal(dev, ret, "input_register_device(ptr)");
+		goto error;
+	}
+	info->ptr = ptr;
+
+	ret = xenkbd_connect_backend(dev, info);
+	if (ret < 0)
+		goto error;
+
+	return 0;
+
+ error_nomem:
+	ret = -ENOMEM;
+	xenbus_dev_fatal(dev, ret, "allocating device memory");
+ error:
+	xenkbd_remove(dev);
+	return ret;
+}
+
+static int xenkbd_resume(struct xenbus_device *dev)
+{
+	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
+
+	xenkbd_disconnect_backend(info);
+	memset(info->page, 0, PAGE_SIZE);
+	return xenkbd_connect_backend(dev, info);
+}
+
+static int xenkbd_remove(struct xenbus_device *dev)
+{
+	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
+
+	xenkbd_disconnect_backend(info);
+	if (info->kbd)
+		input_unregister_device(info->kbd);
+	if (info->ptr)
+		input_unregister_device(info->ptr);
+	free_page((unsigned long)info->page);
+	kfree(info);
+	return 0;
+}
+
+static int xenkbd_connect_backend(struct xenbus_device *dev,
+				  struct xenkbd_info *info)
+{
+	int ret, evtchn;
+	struct xenbus_transaction xbt;
+
+	ret = gnttab_grant_foreign_access(dev->otherend_id,
+	                                  virt_to_gfn(info->page), 0);
+	if (ret < 0)
+		return ret;
+	info->gref = ret;
+
+	ret = xenbus_alloc_evtchn(dev, &evtchn);
+	if (ret)
+		goto error_grant;
+	ret = bind_evtchn_to_irqhandler(evtchn, input_handler,
+					0, dev->devicetype, info);
+	if (ret < 0) {
+		xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
+		goto error_evtchan;
+	}
+	info->irq = ret;
+
+ again:
+	ret = xenbus_transaction_start(&xbt);
+	if (ret) {
+		xenbus_dev_fatal(dev, ret, "starting transaction");
+		goto error_irqh;
+	}
+	ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
+			    virt_to_gfn(info->page));
+	if (ret)
+		goto error_xenbus;
+	ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref);
+	if (ret)
+		goto error_xenbus;
+	ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
+			    evtchn);
+	if (ret)
+		goto error_xenbus;
+	ret = xenbus_transaction_end(xbt, 0);
+	if (ret) {
+		if (ret == -EAGAIN)
+			goto again;
+		xenbus_dev_fatal(dev, ret, "completing transaction");
+		goto error_irqh;
+	}
+
+	xenbus_switch_state(dev, XenbusStateInitialised);
+	return 0;
+
+ error_xenbus:
+	xenbus_transaction_end(xbt, 1);
+	xenbus_dev_fatal(dev, ret, "writing xenstore");
+ error_irqh:
+	unbind_from_irqhandler(info->irq, info);
+	info->irq = -1;
+ error_evtchan:
+	xenbus_free_evtchn(dev, evtchn);
+ error_grant:
+	gnttab_end_foreign_access(info->gref, 0, 0UL);
+	info->gref = -1;
+	return ret;
+}
+
+static void xenkbd_disconnect_backend(struct xenkbd_info *info)
+{
+	if (info->irq >= 0)
+		unbind_from_irqhandler(info->irq, info);
+	info->irq = -1;
+	if (info->gref >= 0)
+		gnttab_end_foreign_access(info->gref, 0, 0UL);
+	info->gref = -1;
+}
+
+static void xenkbd_backend_changed(struct xenbus_device *dev,
+				   enum xenbus_state backend_state)
+{
+	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
+	int ret, val;
+
+	switch (backend_state) {
+	case XenbusStateInitialising:
+	case XenbusStateInitialised:
+	case XenbusStateReconfiguring:
+	case XenbusStateReconfigured:
+	case XenbusStateUnknown:
+		break;
+
+	case XenbusStateInitWait:
+InitWait:
+		ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+				   "feature-abs-pointer", "%d", &val);
+		if (ret < 0)
+			val = 0;
+		if (val) {
+			ret = xenbus_printf(XBT_NIL, info->xbdev->nodename,
+					    "request-abs-pointer", "1");
+			if (ret)
+				pr_warning("xenkbd: can't request abs-pointer");
+		}
+
+		xenbus_switch_state(dev, XenbusStateConnected);
+		break;
+
+	case XenbusStateConnected:
+		/*
+		 * Work around xenbus race condition: If backend goes
+		 * through InitWait to Connected fast enough, we can
+		 * get Connected twice here.
+		 */
+		if (dev->state != XenbusStateConnected)
+			goto InitWait; /* no InitWait seen yet, fudge it */
+
+		/* Set input abs params to match backend screen res */
+		if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+				 "width", "%d", &val) > 0)
+			input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0);
+
+		if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+				 "height", "%d", &val) > 0)
+			input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0);
+
+		break;
+
+	case XenbusStateClosed:
+		if (dev->state == XenbusStateClosed)
+			break;
+		/* Missed the backend's CLOSING state -- fallthrough */
+	case XenbusStateClosing:
+		xenbus_frontend_closed(dev);
+		break;
+	}
+}
+
+static const struct xenbus_device_id xenkbd_ids[] = {
+	{ "vkbd" },
+	{ "" }
+};
+
+static struct xenbus_driver xenkbd_driver = {
+	.ids = xenkbd_ids,
+	.probe = xenkbd_probe,
+	.remove = xenkbd_remove,
+	.resume = xenkbd_resume,
+	.otherend_changed = xenkbd_backend_changed,
+};
+
+static int __init xenkbd_init(void)
+{
+	if (!xen_domain())
+		return -ENODEV;
+
+	/* Nothing to do if running in dom0. */
+	if (xen_initial_domain())
+		return -ENODEV;
+
+	if (!xen_has_pv_devices())
+		return -ENODEV;
+
+	return xenbus_register_frontend(&xenkbd_driver);
+}
+
+static void __exit xenkbd_cleanup(void)
+{
+	xenbus_unregister_driver(&xenkbd_driver);
+}
+
+module_init(xenkbd_init);
+module_exit(xenkbd_cleanup);
+
+MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("xen:vkbd");
diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c
new file mode 100644
index 0000000..6e7ff95
--- /dev/null
+++ b/drivers/input/misc/yealink.c
@@ -0,0 +1,1011 @@
+/*
+ * drivers/usb/input/yealink.c
+ *
+ * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * Description:
+ *   Driver for the USB-P1K voip usb phone.
+ *   This device is produced by Yealink Network Technology Co Ltd
+ *   but may be branded under several names:
+ *	- Yealink usb-p1k
+ *	- Tiptel 115
+ *	- ...
+ *
+ * This driver is based on:
+ *   - the usbb2k-api	http://savannah.nongnu.org/projects/usbb2k-api/
+ *   - information from	http://memeteau.free.fr/usbb2k
+ *   - the xpad-driver	drivers/input/joystick/xpad.c
+ *
+ * Thanks to:
+ *   - Olivier Vandorpe, for providing the usbb2k-api.
+ *   - Martin Diehl, for spotting my memory allocation bug.
+ *
+ * History:
+ *   20050527 henk	First version, functional keyboard. Keyboard events
+ *			will pop-up on the ../input/eventX bus.
+ *   20050531 henk	Added led, LCD, dialtone and sysfs interface.
+ *   20050610 henk	Cleanups, make it ready for public consumption.
+ *   20050630 henk	Cleanups, fixes in response to comments.
+ *   20050701 henk	sysfs write serialisation, fix potential unload races
+ *   20050801 henk	Added ringtone, restructure USB
+ *   20050816 henk	Merge 2.6.13-rc6
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/rwsem.h>
+#include <linux/usb/input.h>
+#include <linux/map_to_7segment.h>
+
+#include "yealink.h"
+
+#define DRIVER_VERSION "yld-20051230"
+#define DRIVER_AUTHOR "Henk Vergonet"
+#define DRIVER_DESC "Yealink phone driver"
+
+#define YEALINK_POLLING_FREQUENCY	10	/* in [Hz] */
+
+struct yld_status {
+	u8	lcd[24];
+	u8	led;
+	u8	dialtone;
+	u8	ringtone;
+	u8	keynum;
+} __attribute__ ((packed));
+
+/*
+ * Register the LCD segment and icon map
+ */
+#define _LOC(k,l)	{ .a = (k), .m = (l) }
+#define _SEG(t, a, am, b, bm, c, cm, d, dm, e, em, f, fm, g, gm)	\
+	{ .type	= (t),							\
+	  .u = { .s = {	_LOC(a, am), _LOC(b, bm), _LOC(c, cm),		\
+		        _LOC(d, dm), _LOC(e, em), _LOC(g, gm),		\
+			_LOC(f, fm) } } }
+#define _PIC(t, h, hm, n)						\
+	{ .type	= (t),							\
+ 	  .u = { .p = { .name = (n), .a = (h), .m = (hm) } } }
+
+static const struct lcd_segment_map {
+	char	type;
+	union {
+		struct pictogram_map {
+			u8	a,m;
+			char	name[10];
+		}	p;
+		struct segment_map {
+			u8	a,m;
+		} s[7];
+	} u;
+} lcdMap[] = {
+#include "yealink.h"
+};
+
+struct yealink_dev {
+	struct input_dev *idev;		/* input device */
+	struct usb_device *udev;	/* usb device */
+	struct usb_interface *intf;	/* usb interface */
+
+	/* irq input channel */
+	struct yld_ctl_packet	*irq_data;
+	dma_addr_t		irq_dma;
+	struct urb		*urb_irq;
+
+	/* control output channel */
+	struct yld_ctl_packet	*ctl_data;
+	dma_addr_t		ctl_dma;
+	struct usb_ctrlrequest	*ctl_req;
+	struct urb		*urb_ctl;
+
+	char phys[64];			/* physical device path */
+
+	u8 lcdMap[ARRAY_SIZE(lcdMap)];	/* state of LCD, LED ... */
+	int key_code;			/* last reported key	 */
+
+	unsigned int shutdown:1;
+
+	int	stat_ix;
+	union {
+		struct yld_status s;
+		u8		  b[sizeof(struct yld_status)];
+	} master, copy;
+};
+
+
+/*******************************************************************************
+ * Yealink lcd interface
+ ******************************************************************************/
+
+/*
+ * Register a default 7 segment character set
+ */
+static SEG7_DEFAULT_MAP(map_seg7);
+
+ /* Display a char,
+  * char '\9' and '\n' are placeholders and do not overwrite the original text.
+  * A space will always hide an icon.
+  */
+static int setChar(struct yealink_dev *yld, int el, int chr)
+{
+	int i, a, m, val;
+
+	if (el >= ARRAY_SIZE(lcdMap))
+		return -EINVAL;
+
+	if (chr == '\t' || chr == '\n')
+	    return 0;
+
+	yld->lcdMap[el] = chr;
+
+	if (lcdMap[el].type == '.') {
+		a = lcdMap[el].u.p.a;
+		m = lcdMap[el].u.p.m;
+		if (chr != ' ')
+			yld->master.b[a] |= m;
+		else
+			yld->master.b[a] &= ~m;
+		return 0;
+	}
+
+	val = map_to_seg7(&map_seg7, chr);
+	for (i = 0; i < ARRAY_SIZE(lcdMap[0].u.s); i++) {
+		m = lcdMap[el].u.s[i].m;
+
+		if (m == 0)
+			continue;
+
+		a = lcdMap[el].u.s[i].a;
+		if (val & 1)
+			yld->master.b[a] |= m;
+		else
+			yld->master.b[a] &= ~m;
+		val = val >> 1;
+	}
+	return 0;
+};
+
+/*******************************************************************************
+ * Yealink key interface
+ ******************************************************************************/
+
+/* Map device buttons to internal key events.
+ *
+ * USB-P1K button layout:
+ *
+ *             up
+ *       IN           OUT
+ *            down
+ *
+ *     pickup   C    hangup
+ *       1      2      3
+ *       4      5      6
+ *       7      8      9
+ *       *      0      #
+ *
+ * The "up" and "down" keys, are symbolised by arrows on the button.
+ * The "pickup" and "hangup" keys are symbolised by a green and red phone
+ * on the button.
+ */
+static int map_p1k_to_key(int scancode)
+{
+	switch(scancode) {		/* phone key:	*/
+	case 0x23: return KEY_LEFT;	/*   IN		*/
+	case 0x33: return KEY_UP;	/*   up		*/
+	case 0x04: return KEY_RIGHT;	/*   OUT	*/
+	case 0x24: return KEY_DOWN;	/*   down	*/
+	case 0x03: return KEY_ENTER;	/*   pickup	*/
+	case 0x14: return KEY_BACKSPACE; /*  C		*/
+	case 0x13: return KEY_ESC;	/*   hangup	*/
+	case 0x00: return KEY_1;	/*   1		*/
+	case 0x01: return KEY_2;	/*   2 		*/
+	case 0x02: return KEY_3;	/*   3		*/
+	case 0x10: return KEY_4;	/*   4		*/
+	case 0x11: return KEY_5;	/*   5		*/
+	case 0x12: return KEY_6;	/*   6		*/
+	case 0x20: return KEY_7;	/*   7		*/
+	case 0x21: return KEY_8;	/*   8		*/
+	case 0x22: return KEY_9;	/*   9		*/
+	case 0x30: return KEY_KPASTERISK; /* *		*/
+	case 0x31: return KEY_0;	/*   0		*/
+	case 0x32: return KEY_LEFTSHIFT |
+			  KEY_3 << 8;	/*   #		*/
+	}
+	return -EINVAL;
+}
+
+/* Completes a request by converting the data into events for the
+ * input subsystem.
+ *
+ * The key parameter can be cascaded: key2 << 8 | key1
+ */
+static void report_key(struct yealink_dev *yld, int key)
+{
+	struct input_dev *idev = yld->idev;
+
+	if (yld->key_code >= 0) {
+		/* old key up */
+		input_report_key(idev, yld->key_code & 0xff, 0);
+		if (yld->key_code >> 8)
+			input_report_key(idev, yld->key_code >> 8, 0);
+	}
+
+	yld->key_code = key;
+	if (key >= 0) {
+		/* new valid key */
+		input_report_key(idev, key & 0xff, 1);
+		if (key >> 8)
+			input_report_key(idev, key >> 8, 1);
+	}
+	input_sync(idev);
+}
+
+/*******************************************************************************
+ * Yealink usb communication interface
+ ******************************************************************************/
+
+static int yealink_cmd(struct yealink_dev *yld, struct yld_ctl_packet *p)
+{
+	u8	*buf = (u8 *)p;
+	int	i;
+	u8	sum = 0;
+
+	for(i=0; i<USB_PKT_LEN-1; i++)
+		sum -= buf[i];
+	p->sum = sum;
+	return usb_control_msg(yld->udev,
+			usb_sndctrlpipe(yld->udev, 0),
+			USB_REQ_SET_CONFIGURATION,
+			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+			0x200, 3,
+			p, sizeof(*p),
+			USB_CTRL_SET_TIMEOUT);
+}
+
+static u8 default_ringtone[] = {
+	0xEF,			/* volume [0-255] */
+	0xFB, 0x1E, 0x00, 0x0C,	/* 1250 [hz], 12/100 [s] */
+	0xFC, 0x18, 0x00, 0x0C,	/* 1000 [hz], 12/100 [s] */
+	0xFB, 0x1E, 0x00, 0x0C,
+	0xFC, 0x18, 0x00, 0x0C,
+	0xFB, 0x1E, 0x00, 0x0C,
+	0xFC, 0x18, 0x00, 0x0C,
+	0xFB, 0x1E, 0x00, 0x0C,
+	0xFC, 0x18, 0x00, 0x0C,
+	0xFF, 0xFF, 0x01, 0x90,	/* silent, 400/100 [s] */
+	0x00, 0x00		/* end of sequence */
+};
+
+static int yealink_set_ringtone(struct yealink_dev *yld, u8 *buf, size_t size)
+{
+	struct yld_ctl_packet *p = yld->ctl_data;
+	int	ix, len;
+
+	if (size <= 0)
+		return -EINVAL;
+
+	/* Set the ringtone volume */
+	memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
+	yld->ctl_data->cmd	= CMD_RING_VOLUME;
+	yld->ctl_data->size	= 1;
+	yld->ctl_data->data[0]	= buf[0];
+	yealink_cmd(yld, p);
+
+	buf++;
+	size--;
+
+	p->cmd = CMD_RING_NOTE;
+	ix = 0;
+	while (size != ix) {
+		len = size - ix;
+		if (len > sizeof(p->data))
+			len = sizeof(p->data);
+		p->size	  = len;
+		p->offset = cpu_to_be16(ix);
+		memcpy(p->data, &buf[ix], len);
+		yealink_cmd(yld, p);
+		ix += len;
+	}
+	return 0;
+}
+
+/* keep stat_master & stat_copy in sync.
+ */
+static int yealink_do_idle_tasks(struct yealink_dev *yld)
+{
+	u8 val;
+	int i, ix, len;
+
+	ix = yld->stat_ix;
+
+	memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
+	yld->ctl_data->cmd  = CMD_KEYPRESS;
+	yld->ctl_data->size = 1;
+	yld->ctl_data->sum  = 0xff - CMD_KEYPRESS;
+
+	/* If state update pointer wraps do a KEYPRESS first. */
+	if (ix >= sizeof(yld->master)) {
+		yld->stat_ix = 0;
+		return 0;
+	}
+
+	/* find update candidates: copy != master */
+	do {
+		val = yld->master.b[ix];
+		if (val != yld->copy.b[ix])
+			goto send_update;
+	} while (++ix < sizeof(yld->master));
+
+	/* nothing todo, wait a bit and poll for a KEYPRESS */
+	yld->stat_ix = 0;
+	/* TODO how can we wait abit. ??
+	 * msleep_interruptible(1000 / YEALINK_POLLING_FREQUENCY);
+	 */
+	return 0;
+
+send_update:
+
+	/* Setup an appropriate update request */
+	yld->copy.b[ix] = val;
+	yld->ctl_data->data[0] = val;
+
+	switch(ix) {
+	case offsetof(struct yld_status, led):
+		yld->ctl_data->cmd	= CMD_LED;
+		yld->ctl_data->sum	= -1 - CMD_LED - val;
+		break;
+	case offsetof(struct yld_status, dialtone):
+		yld->ctl_data->cmd	= CMD_DIALTONE;
+		yld->ctl_data->sum	= -1 - CMD_DIALTONE - val;
+		break;
+	case offsetof(struct yld_status, ringtone):
+		yld->ctl_data->cmd	= CMD_RINGTONE;
+		yld->ctl_data->sum	= -1 - CMD_RINGTONE - val;
+		break;
+	case offsetof(struct yld_status, keynum):
+		val--;
+		val &= 0x1f;
+		yld->ctl_data->cmd	= CMD_SCANCODE;
+		yld->ctl_data->offset	= cpu_to_be16(val);
+		yld->ctl_data->data[0]	= 0;
+		yld->ctl_data->sum	= -1 - CMD_SCANCODE - val;
+		break;
+	default:
+		len = sizeof(yld->master.s.lcd) - ix;
+		if (len > sizeof(yld->ctl_data->data))
+			len = sizeof(yld->ctl_data->data);
+
+		/* Combine up to <len> consecutive LCD bytes in a singe request
+		 */
+		yld->ctl_data->cmd	= CMD_LCD;
+		yld->ctl_data->offset	= cpu_to_be16(ix);
+		yld->ctl_data->size	= len;
+		yld->ctl_data->sum	= -CMD_LCD - ix - val - len;
+		for(i=1; i<len; i++) {
+			ix++;
+			val = yld->master.b[ix];
+			yld->copy.b[ix]		= val;
+			yld->ctl_data->data[i]	= val;
+			yld->ctl_data->sum     -= val;
+		}
+	}
+	yld->stat_ix = ix + 1;
+	return 1;
+}
+
+/* Decide on how to handle responses
+ *
+ * The state transition diagram is somethhing like:
+ *
+ *          syncState<--+
+ *               |      |
+ *               |    idle
+ *              \|/     |
+ * init --ok--> waitForKey --ok--> getKey
+ *  ^               ^                |
+ *  |               +-------ok-------+
+ * error,start
+ *
+ */
+static void urb_irq_callback(struct urb *urb)
+{
+	struct yealink_dev *yld = urb->context;
+	int ret, status = urb->status;
+
+	if (status)
+		dev_err(&yld->intf->dev, "%s - urb status %d\n",
+			__func__, status);
+
+	switch (yld->irq_data->cmd) {
+	case CMD_KEYPRESS:
+
+		yld->master.s.keynum = yld->irq_data->data[0];
+		break;
+
+	case CMD_SCANCODE:
+		dev_dbg(&yld->intf->dev, "get scancode %x\n",
+			yld->irq_data->data[0]);
+
+		report_key(yld, map_p1k_to_key(yld->irq_data->data[0]));
+		break;
+
+	default:
+		dev_err(&yld->intf->dev, "unexpected response %x\n",
+			yld->irq_data->cmd);
+	}
+
+	yealink_do_idle_tasks(yld);
+
+	if (!yld->shutdown) {
+		ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
+		if (ret && ret != -EPERM)
+			dev_err(&yld->intf->dev,
+				"%s - usb_submit_urb failed %d\n",
+				__func__, ret);
+	}
+}
+
+static void urb_ctl_callback(struct urb *urb)
+{
+	struct yealink_dev *yld = urb->context;
+	int ret = 0, status = urb->status;
+
+	if (status)
+		dev_err(&yld->intf->dev, "%s - urb status %d\n",
+			__func__, status);
+
+	switch (yld->ctl_data->cmd) {
+	case CMD_KEYPRESS:
+	case CMD_SCANCODE:
+		/* ask for a response */
+		if (!yld->shutdown)
+			ret = usb_submit_urb(yld->urb_irq, GFP_ATOMIC);
+		break;
+	default:
+		/* send new command */
+		yealink_do_idle_tasks(yld);
+		if (!yld->shutdown)
+			ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
+		break;
+	}
+
+	if (ret && ret != -EPERM)
+		dev_err(&yld->intf->dev, "%s - usb_submit_urb failed %d\n",
+			__func__, ret);
+}
+
+/*******************************************************************************
+ * input event interface
+ ******************************************************************************/
+
+/* TODO should we issue a ringtone on a SND_BELL event?
+static int input_ev(struct input_dev *dev, unsigned int type,
+		unsigned int code, int value)
+{
+
+	if (type != EV_SND)
+		return -EINVAL;
+
+	switch (code) {
+	case SND_BELL:
+	case SND_TONE:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+*/
+
+static int input_open(struct input_dev *dev)
+{
+	struct yealink_dev *yld = input_get_drvdata(dev);
+	int i, ret;
+
+	dev_dbg(&yld->intf->dev, "%s\n", __func__);
+
+	/* force updates to device */
+	for (i = 0; i<sizeof(yld->master); i++)
+		yld->copy.b[i] = ~yld->master.b[i];
+	yld->key_code = -1;	/* no keys pressed */
+
+        yealink_set_ringtone(yld, default_ringtone, sizeof(default_ringtone));
+
+	/* issue INIT */
+	memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
+	yld->ctl_data->cmd	= CMD_INIT;
+	yld->ctl_data->size	= 10;
+	yld->ctl_data->sum	= 0x100-CMD_INIT-10;
+	if ((ret = usb_submit_urb(yld->urb_ctl, GFP_KERNEL)) != 0) {
+		dev_dbg(&yld->intf->dev,
+			"%s - usb_submit_urb failed with result %d\n",
+			__func__, ret);
+		return ret;
+	}
+	return 0;
+}
+
+static void input_close(struct input_dev *dev)
+{
+	struct yealink_dev *yld = input_get_drvdata(dev);
+
+	yld->shutdown = 1;
+	/*
+	 * Make sure the flag is seen by other CPUs before we start
+	 * killing URBs so new URBs won't be submitted
+	 */
+	smp_wmb();
+
+	usb_kill_urb(yld->urb_ctl);
+	usb_kill_urb(yld->urb_irq);
+
+	yld->shutdown = 0;
+	smp_wmb();
+}
+
+/*******************************************************************************
+ * sysfs interface
+ ******************************************************************************/
+
+static DECLARE_RWSEM(sysfs_rwsema);
+
+/* Interface to the 7-segments translation table aka. char set.
+ */
+static ssize_t show_map(struct device *dev, struct device_attribute *attr,
+				char *buf)
+{
+	memcpy(buf, &map_seg7, sizeof(map_seg7));
+	return sizeof(map_seg7);
+}
+
+static ssize_t store_map(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t cnt)
+{
+	if (cnt != sizeof(map_seg7))
+		return -EINVAL;
+	memcpy(&map_seg7, buf, sizeof(map_seg7));
+	return sizeof(map_seg7);
+}
+
+/* Interface to the LCD.
+ */
+
+/* Reading /sys/../lineX will return the format string with its settings:
+ *
+ * Example:
+ * cat ./line3
+ * 888888888888
+ * Linux Rocks!
+ */
+static ssize_t show_line(struct device *dev, char *buf, int a, int b)
+{
+	struct yealink_dev *yld;
+	int i;
+
+	down_read(&sysfs_rwsema);
+	yld = dev_get_drvdata(dev);
+	if (yld == NULL) {
+		up_read(&sysfs_rwsema);
+		return -ENODEV;
+	}
+
+	for (i = a; i < b; i++)
+		*buf++ = lcdMap[i].type;
+	*buf++ = '\n';
+	for (i = a; i < b; i++)
+		*buf++ = yld->lcdMap[i];
+	*buf++ = '\n';
+	*buf = 0;
+
+	up_read(&sysfs_rwsema);
+	return 3 + ((b - a) << 1);
+}
+
+static ssize_t show_line1(struct device *dev, struct device_attribute *attr,
+			char *buf)
+{
+	return show_line(dev, buf, LCD_LINE1_OFFSET, LCD_LINE2_OFFSET);
+}
+
+static ssize_t show_line2(struct device *dev, struct device_attribute *attr,
+			char *buf)
+{
+	return show_line(dev, buf, LCD_LINE2_OFFSET, LCD_LINE3_OFFSET);
+}
+
+static ssize_t show_line3(struct device *dev, struct device_attribute *attr,
+			char *buf)
+{
+	return show_line(dev, buf, LCD_LINE3_OFFSET, LCD_LINE4_OFFSET);
+}
+
+/* Writing to /sys/../lineX will set the coresponding LCD line.
+ * - Excess characters are ignored.
+ * - If less characters are written than allowed, the remaining digits are
+ *   unchanged.
+ * - The '\n' or '\t' char is a placeholder, it does not overwrite the
+ *   original content.
+ */
+static ssize_t store_line(struct device *dev, const char *buf, size_t count,
+		int el, size_t len)
+{
+	struct yealink_dev *yld;
+	int i;
+
+	down_write(&sysfs_rwsema);
+	yld = dev_get_drvdata(dev);
+	if (yld == NULL) {
+		up_write(&sysfs_rwsema);
+		return -ENODEV;
+	}
+
+	if (len > count)
+		len = count;
+	for (i = 0; i < len; i++)
+		setChar(yld, el++, buf[i]);
+
+	up_write(&sysfs_rwsema);
+	return count;
+}
+
+static ssize_t store_line1(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return store_line(dev, buf, count, LCD_LINE1_OFFSET, LCD_LINE1_SIZE);
+}
+
+static ssize_t store_line2(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return store_line(dev, buf, count, LCD_LINE2_OFFSET, LCD_LINE2_SIZE);
+}
+
+static ssize_t store_line3(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return store_line(dev, buf, count, LCD_LINE3_OFFSET, LCD_LINE3_SIZE);
+}
+
+/* Interface to visible and audible "icons", these include:
+ * pictures on the LCD, the LED, and the dialtone signal.
+ */
+
+/* Get a list of "switchable elements" with their current state. */
+static ssize_t get_icons(struct device *dev, struct device_attribute *attr,
+			char *buf)
+{
+	struct yealink_dev *yld;
+	int i, ret = 1;
+
+	down_read(&sysfs_rwsema);
+	yld = dev_get_drvdata(dev);
+	if (yld == NULL) {
+		up_read(&sysfs_rwsema);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
+		if (lcdMap[i].type != '.')
+			continue;
+		ret += sprintf(&buf[ret], "%s %s\n",
+				yld->lcdMap[i] == ' ' ? "  " : "on",
+				lcdMap[i].u.p.name);
+	}
+	up_read(&sysfs_rwsema);
+	return ret;
+}
+
+/* Change the visibility of a particular element. */
+static ssize_t set_icon(struct device *dev, const char *buf, size_t count,
+			int chr)
+{
+	struct yealink_dev *yld;
+	int i;
+
+	down_write(&sysfs_rwsema);
+	yld = dev_get_drvdata(dev);
+	if (yld == NULL) {
+		up_write(&sysfs_rwsema);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
+		if (lcdMap[i].type != '.')
+			continue;
+		if (strncmp(buf, lcdMap[i].u.p.name, count) == 0) {
+			setChar(yld, i, chr);
+			break;
+		}
+	}
+
+	up_write(&sysfs_rwsema);
+	return count;
+}
+
+static ssize_t show_icon(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	return set_icon(dev, buf, count, buf[0]);
+}
+
+static ssize_t hide_icon(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	return set_icon(dev, buf, count, ' ');
+}
+
+/* Upload a ringtone to the device.
+ */
+
+/* Stores raw ringtone data in the phone */
+static ssize_t store_ringtone(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct yealink_dev *yld;
+
+	down_write(&sysfs_rwsema);
+	yld = dev_get_drvdata(dev);
+	if (yld == NULL) {
+		up_write(&sysfs_rwsema);
+		return -ENODEV;
+	}
+
+	/* TODO locking with async usb control interface??? */
+	yealink_set_ringtone(yld, (char *)buf, count);
+	up_write(&sysfs_rwsema);
+	return count;
+}
+
+#define _M444	S_IRUGO
+#define _M664	S_IRUGO|S_IWUSR|S_IWGRP
+#define _M220	S_IWUSR|S_IWGRP
+
+static DEVICE_ATTR(map_seg7	, _M664, show_map	, store_map	);
+static DEVICE_ATTR(line1	, _M664, show_line1	, store_line1	);
+static DEVICE_ATTR(line2	, _M664, show_line2	, store_line2	);
+static DEVICE_ATTR(line3	, _M664, show_line3	, store_line3	);
+static DEVICE_ATTR(get_icons	, _M444, get_icons	, NULL		);
+static DEVICE_ATTR(show_icon	, _M220, NULL		, show_icon	);
+static DEVICE_ATTR(hide_icon	, _M220, NULL		, hide_icon	);
+static DEVICE_ATTR(ringtone	, _M220, NULL		, store_ringtone);
+
+static struct attribute *yld_attributes[] = {
+	&dev_attr_line1.attr,
+	&dev_attr_line2.attr,
+	&dev_attr_line3.attr,
+	&dev_attr_get_icons.attr,
+	&dev_attr_show_icon.attr,
+	&dev_attr_hide_icon.attr,
+	&dev_attr_map_seg7.attr,
+	&dev_attr_ringtone.attr,
+	NULL
+};
+
+static struct attribute_group yld_attr_group = {
+	.attrs = yld_attributes
+};
+
+/*******************************************************************************
+ * Linux interface and usb initialisation
+ ******************************************************************************/
+
+struct driver_info {
+	char *name;
+};
+
+static const struct driver_info info_P1K = {
+	.name	= "Yealink usb-p1k",
+};
+
+static const struct usb_device_id usb_table [] = {
+	{
+		.match_flags		= USB_DEVICE_ID_MATCH_DEVICE |
+						USB_DEVICE_ID_MATCH_INT_INFO,
+		.idVendor		= 0x6993,
+		.idProduct		= 0xb001,
+		.bInterfaceClass	= USB_CLASS_HID,
+		.bInterfaceSubClass	= 0,
+		.bInterfaceProtocol	= 0,
+		.driver_info		= (kernel_ulong_t)&info_P1K
+	},
+	{ }
+};
+
+static int usb_cleanup(struct yealink_dev *yld, int err)
+{
+	if (yld == NULL)
+		return err;
+
+        if (yld->idev) {
+		if (err)
+			input_free_device(yld->idev);
+		else
+			input_unregister_device(yld->idev);
+	}
+
+	usb_free_urb(yld->urb_irq);
+	usb_free_urb(yld->urb_ctl);
+
+	kfree(yld->ctl_req);
+	usb_free_coherent(yld->udev, USB_PKT_LEN, yld->ctl_data, yld->ctl_dma);
+	usb_free_coherent(yld->udev, USB_PKT_LEN, yld->irq_data, yld->irq_dma);
+
+	kfree(yld);
+	return err;
+}
+
+static void usb_disconnect(struct usb_interface *intf)
+{
+	struct yealink_dev *yld;
+
+	down_write(&sysfs_rwsema);
+	yld = usb_get_intfdata(intf);
+	sysfs_remove_group(&intf->dev.kobj, &yld_attr_group);
+	usb_set_intfdata(intf, NULL);
+	up_write(&sysfs_rwsema);
+
+	usb_cleanup(yld, 0);
+}
+
+static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev (intf);
+	struct driver_info *nfo = (struct driver_info *)id->driver_info;
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct yealink_dev *yld;
+	struct input_dev *input_dev;
+	int ret, pipe, i;
+
+	interface = intf->cur_altsetting;
+
+	if (interface->desc.bNumEndpoints < 1)
+		return -ENODEV;
+
+	endpoint = &interface->endpoint[0].desc;
+	if (!usb_endpoint_is_int_in(endpoint))
+		return -ENODEV;
+
+	yld = kzalloc(sizeof(struct yealink_dev), GFP_KERNEL);
+	if (!yld)
+		return -ENOMEM;
+
+	yld->udev = udev;
+	yld->intf = intf;
+
+	yld->idev = input_dev = input_allocate_device();
+	if (!input_dev)
+		return usb_cleanup(yld, -ENOMEM);
+
+	/* allocate usb buffers */
+	yld->irq_data = usb_alloc_coherent(udev, USB_PKT_LEN,
+					   GFP_ATOMIC, &yld->irq_dma);
+	if (yld->irq_data == NULL)
+		return usb_cleanup(yld, -ENOMEM);
+
+	yld->ctl_data = usb_alloc_coherent(udev, USB_PKT_LEN,
+					   GFP_ATOMIC, &yld->ctl_dma);
+	if (!yld->ctl_data)
+		return usb_cleanup(yld, -ENOMEM);
+
+	yld->ctl_req = kmalloc(sizeof(*(yld->ctl_req)), GFP_KERNEL);
+	if (yld->ctl_req == NULL)
+		return usb_cleanup(yld, -ENOMEM);
+
+	/* allocate urb structures */
+	yld->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
+        if (yld->urb_irq == NULL)
+		return usb_cleanup(yld, -ENOMEM);
+
+	yld->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
+        if (yld->urb_ctl == NULL)
+		return usb_cleanup(yld, -ENOMEM);
+
+	/* get a handle to the interrupt data pipe */
+	pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+	ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+	if (ret != USB_PKT_LEN)
+		dev_err(&intf->dev, "invalid payload size %d, expected %zd\n",
+			ret, USB_PKT_LEN);
+
+	/* initialise irq urb */
+	usb_fill_int_urb(yld->urb_irq, udev, pipe, yld->irq_data,
+			USB_PKT_LEN,
+			urb_irq_callback,
+			yld, endpoint->bInterval);
+	yld->urb_irq->transfer_dma = yld->irq_dma;
+	yld->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	yld->urb_irq->dev = udev;
+
+	/* initialise ctl urb */
+	yld->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+				      USB_DIR_OUT;
+	yld->ctl_req->bRequest	= USB_REQ_SET_CONFIGURATION;
+	yld->ctl_req->wValue	= cpu_to_le16(0x200);
+	yld->ctl_req->wIndex	= cpu_to_le16(interface->desc.bInterfaceNumber);
+	yld->ctl_req->wLength	= cpu_to_le16(USB_PKT_LEN);
+
+	usb_fill_control_urb(yld->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
+			(void *)yld->ctl_req, yld->ctl_data, USB_PKT_LEN,
+			urb_ctl_callback, yld);
+	yld->urb_ctl->transfer_dma	= yld->ctl_dma;
+	yld->urb_ctl->transfer_flags	|= URB_NO_TRANSFER_DMA_MAP;
+	yld->urb_ctl->dev = udev;
+
+	/* find out the physical bus location */
+	usb_make_path(udev, yld->phys, sizeof(yld->phys));
+	strlcat(yld->phys,  "/input0", sizeof(yld->phys));
+
+	/* register settings for the input device */
+	input_dev->name = nfo->name;
+	input_dev->phys = yld->phys;
+	usb_to_input_id(udev, &input_dev->id);
+	input_dev->dev.parent = &intf->dev;
+
+	input_set_drvdata(input_dev, yld);
+
+	input_dev->open = input_open;
+	input_dev->close = input_close;
+	/* input_dev->event = input_ev;	TODO */
+
+	/* register available key events */
+	input_dev->evbit[0] = BIT_MASK(EV_KEY);
+	for (i = 0; i < 256; i++) {
+		int k = map_p1k_to_key(i);
+		if (k >= 0) {
+			set_bit(k & 0xff, input_dev->keybit);
+			if (k >> 8)
+				set_bit(k >> 8, input_dev->keybit);
+		}
+	}
+
+	ret = input_register_device(yld->idev);
+	if (ret)
+		return usb_cleanup(yld, ret);
+
+	usb_set_intfdata(intf, yld);
+
+	/* clear visible elements */
+	for (i = 0; i < ARRAY_SIZE(lcdMap); i++)
+		setChar(yld, i, ' ');
+
+	/* display driver version on LCD line 3 */
+	store_line3(&intf->dev, NULL,
+			DRIVER_VERSION, sizeof(DRIVER_VERSION));
+
+	/* Register sysfs hooks (don't care about failure) */
+	ret = sysfs_create_group(&intf->dev.kobj, &yld_attr_group);
+	return 0;
+}
+
+static struct usb_driver yealink_driver = {
+	.name		= "yealink",
+	.probe		= usb_probe,
+	.disconnect	= usb_disconnect,
+	.id_table	= usb_table,
+};
+
+module_usb_driver(yealink_driver);
+
+MODULE_DEVICE_TABLE (usb, usb_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/yealink.h b/drivers/input/misc/yealink.h
new file mode 100644
index 0000000..1e0f523
--- /dev/null
+++ b/drivers/input/misc/yealink.h
@@ -0,0 +1,220 @@
+/*
+ * drivers/usb/input/yealink.h
+ *
+ * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
+ *
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef INPUT_YEALINK_H
+#define INPUT_YEALINK_H
+
+/* Using the control channel on interface 3 various aspects of the phone
+ * can be controlled like LCD, LED, dialtone and the ringtone.
+ */
+
+struct yld_ctl_packet {
+	u8	cmd;		/* command code, see below */
+	u8	size;		/* 1-11, size of used data bytes. */
+	u16	offset;		/* internal packet offset */
+	u8	data[11];
+	s8	sum;		/* negative sum of 15 preceding bytes */
+} __attribute__ ((packed));
+
+#define USB_PKT_LEN	sizeof(struct yld_ctl_packet)
+
+/* The following yld_ctl_packet's are available: */
+
+/* Init registers
+ *
+ * cmd		0x8e
+ * size		10
+ * offset	0
+ * data		0,0,0,0....
+ */
+#define CMD_INIT		0x8e
+
+/* Request key scan
+ *
+ * cmd		0x80
+ * size		1
+ * offset	0
+ * data[0]	on return returns the key number, if it changes there's a new
+ * 		key pressed.
+ */
+#define CMD_KEYPRESS		0x80
+
+/* Request scancode
+ *
+ * cmd		0x81
+ * size		1
+ * offset	key number [0-1f]
+ * data[0]	on return returns the scancode
+ */
+#define CMD_SCANCODE		0x81
+
+/* Set LCD
+ *
+ * cmd		0x04
+ * size		1-11
+ * offset	0-23
+ * data		segment bits
+ */
+#define CMD_LCD			0x04
+
+/* Set led
+ *
+ * cmd		0x05
+ * size		1
+ * offset	0
+ * data[0]	0 OFF / 1 ON
+ */
+#define CMD_LED			0x05
+
+/* Set ringtone volume
+ *
+ * cmd		0x11
+ * size		1
+ * offset	0
+ * data[0]	0-0xff  volume
+ */
+#define CMD_RING_VOLUME		0x11
+
+/* Set ringtone notes
+ *
+ * cmd		0x02
+ * size		1-11
+ * offset	0->
+ * data		binary representation LE16(-freq), LE16(duration) ....
+ */
+#define CMD_RING_NOTE		0x02
+
+/* Sound ringtone via the speaker on the back
+ *
+ * cmd		0x03
+ * size		1
+ * offset	0
+ * data[0]	0 OFF / 0x24 ON
+ */
+#define CMD_RINGTONE		0x03
+
+/* Sound dial tone via the ear speaker
+ *
+ * cmd		0x09
+ * size		1
+ * offset	0
+ * data[0]	0 OFF / 1 ON
+ */
+#define CMD_DIALTONE		0x09
+
+#endif /* INPUT_YEALINK_H */
+
+
+#if defined(_SEG) && defined(_PIC)
+/* This table maps the LCD segments onto individual bit positions in the
+ * yld_status struct.
+ */
+
+/* LCD, each segment must be driven separately.
+ *
+ * Layout:
+ *
+ *   |[]   [][]   [][]   [][]   in   |[][]
+ *   |[] M [][] D [][] : [][]   out  |[][]
+ *                             store
+ *
+ *    NEW REP         SU MO TU WE TH FR SA
+ *
+ *    [] [] [] [] [] [] [] [] [] [] [] []
+ *    [] [] [] [] [] [] [] [] [] [] [] []
+ */
+
+/* Line 1
+ *	Format		: 18.e8.M8.88...188
+ *	Icon names	: M D : IN OUT STORE
+ */
+#define LCD_LINE1_OFFSET	0
+#define LCD_LINE1_SIZE		17
+
+/* Note: first g then f =>			       !      !      */
+/* _SEG(    type    a      b      c      d      e      g      f   )  */
+	_SEG('1',  0,0 , 22,2 , 22,2 ,  0,0 ,  0,0 ,  0,0 ,  0,0	),
+	_SEG('8', 20,1 , 20,2 , 20,4 , 20,8 , 21,4 , 21,2 , 21,1	),
+	_PIC('.', 22,1 , "M"						),
+	_SEG('e', 18,1 , 18,2 , 18,4 , 18,1 , 19,2 , 18,1 , 19,1	),
+	_SEG('8', 16,1 , 16,2 , 16,4 , 16,8 , 17,4 , 17,2 , 17,1	),
+	_PIC('.', 15,8 , "D"						),
+	_SEG('M', 14,1 , 14,2 , 14,4 , 14,1 , 15,4 , 15,2 , 15,1	),
+	_SEG('8', 12,1 , 12,2 , 12,4 , 12,8 , 13,4 , 13,2 , 13,1	),
+	_PIC('.', 11,8 , ":"						),
+	_SEG('8', 10,1 , 10,2 , 10,4 , 10,8 , 11,4 , 11,2 , 11,1	),
+	_SEG('8',  8,1 ,  8,2 ,  8,4 ,  8,8 ,  9,4 ,  9,2 ,  9,1	),
+	_PIC('.',  7,1 , "IN"						),
+	_PIC('.',  7,2 , "OUT"						),
+	_PIC('.',  7,4 , "STORE"					),
+	_SEG('1',  0,0 ,  5,1 ,  5,1 ,  0,0 ,  0,0 ,  0,0 ,  0,0	),
+	_SEG('8',  4,1 ,  4,2 ,  4,4 ,  4,8 ,  5,8 ,  5,4 ,  5,2	),
+	_SEG('8',  2,1 ,  2,2 ,  2,4 ,  2,8 ,  3,4 ,  3,2 ,  3,1	),
+
+/* Line 2
+ *	Format		: .........
+ *	Pict. name	: NEW REP SU MO TU WE TH FR SA
+ */
+#define LCD_LINE2_OFFSET	LCD_LINE1_OFFSET + LCD_LINE1_SIZE
+#define LCD_LINE2_SIZE		9
+
+	_PIC('.', 23,2 , "NEW"	),
+	_PIC('.', 23,4 , "REP"	),
+	_PIC('.',  1,8 , "SU"	),
+	_PIC('.',  1,4 , "MO"	),
+	_PIC('.',  1,2 , "TU"	),
+	_PIC('.',  1,1 , "WE"	),
+	_PIC('.',  0,1 , "TH"	),
+	_PIC('.',  0,2 , "FR"	),
+	_PIC('.',  0,4 , "SA"	),
+
+/* Line 3
+ *	Format		: 888888888888
+ */
+#define LCD_LINE3_OFFSET	LCD_LINE2_OFFSET + LCD_LINE2_SIZE
+#define LCD_LINE3_SIZE		12
+
+	_SEG('8', 22,16, 22,32, 22,64, 22,128, 23,128, 23,64, 23,32  ),
+	_SEG('8', 20,16, 20,32, 20,64, 20,128, 21,128, 21,64, 21,32  ),
+	_SEG('8', 18,16, 18,32, 18,64, 18,128, 19,128, 19,64, 19,32  ),
+	_SEG('8', 16,16, 16,32, 16,64, 16,128, 17,128, 17,64, 17,32  ),
+	_SEG('8', 14,16, 14,32, 14,64, 14,128, 15,128, 15,64, 15,32  ),
+	_SEG('8', 12,16, 12,32, 12,64, 12,128, 13,128, 13,64, 13,32  ),
+	_SEG('8', 10,16, 10,32, 10,64, 10,128, 11,128, 11,64, 11,32  ),
+	_SEG('8',  8,16,  8,32,  8,64,  8,128,  9,128,  9,64,  9,32  ),
+	_SEG('8',  6,16,  6,32,  6,64,  6,128,  7,128,  7,64,  7,32  ),
+	_SEG('8',  4,16,  4,32,  4,64,  4,128,  5,128,  5,64,  5,32  ),
+	_SEG('8',  2,16,  2,32,  2,64,  2,128,  3,128,  3,64,  3,32  ),
+	_SEG('8',  0,16,  0,32,  0,64,  0,128,  1,128,  1,64,  1,32  ),
+
+/* Line 4
+ *
+ * The LED, DIALTONE and RINGTONE are implemented as icons and use the same
+ * sysfs interface.
+ */
+#define LCD_LINE4_OFFSET	LCD_LINE3_OFFSET + LCD_LINE3_SIZE
+
+	_PIC('.', offsetof(struct yld_status, led)	, 0x01, "LED" ),
+	_PIC('.', offsetof(struct yld_status, dialtone) , 0x01, "DIALTONE" ),
+	_PIC('.', offsetof(struct yld_status, ringtone) , 0x24, "RINGTONE" ),
+
+#undef _SEG
+#undef _PIC
+#endif /* _SEG && _PIC */
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
new file mode 100644
index 0000000..17f97e5
--- /dev/null
+++ b/drivers/input/mouse/Kconfig
@@ -0,0 +1,422 @@
+#
+# Mouse driver configuration
+#
+menuconfig INPUT_MOUSE
+	bool "Mice"
+	default y
+	help
+	  Say Y here, and a list of supported mice will be displayed.
+	  This option doesn't affect the kernel.
+
+	  If unsure, say Y.
+
+if INPUT_MOUSE
+
+config MOUSE_PS2
+	tristate "PS/2 mouse"
+	default y
+	select SERIO
+	select SERIO_LIBPS2
+	select SERIO_I8042 if ARCH_MIGHT_HAVE_PC_SERIO
+	select SERIO_GSCPS2 if GSC
+	help
+	  Say Y here if you have a PS/2 mouse connected to your system. This
+	  includes the standard 2 or 3-button PS/2 mouse, as well as PS/2
+	  mice with wheels and extra buttons, Microsoft, Logitech or Genius
+	  compatible.
+
+	  Synaptics, ALPS or Elantech TouchPad users might be interested
+	  in a specialized Xorg/XFree86 driver at:
+		<http://w1.894.telia.com/~u89404340/touchpad/index.html>
+	  and a new version of GPM at:
+		<http://www.geocities.com/dt_or/gpm/gpm.html>
+		<http://xorg.freedesktop.org/archive/individual/driver/>
+	  to take advantage of the advanced features of the touchpad.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called psmouse.
+
+config MOUSE_PS2_ALPS
+	bool "ALPS PS/2 mouse protocol extension" if EXPERT
+	default y
+	depends on MOUSE_PS2
+	help
+	  Say Y here if you have an ALPS PS/2 touchpad connected to
+	  your system.
+
+	  If unsure, say Y.
+
+config MOUSE_PS2_LOGIPS2PP
+	bool "Logitech PS/2++ mouse protocol extension" if EXPERT
+	default y
+	depends on MOUSE_PS2
+	help
+	  Say Y here if you have a Logitech PS/2++ mouse connected to
+	  your system.
+
+	  If unsure, say Y.
+
+config MOUSE_PS2_SYNAPTICS
+	bool "Synaptics PS/2 mouse protocol extension" if EXPERT
+	default y
+	depends on MOUSE_PS2
+	help
+	  Say Y here if you have a Synaptics PS/2 TouchPad connected to
+	  your system.
+
+	  If unsure, say Y.
+
+config MOUSE_PS2_CYPRESS
+       bool "Cypress PS/2 mouse protocol extension" if EXPERT
+       default y
+       depends on MOUSE_PS2
+       help
+         Say Y here if you have a Cypress PS/2 Trackpad connected to
+         your system.
+
+         If unsure, say Y.
+
+config MOUSE_PS2_LIFEBOOK
+	bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT
+	default y
+	depends on MOUSE_PS2 && X86 && DMI
+	help
+	  Say Y here if you have a Fujitsu B-series Lifebook PS/2
+	  TouchScreen connected to your system.
+
+	  If unsure, say Y.
+
+config MOUSE_PS2_TRACKPOINT
+	bool "IBM Trackpoint PS/2 mouse protocol extension" if EXPERT
+	default y
+	depends on MOUSE_PS2
+	help
+	  Say Y here if you have an IBM Trackpoint PS/2 mouse connected
+	  to your system.
+
+	  If unsure, say Y.
+
+config MOUSE_PS2_ELANTECH
+	bool "Elantech PS/2 protocol extension"
+	depends on MOUSE_PS2
+	help
+	  Say Y here if you have an Elantech PS/2 touchpad connected
+	  to your system.
+
+	  This driver exposes some configuration registers via sysfs
+	  entries. For further information,
+	  see <file:Documentation/input/elantech.txt>.
+
+	  If unsure, say N.
+
+config MOUSE_PS2_SENTELIC
+	bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
+	depends on MOUSE_PS2
+	help
+	  Say Y here if you have a laptop (such as MSI WIND Netbook)
+	  with Sentelic Finger Sensing Pad touchpad.
+
+	  If unsure, say N.
+
+config MOUSE_PS2_TOUCHKIT
+	bool "eGalax TouchKit PS/2 protocol extension"
+	depends on MOUSE_PS2
+	help
+	  Say Y here if you have an eGalax TouchKit PS/2 touchscreen
+	  connected to your system.
+
+	  If unsure, say N.
+
+config MOUSE_PS2_OLPC
+	bool "OLPC PS/2 mouse protocol extension"
+	depends on MOUSE_PS2 && OLPC
+	help
+	  Say Y here if you have an OLPC XO-1 laptop (with built-in
+	  PS/2 touchpad/tablet device).  The manufacturer calls the
+	  touchpad an HGPK.
+
+	  If unsure, say N.
+
+config MOUSE_PS2_FOCALTECH
+	bool "FocalTech PS/2 mouse protocol extension" if EXPERT
+	default y
+	depends on MOUSE_PS2
+	help
+	  Say Y here if you have a FocalTech PS/2 TouchPad connected to
+	  your system.
+
+	  If unsure, say Y.
+
+config MOUSE_PS2_VMMOUSE
+	bool "Virtual mouse (vmmouse)"
+	depends on MOUSE_PS2 && X86 && HYPERVISOR_GUEST
+	help
+	  Say Y here if you are running under control of VMware hypervisor
+	  (ESXi, Workstation or Fusion). Also make sure that when you enable
+	  this option, you remove the xf86-input-vmmouse user-space driver
+	  or upgrade it to at least xf86-input-vmmouse 13.1.0, which doesn't
+	  load in the presence of an in-kernel vmmouse driver.
+
+	  If unsure, say N.
+
+config MOUSE_SERIAL
+	tristate "Serial mouse"
+	select SERIO
+	help
+	  Say Y here if you have a serial (RS-232, COM port) mouse connected
+	  to your system. This includes Sun, MouseSystems, Microsoft,
+	  Logitech and all other compatible serial mice.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sermouse.
+
+config MOUSE_APPLETOUCH
+	tristate "Apple USB Touchpad support"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you want to use an Apple USB Touchpad.
+
+	  These are the touchpads that can be found on post-February 2005
+	  Apple Powerbooks (prior models have a Synaptics touchpad connected
+	  to the ADB bus).
+
+	  This driver provides a basic mouse driver but can be interfaced
+	  with the synaptics X11 driver to provide acceleration and
+	  scrolling in X11.
+
+	  For further information, see
+	  <file:Documentation/input/appletouch.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called appletouch.
+
+config MOUSE_BCM5974
+	tristate "Apple USB BCM5974 Multitouch trackpad support"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you have an Apple USB BCM5974 Multitouch
+	  trackpad.
+
+	  The BCM5974 is the multitouch trackpad found in the Macbook
+	  Air (JAN2008) and Macbook Pro Penryn (FEB2008) laptops.
+
+	  It is also found in the IPhone (2007) and Ipod Touch (2008).
+
+	  This driver provides multitouch functionality together with
+	  the synaptics X11 driver.
+
+	  The interface is currently identical to the appletouch interface,
+	  for further information, see
+	  <file:Documentation/input/appletouch.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bcm5974.
+
+config MOUSE_CYAPA
+	tristate "Cypress APA I2C Trackpad support"
+	depends on I2C
+	select CRC_ITU_T
+	help
+	  This driver adds support for Cypress All Points Addressable (APA)
+	  I2C Trackpads, including the ones used in 2012 Samsung Chromebooks.
+
+	  Say Y here if you have a Cypress APA I2C Trackpad.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called cyapa.
+
+config MOUSE_ELAN_I2C
+	tristate "ELAN I2C Touchpad support"
+	depends on I2C
+	help
+	  This driver adds support for Elan I2C/SMbus Trackpads.
+
+	  Say Y here if you have a ELAN I2C/SMbus Touchpad.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called elan_i2c.
+
+config MOUSE_ELAN_I2C_I2C
+	bool "Enable I2C support"
+	depends on MOUSE_ELAN_I2C
+	default y
+	help
+	   Say Y here if Elan Touchpad in your system is connected to
+	   a standard I2C controller.
+
+	   If unsure, say Y.
+
+config MOUSE_ELAN_I2C_SMBUS
+	bool "Enable SMbus support"
+	depends on MOUSE_ELAN_I2C
+	help
+	   Say Y here if Elan Touchpad in your system is connected to
+	   a SMbus adapter.
+
+	   If unsure, say Y.
+
+config MOUSE_INPORT
+	tristate "InPort/MS/ATIXL busmouse"
+	depends on ISA
+	help
+	  Say Y here if you have an InPort, Microsoft or ATI XL busmouse.
+	  They are rather rare these days.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called inport.
+
+config MOUSE_ATIXL
+	bool "ATI XL variant"
+	depends on MOUSE_INPORT
+	help
+	  Say Y here if your mouse is of the ATI XL variety.
+
+config MOUSE_LOGIBM
+	tristate "Logitech busmouse"
+	depends on ISA
+	help
+	  Say Y here if you have a Logitech busmouse.
+	  They are rather rare these days.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called logibm.
+
+config MOUSE_PC110PAD
+	tristate "IBM PC110 touchpad"
+	depends on ISA
+	help
+	  Say Y if you have the IBM PC-110 micro-notebook and want its
+	  touchpad supported.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pc110pad.
+
+config MOUSE_AMIGA
+	tristate "Amiga mouse"
+	depends on AMIGA
+	help
+	  Say Y here if you have an Amiga and want its native mouse
+	  supported by the kernel.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called amimouse.
+
+config MOUSE_ATARI
+	tristate "Atari mouse"
+	depends on ATARI
+	select ATARI_KBD_CORE
+	help
+	  Say Y here if you have an Atari and want its native mouse
+	  supported by the kernel.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called atarimouse.
+
+config MOUSE_RISCPC
+	tristate "Acorn RiscPC mouse"
+	depends on ARCH_ACORN
+	help
+	  Say Y here if you have the Acorn RiscPC computer and want its
+	  native mouse supported.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rpcmouse.
+
+config MOUSE_VSXXXAA
+	tristate "DEC VSXXX-AA/GA mouse and VSXXX-AB tablet"
+	select SERIO
+	help
+	  Say Y (or M) if you want to use a DEC VSXXX-AA (hockey
+	  puck) or a VSXXX-GA (rectangular) mouse. Theses mice are
+	  typically used on DECstations or VAXstations, but can also
+	  be used on any box capable of RS232 (with some adaptor
+	  described in the source file). This driver also works with the
+	  digitizer (VSXXX-AB) DEC produced.
+
+config MOUSE_GPIO
+	tristate "GPIO mouse"
+	depends on GPIOLIB || COMPILE_TEST
+	select INPUT_POLLDEV
+	help
+	  This driver simulates a mouse on GPIO lines of various CPUs (and some
+	  other chips).
+
+	  Say Y here if your device has buttons or a simple joystick connected
+	  directly to GPIO lines. Your board-specific setup logic must also
+	  provide a platform device and platform data saying which GPIOs are
+	  used.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gpio_mouse.
+
+config MOUSE_PXA930_TRKBALL
+	tristate "PXA930 Trackball mouse"
+	depends on CPU_PXA930 || CPU_PXA935
+	help
+	  Say Y here to support PXA930 Trackball mouse.
+
+config MOUSE_MAPLE
+	tristate "Maple mouse (for the Dreamcast)"
+	depends on MAPLE
+	help
+	  This driver supports the Maple mouse on the SEGA Dreamcast.
+
+	  Most Dreamcast users, who have a mouse, will say Y here.
+
+	  To compile this driver as a module choose M here: the module will be
+	  called maplemouse.
+
+config MOUSE_SYNAPTICS_I2C
+	tristate "Synaptics I2C Touchpad support"
+	depends on I2C
+	help
+	  This driver supports Synaptics I2C touchpad controller on eXeda
+	  mobile device.
+	  The device will not work the synaptics X11 driver because
+	  (i) it  reports only relative coordinates and has no capabilities
+	  to report absolute coordinates
+	  (ii) the eXeda device itself uses Xfbdev as X Server and it does
+	  not allow using xf86-input-* drivers.
+
+	  Say y here if you have eXeda device and want to use a Synaptics
+	  I2C Touchpad.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called synaptics_i2c.
+
+config MOUSE_SYNAPTICS_USB
+	tristate "Synaptics USB device support"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you want to use a Synaptics USB touchpad or pointing
+	  stick.
+
+	  While these devices emulate an USB mouse by default and can be used
+	  with standard usbhid driver, this driver, together with its X.Org
+	  counterpart, allows you to fully utilize capabilities of the device.
+	  More information can be found at:
+	  <http://jan-steinhoff.de/linux/synaptics-usb.html>
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called synaptics_usb.
+
+config MOUSE_NAVPOINT_PXA27x
+	tristate "Synaptics NavPoint (PXA27x SSP/SPI)"
+	depends on PXA27x && PXA_SSP
+	help
+	  This driver adds support for the Synaptics NavPoint touchpad connected
+	  to a PXA27x SSP port in SPI slave mode. The device emulates a mouse;
+	  a tap or tap-and-a-half drag gesture emulates the left mouse button.
+	  For example, use the xf86-input-evdev driver for an X pointing device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called navpoint.
+
+endif
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
new file mode 100644
index 0000000..ee6a6e9
--- /dev/null
+++ b/drivers/input/mouse/Makefile
@@ -0,0 +1,43 @@
+#
+# Makefile for the mouse drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_MOUSE_AMIGA)		+= amimouse.o
+obj-$(CONFIG_MOUSE_APPLETOUCH)		+= appletouch.o
+obj-$(CONFIG_MOUSE_ATARI)		+= atarimouse.o
+obj-$(CONFIG_MOUSE_BCM5974)		+= bcm5974.o
+obj-$(CONFIG_MOUSE_CYAPA)		+= cyapatp.o
+obj-$(CONFIG_MOUSE_ELAN_I2C)		+= elan_i2c.o
+obj-$(CONFIG_MOUSE_GPIO)		+= gpio_mouse.o
+obj-$(CONFIG_MOUSE_INPORT)		+= inport.o
+obj-$(CONFIG_MOUSE_LOGIBM)		+= logibm.o
+obj-$(CONFIG_MOUSE_MAPLE)		+= maplemouse.o
+obj-$(CONFIG_MOUSE_NAVPOINT_PXA27x)	+= navpoint.o
+obj-$(CONFIG_MOUSE_PC110PAD)		+= pc110pad.o
+obj-$(CONFIG_MOUSE_PS2)			+= psmouse.o
+obj-$(CONFIG_MOUSE_PXA930_TRKBALL)	+= pxa930_trkball.o
+obj-$(CONFIG_MOUSE_RISCPC)		+= rpcmouse.o
+obj-$(CONFIG_MOUSE_SERIAL)		+= sermouse.o
+obj-$(CONFIG_MOUSE_SYNAPTICS_I2C)	+= synaptics_i2c.o
+obj-$(CONFIG_MOUSE_SYNAPTICS_USB)	+= synaptics_usb.o
+obj-$(CONFIG_MOUSE_VSXXXAA)		+= vsxxxaa.o
+
+cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o cyapa_gen6.o
+psmouse-objs := psmouse-base.o synaptics.o focaltech.o
+
+psmouse-$(CONFIG_MOUSE_PS2_ALPS)	+= alps.o
+psmouse-$(CONFIG_MOUSE_PS2_ELANTECH)	+= elantech.o
+psmouse-$(CONFIG_MOUSE_PS2_OLPC)	+= hgpk.o
+psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP)	+= logips2pp.o
+psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK)	+= lifebook.o
+psmouse-$(CONFIG_MOUSE_PS2_SENTELIC)	+= sentelic.o
+psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT)	+= trackpoint.o
+psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT)	+= touchkit_ps2.o
+psmouse-$(CONFIG_MOUSE_PS2_CYPRESS)	+= cypress_ps2.o
+psmouse-$(CONFIG_MOUSE_PS2_VMMOUSE)	+= vmmouse.o
+
+elan_i2c-objs := elan_i2c_core.o
+elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_I2C)	+= elan_i2c_i2c.o
+elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_SMBUS)	+= elan_i2c_smbus.o
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
new file mode 100644
index 0000000..41e6cb5
--- /dev/null
+++ b/drivers/input/mouse/alps.c
@@ -0,0 +1,3040 @@
+/*
+ * ALPS touchpad PS/2 mouse driver
+ *
+ * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com>
+ * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2009 Sebastian Kapfer <sebastian_kapfer@gmx.net>
+ *
+ * ALPS detection, tap switching and status querying info is taken from
+ * tpconfig utility (by C. Scott Ananian and Bruce Kall).
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/dmi.h>
+
+#include "psmouse.h"
+#include "alps.h"
+
+/*
+ * Definitions for ALPS version 3 and 4 command mode protocol
+ */
+#define ALPS_CMD_NIBBLE_10	0x01f2
+
+#define ALPS_REG_BASE_RUSHMORE	0xc2c0
+#define ALPS_REG_BASE_PINNACLE	0x0000
+
+static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
+	{ PSMOUSE_CMD_SETPOLL,		0x00 }, /* 0 */
+	{ PSMOUSE_CMD_RESET_DIS,	0x00 }, /* 1 */
+	{ PSMOUSE_CMD_SETSCALE21,	0x00 }, /* 2 */
+	{ PSMOUSE_CMD_SETRATE,		0x0a }, /* 3 */
+	{ PSMOUSE_CMD_SETRATE,		0x14 }, /* 4 */
+	{ PSMOUSE_CMD_SETRATE,		0x28 }, /* 5 */
+	{ PSMOUSE_CMD_SETRATE,		0x3c }, /* 6 */
+	{ PSMOUSE_CMD_SETRATE,		0x50 }, /* 7 */
+	{ PSMOUSE_CMD_SETRATE,		0x64 }, /* 8 */
+	{ PSMOUSE_CMD_SETRATE,		0xc8 }, /* 9 */
+	{ ALPS_CMD_NIBBLE_10,		0x00 }, /* a */
+	{ PSMOUSE_CMD_SETRES,		0x00 }, /* b */
+	{ PSMOUSE_CMD_SETRES,		0x01 }, /* c */
+	{ PSMOUSE_CMD_SETRES,		0x02 }, /* d */
+	{ PSMOUSE_CMD_SETRES,		0x03 }, /* e */
+	{ PSMOUSE_CMD_SETSCALE11,	0x00 }, /* f */
+};
+
+static const struct alps_nibble_commands alps_v4_nibble_commands[] = {
+	{ PSMOUSE_CMD_ENABLE,		0x00 }, /* 0 */
+	{ PSMOUSE_CMD_RESET_DIS,	0x00 }, /* 1 */
+	{ PSMOUSE_CMD_SETSCALE21,	0x00 }, /* 2 */
+	{ PSMOUSE_CMD_SETRATE,		0x0a }, /* 3 */
+	{ PSMOUSE_CMD_SETRATE,		0x14 }, /* 4 */
+	{ PSMOUSE_CMD_SETRATE,		0x28 }, /* 5 */
+	{ PSMOUSE_CMD_SETRATE,		0x3c }, /* 6 */
+	{ PSMOUSE_CMD_SETRATE,		0x50 }, /* 7 */
+	{ PSMOUSE_CMD_SETRATE,		0x64 }, /* 8 */
+	{ PSMOUSE_CMD_SETRATE,		0xc8 }, /* 9 */
+	{ ALPS_CMD_NIBBLE_10,		0x00 }, /* a */
+	{ PSMOUSE_CMD_SETRES,		0x00 }, /* b */
+	{ PSMOUSE_CMD_SETRES,		0x01 }, /* c */
+	{ PSMOUSE_CMD_SETRES,		0x02 }, /* d */
+	{ PSMOUSE_CMD_SETRES,		0x03 }, /* e */
+	{ PSMOUSE_CMD_SETSCALE11,	0x00 }, /* f */
+};
+
+static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
+	{ PSMOUSE_CMD_ENABLE,		0x00 }, /* 0 */
+	{ PSMOUSE_CMD_SETRATE,		0x0a }, /* 1 */
+	{ PSMOUSE_CMD_SETRATE,		0x14 }, /* 2 */
+	{ PSMOUSE_CMD_SETRATE,		0x28 }, /* 3 */
+	{ PSMOUSE_CMD_SETRATE,		0x3c }, /* 4 */
+	{ PSMOUSE_CMD_SETRATE,		0x50 }, /* 5 */
+	{ PSMOUSE_CMD_SETRATE,		0x64 }, /* 6 */
+	{ PSMOUSE_CMD_SETRATE,		0xc8 }, /* 7 */
+	{ PSMOUSE_CMD_GETID,		0x00 }, /* 8 */
+	{ PSMOUSE_CMD_GETINFO,		0x00 }, /* 9 */
+	{ PSMOUSE_CMD_SETRES,		0x00 }, /* a */
+	{ PSMOUSE_CMD_SETRES,		0x01 }, /* b */
+	{ PSMOUSE_CMD_SETRES,		0x02 }, /* c */
+	{ PSMOUSE_CMD_SETRES,		0x03 }, /* d */
+	{ PSMOUSE_CMD_SETSCALE21,	0x00 }, /* e */
+	{ PSMOUSE_CMD_SETSCALE11,	0x00 }, /* f */
+};
+
+
+#define ALPS_DUALPOINT		0x02	/* touchpad has trackstick */
+#define ALPS_PASS		0x04	/* device has a pass-through port */
+
+#define ALPS_WHEEL		0x08	/* hardware wheel present */
+#define ALPS_FW_BK_1		0x10	/* front & back buttons present */
+#define ALPS_FW_BK_2		0x20	/* front & back buttons present */
+#define ALPS_FOUR_BUTTONS	0x40	/* 4 direction button present */
+#define ALPS_PS2_INTERLEAVED	0x80	/* 3-byte PS/2 packet interleaved with
+					   6-byte ALPS packet */
+#define ALPS_STICK_BITS		0x100	/* separate stick button bits */
+#define ALPS_BUTTONPAD		0x200	/* device is a clickpad */
+
+static const struct alps_model_info alps_model_data[] = {
+	{ { 0x32, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },	/* Toshiba Salellite Pro M10 */
+	{ { 0x33, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V1, 0x88, 0xf8, 0 } },				/* UMAX-530T */
+	{ { 0x53, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+	{ { 0x53, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+	{ { 0x60, 0x03, 0xc8 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },				/* HP ze1115 */
+	{ { 0x63, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+	{ { 0x63, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+	{ { 0x63, 0x02, 0x28 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } },		/* Fujitsu Siemens S6010 */
+	{ { 0x63, 0x02, 0x3c }, 0x00, { ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL } },		/* Toshiba Satellite S2400-103 */
+	{ { 0x63, 0x02, 0x50 }, 0x00, { ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 } },		/* NEC Versa L320 */
+	{ { 0x63, 0x02, 0x64 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+	{ { 0x63, 0x03, 0xc8 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },	/* Dell Latitude D800 */
+	{ { 0x73, 0x00, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT } },		/* ThinkPad R61 8918-5QG */
+	{ { 0x73, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+	{ { 0x73, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } },		/* Ahtec Laptop */
+
+	/*
+	 * XXX This entry is suspicious. First byte has zero lower nibble,
+	 * which is what a normal mouse would report. Also, the value 0x0e
+	 * isn't valid per PS/2 spec.
+	 */
+	{ { 0x20, 0x02, 0x0e }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
+
+	{ { 0x22, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
+	{ { 0x22, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT } },	/* Dell Latitude D600 */
+	/* Dell Latitude E5500, E6400, E6500, Precision M4400 */
+	{ { 0x62, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xcf, 0xcf,
+		ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } },
+	{ { 0x73, 0x00, 0x14 }, 0x00, { ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT } },		/* Dell XT2 */
+	{ { 0x73, 0x02, 0x50 }, 0x00, { ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS } },	/* Dell Vostro 1400 */
+	{ { 0x52, 0x01, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xff, 0xff,
+		ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } },				/* Toshiba Tecra A11-11L */
+	{ { 0x73, 0x02, 0x64 }, 0x8a, { ALPS_PROTO_V4, 0x8f, 0x8f, 0 } },
+};
+
+static const struct alps_protocol_info alps_v3_protocol_data = {
+	ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT
+};
+
+static const struct alps_protocol_info alps_v3_rushmore_data = {
+	ALPS_PROTO_V3_RUSHMORE, 0x8f, 0x8f, ALPS_DUALPOINT
+};
+
+static const struct alps_protocol_info alps_v5_protocol_data = {
+	ALPS_PROTO_V5, 0xc8, 0xd8, 0
+};
+
+static const struct alps_protocol_info alps_v7_protocol_data = {
+	ALPS_PROTO_V7, 0x48, 0x48, ALPS_DUALPOINT
+};
+
+static const struct alps_protocol_info alps_v8_protocol_data = {
+	ALPS_PROTO_V8, 0x18, 0x18, 0
+};
+
+/*
+ * Some v2 models report the stick buttons in separate bits
+ */
+static const struct dmi_system_id alps_dmi_has_separate_stick_buttons[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+	{
+		/* Extrapolated from other entries */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D420"),
+		},
+	},
+	{
+		/* Reported-by: Hans de Bruin <jmdebruin@xmsnet.nl> */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D430"),
+		},
+	},
+	{
+		/* Reported-by: Hans de Goede <hdegoede@redhat.com> */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D620"),
+		},
+	},
+	{
+		/* Extrapolated from other entries */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D630"),
+		},
+	},
+#endif
+	{ }
+};
+
+static void alps_set_abs_params_st(struct alps_data *priv,
+				   struct input_dev *dev1);
+static void alps_set_abs_params_semi_mt(struct alps_data *priv,
+					struct input_dev *dev1);
+static void alps_set_abs_params_v7(struct alps_data *priv,
+				   struct input_dev *dev1);
+static void alps_set_abs_params_ss4_v2(struct alps_data *priv,
+				       struct input_dev *dev1);
+
+/* Packet formats are described in Documentation/input/alps.txt */
+
+static bool alps_is_valid_first_byte(struct alps_data *priv,
+				     unsigned char data)
+{
+	return (data & priv->mask0) == priv->byte0;
+}
+
+static void alps_report_buttons(struct input_dev *dev1, struct input_dev *dev2,
+				int left, int right, int middle)
+{
+	struct input_dev *dev;
+
+	/*
+	 * If shared button has already been reported on the
+	 * other device (dev2) then this event should be also
+	 * sent through that device.
+	 */
+	dev = (dev2 && test_bit(BTN_LEFT, dev2->key)) ? dev2 : dev1;
+	input_report_key(dev, BTN_LEFT, left);
+
+	dev = (dev2 && test_bit(BTN_RIGHT, dev2->key)) ? dev2 : dev1;
+	input_report_key(dev, BTN_RIGHT, right);
+
+	dev = (dev2 && test_bit(BTN_MIDDLE, dev2->key)) ? dev2 : dev1;
+	input_report_key(dev, BTN_MIDDLE, middle);
+
+	/*
+	 * Sync the _other_ device now, we'll do the first
+	 * device later once we report the rest of the events.
+	 */
+	if (dev2)
+		input_sync(dev2);
+}
+
+static void alps_process_packet_v1_v2(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev = psmouse->dev;
+	struct input_dev *dev2 = priv->dev2;
+	int x, y, z, ges, fin, left, right, middle;
+	int back = 0, forward = 0;
+
+	if (priv->proto_version == ALPS_PROTO_V1) {
+		left = packet[2] & 0x10;
+		right = packet[2] & 0x08;
+		middle = 0;
+		x = packet[1] | ((packet[0] & 0x07) << 7);
+		y = packet[4] | ((packet[3] & 0x07) << 7);
+		z = packet[5];
+	} else {
+		left = packet[3] & 1;
+		right = packet[3] & 2;
+		middle = packet[3] & 4;
+		x = packet[1] | ((packet[2] & 0x78) << (7 - 3));
+		y = packet[4] | ((packet[3] & 0x70) << (7 - 4));
+		z = packet[5];
+	}
+
+	if (priv->flags & ALPS_FW_BK_1) {
+		back = packet[0] & 0x10;
+		forward = packet[2] & 4;
+	}
+
+	if (priv->flags & ALPS_FW_BK_2) {
+		back = packet[3] & 4;
+		forward = packet[2] & 4;
+		if ((middle = forward && back))
+			forward = back = 0;
+	}
+
+	ges = packet[2] & 1;
+	fin = packet[2] & 2;
+
+	if ((priv->flags & ALPS_DUALPOINT) && z == 127) {
+		input_report_rel(dev2, REL_X,  (x > 383 ? (x - 768) : x));
+		input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
+
+		alps_report_buttons(dev2, dev, left, right, middle);
+
+		input_sync(dev2);
+		return;
+	}
+
+	/* Some models have separate stick button bits */
+	if (priv->flags & ALPS_STICK_BITS) {
+		left |= packet[0] & 1;
+		right |= packet[0] & 2;
+		middle |= packet[0] & 4;
+	}
+
+	alps_report_buttons(dev, dev2, left, right, middle);
+
+	/* Convert hardware tap to a reasonable Z value */
+	if (ges && !fin)
+		z = 40;
+
+	/*
+	 * A "tap and drag" operation is reported by the hardware as a transition
+	 * from (!fin && ges) to (fin && ges). This should be translated to the
+	 * sequence Z>0, Z==0, Z>0, so the Z==0 event has to be generated manually.
+	 */
+	if (ges && fin && !priv->prev_fin) {
+		input_report_abs(dev, ABS_X, x);
+		input_report_abs(dev, ABS_Y, y);
+		input_report_abs(dev, ABS_PRESSURE, 0);
+		input_report_key(dev, BTN_TOOL_FINGER, 0);
+		input_sync(dev);
+	}
+	priv->prev_fin = fin;
+
+	if (z > 30)
+		input_report_key(dev, BTN_TOUCH, 1);
+	if (z < 25)
+		input_report_key(dev, BTN_TOUCH, 0);
+
+	if (z > 0) {
+		input_report_abs(dev, ABS_X, x);
+		input_report_abs(dev, ABS_Y, y);
+	}
+
+	input_report_abs(dev, ABS_PRESSURE, z);
+	input_report_key(dev, BTN_TOOL_FINGER, z > 0);
+
+	if (priv->flags & ALPS_WHEEL)
+		input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07));
+
+	if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
+		input_report_key(dev, BTN_FORWARD, forward);
+		input_report_key(dev, BTN_BACK, back);
+	}
+
+	if (priv->flags & ALPS_FOUR_BUTTONS) {
+		input_report_key(dev, BTN_0, packet[2] & 4);
+		input_report_key(dev, BTN_1, packet[0] & 0x10);
+		input_report_key(dev, BTN_2, packet[3] & 4);
+		input_report_key(dev, BTN_3, packet[0] & 0x20);
+	}
+
+	input_sync(dev);
+}
+
+static void alps_get_bitmap_points(unsigned int map,
+				   struct alps_bitmap_point *low,
+				   struct alps_bitmap_point *high,
+				   int *fingers)
+{
+	struct alps_bitmap_point *point;
+	int i, bit, prev_bit = 0;
+
+	point = low;
+	for (i = 0; map != 0; i++, map >>= 1) {
+		bit = map & 1;
+		if (bit) {
+			if (!prev_bit) {
+				point->start_bit = i;
+				point->num_bits = 0;
+				(*fingers)++;
+			}
+			point->num_bits++;
+		} else {
+			if (prev_bit)
+				point = high;
+		}
+		prev_bit = bit;
+	}
+}
+
+/*
+ * Process bitmap data from semi-mt protocols. Returns the number of
+ * fingers detected. A return value of 0 means at least one of the
+ * bitmaps was empty.
+ *
+ * The bitmaps don't have enough data to track fingers, so this function
+ * only generates points representing a bounding box of all contacts.
+ * These points are returned in fields->mt when the return value
+ * is greater than 0.
+ */
+static int alps_process_bitmap(struct alps_data *priv,
+			       struct alps_fields *fields)
+{
+	int i, fingers_x = 0, fingers_y = 0, fingers, closest;
+	struct alps_bitmap_point x_low = {0,}, x_high = {0,};
+	struct alps_bitmap_point y_low = {0,}, y_high = {0,};
+	struct input_mt_pos corner[4];
+
+	if (!fields->x_map || !fields->y_map)
+		return 0;
+
+	alps_get_bitmap_points(fields->x_map, &x_low, &x_high, &fingers_x);
+	alps_get_bitmap_points(fields->y_map, &y_low, &y_high, &fingers_y);
+
+	/*
+	 * Fingers can overlap, so we use the maximum count of fingers
+	 * on either axis as the finger count.
+	 */
+	fingers = max(fingers_x, fingers_y);
+
+	/*
+	 * If an axis reports only a single contact, we have overlapping or
+	 * adjacent fingers. Divide the single contact between the two points.
+	 */
+	if (fingers_x == 1) {
+		i = (x_low.num_bits - 1) / 2;
+		x_low.num_bits = x_low.num_bits - i;
+		x_high.start_bit = x_low.start_bit + i;
+		x_high.num_bits = max(i, 1);
+	}
+	if (fingers_y == 1) {
+		i = (y_low.num_bits - 1) / 2;
+		y_low.num_bits = y_low.num_bits - i;
+		y_high.start_bit = y_low.start_bit + i;
+		y_high.num_bits = max(i, 1);
+	}
+
+	/* top-left corner */
+	corner[0].x =
+		(priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
+		(2 * (priv->x_bits - 1));
+	corner[0].y =
+		(priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
+		(2 * (priv->y_bits - 1));
+
+	/* top-right corner */
+	corner[1].x =
+		(priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) /
+		(2 * (priv->x_bits - 1));
+	corner[1].y =
+		(priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
+		(2 * (priv->y_bits - 1));
+
+	/* bottom-right corner */
+	corner[2].x =
+		(priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) /
+		(2 * (priv->x_bits - 1));
+	corner[2].y =
+		(priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) /
+		(2 * (priv->y_bits - 1));
+
+	/* bottom-left corner */
+	corner[3].x =
+		(priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
+		(2 * (priv->x_bits - 1));
+	corner[3].y =
+		(priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) /
+		(2 * (priv->y_bits - 1));
+
+	/* x-bitmap order is reversed on v5 touchpads  */
+	if (priv->proto_version == ALPS_PROTO_V5) {
+		for (i = 0; i < 4; i++)
+			corner[i].x = priv->x_max - corner[i].x;
+	}
+
+	/* y-bitmap order is reversed on v3 and v4 touchpads  */
+	if (priv->proto_version == ALPS_PROTO_V3 ||
+	    priv->proto_version == ALPS_PROTO_V4) {
+		for (i = 0; i < 4; i++)
+			corner[i].y = priv->y_max - corner[i].y;
+	}
+
+	/*
+	 * We only select a corner for the second touch once per 2 finger
+	 * touch sequence to avoid the chosen corner (and thus the coordinates)
+	 * jumping around when the first touch is in the middle.
+	 */
+	if (priv->second_touch == -1) {
+		/* Find corner closest to our st coordinates */
+		closest = 0x7fffffff;
+		for (i = 0; i < 4; i++) {
+			int dx = fields->st.x - corner[i].x;
+			int dy = fields->st.y - corner[i].y;
+			int distance = dx * dx + dy * dy;
+
+			if (distance < closest) {
+				priv->second_touch = i;
+				closest = distance;
+			}
+		}
+		/* And select the opposite corner to use for the 2nd touch */
+		priv->second_touch = (priv->second_touch + 2) % 4;
+	}
+
+	fields->mt[0] = fields->st;
+	fields->mt[1] = corner[priv->second_touch];
+
+	return fingers;
+}
+
+static void alps_set_slot(struct input_dev *dev, int slot, int x, int y)
+{
+	input_mt_slot(dev, slot);
+	input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+	input_report_abs(dev, ABS_MT_POSITION_X, x);
+	input_report_abs(dev, ABS_MT_POSITION_Y, y);
+}
+
+static void alps_report_mt_data(struct psmouse *psmouse, int n)
+{
+	struct alps_data *priv = psmouse->private;
+	struct input_dev *dev = psmouse->dev;
+	struct alps_fields *f = &priv->f;
+	int i, slot[MAX_TOUCHES];
+
+	input_mt_assign_slots(dev, slot, f->mt, n, 0);
+	for (i = 0; i < n; i++)
+		alps_set_slot(dev, slot[i], f->mt[i].x, f->mt[i].y);
+
+	input_mt_sync_frame(dev);
+}
+
+static void alps_report_semi_mt_data(struct psmouse *psmouse, int fingers)
+{
+	struct alps_data *priv = psmouse->private;
+	struct input_dev *dev = psmouse->dev;
+	struct alps_fields *f = &priv->f;
+
+	/* Use st data when we don't have mt data */
+	if (fingers < 2) {
+		f->mt[0].x = f->st.x;
+		f->mt[0].y = f->st.y;
+		fingers = f->pressure > 0 ? 1 : 0;
+		priv->second_touch = -1;
+	}
+
+	if (fingers >= 1)
+		alps_set_slot(dev, 0, f->mt[0].x, f->mt[0].y);
+	if (fingers >= 2)
+		alps_set_slot(dev, 1, f->mt[1].x, f->mt[1].y);
+	input_mt_sync_frame(dev);
+
+	input_mt_report_finger_count(dev, fingers);
+
+	input_report_key(dev, BTN_LEFT, f->left);
+	input_report_key(dev, BTN_RIGHT, f->right);
+	input_report_key(dev, BTN_MIDDLE, f->middle);
+
+	input_report_abs(dev, ABS_PRESSURE, f->pressure);
+
+	input_sync(dev);
+}
+
+static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev = priv->dev2;
+	int x, y, z, left, right, middle;
+
+	/* It should be a DualPoint when received trackstick packet */
+	if (!(priv->flags & ALPS_DUALPOINT)) {
+		psmouse_warn(psmouse,
+			     "Rejected trackstick packet from non DualPoint device");
+		return;
+	}
+
+	/* Sanity check packet */
+	if (!(packet[0] & 0x40)) {
+		psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n");
+		return;
+	}
+
+	/*
+	 * There's a special packet that seems to indicate the end
+	 * of a stream of trackstick data. Filter these out.
+	 */
+	if (packet[1] == 0x7f && packet[2] == 0x7f && packet[4] == 0x7f)
+		return;
+
+	x = (s8)(((packet[0] & 0x20) << 2) | (packet[1] & 0x7f));
+	y = (s8)(((packet[0] & 0x10) << 3) | (packet[2] & 0x7f));
+	z = (packet[4] & 0x7c) >> 2;
+
+	/*
+	 * The x and y values tend to be quite large, and when used
+	 * alone the trackstick is difficult to use. Scale them down
+	 * to compensate.
+	 */
+	x /= 8;
+	y /= 8;
+
+	input_report_rel(dev, REL_X, x);
+	input_report_rel(dev, REL_Y, -y);
+
+	/*
+	 * Most ALPS models report the trackstick buttons in the touchpad
+	 * packets, but a few report them here. No reliable way has been
+	 * found to differentiate between the models upfront, so we enable
+	 * the quirk in response to seeing a button press in the trackstick
+	 * packet.
+	 */
+	left = packet[3] & 0x01;
+	right = packet[3] & 0x02;
+	middle = packet[3] & 0x04;
+
+	if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) &&
+	    (left || right || middle))
+		priv->quirks |= ALPS_QUIRK_TRACKSTICK_BUTTONS;
+
+	if (priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) {
+		input_report_key(dev, BTN_LEFT, left);
+		input_report_key(dev, BTN_RIGHT, right);
+		input_report_key(dev, BTN_MIDDLE, middle);
+	}
+
+	input_sync(dev);
+	return;
+}
+
+static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p)
+{
+	f->left = !!(p[3] & 0x01);
+	f->right = !!(p[3] & 0x02);
+	f->middle = !!(p[3] & 0x04);
+
+	f->ts_left = !!(p[3] & 0x10);
+	f->ts_right = !!(p[3] & 0x20);
+	f->ts_middle = !!(p[3] & 0x40);
+}
+
+static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
+				 struct psmouse *psmouse)
+{
+	f->first_mp = !!(p[4] & 0x40);
+	f->is_mp = !!(p[0] & 0x40);
+
+	if (f->is_mp) {
+		f->fingers = (p[5] & 0x3) + 1;
+		f->x_map = ((p[4] & 0x7e) << 8) |
+			   ((p[1] & 0x7f) << 2) |
+			   ((p[0] & 0x30) >> 4);
+		f->y_map = ((p[3] & 0x70) << 4) |
+			   ((p[2] & 0x7f) << 1) |
+			   (p[4] & 0x01);
+	} else {
+		f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
+		       ((p[0] & 0x30) >> 4);
+		f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
+		f->pressure = p[5] & 0x7f;
+
+		alps_decode_buttons_v3(f, p);
+	}
+
+	return 0;
+}
+
+static int alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
+				 struct psmouse *psmouse)
+{
+	f->first_mp = !!(p[4] & 0x40);
+	f->is_mp = !!(p[5] & 0x40);
+
+	if (f->is_mp) {
+		f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1;
+		f->x_map = ((p[5] & 0x10) << 11) |
+			   ((p[4] & 0x7e) << 8) |
+			   ((p[1] & 0x7f) << 2) |
+			   ((p[0] & 0x30) >> 4);
+		f->y_map = ((p[5] & 0x20) << 6) |
+			   ((p[3] & 0x70) << 4) |
+			   ((p[2] & 0x7f) << 1) |
+			   (p[4] & 0x01);
+	} else {
+		f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
+		       ((p[0] & 0x30) >> 4);
+		f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
+		f->pressure = p[5] & 0x7f;
+
+		alps_decode_buttons_v3(f, p);
+	}
+
+	return 0;
+}
+
+static int alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
+				struct psmouse *psmouse)
+{
+	u64 palm_data = 0;
+	struct alps_data *priv = psmouse->private;
+
+	f->first_mp = !!(p[0] & 0x02);
+	f->is_mp = !!(p[0] & 0x20);
+
+	if (!f->is_mp) {
+		f->st.x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
+		f->st.y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
+		f->pressure = (p[0] & 4) ? 0 : p[5] & 0x7f;
+		alps_decode_buttons_v3(f, p);
+	} else {
+		f->fingers = ((p[0] & 0x6) >> 1 |
+		     (p[0] & 0x10) >> 2);
+
+		palm_data = (p[1] & 0x7f) |
+			    ((p[2] & 0x7f) << 7) |
+			    ((p[4] & 0x7f) << 14) |
+			    ((p[5] & 0x7f) << 21) |
+			    ((p[3] & 0x07) << 28) |
+			    (((u64)p[3] & 0x70) << 27) |
+			    (((u64)p[0] & 0x01) << 34);
+
+		/* Y-profile is stored in P(0) to p(n-1), n = y_bits; */
+		f->y_map = palm_data & (BIT(priv->y_bits) - 1);
+
+		/* X-profile is stored in p(n) to p(n+m-1), m = x_bits; */
+		f->x_map = (palm_data >> priv->y_bits) &
+			   (BIT(priv->x_bits) - 1);
+	}
+
+	return 0;
+}
+
+static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev2 = priv->dev2;
+	struct alps_fields *f = &priv->f;
+	int fingers = 0;
+
+	memset(f, 0, sizeof(*f));
+
+	priv->decode_fields(f, packet, psmouse);
+
+	/*
+	 * There's no single feature of touchpad position and bitmap packets
+	 * that can be used to distinguish between them. We rely on the fact
+	 * that a bitmap packet should always follow a position packet with
+	 * bit 6 of packet[4] set.
+	 */
+	if (priv->multi_packet) {
+		/*
+		 * Sometimes a position packet will indicate a multi-packet
+		 * sequence, but then what follows is another position
+		 * packet. Check for this, and when it happens process the
+		 * position packet as usual.
+		 */
+		if (f->is_mp) {
+			fingers = f->fingers;
+			/*
+			 * Bitmap processing uses position packet's coordinate
+			 * data, so we need to do decode it first.
+			 */
+			priv->decode_fields(f, priv->multi_data, psmouse);
+			if (alps_process_bitmap(priv, f) == 0)
+				fingers = 0; /* Use st data */
+		} else {
+			priv->multi_packet = 0;
+		}
+	}
+
+	/*
+	 * Bit 6 of byte 0 is not usually set in position packets. The only
+	 * times it seems to be set is in situations where the data is
+	 * suspect anyway, e.g. a palm resting flat on the touchpad. Given
+	 * this combined with the fact that this bit is useful for filtering
+	 * out misidentified bitmap packets, we reject anything with this
+	 * bit set.
+	 */
+	if (f->is_mp)
+		return;
+
+	if (!priv->multi_packet && f->first_mp) {
+		priv->multi_packet = 1;
+		memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
+		return;
+	}
+
+	priv->multi_packet = 0;
+
+	/*
+	 * Sometimes the hardware sends a single packet with z = 0
+	 * in the middle of a stream. Real releases generate packets
+	 * with x, y, and z all zero, so these seem to be flukes.
+	 * Ignore them.
+	 */
+	if (f->st.x && f->st.y && !f->pressure)
+		return;
+
+	alps_report_semi_mt_data(psmouse, fingers);
+
+	if ((priv->flags & ALPS_DUALPOINT) &&
+	    !(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
+		input_report_key(dev2, BTN_LEFT, f->ts_left);
+		input_report_key(dev2, BTN_RIGHT, f->ts_right);
+		input_report_key(dev2, BTN_MIDDLE, f->ts_middle);
+		input_sync(dev2);
+	}
+}
+
+static void alps_process_packet_v3(struct psmouse *psmouse)
+{
+	unsigned char *packet = psmouse->packet;
+
+	/*
+	 * v3 protocol packets come in three types, two representing
+	 * touchpad data and one representing trackstick data.
+	 * Trackstick packets seem to be distinguished by always
+	 * having 0x3f in the last byte. This value has never been
+	 * observed in the last byte of either of the other types
+	 * of packets.
+	 */
+	if (packet[5] == 0x3f) {
+		alps_process_trackstick_packet_v3(psmouse);
+		return;
+	}
+
+	alps_process_touchpad_packet_v3_v5(psmouse);
+}
+
+static void alps_process_packet_v6(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev = psmouse->dev;
+	struct input_dev *dev2 = priv->dev2;
+	int x, y, z, left, right, middle;
+
+	/*
+	 * We can use Byte5 to distinguish if the packet is from Touchpad
+	 * or Trackpoint.
+	 * Touchpad:	0 - 0x7E
+	 * Trackpoint:	0x7F
+	 */
+	if (packet[5] == 0x7F) {
+		/* It should be a DualPoint when received Trackpoint packet */
+		if (!(priv->flags & ALPS_DUALPOINT)) {
+			psmouse_warn(psmouse,
+				     "Rejected trackstick packet from non DualPoint device");
+			return;
+		}
+
+		/* Trackpoint packet */
+		x = packet[1] | ((packet[3] & 0x20) << 2);
+		y = packet[2] | ((packet[3] & 0x40) << 1);
+		z = packet[4];
+		left = packet[3] & 0x01;
+		right = packet[3] & 0x02;
+		middle = packet[3] & 0x04;
+
+		/* To prevent the cursor jump when finger lifted */
+		if (x == 0x7F && y == 0x7F && z == 0x7F)
+			x = y = z = 0;
+
+		/* Divide 4 since trackpoint's speed is too fast */
+		input_report_rel(dev2, REL_X, (char)x / 4);
+		input_report_rel(dev2, REL_Y, -((char)y / 4));
+
+		input_report_key(dev2, BTN_LEFT, left);
+		input_report_key(dev2, BTN_RIGHT, right);
+		input_report_key(dev2, BTN_MIDDLE, middle);
+
+		input_sync(dev2);
+		return;
+	}
+
+	/* Touchpad packet */
+	x = packet[1] | ((packet[3] & 0x78) << 4);
+	y = packet[2] | ((packet[4] & 0x78) << 4);
+	z = packet[5];
+	left = packet[3] & 0x01;
+	right = packet[3] & 0x02;
+
+	if (z > 30)
+		input_report_key(dev, BTN_TOUCH, 1);
+	if (z < 25)
+		input_report_key(dev, BTN_TOUCH, 0);
+
+	if (z > 0) {
+		input_report_abs(dev, ABS_X, x);
+		input_report_abs(dev, ABS_Y, y);
+	}
+
+	input_report_abs(dev, ABS_PRESSURE, z);
+	input_report_key(dev, BTN_TOOL_FINGER, z > 0);
+
+	/* v6 touchpad does not have middle button */
+	input_report_key(dev, BTN_LEFT, left);
+	input_report_key(dev, BTN_RIGHT, right);
+
+	input_sync(dev);
+}
+
+static void alps_process_packet_v4(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct alps_fields *f = &priv->f;
+	int offset;
+
+	/*
+	 * v4 has a 6-byte encoding for bitmap data, but this data is
+	 * broken up between 3 normal packets. Use priv->multi_packet to
+	 * track our position in the bitmap packet.
+	 */
+	if (packet[6] & 0x40) {
+		/* sync, reset position */
+		priv->multi_packet = 0;
+	}
+
+	if (WARN_ON_ONCE(priv->multi_packet > 2))
+		return;
+
+	offset = 2 * priv->multi_packet;
+	priv->multi_data[offset] = packet[6];
+	priv->multi_data[offset + 1] = packet[7];
+
+	f->left = !!(packet[4] & 0x01);
+	f->right = !!(packet[4] & 0x02);
+
+	f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
+		  ((packet[0] & 0x30) >> 4);
+	f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
+	f->pressure = packet[5] & 0x7f;
+
+	if (++priv->multi_packet > 2) {
+		priv->multi_packet = 0;
+
+		f->x_map = ((priv->multi_data[2] & 0x1f) << 10) |
+			   ((priv->multi_data[3] & 0x60) << 3) |
+			   ((priv->multi_data[0] & 0x3f) << 2) |
+			   ((priv->multi_data[1] & 0x60) >> 5);
+		f->y_map = ((priv->multi_data[5] & 0x01) << 10) |
+			   ((priv->multi_data[3] & 0x1f) << 5) |
+			    (priv->multi_data[1] & 0x1f);
+
+		f->fingers = alps_process_bitmap(priv, f);
+	}
+
+	alps_report_semi_mt_data(psmouse, f->fingers);
+}
+
+static bool alps_is_valid_package_v7(struct psmouse *psmouse)
+{
+	switch (psmouse->pktcnt) {
+	case 3:
+		return (psmouse->packet[2] & 0x40) == 0x40;
+	case 4:
+		return (psmouse->packet[3] & 0x48) == 0x48;
+	case 6:
+		return (psmouse->packet[5] & 0x40) == 0x00;
+	}
+	return true;
+}
+
+static unsigned char alps_get_packet_id_v7(char *byte)
+{
+	unsigned char packet_id;
+
+	if (byte[4] & 0x40)
+		packet_id = V7_PACKET_ID_TWO;
+	else if (byte[4] & 0x01)
+		packet_id = V7_PACKET_ID_MULTI;
+	else if ((byte[0] & 0x10) && !(byte[4] & 0x43))
+		packet_id = V7_PACKET_ID_NEW;
+	else if (byte[1] == 0x00 && byte[4] == 0x00)
+		packet_id = V7_PACKET_ID_IDLE;
+	else
+		packet_id = V7_PACKET_ID_UNKNOWN;
+
+	return packet_id;
+}
+
+static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt,
+					  unsigned char *pkt,
+					  unsigned char pkt_id)
+{
+	mt[0].x = ((pkt[2] & 0x80) << 4);
+	mt[0].x |= ((pkt[2] & 0x3F) << 5);
+	mt[0].x |= ((pkt[3] & 0x30) >> 1);
+	mt[0].x |= (pkt[3] & 0x07);
+	mt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07);
+
+	mt[1].x = ((pkt[3] & 0x80) << 4);
+	mt[1].x |= ((pkt[4] & 0x80) << 3);
+	mt[1].x |= ((pkt[4] & 0x3F) << 4);
+	mt[1].y = ((pkt[5] & 0x80) << 3);
+	mt[1].y |= ((pkt[5] & 0x3F) << 4);
+
+	switch (pkt_id) {
+	case V7_PACKET_ID_TWO:
+		mt[1].x &= ~0x000F;
+		mt[1].y |= 0x000F;
+		/* Detect false-postive touches where x & y report max value */
+		if (mt[1].y == 0x7ff && mt[1].x == 0xff0) {
+			mt[1].x = 0;
+			/* y gets set to 0 at the end of this function */
+		}
+		break;
+
+	case V7_PACKET_ID_MULTI:
+		mt[1].x &= ~0x003F;
+		mt[1].y &= ~0x0020;
+		mt[1].y |= ((pkt[4] & 0x02) << 4);
+		mt[1].y |= 0x001F;
+		break;
+
+	case V7_PACKET_ID_NEW:
+		mt[1].x &= ~0x003F;
+		mt[1].x |= (pkt[0] & 0x20);
+		mt[1].y |= 0x000F;
+		break;
+	}
+
+	mt[0].y = 0x7FF - mt[0].y;
+	mt[1].y = 0x7FF - mt[1].y;
+}
+
+static int alps_get_mt_count(struct input_mt_pos *mt)
+{
+	int i, fingers = 0;
+
+	for (i = 0; i < MAX_TOUCHES; i++) {
+		if (mt[i].x != 0 || mt[i].y != 0)
+			fingers++;
+	}
+
+	return fingers;
+}
+
+static int alps_decode_packet_v7(struct alps_fields *f,
+				  unsigned char *p,
+				  struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char pkt_id;
+
+	pkt_id = alps_get_packet_id_v7(p);
+	if (pkt_id == V7_PACKET_ID_IDLE)
+		return 0;
+	if (pkt_id == V7_PACKET_ID_UNKNOWN)
+		return -1;
+	/*
+	 * NEW packets are send to indicate a discontinuity in the finger
+	 * coordinate reporting. Specifically a finger may have moved from
+	 * slot 0 to 1 or vice versa. INPUT_MT_TRACK takes care of this for
+	 * us.
+	 *
+	 * NEW packets have 3 problems:
+	 * 1) They do not contain middle / right button info (on non clickpads)
+	 *    this can be worked around by preserving the old button state
+	 * 2) They do not contain an accurate fingercount, and they are
+	 *    typically send when the number of fingers changes. We cannot use
+	 *    the old finger count as that may mismatch with the amount of
+	 *    touch coordinates we've available in the NEW packet
+	 * 3) Their x data for the second touch is inaccurate leading to
+	 *    a possible jump of the x coordinate by 16 units when the first
+	 *    non NEW packet comes in
+	 * Since problems 2 & 3 cannot be worked around, just ignore them.
+	 */
+	if (pkt_id == V7_PACKET_ID_NEW)
+		return 1;
+
+	alps_get_finger_coordinate_v7(f->mt, p, pkt_id);
+
+	if (pkt_id == V7_PACKET_ID_TWO)
+		f->fingers = alps_get_mt_count(f->mt);
+	else /* pkt_id == V7_PACKET_ID_MULTI */
+		f->fingers = 3 + (p[5] & 0x03);
+
+	f->left = (p[0] & 0x80) >> 7;
+	if (priv->flags & ALPS_BUTTONPAD) {
+		if (p[0] & 0x20)
+			f->fingers++;
+		if (p[0] & 0x10)
+			f->fingers++;
+	} else {
+		f->right = (p[0] & 0x20) >> 5;
+		f->middle = (p[0] & 0x10) >> 4;
+	}
+
+	/* Sometimes a single touch is reported in mt[1] rather then mt[0] */
+	if (f->fingers == 1 && f->mt[0].x == 0 && f->mt[0].y == 0) {
+		f->mt[0].x = f->mt[1].x;
+		f->mt[0].y = f->mt[1].y;
+		f->mt[1].x = 0;
+		f->mt[1].y = 0;
+	}
+
+	return 0;
+}
+
+static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev2 = priv->dev2;
+	int x, y, z, left, right, middle;
+
+	/* It should be a DualPoint when received trackstick packet */
+	if (!(priv->flags & ALPS_DUALPOINT)) {
+		psmouse_warn(psmouse,
+			     "Rejected trackstick packet from non DualPoint device");
+		return;
+	}
+
+	x = ((packet[2] & 0xbf)) | ((packet[3] & 0x10) << 2);
+	y = (packet[3] & 0x07) | (packet[4] & 0xb8) |
+	    ((packet[3] & 0x20) << 1);
+	z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1);
+
+	left = (packet[1] & 0x01);
+	right = (packet[1] & 0x02) >> 1;
+	middle = (packet[1] & 0x04) >> 2;
+
+	input_report_rel(dev2, REL_X, (char)x);
+	input_report_rel(dev2, REL_Y, -((char)y));
+
+	input_report_key(dev2, BTN_LEFT, left);
+	input_report_key(dev2, BTN_RIGHT, right);
+	input_report_key(dev2, BTN_MIDDLE, middle);
+
+	input_sync(dev2);
+}
+
+static void alps_process_touchpad_packet_v7(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	struct input_dev *dev = psmouse->dev;
+	struct alps_fields *f = &priv->f;
+
+	memset(f, 0, sizeof(*f));
+
+	if (priv->decode_fields(f, psmouse->packet, psmouse))
+		return;
+
+	alps_report_mt_data(psmouse, alps_get_mt_count(f->mt));
+
+	input_mt_report_finger_count(dev, f->fingers);
+
+	input_report_key(dev, BTN_LEFT, f->left);
+	input_report_key(dev, BTN_RIGHT, f->right);
+	input_report_key(dev, BTN_MIDDLE, f->middle);
+
+	input_sync(dev);
+}
+
+static void alps_process_packet_v7(struct psmouse *psmouse)
+{
+	unsigned char *packet = psmouse->packet;
+
+	if (packet[0] == 0x48 && (packet[4] & 0x47) == 0x06)
+		alps_process_trackstick_packet_v7(psmouse);
+	else
+		alps_process_touchpad_packet_v7(psmouse);
+}
+
+static unsigned char alps_get_pkt_id_ss4_v2(unsigned char *byte)
+{
+	unsigned char pkt_id = SS4_PACKET_ID_IDLE;
+
+	if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 &&
+	    (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 && byte[5] == 0x00) {
+		pkt_id = SS4_PACKET_ID_IDLE;
+	} else if (!(byte[3] & 0x10)) {
+		pkt_id = SS4_PACKET_ID_ONE;
+	} else if (!(byte[3] & 0x20)) {
+		pkt_id = SS4_PACKET_ID_TWO;
+	} else {
+		pkt_id = SS4_PACKET_ID_MULTI;
+	}
+
+	return pkt_id;
+}
+
+static int alps_decode_ss4_v2(struct alps_fields *f,
+			      unsigned char *p, struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char pkt_id;
+	unsigned int no_data_x, no_data_y;
+
+	pkt_id = alps_get_pkt_id_ss4_v2(p);
+
+	/* Current packet is 1Finger coordinate packet */
+	switch (pkt_id) {
+	case SS4_PACKET_ID_ONE:
+		f->mt[0].x = SS4_1F_X_V2(p);
+		f->mt[0].y = SS4_1F_Y_V2(p);
+		f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f;
+		f->fingers = 1;
+		f->first_mp = 0;
+		f->is_mp = 0;
+		break;
+
+	case SS4_PACKET_ID_TWO:
+		if (priv->flags & ALPS_BUTTONPAD) {
+			f->mt[0].x = SS4_BTL_MF_X_V2(p, 0);
+			f->mt[0].y = SS4_BTL_MF_Y_V2(p, 0);
+			f->mt[1].x = SS4_BTL_MF_X_V2(p, 1);
+			f->mt[1].y = SS4_BTL_MF_Y_V2(p, 1);
+		} else {
+			f->mt[0].x = SS4_STD_MF_X_V2(p, 0);
+			f->mt[0].y = SS4_STD_MF_Y_V2(p, 0);
+			f->mt[1].x = SS4_STD_MF_X_V2(p, 1);
+			f->mt[1].y = SS4_STD_MF_Y_V2(p, 1);
+		}
+		f->pressure = SS4_MF_Z_V2(p, 0) ? 0x30 : 0;
+
+		if (SS4_IS_MF_CONTINUE(p)) {
+			f->first_mp = 1;
+		} else {
+			f->fingers = 2;
+			f->first_mp = 0;
+		}
+		f->is_mp = 0;
+
+		break;
+
+	case SS4_PACKET_ID_MULTI:
+		if (priv->flags & ALPS_BUTTONPAD) {
+			f->mt[2].x = SS4_BTL_MF_X_V2(p, 0);
+			f->mt[2].y = SS4_BTL_MF_Y_V2(p, 0);
+			f->mt[3].x = SS4_BTL_MF_X_V2(p, 1);
+			f->mt[3].y = SS4_BTL_MF_Y_V2(p, 1);
+			no_data_x = SS4_MFPACKET_NO_AX_BL;
+			no_data_y = SS4_MFPACKET_NO_AY_BL;
+		} else {
+			f->mt[2].x = SS4_STD_MF_X_V2(p, 0);
+			f->mt[2].y = SS4_STD_MF_Y_V2(p, 0);
+			f->mt[3].x = SS4_STD_MF_X_V2(p, 1);
+			f->mt[3].y = SS4_STD_MF_Y_V2(p, 1);
+			no_data_x = SS4_MFPACKET_NO_AX;
+			no_data_y = SS4_MFPACKET_NO_AY;
+		}
+
+		f->first_mp = 0;
+		f->is_mp = 1;
+
+		if (SS4_IS_5F_DETECTED(p)) {
+			f->fingers = 5;
+		} else if (f->mt[3].x == no_data_x &&
+			     f->mt[3].y == no_data_y) {
+			f->mt[3].x = 0;
+			f->mt[3].y = 0;
+			f->fingers = 3;
+		} else {
+			f->fingers = 4;
+		}
+		break;
+
+	case SS4_PACKET_ID_IDLE:
+	default:
+		memset(f, 0, sizeof(struct alps_fields));
+		break;
+	}
+
+	f->left = !!(SS4_BTN_V2(p) & 0x01);
+	if (!(priv->flags & ALPS_BUTTONPAD)) {
+		f->right = !!(SS4_BTN_V2(p) & 0x02);
+		f->middle = !!(SS4_BTN_V2(p) & 0x04);
+	}
+
+	return 0;
+}
+
+static void alps_process_packet_ss4_v2(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev = psmouse->dev;
+	struct alps_fields *f = &priv->f;
+
+	memset(f, 0, sizeof(struct alps_fields));
+	priv->decode_fields(f, packet, psmouse);
+	if (priv->multi_packet) {
+		/*
+		 * Sometimes the first packet will indicate a multi-packet
+		 * sequence, but sometimes the next multi-packet would not
+		 * come. Check for this, and when it happens process the
+		 * position packet as usual.
+		 */
+		if (f->is_mp) {
+			/* Now process the 1st packet */
+			priv->decode_fields(f, priv->multi_data, psmouse);
+		} else {
+			priv->multi_packet = 0;
+		}
+	}
+
+	/*
+	 * "f.is_mp" would always be '0' after merging the 1st and 2nd packet.
+	 * When it is set, it means 2nd packet comes without 1st packet come.
+	 */
+	if (f->is_mp)
+		return;
+
+	/* Save the first packet */
+	if (!priv->multi_packet && f->first_mp) {
+		priv->multi_packet = 1;
+		memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
+		return;
+	}
+
+	priv->multi_packet = 0;
+
+	alps_report_mt_data(psmouse, (f->fingers <= 4) ? f->fingers : 4);
+
+	input_mt_report_finger_count(dev, f->fingers);
+
+	input_report_key(dev, BTN_LEFT, f->left);
+	input_report_key(dev, BTN_RIGHT, f->right);
+	input_report_key(dev, BTN_MIDDLE, f->middle);
+
+	input_report_abs(dev, ABS_PRESSURE, f->pressure);
+	input_sync(dev);
+}
+
+static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse)
+{
+	if (psmouse->pktcnt == 4 && ((psmouse->packet[3] & 0x08) != 0x08))
+		return false;
+	if (psmouse->pktcnt == 6 && ((psmouse->packet[5] & 0x10) != 0x0))
+		return false;
+	return true;
+}
+
+static DEFINE_MUTEX(alps_mutex);
+
+static void alps_register_bare_ps2_mouse(struct work_struct *work)
+{
+	struct alps_data *priv =
+		container_of(work, struct alps_data, dev3_register_work.work);
+	struct psmouse *psmouse = priv->psmouse;
+	struct input_dev *dev3;
+	int error = 0;
+
+	mutex_lock(&alps_mutex);
+
+	if (priv->dev3)
+		goto out;
+
+	dev3 = input_allocate_device();
+	if (!dev3) {
+		psmouse_err(psmouse, "failed to allocate secondary device\n");
+		error = -ENOMEM;
+		goto out;
+	}
+
+	snprintf(priv->phys3, sizeof(priv->phys3), "%s/%s",
+		 psmouse->ps2dev.serio->phys,
+		 (priv->dev2 ? "input2" : "input1"));
+	dev3->phys = priv->phys3;
+
+	/*
+	 * format of input device name is: "protocol vendor name"
+	 * see function psmouse_switch_protocol() in psmouse-base.c
+	 */
+	dev3->name = "PS/2 ALPS Mouse";
+
+	dev3->id.bustype = BUS_I8042;
+	dev3->id.vendor  = 0x0002;
+	dev3->id.product = PSMOUSE_PS2;
+	dev3->id.version = 0x0000;
+	dev3->dev.parent = &psmouse->ps2dev.serio->dev;
+
+	input_set_capability(dev3, EV_REL, REL_X);
+	input_set_capability(dev3, EV_REL, REL_Y);
+	input_set_capability(dev3, EV_KEY, BTN_LEFT);
+	input_set_capability(dev3, EV_KEY, BTN_RIGHT);
+	input_set_capability(dev3, EV_KEY, BTN_MIDDLE);
+
+	__set_bit(INPUT_PROP_POINTER, dev3->propbit);
+
+	error = input_register_device(dev3);
+	if (error) {
+		psmouse_err(psmouse,
+			    "failed to register secondary device: %d\n",
+			    error);
+		input_free_device(dev3);
+		goto out;
+	}
+
+	priv->dev3 = dev3;
+
+out:
+	/*
+	 * Save the error code so that we can detect that we
+	 * already tried to create the device.
+	 */
+	if (error)
+		priv->dev3 = ERR_PTR(error);
+
+	mutex_unlock(&alps_mutex);
+}
+
+static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
+					unsigned char packet[],
+					bool report_buttons)
+{
+	struct alps_data *priv = psmouse->private;
+	struct input_dev *dev, *dev2 = NULL;
+
+	/* Figure out which device to use to report the bare packet */
+	if (priv->proto_version == ALPS_PROTO_V2 &&
+	    (priv->flags & ALPS_DUALPOINT)) {
+		/* On V2 devices the DualPoint Stick reports bare packets */
+		dev = priv->dev2;
+		dev2 = psmouse->dev;
+	} else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) {
+		/* Register dev3 mouse if we received PS/2 packet first time */
+		if (!IS_ERR(priv->dev3))
+			psmouse_queue_work(psmouse, &priv->dev3_register_work,
+					   0);
+		return;
+	} else {
+		dev = priv->dev3;
+	}
+
+	if (report_buttons)
+		alps_report_buttons(dev, dev2,
+				packet[0] & 1, packet[0] & 2, packet[0] & 4);
+
+	input_report_rel(dev, REL_X,
+		packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
+	input_report_rel(dev, REL_Y,
+		packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);
+
+	input_sync(dev);
+}
+
+static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+
+	if (psmouse->pktcnt < 6)
+		return PSMOUSE_GOOD_DATA;
+
+	if (psmouse->pktcnt == 6) {
+		/*
+		 * Start a timer to flush the packet if it ends up last
+		 * 6-byte packet in the stream. Timer needs to fire
+		 * psmouse core times out itself. 20 ms should be enough
+		 * to decide if we are getting more data or not.
+		 */
+		mod_timer(&priv->timer, jiffies + msecs_to_jiffies(20));
+		return PSMOUSE_GOOD_DATA;
+	}
+
+	del_timer(&priv->timer);
+
+	if (psmouse->packet[6] & 0x80) {
+
+		/*
+		 * Highest bit is set - that means we either had
+		 * complete ALPS packet and this is start of the
+		 * next packet or we got garbage.
+		 */
+
+		if (((psmouse->packet[3] |
+		      psmouse->packet[4] |
+		      psmouse->packet[5]) & 0x80) ||
+		    (!alps_is_valid_first_byte(priv, psmouse->packet[6]))) {
+			psmouse_dbg(psmouse,
+				    "refusing packet %4ph (suspected interleaved ps/2)\n",
+				    psmouse->packet + 3);
+			return PSMOUSE_BAD_DATA;
+		}
+
+		priv->process_packet(psmouse);
+
+		/* Continue with the next packet */
+		psmouse->packet[0] = psmouse->packet[6];
+		psmouse->pktcnt = 1;
+
+	} else {
+
+		/*
+		 * High bit is 0 - that means that we indeed got a PS/2
+		 * packet in the middle of ALPS packet.
+		 *
+		 * There is also possibility that we got 6-byte ALPS
+		 * packet followed  by 3-byte packet from trackpoint. We
+		 * can not distinguish between these 2 scenarios but
+		 * because the latter is unlikely to happen in course of
+		 * normal operation (user would need to press all
+		 * buttons on the pad and start moving trackpoint
+		 * without touching the pad surface) we assume former.
+		 * Even if we are wrong the wost thing that would happen
+		 * the cursor would jump but we should not get protocol
+		 * de-synchronization.
+		 */
+
+		alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3],
+					    false);
+
+		/*
+		 * Continue with the standard ALPS protocol handling,
+		 * but make sure we won't process it as an interleaved
+		 * packet again, which may happen if all buttons are
+		 * pressed. To avoid this let's reset the 4th bit which
+		 * is normally 1.
+		 */
+		psmouse->packet[3] = psmouse->packet[6] & 0xf7;
+		psmouse->pktcnt = 4;
+	}
+
+	return PSMOUSE_GOOD_DATA;
+}
+
+static void alps_flush_packet(unsigned long data)
+{
+	struct psmouse *psmouse = (struct psmouse *)data;
+	struct alps_data *priv = psmouse->private;
+
+	serio_pause_rx(psmouse->ps2dev.serio);
+
+	if (psmouse->pktcnt == psmouse->pktsize) {
+
+		/*
+		 * We did not any more data in reasonable amount of time.
+		 * Validate the last 3 bytes and process as a standard
+		 * ALPS packet.
+		 */
+		if ((psmouse->packet[3] |
+		     psmouse->packet[4] |
+		     psmouse->packet[5]) & 0x80) {
+			psmouse_dbg(psmouse,
+				    "refusing packet %3ph (suspected interleaved ps/2)\n",
+				    psmouse->packet + 3);
+		} else {
+			priv->process_packet(psmouse);
+		}
+		psmouse->pktcnt = 0;
+	}
+
+	serio_continue_rx(psmouse->ps2dev.serio);
+}
+
+static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+
+	/*
+	 * Check if we are dealing with a bare PS/2 packet, presumably from
+	 * a device connected to the external PS/2 port. Because bare PS/2
+	 * protocol does not have enough constant bits to self-synchronize
+	 * properly we only do this if the device is fully synchronized.
+	 * Can not distinguish V8's first byte from PS/2 packet's
+	 */
+	if (priv->proto_version != ALPS_PROTO_V8 &&
+	    !psmouse->out_of_sync_cnt &&
+	    (psmouse->packet[0] & 0xc8) == 0x08) {
+
+		if (psmouse->pktcnt == 3) {
+			alps_report_bare_ps2_packet(psmouse, psmouse->packet,
+						    true);
+			return PSMOUSE_FULL_PACKET;
+		}
+		return PSMOUSE_GOOD_DATA;
+	}
+
+	/* Check for PS/2 packet stuffed in the middle of ALPS packet. */
+
+	if ((priv->flags & ALPS_PS2_INTERLEAVED) &&
+	    psmouse->pktcnt >= 4 && (psmouse->packet[3] & 0x0f) == 0x0f) {
+		return alps_handle_interleaved_ps2(psmouse);
+	}
+
+	if (!alps_is_valid_first_byte(priv, psmouse->packet[0])) {
+		psmouse_dbg(psmouse,
+			    "refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n",
+			    psmouse->packet[0], priv->mask0, priv->byte0);
+		return PSMOUSE_BAD_DATA;
+	}
+
+	/* Bytes 2 - pktsize should have 0 in the highest bit */
+	if (priv->proto_version < ALPS_PROTO_V5 &&
+	    psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize &&
+	    (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) {
+		psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
+			    psmouse->pktcnt - 1,
+			    psmouse->packet[psmouse->pktcnt - 1]);
+
+		if (priv->proto_version == ALPS_PROTO_V3_RUSHMORE &&
+		    psmouse->pktcnt == psmouse->pktsize) {
+			/*
+			 * Some Dell boxes, such as Latitude E6440 or E7440
+			 * with closed lid, quite often smash last byte of
+			 * otherwise valid packet with 0xff. Given that the
+			 * next packet is very likely to be valid let's
+			 * report PSMOUSE_FULL_PACKET but not process data,
+			 * rather than reporting PSMOUSE_BAD_DATA and
+			 * filling the logs.
+			 */
+			return PSMOUSE_FULL_PACKET;
+		}
+
+		return PSMOUSE_BAD_DATA;
+	}
+
+	if ((priv->proto_version == ALPS_PROTO_V7 &&
+			!alps_is_valid_package_v7(psmouse)) ||
+	    (priv->proto_version == ALPS_PROTO_V8 &&
+			!alps_is_valid_package_ss4_v2(psmouse))) {
+		psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
+			    psmouse->pktcnt - 1,
+			    psmouse->packet[psmouse->pktcnt - 1]);
+		return PSMOUSE_BAD_DATA;
+	}
+
+	if (psmouse->pktcnt == psmouse->pktsize) {
+		priv->process_packet(psmouse);
+		return PSMOUSE_FULL_PACKET;
+	}
+
+	return PSMOUSE_GOOD_DATA;
+}
+
+static int alps_command_mode_send_nibble(struct psmouse *psmouse, int nibble)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	struct alps_data *priv = psmouse->private;
+	int command;
+	unsigned char *param;
+	unsigned char dummy[4];
+
+	BUG_ON(nibble > 0xf);
+
+	command = priv->nibble_commands[nibble].command;
+	param = (command & 0x0f00) ?
+		dummy : (unsigned char *)&priv->nibble_commands[nibble].data;
+
+	if (ps2_command(ps2dev, param, command))
+		return -1;
+
+	return 0;
+}
+
+static int alps_command_mode_set_addr(struct psmouse *psmouse, int addr)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	struct alps_data *priv = psmouse->private;
+	int i, nibble;
+
+	if (ps2_command(ps2dev, NULL, priv->addr_command))
+		return -1;
+
+	for (i = 12; i >= 0; i -= 4) {
+		nibble = (addr >> i) & 0xf;
+		if (alps_command_mode_send_nibble(psmouse, nibble))
+			return -1;
+	}
+
+	return 0;
+}
+
+static int __alps_command_mode_read_reg(struct psmouse *psmouse, int addr)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4];
+
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+		return -1;
+
+	/*
+	 * The address being read is returned in the first two bytes
+	 * of the result. Check that this address matches the expected
+	 * address.
+	 */
+	if (addr != ((param[0] << 8) | param[1]))
+		return -1;
+
+	return param[2];
+}
+
+static int alps_command_mode_read_reg(struct psmouse *psmouse, int addr)
+{
+	if (alps_command_mode_set_addr(psmouse, addr))
+		return -1;
+	return __alps_command_mode_read_reg(psmouse, addr);
+}
+
+static int __alps_command_mode_write_reg(struct psmouse *psmouse, u8 value)
+{
+	if (alps_command_mode_send_nibble(psmouse, (value >> 4) & 0xf))
+		return -1;
+	if (alps_command_mode_send_nibble(psmouse, value & 0xf))
+		return -1;
+	return 0;
+}
+
+static int alps_command_mode_write_reg(struct psmouse *psmouse, int addr,
+				       u8 value)
+{
+	if (alps_command_mode_set_addr(psmouse, addr))
+		return -1;
+	return __alps_command_mode_write_reg(psmouse, value);
+}
+
+static int alps_rpt_cmd(struct psmouse *psmouse, int init_command,
+			int repeated_command, unsigned char *param)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+	param[0] = 0;
+	if (init_command && ps2_command(ps2dev, param, init_command))
+		return -EIO;
+
+	if (ps2_command(ps2dev,  NULL, repeated_command) ||
+	    ps2_command(ps2dev,  NULL, repeated_command) ||
+	    ps2_command(ps2dev,  NULL, repeated_command))
+		return -EIO;
+
+	param[0] = param[1] = param[2] = 0xff;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+		return -EIO;
+
+	psmouse_dbg(psmouse, "%2.2X report: %3ph\n",
+		    repeated_command, param);
+	return 0;
+}
+
+static bool alps_check_valid_firmware_id(unsigned char id[])
+{
+	if (id[0] == 0x73)
+		return true;
+
+	if (id[0] == 0x88 &&
+	    (id[1] == 0x07 ||
+	     id[1] == 0x08 ||
+	     (id[1] & 0xf0) == 0xb0 ||
+	     (id[1] & 0xf0) == 0xc0)) {
+		return true;
+	}
+
+	return false;
+}
+
+static int alps_enter_command_mode(struct psmouse *psmouse)
+{
+	unsigned char param[4];
+
+	if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_RESET_WRAP, param)) {
+		psmouse_err(psmouse, "failed to enter command mode\n");
+		return -1;
+	}
+
+	if (!alps_check_valid_firmware_id(param)) {
+		psmouse_dbg(psmouse,
+			    "unknown response while entering command mode\n");
+		return -1;
+	}
+	return 0;
+}
+
+static inline int alps_exit_command_mode(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM))
+		return -1;
+	return 0;
+}
+
+/*
+ * For DualPoint devices select the device that should respond to
+ * subsequent commands. It looks like glidepad is behind stickpointer,
+ * I'd thought it would be other way around...
+ */
+static int alps_passthrough_mode_v2(struct psmouse *psmouse, bool enable)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11;
+
+	if (ps2_command(ps2dev, NULL, cmd) ||
+	    ps2_command(ps2dev, NULL, cmd) ||
+	    ps2_command(ps2dev, NULL, cmd) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE))
+		return -1;
+
+	/* we may get 3 more bytes, just ignore them */
+	ps2_drain(ps2dev, 3, 100);
+
+	return 0;
+}
+
+static int alps_absolute_mode_v1_v2(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+	/* Try ALPS magic knock - 4 disable before enable */
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE))
+		return -1;
+
+	/*
+	 * Switch mouse to poll (remote) mode so motion data will not
+	 * get in our way
+	 */
+	return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL);
+}
+
+static int alps_monitor_mode_send_word(struct psmouse *psmouse, u16 word)
+{
+	int i, nibble;
+
+	/*
+	 * b0-b11 are valid bits, send sequence is inverse.
+	 * e.g. when word = 0x0123, nibble send sequence is 3, 2, 1
+	 */
+	for (i = 0; i <= 8; i += 4) {
+		nibble = (word >> i) & 0xf;
+		if (alps_command_mode_send_nibble(psmouse, nibble))
+			return -1;
+	}
+
+	return 0;
+}
+
+static int alps_monitor_mode_write_reg(struct psmouse *psmouse,
+				       u16 addr, u16 value)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+	/* 0x0A0 is the command to write the word */
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE) ||
+	    alps_monitor_mode_send_word(psmouse, 0x0A0) ||
+	    alps_monitor_mode_send_word(psmouse, addr) ||
+	    alps_monitor_mode_send_word(psmouse, value) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE))
+		return -1;
+
+	return 0;
+}
+
+static int alps_monitor_mode(struct psmouse *psmouse, bool enable)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+	if (enable) {
+		/* EC E9 F5 F5 E7 E6 E7 E9 to enter monitor mode */
+		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_GETINFO) ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_GETINFO))
+			return -1;
+	} else {
+		/* EC to exit monitor mode */
+		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP))
+			return -1;
+	}
+
+	return 0;
+}
+
+static int alps_absolute_mode_v6(struct psmouse *psmouse)
+{
+	u16 reg_val = 0x181;
+	int ret = -1;
+
+	/* enter monitor mode, to write the register */
+	if (alps_monitor_mode(psmouse, true))
+		return -1;
+
+	ret = alps_monitor_mode_write_reg(psmouse, 0x000, reg_val);
+
+	if (alps_monitor_mode(psmouse, false))
+		ret = -1;
+
+	return ret;
+}
+
+static int alps_get_status(struct psmouse *psmouse, char *param)
+{
+	/* Get status: 0xF5 0xF5 0xF5 0xE9 */
+	if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_DISABLE, param))
+		return -1;
+
+	return 0;
+}
+
+/*
+ * Turn touchpad tapping on or off. The sequences are:
+ * 0xE9 0xF5 0xF5 0xF3 0x0A to enable,
+ * 0xE9 0xF5 0xF5 0xE8 0x00 to disable.
+ * My guess that 0xE9 (GetInfo) is here as a sync point.
+ * For models that also have stickpointer (DualPoints) its tapping
+ * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but
+ * we don't fiddle with it.
+ */
+static int alps_tap_mode(struct psmouse *psmouse, int enable)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES;
+	unsigned char tap_arg = enable ? 0x0A : 0x00;
+	unsigned char param[4];
+
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, &tap_arg, cmd))
+		return -1;
+
+	if (alps_get_status(psmouse, param))
+		return -1;
+
+	return 0;
+}
+
+/*
+ * alps_poll() - poll the touchpad for current motion packet.
+ * Used in resync.
+ */
+static int alps_poll(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char buf[sizeof(psmouse->packet)];
+	bool poll_failed;
+
+	if (priv->flags & ALPS_PASS)
+		alps_passthrough_mode_v2(psmouse, true);
+
+	poll_failed = ps2_command(&psmouse->ps2dev, buf,
+				  PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0;
+
+	if (priv->flags & ALPS_PASS)
+		alps_passthrough_mode_v2(psmouse, false);
+
+	if (poll_failed || (buf[0] & priv->mask0) != priv->byte0)
+		return -1;
+
+	if ((psmouse->badbyte & 0xc8) == 0x08) {
+/*
+ * Poll the track stick ...
+ */
+		if (ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (3 << 8)))
+			return -1;
+	}
+
+	memcpy(psmouse->packet, buf, sizeof(buf));
+	return 0;
+}
+
+static int alps_hw_init_v1_v2(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+
+	if ((priv->flags & ALPS_PASS) &&
+	    alps_passthrough_mode_v2(psmouse, true)) {
+		return -1;
+	}
+
+	if (alps_tap_mode(psmouse, true)) {
+		psmouse_warn(psmouse, "Failed to enable hardware tapping\n");
+		return -1;
+	}
+
+	if (alps_absolute_mode_v1_v2(psmouse)) {
+		psmouse_err(psmouse, "Failed to enable absolute mode\n");
+		return -1;
+	}
+
+	if ((priv->flags & ALPS_PASS) &&
+	    alps_passthrough_mode_v2(psmouse, false)) {
+		return -1;
+	}
+
+	/* ALPS needs stream mode, otherwise it won't report any data */
+	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) {
+		psmouse_err(psmouse, "Failed to enable stream mode\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int alps_hw_init_v6(struct psmouse *psmouse)
+{
+	unsigned char param[2] = {0xC8, 0x14};
+
+	/* Enter passthrough mode to let trackpoint enter 6byte raw mode */
+	if (alps_passthrough_mode_v2(psmouse, true))
+		return -1;
+
+	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+	    ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+	    ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+	    ps2_command(&psmouse->ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
+	    ps2_command(&psmouse->ps2dev, &param[1], PSMOUSE_CMD_SETRATE))
+		return -1;
+
+	if (alps_passthrough_mode_v2(psmouse, false))
+		return -1;
+
+	if (alps_absolute_mode_v6(psmouse)) {
+		psmouse_err(psmouse, "Failed to enable absolute mode\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Enable or disable passthrough mode to the trackstick.
+ */
+static int alps_passthrough_mode_v3(struct psmouse *psmouse,
+				    int reg_base, bool enable)
+{
+	int reg_val, ret = -1;
+
+	if (alps_enter_command_mode(psmouse))
+		return -1;
+
+	reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x0008);
+	if (reg_val == -1)
+		goto error;
+
+	if (enable)
+		reg_val |= 0x01;
+	else
+		reg_val &= ~0x01;
+
+	ret = __alps_command_mode_write_reg(psmouse, reg_val);
+
+error:
+	if (alps_exit_command_mode(psmouse))
+		ret = -1;
+	return ret;
+}
+
+/* Must be in command mode when calling this function */
+static int alps_absolute_mode_v3(struct psmouse *psmouse)
+{
+	int reg_val;
+
+	reg_val = alps_command_mode_read_reg(psmouse, 0x0004);
+	if (reg_val == -1)
+		return -1;
+
+	reg_val |= 0x06;
+	if (__alps_command_mode_write_reg(psmouse, reg_val))
+		return -1;
+
+	return 0;
+}
+
+static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base)
+{
+	int ret = -EIO, reg_val;
+
+	if (alps_enter_command_mode(psmouse))
+		goto error;
+
+	reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x08);
+	if (reg_val == -1)
+		goto error;
+
+	/* bit 7: trackstick is present */
+	ret = reg_val & 0x80 ? 0 : -ENODEV;
+
+error:
+	alps_exit_command_mode(psmouse);
+	return ret;
+}
+
+static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	int ret = 0;
+	unsigned char param[4];
+
+	if (alps_passthrough_mode_v3(psmouse, reg_base, true))
+		return -EIO;
+
+	/*
+	 * E7 report for the trackstick
+	 *
+	 * There have been reports of failures to seem to trace back
+	 * to the above trackstick check failing. When these occur
+	 * this E7 report fails, so when that happens we continue
+	 * with the assumption that there isn't a trackstick after
+	 * all.
+	 */
+	if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_SETSCALE21, param)) {
+		psmouse_warn(psmouse, "Failed to initialize trackstick (E7 report failed)\n");
+		ret = -ENODEV;
+	} else {
+		psmouse_dbg(psmouse, "trackstick E7 report: %3ph\n", param);
+
+		/*
+		 * Not sure what this does, but it is absolutely
+		 * essential. Without it, the touchpad does not
+		 * work at all and the trackstick just emits normal
+		 * PS/2 packets.
+		 */
+		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+		    alps_command_mode_send_nibble(psmouse, 0x9) ||
+		    alps_command_mode_send_nibble(psmouse, 0x4)) {
+			psmouse_err(psmouse,
+				    "Error sending magic E6 sequence\n");
+			ret = -EIO;
+			goto error;
+		}
+
+		/*
+		 * This ensures the trackstick packets are in the format
+		 * supported by this driver. If bit 1 isn't set the packet
+		 * format is different.
+		 */
+		if (alps_enter_command_mode(psmouse) ||
+		    alps_command_mode_write_reg(psmouse,
+						reg_base + 0x08, 0x82) ||
+		    alps_exit_command_mode(psmouse))
+			ret = -EIO;
+	}
+
+error:
+	if (alps_passthrough_mode_v3(psmouse, reg_base, false))
+		ret = -EIO;
+
+	return ret;
+}
+
+static int alps_hw_init_v3(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	int reg_val;
+	unsigned char param[4];
+
+	reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE);
+	if (reg_val == -EIO)
+		goto error;
+
+	if (reg_val == 0 &&
+	    alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO)
+		goto error;
+
+	if (alps_enter_command_mode(psmouse) ||
+	    alps_absolute_mode_v3(psmouse)) {
+		psmouse_err(psmouse, "Failed to enter absolute mode\n");
+		goto error;
+	}
+
+	reg_val = alps_command_mode_read_reg(psmouse, 0x0006);
+	if (reg_val == -1)
+		goto error;
+	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01))
+		goto error;
+
+	reg_val = alps_command_mode_read_reg(psmouse, 0x0007);
+	if (reg_val == -1)
+		goto error;
+	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01))
+		goto error;
+
+	if (alps_command_mode_read_reg(psmouse, 0x0144) == -1)
+		goto error;
+	if (__alps_command_mode_write_reg(psmouse, 0x04))
+		goto error;
+
+	if (alps_command_mode_read_reg(psmouse, 0x0159) == -1)
+		goto error;
+	if (__alps_command_mode_write_reg(psmouse, 0x03))
+		goto error;
+
+	if (alps_command_mode_read_reg(psmouse, 0x0163) == -1)
+		goto error;
+	if (alps_command_mode_write_reg(psmouse, 0x0163, 0x03))
+		goto error;
+
+	if (alps_command_mode_read_reg(psmouse, 0x0162) == -1)
+		goto error;
+	if (alps_command_mode_write_reg(psmouse, 0x0162, 0x04))
+		goto error;
+
+	alps_exit_command_mode(psmouse);
+
+	/* Set rate and enable data reporting */
+	param[0] = 0x64;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
+		psmouse_err(psmouse, "Failed to enable data reporting\n");
+		return -1;
+	}
+
+	return 0;
+
+error:
+	/*
+	 * Leaving the touchpad in command mode will essentially render
+	 * it unusable until the machine reboots, so exit it here just
+	 * to be safe
+	 */
+	alps_exit_command_mode(psmouse);
+	return -1;
+}
+
+static int alps_get_v3_v7_resolution(struct psmouse *psmouse, int reg_pitch)
+{
+	int reg, x_pitch, y_pitch, x_electrode, y_electrode, x_phys, y_phys;
+	struct alps_data *priv = psmouse->private;
+
+	reg = alps_command_mode_read_reg(psmouse, reg_pitch);
+	if (reg < 0)
+		return reg;
+
+	x_pitch = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */
+	x_pitch = 50 + 2 * x_pitch; /* In 0.1 mm units */
+
+	y_pitch = (char)reg >> 4; /* sign extend upper 4 bits */
+	y_pitch = 36 + 2 * y_pitch; /* In 0.1 mm units */
+
+	reg = alps_command_mode_read_reg(psmouse, reg_pitch + 1);
+	if (reg < 0)
+		return reg;
+
+	x_electrode = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */
+	x_electrode = 17 + x_electrode;
+
+	y_electrode = (char)reg >> 4; /* sign extend upper 4 bits */
+	y_electrode = 13 + y_electrode;
+
+	x_phys = x_pitch * (x_electrode - 1); /* In 0.1 mm units */
+	y_phys = y_pitch * (y_electrode - 1); /* In 0.1 mm units */
+
+	priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */
+	priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */
+
+	psmouse_dbg(psmouse,
+		    "pitch %dx%d num-electrodes %dx%d physical size %dx%d mm res %dx%d\n",
+		    x_pitch, y_pitch, x_electrode, y_electrode,
+		    x_phys / 10, y_phys / 10, priv->x_res, priv->y_res);
+
+	return 0;
+}
+
+static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	int reg_val, ret = -1;
+
+	if (priv->flags & ALPS_DUALPOINT) {
+		reg_val = alps_setup_trackstick_v3(psmouse,
+						   ALPS_REG_BASE_RUSHMORE);
+		if (reg_val == -EIO)
+			goto error;
+	}
+
+	if (alps_enter_command_mode(psmouse) ||
+	    alps_command_mode_read_reg(psmouse, 0xc2d9) == -1 ||
+	    alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00))
+		goto error;
+
+	if (alps_get_v3_v7_resolution(psmouse, 0xc2da))
+		goto error;
+
+	reg_val = alps_command_mode_read_reg(psmouse, 0xc2c6);
+	if (reg_val == -1)
+		goto error;
+	if (__alps_command_mode_write_reg(psmouse, reg_val & 0xfd))
+		goto error;
+
+	if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
+		goto error;
+
+	/* enter absolute mode */
+	reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4);
+	if (reg_val == -1)
+		goto error;
+	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02))
+		goto error;
+
+	alps_exit_command_mode(psmouse);
+	return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+error:
+	alps_exit_command_mode(psmouse);
+	return ret;
+}
+
+/* Must be in command mode when calling this function */
+static int alps_absolute_mode_v4(struct psmouse *psmouse)
+{
+	int reg_val;
+
+	reg_val = alps_command_mode_read_reg(psmouse, 0x0004);
+	if (reg_val == -1)
+		return -1;
+
+	reg_val |= 0x02;
+	if (__alps_command_mode_write_reg(psmouse, reg_val))
+		return -1;
+
+	return 0;
+}
+
+static int alps_hw_init_v4(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4];
+
+	if (alps_enter_command_mode(psmouse))
+		goto error;
+
+	if (alps_absolute_mode_v4(psmouse)) {
+		psmouse_err(psmouse, "Failed to enter absolute mode\n");
+		goto error;
+	}
+
+	if (alps_command_mode_write_reg(psmouse, 0x0007, 0x8c))
+		goto error;
+
+	if (alps_command_mode_write_reg(psmouse, 0x0149, 0x03))
+		goto error;
+
+	if (alps_command_mode_write_reg(psmouse, 0x0160, 0x03))
+		goto error;
+
+	if (alps_command_mode_write_reg(psmouse, 0x017f, 0x15))
+		goto error;
+
+	if (alps_command_mode_write_reg(psmouse, 0x0151, 0x01))
+		goto error;
+
+	if (alps_command_mode_write_reg(psmouse, 0x0168, 0x03))
+		goto error;
+
+	if (alps_command_mode_write_reg(psmouse, 0x014a, 0x03))
+		goto error;
+
+	if (alps_command_mode_write_reg(psmouse, 0x0161, 0x03))
+		goto error;
+
+	alps_exit_command_mode(psmouse);
+
+	/*
+	 * This sequence changes the output from a 9-byte to an
+	 * 8-byte format. All the same data seems to be present,
+	 * just in a more compact format.
+	 */
+	param[0] = 0xc8;
+	param[1] = 0x64;
+	param[2] = 0x50;
+	if (ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
+	    ps2_command(ps2dev, &param[1], PSMOUSE_CMD_SETRATE) ||
+	    ps2_command(ps2dev, &param[2], PSMOUSE_CMD_SETRATE) ||
+	    ps2_command(ps2dev, param, PSMOUSE_CMD_GETID))
+		return -1;
+
+	/* Set rate and enable data reporting */
+	param[0] = 0x64;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
+		psmouse_err(psmouse, "Failed to enable data reporting\n");
+		return -1;
+	}
+
+	return 0;
+
+error:
+	/*
+	 * Leaving the touchpad in command mode will essentially render
+	 * it unusable until the machine reboots, so exit it here just
+	 * to be safe
+	 */
+	alps_exit_command_mode(psmouse);
+	return -1;
+}
+
+static int alps_get_otp_values_ss4_v2(struct psmouse *psmouse,
+				      unsigned char index, unsigned char otp[])
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+	switch (index) {
+	case 0:
+		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)  ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)  ||
+		    ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO))
+			return -1;
+
+		break;
+
+	case 1:
+		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL)  ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL)  ||
+		    ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO))
+			return -1;
+
+		break;
+	}
+
+	return 0;
+}
+
+static int alps_update_device_area_ss4_v2(unsigned char otp[][4],
+					  struct alps_data *priv)
+{
+	int num_x_electrode;
+	int num_y_electrode;
+	int x_pitch, y_pitch, x_phys, y_phys;
+
+	num_x_electrode = SS4_NUMSENSOR_XOFFSET + (otp[1][0] & 0x0F);
+	num_y_electrode = SS4_NUMSENSOR_YOFFSET + ((otp[1][0] >> 4) & 0x0F);
+
+	priv->x_max = (num_x_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
+	priv->y_max = (num_y_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
+
+	x_pitch = ((otp[1][2] >> 2) & 0x07) + SS4_MIN_PITCH_MM;
+	y_pitch = ((otp[1][2] >> 5) & 0x07) + SS4_MIN_PITCH_MM;
+
+	x_phys = x_pitch * (num_x_electrode - 1); /* In 0.1 mm units */
+	y_phys = y_pitch * (num_y_electrode - 1); /* In 0.1 mm units */
+
+	priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */
+	priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */
+
+	return 0;
+}
+
+static int alps_update_btn_info_ss4_v2(unsigned char otp[][4],
+				       struct alps_data *priv)
+{
+	unsigned char is_btnless;
+
+	is_btnless = (otp[1][1] >> 3) & 0x01;
+
+	if (is_btnless)
+		priv->flags |= ALPS_BUTTONPAD;
+
+	return 0;
+}
+
+static int alps_set_defaults_ss4_v2(struct psmouse *psmouse,
+				    struct alps_data *priv)
+{
+	unsigned char otp[2][4];
+
+	memset(otp, 0, sizeof(otp));
+
+	if (alps_get_otp_values_ss4_v2(psmouse, 0, &otp[0][0]) ||
+	    alps_get_otp_values_ss4_v2(psmouse, 1, &otp[1][0]))
+		return -1;
+
+	alps_update_device_area_ss4_v2(otp, priv);
+
+	alps_update_btn_info_ss4_v2(otp, priv);
+
+	return 0;
+}
+
+static int alps_dolphin_get_device_area(struct psmouse *psmouse,
+					struct alps_data *priv)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4] = {0};
+	int num_x_electrode, num_y_electrode;
+
+	if (alps_enter_command_mode(psmouse))
+		return -1;
+
+	param[0] = 0x0a;
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) ||
+	    ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
+	    ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE))
+		return -1;
+
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+		return -1;
+
+	/*
+	 * Dolphin's sensor line number is not fixed. It can be calculated
+	 * by adding the device's register value with DOLPHIN_PROFILE_X/YOFFSET.
+	 * Further more, we can get device's x_max and y_max by multiplying
+	 * sensor line number with DOLPHIN_COUNT_PER_ELECTRODE.
+	 *
+	 * e.g. When we get register's sensor_x = 11 & sensor_y = 8,
+	 *	real sensor line number X = 11 + 8 = 19, and
+	 *	real sensor line number Y = 8 + 1 = 9.
+	 *	So, x_max = (19 - 1) * 64 = 1152, and
+	 *	    y_max = (9 - 1) * 64 = 512.
+	 */
+	num_x_electrode = DOLPHIN_PROFILE_XOFFSET + (param[2] & 0x0F);
+	num_y_electrode = DOLPHIN_PROFILE_YOFFSET + ((param[2] >> 4) & 0x0F);
+	priv->x_bits = num_x_electrode;
+	priv->y_bits = num_y_electrode;
+	priv->x_max = (num_x_electrode - 1) * DOLPHIN_COUNT_PER_ELECTRODE;
+	priv->y_max = (num_y_electrode - 1) * DOLPHIN_COUNT_PER_ELECTRODE;
+
+	if (alps_exit_command_mode(psmouse))
+		return -1;
+
+	return 0;
+}
+
+static int alps_hw_init_dolphin_v1(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[2];
+
+	/* This is dolphin "v1" as empirically defined by florin9doi */
+	param[0] = 0x64;
+	param[1] = 0x28;
+
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+	    ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
+	    ps2_command(ps2dev, &param[1], PSMOUSE_CMD_SETRATE))
+		return -1;
+
+	return 0;
+}
+
+static int alps_hw_init_v7(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	int reg_val, ret = -1;
+
+	if (alps_enter_command_mode(psmouse) ||
+	    alps_command_mode_read_reg(psmouse, 0xc2d9) == -1)
+		goto error;
+
+	if (alps_get_v3_v7_resolution(psmouse, 0xc397))
+		goto error;
+
+	if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
+		goto error;
+
+	reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4);
+	if (reg_val == -1)
+		goto error;
+	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02))
+		goto error;
+
+	alps_exit_command_mode(psmouse);
+	return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+error:
+	alps_exit_command_mode(psmouse);
+	return ret;
+}
+
+static int alps_hw_init_ss4_v2(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	char param[2] = {0x64, 0x28};
+	int ret = -1;
+
+	/* enter absolute mode */
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+	    ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
+	    ps2_command(ps2dev, &param[1], PSMOUSE_CMD_SETRATE)) {
+		goto error;
+	}
+
+	/* T.B.D. Decread noise packet number, delete in the future */
+	if (alps_exit_command_mode(psmouse) ||
+	    alps_enter_command_mode(psmouse) ||
+	    alps_command_mode_write_reg(psmouse, 0x001D, 0x20)) {
+		goto error;
+	}
+	alps_exit_command_mode(psmouse);
+
+	return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+error:
+	alps_exit_command_mode(psmouse);
+	return ret;
+}
+
+static int alps_set_protocol(struct psmouse *psmouse,
+			     struct alps_data *priv,
+			     const struct alps_protocol_info *protocol)
+{
+	psmouse->private = priv;
+
+	setup_timer(&priv->timer, alps_flush_packet, (unsigned long)psmouse);
+
+	priv->proto_version = protocol->version;
+	priv->byte0 = protocol->byte0;
+	priv->mask0 = protocol->mask0;
+	priv->flags = protocol->flags;
+
+	priv->x_max = 2000;
+	priv->y_max = 1400;
+	priv->x_bits = 15;
+	priv->y_bits = 11;
+
+	switch (priv->proto_version) {
+	case ALPS_PROTO_V1:
+	case ALPS_PROTO_V2:
+		priv->hw_init = alps_hw_init_v1_v2;
+		priv->process_packet = alps_process_packet_v1_v2;
+		priv->set_abs_params = alps_set_abs_params_st;
+		priv->x_max = 1023;
+		priv->y_max = 767;
+		if (dmi_check_system(alps_dmi_has_separate_stick_buttons))
+			priv->flags |= ALPS_STICK_BITS;
+		break;
+
+	case ALPS_PROTO_V3:
+		priv->hw_init = alps_hw_init_v3;
+		priv->process_packet = alps_process_packet_v3;
+		priv->set_abs_params = alps_set_abs_params_semi_mt;
+		priv->decode_fields = alps_decode_pinnacle;
+		priv->nibble_commands = alps_v3_nibble_commands;
+		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+		break;
+
+	case ALPS_PROTO_V3_RUSHMORE:
+		priv->hw_init = alps_hw_init_rushmore_v3;
+		priv->process_packet = alps_process_packet_v3;
+		priv->set_abs_params = alps_set_abs_params_semi_mt;
+		priv->decode_fields = alps_decode_rushmore;
+		priv->nibble_commands = alps_v3_nibble_commands;
+		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+		priv->x_bits = 16;
+		priv->y_bits = 12;
+
+		if (alps_probe_trackstick_v3(psmouse,
+					     ALPS_REG_BASE_RUSHMORE) < 0)
+			priv->flags &= ~ALPS_DUALPOINT;
+
+		break;
+
+	case ALPS_PROTO_V4:
+		priv->hw_init = alps_hw_init_v4;
+		priv->process_packet = alps_process_packet_v4;
+		priv->set_abs_params = alps_set_abs_params_semi_mt;
+		priv->nibble_commands = alps_v4_nibble_commands;
+		priv->addr_command = PSMOUSE_CMD_DISABLE;
+		break;
+
+	case ALPS_PROTO_V5:
+		priv->hw_init = alps_hw_init_dolphin_v1;
+		priv->process_packet = alps_process_touchpad_packet_v3_v5;
+		priv->decode_fields = alps_decode_dolphin;
+		priv->set_abs_params = alps_set_abs_params_semi_mt;
+		priv->nibble_commands = alps_v3_nibble_commands;
+		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+		priv->x_bits = 23;
+		priv->y_bits = 12;
+
+		if (alps_dolphin_get_device_area(psmouse, priv))
+			return -EIO;
+
+		break;
+
+	case ALPS_PROTO_V6:
+		priv->hw_init = alps_hw_init_v6;
+		priv->process_packet = alps_process_packet_v6;
+		priv->set_abs_params = alps_set_abs_params_st;
+		priv->nibble_commands = alps_v6_nibble_commands;
+		priv->x_max = 2047;
+		priv->y_max = 1535;
+		break;
+
+	case ALPS_PROTO_V7:
+		priv->hw_init = alps_hw_init_v7;
+		priv->process_packet = alps_process_packet_v7;
+		priv->decode_fields = alps_decode_packet_v7;
+		priv->set_abs_params = alps_set_abs_params_v7;
+		priv->nibble_commands = alps_v3_nibble_commands;
+		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+		priv->x_max = 0xfff;
+		priv->y_max = 0x7ff;
+
+		if (priv->fw_ver[1] != 0xba)
+			priv->flags |= ALPS_BUTTONPAD;
+
+		break;
+
+	case ALPS_PROTO_V8:
+		priv->hw_init = alps_hw_init_ss4_v2;
+		priv->process_packet = alps_process_packet_ss4_v2;
+		priv->decode_fields = alps_decode_ss4_v2;
+		priv->set_abs_params = alps_set_abs_params_ss4_v2;
+		priv->nibble_commands = alps_v3_nibble_commands;
+		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+
+		if (alps_set_defaults_ss4_v2(psmouse, priv))
+			return -EIO;
+
+		break;
+	}
+
+	return 0;
+}
+
+static const struct alps_protocol_info *alps_match_table(unsigned char *e7,
+							 unsigned char *ec)
+{
+	const struct alps_model_info *model;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) {
+		model = &alps_model_data[i];
+
+		if (!memcmp(e7, model->signature, sizeof(model->signature)) &&
+		    (!model->command_mode_resp ||
+		     model->command_mode_resp == ec[2])) {
+
+			return &model->protocol_info;
+		}
+	}
+
+	return NULL;
+}
+
+static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
+{
+	const struct alps_protocol_info *protocol;
+	unsigned char e6[4], e7[4], ec[4];
+	int error;
+
+	/*
+	 * First try "E6 report".
+	 * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed.
+	 * The bits 0-2 of the first byte will be 1s if some buttons are
+	 * pressed.
+	 */
+	if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES,
+			 PSMOUSE_CMD_SETSCALE11, e6))
+		return -EIO;
+
+	if ((e6[0] & 0xf8) != 0 || e6[1] != 0 || (e6[2] != 10 && e6[2] != 100))
+		return -EINVAL;
+
+	/*
+	 * Now get the "E7" and "EC" reports.  These will uniquely identify
+	 * most ALPS touchpads.
+	 */
+	if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES,
+			 PSMOUSE_CMD_SETSCALE21, e7) ||
+	    alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES,
+			 PSMOUSE_CMD_RESET_WRAP, ec) ||
+	    alps_exit_command_mode(psmouse))
+		return -EIO;
+
+	protocol = alps_match_table(e7, ec);
+	if (!protocol) {
+		if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
+			   ec[0] == 0x73 && (ec[1] == 0x01 || ec[1] == 0x02)) {
+			protocol = &alps_v5_protocol_data;
+		} else if (ec[0] == 0x88 &&
+			   ((ec[1] & 0xf0) == 0xb0 || (ec[1] & 0xf0) == 0xc0)) {
+			protocol = &alps_v7_protocol_data;
+		} else if (ec[0] == 0x88 && ec[1] == 0x08) {
+			protocol = &alps_v3_rushmore_data;
+		} else if (ec[0] == 0x88 && ec[1] == 0x07 &&
+			   ec[2] >= 0x90 && ec[2] <= 0x9d) {
+			protocol = &alps_v3_protocol_data;
+		} else if (e7[0] == 0x73 && e7[1] == 0x03 &&
+			   e7[2] == 0x14 && ec[1] == 0x02) {
+			protocol = &alps_v8_protocol_data;
+		} else {
+			psmouse_dbg(psmouse,
+				    "Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
+			return -EINVAL;
+		}
+	}
+
+	if (priv) {
+		/* Save the Firmware version */
+		memcpy(priv->fw_ver, ec, 3);
+		error = alps_set_protocol(psmouse, priv, protocol);
+		if (error)
+			return error;
+	}
+
+	return 0;
+}
+
+static int alps_reconnect(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+
+	psmouse_reset(psmouse);
+
+	if (alps_identify(psmouse, priv) < 0)
+		return -1;
+
+	return priv->hw_init(psmouse);
+}
+
+static void alps_disconnect(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+
+	psmouse_reset(psmouse);
+	del_timer_sync(&priv->timer);
+	if (priv->dev2)
+		input_unregister_device(priv->dev2);
+	if (!IS_ERR_OR_NULL(priv->dev3))
+		input_unregister_device(priv->dev3);
+	kfree(priv);
+}
+
+static void alps_set_abs_params_st(struct alps_data *priv,
+				   struct input_dev *dev1)
+{
+	input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0);
+	input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0);
+	input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
+}
+
+static void alps_set_abs_params_mt_common(struct alps_data *priv,
+					  struct input_dev *dev1)
+{
+	input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
+	input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
+
+	input_abs_set_res(dev1, ABS_MT_POSITION_X, priv->x_res);
+	input_abs_set_res(dev1, ABS_MT_POSITION_Y, priv->y_res);
+
+	set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
+	set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
+}
+
+static void alps_set_abs_params_semi_mt(struct alps_data *priv,
+					struct input_dev *dev1)
+{
+	alps_set_abs_params_mt_common(priv, dev1);
+	input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
+
+	input_mt_init_slots(dev1, MAX_TOUCHES,
+			    INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
+				INPUT_MT_SEMI_MT);
+}
+
+static void alps_set_abs_params_v7(struct alps_data *priv,
+				   struct input_dev *dev1)
+{
+	alps_set_abs_params_mt_common(priv, dev1);
+	set_bit(BTN_TOOL_QUINTTAP, dev1->keybit);
+
+	input_mt_init_slots(dev1, MAX_TOUCHES,
+			    INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
+				INPUT_MT_TRACK);
+
+	set_bit(BTN_TOOL_QUINTTAP, dev1->keybit);
+}
+
+static void alps_set_abs_params_ss4_v2(struct alps_data *priv,
+				       struct input_dev *dev1)
+{
+	alps_set_abs_params_mt_common(priv, dev1);
+	input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
+	set_bit(BTN_TOOL_QUINTTAP, dev1->keybit);
+
+	input_mt_init_slots(dev1, MAX_TOUCHES,
+			    INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
+				INPUT_MT_TRACK);
+}
+
+int alps_init(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	struct input_dev *dev1 = psmouse->dev;
+	int error;
+
+	error = priv->hw_init(psmouse);
+	if (error)
+		goto init_fail;
+
+	/*
+	 * Undo part of setup done for us by psmouse core since touchpad
+	 * is not a relative device.
+	 */
+	__clear_bit(EV_REL, dev1->evbit);
+	__clear_bit(REL_X, dev1->relbit);
+	__clear_bit(REL_Y, dev1->relbit);
+
+	/*
+	 * Now set up our capabilities.
+	 */
+	dev1->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY);
+	dev1->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
+	dev1->keybit[BIT_WORD(BTN_TOOL_FINGER)] |= BIT_MASK(BTN_TOOL_FINGER);
+	dev1->keybit[BIT_WORD(BTN_LEFT)] |=
+		BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
+
+	dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
+
+	priv->set_abs_params(priv, dev1);
+
+	if (priv->flags & ALPS_WHEEL) {
+		dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
+		dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL);
+	}
+
+	if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
+		dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD);
+		dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK);
+	}
+
+	if (priv->flags & ALPS_FOUR_BUTTONS) {
+		dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0);
+		dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1);
+		dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2);
+		dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3);
+	} else if (priv->flags & ALPS_BUTTONPAD) {
+		set_bit(INPUT_PROP_BUTTONPAD, dev1->propbit);
+		clear_bit(BTN_RIGHT, dev1->keybit);
+	} else {
+		dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE);
+	}
+
+	if (priv->flags & ALPS_DUALPOINT) {
+		struct input_dev *dev2;
+
+		dev2 = input_allocate_device();
+		if (!dev2) {
+			psmouse_err(psmouse,
+				    "failed to allocate trackstick device\n");
+			error = -ENOMEM;
+			goto init_fail;
+		}
+
+		snprintf(priv->phys2, sizeof(priv->phys2), "%s/input1",
+			 psmouse->ps2dev.serio->phys);
+		dev2->phys = priv->phys2;
+
+		/*
+		 * format of input device name is: "protocol vendor name"
+		 * see function psmouse_switch_protocol() in psmouse-base.c
+		 */
+		dev2->name = "AlpsPS/2 ALPS DualPoint Stick";
+
+		dev2->id.bustype = BUS_I8042;
+		dev2->id.vendor  = 0x0002;
+		dev2->id.product = PSMOUSE_ALPS;
+		dev2->id.version = priv->proto_version;
+		dev2->dev.parent = &psmouse->ps2dev.serio->dev;
+
+		input_set_capability(dev2, EV_REL, REL_X);
+		input_set_capability(dev2, EV_REL, REL_Y);
+		input_set_capability(dev2, EV_KEY, BTN_LEFT);
+		input_set_capability(dev2, EV_KEY, BTN_RIGHT);
+		input_set_capability(dev2, EV_KEY, BTN_MIDDLE);
+
+		__set_bit(INPUT_PROP_POINTER, dev2->propbit);
+		__set_bit(INPUT_PROP_POINTING_STICK, dev2->propbit);
+
+		error = input_register_device(dev2);
+		if (error) {
+			psmouse_err(psmouse,
+				    "failed to register trackstick device: %d\n",
+				    error);
+			input_free_device(dev2);
+			goto init_fail;
+		}
+
+		priv->dev2 = dev2;
+	}
+
+	priv->psmouse = psmouse;
+
+	INIT_DELAYED_WORK(&priv->dev3_register_work,
+			  alps_register_bare_ps2_mouse);
+
+	psmouse->protocol_handler = alps_process_byte;
+	psmouse->poll = alps_poll;
+	psmouse->disconnect = alps_disconnect;
+	psmouse->reconnect = alps_reconnect;
+	psmouse->pktsize = priv->proto_version == ALPS_PROTO_V4 ? 8 : 6;
+
+	/* We are having trouble resyncing ALPS touchpads so disable it for now */
+	psmouse->resync_time = 0;
+
+	/* Allow 2 invalid packets without resetting device */
+	psmouse->resetafter = psmouse->pktsize * 2;
+
+	return 0;
+
+init_fail:
+	psmouse_reset(psmouse);
+	/*
+	 * Even though we did not allocate psmouse->private we do free
+	 * it here.
+	 */
+	kfree(psmouse->private);
+	psmouse->private = NULL;
+	return error;
+}
+
+int alps_detect(struct psmouse *psmouse, bool set_properties)
+{
+	struct alps_data *priv;
+	int error;
+
+	error = alps_identify(psmouse, NULL);
+	if (error)
+		return error;
+
+	/*
+	 * Reset the device to make sure it is fully operational:
+	 * on some laptops, like certain Dell Latitudes, we may
+	 * fail to properly detect presence of trackstick if device
+	 * has not been reset.
+	 */
+	psmouse_reset(psmouse);
+
+	priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	error = alps_identify(psmouse, priv);
+	if (error) {
+		kfree(priv);
+		return error;
+	}
+
+	if (set_properties) {
+		psmouse->vendor = "ALPS";
+		psmouse->name = priv->flags & ALPS_DUALPOINT ?
+				"DualPoint TouchPad" : "GlidePoint";
+		psmouse->model = priv->proto_version;
+	} else {
+		/*
+		 * Destroy alps_data structure we allocated earlier since
+		 * this was just a "trial run". Otherwise we'll keep it
+		 * to be used by alps_init() which has to be called if
+		 * we succeed and set_properties is true.
+		 */
+		kfree(priv);
+		psmouse->private = NULL;
+	}
+
+	return 0;
+}
+
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
new file mode 100644
index 0000000..d37f814
--- /dev/null
+++ b/drivers/input/mouse/alps.h
@@ -0,0 +1,304 @@
+/*
+ * ALPS touchpad PS/2 mouse driver
+ *
+ * Copyright (c) 2003 Peter Osterlund <petero2@telia.com>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _ALPS_H
+#define _ALPS_H
+
+#include <linux/input/mt.h>
+
+#define ALPS_PROTO_V1		0x100
+#define ALPS_PROTO_V2		0x200
+#define ALPS_PROTO_V3		0x300
+#define ALPS_PROTO_V3_RUSHMORE	0x310
+#define ALPS_PROTO_V4		0x400
+#define ALPS_PROTO_V5		0x500
+#define ALPS_PROTO_V6		0x600
+#define ALPS_PROTO_V7		0x700	/* t3btl t4s */
+#define ALPS_PROTO_V8		0x800	/* SS4btl SS4s */
+
+#define MAX_TOUCHES	4
+
+#define DOLPHIN_COUNT_PER_ELECTRODE	64
+#define DOLPHIN_PROFILE_XOFFSET		8	/* x-electrode offset */
+#define DOLPHIN_PROFILE_YOFFSET		1	/* y-electrode offset */
+
+/*
+ * enum SS4_PACKET_ID - defines the packet type for V8
+ * SS4_PACKET_ID_IDLE: There's no finger and no button activity.
+ * SS4_PACKET_ID_ONE: There's one finger on touchpad
+ *  or there's button activities.
+ * SS4_PACKET_ID_TWO: There's two or more fingers on touchpad
+ * SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad
+*/
+enum SS4_PACKET_ID {
+	SS4_PACKET_ID_IDLE = 0,
+	SS4_PACKET_ID_ONE,
+	SS4_PACKET_ID_TWO,
+	SS4_PACKET_ID_MULTI,
+};
+
+#define SS4_COUNT_PER_ELECTRODE		256
+#define SS4_NUMSENSOR_XOFFSET		7
+#define SS4_NUMSENSOR_YOFFSET		7
+#define SS4_MIN_PITCH_MM		50
+
+#define SS4_MASK_NORMAL_BUTTONS		0x07
+
+#define SS4_1F_X_V2(_b)		((_b[0] & 0x0007) |		\
+				 ((_b[1] << 3) & 0x0078) |	\
+				 ((_b[1] << 2) & 0x0380) |	\
+				 ((_b[2] << 5) & 0x1C00)	\
+				)
+
+#define SS4_1F_Y_V2(_b)		(((_b[2]) & 0x000F) |		\
+				 ((_b[3] >> 2) & 0x0030) |	\
+				 ((_b[4] << 6) & 0x03C0) |	\
+				 ((_b[4] << 5) & 0x0C00)	\
+				)
+
+#define SS4_1F_Z_V2(_b)		(((_b[5]) & 0x0F) |		\
+				 ((_b[5] >> 1) & 0x70) |	\
+				 ((_b[4]) & 0x80)		\
+				)
+
+#define SS4_1F_LFB_V2(_b)	(((_b[2] >> 4) & 0x01) == 0x01)
+
+#define SS4_MF_LF_V2(_b, _i)	((_b[1 + (_i) * 3] & 0x0004) == 0x0004)
+
+#define SS4_BTN_V2(_b)		((_b[0] >> 5) & SS4_MASK_NORMAL_BUTTONS)
+
+#define SS4_STD_MF_X_V2(_b, _i)	(((_b[0 + (_i) * 3] << 5) & 0x00E0) |	\
+				 ((_b[1 + _i * 3]  << 5) & 0x1F00)	\
+				)
+
+#define SS4_STD_MF_Y_V2(_b, _i)	(((_b[1 + (_i) * 3] << 3) & 0x0010) |	\
+				 ((_b[2 + (_i) * 3] << 5) & 0x01E0) |	\
+				 ((_b[2 + (_i) * 3] << 4) & 0x0E00)	\
+				)
+
+#define SS4_BTL_MF_X_V2(_b, _i)	(SS4_STD_MF_X_V2(_b, _i) |		\
+				 ((_b[0 + (_i) * 3] >> 3) & 0x0010)	\
+				)
+
+#define SS4_BTL_MF_Y_V2(_b, _i)	(SS4_STD_MF_Y_V2(_b, _i) | \
+				 ((_b[0 + (_i) * 3] >> 3) & 0x0008)	\
+				)
+
+#define SS4_MF_Z_V2(_b, _i)	(((_b[1 + (_i) * 3]) & 0x0001) |	\
+				 ((_b[1 + (_i) * 3] >> 1) & 0x0002)	\
+				)
+
+#define SS4_IS_MF_CONTINUE(_b)	((_b[2] & 0x10) == 0x10)
+#define SS4_IS_5F_DETECTED(_b)	((_b[2] & 0x10) == 0x10)
+
+
+#define SS4_MFPACKET_NO_AX	8160	/* X-Coordinate value */
+#define SS4_MFPACKET_NO_AY	4080	/* Y-Coordinate value */
+#define SS4_MFPACKET_NO_AX_BL	8176	/* Buttonless X-Coordinate value */
+#define SS4_MFPACKET_NO_AY_BL	4088	/* Buttonless Y-Coordinate value */
+
+/*
+ * enum V7_PACKET_ID - defines the packet type for V7
+ * V7_PACKET_ID_IDLE: There's no finger and no button activity.
+ * V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad
+ *  or there's button activities.
+ * V7_PACKET_ID_MULTI: There are at least three non-resting fingers.
+ * V7_PACKET_ID_NEW: The finger position in slot is not continues from
+ *  previous packet.
+*/
+enum V7_PACKET_ID {
+	 V7_PACKET_ID_IDLE,
+	 V7_PACKET_ID_TWO,
+	 V7_PACKET_ID_MULTI,
+	 V7_PACKET_ID_NEW,
+	 V7_PACKET_ID_UNKNOWN,
+};
+
+/**
+ * struct alps_protocol_info - information about protocol used by a device
+ * @version: Indicates V1/V2/V3/...
+ * @byte0: Helps figure out whether a position report packet matches the
+ *   known format for this model.  The first byte of the report, ANDed with
+ *   mask0, should match byte0.
+ * @mask0: The mask used to check the first byte of the report.
+ * @flags: Additional device capabilities (passthrough port, trackstick, etc.).
+ */
+struct alps_protocol_info {
+	u16 version;
+	u8 byte0, mask0;
+	unsigned int flags;
+};
+
+/**
+ * struct alps_model_info - touchpad ID table
+ * @signature: E7 response string to match.
+ * @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response
+ *   (aka command mode response) identifies the firmware minor version.  This
+ *   can be used to distinguish different hardware models which are not
+ *   uniquely identifiable through their E7 responses.
+ * @protocol_info: information about protcol used by the device.
+ *
+ * Many (but not all) ALPS touchpads can be identified by looking at the
+ * values returned in the "E7 report" and/or the "EC report."  This table
+ * lists a number of such touchpads.
+ */
+struct alps_model_info {
+	u8 signature[3];
+	u8 command_mode_resp;
+	struct alps_protocol_info protocol_info;
+};
+
+/**
+ * struct alps_nibble_commands - encodings for register accesses
+ * @command: PS/2 command used for the nibble
+ * @data: Data supplied as an argument to the PS/2 command, if applicable
+ *
+ * The ALPS protocol uses magic sequences to transmit binary data to the
+ * touchpad, as it is generally not OK to send arbitrary bytes out the
+ * PS/2 port.  Each of the sequences in this table sends one nibble of the
+ * register address or (write) data.  Different versions of the ALPS protocol
+ * use slightly different encodings.
+ */
+struct alps_nibble_commands {
+	int command;
+	unsigned char data;
+};
+
+struct alps_bitmap_point {
+	int start_bit;
+	int num_bits;
+};
+
+/**
+ * struct alps_fields - decoded version of the report packet
+ * @x_map: Bitmap of active X positions for MT.
+ * @y_map: Bitmap of active Y positions for MT.
+ * @fingers: Number of fingers for MT.
+ * @pressure: Pressure.
+ * @st: position for ST.
+ * @mt: position for MT.
+ * @first_mp: Packet is the first of a multi-packet report.
+ * @is_mp: Packet is part of a multi-packet report.
+ * @left: Left touchpad button is active.
+ * @right: Right touchpad button is active.
+ * @middle: Middle touchpad button is active.
+ * @ts_left: Left trackstick button is active.
+ * @ts_right: Right trackstick button is active.
+ * @ts_middle: Middle trackstick button is active.
+ */
+struct alps_fields {
+	unsigned int x_map;
+	unsigned int y_map;
+	unsigned int fingers;
+
+	int pressure;
+	struct input_mt_pos st;
+	struct input_mt_pos mt[MAX_TOUCHES];
+
+	unsigned int first_mp:1;
+	unsigned int is_mp:1;
+
+	unsigned int left:1;
+	unsigned int right:1;
+	unsigned int middle:1;
+
+	unsigned int ts_left:1;
+	unsigned int ts_right:1;
+	unsigned int ts_middle:1;
+};
+
+/**
+ * struct alps_data - private data structure for the ALPS driver
+ * @psmouse: Pointer to parent psmouse device
+ * @dev2: Trackstick device (can be NULL).
+ * @dev3: Generic PS/2 mouse (can be NULL, delayed registering).
+ * @phys2: Physical path for the trackstick device.
+ * @phys3: Physical path for the generic PS/2 mouse.
+ * @dev3_register_work: Delayed work for registering PS/2 mouse.
+ * @nibble_commands: Command mapping used for touchpad register accesses.
+ * @addr_command: Command used to tell the touchpad that a register address
+ *   follows.
+ * @proto_version: Indicates V1/V2/V3/...
+ * @byte0: Helps figure out whether a position report packet matches the
+ *   known format for this model.  The first byte of the report, ANDed with
+ *   mask0, should match byte0.
+ * @mask0: The mask used to check the first byte of the report.
+ * @fw_ver: cached copy of firmware version (EC report)
+ * @flags: Additional device capabilities (passthrough port, trackstick, etc.).
+ * @x_max: Largest possible X position value.
+ * @y_max: Largest possible Y position value.
+ * @x_bits: Number of X bits in the MT bitmap.
+ * @y_bits: Number of Y bits in the MT bitmap.
+ * @hw_init: Protocol-specific hardware init function.
+ * @process_packet: Protocol-specific function to process a report packet.
+ * @decode_fields: Protocol-specific function to read packet bitfields.
+ * @set_abs_params: Protocol-specific function to configure the input_dev.
+ * @prev_fin: Finger bit from previous packet.
+ * @multi_packet: Multi-packet data in progress.
+ * @multi_data: Saved multi-packet data.
+ * @f: Decoded packet data fields.
+ * @quirks: Bitmap of ALPS_QUIRK_*.
+ * @timer: Timer for flushing out the final report packet in the stream.
+ */
+struct alps_data {
+	struct psmouse *psmouse;
+	struct input_dev *dev2;
+	struct input_dev *dev3;
+	char phys2[32];
+	char phys3[32];
+	struct delayed_work dev3_register_work;
+
+	/* these are autodetected when the device is identified */
+	const struct alps_nibble_commands *nibble_commands;
+	int addr_command;
+	u16 proto_version;
+	u8 byte0, mask0;
+	u8 fw_ver[3];
+	int flags;
+	int x_max;
+	int y_max;
+	int x_bits;
+	int y_bits;
+	unsigned int x_res;
+	unsigned int y_res;
+
+	int (*hw_init)(struct psmouse *psmouse);
+	void (*process_packet)(struct psmouse *psmouse);
+	int (*decode_fields)(struct alps_fields *f, unsigned char *p,
+			      struct psmouse *psmouse);
+	void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1);
+
+	int prev_fin;
+	int multi_packet;
+	int second_touch;
+	unsigned char multi_data[6];
+	struct alps_fields f;
+	u8 quirks;
+	struct timer_list timer;
+};
+
+#define ALPS_QUIRK_TRACKSTICK_BUTTONS	1 /* trakcstick buttons in trackstick packet */
+
+#ifdef CONFIG_MOUSE_PS2_ALPS
+int alps_detect(struct psmouse *psmouse, bool set_properties);
+int alps_init(struct psmouse *psmouse);
+#else
+inline int alps_detect(struct psmouse *psmouse, bool set_properties)
+{
+	return -ENOSYS;
+}
+inline int alps_init(struct psmouse *psmouse)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_ALPS */
+
+#endif
diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c
new file mode 100644
index 0000000..a7fd8f2
--- /dev/null
+++ b/drivers/input/mouse/amimouse.c
@@ -0,0 +1,149 @@
+/*
+ *  Amiga mouse driver for Linux/m68k
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Michael Rausch		James Banks
+ *	Matther Dillon		David Giller
+ *	Nathan Laredo		Linus Torvalds
+ *	Johan Myreen		Jes Sorensen
+ *	Russell King
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/uaccess.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Amiga mouse driver");
+MODULE_LICENSE("GPL");
+
+static int amimouse_lastx, amimouse_lasty;
+
+static irqreturn_t amimouse_interrupt(int irq, void *data)
+{
+	struct input_dev *dev = data;
+	unsigned short joy0dat, potgor;
+	int nx, ny, dx, dy;
+
+	joy0dat = amiga_custom.joy0dat;
+
+	nx = joy0dat & 0xff;
+	ny = joy0dat >> 8;
+
+	dx = nx - amimouse_lastx;
+	dy = ny - amimouse_lasty;
+
+	if (dx < -127) dx = (256 + nx) - amimouse_lastx;
+	if (dx >  127) dx = (nx - 256) - amimouse_lastx;
+	if (dy < -127) dy = (256 + ny) - amimouse_lasty;
+	if (dy >  127) dy = (ny - 256) - amimouse_lasty;
+
+	amimouse_lastx = nx;
+	amimouse_lasty = ny;
+
+	potgor = amiga_custom.potgor;
+
+	input_report_rel(dev, REL_X, dx);
+	input_report_rel(dev, REL_Y, dy);
+
+	input_report_key(dev, BTN_LEFT,   ciaa.pra & 0x40);
+	input_report_key(dev, BTN_MIDDLE, potgor & 0x0100);
+	input_report_key(dev, BTN_RIGHT,  potgor & 0x0400);
+
+	input_sync(dev);
+
+	return IRQ_HANDLED;
+}
+
+static int amimouse_open(struct input_dev *dev)
+{
+	unsigned short joy0dat;
+	int error;
+
+	joy0dat = amiga_custom.joy0dat;
+
+	amimouse_lastx = joy0dat & 0xff;
+	amimouse_lasty = joy0dat >> 8;
+
+	error = request_irq(IRQ_AMIGA_VERTB, amimouse_interrupt, 0, "amimouse",
+			    dev);
+	if (error)
+		dev_err(&dev->dev, "Can't allocate irq %d\n", IRQ_AMIGA_VERTB);
+
+	return error;
+}
+
+static void amimouse_close(struct input_dev *dev)
+{
+	free_irq(IRQ_AMIGA_VERTB, dev);
+}
+
+static int __init amimouse_probe(struct platform_device *pdev)
+{
+	int err;
+	struct input_dev *dev;
+
+	dev = input_allocate_device();
+	if (!dev)
+		return -ENOMEM;
+
+	dev->name = pdev->name;
+	dev->phys = "amimouse/input0";
+	dev->id.bustype = BUS_AMIGA;
+	dev->id.vendor = 0x0001;
+	dev->id.product = 0x0002;
+	dev->id.version = 0x0100;
+
+	dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+	dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+	dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+		BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+	dev->open = amimouse_open;
+	dev->close = amimouse_close;
+	dev->dev.parent = &pdev->dev;
+
+	err = input_register_device(dev);
+	if (err) {
+		input_free_device(dev);
+		return err;
+	}
+
+	platform_set_drvdata(pdev, dev);
+
+	return 0;
+}
+
+static int __exit amimouse_remove(struct platform_device *pdev)
+{
+	struct input_dev *dev = platform_get_drvdata(pdev);
+
+	input_unregister_device(dev);
+	return 0;
+}
+
+static struct platform_driver amimouse_driver = {
+	.remove = __exit_p(amimouse_remove),
+	.driver   = {
+		.name	= "amiga-mouse",
+	},
+};
+
+module_platform_driver_probe(amimouse_driver, amimouse_probe);
+
+MODULE_ALIAS("platform:amiga-mouse");
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c
new file mode 100644
index 0000000..ef234c9
--- /dev/null
+++ b/drivers/input/mouse/appletouch.c
@@ -0,0 +1,1020 @@
+/*
+ * Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver
+ *
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2005-2008 Johannes Berg (johannes@sipsolutions.net)
+ * Copyright (C) 2005-2008 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2005      Frank Arnold (frank@scirocco-5v-turbo.de)
+ * Copyright (C) 2005      Peter Osterlund (petero2@telia.com)
+ * Copyright (C) 2005      Michael Hanselmann (linux-kernel@hansmi.ch)
+ * Copyright (C) 2006      Nicolas Boichat (nicolas@boichat.ch)
+ * Copyright (C) 2007-2008 Sven Anders (anders@anduras.de)
+ *
+ * Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+
+/*
+ * Note: We try to keep the touchpad aspect ratio while still doing only
+ * simple arithmetics:
+ *	0 <= x <= (xsensors - 1) * xfact
+ *	0 <= y <= (ysensors - 1) * yfact
+ */
+struct atp_info {
+	int xsensors;				/* number of X sensors */
+	int xsensors_17;			/* 17" models have more sensors */
+	int ysensors;				/* number of Y sensors */
+	int xfact;				/* X multiplication factor */
+	int yfact;				/* Y multiplication factor */
+	int datalen;				/* size of USB transfers */
+	void (*callback)(struct urb *);		/* callback function */
+	int fuzz;				/* fuzz touchpad generates */
+};
+
+static void atp_complete_geyser_1_2(struct urb *urb);
+static void atp_complete_geyser_3_4(struct urb *urb);
+
+static const struct atp_info fountain_info = {
+	.xsensors	= 16,
+	.xsensors_17	= 26,
+	.ysensors	= 16,
+	.xfact		= 64,
+	.yfact		= 43,
+	.datalen	= 81,
+	.callback	= atp_complete_geyser_1_2,
+	.fuzz		= 16,
+};
+
+static const struct atp_info geyser1_info = {
+	.xsensors	= 16,
+	.xsensors_17	= 26,
+	.ysensors	= 16,
+	.xfact		= 64,
+	.yfact		= 43,
+	.datalen	= 81,
+	.callback	= atp_complete_geyser_1_2,
+	.fuzz		= 16,
+};
+
+static const struct atp_info geyser2_info = {
+	.xsensors	= 15,
+	.xsensors_17	= 20,
+	.ysensors	= 9,
+	.xfact		= 64,
+	.yfact		= 43,
+	.datalen	= 64,
+	.callback	= atp_complete_geyser_1_2,
+	.fuzz		= 0,
+};
+
+static const struct atp_info geyser3_info = {
+	.xsensors	= 20,
+	.ysensors	= 10,
+	.xfact		= 64,
+	.yfact		= 64,
+	.datalen	= 64,
+	.callback	= atp_complete_geyser_3_4,
+	.fuzz		= 0,
+};
+
+static const struct atp_info geyser4_info = {
+	.xsensors	= 20,
+	.ysensors	= 10,
+	.xfact		= 64,
+	.yfact		= 64,
+	.datalen	= 64,
+	.callback	= atp_complete_geyser_3_4,
+	.fuzz		= 0,
+};
+
+#define ATP_DEVICE(prod, info)					\
+{								\
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |		\
+		       USB_DEVICE_ID_MATCH_INT_CLASS |		\
+		       USB_DEVICE_ID_MATCH_INT_PROTOCOL,	\
+	.idVendor = 0x05ac, /* Apple */				\
+	.idProduct = (prod),					\
+	.bInterfaceClass = 0x03,				\
+	.bInterfaceProtocol = 0x02,				\
+	.driver_info = (unsigned long) &info,			\
+}
+
+/*
+ * Table of devices (Product IDs) that work with this driver.
+ * (The names come from Info.plist in AppleUSBTrackpad.kext,
+ *  According to Info.plist Geyser IV is the same as Geyser III.)
+ */
+
+static struct usb_device_id atp_table[] = {
+	/* PowerBooks Feb 2005, iBooks G4 */
+	ATP_DEVICE(0x020e, fountain_info),	/* FOUNTAIN ANSI */
+	ATP_DEVICE(0x020f, fountain_info),	/* FOUNTAIN ISO */
+	ATP_DEVICE(0x030a, fountain_info),	/* FOUNTAIN TP ONLY */
+	ATP_DEVICE(0x030b, geyser1_info),	/* GEYSER 1 TP ONLY */
+
+	/* PowerBooks Oct 2005 */
+	ATP_DEVICE(0x0214, geyser2_info),	/* GEYSER 2 ANSI */
+	ATP_DEVICE(0x0215, geyser2_info),	/* GEYSER 2 ISO */
+	ATP_DEVICE(0x0216, geyser2_info),	/* GEYSER 2 JIS */
+
+	/* Core Duo MacBook & MacBook Pro */
+	ATP_DEVICE(0x0217, geyser3_info),	/* GEYSER 3 ANSI */
+	ATP_DEVICE(0x0218, geyser3_info),	/* GEYSER 3 ISO */
+	ATP_DEVICE(0x0219, geyser3_info),	/* GEYSER 3 JIS */
+
+	/* Core2 Duo MacBook & MacBook Pro */
+	ATP_DEVICE(0x021a, geyser4_info),	/* GEYSER 4 ANSI */
+	ATP_DEVICE(0x021b, geyser4_info),	/* GEYSER 4 ISO */
+	ATP_DEVICE(0x021c, geyser4_info),	/* GEYSER 4 JIS */
+
+	/* Core2 Duo MacBook3,1 */
+	ATP_DEVICE(0x0229, geyser4_info),	/* GEYSER 4 HF ANSI */
+	ATP_DEVICE(0x022a, geyser4_info),	/* GEYSER 4 HF ISO */
+	ATP_DEVICE(0x022b, geyser4_info),	/* GEYSER 4 HF JIS */
+
+	/* Terminating entry */
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, atp_table);
+
+/* maximum number of sensors */
+#define ATP_XSENSORS	26
+#define ATP_YSENSORS	16
+
+/*
+ * The largest possible bank of sensors with additional buffer of 4 extra values
+ * on either side, for an array of smoothed sensor values.
+ */
+#define ATP_SMOOTHSIZE	34
+
+/* maximum pressure this driver will report */
+#define ATP_PRESSURE	300
+
+/*
+ * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
+ * ignored.
+ */
+#define ATP_THRESHOLD	5
+
+/*
+ * How far we'll bitshift our sensor values before averaging them. Mitigates
+ * rounding errors.
+ */
+#define ATP_SCALE	12
+
+/* Geyser initialization constants */
+#define ATP_GEYSER_MODE_READ_REQUEST_ID		1
+#define ATP_GEYSER_MODE_WRITE_REQUEST_ID	9
+#define ATP_GEYSER_MODE_REQUEST_VALUE		0x300
+#define ATP_GEYSER_MODE_REQUEST_INDEX		0
+#define ATP_GEYSER_MODE_VENDOR_VALUE		0x04
+
+/**
+ * enum atp_status_bits - status bit meanings
+ *
+ * These constants represent the meaning of the status bits.
+ * (only Geyser 3/4)
+ *
+ * @ATP_STATUS_BUTTON: The button was pressed
+ * @ATP_STATUS_BASE_UPDATE: Update of the base values (untouched pad)
+ * @ATP_STATUS_FROM_RESET: Reset previously performed
+ */
+enum atp_status_bits {
+	ATP_STATUS_BUTTON	= BIT(0),
+	ATP_STATUS_BASE_UPDATE	= BIT(2),
+	ATP_STATUS_FROM_RESET	= BIT(4),
+};
+
+/* Structure to hold all of our device specific stuff */
+struct atp {
+	char			phys[64];
+	struct usb_device	*udev;		/* usb device */
+	struct usb_interface	*intf;		/* usb interface */
+	struct urb		*urb;		/* usb request block */
+	u8			*data;		/* transferred data */
+	struct input_dev	*input;		/* input dev */
+	const struct atp_info	*info;		/* touchpad model */
+	bool			open;
+	bool			valid;		/* are the samples valid? */
+	bool			size_detect_done;
+	bool			overflow_warned;
+	int			fingers_old;	/* last reported finger count */
+	int			x_old;		/* last reported x/y, */
+	int			y_old;		/* used for smoothing */
+	signed char		xy_cur[ATP_XSENSORS + ATP_YSENSORS];
+	signed char		xy_old[ATP_XSENSORS + ATP_YSENSORS];
+	int			xy_acc[ATP_XSENSORS + ATP_YSENSORS];
+	int			smooth[ATP_SMOOTHSIZE];
+	int			smooth_tmp[ATP_SMOOTHSIZE];
+	int			idlecount;	/* number of empty packets */
+	struct work_struct	work;
+};
+
+#define dbg_dump(msg, tab) \
+	if (debug > 1) {						\
+		int __i;						\
+		printk(KERN_DEBUG "appletouch: %s", msg);		\
+		for (__i = 0; __i < ATP_XSENSORS + ATP_YSENSORS; __i++)	\
+			printk(" %02x", tab[__i]);			\
+		printk("\n");						\
+	}
+
+#define dprintk(format, a...)						\
+	do {								\
+		if (debug)						\
+			printk(KERN_DEBUG format, ##a);			\
+	} while (0)
+
+MODULE_AUTHOR("Johannes Berg");
+MODULE_AUTHOR("Stelian Pop");
+MODULE_AUTHOR("Frank Arnold");
+MODULE_AUTHOR("Michael Hanselmann");
+MODULE_AUTHOR("Sven Anders");
+MODULE_DESCRIPTION("Apple PowerBook and MacBook USB touchpad driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Make the threshold a module parameter
+ */
+static int threshold = ATP_THRESHOLD;
+module_param(threshold, int, 0644);
+MODULE_PARM_DESC(threshold, "Discard any change in data from a sensor"
+			    " (the trackpad has many of these sensors)"
+			    " less than this value.");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activate debugging output");
+
+/*
+ * By default newer Geyser devices send standard USB HID mouse
+ * packets (Report ID 2). This code changes device mode, so it
+ * sends raw sensor reports (Report ID 5).
+ */
+static int atp_geyser_init(struct atp *dev)
+{
+	struct usb_device *udev = dev->udev;
+	char *data;
+	int size;
+	int i;
+	int ret;
+
+	data = kmalloc(8, GFP_KERNEL);
+	if (!data) {
+		dev_err(&dev->intf->dev, "Out of memory\n");
+		return -ENOMEM;
+	}
+
+	size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			ATP_GEYSER_MODE_READ_REQUEST_ID,
+			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			ATP_GEYSER_MODE_REQUEST_VALUE,
+			ATP_GEYSER_MODE_REQUEST_INDEX, data, 8, 5000);
+
+	if (size != 8) {
+		dprintk("atp_geyser_init: read error\n");
+		for (i = 0; i < 8; i++)
+			dprintk("appletouch[%d]: %d\n", i, data[i]);
+
+		dev_err(&dev->intf->dev, "Failed to read mode from device.\n");
+		ret = -EIO;
+		goto out_free;
+	}
+
+	/* Apply the mode switch */
+	data[0] = ATP_GEYSER_MODE_VENDOR_VALUE;
+
+	size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			ATP_GEYSER_MODE_WRITE_REQUEST_ID,
+			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			ATP_GEYSER_MODE_REQUEST_VALUE,
+			ATP_GEYSER_MODE_REQUEST_INDEX, data, 8, 5000);
+
+	if (size != 8) {
+		dprintk("atp_geyser_init: write error\n");
+		for (i = 0; i < 8; i++)
+			dprintk("appletouch[%d]: %d\n", i, data[i]);
+
+		dev_err(&dev->intf->dev, "Failed to request geyser raw mode\n");
+		ret = -EIO;
+		goto out_free;
+	}
+	ret = 0;
+out_free:
+	kfree(data);
+	return ret;
+}
+
+/*
+ * Reinitialise the device. This usually stops stream of empty packets
+ * coming from it.
+ */
+static void atp_reinit(struct work_struct *work)
+{
+	struct atp *dev = container_of(work, struct atp, work);
+	int retval;
+
+	dprintk("appletouch: putting appletouch to sleep (reinit)\n");
+	atp_geyser_init(dev);
+
+	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&dev->intf->dev,
+			"atp_reinit: usb_submit_urb failed with error %d\n",
+			retval);
+}
+
+static int atp_calculate_abs(struct atp *dev, int offset, int nb_sensors,
+			     int fact, int *z, int *fingers)
+{
+	int i, pass;
+
+	/*
+	 * Use offset to point xy_sensors at the first value in dev->xy_acc
+	 * for whichever dimension we're looking at this particular go-round.
+	 */
+	int *xy_sensors = dev->xy_acc + offset;
+
+	/* values to calculate mean */
+	int pcum = 0, psum = 0;
+	int is_increasing = 0;
+
+	*fingers = 0;
+
+	for (i = 0; i < nb_sensors; i++) {
+		if (xy_sensors[i] < threshold) {
+			if (is_increasing)
+				is_increasing = 0;
+
+		/*
+		 * Makes the finger detection more versatile.  For example,
+		 * two fingers with no gap will be detected.  Also, my
+		 * tests show it less likely to have intermittent loss
+		 * of multiple finger readings while moving around (scrolling).
+		 *
+		 * Changes the multiple finger detection to counting humps on
+		 * sensors (transitions from nonincreasing to increasing)
+		 * instead of counting transitions from low sensors (no
+		 * finger reading) to high sensors (finger above
+		 * sensor)
+		 *
+		 * - Jason Parekh <jasonparekh@gmail.com>
+		 */
+
+		} else if (i < 1 ||
+		    (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
+			(*fingers)++;
+			is_increasing = 1;
+		} else if (i > 0 && (xy_sensors[i - 1] - xy_sensors[i] > threshold)) {
+			is_increasing = 0;
+		}
+	}
+
+	if (*fingers < 1)     /* No need to continue if no fingers are found. */
+		return 0;
+
+	/*
+	 * Use a smoothed version of sensor data for movement calculations, to
+	 * combat noise without needing to rely so heavily on a threshold.
+	 * This improves tracking.
+	 *
+	 * The smoothed array is bigger than the original so that the smoothing
+	 * doesn't result in edge values being truncated.
+	 */
+
+	memset(dev->smooth, 0, 4 * sizeof(dev->smooth[0]));
+	/* Pull base values, scaled up to help avoid truncation errors. */
+	for (i = 0; i < nb_sensors; i++)
+		dev->smooth[i + 4] = xy_sensors[i] << ATP_SCALE;
+	memset(&dev->smooth[nb_sensors + 4], 0, 4 * sizeof(dev->smooth[0]));
+
+	for (pass = 0; pass < 4; pass++) {
+		/* Handle edge. */
+		dev->smooth_tmp[0] = (dev->smooth[0] + dev->smooth[1]) / 2;
+
+		/* Average values with neighbors. */
+		for (i = 1; i < nb_sensors + 7; i++)
+			dev->smooth_tmp[i] = (dev->smooth[i - 1] +
+					      dev->smooth[i] * 2 +
+					      dev->smooth[i + 1]) / 4;
+
+		/* Handle other edge. */
+		dev->smooth_tmp[i] = (dev->smooth[i - 1] + dev->smooth[i]) / 2;
+
+		memcpy(dev->smooth, dev->smooth_tmp, sizeof(dev->smooth));
+	}
+
+	for (i = 0; i < nb_sensors + 8; i++) {
+		/*
+		 * Skip values if they're small enough to be truncated to 0
+		 * by scale. Mostly noise.
+		 */
+		if ((dev->smooth[i] >> ATP_SCALE) > 0) {
+			pcum += dev->smooth[i] * i;
+			psum += dev->smooth[i];
+		}
+	}
+
+	if (psum > 0) {
+		*z = psum >> ATP_SCALE;        /* Scale down pressure output. */
+		return pcum * fact / psum;
+	}
+
+	return 0;
+}
+
+static inline void atp_report_fingers(struct input_dev *input, int fingers)
+{
+	input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
+	input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
+	input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
+}
+
+/* Check URB status and for correct length of data package */
+
+#define ATP_URB_STATUS_SUCCESS		0
+#define ATP_URB_STATUS_ERROR		1
+#define ATP_URB_STATUS_ERROR_FATAL	2
+
+static int atp_status_check(struct urb *urb)
+{
+	struct atp *dev = urb->context;
+	struct usb_interface *intf = dev->intf;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -EOVERFLOW:
+		if (!dev->overflow_warned) {
+			dev_warn(&intf->dev,
+				"appletouch: OVERFLOW with data length %d, actual length is %d\n",
+				dev->info->datalen, dev->urb->actual_length);
+			dev->overflow_warned = true;
+		}
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* This urb is terminated, clean up */
+		dev_dbg(&intf->dev,
+			"atp_complete: urb shutting down with status: %d\n",
+			urb->status);
+		return ATP_URB_STATUS_ERROR_FATAL;
+
+	default:
+		dev_dbg(&intf->dev,
+			"atp_complete: nonzero urb status received: %d\n",
+			urb->status);
+		return ATP_URB_STATUS_ERROR;
+	}
+
+	/* drop incomplete datasets */
+	if (dev->urb->actual_length != dev->info->datalen) {
+		dprintk("appletouch: incomplete data package"
+			" (first byte: %d, length: %d).\n",
+			dev->data[0], dev->urb->actual_length);
+		return ATP_URB_STATUS_ERROR;
+	}
+
+	return ATP_URB_STATUS_SUCCESS;
+}
+
+static void atp_detect_size(struct atp *dev)
+{
+	int i;
+
+	/* 17" Powerbooks have extra X sensors */
+	for (i = dev->info->xsensors; i < ATP_XSENSORS; i++) {
+		if (dev->xy_cur[i]) {
+
+			dev_info(&dev->intf->dev,
+				"appletouch: 17\" model detected.\n");
+
+			input_set_abs_params(dev->input, ABS_X, 0,
+					     (dev->info->xsensors_17 - 1) *
+							dev->info->xfact - 1,
+					     dev->info->fuzz, 0);
+			break;
+		}
+	}
+}
+
+/*
+ * USB interrupt callback functions
+ */
+
+/* Interrupt function for older touchpads: FOUNTAIN/GEYSER1/GEYSER2 */
+
+static void atp_complete_geyser_1_2(struct urb *urb)
+{
+	int x, y, x_z, y_z, x_f, y_f;
+	int retval, i, j;
+	int key, fingers;
+	struct atp *dev = urb->context;
+	int status = atp_status_check(urb);
+
+	if (status == ATP_URB_STATUS_ERROR_FATAL)
+		return;
+	else if (status == ATP_URB_STATUS_ERROR)
+		goto exit;
+
+	/* reorder the sensors values */
+	if (dev->info == &geyser2_info) {
+		memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
+
+		/*
+		 * The values are laid out like this:
+		 * Y1, Y2, -, Y3, Y4, -, ..., X1, X2, -, X3, X4, -, ...
+		 * '-' is an unused value.
+		 */
+
+		/* read X values */
+		for (i = 0, j = 19; i < 20; i += 2, j += 3) {
+			dev->xy_cur[i] = dev->data[j];
+			dev->xy_cur[i + 1] = dev->data[j + 1];
+		}
+
+		/* read Y values */
+		for (i = 0, j = 1; i < 9; i += 2, j += 3) {
+			dev->xy_cur[ATP_XSENSORS + i] = dev->data[j];
+			dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 1];
+		}
+	} else {
+		for (i = 0; i < 8; i++) {
+			/* X values */
+			dev->xy_cur[i +  0] = dev->data[5 * i +  2];
+			dev->xy_cur[i +  8] = dev->data[5 * i +  4];
+			dev->xy_cur[i + 16] = dev->data[5 * i + 42];
+			if (i < 2)
+				dev->xy_cur[i + 24] = dev->data[5 * i + 44];
+
+			/* Y values */
+			dev->xy_cur[ATP_XSENSORS + i] = dev->data[5 * i +  1];
+			dev->xy_cur[ATP_XSENSORS + i + 8] = dev->data[5 * i + 3];
+		}
+	}
+
+	dbg_dump("sample", dev->xy_cur);
+
+	if (!dev->valid) {
+		/* first sample */
+		dev->valid = true;
+		dev->x_old = dev->y_old = -1;
+
+		/* Store first sample */
+		memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
+
+		/* Perform size detection, if not done already */
+		if (unlikely(!dev->size_detect_done)) {
+			atp_detect_size(dev);
+			dev->size_detect_done = 1;
+			goto exit;
+		}
+	}
+
+	for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
+		/* accumulate the change */
+		signed char change = dev->xy_old[i] - dev->xy_cur[i];
+		dev->xy_acc[i] -= change;
+
+		/* prevent down drifting */
+		if (dev->xy_acc[i] < 0)
+			dev->xy_acc[i] = 0;
+	}
+
+	memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
+
+	dbg_dump("accumulator", dev->xy_acc);
+
+	x = atp_calculate_abs(dev, 0, ATP_XSENSORS,
+			      dev->info->xfact, &x_z, &x_f);
+	y = atp_calculate_abs(dev, ATP_XSENSORS, ATP_YSENSORS,
+			      dev->info->yfact, &y_z, &y_f);
+	key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
+
+	fingers = max(x_f, y_f);
+
+	if (x && y && fingers == dev->fingers_old) {
+		if (dev->x_old != -1) {
+			x = (dev->x_old * 7 + x) >> 3;
+			y = (dev->y_old * 7 + y) >> 3;
+			dev->x_old = x;
+			dev->y_old = y;
+
+			if (debug > 1)
+				printk(KERN_DEBUG "appletouch: "
+					"X: %3d Y: %3d Xz: %3d Yz: %3d\n",
+					x, y, x_z, y_z);
+
+			input_report_key(dev->input, BTN_TOUCH, 1);
+			input_report_abs(dev->input, ABS_X, x);
+			input_report_abs(dev->input, ABS_Y, y);
+			input_report_abs(dev->input, ABS_PRESSURE,
+					 min(ATP_PRESSURE, x_z + y_z));
+			atp_report_fingers(dev->input, fingers);
+		}
+		dev->x_old = x;
+		dev->y_old = y;
+
+	} else if (!x && !y) {
+
+		dev->x_old = dev->y_old = -1;
+		dev->fingers_old = 0;
+		input_report_key(dev->input, BTN_TOUCH, 0);
+		input_report_abs(dev->input, ABS_PRESSURE, 0);
+		atp_report_fingers(dev->input, 0);
+
+		/* reset the accumulator on release */
+		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
+	}
+
+	if (fingers != dev->fingers_old)
+		dev->x_old = dev->y_old = -1;
+	dev->fingers_old = fingers;
+
+	input_report_key(dev->input, BTN_LEFT, key);
+	input_sync(dev->input);
+
+ exit:
+	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&dev->intf->dev,
+			"atp_complete: usb_submit_urb failed with result %d\n",
+			retval);
+}
+
+/* Interrupt function for older touchpads: GEYSER3/GEYSER4 */
+
+static void atp_complete_geyser_3_4(struct urb *urb)
+{
+	int x, y, x_z, y_z, x_f, y_f;
+	int retval, i, j;
+	int key, fingers;
+	struct atp *dev = urb->context;
+	int status = atp_status_check(urb);
+
+	if (status == ATP_URB_STATUS_ERROR_FATAL)
+		return;
+	else if (status == ATP_URB_STATUS_ERROR)
+		goto exit;
+
+	/* Reorder the sensors values:
+	 *
+	 * The values are laid out like this:
+	 * -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ...
+	 * '-' is an unused value.
+	 */
+
+	/* read X values */
+	for (i = 0, j = 19; i < 20; i += 2, j += 3) {
+		dev->xy_cur[i] = dev->data[j + 1];
+		dev->xy_cur[i + 1] = dev->data[j + 2];
+	}
+	/* read Y values */
+	for (i = 0, j = 1; i < 9; i += 2, j += 3) {
+		dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1];
+		dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2];
+	}
+
+	dbg_dump("sample", dev->xy_cur);
+
+	/* Just update the base values (i.e. touchpad in untouched state) */
+	if (dev->data[dev->info->datalen - 1] & ATP_STATUS_BASE_UPDATE) {
+
+		dprintk("appletouch: updated base values\n");
+
+		memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
+		goto exit;
+	}
+
+	for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
+		/* calculate the change */
+		dev->xy_acc[i] = dev->xy_cur[i] - dev->xy_old[i];
+
+		/* this is a round-robin value, so couple with that */
+		if (dev->xy_acc[i] > 127)
+			dev->xy_acc[i] -= 256;
+
+		if (dev->xy_acc[i] < -127)
+			dev->xy_acc[i] += 256;
+
+		/* prevent down drifting */
+		if (dev->xy_acc[i] < 0)
+			dev->xy_acc[i] = 0;
+	}
+
+	dbg_dump("accumulator", dev->xy_acc);
+
+	x = atp_calculate_abs(dev, 0, ATP_XSENSORS,
+			      dev->info->xfact, &x_z, &x_f);
+	y = atp_calculate_abs(dev, ATP_XSENSORS, ATP_YSENSORS,
+			      dev->info->yfact, &y_z, &y_f);
+
+	key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
+
+	fingers = max(x_f, y_f);
+
+	if (x && y && fingers == dev->fingers_old) {
+		if (dev->x_old != -1) {
+			x = (dev->x_old * 7 + x) >> 3;
+			y = (dev->y_old * 7 + y) >> 3;
+			dev->x_old = x;
+			dev->y_old = y;
+
+			if (debug > 1)
+				printk(KERN_DEBUG "appletouch: X: %3d Y: %3d "
+				       "Xz: %3d Yz: %3d\n",
+				       x, y, x_z, y_z);
+
+			input_report_key(dev->input, BTN_TOUCH, 1);
+			input_report_abs(dev->input, ABS_X, x);
+			input_report_abs(dev->input, ABS_Y, y);
+			input_report_abs(dev->input, ABS_PRESSURE,
+					 min(ATP_PRESSURE, x_z + y_z));
+			atp_report_fingers(dev->input, fingers);
+		}
+		dev->x_old = x;
+		dev->y_old = y;
+
+	} else if (!x && !y) {
+
+		dev->x_old = dev->y_old = -1;
+		dev->fingers_old = 0;
+		input_report_key(dev->input, BTN_TOUCH, 0);
+		input_report_abs(dev->input, ABS_PRESSURE, 0);
+		atp_report_fingers(dev->input, 0);
+
+		/* reset the accumulator on release */
+		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
+	}
+
+	if (fingers != dev->fingers_old)
+		dev->x_old = dev->y_old = -1;
+	dev->fingers_old = fingers;
+
+	input_report_key(dev->input, BTN_LEFT, key);
+	input_sync(dev->input);
+
+	/*
+	 * Geysers 3/4 will continue to send packets continually after
+	 * the first touch unless reinitialised. Do so if it's been
+	 * idle for a while in order to avoid waking the kernel up
+	 * several hundred times a second.
+	 */
+
+	/*
+	 * Button must not be pressed when entering suspend,
+	 * otherwise we will never release the button.
+	 */
+	if (!x && !y && !key) {
+		dev->idlecount++;
+		if (dev->idlecount == 10) {
+			dev->x_old = dev->y_old = -1;
+			dev->idlecount = 0;
+			schedule_work(&dev->work);
+			/* Don't resubmit urb here, wait for reinit */
+			return;
+		}
+	} else
+		dev->idlecount = 0;
+
+ exit:
+	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&dev->intf->dev,
+			"atp_complete: usb_submit_urb failed with result %d\n",
+			retval);
+}
+
+static int atp_open(struct input_dev *input)
+{
+	struct atp *dev = input_get_drvdata(input);
+
+	if (usb_submit_urb(dev->urb, GFP_ATOMIC))
+		return -EIO;
+
+	dev->open = 1;
+	return 0;
+}
+
+static void atp_close(struct input_dev *input)
+{
+	struct atp *dev = input_get_drvdata(input);
+
+	usb_kill_urb(dev->urb);
+	cancel_work_sync(&dev->work);
+	dev->open = 0;
+}
+
+static int atp_handle_geyser(struct atp *dev)
+{
+	if (dev->info != &fountain_info) {
+		/* switch to raw sensor mode */
+		if (atp_geyser_init(dev))
+			return -EIO;
+
+		dev_info(&dev->intf->dev, "Geyser mode initialized.\n");
+	}
+
+	return 0;
+}
+
+static int atp_probe(struct usb_interface *iface,
+		     const struct usb_device_id *id)
+{
+	struct atp *dev;
+	struct input_dev *input_dev;
+	struct usb_device *udev = interface_to_usbdev(iface);
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	int int_in_endpointAddr = 0;
+	int i, error = -ENOMEM;
+	const struct atp_info *info = (const struct atp_info *)id->driver_info;
+
+	/* set up the endpoint information */
+	/* use only the first interrupt-in endpoint */
+	iface_desc = iface->cur_altsetting;
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+		endpoint = &iface_desc->endpoint[i].desc;
+		if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
+			/* we found an interrupt in endpoint */
+			int_in_endpointAddr = endpoint->bEndpointAddress;
+			break;
+		}
+	}
+	if (!int_in_endpointAddr) {
+		dev_err(&iface->dev, "Could not find int-in endpoint\n");
+		return -EIO;
+	}
+
+	/* allocate memory for our device state and initialize it */
+	dev = kzalloc(sizeof(struct atp), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!dev || !input_dev) {
+		dev_err(&iface->dev, "Out of memory\n");
+		goto err_free_devs;
+	}
+
+	dev->udev = udev;
+	dev->intf = iface;
+	dev->input = input_dev;
+	dev->info = info;
+	dev->overflow_warned = false;
+
+	dev->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->urb)
+		goto err_free_devs;
+
+	dev->data = usb_alloc_coherent(dev->udev, dev->info->datalen, GFP_KERNEL,
+				       &dev->urb->transfer_dma);
+	if (!dev->data)
+		goto err_free_urb;
+
+	usb_fill_int_urb(dev->urb, udev,
+			 usb_rcvintpipe(udev, int_in_endpointAddr),
+			 dev->data, dev->info->datalen,
+			 dev->info->callback, dev, 1);
+
+	error = atp_handle_geyser(dev);
+	if (error)
+		goto err_free_buffer;
+
+	usb_make_path(udev, dev->phys, sizeof(dev->phys));
+	strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+	input_dev->name = "appletouch";
+	input_dev->phys = dev->phys;
+	usb_to_input_id(dev->udev, &input_dev->id);
+	input_dev->dev.parent = &iface->dev;
+
+	input_set_drvdata(input_dev, dev);
+
+	input_dev->open = atp_open;
+	input_dev->close = atp_close;
+
+	set_bit(EV_ABS, input_dev->evbit);
+
+	input_set_abs_params(input_dev, ABS_X, 0,
+			     (dev->info->xsensors - 1) * dev->info->xfact - 1,
+			     dev->info->fuzz, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0,
+			     (dev->info->ysensors - 1) * dev->info->yfact - 1,
+			     dev->info->fuzz, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
+
+	set_bit(EV_KEY, input_dev->evbit);
+	set_bit(BTN_TOUCH, input_dev->keybit);
+	set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+	set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+	set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
+	set_bit(BTN_LEFT, input_dev->keybit);
+
+	error = input_register_device(dev->input);
+	if (error)
+		goto err_free_buffer;
+
+	/* save our data pointer in this interface device */
+	usb_set_intfdata(iface, dev);
+
+	INIT_WORK(&dev->work, atp_reinit);
+
+	return 0;
+
+ err_free_buffer:
+	usb_free_coherent(dev->udev, dev->info->datalen,
+			  dev->data, dev->urb->transfer_dma);
+ err_free_urb:
+	usb_free_urb(dev->urb);
+ err_free_devs:
+	usb_set_intfdata(iface, NULL);
+	kfree(dev);
+	input_free_device(input_dev);
+	return error;
+}
+
+static void atp_disconnect(struct usb_interface *iface)
+{
+	struct atp *dev = usb_get_intfdata(iface);
+
+	usb_set_intfdata(iface, NULL);
+	if (dev) {
+		usb_kill_urb(dev->urb);
+		input_unregister_device(dev->input);
+		usb_free_coherent(dev->udev, dev->info->datalen,
+				  dev->data, dev->urb->transfer_dma);
+		usb_free_urb(dev->urb);
+		kfree(dev);
+	}
+	dev_info(&iface->dev, "input: appletouch disconnected\n");
+}
+
+static int atp_recover(struct atp *dev)
+{
+	int error;
+
+	error = atp_handle_geyser(dev);
+	if (error)
+		return error;
+
+	if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
+		return -EIO;
+
+	return 0;
+}
+
+static int atp_suspend(struct usb_interface *iface, pm_message_t message)
+{
+	struct atp *dev = usb_get_intfdata(iface);
+
+	usb_kill_urb(dev->urb);
+	return 0;
+}
+
+static int atp_resume(struct usb_interface *iface)
+{
+	struct atp *dev = usb_get_intfdata(iface);
+
+	if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
+		return -EIO;
+
+	return 0;
+}
+
+static int atp_reset_resume(struct usb_interface *iface)
+{
+	struct atp *dev = usb_get_intfdata(iface);
+
+	return atp_recover(dev);
+}
+
+static struct usb_driver atp_driver = {
+	.name		= "appletouch",
+	.probe		= atp_probe,
+	.disconnect	= atp_disconnect,
+	.suspend	= atp_suspend,
+	.resume		= atp_resume,
+	.reset_resume	= atp_reset_resume,
+	.id_table	= atp_table,
+};
+
+module_usb_driver(atp_driver);
diff --git a/drivers/input/mouse/atarimouse.c b/drivers/input/mouse/atarimouse.c
new file mode 100644
index 0000000..d1c4323
--- /dev/null
+++ b/drivers/input/mouse/atarimouse.c
@@ -0,0 +1,158 @@
+/*
+ *  Atari mouse driver for Linux/m68k
+ *
+ *  Copyright (c) 2005 Michael Schmitz
+ *
+ *  Based on:
+ *  Amiga mouse driver for Linux/m68k
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik
+ *
+ */
+/*
+ * The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c
+ * (the keyboard ACIA also handles the mouse and joystick data, and the keyboard
+ * interrupt is shared with the MIDI ACIA so MIDI data also get handled there).
+ * This driver only deals with handing key events off to the input layer.
+ *
+ * Largely based on the old:
+ *
+ * Atari Mouse Driver for Linux
+ * by Robert de Vries (robert@and.nl) 19Jul93
+ *
+ * 16 Nov 1994 Andreas Schwab
+ * Compatibility with busmouse
+ * Support for three button mouse (shamelessly stolen from MiNT)
+ * third button wired to one of the joystick directions on joystick 1
+ *
+ * 1996/02/11 Andreas Schwab
+ * Module support
+ * Allow multiple open's
+ *
+ * Converted to use new generic busmouse code.  5 Apr 1998
+ *   Russell King <rmk@arm.uk.linux.org>
+ */
+
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/uaccess.h>
+#include <asm/atarihw.h>
+#include <asm/atarikb.h>
+#include <asm/atariints.h>
+
+MODULE_AUTHOR("Michael Schmitz <schmitz@biophys.uni-duesseldorf.de>");
+MODULE_DESCRIPTION("Atari mouse driver");
+MODULE_LICENSE("GPL");
+
+static int mouse_threshold[2] = {2, 2};
+module_param_array(mouse_threshold, int, NULL, 0);
+
+#ifdef FIXED_ATARI_JOYSTICK
+extern int atari_mouse_buttons;
+#endif
+
+static struct input_dev *atamouse_dev;
+
+static void atamouse_interrupt(char *buf)
+{
+	int buttons, dx, dy;
+
+	buttons = (buf[0] & 1) | ((buf[0] & 2) << 1);
+#ifdef FIXED_ATARI_JOYSTICK
+	buttons |= atari_mouse_buttons & 2;
+	atari_mouse_buttons = buttons;
+#endif
+
+	/* only relative events get here */
+	dx = buf[1];
+	dy = buf[2];
+
+	input_report_rel(atamouse_dev, REL_X, dx);
+	input_report_rel(atamouse_dev, REL_Y, dy);
+
+	input_report_key(atamouse_dev, BTN_LEFT,   buttons & 0x4);
+	input_report_key(atamouse_dev, BTN_MIDDLE, buttons & 0x2);
+	input_report_key(atamouse_dev, BTN_RIGHT,  buttons & 0x1);
+
+	input_sync(atamouse_dev);
+
+	return;
+}
+
+static int atamouse_open(struct input_dev *dev)
+{
+#ifdef FIXED_ATARI_JOYSTICK
+	atari_mouse_buttons = 0;
+#endif
+	ikbd_mouse_y0_top();
+	ikbd_mouse_thresh(mouse_threshold[0], mouse_threshold[1]);
+	ikbd_mouse_rel_pos();
+	atari_input_mouse_interrupt_hook = atamouse_interrupt;
+
+	return 0;
+}
+
+static void atamouse_close(struct input_dev *dev)
+{
+	ikbd_mouse_disable();
+	atari_input_mouse_interrupt_hook = NULL;
+}
+
+static int __init atamouse_init(void)
+{
+	int error;
+
+	if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP))
+		return -ENODEV;
+
+	error = atari_keyb_init();
+	if (error)
+		return error;
+
+	atamouse_dev = input_allocate_device();
+	if (!atamouse_dev)
+		return -ENOMEM;
+
+	atamouse_dev->name = "Atari mouse";
+	atamouse_dev->phys = "atamouse/input0";
+	atamouse_dev->id.bustype = BUS_HOST;
+	atamouse_dev->id.vendor = 0x0001;
+	atamouse_dev->id.product = 0x0002;
+	atamouse_dev->id.version = 0x0100;
+
+	atamouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+	atamouse_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+	atamouse_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+		BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+
+	atamouse_dev->open = atamouse_open;
+	atamouse_dev->close = atamouse_close;
+
+	error = input_register_device(atamouse_dev);
+	if (error) {
+		input_free_device(atamouse_dev);
+		return error;
+	}
+
+	return 0;
+}
+
+static void __exit atamouse_exit(void)
+{
+	input_unregister_device(atamouse_dev);
+}
+
+module_init(atamouse_init);
+module_exit(atamouse_exit);
diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
new file mode 100644
index 0000000..30e3442
--- /dev/null
+++ b/drivers/input/mouse/bcm5974.c
@@ -0,0 +1,1042 @@
+/*
+ * Apple USB BCM5974 (Macbook Air and Penryn Macbook Pro) multitouch driver
+ *
+ * Copyright (C) 2008	   Henrik Rydberg (rydberg@euromail.se)
+ * Copyright (C) 2015      John Horan (knasher@gmail.com)
+ *
+ * The USB initialization and package decoding was made by
+ * Scott Shawcroft as part of the touchd user-space driver project:
+ * Copyright (C) 2008	   Scott Shawcroft (scott.shawcroft@gmail.com)
+ *
+ * The BCM5974 driver is based on the appletouch driver:
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2005      Johannes Berg (johannes@sipsolutions.net)
+ * Copyright (C) 2005	   Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2005	   Frank Arnold (frank@scirocco-5v-turbo.de)
+ * Copyright (C) 2005	   Peter Osterlund (petero2@telia.com)
+ * Copyright (C) 2005	   Michael Hanselmann (linux-kernel@hansmi.ch)
+ * Copyright (C) 2006	   Nicolas Boichat (nicolas@boichat.ch)
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+#include <linux/hid.h>
+#include <linux/mutex.h>
+#include <linux/input/mt.h>
+
+#define USB_VENDOR_ID_APPLE		0x05ac
+
+/* MacbookAir, aka wellspring */
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI	0x0223
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO	0x0224
+#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS	0x0225
+/* MacbookProPenryn, aka wellspring2 */
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI	0x0230
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO	0x0231
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS	0x0232
+/* Macbook5,1 (unibody), aka wellspring3 */
+#define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI	0x0236
+#define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO	0x0237
+#define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS	0x0238
+/* MacbookAir3,2 (unibody), aka wellspring5 */
+#define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI	0x023f
+#define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO	0x0240
+#define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS	0x0241
+/* MacbookAir3,1 (unibody), aka wellspring4 */
+#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI	0x0242
+#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO	0x0243
+#define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS	0x0244
+/* Macbook8 (unibody, March 2011) */
+#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI	0x0245
+#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO	0x0246
+#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS	0x0247
+/* MacbookAir4,1 (unibody, July 2011) */
+#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI	0x0249
+#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO	0x024a
+#define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS	0x024b
+/* MacbookAir4,2 (unibody, July 2011) */
+#define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI	0x024c
+#define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO	0x024d
+#define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS	0x024e
+/* Macbook8,2 (unibody) */
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI	0x0252
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO	0x0253
+#define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS	0x0254
+/* MacbookPro10,1 (unibody, June 2012) */
+#define USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI	0x0262
+#define USB_DEVICE_ID_APPLE_WELLSPRING7_ISO	0x0263
+#define USB_DEVICE_ID_APPLE_WELLSPRING7_JIS	0x0264
+/* MacbookPro10,2 (unibody, October 2012) */
+#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI	0x0259
+#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO	0x025a
+#define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS	0x025b
+/* MacbookAir6,2 (unibody, June 2013) */
+#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI	0x0290
+#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO	0x0291
+#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS	0x0292
+/* MacbookPro12,1 (2015) */
+#define USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI	0x0272
+#define USB_DEVICE_ID_APPLE_WELLSPRING9_ISO	0x0273
+#define USB_DEVICE_ID_APPLE_WELLSPRING9_JIS	0x0274
+
+#define BCM5974_DEVICE(prod) {					\
+	.match_flags = (USB_DEVICE_ID_MATCH_DEVICE |		\
+			USB_DEVICE_ID_MATCH_INT_CLASS |		\
+			USB_DEVICE_ID_MATCH_INT_PROTOCOL),	\
+	.idVendor = USB_VENDOR_ID_APPLE,			\
+	.idProduct = (prod),					\
+	.bInterfaceClass = USB_INTERFACE_CLASS_HID,		\
+	.bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE	\
+}
+
+/* table of devices that work with this driver */
+static const struct usb_device_id bcm5974_table[] = {
+	/* MacbookAir1.1 */
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ISO),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_JIS),
+	/* MacbookProPenryn */
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ISO),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_JIS),
+	/* Macbook5,1 */
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ISO),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_JIS),
+	/* MacbookAir3,2 */
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ISO),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_JIS),
+	/* MacbookAir3,1 */
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS),
+	/* MacbookPro8 */
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ISO),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_JIS),
+	/* MacbookAir4,1 */
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS),
+	/* MacbookAir4,2 */
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ISO),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_JIS),
+	/* MacbookPro8,2 */
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS),
+	/* MacbookPro10,1 */
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_ISO),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_JIS),
+	/* MacbookPro10,2 */
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS),
+	/* MacbookAir6,2 */
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ISO),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_JIS),
+	/* MacbookPro12,1 */
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_ISO),
+	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_JIS),
+	/* Terminating entry */
+	{}
+};
+MODULE_DEVICE_TABLE(usb, bcm5974_table);
+
+MODULE_AUTHOR("Henrik Rydberg");
+MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver");
+MODULE_LICENSE("GPL");
+
+#define dprintk(level, format, a...)\
+	{ if (debug >= level) printk(KERN_DEBUG format, ##a); }
+
+static int debug = 1;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activate debugging output");
+
+/* button data structure */
+struct bt_data {
+	u8 unknown1;		/* constant */
+	u8 button;		/* left button */
+	u8 rel_x;		/* relative x coordinate */
+	u8 rel_y;		/* relative y coordinate */
+};
+
+/* trackpad header types */
+enum tp_type {
+	TYPE1,			/* plain trackpad */
+	TYPE2,			/* button integrated in trackpad */
+	TYPE3,			/* additional header fields since June 2013 */
+	TYPE4			/* additional header field for pressure data */
+};
+
+/* trackpad finger data offsets, le16-aligned */
+#define HEADER_TYPE1		(13 * sizeof(__le16))
+#define HEADER_TYPE2		(15 * sizeof(__le16))
+#define HEADER_TYPE3		(19 * sizeof(__le16))
+#define HEADER_TYPE4		(23 * sizeof(__le16))
+
+/* trackpad button data offsets */
+#define BUTTON_TYPE1		0
+#define BUTTON_TYPE2		15
+#define BUTTON_TYPE3		23
+#define BUTTON_TYPE4		31
+
+/* list of device capability bits */
+#define HAS_INTEGRATED_BUTTON	1
+
+/* trackpad finger data block size */
+#define FSIZE_TYPE1		(14 * sizeof(__le16))
+#define FSIZE_TYPE2		(14 * sizeof(__le16))
+#define FSIZE_TYPE3		(14 * sizeof(__le16))
+#define FSIZE_TYPE4		(15 * sizeof(__le16))
+
+/* offset from header to finger struct */
+#define DELTA_TYPE1		(0 * sizeof(__le16))
+#define DELTA_TYPE2		(0 * sizeof(__le16))
+#define DELTA_TYPE3		(0 * sizeof(__le16))
+#define DELTA_TYPE4		(1 * sizeof(__le16))
+
+/* usb control message mode switch data */
+#define USBMSG_TYPE1		8, 0x300, 0, 0, 0x1, 0x8
+#define USBMSG_TYPE2		8, 0x300, 0, 0, 0x1, 0x8
+#define USBMSG_TYPE3		8, 0x300, 0, 0, 0x1, 0x8
+#define USBMSG_TYPE4		2, 0x302, 2, 1, 0x1, 0x0
+
+/* Wellspring initialization constants */
+#define BCM5974_WELLSPRING_MODE_READ_REQUEST_ID		1
+#define BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID	9
+
+/* trackpad finger structure, le16-aligned */
+struct tp_finger {
+	__le16 origin;		/* zero when switching track finger */
+	__le16 abs_x;		/* absolute x coodinate */
+	__le16 abs_y;		/* absolute y coodinate */
+	__le16 rel_x;		/* relative x coodinate */
+	__le16 rel_y;		/* relative y coodinate */
+	__le16 tool_major;	/* tool area, major axis */
+	__le16 tool_minor;	/* tool area, minor axis */
+	__le16 orientation;	/* 16384 when point, else 15 bit angle */
+	__le16 touch_major;	/* touch area, major axis */
+	__le16 touch_minor;	/* touch area, minor axis */
+	__le16 unused[2];	/* zeros */
+	__le16 pressure;	/* pressure on forcetouch touchpad */
+	__le16 multi;		/* one finger: varies, more fingers: constant */
+} __attribute__((packed,aligned(2)));
+
+/* trackpad finger data size, empirically at least ten fingers */
+#define MAX_FINGERS		16
+#define MAX_FINGER_ORIENTATION	16384
+
+/* device-specific parameters */
+struct bcm5974_param {
+	int snratio;		/* signal-to-noise ratio */
+	int min;		/* device minimum reading */
+	int max;		/* device maximum reading */
+};
+
+/* device-specific configuration */
+struct bcm5974_config {
+	int ansi, iso, jis;	/* the product id of this device */
+	int caps;		/* device capability bitmask */
+	int bt_ep;		/* the endpoint of the button interface */
+	int bt_datalen;		/* data length of the button interface */
+	int tp_ep;		/* the endpoint of the trackpad interface */
+	enum tp_type tp_type;	/* type of trackpad interface */
+	int tp_header;		/* bytes in header block */
+	int tp_datalen;		/* data length of the trackpad interface */
+	int tp_button;		/* offset to button data */
+	int tp_fsize;		/* bytes in single finger block */
+	int tp_delta;		/* offset from header to finger struct */
+	int um_size;		/* usb control message length */
+	int um_req_val;		/* usb control message value */
+	int um_req_idx;		/* usb control message index */
+	int um_switch_idx;	/* usb control message mode switch index */
+	int um_switch_on;	/* usb control message mode switch on */
+	int um_switch_off;	/* usb control message mode switch off */
+	struct bcm5974_param p;	/* finger pressure limits */
+	struct bcm5974_param w;	/* finger width limits */
+	struct bcm5974_param x;	/* horizontal limits */
+	struct bcm5974_param y;	/* vertical limits */
+	struct bcm5974_param o;	/* orientation limits */
+};
+
+/* logical device structure */
+struct bcm5974 {
+	char phys[64];
+	struct usb_device *udev;	/* usb device */
+	struct usb_interface *intf;	/* our interface */
+	struct input_dev *input;	/* input dev */
+	struct bcm5974_config cfg;	/* device configuration */
+	struct mutex pm_mutex;		/* serialize access to open/suspend */
+	int opened;			/* 1: opened, 0: closed */
+	struct urb *bt_urb;		/* button usb request block */
+	struct bt_data *bt_data;	/* button transferred data */
+	struct urb *tp_urb;		/* trackpad usb request block */
+	u8 *tp_data;			/* trackpad transferred data */
+	const struct tp_finger *index[MAX_FINGERS];	/* finger index data */
+	struct input_mt_pos pos[MAX_FINGERS];		/* position array */
+	int slots[MAX_FINGERS];				/* slot assignments */
+};
+
+/* trackpad finger block data, le16-aligned */
+static const struct tp_finger *get_tp_finger(const struct bcm5974 *dev, int i)
+{
+	const struct bcm5974_config *c = &dev->cfg;
+	u8 *f_base = dev->tp_data + c->tp_header + c->tp_delta;
+
+	return (const struct tp_finger *)(f_base + i * c->tp_fsize);
+}
+
+#define DATAFORMAT(type)				\
+	type,						\
+	HEADER_##type,					\
+	HEADER_##type + (MAX_FINGERS) * (FSIZE_##type),	\
+	BUTTON_##type,					\
+	FSIZE_##type,					\
+	DELTA_##type,					\
+	USBMSG_##type
+
+/* logical signal quality */
+#define SN_PRESSURE	45		/* pressure signal-to-noise ratio */
+#define SN_WIDTH	25		/* width signal-to-noise ratio */
+#define SN_COORD	250		/* coordinate signal-to-noise ratio */
+#define SN_ORIENT	10		/* orientation signal-to-noise ratio */
+
+/* device constants */
+static const struct bcm5974_config bcm5974_config_table[] = {
+	{
+		USB_DEVICE_ID_APPLE_WELLSPRING_ANSI,
+		USB_DEVICE_ID_APPLE_WELLSPRING_ISO,
+		USB_DEVICE_ID_APPLE_WELLSPRING_JIS,
+		0,
+		0x84, sizeof(struct bt_data),
+		0x81, DATAFORMAT(TYPE1),
+		{ SN_PRESSURE, 0, 256 },
+		{ SN_WIDTH, 0, 2048 },
+		{ SN_COORD, -4824, 5342 },
+		{ SN_COORD, -172, 5820 },
+		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+	},
+	{
+		USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI,
+		USB_DEVICE_ID_APPLE_WELLSPRING2_ISO,
+		USB_DEVICE_ID_APPLE_WELLSPRING2_JIS,
+		0,
+		0x84, sizeof(struct bt_data),
+		0x81, DATAFORMAT(TYPE1),
+		{ SN_PRESSURE, 0, 256 },
+		{ SN_WIDTH, 0, 2048 },
+		{ SN_COORD, -4824, 4824 },
+		{ SN_COORD, -172, 4290 },
+		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+	},
+	{
+		USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI,
+		USB_DEVICE_ID_APPLE_WELLSPRING3_ISO,
+		USB_DEVICE_ID_APPLE_WELLSPRING3_JIS,
+		HAS_INTEGRATED_BUTTON,
+		0x84, sizeof(struct bt_data),
+		0x81, DATAFORMAT(TYPE2),
+		{ SN_PRESSURE, 0, 300 },
+		{ SN_WIDTH, 0, 2048 },
+		{ SN_COORD, -4460, 5166 },
+		{ SN_COORD, -75, 6700 },
+		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+	},
+	{
+		USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI,
+		USB_DEVICE_ID_APPLE_WELLSPRING4_ISO,
+		USB_DEVICE_ID_APPLE_WELLSPRING4_JIS,
+		HAS_INTEGRATED_BUTTON,
+		0x84, sizeof(struct bt_data),
+		0x81, DATAFORMAT(TYPE2),
+		{ SN_PRESSURE, 0, 300 },
+		{ SN_WIDTH, 0, 2048 },
+		{ SN_COORD, -4620, 5140 },
+		{ SN_COORD, -150, 6600 },
+		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+	},
+	{
+		USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI,
+		USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO,
+		USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS,
+		HAS_INTEGRATED_BUTTON,
+		0x84, sizeof(struct bt_data),
+		0x81, DATAFORMAT(TYPE2),
+		{ SN_PRESSURE, 0, 300 },
+		{ SN_WIDTH, 0, 2048 },
+		{ SN_COORD, -4616, 5112 },
+		{ SN_COORD, -142, 5234 },
+		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+	},
+	{
+		USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI,
+		USB_DEVICE_ID_APPLE_WELLSPRING5_ISO,
+		USB_DEVICE_ID_APPLE_WELLSPRING5_JIS,
+		HAS_INTEGRATED_BUTTON,
+		0x84, sizeof(struct bt_data),
+		0x81, DATAFORMAT(TYPE2),
+		{ SN_PRESSURE, 0, 300 },
+		{ SN_WIDTH, 0, 2048 },
+		{ SN_COORD, -4415, 5050 },
+		{ SN_COORD, -55, 6680 },
+		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+	},
+	{
+		USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI,
+		USB_DEVICE_ID_APPLE_WELLSPRING6_ISO,
+		USB_DEVICE_ID_APPLE_WELLSPRING6_JIS,
+		HAS_INTEGRATED_BUTTON,
+		0x84, sizeof(struct bt_data),
+		0x81, DATAFORMAT(TYPE2),
+		{ SN_PRESSURE, 0, 300 },
+		{ SN_WIDTH, 0, 2048 },
+		{ SN_COORD, -4620, 5140 },
+		{ SN_COORD, -150, 6600 },
+		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+	},
+	{
+		USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI,
+		USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO,
+		USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS,
+		HAS_INTEGRATED_BUTTON,
+		0x84, sizeof(struct bt_data),
+		0x81, DATAFORMAT(TYPE2),
+		{ SN_PRESSURE, 0, 300 },
+		{ SN_WIDTH, 0, 2048 },
+		{ SN_COORD, -4750, 5280 },
+		{ SN_COORD, -150, 6730 },
+		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+	},
+	{
+		USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI,
+		USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO,
+		USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS,
+		HAS_INTEGRATED_BUTTON,
+		0x84, sizeof(struct bt_data),
+		0x81, DATAFORMAT(TYPE2),
+		{ SN_PRESSURE, 0, 300 },
+		{ SN_WIDTH, 0, 2048 },
+		{ SN_COORD, -4620, 5140 },
+		{ SN_COORD, -150, 6600 },
+		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+	},
+	{
+		USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI,
+		USB_DEVICE_ID_APPLE_WELLSPRING7_ISO,
+		USB_DEVICE_ID_APPLE_WELLSPRING7_JIS,
+		HAS_INTEGRATED_BUTTON,
+		0x84, sizeof(struct bt_data),
+		0x81, DATAFORMAT(TYPE2),
+		{ SN_PRESSURE, 0, 300 },
+		{ SN_WIDTH, 0, 2048 },
+		{ SN_COORD, -4750, 5280 },
+		{ SN_COORD, -150, 6730 },
+		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+	},
+	{
+		USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI,
+		USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO,
+		USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS,
+		HAS_INTEGRATED_BUTTON,
+		0x84, sizeof(struct bt_data),
+		0x81, DATAFORMAT(TYPE2),
+		{ SN_PRESSURE, 0, 300 },
+		{ SN_WIDTH, 0, 2048 },
+		{ SN_COORD, -4750, 5280 },
+		{ SN_COORD, -150, 6730 },
+		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+	},
+	{
+		USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI,
+		USB_DEVICE_ID_APPLE_WELLSPRING8_ISO,
+		USB_DEVICE_ID_APPLE_WELLSPRING8_JIS,
+		HAS_INTEGRATED_BUTTON,
+		0, sizeof(struct bt_data),
+		0x83, DATAFORMAT(TYPE3),
+		{ SN_PRESSURE, 0, 300 },
+		{ SN_WIDTH, 0, 2048 },
+		{ SN_COORD, -4620, 5140 },
+		{ SN_COORD, -150, 6600 },
+		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+	},
+	{
+		USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI,
+		USB_DEVICE_ID_APPLE_WELLSPRING9_ISO,
+		USB_DEVICE_ID_APPLE_WELLSPRING9_JIS,
+		HAS_INTEGRATED_BUTTON,
+		0, sizeof(struct bt_data),
+		0x83, DATAFORMAT(TYPE4),
+		{ SN_PRESSURE, 0, 300 },
+		{ SN_WIDTH, 0, 2048 },
+		{ SN_COORD, -4828, 5345 },
+		{ SN_COORD, -203, 6803 },
+		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
+	},
+	{}
+};
+
+/* return the device-specific configuration by device */
+static const struct bcm5974_config *bcm5974_get_config(struct usb_device *udev)
+{
+	u16 id = le16_to_cpu(udev->descriptor.idProduct);
+	const struct bcm5974_config *cfg;
+
+	for (cfg = bcm5974_config_table; cfg->ansi; ++cfg)
+		if (cfg->ansi == id || cfg->iso == id || cfg->jis == id)
+			return cfg;
+
+	return bcm5974_config_table;
+}
+
+/* convert 16-bit little endian to signed integer */
+static inline int raw2int(__le16 x)
+{
+	return (signed short)le16_to_cpu(x);
+}
+
+static void set_abs(struct input_dev *input, unsigned int code,
+		    const struct bcm5974_param *p)
+{
+	int fuzz = p->snratio ? (p->max - p->min) / p->snratio : 0;
+	input_set_abs_params(input, code, p->min, p->max, fuzz, 0);
+}
+
+/* setup which logical events to report */
+static void setup_events_to_report(struct input_dev *input_dev,
+				   const struct bcm5974_config *cfg)
+{
+	__set_bit(EV_ABS, input_dev->evbit);
+
+	/* for synaptics only */
+	input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 5, 0);
+	input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 16, 0, 0);
+
+	/* finger touch area */
+	set_abs(input_dev, ABS_MT_TOUCH_MAJOR, &cfg->w);
+	set_abs(input_dev, ABS_MT_TOUCH_MINOR, &cfg->w);
+	/* finger approach area */
+	set_abs(input_dev, ABS_MT_WIDTH_MAJOR, &cfg->w);
+	set_abs(input_dev, ABS_MT_WIDTH_MINOR, &cfg->w);
+	/* finger orientation */
+	set_abs(input_dev, ABS_MT_ORIENTATION, &cfg->o);
+	/* finger position */
+	set_abs(input_dev, ABS_MT_POSITION_X, &cfg->x);
+	set_abs(input_dev, ABS_MT_POSITION_Y, &cfg->y);
+
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(BTN_LEFT, input_dev->keybit);
+
+	if (cfg->caps & HAS_INTEGRATED_BUTTON)
+		__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+
+	input_mt_init_slots(input_dev, MAX_FINGERS,
+		INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK);
+}
+
+/* report button data as logical button state */
+static int report_bt_state(struct bcm5974 *dev, int size)
+{
+	if (size != sizeof(struct bt_data))
+		return -EIO;
+
+	dprintk(7,
+		"bcm5974: button data: %x %x %x %x\n",
+		dev->bt_data->unknown1, dev->bt_data->button,
+		dev->bt_data->rel_x, dev->bt_data->rel_y);
+
+	input_report_key(dev->input, BTN_LEFT, dev->bt_data->button);
+	input_sync(dev->input);
+
+	return 0;
+}
+
+static void report_finger_data(struct input_dev *input, int slot,
+			       const struct input_mt_pos *pos,
+			       const struct tp_finger *f)
+{
+	input_mt_slot(input, slot);
+	input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+
+	input_report_abs(input, ABS_MT_TOUCH_MAJOR,
+			 raw2int(f->touch_major) << 1);
+	input_report_abs(input, ABS_MT_TOUCH_MINOR,
+			 raw2int(f->touch_minor) << 1);
+	input_report_abs(input, ABS_MT_WIDTH_MAJOR,
+			 raw2int(f->tool_major) << 1);
+	input_report_abs(input, ABS_MT_WIDTH_MINOR,
+			 raw2int(f->tool_minor) << 1);
+	input_report_abs(input, ABS_MT_ORIENTATION,
+			 MAX_FINGER_ORIENTATION - raw2int(f->orientation));
+	input_report_abs(input, ABS_MT_POSITION_X, pos->x);
+	input_report_abs(input, ABS_MT_POSITION_Y, pos->y);
+}
+
+static void report_synaptics_data(struct input_dev *input,
+				  const struct bcm5974_config *cfg,
+				  const struct tp_finger *f, int raw_n)
+{
+	int abs_p = 0, abs_w = 0;
+
+	if (raw_n) {
+		int p = raw2int(f->touch_major);
+		int w = raw2int(f->tool_major);
+		if (p > 0 && raw2int(f->origin)) {
+			abs_p = clamp_val(256 * p / cfg->p.max, 0, 255);
+			abs_w = clamp_val(16 * w / cfg->w.max, 0, 15);
+		}
+	}
+
+	input_report_abs(input, ABS_PRESSURE, abs_p);
+	input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
+}
+
+/* report trackpad data as logical trackpad state */
+static int report_tp_state(struct bcm5974 *dev, int size)
+{
+	const struct bcm5974_config *c = &dev->cfg;
+	const struct tp_finger *f;
+	struct input_dev *input = dev->input;
+	int raw_n, i, n = 0;
+
+	if (size < c->tp_header || (size - c->tp_header) % c->tp_fsize != 0)
+		return -EIO;
+
+	raw_n = (size - c->tp_header) / c->tp_fsize;
+
+	for (i = 0; i < raw_n; i++) {
+		f = get_tp_finger(dev, i);
+		if (raw2int(f->touch_major) == 0)
+			continue;
+		dev->pos[n].x = raw2int(f->abs_x);
+		dev->pos[n].y = c->y.min + c->y.max - raw2int(f->abs_y);
+		dev->index[n++] = f;
+	}
+
+	input_mt_assign_slots(input, dev->slots, dev->pos, n, 0);
+
+	for (i = 0; i < n; i++)
+		report_finger_data(input, dev->slots[i],
+				   &dev->pos[i], dev->index[i]);
+
+	input_mt_sync_frame(input);
+
+	report_synaptics_data(input, c, get_tp_finger(dev, 0), raw_n);
+
+	/* later types report button events via integrated button only */
+	if (c->caps & HAS_INTEGRATED_BUTTON) {
+		int ibt = raw2int(dev->tp_data[c->tp_button]);
+		input_report_key(input, BTN_LEFT, ibt);
+	}
+
+	input_sync(input);
+
+	return 0;
+}
+
+static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on)
+{
+	const struct bcm5974_config *c = &dev->cfg;
+	int retval = 0, size;
+	char *data;
+
+	/* Type 3 does not require a mode switch */
+	if (dev->cfg.tp_type == TYPE3)
+		return 0;
+
+	data = kmalloc(c->um_size, GFP_KERNEL);
+	if (!data) {
+		dev_err(&dev->intf->dev, "out of memory\n");
+		retval = -ENOMEM;
+		goto out;
+	}
+
+	/* read configuration */
+	size = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+			BCM5974_WELLSPRING_MODE_READ_REQUEST_ID,
+			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			c->um_req_val, c->um_req_idx, data, c->um_size, 5000);
+
+	if (size != c->um_size) {
+		dev_err(&dev->intf->dev, "could not read from device\n");
+		retval = -EIO;
+		goto out;
+	}
+
+	/* apply the mode switch */
+	data[c->um_switch_idx] = on ? c->um_switch_on : c->um_switch_off;
+
+	/* write configuration */
+	size = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+			BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID,
+			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			c->um_req_val, c->um_req_idx, data, c->um_size, 5000);
+
+	if (size != c->um_size) {
+		dev_err(&dev->intf->dev, "could not write to device\n");
+		retval = -EIO;
+		goto out;
+	}
+
+	dprintk(2, "bcm5974: switched to %s mode.\n",
+		on ? "wellspring" : "normal");
+
+ out:
+	kfree(data);
+	return retval;
+}
+
+static void bcm5974_irq_button(struct urb *urb)
+{
+	struct bcm5974 *dev = urb->context;
+	struct usb_interface *intf = dev->intf;
+	int error;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -EOVERFLOW:
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		dev_dbg(&intf->dev, "button urb shutting down: %d\n",
+			urb->status);
+		return;
+	default:
+		dev_dbg(&intf->dev, "button urb status: %d\n", urb->status);
+		goto exit;
+	}
+
+	if (report_bt_state(dev, dev->bt_urb->actual_length))
+		dprintk(1, "bcm5974: bad button package, length: %d\n",
+			dev->bt_urb->actual_length);
+
+exit:
+	error = usb_submit_urb(dev->bt_urb, GFP_ATOMIC);
+	if (error)
+		dev_err(&intf->dev, "button urb failed: %d\n", error);
+}
+
+static void bcm5974_irq_trackpad(struct urb *urb)
+{
+	struct bcm5974 *dev = urb->context;
+	struct usb_interface *intf = dev->intf;
+	int error;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -EOVERFLOW:
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		dev_dbg(&intf->dev, "trackpad urb shutting down: %d\n",
+			urb->status);
+		return;
+	default:
+		dev_dbg(&intf->dev, "trackpad urb status: %d\n", urb->status);
+		goto exit;
+	}
+
+	/* control response ignored */
+	if (dev->tp_urb->actual_length == 2)
+		goto exit;
+
+	if (report_tp_state(dev, dev->tp_urb->actual_length))
+		dprintk(1, "bcm5974: bad trackpad package, length: %d\n",
+			dev->tp_urb->actual_length);
+
+exit:
+	error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
+	if (error)
+		dev_err(&intf->dev, "trackpad urb failed: %d\n", error);
+}
+
+/*
+ * The Wellspring trackpad, like many recent Apple trackpads, share
+ * the usb device with the keyboard. Since keyboards are usually
+ * handled by the HID system, the device ends up being handled by two
+ * modules. Setting up the device therefore becomes slightly
+ * complicated. To enable multitouch features, a mode switch is
+ * required, which is usually applied via the control interface of the
+ * device.  It can be argued where this switch should take place. In
+ * some drivers, like appletouch, the switch is made during
+ * probe. However, the hid module may also alter the state of the
+ * device, resulting in trackpad malfunction under certain
+ * circumstances. To get around this problem, there is at least one
+ * example that utilizes the USB_QUIRK_RESET_RESUME quirk in order to
+ * receive a reset_resume request rather than the normal resume.
+ * Since the implementation of reset_resume is equal to mode switch
+ * plus start_traffic, it seems easier to always do the switch when
+ * starting traffic on the device.
+ */
+static int bcm5974_start_traffic(struct bcm5974 *dev)
+{
+	int error;
+
+	error = bcm5974_wellspring_mode(dev, true);
+	if (error) {
+		dprintk(1, "bcm5974: mode switch failed\n");
+		goto err_out;
+	}
+
+	if (dev->bt_urb) {
+		error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
+		if (error)
+			goto err_reset_mode;
+	}
+
+	error = usb_submit_urb(dev->tp_urb, GFP_KERNEL);
+	if (error)
+		goto err_kill_bt;
+
+	return 0;
+
+err_kill_bt:
+	usb_kill_urb(dev->bt_urb);
+err_reset_mode:
+	bcm5974_wellspring_mode(dev, false);
+err_out:
+	return error;
+}
+
+static void bcm5974_pause_traffic(struct bcm5974 *dev)
+{
+	usb_kill_urb(dev->tp_urb);
+	usb_kill_urb(dev->bt_urb);
+	bcm5974_wellspring_mode(dev, false);
+}
+
+/*
+ * The code below implements open/close and manual suspend/resume.
+ * All functions may be called in random order.
+ *
+ * Opening a suspended device fails with EACCES - permission denied.
+ *
+ * Failing a resume leaves the device resumed but closed.
+ */
+static int bcm5974_open(struct input_dev *input)
+{
+	struct bcm5974 *dev = input_get_drvdata(input);
+	int error;
+
+	error = usb_autopm_get_interface(dev->intf);
+	if (error)
+		return error;
+
+	mutex_lock(&dev->pm_mutex);
+
+	error = bcm5974_start_traffic(dev);
+	if (!error)
+		dev->opened = 1;
+
+	mutex_unlock(&dev->pm_mutex);
+
+	if (error)
+		usb_autopm_put_interface(dev->intf);
+
+	return error;
+}
+
+static void bcm5974_close(struct input_dev *input)
+{
+	struct bcm5974 *dev = input_get_drvdata(input);
+
+	mutex_lock(&dev->pm_mutex);
+
+	bcm5974_pause_traffic(dev);
+	dev->opened = 0;
+
+	mutex_unlock(&dev->pm_mutex);
+
+	usb_autopm_put_interface(dev->intf);
+}
+
+static int bcm5974_suspend(struct usb_interface *iface, pm_message_t message)
+{
+	struct bcm5974 *dev = usb_get_intfdata(iface);
+
+	mutex_lock(&dev->pm_mutex);
+
+	if (dev->opened)
+		bcm5974_pause_traffic(dev);
+
+	mutex_unlock(&dev->pm_mutex);
+
+	return 0;
+}
+
+static int bcm5974_resume(struct usb_interface *iface)
+{
+	struct bcm5974 *dev = usb_get_intfdata(iface);
+	int error = 0;
+
+	mutex_lock(&dev->pm_mutex);
+
+	if (dev->opened)
+		error = bcm5974_start_traffic(dev);
+
+	mutex_unlock(&dev->pm_mutex);
+
+	return error;
+}
+
+static int bcm5974_probe(struct usb_interface *iface,
+			 const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(iface);
+	const struct bcm5974_config *cfg;
+	struct bcm5974 *dev;
+	struct input_dev *input_dev;
+	int error = -ENOMEM;
+
+	/* find the product index */
+	cfg = bcm5974_get_config(udev);
+
+	/* allocate memory for our device state and initialize it */
+	dev = kzalloc(sizeof(struct bcm5974), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!dev || !input_dev) {
+		dev_err(&iface->dev, "out of memory\n");
+		goto err_free_devs;
+	}
+
+	dev->udev = udev;
+	dev->intf = iface;
+	dev->input = input_dev;
+	dev->cfg = *cfg;
+	mutex_init(&dev->pm_mutex);
+
+	/* setup urbs */
+	if (cfg->tp_type == TYPE1) {
+		dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!dev->bt_urb)
+			goto err_free_devs;
+	}
+
+	dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->tp_urb)
+		goto err_free_bt_urb;
+
+	if (dev->bt_urb) {
+		dev->bt_data = usb_alloc_coherent(dev->udev,
+					  dev->cfg.bt_datalen, GFP_KERNEL,
+					  &dev->bt_urb->transfer_dma);
+		if (!dev->bt_data)
+			goto err_free_urb;
+	}
+
+	dev->tp_data = usb_alloc_coherent(dev->udev,
+					  dev->cfg.tp_datalen, GFP_KERNEL,
+					  &dev->tp_urb->transfer_dma);
+	if (!dev->tp_data)
+		goto err_free_bt_buffer;
+
+	if (dev->bt_urb)
+		usb_fill_int_urb(dev->bt_urb, udev,
+				 usb_rcvintpipe(udev, cfg->bt_ep),
+				 dev->bt_data, dev->cfg.bt_datalen,
+				 bcm5974_irq_button, dev, 1);
+
+	usb_fill_int_urb(dev->tp_urb, udev,
+			 usb_rcvintpipe(udev, cfg->tp_ep),
+			 dev->tp_data, dev->cfg.tp_datalen,
+			 bcm5974_irq_trackpad, dev, 1);
+
+	/* create bcm5974 device */
+	usb_make_path(udev, dev->phys, sizeof(dev->phys));
+	strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+	input_dev->name = "bcm5974";
+	input_dev->phys = dev->phys;
+	usb_to_input_id(dev->udev, &input_dev->id);
+	/* report driver capabilities via the version field */
+	input_dev->id.version = cfg->caps;
+	input_dev->dev.parent = &iface->dev;
+
+	input_set_drvdata(input_dev, dev);
+
+	input_dev->open = bcm5974_open;
+	input_dev->close = bcm5974_close;
+
+	setup_events_to_report(input_dev, cfg);
+
+	error = input_register_device(dev->input);
+	if (error)
+		goto err_free_buffer;
+
+	/* save our data pointer in this interface device */
+	usb_set_intfdata(iface, dev);
+
+	return 0;
+
+err_free_buffer:
+	usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
+		dev->tp_data, dev->tp_urb->transfer_dma);
+err_free_bt_buffer:
+	if (dev->bt_urb)
+		usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
+				  dev->bt_data, dev->bt_urb->transfer_dma);
+err_free_urb:
+	usb_free_urb(dev->tp_urb);
+err_free_bt_urb:
+	usb_free_urb(dev->bt_urb);
+err_free_devs:
+	usb_set_intfdata(iface, NULL);
+	input_free_device(input_dev);
+	kfree(dev);
+	return error;
+}
+
+static void bcm5974_disconnect(struct usb_interface *iface)
+{
+	struct bcm5974 *dev = usb_get_intfdata(iface);
+
+	usb_set_intfdata(iface, NULL);
+
+	input_unregister_device(dev->input);
+	usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
+			  dev->tp_data, dev->tp_urb->transfer_dma);
+	if (dev->bt_urb)
+		usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
+				  dev->bt_data, dev->bt_urb->transfer_dma);
+	usb_free_urb(dev->tp_urb);
+	usb_free_urb(dev->bt_urb);
+	kfree(dev);
+}
+
+static struct usb_driver bcm5974_driver = {
+	.name			= "bcm5974",
+	.probe			= bcm5974_probe,
+	.disconnect		= bcm5974_disconnect,
+	.suspend		= bcm5974_suspend,
+	.resume			= bcm5974_resume,
+	.id_table		= bcm5974_table,
+	.supports_autosuspend	= 1,
+};
+
+module_usb_driver(bcm5974_driver);
diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
new file mode 100644
index 0000000..eb76b61
--- /dev/null
+++ b/drivers/input/mouse/cyapa.c
@@ -0,0 +1,1515 @@
+/*
+ * Cypress APA trackpad with I2C interface
+ *
+ * Author: Dudley Du <dudl@cypress.com>
+ * Further cleanup and restructuring by:
+ *   Daniel Kurtz <djkurtz@chromium.org>
+ *   Benson Leung <bleung@chromium.org>
+ *
+ * Copyright (C) 2011-2015 Cypress Semiconductor, Inc.
+ * Copyright (C) 2011-2012 Google, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/pm_runtime.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include "cyapa.h"
+
+
+#define CYAPA_ADAPTER_FUNC_NONE   0
+#define CYAPA_ADAPTER_FUNC_I2C    1
+#define CYAPA_ADAPTER_FUNC_SMBUS  2
+#define CYAPA_ADAPTER_FUNC_BOTH   3
+
+#define CYAPA_FW_NAME		"cyapa.bin"
+
+const char product_id[] = "CYTRA";
+
+static int cyapa_reinitialize(struct cyapa *cyapa);
+
+bool cyapa_is_pip_bl_mode(struct cyapa *cyapa)
+{
+	if (cyapa->gen == CYAPA_GEN6 && cyapa->state == CYAPA_STATE_GEN6_BL)
+		return true;
+
+	if (cyapa->gen == CYAPA_GEN5 && cyapa->state == CYAPA_STATE_GEN5_BL)
+		return true;
+
+	return false;
+}
+
+bool cyapa_is_pip_app_mode(struct cyapa *cyapa)
+{
+	if (cyapa->gen == CYAPA_GEN6 && cyapa->state == CYAPA_STATE_GEN6_APP)
+		return true;
+
+	if (cyapa->gen == CYAPA_GEN5 && cyapa->state == CYAPA_STATE_GEN5_APP)
+		return true;
+
+	return false;
+}
+
+static bool cyapa_is_bootloader_mode(struct cyapa *cyapa)
+{
+	if (cyapa_is_pip_bl_mode(cyapa))
+		return true;
+
+	if (cyapa->gen == CYAPA_GEN3 &&
+		cyapa->state >= CYAPA_STATE_BL_BUSY &&
+		cyapa->state <= CYAPA_STATE_BL_ACTIVE)
+		return true;
+
+	return false;
+}
+
+static inline bool cyapa_is_operational_mode(struct cyapa *cyapa)
+{
+	if (cyapa_is_pip_app_mode(cyapa))
+		return true;
+
+	if (cyapa->gen == CYAPA_GEN3 && cyapa->state == CYAPA_STATE_OP)
+		return true;
+
+	return false;
+}
+
+/* Returns 0 on success, else negative errno on failure. */
+static ssize_t cyapa_i2c_read(struct cyapa *cyapa, u8 reg, size_t len,
+					u8 *values)
+{
+	struct i2c_client *client = cyapa->client;
+	struct i2c_msg msgs[] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &reg,
+		},
+		{
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = values,
+		},
+	};
+	int ret;
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+
+	if (ret != ARRAY_SIZE(msgs))
+		return ret < 0 ? ret : -EIO;
+
+	return 0;
+}
+
+/**
+ * cyapa_i2c_write - Execute i2c block data write operation
+ * @cyapa: Handle to this driver
+ * @ret: Offset of the data to written in the register map
+ * @len: number of bytes to write
+ * @values: Data to be written
+ *
+ * Return negative errno code on error; return zero when success.
+ */
+static int cyapa_i2c_write(struct cyapa *cyapa, u8 reg,
+					 size_t len, const void *values)
+{
+	struct i2c_client *client = cyapa->client;
+	char buf[32];
+	int ret;
+
+	if (len > sizeof(buf) - 1)
+		return -ENOMEM;
+
+	buf[0] = reg;
+	memcpy(&buf[1], values, len);
+
+	ret = i2c_master_send(client, buf, len + 1);
+	if (ret != len + 1)
+		return ret < 0 ? ret : -EIO;
+
+	return 0;
+}
+
+static u8 cyapa_check_adapter_functionality(struct i2c_client *client)
+{
+	u8 ret = CYAPA_ADAPTER_FUNC_NONE;
+
+	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		ret |= CYAPA_ADAPTER_FUNC_I2C;
+	if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+				     I2C_FUNC_SMBUS_BLOCK_DATA |
+				     I2C_FUNC_SMBUS_I2C_BLOCK))
+		ret |= CYAPA_ADAPTER_FUNC_SMBUS;
+	return ret;
+}
+
+/*
+ * Query device for its current operating state.
+ */
+static int cyapa_get_state(struct cyapa *cyapa)
+{
+	u8 status[BL_STATUS_SIZE];
+	u8 cmd[32];
+	/* The i2c address of gen4 and gen5 trackpad device must be even. */
+	bool even_addr = ((cyapa->client->addr & 0x0001) == 0);
+	bool smbus = false;
+	int retries = 2;
+	int error;
+
+	cyapa->state = CYAPA_STATE_NO_DEVICE;
+
+	/*
+	 * Get trackpad status by reading 3 registers starting from 0.
+	 * If the device is in the bootloader, this will be BL_HEAD.
+	 * If the device is in operation mode, this will be the DATA regs.
+	 *
+	 */
+	error = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE,
+				       status);
+
+	/*
+	 * On smbus systems in OP mode, the i2c_reg_read will fail with
+	 * -ETIMEDOUT.  In this case, try again using the smbus equivalent
+	 * command.  This should return a BL_HEAD indicating CYAPA_STATE_OP.
+	 */
+	if (cyapa->smbus && (error == -ETIMEDOUT || error == -ENXIO)) {
+		if (!even_addr)
+			error = cyapa_read_block(cyapa,
+					CYAPA_CMD_BL_STATUS, status);
+		smbus = true;
+	}
+
+	if (error != BL_STATUS_SIZE)
+		goto error;
+
+	/*
+	 * Detect trackpad protocol based on characteristic registers and bits.
+	 */
+	do {
+		cyapa->status[REG_OP_STATUS] = status[REG_OP_STATUS];
+		cyapa->status[REG_BL_STATUS] = status[REG_BL_STATUS];
+		cyapa->status[REG_BL_ERROR] = status[REG_BL_ERROR];
+
+		if (cyapa->gen == CYAPA_GEN_UNKNOWN ||
+				cyapa->gen == CYAPA_GEN3) {
+			error = cyapa_gen3_ops.state_parse(cyapa,
+					status, BL_STATUS_SIZE);
+			if (!error)
+				goto out_detected;
+		}
+		if (cyapa->gen == CYAPA_GEN_UNKNOWN ||
+				cyapa->gen == CYAPA_GEN6 ||
+				cyapa->gen == CYAPA_GEN5) {
+			error = cyapa_pip_state_parse(cyapa,
+					status, BL_STATUS_SIZE);
+			if (!error)
+				goto out_detected;
+		}
+		/* For old Gen5 trackpads detecting. */
+		if ((cyapa->gen == CYAPA_GEN_UNKNOWN ||
+				cyapa->gen == CYAPA_GEN5) &&
+			!smbus && even_addr) {
+			error = cyapa_gen5_ops.state_parse(cyapa,
+					status, BL_STATUS_SIZE);
+			if (!error)
+				goto out_detected;
+		}
+
+		/*
+		 * Write 0x00 0x00 to trackpad device to force update its
+		 * status, then redo the detection again.
+		 */
+		if (!smbus) {
+			cmd[0] = 0x00;
+			cmd[1] = 0x00;
+			error = cyapa_i2c_write(cyapa, 0, 2, cmd);
+			if (error)
+				goto error;
+
+			msleep(50);
+
+			error = cyapa_i2c_read(cyapa, BL_HEAD_OFFSET,
+					BL_STATUS_SIZE, status);
+			if (error)
+				goto error;
+		}
+	} while (--retries > 0 && !smbus);
+
+	goto error;
+
+out_detected:
+	if (cyapa->state <= CYAPA_STATE_BL_BUSY)
+		return -EAGAIN;
+	return 0;
+
+error:
+	return (error < 0) ? error : -EAGAIN;
+}
+
+/*
+ * Poll device for its status in a loop, waiting up to timeout for a response.
+ *
+ * When the device switches state, it usually takes ~300 ms.
+ * However, when running a new firmware image, the device must calibrate its
+ * sensors, which can take as long as 2 seconds.
+ *
+ * Note: The timeout has granularity of the polling rate, which is 100 ms.
+ *
+ * Returns:
+ *   0 when the device eventually responds with a valid non-busy state.
+ *   -ETIMEDOUT if device never responds (too many -EAGAIN)
+ *   -EAGAIN    if bootload is busy, or unknown state.
+ *   < 0        other errors
+ */
+int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout)
+{
+	int error;
+	int tries = timeout / 100;
+
+	do {
+		error = cyapa_get_state(cyapa);
+		if (!error && cyapa->state > CYAPA_STATE_BL_BUSY)
+			return 0;
+
+		msleep(100);
+	} while (tries--);
+
+	return (error == -EAGAIN || error == -ETIMEDOUT) ? -ETIMEDOUT : error;
+}
+
+/*
+ * Check if device is operational.
+ *
+ * An operational device is responding, has exited bootloader, and has
+ * firmware supported by this driver.
+ *
+ * Returns:
+ *   -ENODEV no device
+ *   -EBUSY  no device or in bootloader
+ *   -EIO    failure while reading from device
+ *   -ETIMEDOUT timeout failure for bus idle or bus no response
+ *   -EAGAIN device is still in bootloader
+ *           if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware
+ *   -EINVAL device is in operational mode, but not supported by this driver
+ *   0       device is supported
+ */
+static int cyapa_check_is_operational(struct cyapa *cyapa)
+{
+	int error;
+
+	error = cyapa_poll_state(cyapa, 4000);
+	if (error)
+		return error;
+
+	switch (cyapa->gen) {
+	case CYAPA_GEN6:
+		cyapa->ops = &cyapa_gen6_ops;
+		break;
+	case CYAPA_GEN5:
+		cyapa->ops = &cyapa_gen5_ops;
+		break;
+	case CYAPA_GEN3:
+		cyapa->ops = &cyapa_gen3_ops;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	error = cyapa->ops->operational_check(cyapa);
+	if (!error && cyapa_is_operational_mode(cyapa))
+		cyapa->operational = true;
+	else
+		cyapa->operational = false;
+
+	return error;
+}
+
+
+/*
+ * Returns 0 on device detected, negative errno on no device detected.
+ * And when the device is detected and operational, it will be reset to
+ * full power active mode automatically.
+ */
+static int cyapa_detect(struct cyapa *cyapa)
+{
+	struct device *dev = &cyapa->client->dev;
+	int error;
+
+	error = cyapa_check_is_operational(cyapa);
+	if (error) {
+		if (error != -ETIMEDOUT && error != -ENODEV &&
+			cyapa_is_bootloader_mode(cyapa)) {
+			dev_warn(dev, "device detected but not operational\n");
+			return 0;
+		}
+
+		dev_err(dev, "no device detected: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int cyapa_open(struct input_dev *input)
+{
+	struct cyapa *cyapa = input_get_drvdata(input);
+	struct i2c_client *client = cyapa->client;
+	struct device *dev = &client->dev;
+	int error;
+
+	error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+	if (error)
+		return error;
+
+	if (cyapa->operational) {
+		/*
+		 * though failed to set active power mode,
+		 * but still may be able to work in lower scan rate
+		 * when in operational mode.
+		 */
+		error = cyapa->ops->set_power_mode(cyapa,
+				PWR_MODE_FULL_ACTIVE, 0, false);
+		if (error) {
+			dev_warn(dev, "set active power failed: %d\n", error);
+			goto out;
+		}
+	} else {
+		error = cyapa_reinitialize(cyapa);
+		if (error || !cyapa->operational) {
+			error = error ? error : -EAGAIN;
+			goto out;
+		}
+	}
+
+	enable_irq(client->irq);
+	if (!pm_runtime_enabled(dev)) {
+		pm_runtime_set_active(dev);
+		pm_runtime_enable(dev);
+	}
+
+	pm_runtime_get_sync(dev);
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_sync_autosuspend(dev);
+out:
+	mutex_unlock(&cyapa->state_sync_lock);
+	return error;
+}
+
+static void cyapa_close(struct input_dev *input)
+{
+	struct cyapa *cyapa = input_get_drvdata(input);
+	struct i2c_client *client = cyapa->client;
+	struct device *dev = &cyapa->client->dev;
+
+	mutex_lock(&cyapa->state_sync_lock);
+
+	disable_irq(client->irq);
+	if (pm_runtime_enabled(dev))
+		pm_runtime_disable(dev);
+	pm_runtime_set_suspended(dev);
+
+	if (cyapa->operational)
+		cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false);
+
+	mutex_unlock(&cyapa->state_sync_lock);
+}
+
+static int cyapa_create_input_dev(struct cyapa *cyapa)
+{
+	struct device *dev = &cyapa->client->dev;
+	struct input_dev *input;
+	int error;
+
+	if (!cyapa->physical_size_x || !cyapa->physical_size_y)
+		return -EINVAL;
+
+	input = devm_input_allocate_device(dev);
+	if (!input) {
+		dev_err(dev, "failed to allocate memory for input device.\n");
+		return -ENOMEM;
+	}
+
+	input->name = CYAPA_NAME;
+	input->phys = cyapa->phys;
+	input->id.bustype = BUS_I2C;
+	input->id.version = 1;
+	input->id.product = 0;  /* Means any product in eventcomm. */
+	input->dev.parent = &cyapa->client->dev;
+
+	input->open = cyapa_open;
+	input->close = cyapa_close;
+
+	input_set_drvdata(input, cyapa);
+
+	__set_bit(EV_ABS, input->evbit);
+
+	/* Finger position */
+	input_set_abs_params(input, ABS_MT_POSITION_X, 0, cyapa->max_abs_x, 0,
+			     0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cyapa->max_abs_y, 0,
+			     0);
+	input_set_abs_params(input, ABS_MT_PRESSURE, 0, cyapa->max_z, 0, 0);
+	if (cyapa->gen > CYAPA_GEN3) {
+		input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+		input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0);
+		/*
+		 * Orientation is the angle between the vertical axis and
+		 * the major axis of the contact ellipse.
+		 * The range is -127 to 127.
+		 * the positive direction is clockwise form the vertical axis.
+		 * If the ellipse of contact degenerates into a circle,
+		 * orientation is reported as 0.
+		 *
+		 * Also, for Gen5 trackpad the accurate of this orientation
+		 * value is value + (-30 ~ 30).
+		 */
+		input_set_abs_params(input, ABS_MT_ORIENTATION,
+				-127, 127, 0, 0);
+	}
+	if (cyapa->gen >= CYAPA_GEN5) {
+		input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
+		input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 255, 0, 0);
+		input_set_abs_params(input, ABS_DISTANCE, 0, 1, 0, 0);
+	}
+
+	input_abs_set_res(input, ABS_MT_POSITION_X,
+			  cyapa->max_abs_x / cyapa->physical_size_x);
+	input_abs_set_res(input, ABS_MT_POSITION_Y,
+			  cyapa->max_abs_y / cyapa->physical_size_y);
+
+	if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK)
+		__set_bit(BTN_LEFT, input->keybit);
+	if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK)
+		__set_bit(BTN_MIDDLE, input->keybit);
+	if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK)
+		__set_bit(BTN_RIGHT, input->keybit);
+
+	if (cyapa->btn_capability == CAPABILITY_LEFT_BTN_MASK)
+		__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+
+	/* Handle pointer emulation and unused slots in core */
+	error = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS,
+				    INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(dev, "failed to initialize MT slots: %d\n", error);
+		return error;
+	}
+
+	/* Register the device in input subsystem */
+	error = input_register_device(input);
+	if (error) {
+		dev_err(dev, "failed to register input device: %d\n", error);
+		return error;
+	}
+
+	cyapa->input = input;
+	return 0;
+}
+
+static void cyapa_enable_irq_for_cmd(struct cyapa *cyapa)
+{
+	struct input_dev *input = cyapa->input;
+
+	if (!input || !input->users) {
+		/*
+		 * When input is NULL, TP must be in deep sleep mode.
+		 * In this mode, later non-power I2C command will always failed
+		 * if not bring it out of deep sleep mode firstly,
+		 * so must command TP to active mode here.
+		 */
+		if (!input || cyapa->operational)
+			cyapa->ops->set_power_mode(cyapa,
+				PWR_MODE_FULL_ACTIVE, 0, false);
+		/* Gen3 always using polling mode for command. */
+		if (cyapa->gen >= CYAPA_GEN5)
+			enable_irq(cyapa->client->irq);
+	}
+}
+
+static void cyapa_disable_irq_for_cmd(struct cyapa *cyapa)
+{
+	struct input_dev *input = cyapa->input;
+
+	if (!input || !input->users) {
+		if (cyapa->gen >= CYAPA_GEN5)
+			disable_irq(cyapa->client->irq);
+		if (!input || cyapa->operational)
+			cyapa->ops->set_power_mode(cyapa,
+						   PWR_MODE_OFF, 0, false);
+	}
+}
+
+/*
+ * cyapa_sleep_time_to_pwr_cmd and cyapa_pwr_cmd_to_sleep_time
+ *
+ * These are helper functions that convert to and from integer idle
+ * times and register settings to write to the PowerMode register.
+ * The trackpad supports between 20ms to 1000ms scan intervals.
+ * The time will be increased in increments of 10ms from 20ms to 100ms.
+ * From 100ms to 1000ms, time will be increased in increments of 20ms.
+ *
+ * When Idle_Time < 100, the format to convert Idle_Time to Idle_Command is:
+ *   Idle_Command = Idle Time / 10;
+ * When Idle_Time >= 100, the format to convert Idle_Time to Idle_Command is:
+ *   Idle_Command = Idle Time / 20 + 5;
+ */
+u8 cyapa_sleep_time_to_pwr_cmd(u16 sleep_time)
+{
+	u16 encoded_time;
+
+	sleep_time = clamp_val(sleep_time, 20, 1000);
+	encoded_time = sleep_time < 100 ? sleep_time / 10 : sleep_time / 20 + 5;
+	return (encoded_time << 2) & PWR_MODE_MASK;
+}
+
+u16 cyapa_pwr_cmd_to_sleep_time(u8 pwr_mode)
+{
+	u8 encoded_time = pwr_mode >> 2;
+
+	return (encoded_time < 10) ? encoded_time * 10
+				   : (encoded_time - 5) * 20;
+}
+
+/* 0 on driver initialize and detected successfully, negative on failure. */
+static int cyapa_initialize(struct cyapa *cyapa)
+{
+	int error = 0;
+
+	cyapa->state = CYAPA_STATE_NO_DEVICE;
+	cyapa->gen = CYAPA_GEN_UNKNOWN;
+	mutex_init(&cyapa->state_sync_lock);
+
+	/*
+	 * Set to hard code default, they will be updated with trackpad set
+	 * default values after probe and initialized.
+	 */
+	cyapa->suspend_power_mode = PWR_MODE_SLEEP;
+	cyapa->suspend_sleep_time =
+		cyapa_pwr_cmd_to_sleep_time(cyapa->suspend_power_mode);
+
+	/* ops.initialize() is aimed to prepare for module communications. */
+	error = cyapa_gen3_ops.initialize(cyapa);
+	if (!error)
+		error = cyapa_gen5_ops.initialize(cyapa);
+	if (!error)
+		error = cyapa_gen6_ops.initialize(cyapa);
+	if (error)
+		return error;
+
+	error = cyapa_detect(cyapa);
+	if (error)
+		return error;
+
+	/* Power down the device until we need it. */
+	if (cyapa->operational)
+		cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false);
+
+	return 0;
+}
+
+static int cyapa_reinitialize(struct cyapa *cyapa)
+{
+	struct device *dev = &cyapa->client->dev;
+	struct input_dev *input = cyapa->input;
+	int error;
+
+	if (pm_runtime_enabled(dev))
+		pm_runtime_disable(dev);
+
+	/* Avoid command failures when TP was in OFF state. */
+	if (cyapa->operational)
+		cyapa->ops->set_power_mode(cyapa,
+					   PWR_MODE_FULL_ACTIVE, 0, false);
+
+	error = cyapa_detect(cyapa);
+	if (error)
+		goto out;
+
+	if (!input && cyapa->operational) {
+		error = cyapa_create_input_dev(cyapa);
+		if (error) {
+			dev_err(dev, "create input_dev instance failed: %d\n",
+					error);
+			goto out;
+		}
+	}
+
+out:
+	if (!input || !input->users) {
+		/* Reset to power OFF state to save power when no user open. */
+		if (cyapa->operational)
+			cyapa->ops->set_power_mode(cyapa,
+						   PWR_MODE_OFF, 0, false);
+	} else if (!error && cyapa->operational) {
+		/*
+		 * Make sure only enable runtime PM when device is
+		 * in operational mode and input->users > 0.
+		 */
+		pm_runtime_set_active(dev);
+		pm_runtime_enable(dev);
+
+		pm_runtime_get_sync(dev);
+		pm_runtime_mark_last_busy(dev);
+		pm_runtime_put_sync_autosuspend(dev);
+	}
+
+	return error;
+}
+
+static irqreturn_t cyapa_irq(int irq, void *dev_id)
+{
+	struct cyapa *cyapa = dev_id;
+	struct device *dev = &cyapa->client->dev;
+	int error;
+
+	if (device_may_wakeup(dev))
+		pm_wakeup_event(dev, 0);
+
+	/* Interrupt event can be caused by host command to trackpad device. */
+	if (cyapa->ops->irq_cmd_handler(cyapa)) {
+		/*
+		 * Interrupt event maybe from trackpad device input reporting.
+		 */
+		if (!cyapa->input) {
+			/*
+			 * Still in probing or in firmware image
+			 * updating or reading.
+			 */
+			cyapa->ops->sort_empty_output_data(cyapa,
+					NULL, NULL, NULL);
+			goto out;
+		}
+
+		if (cyapa->operational) {
+			error = cyapa->ops->irq_handler(cyapa);
+
+			/*
+			 * Apply runtime power management to touch report event
+			 * except the events caused by the command responses.
+			 * Note:
+			 * It will introduce about 20~40 ms additional delay
+			 * time in receiving for first valid touch report data.
+			 * The time is used to execute device runtime resume
+			 * process.
+			 */
+			pm_runtime_get_sync(dev);
+			pm_runtime_mark_last_busy(dev);
+			pm_runtime_put_sync_autosuspend(dev);
+		}
+
+		if (!cyapa->operational || error) {
+			if (!mutex_trylock(&cyapa->state_sync_lock)) {
+				cyapa->ops->sort_empty_output_data(cyapa,
+					NULL, NULL, NULL);
+				goto out;
+			}
+			cyapa_reinitialize(cyapa);
+			mutex_unlock(&cyapa->state_sync_lock);
+		}
+	}
+
+out:
+	return IRQ_HANDLED;
+}
+
+/*
+ **************************************************************
+ * sysfs interface
+ **************************************************************
+*/
+#ifdef CONFIG_PM_SLEEP
+static ssize_t cyapa_show_suspend_scanrate(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	u8 pwr_cmd = cyapa->suspend_power_mode;
+	u16 sleep_time;
+	int len;
+	int error;
+
+	error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+	if (error)
+		return error;
+
+	pwr_cmd = cyapa->suspend_power_mode;
+	sleep_time = cyapa->suspend_sleep_time;
+
+	mutex_unlock(&cyapa->state_sync_lock);
+
+	switch (pwr_cmd) {
+	case PWR_MODE_BTN_ONLY:
+		len = scnprintf(buf, PAGE_SIZE, "%s\n", BTN_ONLY_MODE_NAME);
+		break;
+
+	case PWR_MODE_OFF:
+		len = scnprintf(buf, PAGE_SIZE, "%s\n", OFF_MODE_NAME);
+		break;
+
+	default:
+		len = scnprintf(buf, PAGE_SIZE, "%u\n",
+				cyapa->gen == CYAPA_GEN3 ?
+					cyapa_pwr_cmd_to_sleep_time(pwr_cmd) :
+					sleep_time);
+		break;
+	}
+
+	return len;
+}
+
+static ssize_t cyapa_update_suspend_scanrate(struct device *dev,
+					     struct device_attribute *attr,
+					     const char *buf, size_t count)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	u16 sleep_time;
+	int error;
+
+	error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+	if (error)
+		return error;
+
+	if (sysfs_streq(buf, BTN_ONLY_MODE_NAME)) {
+		cyapa->suspend_power_mode = PWR_MODE_BTN_ONLY;
+	} else if (sysfs_streq(buf, OFF_MODE_NAME)) {
+		cyapa->suspend_power_mode = PWR_MODE_OFF;
+	} else if (!kstrtou16(buf, 10, &sleep_time)) {
+		cyapa->suspend_sleep_time = min_t(u16, sleep_time, 1000);
+		cyapa->suspend_power_mode =
+			cyapa_sleep_time_to_pwr_cmd(cyapa->suspend_sleep_time);
+	} else {
+		count = -EINVAL;
+	}
+
+	mutex_unlock(&cyapa->state_sync_lock);
+
+	return count;
+}
+
+static DEVICE_ATTR(suspend_scanrate_ms, S_IRUGO|S_IWUSR,
+		   cyapa_show_suspend_scanrate,
+		   cyapa_update_suspend_scanrate);
+
+static struct attribute *cyapa_power_wakeup_entries[] = {
+	&dev_attr_suspend_scanrate_ms.attr,
+	NULL,
+};
+
+static const struct attribute_group cyapa_power_wakeup_group = {
+	.name = power_group_name,
+	.attrs = cyapa_power_wakeup_entries,
+};
+
+static void cyapa_remove_power_wakeup_group(void *data)
+{
+	struct cyapa *cyapa = data;
+
+	sysfs_unmerge_group(&cyapa->client->dev.kobj,
+				&cyapa_power_wakeup_group);
+}
+
+static int cyapa_prepare_wakeup_controls(struct cyapa *cyapa)
+{
+	struct i2c_client *client = cyapa->client;
+	struct device *dev = &client->dev;
+	int error;
+
+	if (device_can_wakeup(dev)) {
+		error = sysfs_merge_group(&client->dev.kobj,
+					&cyapa_power_wakeup_group);
+		if (error) {
+			dev_err(dev, "failed to add power wakeup group: %d\n",
+				error);
+			return error;
+		}
+
+		error = devm_add_action(dev,
+				cyapa_remove_power_wakeup_group, cyapa);
+		if (error) {
+			cyapa_remove_power_wakeup_group(cyapa);
+			dev_err(dev, "failed to add power cleanup action: %d\n",
+				error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+#else
+static inline int cyapa_prepare_wakeup_controls(struct cyapa *cyapa)
+{
+	return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM
+static ssize_t cyapa_show_rt_suspend_scanrate(struct device *dev,
+					      struct device_attribute *attr,
+					      char *buf)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	u8 pwr_cmd;
+	u16 sleep_time;
+	int error;
+
+	error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+	if (error)
+		return error;
+
+	pwr_cmd = cyapa->runtime_suspend_power_mode;
+	sleep_time = cyapa->runtime_suspend_sleep_time;
+
+	mutex_unlock(&cyapa->state_sync_lock);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 cyapa->gen == CYAPA_GEN3 ?
+				cyapa_pwr_cmd_to_sleep_time(pwr_cmd) :
+				sleep_time);
+}
+
+static ssize_t cyapa_update_rt_suspend_scanrate(struct device *dev,
+						struct device_attribute *attr,
+						const char *buf, size_t count)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	u16 time;
+	int error;
+
+	if (buf == NULL || count == 0 || kstrtou16(buf, 10, &time)) {
+		dev_err(dev, "invalid runtime suspend scanrate ms parameter\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * When the suspend scanrate is changed, pm_runtime_get to resume
+	 * a potentially suspended device, update to the new pwr_cmd
+	 * and then pm_runtime_put to suspend into the new power mode.
+	 */
+	pm_runtime_get_sync(dev);
+
+	error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+	if (error)
+		return error;
+
+	cyapa->runtime_suspend_sleep_time = min_t(u16, time, 1000);
+	cyapa->runtime_suspend_power_mode =
+		cyapa_sleep_time_to_pwr_cmd(cyapa->runtime_suspend_sleep_time);
+
+	mutex_unlock(&cyapa->state_sync_lock);
+
+	pm_runtime_put_sync_autosuspend(dev);
+
+	return count;
+}
+
+static DEVICE_ATTR(runtime_suspend_scanrate_ms, S_IRUGO|S_IWUSR,
+		   cyapa_show_rt_suspend_scanrate,
+		   cyapa_update_rt_suspend_scanrate);
+
+static struct attribute *cyapa_power_runtime_entries[] = {
+	&dev_attr_runtime_suspend_scanrate_ms.attr,
+	NULL,
+};
+
+static const struct attribute_group cyapa_power_runtime_group = {
+	.name = power_group_name,
+	.attrs = cyapa_power_runtime_entries,
+};
+
+static void cyapa_remove_power_runtime_group(void *data)
+{
+	struct cyapa *cyapa = data;
+
+	sysfs_unmerge_group(&cyapa->client->dev.kobj,
+				&cyapa_power_runtime_group);
+}
+
+static int cyapa_start_runtime(struct cyapa *cyapa)
+{
+	struct device *dev = &cyapa->client->dev;
+	int error;
+
+	cyapa->runtime_suspend_power_mode = PWR_MODE_IDLE;
+	cyapa->runtime_suspend_sleep_time =
+		cyapa_pwr_cmd_to_sleep_time(cyapa->runtime_suspend_power_mode);
+
+	error = sysfs_merge_group(&dev->kobj, &cyapa_power_runtime_group);
+	if (error) {
+		dev_err(dev,
+			"failed to create power runtime group: %d\n", error);
+		return error;
+	}
+
+	error = devm_add_action(dev, cyapa_remove_power_runtime_group, cyapa);
+	if (error) {
+		cyapa_remove_power_runtime_group(cyapa);
+		dev_err(dev,
+			"failed to add power runtime cleanup action: %d\n",
+			error);
+		return error;
+	}
+
+	/* runtime is enabled until device is operational and opened. */
+	pm_runtime_set_suspended(dev);
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_DELAY);
+
+	return 0;
+}
+#else
+static inline int cyapa_start_runtime(struct cyapa *cyapa)
+{
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static ssize_t cyapa_show_fm_ver(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	int error;
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+
+	error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+	if (error)
+		return error;
+	error = scnprintf(buf, PAGE_SIZE, "%d.%d\n", cyapa->fw_maj_ver,
+			 cyapa->fw_min_ver);
+	mutex_unlock(&cyapa->state_sync_lock);
+	return error;
+}
+
+static ssize_t cyapa_show_product_id(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	int size;
+	int error;
+
+	error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+	if (error)
+		return error;
+	size = scnprintf(buf, PAGE_SIZE, "%s\n", cyapa->product_id);
+	mutex_unlock(&cyapa->state_sync_lock);
+	return size;
+}
+
+static int cyapa_firmware(struct cyapa *cyapa, const char *fw_name)
+{
+	struct device *dev = &cyapa->client->dev;
+	const struct firmware *fw;
+	int error;
+
+	error = request_firmware(&fw, fw_name, dev);
+	if (error) {
+		dev_err(dev, "Could not load firmware from %s: %d\n",
+			fw_name, error);
+		return error;
+	}
+
+	error = cyapa->ops->check_fw(cyapa, fw);
+	if (error) {
+		dev_err(dev, "Invalid CYAPA firmware image: %s\n",
+				fw_name);
+		goto done;
+	}
+
+	/*
+	 * Resume the potentially suspended device because doing FW
+	 * update on a device not in the FULL mode has a chance to
+	 * fail.
+	 */
+	pm_runtime_get_sync(dev);
+
+	/* Require IRQ support for firmware update commands. */
+	cyapa_enable_irq_for_cmd(cyapa);
+
+	error = cyapa->ops->bl_enter(cyapa);
+	if (error) {
+		dev_err(dev, "bl_enter failed, %d\n", error);
+		goto err_detect;
+	}
+
+	error = cyapa->ops->bl_activate(cyapa);
+	if (error) {
+		dev_err(dev, "bl_activate failed, %d\n", error);
+		goto err_detect;
+	}
+
+	error = cyapa->ops->bl_initiate(cyapa, fw);
+	if (error) {
+		dev_err(dev, "bl_initiate failed, %d\n", error);
+		goto err_detect;
+	}
+
+	error = cyapa->ops->update_fw(cyapa, fw);
+	if (error) {
+		dev_err(dev, "update_fw failed, %d\n", error);
+		goto err_detect;
+	}
+
+err_detect:
+	cyapa_disable_irq_for_cmd(cyapa);
+	pm_runtime_put_noidle(dev);
+
+done:
+	release_firmware(fw);
+	return error;
+}
+
+static ssize_t cyapa_update_fw_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	char fw_name[NAME_MAX];
+	int ret, error;
+
+	if (count >= NAME_MAX) {
+		dev_err(dev, "File name too long\n");
+		return -EINVAL;
+	}
+
+	memcpy(fw_name, buf, count);
+	if (fw_name[count - 1] == '\n')
+		fw_name[count - 1] = '\0';
+	else
+		fw_name[count] = '\0';
+
+	if (cyapa->input) {
+		/*
+		 * Force the input device to be registered after the firmware
+		 * image is updated, so if the corresponding parameters updated
+		 * in the new firmware image can taken effect immediately.
+		 */
+		input_unregister_device(cyapa->input);
+		cyapa->input = NULL;
+	}
+
+	error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+	if (error) {
+		/*
+		 * Whatever, do reinitialize to try to recover TP state to
+		 * previous state just as it entered fw update entrance.
+		 */
+		cyapa_reinitialize(cyapa);
+		return error;
+	}
+
+	error = cyapa_firmware(cyapa, fw_name);
+	if (error)
+		dev_err(dev, "firmware update failed: %d\n", error);
+	else
+		dev_dbg(dev, "firmware update successfully done.\n");
+
+	/*
+	 * Re-detect trackpad device states because firmware update process
+	 * will reset trackpad device into bootloader mode.
+	 */
+	ret = cyapa_reinitialize(cyapa);
+	if (ret) {
+		dev_err(dev, "failed to re-detect after updated: %d\n", ret);
+		error = error ? error : ret;
+	}
+
+	mutex_unlock(&cyapa->state_sync_lock);
+
+	return error ? error : count;
+}
+
+static ssize_t cyapa_calibrate_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	int error;
+
+	error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+	if (error)
+		return error;
+
+	if (cyapa->operational) {
+		cyapa_enable_irq_for_cmd(cyapa);
+		error = cyapa->ops->calibrate_store(dev, attr, buf, count);
+		cyapa_disable_irq_for_cmd(cyapa);
+	} else {
+		error = -EBUSY;  /* Still running in bootloader mode. */
+	}
+
+	mutex_unlock(&cyapa->state_sync_lock);
+	return error < 0 ? error : count;
+}
+
+static ssize_t cyapa_show_baseline(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	ssize_t error;
+
+	error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+	if (error)
+		return error;
+
+	if (cyapa->operational) {
+		cyapa_enable_irq_for_cmd(cyapa);
+		error = cyapa->ops->show_baseline(dev, attr, buf);
+		cyapa_disable_irq_for_cmd(cyapa);
+	} else {
+		error = -EBUSY;  /* Still running in bootloader mode. */
+	}
+
+	mutex_unlock(&cyapa->state_sync_lock);
+	return error;
+}
+
+static char *cyapa_state_to_string(struct cyapa *cyapa)
+{
+	switch (cyapa->state) {
+	case CYAPA_STATE_BL_BUSY:
+		return "bootloader busy";
+	case CYAPA_STATE_BL_IDLE:
+		return "bootloader idle";
+	case CYAPA_STATE_BL_ACTIVE:
+		return "bootloader active";
+	case CYAPA_STATE_GEN5_BL:
+	case CYAPA_STATE_GEN6_BL:
+		return "bootloader";
+	case CYAPA_STATE_OP:
+	case CYAPA_STATE_GEN5_APP:
+	case CYAPA_STATE_GEN6_APP:
+		return "operational";  /* Normal valid state. */
+	default:
+		return "invalid mode";
+	}
+}
+
+static ssize_t cyapa_show_mode(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	int size;
+	int error;
+
+	error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+	if (error)
+		return error;
+
+	size = scnprintf(buf, PAGE_SIZE, "gen%d %s\n",
+			cyapa->gen, cyapa_state_to_string(cyapa));
+
+	mutex_unlock(&cyapa->state_sync_lock);
+	return size;
+}
+
+static DEVICE_ATTR(firmware_version, S_IRUGO, cyapa_show_fm_ver, NULL);
+static DEVICE_ATTR(product_id, S_IRUGO, cyapa_show_product_id, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, cyapa_update_fw_store);
+static DEVICE_ATTR(baseline, S_IRUGO, cyapa_show_baseline, NULL);
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, cyapa_calibrate_store);
+static DEVICE_ATTR(mode, S_IRUGO, cyapa_show_mode, NULL);
+
+static struct attribute *cyapa_sysfs_entries[] = {
+	&dev_attr_firmware_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_update_fw.attr,
+	&dev_attr_baseline.attr,
+	&dev_attr_calibrate.attr,
+	&dev_attr_mode.attr,
+	NULL,
+};
+
+static const struct attribute_group cyapa_sysfs_group = {
+	.attrs = cyapa_sysfs_entries,
+};
+
+static void cyapa_remove_sysfs_group(void *data)
+{
+	struct cyapa *cyapa = data;
+
+	sysfs_remove_group(&cyapa->client->dev.kobj, &cyapa_sysfs_group);
+}
+
+static void cyapa_disable_regulator(void *data)
+{
+	struct cyapa *cyapa = data;
+
+	regulator_disable(cyapa->vcc);
+}
+
+static int cyapa_probe(struct i2c_client *client,
+		       const struct i2c_device_id *dev_id)
+{
+	struct device *dev = &client->dev;
+	struct cyapa *cyapa;
+	u8 adapter_func;
+	union i2c_smbus_data dummy;
+	int error;
+
+	adapter_func = cyapa_check_adapter_functionality(client);
+	if (adapter_func == CYAPA_ADAPTER_FUNC_NONE) {
+		dev_err(dev, "not a supported I2C/SMBus adapter\n");
+		return -EIO;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0)
+		return -ENODEV;
+
+	cyapa = devm_kzalloc(dev, sizeof(struct cyapa), GFP_KERNEL);
+	if (!cyapa)
+		return -ENOMEM;
+
+	/* i2c isn't supported, use smbus */
+	if (adapter_func == CYAPA_ADAPTER_FUNC_SMBUS)
+		cyapa->smbus = true;
+
+	cyapa->client = client;
+	i2c_set_clientdata(client, cyapa);
+	sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr,
+		client->addr);
+
+	cyapa->vcc = devm_regulator_get(dev, "vcc");
+	if (IS_ERR(cyapa->vcc)) {
+		error = PTR_ERR(cyapa->vcc);
+		dev_err(dev, "failed to get vcc regulator: %d\n", error);
+		return error;
+	}
+
+	error = regulator_enable(cyapa->vcc);
+	if (error) {
+		dev_err(dev, "failed to enable regulator: %d\n", error);
+		return error;
+	}
+
+	error = devm_add_action(dev, cyapa_disable_regulator, cyapa);
+	if (error) {
+		cyapa_disable_regulator(cyapa);
+		dev_err(dev, "failed to add disable regulator action: %d\n",
+			error);
+		return error;
+	}
+
+	error = cyapa_initialize(cyapa);
+	if (error) {
+		dev_err(dev, "failed to detect and initialize tp device.\n");
+		return error;
+	}
+
+	error = sysfs_create_group(&client->dev.kobj, &cyapa_sysfs_group);
+	if (error) {
+		dev_err(dev, "failed to create sysfs entries: %d\n", error);
+		return error;
+	}
+
+	error = devm_add_action(dev, cyapa_remove_sysfs_group, cyapa);
+	if (error) {
+		cyapa_remove_sysfs_group(cyapa);
+		dev_err(dev, "failed to add sysfs cleanup action: %d\n", error);
+		return error;
+	}
+
+	error = cyapa_prepare_wakeup_controls(cyapa);
+	if (error) {
+		dev_err(dev, "failed to prepare wakeup controls: %d\n", error);
+		return error;
+	}
+
+	error = cyapa_start_runtime(cyapa);
+	if (error) {
+		dev_err(dev, "failed to start pm_runtime: %d\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(dev, client->irq,
+					  NULL, cyapa_irq,
+					  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					  "cyapa", cyapa);
+	if (error) {
+		dev_err(dev, "failed to request threaded irq: %d\n", error);
+		return error;
+	}
+
+	/* Disable IRQ until the device is opened */
+	disable_irq(client->irq);
+
+	/*
+	 * Register the device in the input subsystem when it's operational.
+	 * Otherwise, keep in this driver, so it can be be recovered or updated
+	 * through the sysfs mode and update_fw interfaces by user or apps.
+	 */
+	if (cyapa->operational) {
+		error = cyapa_create_input_dev(cyapa);
+		if (error) {
+			dev_err(dev, "create input_dev instance failed: %d\n",
+					error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static int __maybe_unused cyapa_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct cyapa *cyapa = i2c_get_clientdata(client);
+	u8 power_mode;
+	int error;
+
+	error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+	if (error)
+		return error;
+
+	/*
+	 * Runtime PM is enable only when device is in operational mode and
+	 * users in use, so need check it before disable it to
+	 * avoid unbalance warning.
+	 */
+	if (pm_runtime_enabled(dev))
+		pm_runtime_disable(dev);
+	disable_irq(client->irq);
+
+	/*
+	 * Set trackpad device to idle mode if wakeup is allowed,
+	 * otherwise turn off.
+	 */
+	if (cyapa->operational) {
+		power_mode = device_may_wakeup(dev) ? cyapa->suspend_power_mode
+						    : PWR_MODE_OFF;
+		error = cyapa->ops->set_power_mode(cyapa, power_mode,
+				cyapa->suspend_sleep_time, true);
+		if (error)
+			dev_err(dev, "suspend set power mode failed: %d\n",
+					error);
+	}
+
+	/*
+	 * Disable proximity interrupt when system idle, want true touch to
+	 * wake the system.
+	 */
+	if (cyapa->dev_pwr_mode != PWR_MODE_OFF)
+		cyapa->ops->set_proximity(cyapa, false);
+
+	if (device_may_wakeup(dev))
+		cyapa->irq_wake = (enable_irq_wake(client->irq) == 0);
+
+	mutex_unlock(&cyapa->state_sync_lock);
+	return 0;
+}
+
+static int __maybe_unused cyapa_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct cyapa *cyapa = i2c_get_clientdata(client);
+	int error;
+
+	mutex_lock(&cyapa->state_sync_lock);
+
+	if (device_may_wakeup(dev) && cyapa->irq_wake) {
+		disable_irq_wake(client->irq);
+		cyapa->irq_wake = false;
+	}
+
+	/*
+	 * Update device states and runtime PM states.
+	 * Re-Enable proximity interrupt after enter operational mode.
+	 */
+	error = cyapa_reinitialize(cyapa);
+	if (error)
+		dev_warn(dev, "failed to reinitialize TP device: %d\n", error);
+
+	enable_irq(client->irq);
+
+	mutex_unlock(&cyapa->state_sync_lock);
+	return 0;
+}
+
+static int __maybe_unused cyapa_runtime_suspend(struct device *dev)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	int error;
+
+	error = cyapa->ops->set_power_mode(cyapa,
+			cyapa->runtime_suspend_power_mode,
+			cyapa->runtime_suspend_sleep_time,
+			false);
+	if (error)
+		dev_warn(dev, "runtime suspend failed: %d\n", error);
+
+	return 0;
+}
+
+static int __maybe_unused cyapa_runtime_resume(struct device *dev)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	int error;
+
+	error = cyapa->ops->set_power_mode(cyapa,
+					   PWR_MODE_FULL_ACTIVE, 0, false);
+	if (error)
+		dev_warn(dev, "runtime resume failed: %d\n", error);
+
+	return 0;
+}
+
+static const struct dev_pm_ops cyapa_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(cyapa_suspend, cyapa_resume)
+	SET_RUNTIME_PM_OPS(cyapa_runtime_suspend, cyapa_runtime_resume, NULL)
+};
+
+static const struct i2c_device_id cyapa_id_table[] = {
+	{ "cyapa", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, cyapa_id_table);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cyapa_acpi_id[] = {
+	{ "CYAP0000", 0 },  /* Gen3 trackpad with 0x67 I2C address. */
+	{ "CYAP0001", 0 },  /* Gen5 trackpad with 0x24 I2C address. */
+	{ "CYAP0002", 0 },  /* Gen6 trackpad with 0x24 I2C address. */
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, cyapa_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id cyapa_of_match[] = {
+	{ .compatible = "cypress,cyapa" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cyapa_of_match);
+#endif
+
+static struct i2c_driver cyapa_driver = {
+	.driver = {
+		.name = "cyapa",
+		.pm = &cyapa_pm_ops,
+		.acpi_match_table = ACPI_PTR(cyapa_acpi_id),
+		.of_match_table = of_match_ptr(cyapa_of_match),
+	},
+
+	.probe = cyapa_probe,
+	.id_table = cyapa_id_table,
+};
+
+module_i2c_driver(cyapa_driver);
+
+MODULE_DESCRIPTION("Cypress APA I2C Trackpad Driver");
+MODULE_AUTHOR("Dudley Du <dudl@cypress.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h
new file mode 100644
index 0000000..b812bba
--- /dev/null
+++ b/drivers/input/mouse/cyapa.h
@@ -0,0 +1,434 @@
+/*
+ * Cypress APA trackpad with I2C interface
+ *
+ * Author: Dudley Du <dudl@cypress.com>
+ *
+ * Copyright (C) 2014-2015 Cypress Semiconductor, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#ifndef _CYAPA_H
+#define _CYAPA_H
+
+#include <linux/firmware.h>
+
+/* APA trackpad firmware generation number. */
+#define CYAPA_GEN_UNKNOWN   0x00   /* unknown protocol. */
+#define CYAPA_GEN3   0x03   /* support MT-protocol B with tracking ID. */
+#define CYAPA_GEN5   0x05   /* support TrueTouch GEN5 trackpad device. */
+#define CYAPA_GEN6   0x06   /* support TrueTouch GEN6 trackpad device. */
+
+#define CYAPA_NAME   "Cypress APA Trackpad (cyapa)"
+
+/*
+ * Macros for SMBus communication
+ */
+#define SMBUS_READ  0x01
+#define SMBUS_WRITE 0x00
+#define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1))
+#define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01))
+#define SMBUS_BYTE_BLOCK_CMD_MASK 0x80
+#define SMBUS_GROUP_BLOCK_CMD_MASK 0x40
+
+/* Commands for read/write registers of Cypress trackpad */
+#define CYAPA_CMD_SOFT_RESET       0x00
+#define CYAPA_CMD_POWER_MODE       0x01
+#define CYAPA_CMD_DEV_STATUS       0x02
+#define CYAPA_CMD_GROUP_DATA       0x03
+#define CYAPA_CMD_GROUP_CMD        0x04
+#define CYAPA_CMD_GROUP_QUERY      0x05
+#define CYAPA_CMD_BL_STATUS        0x06
+#define CYAPA_CMD_BL_HEAD          0x07
+#define CYAPA_CMD_BL_CMD           0x08
+#define CYAPA_CMD_BL_DATA          0x09
+#define CYAPA_CMD_BL_ALL           0x0a
+#define CYAPA_CMD_BLK_PRODUCT_ID   0x0b
+#define CYAPA_CMD_BLK_HEAD         0x0c
+#define CYAPA_CMD_MAX_BASELINE     0x0d
+#define CYAPA_CMD_MIN_BASELINE     0x0e
+
+#define BL_HEAD_OFFSET 0x00
+#define BL_DATA_OFFSET 0x10
+
+#define BL_STATUS_SIZE  3  /* Length of gen3 bootloader status registers */
+#define CYAPA_REG_MAP_SIZE  256
+
+/*
+ * Gen3 Operational Device Status Register
+ *
+ * bit 7: Valid interrupt source
+ * bit 6 - 4: Reserved
+ * bit 3 - 2: Power status
+ * bit 1 - 0: Device status
+ */
+#define REG_OP_STATUS     0x00
+#define OP_STATUS_SRC     0x80
+#define OP_STATUS_POWER   0x0c
+#define OP_STATUS_DEV     0x03
+#define OP_STATUS_MASK (OP_STATUS_SRC | OP_STATUS_POWER | OP_STATUS_DEV)
+
+/*
+ * Operational Finger Count/Button Flags Register
+ *
+ * bit 7 - 4: Number of touched finger
+ * bit 3: Valid data
+ * bit 2: Middle Physical Button
+ * bit 1: Right Physical Button
+ * bit 0: Left physical Button
+ */
+#define REG_OP_DATA1       0x01
+#define OP_DATA_VALID      0x08
+#define OP_DATA_MIDDLE_BTN 0x04
+#define OP_DATA_RIGHT_BTN  0x02
+#define OP_DATA_LEFT_BTN   0x01
+#define OP_DATA_BTN_MASK (OP_DATA_MIDDLE_BTN | OP_DATA_RIGHT_BTN | \
+			  OP_DATA_LEFT_BTN)
+
+/*
+ * Write-only command file register used to issue commands and
+ * parameters to the bootloader.
+ * The default value read from it is always 0x00.
+ */
+#define REG_BL_FILE	0x00
+#define BL_FILE		0x00
+
+/*
+ * Bootloader Status Register
+ *
+ * bit 7: Busy
+ * bit 6 - 5: Reserved
+ * bit 4: Bootloader running
+ * bit 3 - 2: Reserved
+ * bit 1: Watchdog Reset
+ * bit 0: Checksum valid
+ */
+#define REG_BL_STATUS        0x01
+#define BL_STATUS_REV_6_5    0x60
+#define BL_STATUS_BUSY       0x80
+#define BL_STATUS_RUNNING    0x10
+#define BL_STATUS_REV_3_2    0x0c
+#define BL_STATUS_WATCHDOG   0x02
+#define BL_STATUS_CSUM_VALID 0x01
+#define BL_STATUS_REV_MASK (BL_STATUS_WATCHDOG | BL_STATUS_REV_3_2 | \
+			    BL_STATUS_REV_6_5)
+
+/*
+ * Bootloader Error Register
+ *
+ * bit 7: Invalid
+ * bit 6: Invalid security key
+ * bit 5: Bootloading
+ * bit 4: Command checksum
+ * bit 3: Flash protection error
+ * bit 2: Flash checksum error
+ * bit 1 - 0: Reserved
+ */
+#define REG_BL_ERROR         0x02
+#define BL_ERROR_INVALID     0x80
+#define BL_ERROR_INVALID_KEY 0x40
+#define BL_ERROR_BOOTLOADING 0x20
+#define BL_ERROR_CMD_CSUM    0x10
+#define BL_ERROR_FLASH_PROT  0x08
+#define BL_ERROR_FLASH_CSUM  0x04
+#define BL_ERROR_RESERVED    0x03
+#define BL_ERROR_NO_ERR_IDLE    0x00
+#define BL_ERROR_NO_ERR_ACTIVE  (BL_ERROR_BOOTLOADING)
+
+#define CAPABILITY_BTN_SHIFT            3
+#define CAPABILITY_LEFT_BTN_MASK	(0x01 << 3)
+#define CAPABILITY_RIGHT_BTN_MASK	(0x01 << 4)
+#define CAPABILITY_MIDDLE_BTN_MASK	(0x01 << 5)
+#define CAPABILITY_BTN_MASK  (CAPABILITY_LEFT_BTN_MASK | \
+			      CAPABILITY_RIGHT_BTN_MASK | \
+			      CAPABILITY_MIDDLE_BTN_MASK)
+
+#define PWR_MODE_MASK   0xfc
+#define PWR_MODE_FULL_ACTIVE (0x3f << 2)
+#define PWR_MODE_IDLE        (0x03 << 2) /* Default rt suspend scanrate: 30ms */
+#define PWR_MODE_SLEEP       (0x05 << 2) /* Default suspend scanrate: 50ms */
+#define PWR_MODE_BTN_ONLY    (0x01 << 2)
+#define PWR_MODE_OFF         (0x00 << 2)
+
+#define PWR_STATUS_MASK      0x0c
+#define PWR_STATUS_ACTIVE    (0x03 << 2)
+#define PWR_STATUS_IDLE      (0x02 << 2)
+#define PWR_STATUS_BTN_ONLY  (0x01 << 2)
+#define PWR_STATUS_OFF       (0x00 << 2)
+
+#define AUTOSUSPEND_DELAY   2000 /* unit : ms */
+
+#define BTN_ONLY_MODE_NAME   "buttononly"
+#define OFF_MODE_NAME        "off"
+
+/* Common macros for PIP interface. */
+#define PIP_HID_DESCRIPTOR_ADDR		0x0001
+#define PIP_REPORT_DESCRIPTOR_ADDR	0x0002
+#define PIP_INPUT_REPORT_ADDR		0x0003
+#define PIP_OUTPUT_REPORT_ADDR		0x0004
+#define PIP_CMD_DATA_ADDR		0x0006
+
+#define PIP_RETRIEVE_DATA_STRUCTURE	0x24
+#define PIP_CMD_CALIBRATE		0x28
+#define PIP_BL_CMD_VERIFY_APP_INTEGRITY	0x31
+#define PIP_BL_CMD_GET_BL_INFO		0x38
+#define PIP_BL_CMD_PROGRAM_VERIFY_ROW	0x39
+#define PIP_BL_CMD_LAUNCH_APP		0x3b
+#define PIP_BL_CMD_INITIATE_BL		0x48
+#define PIP_INVALID_CMD			0xff
+
+#define PIP_HID_DESCRIPTOR_SIZE		32
+#define PIP_HID_APP_REPORT_ID		0xf7
+#define PIP_HID_BL_REPORT_ID		0xff
+
+#define PIP_BL_CMD_REPORT_ID		0x40
+#define PIP_BL_RESP_REPORT_ID		0x30
+#define PIP_APP_CMD_REPORT_ID		0x2f
+#define PIP_APP_RESP_REPORT_ID		0x1f
+
+#define PIP_READ_SYS_INFO_CMD_LENGTH	7
+#define PIP_BL_READ_APP_INFO_CMD_LENGTH	13
+#define PIP_MIN_BL_CMD_LENGTH		13
+#define PIP_MIN_BL_RESP_LENGTH		11
+#define PIP_MIN_APP_CMD_LENGTH		7
+#define PIP_MIN_APP_RESP_LENGTH		5
+#define PIP_UNSUPPORTED_CMD_RESP_LENGTH	6
+#define PIP_READ_SYS_INFO_RESP_LENGTH	71
+#define PIP_BL_APP_INFO_RESP_LENGTH	30
+#define PIP_BL_GET_INFO_RESP_LENGTH	19
+
+#define PIP_BL_PLATFORM_VER_SHIFT	4
+#define PIP_BL_PLATFORM_VER_MASK	0x0f
+
+#define PIP_PRODUCT_FAMILY_MASK		0xf000
+#define PIP_PRODUCT_FAMILY_TRACKPAD	0x1000
+
+#define PIP_DEEP_SLEEP_STATE_ON		0x00
+#define PIP_DEEP_SLEEP_STATE_OFF	0x01
+#define PIP_DEEP_SLEEP_STATE_MASK	0x03
+#define PIP_APP_DEEP_SLEEP_REPORT_ID	0xf0
+#define PIP_DEEP_SLEEP_RESP_LENGTH	5
+#define PIP_DEEP_SLEEP_OPCODE		0x08
+#define PIP_DEEP_SLEEP_OPCODE_MASK	0x0f
+
+#define PIP_RESP_LENGTH_OFFSET		0
+#define	    PIP_RESP_LENGTH_SIZE	2
+#define PIP_RESP_REPORT_ID_OFFSET	2
+#define PIP_RESP_RSVD_OFFSET		3
+#define     PIP_RESP_RSVD_KEY		0x00
+#define PIP_RESP_BL_SOP_OFFSET		4
+#define     PIP_SOP_KEY			0x01  /* Start of Packet */
+#define     PIP_EOP_KEY			0x17  /* End of Packet */
+#define PIP_RESP_APP_CMD_OFFSET		4
+#define     GET_PIP_CMD_CODE(reg)	((reg) & 0x7f)
+#define PIP_RESP_STATUS_OFFSET		5
+
+#define VALID_CMD_RESP_HEADER(resp, cmd)				  \
+	(((resp)[PIP_RESP_REPORT_ID_OFFSET] == PIP_APP_RESP_REPORT_ID) && \
+	((resp)[PIP_RESP_RSVD_OFFSET] == PIP_RESP_RSVD_KEY) &&		  \
+	(GET_PIP_CMD_CODE((resp)[PIP_RESP_APP_CMD_OFFSET]) == (cmd)))
+
+#define PIP_CMD_COMPLETE_SUCCESS(resp_data) \
+	((resp_data)[PIP_RESP_STATUS_OFFSET] == 0x00)
+
+/* Variables to record latest gen5 trackpad power states. */
+#define UNINIT_SLEEP_TIME	0xffff
+#define UNINIT_PWR_MODE		0xff
+#define PIP_DEV_SET_PWR_STATE(cyapa, s)		((cyapa)->dev_pwr_mode = (s))
+#define PIP_DEV_GET_PWR_STATE(cyapa)		((cyapa)->dev_pwr_mode)
+#define PIP_DEV_SET_SLEEP_TIME(cyapa, t)	((cyapa)->dev_sleep_time = (t))
+#define PIP_DEV_GET_SLEEP_TIME(cyapa)		((cyapa)->dev_sleep_time)
+#define PIP_DEV_UNINIT_SLEEP_TIME(cyapa)	\
+		(((cyapa)->dev_sleep_time) == UNINIT_SLEEP_TIME)
+
+/* The touch.id is used as the MT slot id, thus max MT slot is 15 */
+#define CYAPA_MAX_MT_SLOTS  15
+
+struct cyapa;
+
+typedef bool (*cb_sort)(struct cyapa *, u8 *, int);
+
+struct cyapa_dev_ops {
+	int (*check_fw)(struct cyapa *, const struct firmware *);
+	int (*bl_enter)(struct cyapa *);
+	int (*bl_activate)(struct cyapa *);
+	int (*bl_initiate)(struct cyapa *, const struct firmware *);
+	int (*update_fw)(struct cyapa *, const struct firmware *);
+	int (*bl_deactivate)(struct cyapa *);
+
+	ssize_t (*show_baseline)(struct device *,
+			struct device_attribute *, char *);
+	ssize_t (*calibrate_store)(struct device *,
+			struct device_attribute *, const char *, size_t);
+
+	int (*initialize)(struct cyapa *cyapa);
+
+	int (*state_parse)(struct cyapa *cyapa, u8 *reg_status, int len);
+	int (*operational_check)(struct cyapa *cyapa);
+
+	int (*irq_handler)(struct cyapa *);
+	bool (*irq_cmd_handler)(struct cyapa *);
+	int (*sort_empty_output_data)(struct cyapa *,
+			u8 *, int *, cb_sort);
+
+	int (*set_power_mode)(struct cyapa *, u8, u16, bool);
+
+	int (*set_proximity)(struct cyapa *, bool);
+};
+
+struct cyapa_pip_cmd_states {
+	struct mutex cmd_lock;
+	struct completion cmd_ready;
+	atomic_t cmd_issued;
+	u8 in_progress_cmd;
+	bool is_irq_mode;
+
+	cb_sort resp_sort_func;
+	u8 *resp_data;
+	int *resp_len;
+
+	u8 irq_cmd_buf[CYAPA_REG_MAP_SIZE];
+	u8 empty_buf[CYAPA_REG_MAP_SIZE];
+};
+
+union cyapa_cmd_states {
+	struct cyapa_pip_cmd_states pip;
+};
+
+enum cyapa_state {
+	CYAPA_STATE_NO_DEVICE,
+	CYAPA_STATE_BL_BUSY,
+	CYAPA_STATE_BL_IDLE,
+	CYAPA_STATE_BL_ACTIVE,
+	CYAPA_STATE_OP,
+	CYAPA_STATE_GEN5_BL,
+	CYAPA_STATE_GEN5_APP,
+	CYAPA_STATE_GEN6_BL,
+	CYAPA_STATE_GEN6_APP,
+};
+
+struct gen6_interval_setting {
+	u16 active_interval;
+	u16 lp1_interval;
+	u16 lp2_interval;
+};
+
+/* The main device structure */
+struct cyapa {
+	enum cyapa_state state;
+	u8 status[BL_STATUS_SIZE];
+	bool operational; /* true: ready for data reporting; false: not. */
+
+	struct regulator *vcc;
+	struct i2c_client *client;
+	struct input_dev *input;
+	char phys[32];	/* Device physical location */
+	bool irq_wake;  /* Irq wake is enabled */
+	bool smbus;
+
+	/* power mode settings */
+	u8 suspend_power_mode;
+	u16 suspend_sleep_time;
+	u8 runtime_suspend_power_mode;
+	u16 runtime_suspend_sleep_time;
+	u8 dev_pwr_mode;
+	u16 dev_sleep_time;
+	struct gen6_interval_setting gen6_interval_setting;
+
+	/* Read from query data region. */
+	char product_id[16];
+	u8 platform_ver;  /* Platform version. */
+	u8 fw_maj_ver;  /* Firmware major version. */
+	u8 fw_min_ver;  /* Firmware minor version. */
+	u8 btn_capability;
+	u8 gen;
+	int max_abs_x;
+	int max_abs_y;
+	int physical_size_x;
+	int physical_size_y;
+
+	/* Used in ttsp and truetouch based trackpad devices. */
+	u8 x_origin;  /* X Axis Origin: 0 = left side; 1 = right side. */
+	u8 y_origin;  /* Y Axis Origin: 0 = top; 1 = bottom. */
+	int electrodes_x;  /* Number of electrodes on the X Axis*/
+	int electrodes_y;  /* Number of electrodes on the Y Axis*/
+	int electrodes_rx;  /* Number of Rx electrodes */
+	int aligned_electrodes_rx;  /* 4 aligned */
+	int max_z;
+
+	/*
+	 * Used to synchronize the access or update the device state.
+	 * And since update firmware and read firmware image process will take
+	 * quite long time, maybe more than 10 seconds, so use mutex_lock
+	 * to sync and wait other interface and detecting are done or ready.
+	 */
+	struct mutex state_sync_lock;
+
+	const struct cyapa_dev_ops *ops;
+
+	union cyapa_cmd_states cmd_states;
+};
+
+
+ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len,
+				 u8 *values);
+ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len,
+			       u8 *values);
+
+ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values);
+
+int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout);
+
+u8 cyapa_sleep_time_to_pwr_cmd(u16 sleep_time);
+u16 cyapa_pwr_cmd_to_sleep_time(u8 pwr_mode);
+
+ssize_t cyapa_i2c_pip_read(struct cyapa *cyapa, u8 *buf, size_t size);
+ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size);
+int cyapa_empty_pip_output_data(struct cyapa *cyapa,
+				u8 *buf, int *len, cb_sort func);
+int cyapa_i2c_pip_cmd_irq_sync(struct cyapa *cyapa,
+			       u8 *cmd, int cmd_len,
+			       u8 *resp_data, int *resp_len,
+			       unsigned long timeout,
+			       cb_sort func,
+			       bool irq_mode);
+int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len);
+bool cyapa_pip_sort_system_info_data(struct cyapa *cyapa, u8 *buf, int len);
+bool cyapa_sort_tsg_pip_bl_resp_data(struct cyapa *cyapa, u8 *data, int len);
+int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state);
+bool cyapa_sort_tsg_pip_app_resp_data(struct cyapa *cyapa, u8 *data, int len);
+int cyapa_pip_bl_exit(struct cyapa *cyapa);
+int cyapa_pip_bl_enter(struct cyapa *cyapa);
+
+
+bool cyapa_is_pip_bl_mode(struct cyapa *cyapa);
+bool cyapa_is_pip_app_mode(struct cyapa *cyapa);
+int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa);
+
+int cyapa_pip_resume_scanning(struct cyapa *cyapa);
+int cyapa_pip_suspend_scanning(struct cyapa *cyapa);
+
+int cyapa_pip_check_fw(struct cyapa *cyapa, const struct firmware *fw);
+int cyapa_pip_bl_initiate(struct cyapa *cyapa, const struct firmware *fw);
+int cyapa_pip_do_fw_update(struct cyapa *cyapa, const struct firmware *fw);
+int cyapa_pip_bl_activate(struct cyapa *cyapa);
+int cyapa_pip_bl_deactivate(struct cyapa *cyapa);
+ssize_t cyapa_pip_do_calibrate(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count);
+int cyapa_pip_set_proximity(struct cyapa *cyapa, bool enable);
+
+bool cyapa_pip_irq_cmd_handler(struct cyapa *cyapa);
+int cyapa_pip_irq_handler(struct cyapa *cyapa);
+
+
+extern u8 pip_read_sys_info[];
+extern u8 pip_bl_read_app_info[];
+extern const char product_id[];
+extern const struct cyapa_dev_ops cyapa_gen3_ops;
+extern const struct cyapa_dev_ops cyapa_gen5_ops;
+extern const struct cyapa_dev_ops cyapa_gen6_ops;
+
+#endif
diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c
new file mode 100644
index 0000000..1a9d12a
--- /dev/null
+++ b/drivers/input/mouse/cyapa_gen3.c
@@ -0,0 +1,1253 @@
+/*
+ * Cypress APA trackpad with I2C interface
+ *
+ * Author: Dudley Du <dudl@cypress.com>
+ * Further cleanup and restructuring by:
+ *   Daniel Kurtz <djkurtz@chromium.org>
+ *   Benson Leung <bleung@chromium.org>
+ *
+ * Copyright (C) 2011-2015 Cypress Semiconductor, Inc.
+ * Copyright (C) 2011-2012 Google, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include "cyapa.h"
+
+
+#define GEN3_MAX_FINGERS 5
+#define GEN3_FINGER_NUM(x) (((x) >> 4) & 0x07)
+
+#define BLK_HEAD_BYTES 32
+
+/* Macro for register map group offset. */
+#define PRODUCT_ID_SIZE  16
+#define QUERY_DATA_SIZE  27
+#define REG_PROTOCOL_GEN_QUERY_OFFSET  20
+
+#define REG_OFFSET_DATA_BASE     0x0000
+#define REG_OFFSET_COMMAND_BASE  0x0028
+#define REG_OFFSET_QUERY_BASE    0x002a
+
+#define CYAPA_OFFSET_SOFT_RESET  REG_OFFSET_COMMAND_BASE
+#define OP_RECALIBRATION_MASK    0x80
+#define OP_REPORT_BASELINE_MASK  0x40
+#define REG_OFFSET_MAX_BASELINE  0x0026
+#define REG_OFFSET_MIN_BASELINE  0x0027
+
+#define REG_OFFSET_POWER_MODE (REG_OFFSET_COMMAND_BASE + 1)
+#define SET_POWER_MODE_DELAY   10000  /* Unit: us */
+#define SET_POWER_MODE_TRIES   5
+
+#define GEN3_BL_CMD_CHECKSUM_SEED 0xff
+#define GEN3_BL_CMD_INITIATE_BL   0x38
+#define GEN3_BL_CMD_WRITE_BLOCK   0x39
+#define GEN3_BL_CMD_VERIFY_BLOCK  0x3a
+#define GEN3_BL_CMD_TERMINATE_BL  0x3b
+#define GEN3_BL_CMD_LAUNCH_APP    0xa5
+
+/*
+ * CYAPA trackpad device states.
+ * Used in register 0x00, bit1-0, DeviceStatus field.
+ * Other values indicate device is in an abnormal state and must be reset.
+ */
+#define CYAPA_DEV_NORMAL  0x03
+#define CYAPA_DEV_BUSY    0x01
+
+#define CYAPA_FW_BLOCK_SIZE	64
+#define CYAPA_FW_READ_SIZE	16
+#define CYAPA_FW_HDR_START	0x0780
+#define CYAPA_FW_HDR_BLOCK_COUNT  2
+#define CYAPA_FW_HDR_BLOCK_START  (CYAPA_FW_HDR_START / CYAPA_FW_BLOCK_SIZE)
+#define CYAPA_FW_HDR_SIZE	  (CYAPA_FW_HDR_BLOCK_COUNT * \
+					CYAPA_FW_BLOCK_SIZE)
+#define CYAPA_FW_DATA_START	0x0800
+#define CYAPA_FW_DATA_BLOCK_COUNT  480
+#define CYAPA_FW_DATA_BLOCK_START  (CYAPA_FW_DATA_START / CYAPA_FW_BLOCK_SIZE)
+#define CYAPA_FW_DATA_SIZE	(CYAPA_FW_DATA_BLOCK_COUNT * \
+				 CYAPA_FW_BLOCK_SIZE)
+#define CYAPA_FW_SIZE		(CYAPA_FW_HDR_SIZE + CYAPA_FW_DATA_SIZE)
+#define CYAPA_CMD_LEN		16
+
+#define GEN3_BL_IDLE_FW_MAJ_VER_OFFSET 0x0b
+#define GEN3_BL_IDLE_FW_MIN_VER_OFFSET (GEN3_BL_IDLE_FW_MAJ_VER_OFFSET + 1)
+
+
+struct cyapa_touch {
+	/*
+	 * high bits or x/y position value
+	 * bit 7 - 4: high 4 bits of x position value
+	 * bit 3 - 0: high 4 bits of y position value
+	 */
+	u8 xy_hi;
+	u8 x_lo;  /* low 8 bits of x position value. */
+	u8 y_lo;  /* low 8 bits of y position value. */
+	u8 pressure;
+	/* id range is 1 - 15.  It is incremented with every new touch. */
+	u8 id;
+} __packed;
+
+struct cyapa_reg_data {
+	/*
+	 * bit 0 - 1: device status
+	 * bit 3 - 2: power mode
+	 * bit 6 - 4: reserved
+	 * bit 7: interrupt valid bit
+	 */
+	u8 device_status;
+	/*
+	 * bit 7 - 4: number of fingers currently touching pad
+	 * bit 3: valid data check bit
+	 * bit 2: middle mechanism button state if exists
+	 * bit 1: right mechanism button state if exists
+	 * bit 0: left mechanism button state if exists
+	 */
+	u8 finger_btn;
+	/* CYAPA reports up to 5 touches per packet. */
+	struct cyapa_touch touches[5];
+} __packed;
+
+struct gen3_write_block_cmd {
+	u8 checksum_seed;  /* Always be 0xff */
+	u8 cmd_code;       /* command code: 0x39 */
+	u8 key[8];         /* 8-byte security key */
+	__be16 block_num;
+	u8 block_data[CYAPA_FW_BLOCK_SIZE];
+	u8 block_checksum;  /* Calculated using bytes 12 - 75 */
+	u8 cmd_checksum;    /* Calculated using bytes 0-76 */
+} __packed;
+
+static const u8 security_key[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
+static const u8 bl_activate[] = { 0x00, 0xff, 0x38, 0x00, 0x01, 0x02, 0x03,
+		0x04, 0x05, 0x06, 0x07 };
+static const u8 bl_deactivate[] = { 0x00, 0xff, 0x3b, 0x00, 0x01, 0x02, 0x03,
+		0x04, 0x05, 0x06, 0x07 };
+static const u8 bl_exit[] = { 0x00, 0xff, 0xa5, 0x00, 0x01, 0x02, 0x03, 0x04,
+		0x05, 0x06, 0x07 };
+
+
+ /* for byte read/write command */
+#define CMD_RESET      0
+#define CMD_POWER_MODE 1
+#define CMD_DEV_STATUS 2
+#define CMD_REPORT_MAX_BASELINE 3
+#define CMD_REPORT_MIN_BASELINE 4
+#define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1)
+#define CYAPA_SMBUS_RESET         SMBUS_BYTE_CMD(CMD_RESET)
+#define CYAPA_SMBUS_POWER_MODE    SMBUS_BYTE_CMD(CMD_POWER_MODE)
+#define CYAPA_SMBUS_DEV_STATUS    SMBUS_BYTE_CMD(CMD_DEV_STATUS)
+#define CYAPA_SMBUS_MAX_BASELINE  SMBUS_BYTE_CMD(CMD_REPORT_MAX_BASELINE)
+#define CYAPA_SMBUS_MIN_BASELINE  SMBUS_BYTE_CMD(CMD_REPORT_MIN_BASELINE)
+
+ /* for group registers read/write command */
+#define REG_GROUP_DATA 0
+#define REG_GROUP_CMD 2
+#define REG_GROUP_QUERY 3
+#define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3))
+#define CYAPA_SMBUS_GROUP_DATA	 SMBUS_GROUP_CMD(REG_GROUP_DATA)
+#define CYAPA_SMBUS_GROUP_CMD	 SMBUS_GROUP_CMD(REG_GROUP_CMD)
+#define CYAPA_SMBUS_GROUP_QUERY	 SMBUS_GROUP_CMD(REG_GROUP_QUERY)
+
+ /* for register block read/write command */
+#define CMD_BL_STATUS 0
+#define CMD_BL_HEAD 1
+#define CMD_BL_CMD 2
+#define CMD_BL_DATA 3
+#define CMD_BL_ALL 4
+#define CMD_BLK_PRODUCT_ID 5
+#define CMD_BLK_HEAD 6
+#define SMBUS_BLOCK_CMD(cmd) (0xc0 | (((cmd) & 0x1f) << 1))
+
+/* register block read/write command in bootloader mode */
+#define CYAPA_SMBUS_BL_STATUS  SMBUS_BLOCK_CMD(CMD_BL_STATUS)
+#define CYAPA_SMBUS_BL_HEAD    SMBUS_BLOCK_CMD(CMD_BL_HEAD)
+#define CYAPA_SMBUS_BL_CMD     SMBUS_BLOCK_CMD(CMD_BL_CMD)
+#define CYAPA_SMBUS_BL_DATA    SMBUS_BLOCK_CMD(CMD_BL_DATA)
+#define CYAPA_SMBUS_BL_ALL     SMBUS_BLOCK_CMD(CMD_BL_ALL)
+
+/* register block read/write command in operational mode */
+#define CYAPA_SMBUS_BLK_PRODUCT_ID SMBUS_BLOCK_CMD(CMD_BLK_PRODUCT_ID)
+#define CYAPA_SMBUS_BLK_HEAD SMBUS_BLOCK_CMD(CMD_BLK_HEAD)
+
+ /* for byte read/write command */
+#define CMD_RESET 0
+#define CMD_POWER_MODE 1
+#define CMD_DEV_STATUS 2
+#define CMD_REPORT_MAX_BASELINE 3
+#define CMD_REPORT_MIN_BASELINE 4
+#define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1)
+#define CYAPA_SMBUS_RESET         SMBUS_BYTE_CMD(CMD_RESET)
+#define CYAPA_SMBUS_POWER_MODE    SMBUS_BYTE_CMD(CMD_POWER_MODE)
+#define CYAPA_SMBUS_DEV_STATUS    SMBUS_BYTE_CMD(CMD_DEV_STATUS)
+#define CYAPA_SMBUS_MAX_BASELINE  SMBUS_BYTE_CMD(CMD_REPORT_MAX_BASELINE)
+#define CYAPA_SMBUS_MIN_BASELINE  SMBUS_BYTE_CMD(CMD_REPORT_MIN_BASELINE)
+
+ /* for group registers read/write command */
+#define REG_GROUP_DATA  0
+#define REG_GROUP_CMD   2
+#define REG_GROUP_QUERY 3
+#define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3))
+#define CYAPA_SMBUS_GROUP_DATA  SMBUS_GROUP_CMD(REG_GROUP_DATA)
+#define CYAPA_SMBUS_GROUP_CMD   SMBUS_GROUP_CMD(REG_GROUP_CMD)
+#define CYAPA_SMBUS_GROUP_QUERY SMBUS_GROUP_CMD(REG_GROUP_QUERY)
+
+ /* for register block read/write command */
+#define CMD_BL_STATUS		0
+#define CMD_BL_HEAD		1
+#define CMD_BL_CMD		2
+#define CMD_BL_DATA		3
+#define CMD_BL_ALL		4
+#define CMD_BLK_PRODUCT_ID	5
+#define CMD_BLK_HEAD		6
+#define SMBUS_BLOCK_CMD(cmd) (0xc0 | (((cmd) & 0x1f) << 1))
+
+/* register block read/write command in bootloader mode */
+#define CYAPA_SMBUS_BL_STATUS SMBUS_BLOCK_CMD(CMD_BL_STATUS)
+#define CYAPA_SMBUS_BL_HEAD   SMBUS_BLOCK_CMD(CMD_BL_HEAD)
+#define CYAPA_SMBUS_BL_CMD    SMBUS_BLOCK_CMD(CMD_BL_CMD)
+#define CYAPA_SMBUS_BL_DATA   SMBUS_BLOCK_CMD(CMD_BL_DATA)
+#define CYAPA_SMBUS_BL_ALL    SMBUS_BLOCK_CMD(CMD_BL_ALL)
+
+/* register block read/write command in operational mode */
+#define CYAPA_SMBUS_BLK_PRODUCT_ID SMBUS_BLOCK_CMD(CMD_BLK_PRODUCT_ID)
+#define CYAPA_SMBUS_BLK_HEAD       SMBUS_BLOCK_CMD(CMD_BLK_HEAD)
+
+struct cyapa_cmd_len {
+	u8 cmd;
+	u8 len;
+};
+
+/* maps generic CYAPA_CMD_* code to the I2C equivalent */
+static const struct cyapa_cmd_len cyapa_i2c_cmds[] = {
+	{ CYAPA_OFFSET_SOFT_RESET, 1 },		/* CYAPA_CMD_SOFT_RESET */
+	{ REG_OFFSET_COMMAND_BASE + 1, 1 },	/* CYAPA_CMD_POWER_MODE */
+	{ REG_OFFSET_DATA_BASE, 1 },		/* CYAPA_CMD_DEV_STATUS */
+	{ REG_OFFSET_DATA_BASE, sizeof(struct cyapa_reg_data) },
+						/* CYAPA_CMD_GROUP_DATA */
+	{ REG_OFFSET_COMMAND_BASE, 0 },		/* CYAPA_CMD_GROUP_CMD */
+	{ REG_OFFSET_QUERY_BASE, QUERY_DATA_SIZE }, /* CYAPA_CMD_GROUP_QUERY */
+	{ BL_HEAD_OFFSET, 3 },			/* CYAPA_CMD_BL_STATUS */
+	{ BL_HEAD_OFFSET, 16 },			/* CYAPA_CMD_BL_HEAD */
+	{ BL_HEAD_OFFSET, 16 },			/* CYAPA_CMD_BL_CMD */
+	{ BL_DATA_OFFSET, 16 },			/* CYAPA_CMD_BL_DATA */
+	{ BL_HEAD_OFFSET, 32 },			/* CYAPA_CMD_BL_ALL */
+	{ REG_OFFSET_QUERY_BASE, PRODUCT_ID_SIZE },
+						/* CYAPA_CMD_BLK_PRODUCT_ID */
+	{ REG_OFFSET_DATA_BASE, 32 },		/* CYAPA_CMD_BLK_HEAD */
+	{ REG_OFFSET_MAX_BASELINE, 1 },		/* CYAPA_CMD_MAX_BASELINE */
+	{ REG_OFFSET_MIN_BASELINE, 1 },		/* CYAPA_CMD_MIN_BASELINE */
+};
+
+static const struct cyapa_cmd_len cyapa_smbus_cmds[] = {
+	{ CYAPA_SMBUS_RESET, 1 },		/* CYAPA_CMD_SOFT_RESET */
+	{ CYAPA_SMBUS_POWER_MODE, 1 },		/* CYAPA_CMD_POWER_MODE */
+	{ CYAPA_SMBUS_DEV_STATUS, 1 },		/* CYAPA_CMD_DEV_STATUS */
+	{ CYAPA_SMBUS_GROUP_DATA, sizeof(struct cyapa_reg_data) },
+						/* CYAPA_CMD_GROUP_DATA */
+	{ CYAPA_SMBUS_GROUP_CMD, 2 },		/* CYAPA_CMD_GROUP_CMD */
+	{ CYAPA_SMBUS_GROUP_QUERY, QUERY_DATA_SIZE },
+						/* CYAPA_CMD_GROUP_QUERY */
+	{ CYAPA_SMBUS_BL_STATUS, 3 },		/* CYAPA_CMD_BL_STATUS */
+	{ CYAPA_SMBUS_BL_HEAD, 16 },		/* CYAPA_CMD_BL_HEAD */
+	{ CYAPA_SMBUS_BL_CMD, 16 },		/* CYAPA_CMD_BL_CMD */
+	{ CYAPA_SMBUS_BL_DATA, 16 },		/* CYAPA_CMD_BL_DATA */
+	{ CYAPA_SMBUS_BL_ALL, 32 },		/* CYAPA_CMD_BL_ALL */
+	{ CYAPA_SMBUS_BLK_PRODUCT_ID, PRODUCT_ID_SIZE },
+						/* CYAPA_CMD_BLK_PRODUCT_ID */
+	{ CYAPA_SMBUS_BLK_HEAD, 16 },		/* CYAPA_CMD_BLK_HEAD */
+	{ CYAPA_SMBUS_MAX_BASELINE, 1 },	/* CYAPA_CMD_MAX_BASELINE */
+	{ CYAPA_SMBUS_MIN_BASELINE, 1 },	/* CYAPA_CMD_MIN_BASELINE */
+};
+
+
+/*
+ * cyapa_smbus_read_block - perform smbus block read command
+ * @cyapa  - private data structure of the driver
+ * @cmd    - the properly encoded smbus command
+ * @len    - expected length of smbus command result
+ * @values - buffer to store smbus command result
+ *
+ * Returns negative errno, else the number of bytes written.
+ *
+ * Note:
+ * In trackpad device, the memory block allocated for I2C register map
+ * is 256 bytes, so the max read block for I2C bus is 256 bytes.
+ */
+ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len,
+				      u8 *values)
+{
+	ssize_t ret;
+	u8 index;
+	u8 smbus_cmd;
+	u8 *buf;
+	struct i2c_client *client = cyapa->client;
+
+	if (!(SMBUS_BYTE_BLOCK_CMD_MASK & cmd))
+		return -EINVAL;
+
+	if (SMBUS_GROUP_BLOCK_CMD_MASK & cmd) {
+		/* read specific block registers command. */
+		smbus_cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ);
+		ret = i2c_smbus_read_block_data(client, smbus_cmd, values);
+		goto out;
+	}
+
+	ret = 0;
+	for (index = 0; index * I2C_SMBUS_BLOCK_MAX < len; index++) {
+		smbus_cmd = SMBUS_ENCODE_IDX(cmd, index);
+		smbus_cmd = SMBUS_ENCODE_RW(smbus_cmd, SMBUS_READ);
+		buf = values + I2C_SMBUS_BLOCK_MAX * index;
+		ret = i2c_smbus_read_block_data(client, smbus_cmd, buf);
+		if (ret < 0)
+			goto out;
+	}
+
+out:
+	return ret > 0 ? len : ret;
+}
+
+static s32 cyapa_read_byte(struct cyapa *cyapa, u8 cmd_idx)
+{
+	u8 cmd;
+
+	if (cyapa->smbus) {
+		cmd = cyapa_smbus_cmds[cmd_idx].cmd;
+		cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ);
+	} else {
+		cmd = cyapa_i2c_cmds[cmd_idx].cmd;
+	}
+	return i2c_smbus_read_byte_data(cyapa->client, cmd);
+}
+
+static s32 cyapa_write_byte(struct cyapa *cyapa, u8 cmd_idx, u8 value)
+{
+	u8 cmd;
+
+	if (cyapa->smbus) {
+		cmd = cyapa_smbus_cmds[cmd_idx].cmd;
+		cmd = SMBUS_ENCODE_RW(cmd, SMBUS_WRITE);
+	} else {
+		cmd = cyapa_i2c_cmds[cmd_idx].cmd;
+	}
+	return i2c_smbus_write_byte_data(cyapa->client, cmd, value);
+}
+
+ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len,
+					u8 *values)
+{
+	return i2c_smbus_read_i2c_block_data(cyapa->client, reg, len, values);
+}
+
+static ssize_t cyapa_i2c_reg_write_block(struct cyapa *cyapa, u8 reg,
+					 size_t len, const u8 *values)
+{
+	return i2c_smbus_write_i2c_block_data(cyapa->client, reg, len, values);
+}
+
+ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values)
+{
+	u8 cmd;
+	size_t len;
+
+	if (cyapa->smbus) {
+		cmd = cyapa_smbus_cmds[cmd_idx].cmd;
+		len = cyapa_smbus_cmds[cmd_idx].len;
+		return cyapa_smbus_read_block(cyapa, cmd, len, values);
+	}
+	cmd = cyapa_i2c_cmds[cmd_idx].cmd;
+	len = cyapa_i2c_cmds[cmd_idx].len;
+	return cyapa_i2c_reg_read_block(cyapa, cmd, len, values);
+}
+
+/*
+ * Determine the Gen3 trackpad device's current operating state.
+ *
+ */
+static int cyapa_gen3_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
+{
+	cyapa->state = CYAPA_STATE_NO_DEVICE;
+
+	/* Parse based on Gen3 characteristic registers and bits */
+	if (reg_data[REG_BL_FILE] == BL_FILE &&
+		reg_data[REG_BL_ERROR] == BL_ERROR_NO_ERR_IDLE &&
+		(reg_data[REG_BL_STATUS] ==
+			(BL_STATUS_RUNNING | BL_STATUS_CSUM_VALID) ||
+			reg_data[REG_BL_STATUS] == BL_STATUS_RUNNING)) {
+		/*
+		 * Normal state after power on or reset,
+		 * REG_BL_STATUS == 0x11, firmware image checksum is valid.
+		 * REG_BL_STATUS == 0x10, firmware image checksum is invalid.
+		 */
+		cyapa->gen = CYAPA_GEN3;
+		cyapa->state = CYAPA_STATE_BL_IDLE;
+	} else if (reg_data[REG_BL_FILE] == BL_FILE &&
+		(reg_data[REG_BL_STATUS] & BL_STATUS_RUNNING) ==
+			BL_STATUS_RUNNING) {
+		cyapa->gen = CYAPA_GEN3;
+		if (reg_data[REG_BL_STATUS] & BL_STATUS_BUSY) {
+			cyapa->state = CYAPA_STATE_BL_BUSY;
+		} else {
+			if ((reg_data[REG_BL_ERROR] & BL_ERROR_BOOTLOADING) ==
+					BL_ERROR_BOOTLOADING)
+				cyapa->state = CYAPA_STATE_BL_ACTIVE;
+			else
+				cyapa->state = CYAPA_STATE_BL_IDLE;
+		}
+	} else if ((reg_data[REG_OP_STATUS] & OP_STATUS_SRC) &&
+			(reg_data[REG_OP_DATA1] & OP_DATA_VALID)) {
+		/*
+		 * Normal state when running in operational mode,
+		 * may also not in full power state or
+		 * busying in command process.
+		 */
+		if (GEN3_FINGER_NUM(reg_data[REG_OP_DATA1]) <=
+				GEN3_MAX_FINGERS) {
+			/* Finger number data is valid. */
+			cyapa->gen = CYAPA_GEN3;
+			cyapa->state = CYAPA_STATE_OP;
+		}
+	} else if (reg_data[REG_OP_STATUS] == 0x0C &&
+			reg_data[REG_OP_DATA1] == 0x08) {
+		/* Op state when first two registers overwritten with 0x00 */
+		cyapa->gen = CYAPA_GEN3;
+		cyapa->state = CYAPA_STATE_OP;
+	} else if (reg_data[REG_BL_STATUS] &
+			(BL_STATUS_RUNNING | BL_STATUS_BUSY)) {
+		cyapa->gen = CYAPA_GEN3;
+		cyapa->state = CYAPA_STATE_BL_BUSY;
+	}
+
+	if (cyapa->gen == CYAPA_GEN3 && (cyapa->state == CYAPA_STATE_OP ||
+		cyapa->state == CYAPA_STATE_BL_IDLE ||
+		cyapa->state == CYAPA_STATE_BL_ACTIVE ||
+		cyapa->state == CYAPA_STATE_BL_BUSY))
+		return 0;
+
+	return -EAGAIN;
+}
+
+/*
+ * Enter bootloader by soft resetting the device.
+ *
+ * If device is already in the bootloader, the function just returns.
+ * Otherwise, reset the device; after reset, device enters bootloader idle
+ * state immediately.
+ *
+ * Returns:
+ *   0        on success
+ *   -EAGAIN  device was reset, but is not now in bootloader idle state
+ *   < 0      if the device never responds within the timeout
+ */
+static int cyapa_gen3_bl_enter(struct cyapa *cyapa)
+{
+	int error;
+	int waiting_time;
+
+	error = cyapa_poll_state(cyapa, 500);
+	if (error)
+		return error;
+	if (cyapa->state == CYAPA_STATE_BL_IDLE) {
+		/* Already in BL_IDLE. Skipping reset. */
+		return 0;
+	}
+
+	if (cyapa->state != CYAPA_STATE_OP)
+		return -EAGAIN;
+
+	cyapa->operational = false;
+	cyapa->state = CYAPA_STATE_NO_DEVICE;
+	error = cyapa_write_byte(cyapa, CYAPA_CMD_SOFT_RESET, 0x01);
+	if (error)
+		return -EIO;
+
+	usleep_range(25000, 50000);
+	waiting_time = 2000;  /* For some shipset, max waiting time is 1~2s. */
+	do {
+		error = cyapa_poll_state(cyapa, 500);
+		if (error) {
+			if (error == -ETIMEDOUT) {
+				waiting_time -= 500;
+				continue;
+			}
+			return error;
+		}
+
+		if ((cyapa->state == CYAPA_STATE_BL_IDLE) &&
+			!(cyapa->status[REG_BL_STATUS] & BL_STATUS_WATCHDOG))
+			break;
+
+		msleep(100);
+		waiting_time -= 100;
+	} while (waiting_time > 0);
+
+	if ((cyapa->state != CYAPA_STATE_BL_IDLE) ||
+		(cyapa->status[REG_BL_STATUS] & BL_STATUS_WATCHDOG))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static int cyapa_gen3_bl_activate(struct cyapa *cyapa)
+{
+	int error;
+
+	error = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_activate),
+					bl_activate);
+	if (error)
+		return error;
+
+	/* Wait for bootloader to activate; takes between 2 and 12 seconds */
+	msleep(2000);
+	error = cyapa_poll_state(cyapa, 11000);
+	if (error)
+		return error;
+	if (cyapa->state != CYAPA_STATE_BL_ACTIVE)
+		return -EAGAIN;
+
+	return 0;
+}
+
+static int cyapa_gen3_bl_deactivate(struct cyapa *cyapa)
+{
+	int error;
+
+	error = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_deactivate),
+					bl_deactivate);
+	if (error)
+		return error;
+
+	/* Wait for bootloader to switch to idle state; should take < 100ms */
+	msleep(100);
+	error = cyapa_poll_state(cyapa, 500);
+	if (error)
+		return error;
+	if (cyapa->state != CYAPA_STATE_BL_IDLE)
+		return -EAGAIN;
+	return 0;
+}
+
+/*
+ * Exit bootloader
+ *
+ * Send bl_exit command, then wait 50 - 100 ms to let device transition to
+ * operational mode.  If this is the first time the device's firmware is
+ * running, it can take up to 2 seconds to calibrate its sensors.  So, poll
+ * the device's new state for up to 2 seconds.
+ *
+ * Returns:
+ *   -EIO    failure while reading from device
+ *   -EAGAIN device is stuck in bootloader, b/c it has invalid firmware
+ *   0       device is supported and in operational mode
+ */
+static int cyapa_gen3_bl_exit(struct cyapa *cyapa)
+{
+	int error;
+
+	error = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_exit), bl_exit);
+	if (error)
+		return error;
+
+	/*
+	 * Wait for bootloader to exit, and operation mode to start.
+	 * Normally, this takes at least 50 ms.
+	 */
+	usleep_range(50000, 100000);
+	/*
+	 * In addition, when a device boots for the first time after being
+	 * updated to new firmware, it must first calibrate its sensors, which
+	 * can take up to an additional 2 seconds. If the device power is
+	 * running low, this may take even longer.
+	 */
+	error = cyapa_poll_state(cyapa, 4000);
+	if (error < 0)
+		return error;
+	if (cyapa->state != CYAPA_STATE_OP)
+		return -EAGAIN;
+
+	return 0;
+}
+
+static u16 cyapa_gen3_csum(const u8 *buf, size_t count)
+{
+	int i;
+	u16 csum = 0;
+
+	for (i = 0; i < count; i++)
+		csum += buf[i];
+
+	return csum;
+}
+
+/*
+ * Verify the integrity of a CYAPA firmware image file.
+ *
+ * The firmware image file is 30848 bytes, composed of 482 64-byte blocks.
+ *
+ * The first 2 blocks are the firmware header.
+ * The next 480 blocks are the firmware image.
+ *
+ * The first two bytes of the header hold the header checksum, computed by
+ * summing the other 126 bytes of the header.
+ * The last two bytes of the header hold the firmware image checksum, computed
+ * by summing the 30720 bytes of the image modulo 0xffff.
+ *
+ * Both checksums are stored little-endian.
+ */
+static int cyapa_gen3_check_fw(struct cyapa *cyapa, const struct firmware *fw)
+{
+	struct device *dev = &cyapa->client->dev;
+	u16 csum;
+	u16 csum_expected;
+
+	/* Firmware must match exact 30848 bytes = 482 64-byte blocks. */
+	if (fw->size != CYAPA_FW_SIZE) {
+		dev_err(dev, "invalid firmware size = %zu, expected %u.\n",
+			fw->size, CYAPA_FW_SIZE);
+		return -EINVAL;
+	}
+
+	/* Verify header block */
+	csum_expected = (fw->data[0] << 8) | fw->data[1];
+	csum = cyapa_gen3_csum(&fw->data[2], CYAPA_FW_HDR_SIZE - 2);
+	if (csum != csum_expected) {
+		dev_err(dev, "%s %04x, expected: %04x\n",
+			"invalid firmware header checksum = ",
+			csum, csum_expected);
+		return -EINVAL;
+	}
+
+	/* Verify firmware image */
+	csum_expected = (fw->data[CYAPA_FW_HDR_SIZE - 2] << 8) |
+			 fw->data[CYAPA_FW_HDR_SIZE - 1];
+	csum = cyapa_gen3_csum(&fw->data[CYAPA_FW_HDR_SIZE],
+			CYAPA_FW_DATA_SIZE);
+	if (csum != csum_expected) {
+		dev_err(dev, "%s %04x, expected: %04x\n",
+			"invalid firmware header checksum = ",
+			csum, csum_expected);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * Write a |len| byte long buffer |buf| to the device, by chopping it up into a
+ * sequence of smaller |CYAPA_CMD_LEN|-length write commands.
+ *
+ * The data bytes for a write command are prepended with the 1-byte offset
+ * of the data relative to the start of |buf|.
+ */
+static int cyapa_gen3_write_buffer(struct cyapa *cyapa,
+		const u8 *buf, size_t len)
+{
+	int error;
+	size_t i;
+	unsigned char cmd[CYAPA_CMD_LEN + 1];
+	size_t cmd_len;
+
+	for (i = 0; i < len; i += CYAPA_CMD_LEN) {
+		const u8 *payload = &buf[i];
+
+		cmd_len = (len - i >= CYAPA_CMD_LEN) ? CYAPA_CMD_LEN : len - i;
+		cmd[0] = i;
+		memcpy(&cmd[1], payload, cmd_len);
+
+		error = cyapa_i2c_reg_write_block(cyapa, 0, cmd_len + 1, cmd);
+		if (error)
+			return error;
+	}
+	return 0;
+}
+
+/*
+ * A firmware block write command writes 64 bytes of data to a single flash
+ * page in the device.  The 78-byte block write command has the format:
+ *   <0xff> <CMD> <Key> <Start> <Data> <Data-Checksum> <CMD Checksum>
+ *
+ *  <0xff>  - every command starts with 0xff
+ *  <CMD>   - the write command value is 0x39
+ *  <Key>   - write commands include an 8-byte key: { 00 01 02 03 04 05 06 07 }
+ *  <Block> - Memory Block number (address / 64) (16-bit, big-endian)
+ *  <Data>  - 64 bytes of firmware image data
+ *  <Data Checksum> - sum of 64 <Data> bytes, modulo 0xff
+ *  <CMD Checksum> - sum of 77 bytes, from 0xff to <Data Checksum>
+ *
+ * Each write command is split into 5 i2c write transactions of up to 16 bytes.
+ * Each transaction starts with an i2c register offset: (00, 10, 20, 30, 40).
+ */
+static int cyapa_gen3_write_fw_block(struct cyapa *cyapa,
+		u16 block, const u8 *data)
+{
+	int ret;
+	struct gen3_write_block_cmd write_block_cmd;
+	u8 status[BL_STATUS_SIZE];
+	int tries;
+	u8 bl_status, bl_error;
+
+	/* Set write command and security key bytes. */
+	write_block_cmd.checksum_seed = GEN3_BL_CMD_CHECKSUM_SEED;
+	write_block_cmd.cmd_code = GEN3_BL_CMD_WRITE_BLOCK;
+	memcpy(write_block_cmd.key, security_key, sizeof(security_key));
+	put_unaligned_be16(block, &write_block_cmd.block_num);
+	memcpy(write_block_cmd.block_data, data, CYAPA_FW_BLOCK_SIZE);
+	write_block_cmd.block_checksum = cyapa_gen3_csum(
+			write_block_cmd.block_data, CYAPA_FW_BLOCK_SIZE);
+	write_block_cmd.cmd_checksum = cyapa_gen3_csum((u8 *)&write_block_cmd,
+			sizeof(write_block_cmd) - 1);
+
+	ret = cyapa_gen3_write_buffer(cyapa, (u8 *)&write_block_cmd,
+			sizeof(write_block_cmd));
+	if (ret)
+		return ret;
+
+	/* Wait for write to finish */
+	tries = 11;  /* Programming for one block can take about 100ms. */
+	do {
+		usleep_range(10000, 20000);
+
+		/* Check block write command result status. */
+		ret = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET,
+					       BL_STATUS_SIZE, status);
+		if (ret != BL_STATUS_SIZE)
+			return (ret < 0) ? ret : -EIO;
+	} while ((status[REG_BL_STATUS] & BL_STATUS_BUSY) && --tries);
+
+	/* Ignore WATCHDOG bit and reserved bits. */
+	bl_status = status[REG_BL_STATUS] & ~BL_STATUS_REV_MASK;
+	bl_error = status[REG_BL_ERROR] & ~BL_ERROR_RESERVED;
+
+	if (bl_status & BL_STATUS_BUSY)
+		ret = -ETIMEDOUT;
+	else if (bl_status != BL_STATUS_RUNNING ||
+		bl_error != BL_ERROR_BOOTLOADING)
+		ret = -EIO;
+	else
+		ret = 0;
+
+	return ret;
+}
+
+static int cyapa_gen3_write_blocks(struct cyapa *cyapa,
+		size_t start_block, size_t block_count,
+		const u8 *image_data)
+{
+	int error;
+	int i;
+
+	for (i = 0; i < block_count; i++) {
+		size_t block = start_block + i;
+		size_t addr = i * CYAPA_FW_BLOCK_SIZE;
+		const u8 *data = &image_data[addr];
+
+		error = cyapa_gen3_write_fw_block(cyapa, block, data);
+		if (error)
+			return error;
+	}
+	return 0;
+}
+
+static int cyapa_gen3_do_fw_update(struct cyapa *cyapa,
+		const struct firmware *fw)
+{
+	struct device *dev = &cyapa->client->dev;
+	int error;
+
+	/* First write data, starting at byte 128 of fw->data */
+	error = cyapa_gen3_write_blocks(cyapa,
+		CYAPA_FW_DATA_BLOCK_START, CYAPA_FW_DATA_BLOCK_COUNT,
+		&fw->data[CYAPA_FW_HDR_BLOCK_COUNT * CYAPA_FW_BLOCK_SIZE]);
+	if (error) {
+		dev_err(dev, "FW update aborted, write image: %d\n", error);
+		return error;
+	}
+
+	/* Then write checksum */
+	error = cyapa_gen3_write_blocks(cyapa,
+		CYAPA_FW_HDR_BLOCK_START, CYAPA_FW_HDR_BLOCK_COUNT,
+		&fw->data[0]);
+	if (error) {
+		dev_err(dev, "FW update aborted, write checksum: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static ssize_t cyapa_gen3_do_calibrate(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	int tries;
+	int ret;
+
+	ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS);
+	if (ret < 0) {
+		dev_err(dev, "Error reading dev status: %d\n", ret);
+		goto out;
+	}
+	if ((ret & CYAPA_DEV_NORMAL) != CYAPA_DEV_NORMAL) {
+		dev_warn(dev, "Trackpad device is busy, device state: 0x%02x\n",
+			 ret);
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	ret = cyapa_write_byte(cyapa, CYAPA_CMD_SOFT_RESET,
+			       OP_RECALIBRATION_MASK);
+	if (ret < 0) {
+		dev_err(dev, "Failed to send calibrate command: %d\n",
+			ret);
+		goto out;
+	}
+
+	tries = 20;  /* max recalibration timeout 2s. */
+	do {
+		/*
+		 * For this recalibration, the max time will not exceed 2s.
+		 * The average time is approximately 500 - 700 ms, and we
+		 * will check the status every 100 - 200ms.
+		 */
+		usleep_range(100000, 200000);
+
+		ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS);
+		if (ret < 0) {
+			dev_err(dev, "Error reading dev status: %d\n",
+				ret);
+			goto out;
+		}
+		if ((ret & CYAPA_DEV_NORMAL) == CYAPA_DEV_NORMAL)
+			break;
+	} while (--tries);
+
+	if (tries == 0) {
+		dev_err(dev, "Failed to calibrate. Timeout.\n");
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+	dev_dbg(dev, "Calibration successful.\n");
+
+out:
+	return ret < 0 ? ret : count;
+}
+
+static ssize_t cyapa_gen3_show_baseline(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	int max_baseline, min_baseline;
+	int tries;
+	int ret;
+
+	ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS);
+	if (ret < 0) {
+		dev_err(dev, "Error reading dev status. err = %d\n", ret);
+		goto out;
+	}
+	if ((ret & CYAPA_DEV_NORMAL) != CYAPA_DEV_NORMAL) {
+		dev_warn(dev, "Trackpad device is busy. device state = 0x%x\n",
+			 ret);
+		ret = -EAGAIN;
+		goto out;
+	}
+
+	ret = cyapa_write_byte(cyapa, CYAPA_CMD_SOFT_RESET,
+			       OP_REPORT_BASELINE_MASK);
+	if (ret < 0) {
+		dev_err(dev, "Failed to send report baseline command. %d\n",
+			ret);
+		goto out;
+	}
+
+	tries = 3;  /* Try for 30 to 60 ms */
+	do {
+		usleep_range(10000, 20000);
+
+		ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS);
+		if (ret < 0) {
+			dev_err(dev, "Error reading dev status. err = %d\n",
+				ret);
+			goto out;
+		}
+		if ((ret & CYAPA_DEV_NORMAL) == CYAPA_DEV_NORMAL)
+			break;
+	} while (--tries);
+
+	if (tries == 0) {
+		dev_err(dev, "Device timed out going to Normal state.\n");
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	ret = cyapa_read_byte(cyapa, CYAPA_CMD_MAX_BASELINE);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read max baseline. err = %d\n", ret);
+		goto out;
+	}
+	max_baseline = ret;
+
+	ret = cyapa_read_byte(cyapa, CYAPA_CMD_MIN_BASELINE);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read min baseline. err = %d\n", ret);
+		goto out;
+	}
+	min_baseline = ret;
+
+	dev_dbg(dev, "Baseline report successful. Max: %d Min: %d\n",
+		max_baseline, min_baseline);
+	ret = scnprintf(buf, PAGE_SIZE, "%d %d\n", max_baseline, min_baseline);
+
+out:
+	return ret;
+}
+
+/*
+ * cyapa_get_wait_time_for_pwr_cmd
+ *
+ * Compute the amount of time we need to wait after updating the touchpad
+ * power mode. The touchpad needs to consume the incoming power mode set
+ * command at the current clock rate.
+ */
+
+static u16 cyapa_get_wait_time_for_pwr_cmd(u8 pwr_mode)
+{
+	switch (pwr_mode) {
+	case PWR_MODE_FULL_ACTIVE: return 20;
+	case PWR_MODE_BTN_ONLY: return 20;
+	case PWR_MODE_OFF: return 20;
+	default: return cyapa_pwr_cmd_to_sleep_time(pwr_mode) + 50;
+	}
+}
+
+/*
+ * Set device power mode
+ *
+ * Write to the field to configure power state. Power states include :
+ *   Full : Max scans and report rate.
+ *   Idle : Report rate set by user specified time.
+ *   ButtonOnly : No scans for fingers. When the button is triggered,
+ *     a slave interrupt is asserted to notify host to wake up.
+ *   Off : Only awake for i2c commands from host. No function for button
+ *     or touch sensors.
+ *
+ * The power_mode command should conform to the following :
+ *   Full : 0x3f
+ *   Idle : Configurable from 20 to 1000ms. See note below for
+ *     cyapa_sleep_time_to_pwr_cmd and cyapa_pwr_cmd_to_sleep_time
+ *   ButtonOnly : 0x01
+ *   Off : 0x00
+ *
+ * Device power mode can only be set when device is in operational mode.
+ */
+static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode,
+		u16 always_unused, bool is_suspend_unused)
+{
+	int ret;
+	u8 power;
+	int tries;
+	u16 sleep_time;
+
+	if (cyapa->state != CYAPA_STATE_OP)
+		return 0;
+
+	tries = SET_POWER_MODE_TRIES;
+	while (tries--) {
+		ret = cyapa_read_byte(cyapa, CYAPA_CMD_POWER_MODE);
+		if (ret >= 0)
+			break;
+		usleep_range(SET_POWER_MODE_DELAY, 2 * SET_POWER_MODE_DELAY);
+	}
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Return early if the power mode to set is the same as the current
+	 * one.
+	 */
+	if ((ret & PWR_MODE_MASK) == power_mode)
+		return 0;
+
+	sleep_time = cyapa_get_wait_time_for_pwr_cmd(ret & PWR_MODE_MASK);
+	power = ret;
+	power &= ~PWR_MODE_MASK;
+	power |= power_mode & PWR_MODE_MASK;
+	tries = SET_POWER_MODE_TRIES;
+	while (tries--) {
+		ret = cyapa_write_byte(cyapa, CYAPA_CMD_POWER_MODE, power);
+		if (!ret)
+			break;
+		usleep_range(SET_POWER_MODE_DELAY, 2 * SET_POWER_MODE_DELAY);
+	}
+
+	/*
+	 * Wait for the newly set power command to go in at the previous
+	 * clock speed (scanrate) used by the touchpad firmware. Not
+	 * doing so before issuing the next command may result in errors
+	 * depending on the command's content.
+	 */
+	msleep(sleep_time);
+	return ret;
+}
+
+static int cyapa_gen3_set_proximity(struct cyapa *cyapa, bool enable)
+{
+	return -EOPNOTSUPP;
+}
+
+static int cyapa_gen3_get_query_data(struct cyapa *cyapa)
+{
+	u8 query_data[QUERY_DATA_SIZE];
+	int ret;
+
+	if (cyapa->state != CYAPA_STATE_OP)
+		return -EBUSY;
+
+	ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_QUERY, query_data);
+	if (ret != QUERY_DATA_SIZE)
+		return (ret < 0) ? ret : -EIO;
+
+	memcpy(&cyapa->product_id[0], &query_data[0], 5);
+	cyapa->product_id[5] = '-';
+	memcpy(&cyapa->product_id[6], &query_data[5], 6);
+	cyapa->product_id[12] = '-';
+	memcpy(&cyapa->product_id[13], &query_data[11], 2);
+	cyapa->product_id[15] = '\0';
+
+	cyapa->fw_maj_ver = query_data[15];
+	cyapa->fw_min_ver = query_data[16];
+
+	cyapa->btn_capability = query_data[19] & CAPABILITY_BTN_MASK;
+
+	cyapa->gen = query_data[20] & 0x0f;
+
+	cyapa->max_abs_x = ((query_data[21] & 0xf0) << 4) | query_data[22];
+	cyapa->max_abs_y = ((query_data[21] & 0x0f) << 8) | query_data[23];
+
+	cyapa->physical_size_x =
+		((query_data[24] & 0xf0) << 4) | query_data[25];
+	cyapa->physical_size_y =
+		((query_data[24] & 0x0f) << 8) | query_data[26];
+
+	cyapa->max_z = 255;
+
+	return 0;
+}
+
+static int cyapa_gen3_bl_query_data(struct cyapa *cyapa)
+{
+	u8 bl_data[CYAPA_CMD_LEN];
+	int ret;
+
+	ret = cyapa_i2c_reg_read_block(cyapa, 0, CYAPA_CMD_LEN, bl_data);
+	if (ret != CYAPA_CMD_LEN)
+		return (ret < 0) ? ret : -EIO;
+
+	/*
+	 * This value will be updated again when entered application mode.
+	 * If TP failed to enter application mode, this fw version values
+	 * can be used as a reference.
+	 * This firmware version valid when fw image checksum is valid.
+	 */
+	if (bl_data[REG_BL_STATUS] ==
+			(BL_STATUS_RUNNING | BL_STATUS_CSUM_VALID)) {
+		cyapa->fw_maj_ver = bl_data[GEN3_BL_IDLE_FW_MAJ_VER_OFFSET];
+		cyapa->fw_min_ver = bl_data[GEN3_BL_IDLE_FW_MIN_VER_OFFSET];
+	}
+
+	return 0;
+}
+
+/*
+ * Check if device is operational.
+ *
+ * An operational device is responding, has exited bootloader, and has
+ * firmware supported by this driver.
+ *
+ * Returns:
+ *   -EBUSY  no device or in bootloader
+ *   -EIO    failure while reading from device
+ *   -EAGAIN device is still in bootloader
+ *           if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware
+ *   -EINVAL device is in operational mode, but not supported by this driver
+ *   0       device is supported
+ */
+static int cyapa_gen3_do_operational_check(struct cyapa *cyapa)
+{
+	struct device *dev = &cyapa->client->dev;
+	int error;
+
+	switch (cyapa->state) {
+	case CYAPA_STATE_BL_ACTIVE:
+		error = cyapa_gen3_bl_deactivate(cyapa);
+		if (error) {
+			dev_err(dev, "failed to bl_deactivate: %d\n", error);
+			return error;
+		}
+
+	/* Fallthrough state */
+	case CYAPA_STATE_BL_IDLE:
+		/* Try to get firmware version in bootloader mode. */
+		cyapa_gen3_bl_query_data(cyapa);
+
+		error = cyapa_gen3_bl_exit(cyapa);
+		if (error) {
+			dev_err(dev, "failed to bl_exit: %d\n", error);
+			return error;
+		}
+
+	/* Fallthrough state */
+	case CYAPA_STATE_OP:
+		/*
+		 * Reading query data before going back to the full mode
+		 * may cause problems, so we set the power mode first here.
+		 */
+		error = cyapa_gen3_set_power_mode(cyapa,
+				PWR_MODE_FULL_ACTIVE, 0, false);
+		if (error)
+			dev_err(dev, "%s: set full power mode failed: %d\n",
+				__func__, error);
+		error = cyapa_gen3_get_query_data(cyapa);
+		if (error < 0)
+			return error;
+
+		/* Only support firmware protocol gen3 */
+		if (cyapa->gen != CYAPA_GEN3) {
+			dev_err(dev, "unsupported protocol version (%d)",
+				cyapa->gen);
+			return -EINVAL;
+		}
+
+		/* Only support product ID starting with CYTRA */
+		if (memcmp(cyapa->product_id, product_id,
+				strlen(product_id)) != 0) {
+			dev_err(dev, "unsupported product ID (%s)\n",
+				cyapa->product_id);
+			return -EINVAL;
+		}
+
+		return 0;
+
+	default:
+		return -EIO;
+	}
+	return 0;
+}
+
+/*
+ * Return false, do not continue process
+ * Return true, continue process.
+ */
+static bool cyapa_gen3_irq_cmd_handler(struct cyapa *cyapa)
+{
+	/* Not gen3 irq command response, skip for continue. */
+	if (cyapa->gen != CYAPA_GEN3)
+		return true;
+
+	if (cyapa->operational)
+		return true;
+
+	/*
+	 * Driver in detecting or other interface function processing,
+	 * so, stop cyapa_gen3_irq_handler to continue process to
+	 * avoid unwanted to error detecting and processing.
+	 *
+	 * And also, avoid the periodically asserted interrupts to be processed
+	 * as touch inputs when gen3 failed to launch into application mode,
+	 * which will cause gen3 stays in bootloader mode.
+	 */
+	return false;
+}
+
+static int cyapa_gen3_irq_handler(struct cyapa *cyapa)
+{
+	struct input_dev *input = cyapa->input;
+	struct device *dev = &cyapa->client->dev;
+	struct cyapa_reg_data data;
+	int num_fingers;
+	int ret;
+	int i;
+
+	ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data);
+	if (ret != sizeof(data)) {
+		dev_err(dev, "failed to read report data, (%d)\n", ret);
+		return -EINVAL;
+	}
+
+	if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC ||
+	    (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL ||
+	    (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) {
+		dev_err(dev, "invalid device state bytes, %02x %02x\n",
+			data.device_status, data.finger_btn);
+		return -EINVAL;
+	}
+
+	num_fingers = (data.finger_btn >> 4) & 0x0f;
+	for (i = 0; i < num_fingers; i++) {
+		const struct cyapa_touch *touch = &data.touches[i];
+		/* Note: touch->id range is 1 to 15; slots are 0 to 14. */
+		int slot = touch->id - 1;
+
+		input_mt_slot(input, slot);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+		input_report_abs(input, ABS_MT_POSITION_X,
+				 ((touch->xy_hi & 0xf0) << 4) | touch->x_lo);
+		input_report_abs(input, ABS_MT_POSITION_Y,
+				 ((touch->xy_hi & 0x0f) << 8) | touch->y_lo);
+		input_report_abs(input, ABS_MT_PRESSURE, touch->pressure);
+	}
+
+	input_mt_sync_frame(input);
+
+	if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK)
+		input_report_key(input, BTN_LEFT,
+				 !!(data.finger_btn & OP_DATA_LEFT_BTN));
+	if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK)
+		input_report_key(input, BTN_MIDDLE,
+				 !!(data.finger_btn & OP_DATA_MIDDLE_BTN));
+	if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK)
+		input_report_key(input, BTN_RIGHT,
+				 !!(data.finger_btn & OP_DATA_RIGHT_BTN));
+	input_sync(input);
+
+	return 0;
+}
+
+static int cyapa_gen3_initialize(struct cyapa *cyapa) { return 0; }
+static int cyapa_gen3_bl_initiate(struct cyapa *cyapa,
+		const struct firmware *fw) { return 0; }
+static int cyapa_gen3_empty_output_data(struct cyapa *cyapa,
+		u8 *buf, int *len, cb_sort func) { return 0; }
+
+const struct cyapa_dev_ops cyapa_gen3_ops = {
+	.check_fw = cyapa_gen3_check_fw,
+	.bl_enter = cyapa_gen3_bl_enter,
+	.bl_activate = cyapa_gen3_bl_activate,
+	.update_fw = cyapa_gen3_do_fw_update,
+	.bl_deactivate = cyapa_gen3_bl_deactivate,
+	.bl_initiate = cyapa_gen3_bl_initiate,
+
+	.show_baseline = cyapa_gen3_show_baseline,
+	.calibrate_store = cyapa_gen3_do_calibrate,
+
+	.initialize = cyapa_gen3_initialize,
+
+	.state_parse = cyapa_gen3_state_parse,
+	.operational_check = cyapa_gen3_do_operational_check,
+
+	.irq_handler = cyapa_gen3_irq_handler,
+	.irq_cmd_handler = cyapa_gen3_irq_cmd_handler,
+	.sort_empty_output_data = cyapa_gen3_empty_output_data,
+	.set_power_mode = cyapa_gen3_set_power_mode,
+
+	.set_proximity = cyapa_gen3_set_proximity,
+};
diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
new file mode 100644
index 0000000..118ba97
--- /dev/null
+++ b/drivers/input/mouse/cyapa_gen5.c
@@ -0,0 +1,2843 @@
+/*
+ * Cypress APA trackpad with I2C interface
+ *
+ * Author: Dudley Du <dudl@cypress.com>
+ *
+ * Copyright (C) 2014-2015 Cypress Semiconductor, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <linux/crc-itu-t.h>
+#include <linux/pm_runtime.h>
+#include "cyapa.h"
+
+
+/* Macro of TSG firmware image */
+#define CYAPA_TSG_FLASH_MAP_BLOCK_SIZE      0x80
+#define CYAPA_TSG_IMG_FW_HDR_SIZE           13
+#define CYAPA_TSG_FW_ROW_SIZE               (CYAPA_TSG_FLASH_MAP_BLOCK_SIZE)
+#define CYAPA_TSG_IMG_START_ROW_NUM         0x002e
+#define CYAPA_TSG_IMG_END_ROW_NUM           0x01fe
+#define CYAPA_TSG_IMG_APP_INTEGRITY_ROW_NUM 0x01ff
+#define CYAPA_TSG_IMG_MAX_RECORDS           (CYAPA_TSG_IMG_END_ROW_NUM - \
+				CYAPA_TSG_IMG_START_ROW_NUM + 1 + 1)
+#define CYAPA_TSG_IMG_READ_SIZE             (CYAPA_TSG_FLASH_MAP_BLOCK_SIZE / 2)
+#define CYAPA_TSG_START_OF_APPLICATION      0x1700
+#define CYAPA_TSG_APP_INTEGRITY_SIZE        60
+#define CYAPA_TSG_FLASH_MAP_METADATA_SIZE   60
+#define CYAPA_TSG_BL_KEY_SIZE               8
+
+#define CYAPA_TSG_MAX_CMD_SIZE              256
+
+/* Macro of PIP interface */
+#define PIP_BL_INITIATE_RESP_LEN            11
+#define PIP_BL_FAIL_EXIT_RESP_LEN           11
+#define PIP_BL_FAIL_EXIT_STATUS_CODE        0x0c
+#define PIP_BL_VERIFY_INTEGRITY_RESP_LEN    12
+#define PIP_BL_INTEGRITY_CHEKC_PASS         0x00
+#define PIP_BL_BLOCK_WRITE_RESP_LEN         11
+
+#define PIP_TOUCH_REPORT_ID         0x01
+#define PIP_BTN_REPORT_ID           0x03
+#define PIP_WAKEUP_EVENT_REPORT_ID  0x04
+#define PIP_PUSH_BTN_REPORT_ID      0x06
+#define GEN5_OLD_PUSH_BTN_REPORT_ID 0x05  /* Special for old Gen5 TP. */
+#define PIP_PROXIMITY_REPORT_ID     0x07
+
+#define PIP_PROXIMITY_REPORT_SIZE	6
+#define PIP_PROXIMITY_DISTANCE_OFFSET	0x05
+#define PIP_PROXIMITY_DISTANCE_MASK	0x01
+
+#define PIP_TOUCH_REPORT_HEAD_SIZE     7
+#define PIP_TOUCH_REPORT_MAX_SIZE      127
+#define PIP_BTN_REPORT_HEAD_SIZE       6
+#define PIP_BTN_REPORT_MAX_SIZE        14
+#define PIP_WAKEUP_EVENT_SIZE          4
+
+#define PIP_NUMBER_OF_TOUCH_OFFSET  5
+#define PIP_NUMBER_OF_TOUCH_MASK    0x1f
+#define PIP_BUTTONS_OFFSET          5
+#define PIP_BUTTONS_MASK            0x0f
+#define PIP_GET_EVENT_ID(reg)       (((reg) >> 5) & 0x03)
+#define PIP_GET_TOUCH_ID(reg)       ((reg) & 0x1f)
+#define PIP_TOUCH_TYPE_FINGER	    0x00
+#define PIP_TOUCH_TYPE_PROXIMITY    0x01
+#define PIP_TOUCH_TYPE_HOVER	    0x02
+#define PIP_GET_TOUCH_TYPE(reg)     ((reg) & 0x07)
+
+#define RECORD_EVENT_NONE        0
+#define RECORD_EVENT_TOUCHDOWN	 1
+#define RECORD_EVENT_DISPLACE    2
+#define RECORD_EVENT_LIFTOFF     3
+
+#define PIP_SENSING_MODE_MUTUAL_CAP_FINE   0x00
+#define PIP_SENSING_MODE_SELF_CAP          0x02
+
+#define PIP_SET_PROXIMITY	0x49
+
+/* Macro of Gen5 */
+#define GEN5_BL_MAX_OUTPUT_LENGTH     0x0100
+#define GEN5_APP_MAX_OUTPUT_LENGTH    0x00fe
+
+#define GEN5_POWER_STATE_ACTIVE              0x01
+#define GEN5_POWER_STATE_LOOK_FOR_TOUCH      0x02
+#define GEN5_POWER_STATE_READY               0x03
+#define GEN5_POWER_STATE_IDLE                0x04
+#define GEN5_POWER_STATE_BTN_ONLY            0x05
+#define GEN5_POWER_STATE_OFF                 0x06
+
+#define GEN5_POWER_READY_MAX_INTRVL_TIME  50   /* Unit: ms */
+#define GEN5_POWER_IDLE_MAX_INTRVL_TIME   250  /* Unit: ms */
+
+#define GEN5_CMD_GET_PARAMETER		     0x05
+#define GEN5_CMD_SET_PARAMETER		     0x06
+#define GEN5_PARAMETER_ACT_INTERVL_ID        0x4d
+#define GEN5_PARAMETER_ACT_INTERVL_SIZE      1
+#define GEN5_PARAMETER_ACT_LFT_INTERVL_ID    0x4f
+#define GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE  2
+#define GEN5_PARAMETER_LP_INTRVL_ID          0x4c
+#define GEN5_PARAMETER_LP_INTRVL_SIZE        2
+
+#define GEN5_PARAMETER_DISABLE_PIP_REPORT    0x08
+
+#define GEN5_BL_REPORT_DESCRIPTOR_SIZE            0x1d
+#define GEN5_BL_REPORT_DESCRIPTOR_ID              0xfe
+#define GEN5_APP_REPORT_DESCRIPTOR_SIZE           0xee
+#define GEN5_APP_CONTRACT_REPORT_DESCRIPTOR_SIZE  0xfa
+#define GEN5_APP_REPORT_DESCRIPTOR_ID             0xf6
+
+#define GEN5_RETRIEVE_MUTUAL_PWC_DATA        0x00
+#define GEN5_RETRIEVE_SELF_CAP_PWC_DATA      0x01
+
+#define GEN5_RETRIEVE_DATA_ELEMENT_SIZE_MASK 0x07
+
+#define GEN5_CMD_EXECUTE_PANEL_SCAN          0x2a
+#define GEN5_CMD_RETRIEVE_PANEL_SCAN         0x2b
+#define GEN5_PANEL_SCAN_MUTUAL_RAW_DATA      0x00
+#define GEN5_PANEL_SCAN_MUTUAL_BASELINE      0x01
+#define GEN5_PANEL_SCAN_MUTUAL_DIFFCOUNT     0x02
+#define GEN5_PANEL_SCAN_SELF_RAW_DATA        0x03
+#define GEN5_PANEL_SCAN_SELF_BASELINE        0x04
+#define GEN5_PANEL_SCAN_SELF_DIFFCOUNT       0x05
+
+/* The offset only valid for retrieve PWC and panel scan commands */
+#define GEN5_RESP_DATA_STRUCTURE_OFFSET      10
+#define GEN5_PWC_DATA_ELEMENT_SIZE_MASK      0x07
+
+
+struct cyapa_pip_touch_record {
+	/*
+	 * Bit 7 - 3: reserved
+	 * Bit 2 - 0: touch type;
+	 *            0 : standard finger;
+	 *            1 : proximity (Start supported in Gen5 TP).
+	 *            2 : finger hover (defined, but not used yet.)
+	 *            3 - 15 : reserved.
+	 */
+	u8 touch_type;
+
+	/*
+	 * Bit 7: indicates touch liftoff status.
+	 *		0 : touch is currently on the panel.
+	 *		1 : touch record indicates a liftoff.
+	 * Bit 6 - 5: indicates an event associated with this touch instance
+	 *		0 : no event
+	 *		1 : touchdown
+	 *		2 : significant displacement (> active distance)
+	 *		3 : liftoff (record reports last known coordinates)
+	 * Bit 4 - 0: An arbitrary ID tag associated with a finger
+	 *		to allow tracking a touch as it moves around the panel.
+	 */
+	u8 touch_tip_event_id;
+
+	/* Bit 7 - 0 of X-axis coordinate of the touch in pixel. */
+	u8 x_lo;
+
+	/* Bit 15 - 8 of X-axis coordinate of the touch in pixel. */
+	u8 x_hi;
+
+	/* Bit 7 - 0 of Y-axis coordinate of the touch in pixel. */
+	u8 y_lo;
+
+	/* Bit 15 - 8 of Y-axis coordinate of the touch in pixel. */
+	u8 y_hi;
+
+	/*
+	 * The meaning of this value is different when touch_type is different.
+	 * For standard finger type:
+	 *	Touch intensity in counts, pressure value.
+	 * For proximity type (Start supported in Gen5 TP):
+	 *	The distance, in surface units, between the contact and
+	 *	the surface.
+	 **/
+	u8 z;
+
+	/*
+	 * The length of the major axis of the ellipse of contact between
+	 * the finger and the panel (ABS_MT_TOUCH_MAJOR).
+	 */
+	u8 major_axis_len;
+
+	/*
+	 * The length of the minor axis of the ellipse of contact between
+	 * the finger and the panel (ABS_MT_TOUCH_MINOR).
+	 */
+	u8 minor_axis_len;
+
+	/*
+	 * The length of the major axis of the approaching tool.
+	 * (ABS_MT_WIDTH_MAJOR)
+	 */
+	u8 major_tool_len;
+
+	/*
+	 * The length of the minor axis of the approaching tool.
+	 * (ABS_MT_WIDTH_MINOR)
+	 */
+	u8 minor_tool_len;
+
+	/*
+	 * The angle between the panel vertical axis and
+	 * the major axis of the contact ellipse. This value is an 8-bit
+	 * signed integer. The range is -127 to +127 (corresponding to
+	 * -90 degree and +90 degree respectively).
+	 * The positive direction is clockwise from the vertical axis.
+	 * If the ellipse of contact degenerates into a circle,
+	 * orientation is reported as 0.
+	 */
+	u8 orientation;
+} __packed;
+
+struct cyapa_pip_report_data {
+	u8 report_head[PIP_TOUCH_REPORT_HEAD_SIZE];
+	struct cyapa_pip_touch_record touch_records[10];
+} __packed;
+
+struct cyapa_tsg_bin_image_head {
+	u8 head_size;  /* Unit: bytes, including itself. */
+	u8 ttda_driver_major_version;  /* Reserved as 0. */
+	u8 ttda_driver_minor_version;  /* Reserved as 0. */
+	u8 fw_major_version;
+	u8 fw_minor_version;
+	u8 fw_revision_control_number[8];
+	u8 silicon_id_hi;
+	u8 silicon_id_lo;
+	u8 chip_revision;
+	u8 family_id;
+	u8 bl_ver_maj;
+	u8 bl_ver_min;
+} __packed;
+
+struct cyapa_tsg_bin_image_data_record {
+	u8 flash_array_id;
+	__be16 row_number;
+	/* The number of bytes of flash data contained in this record. */
+	__be16 record_len;
+	/* The flash program data. */
+	u8 record_data[CYAPA_TSG_FW_ROW_SIZE];
+} __packed;
+
+struct cyapa_tsg_bin_image {
+	struct cyapa_tsg_bin_image_head image_head;
+	struct cyapa_tsg_bin_image_data_record records[0];
+} __packed;
+
+struct pip_bl_packet_start {
+	u8 sop;  /* Start of packet, must be 01h */
+	u8 cmd_code;
+	__le16 data_length;  /* Size of data parameter start from data[0] */
+} __packed;
+
+struct pip_bl_packet_end {
+	__le16 crc;
+	u8 eop;  /* End of packet, must be 17h */
+} __packed;
+
+struct pip_bl_cmd_head {
+	__le16 addr;   /* Output report register address, must be 0004h */
+	/* Size of packet not including output report register address */
+	__le16 length;
+	u8 report_id;  /* Bootloader output report id, must be 40h */
+	u8 rsvd;  /* Reserved, must be 0 */
+	struct pip_bl_packet_start packet_start;
+	u8 data[0];  /* Command data variable based on commands */
+} __packed;
+
+/* Initiate bootload command data structure. */
+struct pip_bl_initiate_cmd_data {
+	/* Key must be "A5h 01h 02h 03h FFh FEh FDh 5Ah" */
+	u8 key[CYAPA_TSG_BL_KEY_SIZE];
+	u8 metadata_raw_parameter[CYAPA_TSG_FLASH_MAP_METADATA_SIZE];
+	__le16 metadata_crc;
+} __packed;
+
+struct tsg_bl_metadata_row_params {
+	__le16 size;
+	__le16 maximum_size;
+	__le32 app_start;
+	__le16 app_len;
+	__le16 app_crc;
+	__le32 app_entry;
+	__le32 upgrade_start;
+	__le16 upgrade_len;
+	__le16 entry_row_crc;
+	u8 padding[36];  /* Padding data must be 0 */
+	__le16 metadata_crc;  /* CRC starts at offset of 60 */
+} __packed;
+
+/* Bootload program and verify row command data structure */
+struct tsg_bl_flash_row_head {
+	u8 flash_array_id;
+	__le16 flash_row_id;
+	u8 flash_data[0];
+} __packed;
+
+struct pip_app_cmd_head {
+	__le16 addr;   /* Output report register address, must be 0004h */
+	/* Size of packet not including output report register address */
+	__le16 length;
+	u8 report_id;  /* Application output report id, must be 2Fh */
+	u8 rsvd;  /* Reserved, must be 0 */
+	/*
+	 * Bit 7: reserved, must be 0.
+	 * Bit 6-0: command code.
+	 */
+	u8 cmd_code;
+	u8 parameter_data[0];  /* Parameter data variable based on cmd_code */
+} __packed;
+
+/* Application get/set parameter command data structure */
+struct gen5_app_set_parameter_data {
+	u8 parameter_id;
+	u8 parameter_size;
+	__le32 value;
+} __packed;
+
+struct gen5_app_get_parameter_data {
+	u8 parameter_id;
+} __packed;
+
+struct gen5_retrieve_panel_scan_data {
+	__le16 read_offset;
+	__le16 read_elements;
+	u8 data_id;
+} __packed;
+
+u8 pip_read_sys_info[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x02 };
+u8 pip_bl_read_app_info[] = { 0x04, 0x00, 0x0b, 0x00, 0x40, 0x00,
+		0x01, 0x3c, 0x00, 0x00, 0xb0, 0x42, 0x17
+	};
+
+static u8 cyapa_pip_bl_cmd_key[] = { 0xa5, 0x01, 0x02, 0x03,
+	0xff, 0xfe, 0xfd, 0x5a };
+
+int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa)
+{
+	struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip;
+
+	init_completion(&pip->cmd_ready);
+	atomic_set(&pip->cmd_issued, 0);
+	mutex_init(&pip->cmd_lock);
+
+	pip->resp_sort_func = NULL;
+	pip->in_progress_cmd = PIP_INVALID_CMD;
+	pip->resp_data = NULL;
+	pip->resp_len = NULL;
+
+	cyapa->dev_pwr_mode = UNINIT_PWR_MODE;
+	cyapa->dev_sleep_time = UNINIT_SLEEP_TIME;
+
+	return 0;
+}
+
+/* Return negative errno, or else the number of bytes read. */
+ssize_t cyapa_i2c_pip_read(struct cyapa *cyapa, u8 *buf, size_t size)
+{
+	int ret;
+
+	if (size == 0)
+		return 0;
+
+	if (!buf || size > CYAPA_REG_MAP_SIZE)
+		return -EINVAL;
+
+	ret = i2c_master_recv(cyapa->client, buf, size);
+
+	if (ret != size)
+		return (ret < 0) ? ret : -EIO;
+	return size;
+}
+
+/**
+ * Return a negative errno code else zero on success.
+ */
+ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size)
+{
+	int ret;
+
+	if (!buf || !size)
+		return -EINVAL;
+
+	ret = i2c_master_send(cyapa->client, buf, size);
+
+	if (ret != size)
+		return (ret < 0) ? ret : -EIO;
+
+	return 0;
+}
+
+/**
+ * This function is aimed to dump all not read data in Gen5 trackpad
+ * before send any command, otherwise, the interrupt line will be blocked.
+ */
+int cyapa_empty_pip_output_data(struct cyapa *cyapa,
+		u8 *buf, int *len, cb_sort func)
+{
+	struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip;
+	int length;
+	int report_count;
+	int empty_count;
+	int buf_len;
+	int error;
+
+	buf_len = 0;
+	if (len) {
+		buf_len = (*len < CYAPA_REG_MAP_SIZE) ?
+				*len : CYAPA_REG_MAP_SIZE;
+		*len = 0;
+	}
+
+	report_count = 8;  /* max 7 pending data before command response data */
+	empty_count = 0;
+	do {
+		/*
+		 * Depending on testing in cyapa driver, there are max 5 "02 00"
+		 * packets between two valid buffered data report in firmware.
+		 * So in order to dump all buffered data out and
+		 * make interrupt line release for reassert again,
+		 * we must set the empty_count check value bigger than 5 to
+		 * make it work. Otherwise, in some situation,
+		 * the interrupt line may unable to reactive again,
+		 * which will cause trackpad device unable to
+		 * report data any more.
+		 * for example, it may happen in EFT and ESD testing.
+		 */
+		if (empty_count > 5)
+			return 0;
+
+		error = cyapa_i2c_pip_read(cyapa, pip->empty_buf,
+				PIP_RESP_LENGTH_SIZE);
+		if (error < 0)
+			return error;
+
+		length = get_unaligned_le16(pip->empty_buf);
+		if (length == PIP_RESP_LENGTH_SIZE) {
+			empty_count++;
+			continue;
+		} else if (length > CYAPA_REG_MAP_SIZE) {
+			/* Should not happen */
+			return -EINVAL;
+		} else if (length == 0) {
+			/* Application or bootloader launch data polled out. */
+			length = PIP_RESP_LENGTH_SIZE;
+			if (buf && buf_len && func &&
+				func(cyapa, pip->empty_buf, length)) {
+				length = min(buf_len, length);
+				memcpy(buf, pip->empty_buf, length);
+				*len = length;
+				/* Response found, success. */
+				return 0;
+			}
+			continue;
+		}
+
+		error = cyapa_i2c_pip_read(cyapa, pip->empty_buf, length);
+		if (error < 0)
+			return error;
+
+		report_count--;
+		empty_count = 0;
+		length = get_unaligned_le16(pip->empty_buf);
+		if (length <= PIP_RESP_LENGTH_SIZE) {
+			empty_count++;
+		} else if (buf && buf_len && func &&
+			func(cyapa, pip->empty_buf, length)) {
+			length = min(buf_len, length);
+			memcpy(buf, pip->empty_buf, length);
+			*len = length;
+			/* Response found, success. */
+			return 0;
+		}
+
+		error = -EINVAL;
+	} while (report_count);
+
+	return error;
+}
+
+static int cyapa_do_i2c_pip_cmd_irq_sync(
+		struct cyapa *cyapa,
+		u8 *cmd, size_t cmd_len,
+		unsigned long timeout)
+{
+	struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip;
+	int error;
+
+	/* Wait for interrupt to set ready completion */
+	init_completion(&pip->cmd_ready);
+
+	atomic_inc(&pip->cmd_issued);
+	error = cyapa_i2c_pip_write(cyapa, cmd, cmd_len);
+	if (error) {
+		atomic_dec(&pip->cmd_issued);
+		return (error < 0) ? error : -EIO;
+	}
+
+	/* Wait for interrupt to indicate command is completed. */
+	timeout = wait_for_completion_timeout(&pip->cmd_ready,
+				msecs_to_jiffies(timeout));
+	if (timeout == 0) {
+		atomic_dec(&pip->cmd_issued);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int cyapa_do_i2c_pip_cmd_polling(
+		struct cyapa *cyapa,
+		u8 *cmd, size_t cmd_len,
+		u8 *resp_data, int *resp_len,
+		unsigned long timeout,
+		cb_sort func)
+{
+	struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip;
+	int tries;
+	int length;
+	int error;
+
+	atomic_inc(&pip->cmd_issued);
+	error = cyapa_i2c_pip_write(cyapa, cmd, cmd_len);
+	if (error) {
+		atomic_dec(&pip->cmd_issued);
+		return error < 0 ? error : -EIO;
+	}
+
+	length = resp_len ? *resp_len : 0;
+	if (resp_data && resp_len && length != 0 && func) {
+		tries = timeout / 5;
+		do {
+			usleep_range(3000, 5000);
+			*resp_len = length;
+			error = cyapa_empty_pip_output_data(cyapa,
+					resp_data, resp_len, func);
+			if (error || *resp_len == 0)
+				continue;
+			else
+				break;
+		} while (--tries > 0);
+		if ((error || *resp_len == 0) || tries <= 0)
+			error = error ? error : -ETIMEDOUT;
+	}
+
+	atomic_dec(&pip->cmd_issued);
+	return error;
+}
+
+int cyapa_i2c_pip_cmd_irq_sync(
+		struct cyapa *cyapa,
+		u8 *cmd, int cmd_len,
+		u8 *resp_data, int *resp_len,
+		unsigned long timeout,
+		cb_sort func,
+		bool irq_mode)
+{
+	struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip;
+	int error;
+
+	if (!cmd || !cmd_len)
+		return -EINVAL;
+
+	/* Commands must be serialized. */
+	error = mutex_lock_interruptible(&pip->cmd_lock);
+	if (error)
+		return error;
+
+	pip->resp_sort_func = func;
+	pip->resp_data = resp_data;
+	pip->resp_len = resp_len;
+
+	if (cmd_len >= PIP_MIN_APP_CMD_LENGTH &&
+			cmd[4] == PIP_APP_CMD_REPORT_ID) {
+		/* Application command */
+		pip->in_progress_cmd = cmd[6] & 0x7f;
+	} else if (cmd_len >= PIP_MIN_BL_CMD_LENGTH &&
+			cmd[4] == PIP_BL_CMD_REPORT_ID) {
+		/* Bootloader command */
+		pip->in_progress_cmd = cmd[7];
+	}
+
+	/* Send command data, wait and read output response data's length. */
+	if (irq_mode) {
+		pip->is_irq_mode = true;
+		error = cyapa_do_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len,
+							timeout);
+		if (error == -ETIMEDOUT && resp_data &&
+				resp_len && *resp_len != 0 && func) {
+			/*
+			 * For some old version, there was no interrupt for
+			 * the command response data, so need to poll here
+			 * to try to get the response data.
+			 */
+			error = cyapa_empty_pip_output_data(cyapa,
+					resp_data, resp_len, func);
+			if (error || *resp_len == 0)
+				error = error ? error : -ETIMEDOUT;
+		}
+	} else {
+		pip->is_irq_mode = false;
+		error = cyapa_do_i2c_pip_cmd_polling(cyapa, cmd, cmd_len,
+				resp_data, resp_len, timeout, func);
+	}
+
+	pip->resp_sort_func = NULL;
+	pip->resp_data = NULL;
+	pip->resp_len = NULL;
+	pip->in_progress_cmd = PIP_INVALID_CMD;
+
+	mutex_unlock(&pip->cmd_lock);
+	return error;
+}
+
+bool cyapa_sort_tsg_pip_bl_resp_data(struct cyapa *cyapa,
+		u8 *data, int len)
+{
+	if (!data || len < PIP_MIN_BL_RESP_LENGTH)
+		return false;
+
+	/* Bootloader input report id 30h */
+	if (data[PIP_RESP_REPORT_ID_OFFSET] == PIP_BL_RESP_REPORT_ID &&
+			data[PIP_RESP_RSVD_OFFSET] == PIP_RESP_RSVD_KEY &&
+			data[PIP_RESP_BL_SOP_OFFSET] == PIP_SOP_KEY)
+		return true;
+
+	return false;
+}
+
+bool cyapa_sort_tsg_pip_app_resp_data(struct cyapa *cyapa,
+		u8 *data, int len)
+{
+	struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip;
+	int resp_len;
+
+	if (!data || len < PIP_MIN_APP_RESP_LENGTH)
+		return false;
+
+	if (data[PIP_RESP_REPORT_ID_OFFSET] == PIP_APP_RESP_REPORT_ID &&
+			data[PIP_RESP_RSVD_OFFSET] == PIP_RESP_RSVD_KEY) {
+		resp_len = get_unaligned_le16(&data[PIP_RESP_LENGTH_OFFSET]);
+		if (GET_PIP_CMD_CODE(data[PIP_RESP_APP_CMD_OFFSET]) == 0x00 &&
+			resp_len == PIP_UNSUPPORTED_CMD_RESP_LENGTH &&
+			data[5] == pip->in_progress_cmd) {
+			/* Unsupported command code */
+			return false;
+		} else if (GET_PIP_CMD_CODE(data[PIP_RESP_APP_CMD_OFFSET]) ==
+				pip->in_progress_cmd) {
+			/* Correct command response received */
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static bool cyapa_sort_pip_application_launch_data(struct cyapa *cyapa,
+		u8 *buf, int len)
+{
+	if (buf == NULL || len < PIP_RESP_LENGTH_SIZE)
+		return false;
+
+	/*
+	 * After reset or power on, trackpad device always sets to 0x00 0x00
+	 * to indicate a reset or power on event.
+	 */
+	if (buf[0] == 0 && buf[1] == 0)
+		return true;
+
+	return false;
+}
+
+static bool cyapa_sort_gen5_hid_descriptor_data(struct cyapa *cyapa,
+		u8 *buf, int len)
+{
+	int resp_len;
+	int max_output_len;
+
+	/* Check hid descriptor. */
+	if (len != PIP_HID_DESCRIPTOR_SIZE)
+		return false;
+
+	resp_len = get_unaligned_le16(&buf[PIP_RESP_LENGTH_OFFSET]);
+	max_output_len = get_unaligned_le16(&buf[16]);
+	if (resp_len == PIP_HID_DESCRIPTOR_SIZE) {
+		if (buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID &&
+				max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) {
+			/* BL mode HID Descriptor */
+			return true;
+		} else if ((buf[PIP_RESP_REPORT_ID_OFFSET] ==
+				PIP_HID_APP_REPORT_ID) &&
+				max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) {
+			/* APP mode HID Descriptor */
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static bool cyapa_sort_pip_deep_sleep_data(struct cyapa *cyapa,
+		u8 *buf, int len)
+{
+	if (len == PIP_DEEP_SLEEP_RESP_LENGTH &&
+		buf[PIP_RESP_REPORT_ID_OFFSET] ==
+			PIP_APP_DEEP_SLEEP_REPORT_ID &&
+		(buf[4] & PIP_DEEP_SLEEP_OPCODE_MASK) ==
+			PIP_DEEP_SLEEP_OPCODE)
+		return true;
+	return false;
+}
+
+static int gen5_idle_state_parse(struct cyapa *cyapa)
+{
+	u8 resp_data[PIP_HID_DESCRIPTOR_SIZE];
+	int max_output_len;
+	int length;
+	u8 cmd[2];
+	int ret;
+	int error;
+
+	/*
+	 * Dump all buffered data firstly for the situation
+	 * when the trackpad is just power on the cyapa go here.
+	 */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	memset(resp_data, 0, sizeof(resp_data));
+	ret = cyapa_i2c_pip_read(cyapa, resp_data, 3);
+	if (ret != 3)
+		return ret < 0 ? ret : -EIO;
+
+	length = get_unaligned_le16(&resp_data[PIP_RESP_LENGTH_OFFSET]);
+	if (length == PIP_RESP_LENGTH_SIZE) {
+		/* Normal state of Gen5 with no data to response */
+		cyapa->gen = CYAPA_GEN5;
+
+		cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+		/* Read description from trackpad device */
+		cmd[0] = 0x01;
+		cmd[1] = 0x00;
+		length = PIP_HID_DESCRIPTOR_SIZE;
+		error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+				cmd, PIP_RESP_LENGTH_SIZE,
+				resp_data, &length,
+				300,
+				cyapa_sort_gen5_hid_descriptor_data,
+				false);
+		if (error)
+			return error;
+
+		length = get_unaligned_le16(
+				&resp_data[PIP_RESP_LENGTH_OFFSET]);
+		max_output_len = get_unaligned_le16(&resp_data[16]);
+		if ((length == PIP_HID_DESCRIPTOR_SIZE ||
+				length == PIP_RESP_LENGTH_SIZE) &&
+			(resp_data[PIP_RESP_REPORT_ID_OFFSET] ==
+				PIP_HID_BL_REPORT_ID) &&
+			max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) {
+			/* BL mode HID Description read */
+			cyapa->state = CYAPA_STATE_GEN5_BL;
+		} else if ((length == PIP_HID_DESCRIPTOR_SIZE ||
+				length == PIP_RESP_LENGTH_SIZE) &&
+			(resp_data[PIP_RESP_REPORT_ID_OFFSET] ==
+				PIP_HID_APP_REPORT_ID) &&
+			max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) {
+			/* APP mode HID Description read */
+			cyapa->state = CYAPA_STATE_GEN5_APP;
+		} else {
+			/* Should not happen!!! */
+			cyapa->state = CYAPA_STATE_NO_DEVICE;
+		}
+	}
+
+	return 0;
+}
+
+static int gen5_hid_description_header_parse(struct cyapa *cyapa, u8 *reg_data)
+{
+	int length;
+	u8 resp_data[32];
+	int max_output_len;
+	int ret;
+
+	/* 0x20 0x00 0xF7 is Gen5 Application HID Description Header;
+	 * 0x20 0x00 0xFF is Gen5 Bootloader HID Description Header.
+	 *
+	 * Must read HID Description content through out,
+	 * otherwise Gen5 trackpad cannot response next command
+	 * or report any touch or button data.
+	 */
+	ret = cyapa_i2c_pip_read(cyapa, resp_data,
+			PIP_HID_DESCRIPTOR_SIZE);
+	if (ret != PIP_HID_DESCRIPTOR_SIZE)
+		return ret < 0 ? ret : -EIO;
+	length = get_unaligned_le16(&resp_data[PIP_RESP_LENGTH_OFFSET]);
+	max_output_len = get_unaligned_le16(&resp_data[16]);
+	if (length == PIP_RESP_LENGTH_SIZE) {
+		if (reg_data[PIP_RESP_REPORT_ID_OFFSET] ==
+				PIP_HID_BL_REPORT_ID) {
+			/*
+			 * BL mode HID Description has been previously
+			 * read out.
+			 */
+			cyapa->gen = CYAPA_GEN5;
+			cyapa->state = CYAPA_STATE_GEN5_BL;
+		} else {
+			/*
+			 * APP mode HID Description has been previously
+			 * read out.
+			 */
+			cyapa->gen = CYAPA_GEN5;
+			cyapa->state = CYAPA_STATE_GEN5_APP;
+		}
+	} else if (length == PIP_HID_DESCRIPTOR_SIZE &&
+			resp_data[2] == PIP_HID_BL_REPORT_ID &&
+			max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) {
+		/* BL mode HID Description read. */
+		cyapa->gen = CYAPA_GEN5;
+		cyapa->state = CYAPA_STATE_GEN5_BL;
+	} else if (length == PIP_HID_DESCRIPTOR_SIZE &&
+			(resp_data[PIP_RESP_REPORT_ID_OFFSET] ==
+				PIP_HID_APP_REPORT_ID) &&
+			max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) {
+		/* APP mode HID Description read. */
+		cyapa->gen = CYAPA_GEN5;
+		cyapa->state = CYAPA_STATE_GEN5_APP;
+	} else {
+		/* Should not happen!!! */
+		cyapa->state = CYAPA_STATE_NO_DEVICE;
+	}
+
+	return 0;
+}
+
+static int gen5_report_data_header_parse(struct cyapa *cyapa, u8 *reg_data)
+{
+	int length;
+
+	length = get_unaligned_le16(&reg_data[PIP_RESP_LENGTH_OFFSET]);
+	switch (reg_data[PIP_RESP_REPORT_ID_OFFSET]) {
+	case PIP_TOUCH_REPORT_ID:
+		if (length < PIP_TOUCH_REPORT_HEAD_SIZE ||
+			length > PIP_TOUCH_REPORT_MAX_SIZE)
+			return -EINVAL;
+		break;
+	case PIP_BTN_REPORT_ID:
+	case GEN5_OLD_PUSH_BTN_REPORT_ID:
+	case PIP_PUSH_BTN_REPORT_ID:
+		if (length < PIP_BTN_REPORT_HEAD_SIZE ||
+			length > PIP_BTN_REPORT_MAX_SIZE)
+			return -EINVAL;
+		break;
+	case PIP_WAKEUP_EVENT_REPORT_ID:
+		if (length != PIP_WAKEUP_EVENT_SIZE)
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	cyapa->gen = CYAPA_GEN5;
+	cyapa->state = CYAPA_STATE_GEN5_APP;
+	return 0;
+}
+
+static int gen5_cmd_resp_header_parse(struct cyapa *cyapa, u8 *reg_data)
+{
+	struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip;
+	int length;
+	int ret;
+
+	/*
+	 * Must read report data through out,
+	 * otherwise Gen5 trackpad cannot response next command
+	 * or report any touch or button data.
+	 */
+	length = get_unaligned_le16(&reg_data[PIP_RESP_LENGTH_OFFSET]);
+	ret = cyapa_i2c_pip_read(cyapa, pip->empty_buf, length);
+	if (ret != length)
+		return ret < 0 ? ret : -EIO;
+
+	if (length == PIP_RESP_LENGTH_SIZE) {
+		/* Previous command has read the data through out. */
+		if (reg_data[PIP_RESP_REPORT_ID_OFFSET] ==
+				PIP_BL_RESP_REPORT_ID) {
+			/* Gen5 BL command response data detected */
+			cyapa->gen = CYAPA_GEN5;
+			cyapa->state = CYAPA_STATE_GEN5_BL;
+		} else {
+			/* Gen5 APP command response data detected */
+			cyapa->gen = CYAPA_GEN5;
+			cyapa->state = CYAPA_STATE_GEN5_APP;
+		}
+	} else if ((pip->empty_buf[PIP_RESP_REPORT_ID_OFFSET] ==
+				PIP_BL_RESP_REPORT_ID) &&
+			(pip->empty_buf[PIP_RESP_RSVD_OFFSET] ==
+				PIP_RESP_RSVD_KEY) &&
+			(pip->empty_buf[PIP_RESP_BL_SOP_OFFSET] ==
+				PIP_SOP_KEY) &&
+			(pip->empty_buf[length - 1] ==
+				PIP_EOP_KEY)) {
+		/* Gen5 BL command response data detected */
+		cyapa->gen = CYAPA_GEN5;
+		cyapa->state = CYAPA_STATE_GEN5_BL;
+	} else if (pip->empty_buf[PIP_RESP_REPORT_ID_OFFSET] ==
+				PIP_APP_RESP_REPORT_ID &&
+			pip->empty_buf[PIP_RESP_RSVD_OFFSET] ==
+				PIP_RESP_RSVD_KEY) {
+		/* Gen5 APP command response data detected */
+		cyapa->gen = CYAPA_GEN5;
+		cyapa->state = CYAPA_STATE_GEN5_APP;
+	} else {
+		/* Should not happen!!! */
+		cyapa->state = CYAPA_STATE_NO_DEVICE;
+	}
+
+	return 0;
+}
+
+static int cyapa_gen5_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
+{
+	int length;
+
+	if (!reg_data || len < 3)
+		return -EINVAL;
+
+	cyapa->state = CYAPA_STATE_NO_DEVICE;
+
+	/* Parse based on Gen5 characteristic registers and bits */
+	length = get_unaligned_le16(&reg_data[PIP_RESP_LENGTH_OFFSET]);
+	if (length == 0 || length == PIP_RESP_LENGTH_SIZE) {
+		gen5_idle_state_parse(cyapa);
+	} else if (length == PIP_HID_DESCRIPTOR_SIZE &&
+			(reg_data[2] == PIP_HID_BL_REPORT_ID ||
+				reg_data[2] == PIP_HID_APP_REPORT_ID)) {
+		gen5_hid_description_header_parse(cyapa, reg_data);
+	} else if ((length == GEN5_APP_REPORT_DESCRIPTOR_SIZE ||
+			length == GEN5_APP_CONTRACT_REPORT_DESCRIPTOR_SIZE) &&
+			reg_data[2] == GEN5_APP_REPORT_DESCRIPTOR_ID) {
+		/* 0xEE 0x00 0xF6 is Gen5 APP report description header. */
+		cyapa->gen = CYAPA_GEN5;
+		cyapa->state = CYAPA_STATE_GEN5_APP;
+	} else if (length == GEN5_BL_REPORT_DESCRIPTOR_SIZE &&
+			reg_data[2] == GEN5_BL_REPORT_DESCRIPTOR_ID) {
+		/* 0x1D 0x00 0xFE is Gen5 BL report descriptor header. */
+		cyapa->gen = CYAPA_GEN5;
+		cyapa->state = CYAPA_STATE_GEN5_BL;
+	} else if (reg_data[2] == PIP_TOUCH_REPORT_ID ||
+			reg_data[2] == PIP_BTN_REPORT_ID ||
+			reg_data[2] == GEN5_OLD_PUSH_BTN_REPORT_ID ||
+			reg_data[2] == PIP_PUSH_BTN_REPORT_ID ||
+			reg_data[2] == PIP_WAKEUP_EVENT_REPORT_ID) {
+		gen5_report_data_header_parse(cyapa, reg_data);
+	} else if (reg_data[2] == PIP_BL_RESP_REPORT_ID ||
+			reg_data[2] == PIP_APP_RESP_REPORT_ID) {
+		gen5_cmd_resp_header_parse(cyapa, reg_data);
+	}
+
+	if (cyapa->gen == CYAPA_GEN5) {
+		/*
+		 * Must read the content (e.g.: report description and so on)
+		 * from trackpad device throughout. Otherwise,
+		 * Gen5 trackpad cannot response to next command or
+		 * report any touch or button data later.
+		 */
+		cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+		if (cyapa->state == CYAPA_STATE_GEN5_APP ||
+			cyapa->state == CYAPA_STATE_GEN5_BL)
+			return 0;
+	}
+
+	return -EAGAIN;
+}
+
+static struct cyapa_tsg_bin_image_data_record *
+cyapa_get_image_record_data_num(const struct firmware *fw,
+		int *record_num)
+{
+	int head_size;
+
+	head_size = fw->data[0] + 1;
+	*record_num = (fw->size - head_size) /
+			sizeof(struct cyapa_tsg_bin_image_data_record);
+	return (struct cyapa_tsg_bin_image_data_record *)&fw->data[head_size];
+}
+
+int cyapa_pip_bl_initiate(struct cyapa *cyapa, const struct firmware *fw)
+{
+	struct cyapa_tsg_bin_image_data_record *image_records;
+	struct pip_bl_cmd_head *bl_cmd_head;
+	struct pip_bl_packet_start *bl_packet_start;
+	struct pip_bl_initiate_cmd_data *cmd_data;
+	struct pip_bl_packet_end *bl_packet_end;
+	u8 cmd[CYAPA_TSG_MAX_CMD_SIZE];
+	int cmd_len;
+	u16 cmd_data_len;
+	u16 cmd_crc = 0;
+	u16 meta_data_crc = 0;
+	u8 resp_data[11];
+	int resp_len;
+	int records_num;
+	u8 *data;
+	int error;
+
+	/* Try to dump all buffered report data before any send command. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE);
+	bl_cmd_head = (struct pip_bl_cmd_head *)cmd;
+	cmd_data_len = CYAPA_TSG_BL_KEY_SIZE + CYAPA_TSG_FLASH_MAP_BLOCK_SIZE;
+	cmd_len = sizeof(struct pip_bl_cmd_head) + cmd_data_len +
+		  sizeof(struct pip_bl_packet_end);
+
+	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &bl_cmd_head->addr);
+	put_unaligned_le16(cmd_len - 2, &bl_cmd_head->length);
+	bl_cmd_head->report_id = PIP_BL_CMD_REPORT_ID;
+
+	bl_packet_start = &bl_cmd_head->packet_start;
+	bl_packet_start->sop = PIP_SOP_KEY;
+	bl_packet_start->cmd_code = PIP_BL_CMD_INITIATE_BL;
+	/* 8 key bytes and 128 bytes block size */
+	put_unaligned_le16(cmd_data_len, &bl_packet_start->data_length);
+
+	cmd_data = (struct pip_bl_initiate_cmd_data *)bl_cmd_head->data;
+	memcpy(cmd_data->key, cyapa_pip_bl_cmd_key, CYAPA_TSG_BL_KEY_SIZE);
+
+	image_records = cyapa_get_image_record_data_num(fw, &records_num);
+
+	/* APP_INTEGRITY row is always the last row block */
+	data = image_records[records_num - 1].record_data;
+	memcpy(cmd_data->metadata_raw_parameter, data,
+		CYAPA_TSG_FLASH_MAP_METADATA_SIZE);
+
+	meta_data_crc = crc_itu_t(0xffff, cmd_data->metadata_raw_parameter,
+				CYAPA_TSG_FLASH_MAP_METADATA_SIZE);
+	put_unaligned_le16(meta_data_crc, &cmd_data->metadata_crc);
+
+	bl_packet_end = (struct pip_bl_packet_end *)(bl_cmd_head->data +
+				cmd_data_len);
+	cmd_crc = crc_itu_t(0xffff, (u8 *)bl_packet_start,
+		sizeof(struct pip_bl_packet_start) + cmd_data_len);
+	put_unaligned_le16(cmd_crc, &bl_packet_end->crc);
+	bl_packet_end->eop = PIP_EOP_KEY;
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, cmd_len,
+			resp_data, &resp_len, 12000,
+			cyapa_sort_tsg_pip_bl_resp_data, true);
+	if (error || resp_len != PIP_BL_INITIATE_RESP_LEN ||
+			resp_data[2] != PIP_BL_RESP_REPORT_ID ||
+			!PIP_CMD_COMPLETE_SUCCESS(resp_data))
+		return error ? error : -EAGAIN;
+
+	return 0;
+}
+
+static bool cyapa_sort_pip_bl_exit_data(struct cyapa *cyapa, u8 *buf, int len)
+{
+	if (buf == NULL || len < PIP_RESP_LENGTH_SIZE)
+		return false;
+
+	if (buf[0] == 0 && buf[1] == 0)
+		return true;
+
+	/* Exit bootloader failed for some reason. */
+	if (len == PIP_BL_FAIL_EXIT_RESP_LEN &&
+			buf[PIP_RESP_REPORT_ID_OFFSET] ==
+				PIP_BL_RESP_REPORT_ID &&
+			buf[PIP_RESP_RSVD_OFFSET] == PIP_RESP_RSVD_KEY &&
+			buf[PIP_RESP_BL_SOP_OFFSET] == PIP_SOP_KEY &&
+			buf[10] == PIP_EOP_KEY)
+		return true;
+
+	return false;
+}
+
+int cyapa_pip_bl_exit(struct cyapa *cyapa)
+{
+
+	u8 bl_gen5_bl_exit[] = { 0x04, 0x00,
+		0x0B, 0x00, 0x40, 0x00, 0x01, 0x3b, 0x00, 0x00,
+		0x20, 0xc7, 0x17
+	};
+	u8 resp_data[11];
+	int resp_len;
+	int error;
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			bl_gen5_bl_exit, sizeof(bl_gen5_bl_exit),
+			resp_data, &resp_len,
+			5000, cyapa_sort_pip_bl_exit_data, false);
+	if (error)
+		return error;
+
+	if (resp_len == PIP_BL_FAIL_EXIT_RESP_LEN ||
+			resp_data[PIP_RESP_REPORT_ID_OFFSET] ==
+				PIP_BL_RESP_REPORT_ID)
+		return -EAGAIN;
+
+	if (resp_data[0] == 0x00 && resp_data[1] == 0x00)
+		return 0;
+
+	return -ENODEV;
+}
+
+int cyapa_pip_bl_enter(struct cyapa *cyapa)
+{
+	u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2F, 0x00, 0x01 };
+	u8 resp_data[2];
+	int resp_len;
+	int error;
+
+	error = cyapa_poll_state(cyapa, 500);
+	if (error < 0)
+		return error;
+
+	/* Already in bootloader mode, Skipping exit. */
+	if (cyapa_is_pip_bl_mode(cyapa))
+		return 0;
+	else if (!cyapa_is_pip_app_mode(cyapa))
+		return -EINVAL;
+
+	/* Try to dump all buffered report data before any send command. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	/*
+	 * Send bootloader enter command to trackpad device,
+	 * after enter bootloader, the response data is two bytes of 0x00 0x00.
+	 */
+	resp_len = sizeof(resp_data);
+	memset(resp_data, 0, resp_len);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			5000, cyapa_sort_pip_application_launch_data,
+			true);
+	if (error || resp_data[0] != 0x00 || resp_data[1] != 0x00)
+		return error < 0 ? error : -EAGAIN;
+
+	cyapa->operational = false;
+	if (cyapa->gen == CYAPA_GEN5)
+		cyapa->state = CYAPA_STATE_GEN5_BL;
+	else if (cyapa->gen == CYAPA_GEN6)
+		cyapa->state = CYAPA_STATE_GEN6_BL;
+	return 0;
+}
+
+static int cyapa_pip_fw_head_check(struct cyapa *cyapa,
+		struct cyapa_tsg_bin_image_head *image_head)
+{
+	if (image_head->head_size != 0x0C && image_head->head_size != 0x12)
+		return -EINVAL;
+
+	switch (cyapa->gen) {
+	case CYAPA_GEN6:
+		if (image_head->family_id != 0x9B ||
+		    image_head->silicon_id_hi != 0x0B)
+			return -EINVAL;
+		break;
+	case CYAPA_GEN5:
+		/* Gen5 without proximity support. */
+		if (cyapa->platform_ver < 2) {
+			if (image_head->head_size == 0x0C)
+				break;
+			return -EINVAL;
+		}
+
+		if (image_head->family_id != 0x91 ||
+		    image_head->silicon_id_hi != 0x02)
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cyapa_pip_check_fw(struct cyapa *cyapa, const struct firmware *fw)
+{
+	struct device *dev = &cyapa->client->dev;
+	struct cyapa_tsg_bin_image_data_record *image_records;
+	const struct cyapa_tsg_bin_image_data_record *app_integrity;
+	const struct tsg_bl_metadata_row_params *metadata;
+	int flash_records_count;
+	u32 fw_app_start, fw_upgrade_start;
+	u16 fw_app_len, fw_upgrade_len;
+	u16 app_crc;
+	u16 app_integrity_crc;
+	int i;
+
+	/* Verify the firmware image not miss-used for Gen5 and Gen6. */
+	if (cyapa_pip_fw_head_check(cyapa,
+		(struct cyapa_tsg_bin_image_head *)fw->data)) {
+		dev_err(dev, "%s: firmware image not match TP device.\n",
+			     __func__);
+		return -EINVAL;
+	}
+
+	image_records =
+		cyapa_get_image_record_data_num(fw, &flash_records_count);
+
+	/*
+	 * APP_INTEGRITY row is always the last row block,
+	 * and the row id must be 0x01ff.
+	 */
+	app_integrity = &image_records[flash_records_count - 1];
+
+	if (app_integrity->flash_array_id != 0x00 ||
+	    get_unaligned_be16(&app_integrity->row_number) != 0x01ff) {
+		dev_err(dev, "%s: invalid app_integrity data.\n", __func__);
+		return -EINVAL;
+	}
+
+	metadata = (const void *)app_integrity->record_data;
+
+	/* Verify app_integrity crc */
+	app_integrity_crc = crc_itu_t(0xffff, app_integrity->record_data,
+				      CYAPA_TSG_APP_INTEGRITY_SIZE);
+	if (app_integrity_crc != get_unaligned_le16(&metadata->metadata_crc)) {
+		dev_err(dev, "%s: invalid app_integrity crc.\n", __func__);
+		return -EINVAL;
+	}
+
+	fw_app_start = get_unaligned_le32(&metadata->app_start);
+	fw_app_len = get_unaligned_le16(&metadata->app_len);
+	fw_upgrade_start = get_unaligned_le32(&metadata->upgrade_start);
+	fw_upgrade_len = get_unaligned_le16(&metadata->upgrade_len);
+
+	if (fw_app_start % CYAPA_TSG_FW_ROW_SIZE ||
+	    fw_app_len % CYAPA_TSG_FW_ROW_SIZE ||
+	    fw_upgrade_start % CYAPA_TSG_FW_ROW_SIZE ||
+	    fw_upgrade_len % CYAPA_TSG_FW_ROW_SIZE) {
+		dev_err(dev, "%s: invalid image alignment.\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Verify application image CRC. */
+	app_crc = 0xffffU;
+	for (i = 0; i < fw_app_len / CYAPA_TSG_FW_ROW_SIZE; i++) {
+		const u8 *data = image_records[i].record_data;
+
+		app_crc = crc_itu_t(app_crc, data, CYAPA_TSG_FW_ROW_SIZE);
+	}
+
+	if (app_crc != get_unaligned_le16(&metadata->app_crc)) {
+		dev_err(dev, "%s: invalid firmware app crc check.\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cyapa_pip_write_fw_block(struct cyapa *cyapa,
+		struct cyapa_tsg_bin_image_data_record *flash_record)
+{
+	struct pip_bl_cmd_head *bl_cmd_head;
+	struct pip_bl_packet_start *bl_packet_start;
+	struct tsg_bl_flash_row_head *flash_row_head;
+	struct pip_bl_packet_end *bl_packet_end;
+	u8 cmd[CYAPA_TSG_MAX_CMD_SIZE];
+	u16 cmd_len;
+	u8 flash_array_id;
+	u16 flash_row_id;
+	u16 record_len;
+	u8 *record_data;
+	u16 data_len;
+	u16 crc;
+	u8 resp_data[11];
+	int resp_len;
+	int error;
+
+	flash_array_id = flash_record->flash_array_id;
+	flash_row_id = get_unaligned_be16(&flash_record->row_number);
+	record_len = get_unaligned_be16(&flash_record->record_len);
+	record_data = flash_record->record_data;
+
+	memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE);
+	bl_cmd_head = (struct pip_bl_cmd_head *)cmd;
+	bl_packet_start = &bl_cmd_head->packet_start;
+	cmd_len = sizeof(struct pip_bl_cmd_head) +
+		  sizeof(struct tsg_bl_flash_row_head) +
+		  CYAPA_TSG_FLASH_MAP_BLOCK_SIZE +
+		  sizeof(struct pip_bl_packet_end);
+
+	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &bl_cmd_head->addr);
+	/* Don't include 2 bytes register address */
+	put_unaligned_le16(cmd_len - 2, &bl_cmd_head->length);
+	bl_cmd_head->report_id = PIP_BL_CMD_REPORT_ID;
+	bl_packet_start->sop = PIP_SOP_KEY;
+	bl_packet_start->cmd_code = PIP_BL_CMD_PROGRAM_VERIFY_ROW;
+
+	/* 1 (Flash Array ID) + 2 (Flash Row ID) + 128 (flash data) */
+	data_len = sizeof(struct tsg_bl_flash_row_head) + record_len;
+	put_unaligned_le16(data_len, &bl_packet_start->data_length);
+
+	flash_row_head = (struct tsg_bl_flash_row_head *)bl_cmd_head->data;
+	flash_row_head->flash_array_id = flash_array_id;
+	put_unaligned_le16(flash_row_id, &flash_row_head->flash_row_id);
+	memcpy(flash_row_head->flash_data, record_data, record_len);
+
+	bl_packet_end = (struct pip_bl_packet_end *)(bl_cmd_head->data +
+						      data_len);
+	crc = crc_itu_t(0xffff, (u8 *)bl_packet_start,
+		sizeof(struct pip_bl_packet_start) + data_len);
+	put_unaligned_le16(crc, &bl_packet_end->crc);
+	bl_packet_end->eop = PIP_EOP_KEY;
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len,
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_bl_resp_data, true);
+	if (error || resp_len != PIP_BL_BLOCK_WRITE_RESP_LEN ||
+			resp_data[2] != PIP_BL_RESP_REPORT_ID ||
+			!PIP_CMD_COMPLETE_SUCCESS(resp_data))
+		return error < 0 ? error : -EAGAIN;
+
+	return 0;
+}
+
+int cyapa_pip_do_fw_update(struct cyapa *cyapa,
+		const struct firmware *fw)
+{
+	struct device *dev = &cyapa->client->dev;
+	struct cyapa_tsg_bin_image_data_record *image_records;
+	int flash_records_count;
+	int i;
+	int error;
+
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	image_records =
+		cyapa_get_image_record_data_num(fw, &flash_records_count);
+
+	/*
+	 * The last flash row 0x01ff has been written through bl_initiate
+	 * command, so DO NOT write flash 0x01ff to trackpad device.
+	 */
+	for (i = 0; i < (flash_records_count - 1); i++) {
+		error = cyapa_pip_write_fw_block(cyapa, &image_records[i]);
+		if (error) {
+			dev_err(dev, "%s: Gen5 FW update aborted: %d\n",
+				__func__, error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static int cyapa_gen5_change_power_state(struct cyapa *cyapa, u8 power_state)
+{
+	u8 cmd[8] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x08, 0x01 };
+	u8 resp_data[6];
+	int resp_len;
+	int error;
+
+	cmd[7] = power_state;
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_app_resp_data, false);
+	if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x08) ||
+			!PIP_CMD_COMPLETE_SUCCESS(resp_data))
+		return error < 0 ? error : -EINVAL;
+
+	return 0;
+}
+
+static int cyapa_gen5_set_interval_time(struct cyapa *cyapa,
+		u8 parameter_id, u16 interval_time)
+{
+	struct pip_app_cmd_head *app_cmd_head;
+	struct gen5_app_set_parameter_data *parameter_data;
+	u8 cmd[CYAPA_TSG_MAX_CMD_SIZE];
+	int cmd_len;
+	u8 resp_data[7];
+	int resp_len;
+	u8 parameter_size;
+	int error;
+
+	memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE);
+	app_cmd_head = (struct pip_app_cmd_head *)cmd;
+	parameter_data = (struct gen5_app_set_parameter_data *)
+			 app_cmd_head->parameter_data;
+	cmd_len = sizeof(struct pip_app_cmd_head) +
+		  sizeof(struct gen5_app_set_parameter_data);
+
+	switch (parameter_id) {
+	case GEN5_PARAMETER_ACT_INTERVL_ID:
+		parameter_size = GEN5_PARAMETER_ACT_INTERVL_SIZE;
+		break;
+	case GEN5_PARAMETER_ACT_LFT_INTERVL_ID:
+		parameter_size = GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE;
+		break;
+	case GEN5_PARAMETER_LP_INTRVL_ID:
+		parameter_size = GEN5_PARAMETER_LP_INTRVL_SIZE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &app_cmd_head->addr);
+	/*
+	 * Don't include unused parameter value bytes and
+	 * 2 bytes register address.
+	 */
+	put_unaligned_le16(cmd_len - (4 - parameter_size) - 2,
+			   &app_cmd_head->length);
+	app_cmd_head->report_id = PIP_APP_CMD_REPORT_ID;
+	app_cmd_head->cmd_code = GEN5_CMD_SET_PARAMETER;
+	parameter_data->parameter_id = parameter_id;
+	parameter_data->parameter_size = parameter_size;
+	put_unaligned_le32((u32)interval_time, &parameter_data->value);
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len,
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_app_resp_data, false);
+	if (error || resp_data[5] != parameter_id ||
+		resp_data[6] != parameter_size ||
+		!VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_SET_PARAMETER))
+		return error < 0 ? error : -EINVAL;
+
+	return 0;
+}
+
+static int cyapa_gen5_get_interval_time(struct cyapa *cyapa,
+		u8 parameter_id, u16 *interval_time)
+{
+	struct pip_app_cmd_head *app_cmd_head;
+	struct gen5_app_get_parameter_data *parameter_data;
+	u8 cmd[CYAPA_TSG_MAX_CMD_SIZE];
+	int cmd_len;
+	u8 resp_data[11];
+	int resp_len;
+	u8 parameter_size;
+	u16 mask, i;
+	int error;
+
+	memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE);
+	app_cmd_head = (struct pip_app_cmd_head *)cmd;
+	parameter_data = (struct gen5_app_get_parameter_data *)
+			 app_cmd_head->parameter_data;
+	cmd_len = sizeof(struct pip_app_cmd_head) +
+		  sizeof(struct gen5_app_get_parameter_data);
+
+	*interval_time = 0;
+	switch (parameter_id) {
+	case GEN5_PARAMETER_ACT_INTERVL_ID:
+		parameter_size = GEN5_PARAMETER_ACT_INTERVL_SIZE;
+		break;
+	case GEN5_PARAMETER_ACT_LFT_INTERVL_ID:
+		parameter_size = GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE;
+		break;
+	case GEN5_PARAMETER_LP_INTRVL_ID:
+		parameter_size = GEN5_PARAMETER_LP_INTRVL_SIZE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &app_cmd_head->addr);
+	/* Don't include 2 bytes register address */
+	put_unaligned_le16(cmd_len - 2, &app_cmd_head->length);
+	app_cmd_head->report_id = PIP_APP_CMD_REPORT_ID;
+	app_cmd_head->cmd_code = GEN5_CMD_GET_PARAMETER;
+	parameter_data->parameter_id = parameter_id;
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len,
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_app_resp_data, false);
+	if (error || resp_data[5] != parameter_id || resp_data[6] == 0 ||
+		!VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_GET_PARAMETER))
+		return error < 0 ? error : -EINVAL;
+
+	mask = 0;
+	for (i = 0; i < parameter_size; i++)
+		mask |= (0xff << (i * 8));
+	*interval_time = get_unaligned_le16(&resp_data[7]) & mask;
+
+	return 0;
+}
+
+static int cyapa_gen5_disable_pip_report(struct cyapa *cyapa)
+{
+	struct pip_app_cmd_head *app_cmd_head;
+	u8 cmd[10];
+	u8 resp_data[7];
+	int resp_len;
+	int error;
+
+	memset(cmd, 0, sizeof(cmd));
+	app_cmd_head = (struct pip_app_cmd_head *)cmd;
+
+	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &app_cmd_head->addr);
+	put_unaligned_le16(sizeof(cmd) - 2, &app_cmd_head->length);
+	app_cmd_head->report_id = PIP_APP_CMD_REPORT_ID;
+	app_cmd_head->cmd_code = GEN5_CMD_SET_PARAMETER;
+	app_cmd_head->parameter_data[0] = GEN5_PARAMETER_DISABLE_PIP_REPORT;
+	app_cmd_head->parameter_data[1] = 0x01;
+	app_cmd_head->parameter_data[2] = 0x01;
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_app_resp_data, false);
+	if (error || resp_data[5] != GEN5_PARAMETER_DISABLE_PIP_REPORT ||
+		!VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_SET_PARAMETER) ||
+		resp_data[6] != 0x01)
+		return error < 0 ? error : -EINVAL;
+
+	return 0;
+}
+
+int cyapa_pip_set_proximity(struct cyapa *cyapa, bool enable)
+{
+	u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, PIP_SET_PROXIMITY,
+		     (u8)!!enable
+	};
+	u8 resp_data[6];
+	int resp_len;
+	int error;
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_app_resp_data, false);
+	if (error || !VALID_CMD_RESP_HEADER(resp_data, PIP_SET_PROXIMITY) ||
+			!PIP_CMD_COMPLETE_SUCCESS(resp_data)) {
+		error = (error == -ETIMEDOUT) ? -EOPNOTSUPP : error;
+		return error < 0 ? error : -EINVAL;
+	}
+
+	return 0;
+}
+
+int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state)
+{
+	u8 cmd[] = { 0x05, 0x00, 0x00, 0x08};
+	u8 resp_data[5];
+	int resp_len;
+	int error;
+
+	cmd[2] = state & PIP_DEEP_SLEEP_STATE_MASK;
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			500, cyapa_sort_pip_deep_sleep_data, false);
+	if (error || ((resp_data[3] & PIP_DEEP_SLEEP_STATE_MASK) != state))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
+		u8 power_mode, u16 sleep_time, bool is_suspend)
+{
+	struct device *dev = &cyapa->client->dev;
+	u8 power_state;
+	int error;
+
+	if (cyapa->state != CYAPA_STATE_GEN5_APP)
+		return 0;
+
+	if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
+		/*
+		 * Assume TP in deep sleep mode when driver is loaded,
+		 * avoid driver unload and reload command IO issue caused by TP
+		 * has been set into deep sleep mode when unloading.
+		 */
+		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
+	}
+
+	if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) &&
+			PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF)
+		if (cyapa_gen5_get_interval_time(cyapa,
+				GEN5_PARAMETER_LP_INTRVL_ID,
+				&cyapa->dev_sleep_time) != 0)
+			PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME);
+
+	if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) {
+		if (power_mode == PWR_MODE_OFF ||
+			power_mode == PWR_MODE_FULL_ACTIVE ||
+			power_mode == PWR_MODE_BTN_ONLY ||
+			PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) {
+			/* Has in correct power mode state, early return. */
+			return 0;
+		}
+	}
+
+	if (power_mode == PWR_MODE_OFF) {
+		error = cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF);
+		if (error) {
+			dev_err(dev, "enter deep sleep fail: %d\n", error);
+			return error;
+		}
+
+		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
+		return 0;
+	}
+
+	/*
+	 * When trackpad in power off mode, it cannot change to other power
+	 * state directly, must be wake up from sleep firstly, then
+	 * continue to do next power sate change.
+	 */
+	if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) {
+		error = cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
+		if (error) {
+			dev_err(dev, "deep sleep wake fail: %d\n", error);
+			return error;
+		}
+	}
+
+	if (power_mode == PWR_MODE_FULL_ACTIVE) {
+		error = cyapa_gen5_change_power_state(cyapa,
+				GEN5_POWER_STATE_ACTIVE);
+		if (error) {
+			dev_err(dev, "change to active fail: %d\n", error);
+			return error;
+		}
+
+		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE);
+	} else if (power_mode == PWR_MODE_BTN_ONLY) {
+		error = cyapa_gen5_change_power_state(cyapa,
+				GEN5_POWER_STATE_BTN_ONLY);
+		if (error) {
+			dev_err(dev, "fail to button only mode: %d\n", error);
+			return error;
+		}
+
+		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY);
+	} else {
+		/*
+		 * Continue to change power mode even failed to set
+		 * interval time, it won't affect the power mode change.
+		 * except the sleep interval time is not correct.
+		 */
+		if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) ||
+				sleep_time != PIP_DEV_GET_SLEEP_TIME(cyapa))
+			if (cyapa_gen5_set_interval_time(cyapa,
+					GEN5_PARAMETER_LP_INTRVL_ID,
+					sleep_time) == 0)
+				PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time);
+
+		if (sleep_time <= GEN5_POWER_READY_MAX_INTRVL_TIME)
+			power_state = GEN5_POWER_STATE_READY;
+		else
+			power_state = GEN5_POWER_STATE_IDLE;
+		error = cyapa_gen5_change_power_state(cyapa, power_state);
+		if (error) {
+			dev_err(dev, "set power state to 0x%02x failed: %d\n",
+				power_state, error);
+			return error;
+		}
+
+		/*
+		 * Disable pip report for a little time, firmware will
+		 * re-enable it automatically. It's used to fix the issue
+		 * that trackpad unable to report signal to wake system up
+		 * in the special situation that system is in suspending, and
+		 * at the same time, user touch trackpad to wake system up.
+		 * This function can avoid the data to be buffered when system
+		 * is suspending which may cause interrupt line unable to be
+		 * asserted again.
+		 */
+		if (is_suspend)
+			cyapa_gen5_disable_pip_report(cyapa);
+
+		PIP_DEV_SET_PWR_STATE(cyapa,
+			cyapa_sleep_time_to_pwr_cmd(sleep_time));
+	}
+
+	return 0;
+}
+
+int cyapa_pip_resume_scanning(struct cyapa *cyapa)
+{
+	u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x04 };
+	u8 resp_data[6];
+	int resp_len;
+	int error;
+
+	/* Try to dump all buffered data before doing command. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_app_resp_data, true);
+	if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x04))
+		return -EINVAL;
+
+	/* Try to dump all buffered data when resuming scanning. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	return 0;
+}
+
+int cyapa_pip_suspend_scanning(struct cyapa *cyapa)
+{
+	u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x03 };
+	u8 resp_data[6];
+	int resp_len;
+	int error;
+
+	/* Try to dump all buffered data before doing command. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_app_resp_data, true);
+	if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x03))
+		return -EINVAL;
+
+	/* Try to dump all buffered data when suspending scanning. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	return 0;
+}
+
+static int cyapa_pip_calibrate_pwcs(struct cyapa *cyapa,
+		u8 calibrate_sensing_mode_type)
+{
+	struct pip_app_cmd_head *app_cmd_head;
+	u8 cmd[8];
+	u8 resp_data[6];
+	int resp_len;
+	int error;
+
+	/* Try to dump all buffered data before doing command. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	memset(cmd, 0, sizeof(cmd));
+	app_cmd_head = (struct pip_app_cmd_head *)cmd;
+	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &app_cmd_head->addr);
+	put_unaligned_le16(sizeof(cmd) - 2, &app_cmd_head->length);
+	app_cmd_head->report_id = PIP_APP_CMD_REPORT_ID;
+	app_cmd_head->cmd_code = PIP_CMD_CALIBRATE;
+	app_cmd_head->parameter_data[0] = calibrate_sensing_mode_type;
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			5000, cyapa_sort_tsg_pip_app_resp_data, true);
+	if (error || !VALID_CMD_RESP_HEADER(resp_data, PIP_CMD_CALIBRATE) ||
+			!PIP_CMD_COMPLETE_SUCCESS(resp_data))
+		return error < 0 ? error : -EAGAIN;
+
+	return 0;
+}
+
+ssize_t cyapa_pip_do_calibrate(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	int error, calibrate_error;
+
+	/* 1. Suspend Scanning*/
+	error = cyapa_pip_suspend_scanning(cyapa);
+	if (error)
+		return error;
+
+	/* 2. Do mutual capacitance fine calibrate. */
+	calibrate_error = cyapa_pip_calibrate_pwcs(cyapa,
+				PIP_SENSING_MODE_MUTUAL_CAP_FINE);
+	if (calibrate_error)
+		goto resume_scanning;
+
+	/* 3. Do self capacitance calibrate. */
+	calibrate_error = cyapa_pip_calibrate_pwcs(cyapa,
+				PIP_SENSING_MODE_SELF_CAP);
+	if (calibrate_error)
+		goto resume_scanning;
+
+resume_scanning:
+	/* 4. Resume Scanning*/
+	error = cyapa_pip_resume_scanning(cyapa);
+	if (error || calibrate_error)
+		return error ? error : calibrate_error;
+
+	return count;
+}
+
+static s32 twos_complement_to_s32(s32 value, int num_bits)
+{
+	if (value >> (num_bits - 1))
+		value |=  -1 << num_bits;
+	return value;
+}
+
+static s32 cyapa_parse_structure_data(u8 data_format, u8 *buf, int buf_len)
+{
+	int data_size;
+	bool big_endian;
+	bool unsigned_type;
+	s32 value;
+
+	data_size = (data_format & 0x07);
+	big_endian = ((data_format & 0x10) == 0x00);
+	unsigned_type = ((data_format & 0x20) == 0x00);
+
+	if (buf_len < data_size)
+		return 0;
+
+	switch (data_size) {
+	case 1:
+		value  = buf[0];
+		break;
+	case 2:
+		if (big_endian)
+			value = get_unaligned_be16(buf);
+		else
+			value = get_unaligned_le16(buf);
+		break;
+	case 4:
+		if (big_endian)
+			value = get_unaligned_be32(buf);
+		else
+			value = get_unaligned_le32(buf);
+		break;
+	default:
+		/* Should not happen, just as default case here. */
+		value = 0;
+		break;
+	}
+
+	if (!unsigned_type)
+		value = twos_complement_to_s32(value, data_size * 8);
+
+	return value;
+}
+
+static void cyapa_gen5_guess_electrodes(struct cyapa *cyapa,
+		int *electrodes_rx, int *electrodes_tx)
+{
+	if (cyapa->electrodes_rx != 0) {
+		*electrodes_rx = cyapa->electrodes_rx;
+		*electrodes_tx = (cyapa->electrodes_x == *electrodes_rx) ?
+				cyapa->electrodes_y : cyapa->electrodes_x;
+	} else {
+		*electrodes_tx = min(cyapa->electrodes_x, cyapa->electrodes_y);
+		*electrodes_rx = max(cyapa->electrodes_x, cyapa->electrodes_y);
+	}
+}
+
+/*
+ * Read all the global mutual or self idac data or mutual or self local PWC
+ * data based on the @idac_data_type.
+ * If the input value of @data_size is 0, then means read global mutual or
+ * self idac data. For read global mutual idac data, @idac_max, @idac_min and
+ * @idac_ave are in order used to return the max value of global mutual idac
+ * data, the min value of global mutual idac and the average value of the
+ * global mutual idac data. For read global self idac data, @idac_max is used
+ * to return the global self cap idac data in Rx direction, @idac_min is used
+ * to return the global self cap idac data in Tx direction. @idac_ave is not
+ * used.
+ * If the input value of @data_size is not 0, than means read the mutual or
+ * self local PWC data. The @idac_max, @idac_min and @idac_ave are used to
+ * return the max, min and average value of the mutual or self local PWC data.
+ * Note, in order to read mutual local PWC data, must read invoke this function
+ * to read the mutual global idac data firstly to set the correct Rx number
+ * value, otherwise, the read mutual idac and PWC data may not correct.
+ */
+static int cyapa_gen5_read_idac_data(struct cyapa *cyapa,
+		u8 cmd_code, u8 idac_data_type, int *data_size,
+		int *idac_max, int *idac_min, int *idac_ave)
+{
+	struct pip_app_cmd_head *cmd_head;
+	u8 cmd[12];
+	u8 resp_data[256];
+	int resp_len;
+	int read_len;
+	int value;
+	u16 offset;
+	int read_elements;
+	bool read_global_idac;
+	int sum, count, max_element_cnt;
+	int tmp_max, tmp_min, tmp_ave, tmp_sum, tmp_count;
+	int electrodes_rx, electrodes_tx;
+	int i;
+	int error;
+
+	if (cmd_code != PIP_RETRIEVE_DATA_STRUCTURE ||
+		(idac_data_type != GEN5_RETRIEVE_MUTUAL_PWC_DATA &&
+		idac_data_type != GEN5_RETRIEVE_SELF_CAP_PWC_DATA) ||
+		!data_size || !idac_max || !idac_min || !idac_ave)
+		return -EINVAL;
+
+	*idac_max = INT_MIN;
+	*idac_min = INT_MAX;
+	sum = count = tmp_count = 0;
+	electrodes_rx = electrodes_tx = 0;
+	if (*data_size == 0) {
+		/*
+		 * Read global idac values firstly.
+		 * Currently, no idac data exceed 4 bytes.
+		 */
+		read_global_idac = true;
+		offset = 0;
+		*data_size = 4;
+		tmp_max = INT_MIN;
+		tmp_min = INT_MAX;
+		tmp_ave = tmp_sum = tmp_count = 0;
+
+		if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) {
+			if (cyapa->aligned_electrodes_rx == 0) {
+				cyapa_gen5_guess_electrodes(cyapa,
+					&electrodes_rx, &electrodes_tx);
+				cyapa->aligned_electrodes_rx =
+					(electrodes_rx + 3) & ~3u;
+			}
+			max_element_cnt =
+				(cyapa->aligned_electrodes_rx + 7) & ~7u;
+		} else {
+			max_element_cnt = 2;
+		}
+	} else {
+		read_global_idac = false;
+		if (*data_size > 4)
+			*data_size = 4;
+		/* Calculate the start offset in bytes of local PWC data. */
+		if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) {
+			offset = cyapa->aligned_electrodes_rx * (*data_size);
+			if (cyapa->electrodes_rx == cyapa->electrodes_x)
+				electrodes_tx = cyapa->electrodes_y;
+			else
+				electrodes_tx = cyapa->electrodes_x;
+			max_element_cnt = ((cyapa->aligned_electrodes_rx + 7) &
+						~7u) * electrodes_tx;
+		} else {
+			offset = 2;
+			max_element_cnt = cyapa->electrodes_x +
+						cyapa->electrodes_y;
+			max_element_cnt = (max_element_cnt + 3) & ~3u;
+		}
+	}
+
+	memset(cmd, 0, sizeof(cmd));
+	cmd_head = (struct pip_app_cmd_head *)cmd;
+	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd_head->addr);
+	put_unaligned_le16(sizeof(cmd) - 2, &cmd_head->length);
+	cmd_head->report_id = PIP_APP_CMD_REPORT_ID;
+	cmd_head->cmd_code = cmd_code;
+	do {
+		read_elements = (256 - GEN5_RESP_DATA_STRUCTURE_OFFSET) /
+				(*data_size);
+		read_elements = min(read_elements, max_element_cnt - count);
+		read_len = read_elements * (*data_size);
+
+		put_unaligned_le16(offset, &cmd_head->parameter_data[0]);
+		put_unaligned_le16(read_len, &cmd_head->parameter_data[2]);
+		cmd_head->parameter_data[4] = idac_data_type;
+		resp_len = GEN5_RESP_DATA_STRUCTURE_OFFSET + read_len;
+		error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+				cmd, sizeof(cmd),
+				resp_data, &resp_len,
+				500, cyapa_sort_tsg_pip_app_resp_data,
+				true);
+		if (error || resp_len < GEN5_RESP_DATA_STRUCTURE_OFFSET ||
+				!VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
+				!PIP_CMD_COMPLETE_SUCCESS(resp_data) ||
+				resp_data[6] != idac_data_type)
+			return (error < 0) ? error : -EAGAIN;
+		read_len = get_unaligned_le16(&resp_data[7]);
+		if (read_len == 0)
+			break;
+
+		*data_size = (resp_data[9] & GEN5_PWC_DATA_ELEMENT_SIZE_MASK);
+		if (read_len < *data_size)
+			return -EINVAL;
+
+		if (read_global_idac &&
+			idac_data_type == GEN5_RETRIEVE_SELF_CAP_PWC_DATA) {
+			/* Rx's self global idac data. */
+			*idac_max = cyapa_parse_structure_data(
+				resp_data[9],
+				&resp_data[GEN5_RESP_DATA_STRUCTURE_OFFSET],
+				*data_size);
+			/* Tx's self global idac data. */
+			*idac_min = cyapa_parse_structure_data(
+				resp_data[9],
+				&resp_data[GEN5_RESP_DATA_STRUCTURE_OFFSET +
+					   *data_size],
+				*data_size);
+			break;
+		}
+
+		/* Read mutual global idac or local mutual/self PWC data. */
+		offset += read_len;
+		for (i = 10; i < (read_len + GEN5_RESP_DATA_STRUCTURE_OFFSET);
+				i += *data_size) {
+			value = cyapa_parse_structure_data(resp_data[9],
+					&resp_data[i], *data_size);
+			*idac_min = min(value, *idac_min);
+			*idac_max = max(value, *idac_max);
+
+			if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA &&
+				tmp_count < cyapa->aligned_electrodes_rx &&
+				read_global_idac) {
+				/*
+				 * The value gap between global and local mutual
+				 * idac data must bigger than 50%.
+				 * Normally, global value bigger than 50,
+				 * local values less than 10.
+				 */
+				if (!tmp_ave || value > tmp_ave / 2) {
+					tmp_min = min(value, tmp_min);
+					tmp_max = max(value, tmp_max);
+					tmp_sum += value;
+					tmp_count++;
+
+					tmp_ave = tmp_sum / tmp_count;
+				}
+			}
+
+			sum += value;
+			count++;
+
+			if (count >= max_element_cnt)
+				goto out;
+		}
+	} while (true);
+
+out:
+	*idac_ave = count ? (sum / count) : 0;
+
+	if (read_global_idac &&
+		idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) {
+		if (tmp_count == 0)
+			return 0;
+
+		if (tmp_count == cyapa->aligned_electrodes_rx) {
+			cyapa->electrodes_rx = cyapa->electrodes_rx ?
+				cyapa->electrodes_rx : electrodes_rx;
+		} else if (tmp_count == electrodes_rx) {
+			cyapa->electrodes_rx = cyapa->electrodes_rx ?
+				cyapa->electrodes_rx : electrodes_rx;
+			cyapa->aligned_electrodes_rx = electrodes_rx;
+		} else {
+			cyapa->electrodes_rx = cyapa->electrodes_rx ?
+				cyapa->electrodes_rx : electrodes_tx;
+			cyapa->aligned_electrodes_rx = tmp_count;
+		}
+
+		*idac_min = tmp_min;
+		*idac_max = tmp_max;
+		*idac_ave = tmp_ave;
+	}
+
+	return 0;
+}
+
+static int cyapa_gen5_read_mutual_idac_data(struct cyapa *cyapa,
+	int *gidac_mutual_max, int *gidac_mutual_min, int *gidac_mutual_ave,
+	int *lidac_mutual_max, int *lidac_mutual_min, int *lidac_mutual_ave)
+{
+	int data_size;
+	int error;
+
+	*gidac_mutual_max = *gidac_mutual_min = *gidac_mutual_ave = 0;
+	*lidac_mutual_max = *lidac_mutual_min = *lidac_mutual_ave = 0;
+
+	data_size = 0;
+	error = cyapa_gen5_read_idac_data(cyapa,
+		PIP_RETRIEVE_DATA_STRUCTURE,
+		GEN5_RETRIEVE_MUTUAL_PWC_DATA,
+		&data_size,
+		gidac_mutual_max, gidac_mutual_min, gidac_mutual_ave);
+	if (error)
+		return error;
+
+	error = cyapa_gen5_read_idac_data(cyapa,
+		PIP_RETRIEVE_DATA_STRUCTURE,
+		GEN5_RETRIEVE_MUTUAL_PWC_DATA,
+		&data_size,
+		lidac_mutual_max, lidac_mutual_min, lidac_mutual_ave);
+	return error;
+}
+
+static int cyapa_gen5_read_self_idac_data(struct cyapa *cyapa,
+		int *gidac_self_rx, int *gidac_self_tx,
+		int *lidac_self_max, int *lidac_self_min, int *lidac_self_ave)
+{
+	int data_size;
+	int error;
+
+	*gidac_self_rx = *gidac_self_tx = 0;
+	*lidac_self_max = *lidac_self_min = *lidac_self_ave = 0;
+
+	data_size = 0;
+	error = cyapa_gen5_read_idac_data(cyapa,
+		PIP_RETRIEVE_DATA_STRUCTURE,
+		GEN5_RETRIEVE_SELF_CAP_PWC_DATA,
+		&data_size,
+		lidac_self_max, lidac_self_min, lidac_self_ave);
+	if (error)
+		return error;
+	*gidac_self_rx = *lidac_self_max;
+	*gidac_self_tx = *lidac_self_min;
+
+	error = cyapa_gen5_read_idac_data(cyapa,
+		PIP_RETRIEVE_DATA_STRUCTURE,
+		GEN5_RETRIEVE_SELF_CAP_PWC_DATA,
+		&data_size,
+		lidac_self_max, lidac_self_min, lidac_self_ave);
+	return error;
+}
+
+static ssize_t cyapa_gen5_execute_panel_scan(struct cyapa *cyapa)
+{
+	struct pip_app_cmd_head *app_cmd_head;
+	u8 cmd[7];
+	u8 resp_data[6];
+	int resp_len;
+	int error;
+
+	memset(cmd, 0, sizeof(cmd));
+	app_cmd_head = (struct pip_app_cmd_head *)cmd;
+	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &app_cmd_head->addr);
+	put_unaligned_le16(sizeof(cmd) - 2, &app_cmd_head->length);
+	app_cmd_head->report_id = PIP_APP_CMD_REPORT_ID;
+	app_cmd_head->cmd_code = GEN5_CMD_EXECUTE_PANEL_SCAN;
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_app_resp_data, true);
+	if (error || resp_len != sizeof(resp_data) ||
+			!VALID_CMD_RESP_HEADER(resp_data,
+				GEN5_CMD_EXECUTE_PANEL_SCAN) ||
+			!PIP_CMD_COMPLETE_SUCCESS(resp_data))
+		return error ? error : -EAGAIN;
+
+	return 0;
+}
+
+static int cyapa_gen5_read_panel_scan_raw_data(struct cyapa *cyapa,
+		u8 cmd_code, u8 raw_data_type, int raw_data_max_num,
+		int *raw_data_max, int *raw_data_min, int *raw_data_ave,
+		u8 *buffer)
+{
+	struct pip_app_cmd_head *app_cmd_head;
+	struct gen5_retrieve_panel_scan_data *panel_sacn_data;
+	u8 cmd[12];
+	u8 resp_data[256];  /* Max bytes can transfer one time. */
+	int resp_len;
+	int read_elements;
+	int read_len;
+	u16 offset;
+	s32 value;
+	int sum, count;
+	int data_size;
+	s32 *intp;
+	int i;
+	int error;
+
+	if (cmd_code != GEN5_CMD_RETRIEVE_PANEL_SCAN ||
+		(raw_data_type > GEN5_PANEL_SCAN_SELF_DIFFCOUNT) ||
+		!raw_data_max || !raw_data_min || !raw_data_ave)
+		return -EINVAL;
+
+	intp = (s32 *)buffer;
+	*raw_data_max = INT_MIN;
+	*raw_data_min = INT_MAX;
+	sum = count = 0;
+	offset = 0;
+	/* Assume max element size is 4 currently. */
+	read_elements = (256 - GEN5_RESP_DATA_STRUCTURE_OFFSET) / 4;
+	read_len = read_elements * 4;
+	app_cmd_head = (struct pip_app_cmd_head *)cmd;
+	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &app_cmd_head->addr);
+	put_unaligned_le16(sizeof(cmd) - 2, &app_cmd_head->length);
+	app_cmd_head->report_id = PIP_APP_CMD_REPORT_ID;
+	app_cmd_head->cmd_code = cmd_code;
+	panel_sacn_data = (struct gen5_retrieve_panel_scan_data *)
+			app_cmd_head->parameter_data;
+	do {
+		put_unaligned_le16(offset, &panel_sacn_data->read_offset);
+		put_unaligned_le16(read_elements,
+			&panel_sacn_data->read_elements);
+		panel_sacn_data->data_id = raw_data_type;
+
+		resp_len = GEN5_RESP_DATA_STRUCTURE_OFFSET + read_len;
+		error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_app_resp_data, true);
+		if (error || resp_len < GEN5_RESP_DATA_STRUCTURE_OFFSET ||
+				!VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
+				!PIP_CMD_COMPLETE_SUCCESS(resp_data) ||
+				resp_data[6] != raw_data_type)
+			return error ? error : -EAGAIN;
+
+		read_elements = get_unaligned_le16(&resp_data[7]);
+		if (read_elements == 0)
+			break;
+
+		data_size = (resp_data[9] & GEN5_PWC_DATA_ELEMENT_SIZE_MASK);
+		offset += read_elements;
+		if (read_elements) {
+			for (i = GEN5_RESP_DATA_STRUCTURE_OFFSET;
+			     i < (read_elements * data_size +
+					GEN5_RESP_DATA_STRUCTURE_OFFSET);
+			     i += data_size) {
+				value = cyapa_parse_structure_data(resp_data[9],
+						&resp_data[i], data_size);
+				*raw_data_min = min(value, *raw_data_min);
+				*raw_data_max = max(value, *raw_data_max);
+
+				if (intp)
+					put_unaligned_le32(value, &intp[count]);
+
+				sum += value;
+				count++;
+
+			}
+		}
+
+		if (count >= raw_data_max_num)
+			break;
+
+		read_elements = (sizeof(resp_data) -
+				GEN5_RESP_DATA_STRUCTURE_OFFSET) / data_size;
+		read_len = read_elements * data_size;
+	} while (true);
+
+	*raw_data_ave = count ? (sum / count) : 0;
+
+	return 0;
+}
+
+static ssize_t cyapa_gen5_show_baseline(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	int gidac_mutual_max, gidac_mutual_min, gidac_mutual_ave;
+	int lidac_mutual_max, lidac_mutual_min, lidac_mutual_ave;
+	int gidac_self_rx, gidac_self_tx;
+	int lidac_self_max, lidac_self_min, lidac_self_ave;
+	int raw_cap_mutual_max, raw_cap_mutual_min, raw_cap_mutual_ave;
+	int raw_cap_self_max, raw_cap_self_min, raw_cap_self_ave;
+	int mutual_diffdata_max, mutual_diffdata_min, mutual_diffdata_ave;
+	int self_diffdata_max, self_diffdata_min, self_diffdata_ave;
+	int mutual_baseline_max, mutual_baseline_min, mutual_baseline_ave;
+	int self_baseline_max, self_baseline_min, self_baseline_ave;
+	int error, resume_error;
+	int size;
+
+	if (!cyapa_is_pip_app_mode(cyapa))
+		return -EBUSY;
+
+	/* 1. Suspend Scanning*/
+	error = cyapa_pip_suspend_scanning(cyapa);
+	if (error)
+		return error;
+
+	/* 2.  Read global and local mutual IDAC data. */
+	gidac_self_rx = gidac_self_tx = 0;
+	error = cyapa_gen5_read_mutual_idac_data(cyapa,
+				&gidac_mutual_max, &gidac_mutual_min,
+				&gidac_mutual_ave, &lidac_mutual_max,
+				&lidac_mutual_min, &lidac_mutual_ave);
+	if (error)
+		goto resume_scanning;
+
+	/* 3.  Read global and local self IDAC data. */
+	error = cyapa_gen5_read_self_idac_data(cyapa,
+				&gidac_self_rx, &gidac_self_tx,
+				&lidac_self_max, &lidac_self_min,
+				&lidac_self_ave);
+	if (error)
+		goto resume_scanning;
+
+	/* 4. Execute panel scan. It must be executed before read data. */
+	error = cyapa_gen5_execute_panel_scan(cyapa);
+	if (error)
+		goto resume_scanning;
+
+	/* 5. Retrieve panel scan, mutual cap raw data. */
+	error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_MUTUAL_RAW_DATA,
+				cyapa->electrodes_x * cyapa->electrodes_y,
+				&raw_cap_mutual_max, &raw_cap_mutual_min,
+				&raw_cap_mutual_ave,
+				NULL);
+	if (error)
+		goto resume_scanning;
+
+	/* 6. Retrieve panel scan, self cap raw data. */
+	error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_SELF_RAW_DATA,
+				cyapa->electrodes_x + cyapa->electrodes_y,
+				&raw_cap_self_max, &raw_cap_self_min,
+				&raw_cap_self_ave,
+				NULL);
+	if (error)
+		goto resume_scanning;
+
+	/* 7. Retrieve panel scan, mutual cap diffcount raw data. */
+	error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_MUTUAL_DIFFCOUNT,
+				cyapa->electrodes_x * cyapa->electrodes_y,
+				&mutual_diffdata_max, &mutual_diffdata_min,
+				&mutual_diffdata_ave,
+				NULL);
+	if (error)
+		goto resume_scanning;
+
+	/* 8. Retrieve panel scan, self cap diffcount raw data. */
+	error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_SELF_DIFFCOUNT,
+				cyapa->electrodes_x + cyapa->electrodes_y,
+				&self_diffdata_max, &self_diffdata_min,
+				&self_diffdata_ave,
+				NULL);
+	if (error)
+		goto resume_scanning;
+
+	/* 9. Retrieve panel scan, mutual cap baseline raw data. */
+	error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_MUTUAL_BASELINE,
+				cyapa->electrodes_x * cyapa->electrodes_y,
+				&mutual_baseline_max, &mutual_baseline_min,
+				&mutual_baseline_ave,
+				NULL);
+	if (error)
+		goto resume_scanning;
+
+	/* 10. Retrieve panel scan, self cap baseline raw data. */
+	error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_SELF_BASELINE,
+				cyapa->electrodes_x + cyapa->electrodes_y,
+				&self_baseline_max, &self_baseline_min,
+				&self_baseline_ave,
+				NULL);
+	if (error)
+		goto resume_scanning;
+
+resume_scanning:
+	/* 11. Resume Scanning*/
+	resume_error = cyapa_pip_resume_scanning(cyapa);
+	if (resume_error || error)
+		return resume_error ? resume_error : error;
+
+	/* 12. Output data strings */
+	size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d %d %d %d %d %d ",
+		gidac_mutual_min, gidac_mutual_max, gidac_mutual_ave,
+		lidac_mutual_min, lidac_mutual_max, lidac_mutual_ave,
+		gidac_self_rx, gidac_self_tx,
+		lidac_self_min, lidac_self_max, lidac_self_ave);
+	size += scnprintf(buf + size, PAGE_SIZE - size,
+		"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+		raw_cap_mutual_min, raw_cap_mutual_max, raw_cap_mutual_ave,
+		raw_cap_self_min, raw_cap_self_max, raw_cap_self_ave,
+		mutual_diffdata_min, mutual_diffdata_max, mutual_diffdata_ave,
+		self_diffdata_min, self_diffdata_max, self_diffdata_ave,
+		mutual_baseline_min, mutual_baseline_max, mutual_baseline_ave,
+		self_baseline_min, self_baseline_max, self_baseline_ave);
+	return size;
+}
+
+bool cyapa_pip_sort_system_info_data(struct cyapa *cyapa,
+		u8 *buf, int len)
+{
+	/* Check the report id and command code */
+	if (VALID_CMD_RESP_HEADER(buf, 0x02))
+		return true;
+
+	return false;
+}
+
+static int cyapa_gen5_bl_query_data(struct cyapa *cyapa)
+{
+	u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH];
+	int resp_len;
+	int error;
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH,
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_bl_resp_data, false);
+	if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH ||
+		!PIP_CMD_COMPLETE_SUCCESS(resp_data))
+		return error ? error : -EIO;
+
+	memcpy(&cyapa->product_id[0], &resp_data[8], 5);
+	cyapa->product_id[5] = '-';
+	memcpy(&cyapa->product_id[6], &resp_data[13], 6);
+	cyapa->product_id[12] = '-';
+	memcpy(&cyapa->product_id[13], &resp_data[19], 2);
+	cyapa->product_id[15] = '\0';
+
+	cyapa->fw_maj_ver = resp_data[22];
+	cyapa->fw_min_ver = resp_data[23];
+
+	cyapa->platform_ver = (resp_data[26] >> PIP_BL_PLATFORM_VER_SHIFT) &
+			      PIP_BL_PLATFORM_VER_MASK;
+
+	return 0;
+}
+
+static int cyapa_gen5_get_query_data(struct cyapa *cyapa)
+{
+	u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
+	int resp_len;
+	u16 product_family;
+	int error;
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
+			resp_data, &resp_len,
+			2000, cyapa_pip_sort_system_info_data, false);
+	if (error || resp_len < sizeof(resp_data))
+		return error ? error : -EIO;
+
+	product_family = get_unaligned_le16(&resp_data[7]);
+	if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
+		PIP_PRODUCT_FAMILY_TRACKPAD)
+		return -EINVAL;
+
+	cyapa->platform_ver = (resp_data[49] >> PIP_BL_PLATFORM_VER_SHIFT) &
+			      PIP_BL_PLATFORM_VER_MASK;
+	if (cyapa->gen == CYAPA_GEN5 && cyapa->platform_ver < 2) {
+		/* Gen5 firmware that does not support proximity. */
+		cyapa->fw_maj_ver = resp_data[15];
+		cyapa->fw_min_ver = resp_data[16];
+	} else {
+		cyapa->fw_maj_ver = resp_data[9];
+		cyapa->fw_min_ver = resp_data[10];
+	}
+
+	cyapa->electrodes_x = resp_data[52];
+	cyapa->electrodes_y = resp_data[53];
+
+	cyapa->physical_size_x =  get_unaligned_le16(&resp_data[54]) / 100;
+	cyapa->physical_size_y = get_unaligned_le16(&resp_data[56]) / 100;
+
+	cyapa->max_abs_x = get_unaligned_le16(&resp_data[58]);
+	cyapa->max_abs_y = get_unaligned_le16(&resp_data[60]);
+
+	cyapa->max_z = get_unaligned_le16(&resp_data[62]);
+
+	cyapa->x_origin = resp_data[64] & 0x01;
+	cyapa->y_origin = resp_data[65] & 0x01;
+
+	cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK;
+
+	memcpy(&cyapa->product_id[0], &resp_data[33], 5);
+	cyapa->product_id[5] = '-';
+	memcpy(&cyapa->product_id[6], &resp_data[38], 6);
+	cyapa->product_id[12] = '-';
+	memcpy(&cyapa->product_id[13], &resp_data[44], 2);
+	cyapa->product_id[15] = '\0';
+
+	if (!cyapa->electrodes_x || !cyapa->electrodes_y ||
+		!cyapa->physical_size_x || !cyapa->physical_size_y ||
+		!cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int cyapa_gen5_do_operational_check(struct cyapa *cyapa)
+{
+	struct device *dev = &cyapa->client->dev;
+	int error;
+
+	if (cyapa->gen != CYAPA_GEN5)
+		return -ENODEV;
+
+	switch (cyapa->state) {
+	case CYAPA_STATE_GEN5_BL:
+		error = cyapa_pip_bl_exit(cyapa);
+		if (error) {
+			/* Try to update trackpad product information. */
+			cyapa_gen5_bl_query_data(cyapa);
+			goto out;
+		}
+
+		cyapa->state = CYAPA_STATE_GEN5_APP;
+
+	case CYAPA_STATE_GEN5_APP:
+		/*
+		 * If trackpad device in deep sleep mode,
+		 * the app command will fail.
+		 * So always try to reset trackpad device to full active when
+		 * the device state is required.
+		 */
+		error = cyapa_gen5_set_power_mode(cyapa,
+				PWR_MODE_FULL_ACTIVE, 0, false);
+		if (error)
+			dev_warn(dev, "%s: failed to set power active mode.\n",
+				__func__);
+
+		/* By default, the trackpad proximity function is enabled. */
+		if (cyapa->platform_ver >= 2) {
+			error = cyapa_pip_set_proximity(cyapa, true);
+			if (error)
+				dev_warn(dev,
+					"%s: failed to enable proximity.\n",
+					__func__);
+		}
+
+		/* Get trackpad product information. */
+		error = cyapa_gen5_get_query_data(cyapa);
+		if (error)
+			goto out;
+		/* Only support product ID starting with CYTRA */
+		if (memcmp(cyapa->product_id, product_id,
+				strlen(product_id)) != 0) {
+			dev_err(dev, "%s: unknown product ID (%s)\n",
+				__func__, cyapa->product_id);
+			error = -EINVAL;
+		}
+		break;
+	default:
+		error = -EINVAL;
+	}
+
+out:
+	return error;
+}
+
+/*
+ * Return false, do not continue process
+ * Return true, continue process.
+ */
+bool cyapa_pip_irq_cmd_handler(struct cyapa *cyapa)
+{
+	struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip;
+	int length;
+
+	if (atomic_read(&pip->cmd_issued)) {
+		/* Polling command response data. */
+		if (pip->is_irq_mode == false)
+			return false;
+
+		/*
+		 * Read out all none command response data.
+		 * these output data may caused by user put finger on
+		 * trackpad when host waiting the command response.
+		 */
+		cyapa_i2c_pip_read(cyapa, pip->irq_cmd_buf,
+			PIP_RESP_LENGTH_SIZE);
+		length = get_unaligned_le16(pip->irq_cmd_buf);
+		length = (length <= PIP_RESP_LENGTH_SIZE) ?
+				PIP_RESP_LENGTH_SIZE : length;
+		if (length > PIP_RESP_LENGTH_SIZE)
+			cyapa_i2c_pip_read(cyapa,
+				pip->irq_cmd_buf, length);
+		if (!(pip->resp_sort_func &&
+			pip->resp_sort_func(cyapa,
+				pip->irq_cmd_buf, length))) {
+			/*
+			 * Cover the Gen5 V1 firmware issue.
+			 * The issue is no interrupt would be asserted from
+			 * trackpad device to host for the command response
+			 * ready event. Because when there was a finger touch
+			 * on trackpad device, and the firmware output queue
+			 * won't be empty (always with touch report data), so
+			 * the interrupt signal won't be asserted again until
+			 * the output queue was previous emptied.
+			 * This issue would happen in the scenario that
+			 * user always has his/her fingers touched on the
+			 * trackpad device during system booting/rebooting.
+			 */
+			length = 0;
+			if (pip->resp_len)
+				length = *pip->resp_len;
+			cyapa_empty_pip_output_data(cyapa,
+					pip->resp_data,
+					&length,
+					pip->resp_sort_func);
+			if (pip->resp_len && length != 0) {
+				*pip->resp_len = length;
+				atomic_dec(&pip->cmd_issued);
+				complete(&pip->cmd_ready);
+			}
+			return false;
+		}
+
+		if (pip->resp_data && pip->resp_len) {
+			*pip->resp_len = (*pip->resp_len < length) ?
+				*pip->resp_len : length;
+			memcpy(pip->resp_data, pip->irq_cmd_buf,
+				*pip->resp_len);
+		}
+		atomic_dec(&pip->cmd_issued);
+		complete(&pip->cmd_ready);
+		return false;
+	}
+
+	return true;
+}
+
+static void cyapa_pip_report_buttons(struct cyapa *cyapa,
+		const struct cyapa_pip_report_data *report_data)
+{
+	struct input_dev *input = cyapa->input;
+	u8 buttons = report_data->report_head[PIP_BUTTONS_OFFSET];
+
+	buttons = (buttons << CAPABILITY_BTN_SHIFT) & CAPABILITY_BTN_MASK;
+
+	if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) {
+		input_report_key(input, BTN_LEFT,
+			!!(buttons & CAPABILITY_LEFT_BTN_MASK));
+	}
+	if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) {
+		input_report_key(input, BTN_MIDDLE,
+			!!(buttons & CAPABILITY_MIDDLE_BTN_MASK));
+	}
+	if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) {
+		input_report_key(input, BTN_RIGHT,
+			!!(buttons & CAPABILITY_RIGHT_BTN_MASK));
+	}
+
+	input_sync(input);
+}
+
+static void cyapa_pip_report_proximity(struct cyapa *cyapa,
+		const struct cyapa_pip_report_data *report_data)
+{
+	struct input_dev *input = cyapa->input;
+	u8 distance = report_data->report_head[PIP_PROXIMITY_DISTANCE_OFFSET] &
+			PIP_PROXIMITY_DISTANCE_MASK;
+
+	input_report_abs(input, ABS_DISTANCE, distance);
+	input_sync(input);
+}
+
+static void cyapa_pip_report_slot_data(struct cyapa *cyapa,
+		const struct cyapa_pip_touch_record *touch)
+{
+	struct input_dev *input = cyapa->input;
+	u8 event_id = PIP_GET_EVENT_ID(touch->touch_tip_event_id);
+	int slot = PIP_GET_TOUCH_ID(touch->touch_tip_event_id);
+	int x, y;
+
+	if (event_id == RECORD_EVENT_LIFTOFF)
+		return;
+
+	input_mt_slot(input, slot);
+	input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+	x = (touch->x_hi << 8) | touch->x_lo;
+	if (cyapa->x_origin)
+		x = cyapa->max_abs_x - x;
+	y = (touch->y_hi << 8) | touch->y_lo;
+	if (cyapa->y_origin)
+		y = cyapa->max_abs_y - y;
+	input_report_abs(input, ABS_MT_POSITION_X, x);
+	input_report_abs(input, ABS_MT_POSITION_Y, y);
+	input_report_abs(input, ABS_DISTANCE, 0);
+	input_report_abs(input, ABS_MT_PRESSURE,
+		touch->z);
+	input_report_abs(input, ABS_MT_TOUCH_MAJOR,
+		touch->major_axis_len);
+	input_report_abs(input, ABS_MT_TOUCH_MINOR,
+		touch->minor_axis_len);
+
+	input_report_abs(input, ABS_MT_WIDTH_MAJOR,
+		touch->major_tool_len);
+	input_report_abs(input, ABS_MT_WIDTH_MINOR,
+		touch->minor_tool_len);
+
+	input_report_abs(input, ABS_MT_ORIENTATION,
+		touch->orientation);
+}
+
+static void cyapa_pip_report_touches(struct cyapa *cyapa,
+		const struct cyapa_pip_report_data *report_data)
+{
+	struct input_dev *input = cyapa->input;
+	unsigned int touch_num;
+	int i;
+
+	touch_num = report_data->report_head[PIP_NUMBER_OF_TOUCH_OFFSET] &
+			PIP_NUMBER_OF_TOUCH_MASK;
+
+	for (i = 0; i < touch_num; i++)
+		cyapa_pip_report_slot_data(cyapa,
+			&report_data->touch_records[i]);
+
+	input_mt_sync_frame(input);
+	input_sync(input);
+}
+
+int cyapa_pip_irq_handler(struct cyapa *cyapa)
+{
+	struct device *dev = &cyapa->client->dev;
+	struct cyapa_pip_report_data report_data;
+	unsigned int report_len;
+	u8 report_id;
+	int ret;
+
+	if (!cyapa_is_pip_app_mode(cyapa)) {
+		dev_err(dev, "invalid device state, gen=%d, state=0x%02x\n",
+			cyapa->gen, cyapa->state);
+		return -EINVAL;
+	}
+
+	ret = cyapa_i2c_pip_read(cyapa, (u8 *)&report_data,
+			PIP_RESP_LENGTH_SIZE);
+	if (ret != PIP_RESP_LENGTH_SIZE) {
+		dev_err(dev, "failed to read length bytes, (%d)\n", ret);
+		return -EINVAL;
+	}
+
+	report_len = get_unaligned_le16(
+			&report_data.report_head[PIP_RESP_LENGTH_OFFSET]);
+	if (report_len < PIP_RESP_LENGTH_SIZE) {
+		/* Invalid length or internal reset happened. */
+		dev_err(dev, "invalid report_len=%d. bytes: %02x %02x\n",
+			report_len, report_data.report_head[0],
+			report_data.report_head[1]);
+		return -EINVAL;
+	}
+
+	/* Idle, no data for report. */
+	if (report_len == PIP_RESP_LENGTH_SIZE)
+		return 0;
+
+	ret = cyapa_i2c_pip_read(cyapa, (u8 *)&report_data, report_len);
+	if (ret != report_len) {
+		dev_err(dev, "failed to read %d bytes report data, (%d)\n",
+			report_len, ret);
+		return -EINVAL;
+	}
+
+	report_id = report_data.report_head[PIP_RESP_REPORT_ID_OFFSET];
+	if (report_id == PIP_WAKEUP_EVENT_REPORT_ID &&
+			report_len == PIP_WAKEUP_EVENT_SIZE) {
+		/*
+		 * Device wake event from deep sleep mode for touch.
+		 * This interrupt event is used to wake system up.
+		 *
+		 * Note:
+		 * It will introduce about 20~40 ms additional delay
+		 * time in receiving for first valid touch report data.
+		 * The time is used to execute device runtime resume
+		 * process.
+		 */
+		pm_runtime_get_sync(dev);
+		pm_runtime_mark_last_busy(dev);
+		pm_runtime_put_sync_autosuspend(dev);
+		return 0;
+	} else if (report_id != PIP_TOUCH_REPORT_ID &&
+			report_id != PIP_BTN_REPORT_ID &&
+			report_id != GEN5_OLD_PUSH_BTN_REPORT_ID &&
+			report_id != PIP_PUSH_BTN_REPORT_ID &&
+			report_id != PIP_PROXIMITY_REPORT_ID) {
+		/* Running in BL mode or unknown response data read. */
+		dev_err(dev, "invalid report_id=0x%02x\n", report_id);
+		return -EINVAL;
+	}
+
+	if (report_id == PIP_TOUCH_REPORT_ID &&
+		(report_len < PIP_TOUCH_REPORT_HEAD_SIZE ||
+			report_len > PIP_TOUCH_REPORT_MAX_SIZE)) {
+		/* Invalid report data length for finger packet. */
+		dev_err(dev, "invalid touch packet length=%d\n", report_len);
+		return 0;
+	}
+
+	if ((report_id == PIP_BTN_REPORT_ID ||
+			report_id == GEN5_OLD_PUSH_BTN_REPORT_ID ||
+			report_id == PIP_PUSH_BTN_REPORT_ID) &&
+		(report_len < PIP_BTN_REPORT_HEAD_SIZE ||
+			report_len > PIP_BTN_REPORT_MAX_SIZE)) {
+		/* Invalid report data length of button packet. */
+		dev_err(dev, "invalid button packet length=%d\n", report_len);
+		return 0;
+	}
+
+	if (report_id == PIP_PROXIMITY_REPORT_ID &&
+			report_len != PIP_PROXIMITY_REPORT_SIZE) {
+		/* Invalid report data length of proximity packet. */
+		dev_err(dev, "invalid proximity data, length=%d\n", report_len);
+		return 0;
+	}
+
+	if (report_id == PIP_TOUCH_REPORT_ID)
+		cyapa_pip_report_touches(cyapa, &report_data);
+	else if (report_id == PIP_PROXIMITY_REPORT_ID)
+		cyapa_pip_report_proximity(cyapa, &report_data);
+	else
+		cyapa_pip_report_buttons(cyapa, &report_data);
+
+	return 0;
+}
+
+int cyapa_pip_bl_activate(struct cyapa *cyapa) { return 0; }
+int cyapa_pip_bl_deactivate(struct cyapa *cyapa) { return 0; }
+
+
+const struct cyapa_dev_ops cyapa_gen5_ops = {
+	.check_fw = cyapa_pip_check_fw,
+	.bl_enter = cyapa_pip_bl_enter,
+	.bl_initiate = cyapa_pip_bl_initiate,
+	.update_fw = cyapa_pip_do_fw_update,
+	.bl_activate = cyapa_pip_bl_activate,
+	.bl_deactivate = cyapa_pip_bl_deactivate,
+
+	.show_baseline = cyapa_gen5_show_baseline,
+	.calibrate_store = cyapa_pip_do_calibrate,
+
+	.initialize = cyapa_pip_cmd_state_initialize,
+
+	.state_parse = cyapa_gen5_state_parse,
+	.operational_check = cyapa_gen5_do_operational_check,
+
+	.irq_handler = cyapa_pip_irq_handler,
+	.irq_cmd_handler = cyapa_pip_irq_cmd_handler,
+	.sort_empty_output_data = cyapa_empty_pip_output_data,
+	.set_power_mode = cyapa_gen5_set_power_mode,
+
+	.set_proximity = cyapa_pip_set_proximity,
+};
diff --git a/drivers/input/mouse/cyapa_gen6.c b/drivers/input/mouse/cyapa_gen6.c
new file mode 100644
index 0000000..e4eb048
--- /dev/null
+++ b/drivers/input/mouse/cyapa_gen6.c
@@ -0,0 +1,745 @@
+/*
+ * Cypress APA trackpad with I2C interface
+ *
+ * Author: Dudley Du <dudl@cypress.com>
+ *
+ * Copyright (C) 2015 Cypress Semiconductor, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <linux/crc-itu-t.h>
+#include "cyapa.h"
+
+
+#define GEN6_ENABLE_CMD_IRQ	0x41
+#define GEN6_DISABLE_CMD_IRQ	0x42
+#define GEN6_ENABLE_DEV_IRQ	0x43
+#define GEN6_DISABLE_DEV_IRQ	0x44
+
+#define GEN6_POWER_MODE_ACTIVE		0x01
+#define GEN6_POWER_MODE_LP_MODE1	0x02
+#define GEN6_POWER_MODE_LP_MODE2	0x03
+#define GEN6_POWER_MODE_BTN_ONLY	0x04
+
+#define GEN6_SET_POWER_MODE_INTERVAL	0x47
+#define GEN6_GET_POWER_MODE_INTERVAL	0x48
+
+#define GEN6_MAX_RX_NUM 14
+#define GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC	0x00
+#define GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM		0x12
+
+
+struct pip_app_cmd_head {
+	__le16 addr;
+	__le16 length;
+	u8 report_id;
+	u8 resv;  /* Reserved, must be 0 */
+	u8 cmd_code;  /* bit7: resv, set to 0; bit6~0: command code.*/
+} __packed;
+
+struct pip_app_resp_head {
+	__le16 length;
+	u8 report_id;
+	u8 resv;  /* Reserved, must be 0 */
+	u8 cmd_code;  /* bit7: TGL; bit6~0: command code.*/
+	/*
+	 * The value of data_status can be the first byte of data or
+	 * the command status or the unsupported command code depending on the
+	 * requested command code.
+	*/
+	u8 data_status;
+} __packed;
+
+struct pip_fixed_info {
+	u8 silicon_id_high;
+	u8 silicon_id_low;
+	u8 family_id;
+};
+
+static u8 pip_get_bl_info[] = {
+	0x04, 0x00, 0x0B, 0x00, 0x40, 0x00, 0x01, 0x38,
+	0x00, 0x00, 0x70, 0x9E, 0x17
+};
+
+static bool cyapa_sort_pip_hid_descriptor_data(struct cyapa *cyapa,
+		u8 *buf, int len)
+{
+	if (len != PIP_HID_DESCRIPTOR_SIZE)
+		return false;
+
+	if (buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID ||
+		buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
+		return true;
+
+	return false;
+}
+
+static int cyapa_get_pip_fixed_info(struct cyapa *cyapa,
+		struct pip_fixed_info *pip_info, bool is_bootloader)
+{
+	u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
+	int resp_len;
+	u16 product_family;
+	int error;
+
+	if (is_bootloader) {
+		/* Read Bootloader Information to determine Gen5 or Gen6. */
+		resp_len = sizeof(resp_data);
+		error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+				pip_get_bl_info, sizeof(pip_get_bl_info),
+				resp_data, &resp_len,
+				2000, cyapa_sort_tsg_pip_bl_resp_data,
+				false);
+		if (error || resp_len < PIP_BL_GET_INFO_RESP_LENGTH)
+			return error ? error : -EIO;
+
+		pip_info->family_id = resp_data[8];
+		pip_info->silicon_id_low = resp_data[10];
+		pip_info->silicon_id_high = resp_data[11];
+
+		return 0;
+	}
+
+	/* Get App System Information to determine Gen5 or Gen6. */
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
+			resp_data, &resp_len,
+			2000, cyapa_pip_sort_system_info_data, false);
+	if (error || resp_len < PIP_READ_SYS_INFO_RESP_LENGTH)
+		return error ? error : -EIO;
+
+	product_family = get_unaligned_le16(&resp_data[7]);
+	if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
+		PIP_PRODUCT_FAMILY_TRACKPAD)
+		return -EINVAL;
+
+	pip_info->family_id = resp_data[19];
+	pip_info->silicon_id_low = resp_data[21];
+	pip_info->silicon_id_high = resp_data[22];
+
+	return 0;
+
+}
+
+int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
+{
+	u8 cmd[] = { 0x01, 0x00};
+	struct pip_fixed_info pip_info;
+	u8 resp_data[PIP_HID_DESCRIPTOR_SIZE];
+	int resp_len;
+	bool is_bootloader;
+	int error;
+
+	cyapa->state = CYAPA_STATE_NO_DEVICE;
+
+	/* Try to wake from it deep sleep state if it is. */
+	cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
+
+	/* Empty the buffer queue to get fresh data with later commands. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	/*
+	 * Read description info from trackpad device to determine running in
+	 * APP mode or Bootloader mode.
+	 */
+	resp_len = PIP_HID_DESCRIPTOR_SIZE;
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			300,
+			cyapa_sort_pip_hid_descriptor_data,
+			false);
+	if (error)
+		return error;
+
+	if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
+		is_bootloader = true;
+	else if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID)
+		is_bootloader = false;
+	else
+		return -EAGAIN;
+
+	/* Get PIP fixed information to determine Gen5 or Gen6. */
+	memset(&pip_info, 0, sizeof(struct pip_fixed_info));
+	error = cyapa_get_pip_fixed_info(cyapa, &pip_info, is_bootloader);
+	if (error)
+		return error;
+
+	if (pip_info.family_id == 0x9B && pip_info.silicon_id_high == 0x0B) {
+		cyapa->gen = CYAPA_GEN6;
+		cyapa->state = is_bootloader ? CYAPA_STATE_GEN6_BL
+					     : CYAPA_STATE_GEN6_APP;
+	} else if (pip_info.family_id == 0x91 &&
+		   pip_info.silicon_id_high == 0x02) {
+		cyapa->gen = CYAPA_GEN5;
+		cyapa->state = is_bootloader ? CYAPA_STATE_GEN5_BL
+					     : CYAPA_STATE_GEN5_APP;
+	}
+
+	return 0;
+}
+
+static int cyapa_gen6_read_sys_info(struct cyapa *cyapa)
+{
+	u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
+	int resp_len;
+	u16 product_family;
+	u8 rotat_align;
+	int error;
+
+	/* Get App System Information to determine Gen5 or Gen6. */
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
+			resp_data, &resp_len,
+			2000, cyapa_pip_sort_system_info_data, false);
+	if (error || resp_len < sizeof(resp_data))
+		return error ? error : -EIO;
+
+	product_family = get_unaligned_le16(&resp_data[7]);
+	if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
+		PIP_PRODUCT_FAMILY_TRACKPAD)
+		return -EINVAL;
+
+	cyapa->platform_ver = (resp_data[67] >> PIP_BL_PLATFORM_VER_SHIFT) &
+			      PIP_BL_PLATFORM_VER_MASK;
+	cyapa->fw_maj_ver = resp_data[9];
+	cyapa->fw_min_ver = resp_data[10];
+
+	cyapa->electrodes_x = resp_data[33];
+	cyapa->electrodes_y = resp_data[34];
+
+	cyapa->physical_size_x =  get_unaligned_le16(&resp_data[35]) / 100;
+	cyapa->physical_size_y = get_unaligned_le16(&resp_data[37]) / 100;
+
+	cyapa->max_abs_x = get_unaligned_le16(&resp_data[39]);
+	cyapa->max_abs_y = get_unaligned_le16(&resp_data[41]);
+
+	cyapa->max_z = get_unaligned_le16(&resp_data[43]);
+
+	cyapa->x_origin = resp_data[45] & 0x01;
+	cyapa->y_origin = resp_data[46] & 0x01;
+
+	cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK;
+
+	memcpy(&cyapa->product_id[0], &resp_data[51], 5);
+	cyapa->product_id[5] = '-';
+	memcpy(&cyapa->product_id[6], &resp_data[56], 6);
+	cyapa->product_id[12] = '-';
+	memcpy(&cyapa->product_id[13], &resp_data[62], 2);
+	cyapa->product_id[15] = '\0';
+
+	/* Get the number of Rx electrodes. */
+	rotat_align = resp_data[68];
+	cyapa->electrodes_rx =
+		rotat_align ? cyapa->electrodes_y : cyapa->electrodes_x;
+	cyapa->aligned_electrodes_rx = (cyapa->electrodes_rx + 3) & ~3u;
+
+	if (!cyapa->electrodes_x || !cyapa->electrodes_y ||
+		!cyapa->physical_size_x || !cyapa->physical_size_y ||
+		!cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int cyapa_gen6_bl_read_app_info(struct cyapa *cyapa)
+{
+	u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH];
+	int resp_len;
+	int error;
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH,
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_bl_resp_data, false);
+	if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH ||
+		!PIP_CMD_COMPLETE_SUCCESS(resp_data))
+		return error ? error : -EIO;
+
+	cyapa->fw_maj_ver = resp_data[8];
+	cyapa->fw_min_ver = resp_data[9];
+
+	cyapa->platform_ver = (resp_data[12] >> PIP_BL_PLATFORM_VER_SHIFT) &
+			      PIP_BL_PLATFORM_VER_MASK;
+
+	memcpy(&cyapa->product_id[0], &resp_data[13], 5);
+	cyapa->product_id[5] = '-';
+	memcpy(&cyapa->product_id[6], &resp_data[18], 6);
+	cyapa->product_id[12] = '-';
+	memcpy(&cyapa->product_id[13], &resp_data[24], 2);
+	cyapa->product_id[15] = '\0';
+
+	return 0;
+
+}
+
+static int cyapa_gen6_config_dev_irq(struct cyapa *cyapa, u8 cmd_code)
+{
+	u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, cmd_code };
+	u8 resp_data[6];
+	int resp_len;
+	int error;
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_app_resp_data, false);
+	if (error || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
+			!PIP_CMD_COMPLETE_SUCCESS(resp_data)
+			)
+		return error < 0 ? error : -EINVAL;
+
+	return 0;
+}
+
+static int cyapa_gen6_set_proximity(struct cyapa *cyapa, bool enable)
+{
+	int error;
+
+	cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
+	error = cyapa_pip_set_proximity(cyapa, enable);
+	cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
+
+	return error;
+}
+
+static int cyapa_gen6_change_power_state(struct cyapa *cyapa, u8 power_mode)
+{
+	u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x46, power_mode };
+	u8 resp_data[6];
+	int resp_len;
+	int error;
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_app_resp_data, false);
+	if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x46))
+		return error < 0 ? error : -EINVAL;
+
+	/* New power state applied in device not match the set power state. */
+	if (resp_data[5] != power_mode)
+		return -EAGAIN;
+
+	return 0;
+}
+
+static int cyapa_gen6_set_interval_setting(struct cyapa *cyapa,
+		struct gen6_interval_setting *interval_setting)
+{
+	struct gen6_set_interval_cmd {
+		__le16 addr;
+		__le16 length;
+		u8 report_id;
+		u8 rsvd;  /* Reserved, must be 0 */
+		u8 cmd_code;
+		__le16 active_interval;
+		__le16 lp1_interval;
+		__le16 lp2_interval;
+	} __packed set_interval_cmd;
+	u8 resp_data[11];
+	int resp_len;
+	int error;
+
+	memset(&set_interval_cmd, 0, sizeof(set_interval_cmd));
+	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &set_interval_cmd.addr);
+	put_unaligned_le16(sizeof(set_interval_cmd) - 2,
+			   &set_interval_cmd.length);
+	set_interval_cmd.report_id = PIP_APP_CMD_REPORT_ID;
+	set_interval_cmd.cmd_code = GEN6_SET_POWER_MODE_INTERVAL;
+	put_unaligned_le16(interval_setting->active_interval,
+			   &set_interval_cmd.active_interval);
+	put_unaligned_le16(interval_setting->lp1_interval,
+			   &set_interval_cmd.lp1_interval);
+	put_unaligned_le16(interval_setting->lp2_interval,
+			   &set_interval_cmd.lp2_interval);
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			(u8 *)&set_interval_cmd, sizeof(set_interval_cmd),
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_app_resp_data, false);
+	if (error ||
+		!VALID_CMD_RESP_HEADER(resp_data, GEN6_SET_POWER_MODE_INTERVAL))
+		return error < 0 ? error : -EINVAL;
+
+	/* Get the real set intervals from response. */
+	interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
+	interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
+	interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
+
+	return 0;
+}
+
+static int cyapa_gen6_get_interval_setting(struct cyapa *cyapa,
+		struct gen6_interval_setting *interval_setting)
+{
+	u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00,
+		     GEN6_GET_POWER_MODE_INTERVAL };
+	u8 resp_data[11];
+	int resp_len;
+	int error;
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			500, cyapa_sort_tsg_pip_app_resp_data, false);
+	if (error ||
+		!VALID_CMD_RESP_HEADER(resp_data, GEN6_GET_POWER_MODE_INTERVAL))
+		return error < 0 ? error : -EINVAL;
+
+	interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
+	interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
+	interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
+
+	return 0;
+}
+
+static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state)
+{
+	u8 ping[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x00 };
+
+	if (state == PIP_DEEP_SLEEP_STATE_ON)
+		/*
+		 * Send ping command to notify device prepare for wake up
+		 * when it's in deep sleep mode. At this time, device will
+		 * response nothing except an I2C NAK.
+		 */
+		cyapa_i2c_pip_write(cyapa, ping, sizeof(ping));
+
+	return cyapa_pip_deep_sleep(cyapa, state);
+}
+
+static int cyapa_gen6_set_power_mode(struct cyapa *cyapa,
+		u8 power_mode, u16 sleep_time, bool is_suspend)
+{
+	struct device *dev = &cyapa->client->dev;
+	struct gen6_interval_setting *interval_setting =
+			&cyapa->gen6_interval_setting;
+	u8 lp_mode;
+	int error;
+
+	if (cyapa->state != CYAPA_STATE_GEN6_APP)
+		return 0;
+
+	if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
+		/*
+		 * Assume TP in deep sleep mode when driver is loaded,
+		 * avoid driver unload and reload command IO issue caused by TP
+		 * has been set into deep sleep mode when unloading.
+		 */
+		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
+	}
+
+	if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) &&
+		PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF)
+		PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME);
+
+	if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) {
+		if (power_mode == PWR_MODE_OFF ||
+			power_mode == PWR_MODE_FULL_ACTIVE ||
+			power_mode == PWR_MODE_BTN_ONLY ||
+			PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) {
+			/* Has in correct power mode state, early return. */
+			return 0;
+		}
+	}
+
+	if (power_mode == PWR_MODE_OFF) {
+		cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
+
+		error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF);
+		if (error) {
+			dev_err(dev, "enter deep sleep fail: %d\n", error);
+			return error;
+		}
+
+		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
+		return 0;
+	}
+
+	/*
+	 * When trackpad in power off mode, it cannot change to other power
+	 * state directly, must be wake up from sleep firstly, then
+	 * continue to do next power sate change.
+	 */
+	if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) {
+		error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
+		if (error) {
+			dev_err(dev, "deep sleep wake fail: %d\n", error);
+			return error;
+		}
+	}
+
+	/*
+	 * Disable device assert interrupts for command response to avoid
+	 * disturbing system suspending or hibernating process.
+	 */
+	cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
+
+	if (power_mode == PWR_MODE_FULL_ACTIVE) {
+		error = cyapa_gen6_change_power_state(cyapa,
+				GEN6_POWER_MODE_ACTIVE);
+		if (error) {
+			dev_err(dev, "change to active fail: %d\n", error);
+			goto out;
+		}
+
+		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE);
+
+		/* Sync the interval setting from device. */
+		cyapa_gen6_get_interval_setting(cyapa, interval_setting);
+
+	} else if (power_mode == PWR_MODE_BTN_ONLY) {
+		error = cyapa_gen6_change_power_state(cyapa,
+				GEN6_POWER_MODE_BTN_ONLY);
+		if (error) {
+			dev_err(dev, "fail to button only mode: %d\n", error);
+			goto out;
+		}
+
+		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY);
+	} else {
+		/*
+		 * Gen6 internally supports to 2 low power scan interval time,
+		 * so can help to switch power mode quickly.
+		 * such as runtime suspend and system suspend.
+		 */
+		if (interval_setting->lp1_interval == sleep_time) {
+			lp_mode = GEN6_POWER_MODE_LP_MODE1;
+		} else if (interval_setting->lp2_interval == sleep_time) {
+			lp_mode = GEN6_POWER_MODE_LP_MODE2;
+		} else {
+			if (interval_setting->lp1_interval == 0) {
+				interval_setting->lp1_interval = sleep_time;
+				lp_mode = GEN6_POWER_MODE_LP_MODE1;
+			} else {
+				interval_setting->lp2_interval = sleep_time;
+				lp_mode = GEN6_POWER_MODE_LP_MODE2;
+			}
+			cyapa_gen6_set_interval_setting(cyapa,
+							interval_setting);
+		}
+
+		error = cyapa_gen6_change_power_state(cyapa, lp_mode);
+		if (error) {
+			dev_err(dev, "set power state to 0x%02x failed: %d\n",
+				lp_mode, error);
+			goto out;
+		}
+
+		PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time);
+		PIP_DEV_SET_PWR_STATE(cyapa,
+			cyapa_sleep_time_to_pwr_cmd(sleep_time));
+	}
+
+out:
+	cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
+	return error;
+}
+
+static int cyapa_gen6_initialize(struct cyapa *cyapa)
+{
+	return 0;
+}
+
+static int cyapa_pip_retrieve_data_structure(struct cyapa *cyapa,
+		u16 read_offset, u16 read_len, u8 data_id,
+		u8 *data, int *data_buf_lens)
+{
+	struct retrieve_data_struct_cmd {
+		struct pip_app_cmd_head head;
+		__le16 read_offset;
+		__le16 read_length;
+		u8 data_id;
+	} __packed cmd;
+	u8 resp_data[GEN6_MAX_RX_NUM + 10];
+	int resp_len;
+	int error;
+
+	memset(&cmd, 0, sizeof(cmd));
+	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd.head.addr);
+	put_unaligned_le16(sizeof(cmd), &cmd.head.length - 2);
+	cmd.head.report_id = PIP_APP_CMD_REPORT_ID;
+	cmd.head.cmd_code = PIP_RETRIEVE_DATA_STRUCTURE;
+	put_unaligned_le16(read_offset, &cmd.read_offset);
+	put_unaligned_le16(read_len, &cmd.read_length);
+	cmd.data_id = data_id;
+
+	resp_len = sizeof(resp_data);
+	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+				(u8 *)&cmd, sizeof(cmd),
+				resp_data, &resp_len,
+				500, cyapa_sort_tsg_pip_app_resp_data,
+				true);
+	if (error || !PIP_CMD_COMPLETE_SUCCESS(resp_data) ||
+		resp_data[6] != data_id ||
+		!VALID_CMD_RESP_HEADER(resp_data, PIP_RETRIEVE_DATA_STRUCTURE))
+		return (error < 0) ? error : -EAGAIN;
+
+	read_len = get_unaligned_le16(&resp_data[7]);
+	if (*data_buf_lens < read_len) {
+		*data_buf_lens = read_len;
+		return -ENOBUFS;
+	}
+
+	memcpy(data, &resp_data[10], read_len);
+	*data_buf_lens = read_len;
+	return 0;
+}
+
+static ssize_t cyapa_gen6_show_baseline(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	u8 data[GEN6_MAX_RX_NUM];
+	int data_len;
+	int size = 0;
+	int i;
+	int error;
+	int resume_error;
+
+	if (!cyapa_is_pip_app_mode(cyapa))
+		return -EBUSY;
+
+	/* 1. Suspend Scanning*/
+	error = cyapa_pip_suspend_scanning(cyapa);
+	if (error)
+		return error;
+
+	/* 2. IDAC and RX Attenuator Calibration Data (Center Frequency). */
+	data_len = sizeof(data);
+	error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
+			GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC,
+			data, &data_len);
+	if (error)
+		goto resume_scanning;
+
+	size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d ",
+			data[0],  /* RX Attenuator Mutual */
+			data[1],  /* IDAC Mutual */
+			data[2],  /* RX Attenuator Self RX */
+			data[3],  /* IDAC Self RX */
+			data[4],  /* RX Attenuator Self TX */
+			data[5]	  /* IDAC Self TX */
+			);
+
+	/* 3. Read Attenuator Trim. */
+	data_len = sizeof(data);
+	error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
+			GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM,
+			data, &data_len);
+	if (error)
+		goto resume_scanning;
+
+	/* set attenuator trim values. */
+	for (i = 0; i < data_len; i++)
+		size += scnprintf(buf + size, PAGE_SIZE - size,	"%d ", data[i]);
+	size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
+
+resume_scanning:
+	/* 4. Resume Scanning*/
+	resume_error = cyapa_pip_resume_scanning(cyapa);
+	if (resume_error || error) {
+		memset(buf, 0, PAGE_SIZE);
+		return resume_error ? resume_error : error;
+	}
+
+	return size;
+}
+
+static int cyapa_gen6_operational_check(struct cyapa *cyapa)
+{
+	struct device *dev = &cyapa->client->dev;
+	int error;
+
+	if (cyapa->gen != CYAPA_GEN6)
+		return -ENODEV;
+
+	switch (cyapa->state) {
+	case CYAPA_STATE_GEN6_BL:
+		error = cyapa_pip_bl_exit(cyapa);
+		if (error) {
+			/* Try to update trackpad product information. */
+			cyapa_gen6_bl_read_app_info(cyapa);
+			goto out;
+		}
+
+		cyapa->state = CYAPA_STATE_GEN6_APP;
+
+	case CYAPA_STATE_GEN6_APP:
+		/*
+		 * If trackpad device in deep sleep mode,
+		 * the app command will fail.
+		 * So always try to reset trackpad device to full active when
+		 * the device state is required.
+		 */
+		error = cyapa_gen6_set_power_mode(cyapa,
+				PWR_MODE_FULL_ACTIVE, 0, false);
+		if (error)
+			dev_warn(dev, "%s: failed to set power active mode.\n",
+				__func__);
+
+		/* By default, the trackpad proximity function is enabled. */
+		error = cyapa_pip_set_proximity(cyapa, true);
+		if (error)
+			dev_warn(dev, "%s: failed to enable proximity.\n",
+				__func__);
+
+		/* Get trackpad product information. */
+		error = cyapa_gen6_read_sys_info(cyapa);
+		if (error)
+			goto out;
+		/* Only support product ID starting with CYTRA */
+		if (memcmp(cyapa->product_id, product_id,
+				strlen(product_id)) != 0) {
+			dev_err(dev, "%s: unknown product ID (%s)\n",
+				__func__, cyapa->product_id);
+			error = -EINVAL;
+		}
+		break;
+	default:
+		error = -EINVAL;
+	}
+
+out:
+	return error;
+}
+
+const struct cyapa_dev_ops cyapa_gen6_ops = {
+	.check_fw = cyapa_pip_check_fw,
+	.bl_enter = cyapa_pip_bl_enter,
+	.bl_initiate = cyapa_pip_bl_initiate,
+	.update_fw = cyapa_pip_do_fw_update,
+	.bl_activate = cyapa_pip_bl_activate,
+	.bl_deactivate = cyapa_pip_bl_deactivate,
+
+	.show_baseline = cyapa_gen6_show_baseline,
+	.calibrate_store = cyapa_pip_do_calibrate,
+
+	.initialize = cyapa_gen6_initialize,
+
+	.state_parse = cyapa_pip_state_parse,
+	.operational_check = cyapa_gen6_operational_check,
+
+	.irq_handler = cyapa_pip_irq_handler,
+	.irq_cmd_handler = cyapa_pip_irq_cmd_handler,
+	.sort_empty_output_data = cyapa_empty_pip_output_data,
+	.set_power_mode = cyapa_gen6_set_power_mode,
+
+	.set_proximity = cyapa_gen6_set_proximity,
+};
diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c
new file mode 100644
index 0000000..28dcfc8
--- /dev/null
+++ b/drivers/input/mouse/cypress_ps2.c
@@ -0,0 +1,712 @@
+/*
+ * Cypress Trackpad PS/2 mouse driver
+ *
+ * Copyright (c) 2012 Cypress Semiconductor Corporation.
+ *
+ * Author:
+ *   Dudley Du <dudl@cypress.com>
+ *
+ * Additional contributors include:
+ *   Kamal Mostafa <kamal@canonical.com>
+ *   Kyle Fazzari <git@status.e4ward.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include "cypress_ps2.h"
+
+#undef CYTP_DEBUG_VERBOSE  /* define this and DEBUG for more verbose dump */
+
+static void cypress_set_packet_size(struct psmouse *psmouse, unsigned int n)
+{
+	struct cytp_data *cytp = psmouse->private;
+	cytp->pkt_size = n;
+}
+
+static const unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200};
+static const unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03};
+
+static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+	if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) {
+		psmouse_dbg(psmouse,
+				"sending command 0x%02x failed, resp 0x%02x\n",
+				value & 0xff, ps2dev->nak);
+		if (ps2dev->nak == CYTP_PS2_RETRY)
+			return CYTP_PS2_RETRY;
+		else
+			return CYTP_PS2_ERROR;
+	}
+
+#ifdef CYTP_DEBUG_VERBOSE
+	psmouse_dbg(psmouse, "sending command 0x%02x succeeded, resp 0xfa\n",
+			value & 0xff);
+#endif
+
+	return 0;
+}
+
+static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd,
+			       unsigned char data)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	int tries = CYTP_PS2_CMD_TRIES;
+	int rc;
+
+	ps2_begin_command(ps2dev);
+
+	do {
+		/*
+		 * Send extension command byte (0xE8 or 0xF3).
+		 * If sending the command fails, send recovery command
+		 * to make the device return to the ready state.
+		 */
+		rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff);
+		if (rc == CYTP_PS2_RETRY) {
+			rc = cypress_ps2_sendbyte(psmouse, 0x00);
+			if (rc == CYTP_PS2_RETRY)
+				rc = cypress_ps2_sendbyte(psmouse, 0x0a);
+		}
+		if (rc == CYTP_PS2_ERROR)
+			continue;
+
+		rc = cypress_ps2_sendbyte(psmouse, data);
+		if (rc == CYTP_PS2_RETRY)
+			rc = cypress_ps2_sendbyte(psmouse, data);
+		if (rc == CYTP_PS2_ERROR)
+			continue;
+		else
+			break;
+	} while (--tries > 0);
+
+	ps2_end_command(ps2dev);
+
+	return rc;
+}
+
+static int cypress_ps2_read_cmd_status(struct psmouse *psmouse,
+				       unsigned char cmd,
+				       unsigned char *param)
+{
+	int rc;
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	enum psmouse_state old_state;
+	int pktsize;
+
+	ps2_begin_command(&psmouse->ps2dev);
+
+	old_state = psmouse->state;
+	psmouse->state = PSMOUSE_CMD_MODE;
+	psmouse->pktcnt = 0;
+
+	pktsize = (cmd == CYTP_CMD_READ_TP_METRICS) ? 8 : 3;
+	memset(param, 0, pktsize);
+
+	rc = cypress_ps2_sendbyte(psmouse, 0xe9);
+	if (rc < 0)
+		goto out;
+
+	wait_event_timeout(ps2dev->wait,
+			(psmouse->pktcnt >= pktsize),
+			msecs_to_jiffies(CYTP_CMD_TIMEOUT));
+
+	memcpy(param, psmouse->packet, pktsize);
+
+	psmouse_dbg(psmouse, "Command 0x%02x response data (0x): %*ph\n",
+			cmd, pktsize, param);
+
+out:
+	psmouse->state = old_state;
+	psmouse->pktcnt = 0;
+
+	ps2_end_command(&psmouse->ps2dev);
+
+	return rc;
+}
+
+static bool cypress_verify_cmd_state(struct psmouse *psmouse,
+				     unsigned char cmd, unsigned char *param)
+{
+	bool rate_match = false;
+	bool resolution_match = false;
+	int i;
+
+	/* callers will do further checking. */
+	if (cmd == CYTP_CMD_READ_CYPRESS_ID ||
+	    cmd == CYTP_CMD_STANDARD_MODE ||
+	    cmd == CYTP_CMD_READ_TP_METRICS)
+		return true;
+
+	if ((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID &&
+	    (param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE) {
+		for (i = 0; i < sizeof(cytp_resolution); i++)
+			if (cytp_resolution[i] == param[1])
+				resolution_match = true;
+
+		for (i = 0; i < sizeof(cytp_rate); i++)
+			if (cytp_rate[i] == param[2])
+				rate_match = true;
+
+		if (resolution_match && rate_match)
+			return true;
+	}
+
+	psmouse_dbg(psmouse, "verify cmd state failed.\n");
+	return false;
+}
+
+static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd,
+				unsigned char *param)
+{
+	int tries = CYTP_PS2_CMD_TRIES;
+	int rc;
+
+	psmouse_dbg(psmouse, "send extension cmd 0x%02x, [%d %d %d %d]\n",
+		 cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd),
+		 DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd));
+
+	do {
+		cypress_ps2_ext_cmd(psmouse,
+				    PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd));
+		cypress_ps2_ext_cmd(psmouse,
+				    PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd));
+		cypress_ps2_ext_cmd(psmouse,
+				    PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd));
+		cypress_ps2_ext_cmd(psmouse,
+				    PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd));
+
+		rc = cypress_ps2_read_cmd_status(psmouse, cmd, param);
+		if (rc)
+			continue;
+
+		if (cypress_verify_cmd_state(psmouse, cmd, param))
+			return 0;
+
+	} while (--tries > 0);
+
+	return -EIO;
+}
+
+int cypress_detect(struct psmouse *psmouse, bool set_properties)
+{
+	unsigned char param[3];
+
+	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
+		return -ENODEV;
+
+	/* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
+	if (param[0] != 0x33 || param[1] != 0xCC)
+		return -ENODEV;
+
+	if (set_properties) {
+		psmouse->vendor = "Cypress";
+		psmouse->name = "Trackpad";
+	}
+
+	return 0;
+}
+
+static int cypress_read_fw_version(struct psmouse *psmouse)
+{
+	struct cytp_data *cytp = psmouse->private;
+	unsigned char param[3];
+
+	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
+		return -ENODEV;
+
+	/* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
+	if (param[0] != 0x33 || param[1] != 0xCC)
+		return -ENODEV;
+
+	cytp->fw_version = param[2] & FW_VERSION_MASX;
+	cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0;
+
+	/*
+	 * Trackpad fw_version 11 (in Dell XPS12) yields a bogus response to
+	 * CYTP_CMD_READ_TP_METRICS so do not try to use it. LP: #1103594.
+	 */
+	if (cytp->fw_version >= 11)
+		cytp->tp_metrics_supported = 0;
+
+	psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version);
+	psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n",
+		 cytp->tp_metrics_supported);
+
+	return 0;
+}
+
+static int cypress_read_tp_metrics(struct psmouse *psmouse)
+{
+	struct cytp_data *cytp = psmouse->private;
+	unsigned char param[8];
+
+	/* set default values for tp metrics. */
+	cytp->tp_width = CYTP_DEFAULT_WIDTH;
+	cytp->tp_high = CYTP_DEFAULT_HIGH;
+	cytp->tp_max_abs_x = CYTP_ABS_MAX_X;
+	cytp->tp_max_abs_y = CYTP_ABS_MAX_Y;
+	cytp->tp_min_pressure = CYTP_MIN_PRESSURE;
+	cytp->tp_max_pressure = CYTP_MAX_PRESSURE;
+	cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
+	cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
+
+	if (!cytp->tp_metrics_supported)
+		return 0;
+
+	memset(param, 0, sizeof(param));
+	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) {
+		/* Update trackpad parameters. */
+		cytp->tp_max_abs_x = (param[1] << 8) | param[0];
+		cytp->tp_max_abs_y = (param[3] << 8) | param[2];
+		cytp->tp_min_pressure = param[4];
+		cytp->tp_max_pressure = param[5];
+	}
+
+	if (!cytp->tp_max_pressure ||
+	    cytp->tp_max_pressure < cytp->tp_min_pressure ||
+	    !cytp->tp_width || !cytp->tp_high ||
+	    !cytp->tp_max_abs_x ||
+	    cytp->tp_max_abs_x < cytp->tp_width ||
+	    !cytp->tp_max_abs_y ||
+	    cytp->tp_max_abs_y < cytp->tp_high)
+		return -EINVAL;
+
+	cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
+	cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
+
+#ifdef CYTP_DEBUG_VERBOSE
+	psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n");
+	psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width);
+	psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high);
+	psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x);
+	psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y);
+	psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure);
+	psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure);
+	psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x);
+	psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y);
+
+	psmouse_dbg(psmouse, "tp_type_APA = %d\n",
+			(param[6] & TP_METRICS_BIT_APA) ? 1 : 0);
+	psmouse_dbg(psmouse, "tp_type_MTG = %d\n",
+			(param[6] & TP_METRICS_BIT_MTG) ? 1 : 0);
+	psmouse_dbg(psmouse, "tp_palm = %d\n",
+			(param[6] & TP_METRICS_BIT_PALM) ? 1 : 0);
+	psmouse_dbg(psmouse, "tp_stubborn = %d\n",
+			(param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0);
+	psmouse_dbg(psmouse, "tp_1f_jitter = %d\n",
+			(param[6] & TP_METRICS_BIT_1F_JITTER) >> 2);
+	psmouse_dbg(psmouse, "tp_2f_jitter = %d\n",
+			(param[6] & TP_METRICS_BIT_2F_JITTER) >> 4);
+	psmouse_dbg(psmouse, "tp_1f_spike = %d\n",
+			param[7] & TP_METRICS_BIT_1F_SPIKE);
+	psmouse_dbg(psmouse, "tp_2f_spike = %d\n",
+			(param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2);
+	psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n",
+			(param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4);
+#endif
+
+	return 0;
+}
+
+static int cypress_query_hardware(struct psmouse *psmouse)
+{
+	int ret;
+
+	ret = cypress_read_fw_version(psmouse);
+	if (ret)
+		return ret;
+
+	ret = cypress_read_tp_metrics(psmouse);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int cypress_set_absolute_mode(struct psmouse *psmouse)
+{
+	struct cytp_data *cytp = psmouse->private;
+	unsigned char param[3];
+
+	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0)
+		return -1;
+
+	cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK)
+			| CYTP_BIT_ABS_PRESSURE;
+	cypress_set_packet_size(psmouse, 5);
+
+	return 0;
+}
+
+/*
+ * Reset trackpad device.
+ * This is also the default mode when trackpad powered on.
+ */
+static void cypress_reset(struct psmouse *psmouse)
+{
+	struct cytp_data *cytp = psmouse->private;
+
+	cytp->mode = 0;
+
+	psmouse_reset(psmouse);
+}
+
+static int cypress_set_input_params(struct input_dev *input,
+				    struct cytp_data *cytp)
+{
+	int ret;
+
+	if (!cytp->tp_res_x || !cytp->tp_res_y)
+		return -EINVAL;
+
+	__set_bit(EV_ABS, input->evbit);
+	input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0);
+	input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0);
+	input_set_abs_params(input, ABS_PRESSURE,
+			     cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0);
+	input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0);
+
+	/* finger position */
+	input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0);
+	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+	ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS,
+			INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK);
+	if (ret < 0)
+		return ret;
+
+	__set_bit(INPUT_PROP_SEMI_MT, input->propbit);
+
+	input_abs_set_res(input, ABS_X, cytp->tp_res_x);
+	input_abs_set_res(input, ABS_Y, cytp->tp_res_y);
+
+	input_abs_set_res(input, ABS_MT_POSITION_X, cytp->tp_res_x);
+	input_abs_set_res(input, ABS_MT_POSITION_Y, cytp->tp_res_y);
+
+	__set_bit(BTN_TOUCH, input->keybit);
+	__set_bit(BTN_TOOL_FINGER, input->keybit);
+	__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
+	__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
+	__set_bit(BTN_TOOL_QUADTAP, input->keybit);
+	__set_bit(BTN_TOOL_QUINTTAP, input->keybit);
+
+	__clear_bit(EV_REL, input->evbit);
+	__clear_bit(REL_X, input->relbit);
+	__clear_bit(REL_Y, input->relbit);
+
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(BTN_LEFT, input->keybit);
+	__set_bit(BTN_RIGHT, input->keybit);
+	__set_bit(BTN_MIDDLE, input->keybit);
+
+	input_set_drvdata(input, cytp);
+
+	return 0;
+}
+
+static int cypress_get_finger_count(unsigned char header_byte)
+{
+	unsigned char bits6_7;
+	int finger_count;
+
+	bits6_7 = header_byte >> 6;
+	finger_count = bits6_7 & 0x03;
+
+	if (finger_count == 1)
+		return 1;
+
+	if (header_byte & ABS_HSCROLL_BIT) {
+		/* HSCROLL gets added on to 0 finger count. */
+		switch (finger_count) {
+			case 0:	return 4;
+			case 2: return 5;
+			default:
+				/* Invalid contact (e.g. palm). Ignore it. */
+				return 0;
+		}
+	}
+
+	return finger_count;
+}
+
+
+static int cypress_parse_packet(struct psmouse *psmouse,
+				struct cytp_data *cytp, struct cytp_report_data *report_data)
+{
+	unsigned char *packet = psmouse->packet;
+	unsigned char header_byte = packet[0];
+
+	memset(report_data, 0, sizeof(struct cytp_report_data));
+
+	report_data->contact_cnt = cypress_get_finger_count(header_byte);
+	report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0;
+
+	if (report_data->contact_cnt == 1) {
+		report_data->contacts[0].x =
+			((packet[1] & 0x70) << 4) | packet[2];
+		report_data->contacts[0].y =
+			((packet[1] & 0x07) << 8) | packet[3];
+		if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
+			report_data->contacts[0].z = packet[4];
+
+	} else if (report_data->contact_cnt >= 2) {
+		report_data->contacts[0].x =
+			((packet[1] & 0x70) << 4) | packet[2];
+		report_data->contacts[0].y =
+			((packet[1] & 0x07) << 8) | packet[3];
+		if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
+			report_data->contacts[0].z = packet[4];
+
+		report_data->contacts[1].x =
+			((packet[5] & 0xf0) << 4) | packet[6];
+		report_data->contacts[1].y =
+			((packet[5] & 0x0f) << 8) | packet[7];
+		if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
+			report_data->contacts[1].z = report_data->contacts[0].z;
+	}
+
+	report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0;
+	report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0;
+
+	/*
+	 * This is only true if one of the mouse buttons were tapped.  Make
+	 * sure it doesn't turn into a click. The regular tap-to-click
+	 * functionality will handle that on its own. If we don't do this,
+	 * disabling tap-to-click won't affect the mouse button zones.
+	 */
+	if (report_data->tap)
+		report_data->left = 0;
+
+#ifdef CYTP_DEBUG_VERBOSE
+	{
+		int i;
+		int n = report_data->contact_cnt;
+		psmouse_dbg(psmouse, "Dump parsed report data as below:\n");
+		psmouse_dbg(psmouse, "contact_cnt = %d\n",
+			report_data->contact_cnt);
+		if (n > CYTP_MAX_MT_SLOTS)
+		    n = CYTP_MAX_MT_SLOTS;
+		for (i = 0; i < n; i++)
+			psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i,
+					report_data->contacts[i].x,
+					report_data->contacts[i].y,
+					report_data->contacts[i].z);
+		psmouse_dbg(psmouse, "left = %d\n", report_data->left);
+		psmouse_dbg(psmouse, "right = %d\n", report_data->right);
+		psmouse_dbg(psmouse, "middle = %d\n", report_data->middle);
+	}
+#endif
+
+	return 0;
+}
+
+static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt)
+{
+	int i;
+	struct input_dev *input = psmouse->dev;
+	struct cytp_data *cytp = psmouse->private;
+	struct cytp_report_data report_data;
+	struct cytp_contact *contact;
+	struct input_mt_pos pos[CYTP_MAX_MT_SLOTS];
+	int slots[CYTP_MAX_MT_SLOTS];
+	int n;
+
+	cypress_parse_packet(psmouse, cytp, &report_data);
+
+	n = report_data.contact_cnt;
+	if (n > CYTP_MAX_MT_SLOTS)
+		n = CYTP_MAX_MT_SLOTS;
+
+	for (i = 0; i < n; i++) {
+		contact = &report_data.contacts[i];
+		pos[i].x = contact->x;
+		pos[i].y = contact->y;
+	}
+
+	input_mt_assign_slots(input, slots, pos, n, 0);
+
+	for (i = 0; i < n; i++) {
+		contact = &report_data.contacts[i];
+		input_mt_slot(input, slots[i]);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+		input_report_abs(input, ABS_MT_POSITION_X, contact->x);
+		input_report_abs(input, ABS_MT_POSITION_Y, contact->y);
+		input_report_abs(input, ABS_MT_PRESSURE, contact->z);
+	}
+
+	input_mt_sync_frame(input);
+
+	input_mt_report_finger_count(input, report_data.contact_cnt);
+
+	input_report_key(input, BTN_LEFT, report_data.left);
+	input_report_key(input, BTN_RIGHT, report_data.right);
+	input_report_key(input, BTN_MIDDLE, report_data.middle);
+
+	input_sync(input);
+}
+
+static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse)
+{
+	int contact_cnt;
+	int index = psmouse->pktcnt - 1;
+	unsigned char *packet = psmouse->packet;
+	struct cytp_data *cytp = psmouse->private;
+
+	if (index < 0 || index > cytp->pkt_size)
+		return PSMOUSE_BAD_DATA;
+
+	if (index == 0 && (packet[0] & 0xfc) == 0) {
+		/* call packet process for reporting finger leave. */
+		cypress_process_packet(psmouse, 1);
+		return PSMOUSE_FULL_PACKET;
+	}
+
+	/*
+	 * Perform validation (and adjust packet size) based only on the
+	 * first byte; allow all further bytes through.
+	 */
+	if (index != 0)
+		return PSMOUSE_GOOD_DATA;
+
+	/*
+	 * If absolute/relative mode bit has not been set yet, just pass
+	 * the byte through.
+	 */
+	if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0)
+		return PSMOUSE_GOOD_DATA;
+
+	if ((packet[0] & 0x08) == 0x08)
+		return PSMOUSE_BAD_DATA;
+
+	contact_cnt = cypress_get_finger_count(packet[0]);
+	if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE)
+		cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4);
+	else
+		cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 5);
+
+	return PSMOUSE_GOOD_DATA;
+}
+
+static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse)
+{
+	struct cytp_data *cytp = psmouse->private;
+
+	if (psmouse->pktcnt >= cytp->pkt_size) {
+		cypress_process_packet(psmouse, 0);
+		return PSMOUSE_FULL_PACKET;
+	}
+
+	return cypress_validate_byte(psmouse);
+}
+
+static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate)
+{
+	struct cytp_data *cytp = psmouse->private;
+
+	if (rate >= 80) {
+		psmouse->rate = 80;
+		cytp->mode |= CYTP_BIT_HIGH_RATE;
+	} else {
+		psmouse->rate = 40;
+		cytp->mode &= ~CYTP_BIT_HIGH_RATE;
+	}
+
+	ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate,
+		    PSMOUSE_CMD_SETRATE);
+}
+
+static void cypress_disconnect(struct psmouse *psmouse)
+{
+	cypress_reset(psmouse);
+	kfree(psmouse->private);
+	psmouse->private = NULL;
+}
+
+static int cypress_reconnect(struct psmouse *psmouse)
+{
+	int tries = CYTP_PS2_CMD_TRIES;
+	int rc;
+
+	do {
+		cypress_reset(psmouse);
+		rc = cypress_detect(psmouse, false);
+	} while (rc && (--tries > 0));
+
+	if (rc) {
+		psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n");
+		return -1;
+	}
+
+	if (cypress_set_absolute_mode(psmouse)) {
+		psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+int cypress_init(struct psmouse *psmouse)
+{
+	struct cytp_data *cytp;
+
+	cytp = kzalloc(sizeof(struct cytp_data), GFP_KERNEL);
+	if (!cytp)
+		return -ENOMEM;
+
+	psmouse->private = cytp;
+	psmouse->pktsize = 8;
+
+	cypress_reset(psmouse);
+
+	if (cypress_query_hardware(psmouse)) {
+		psmouse_err(psmouse, "Unable to query Trackpad hardware.\n");
+		goto err_exit;
+	}
+
+	if (cypress_set_absolute_mode(psmouse)) {
+		psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n");
+		goto err_exit;
+	}
+
+	if (cypress_set_input_params(psmouse->dev, cytp) < 0) {
+		psmouse_err(psmouse, "init: Unable to set input params.\n");
+		goto err_exit;
+	}
+
+	psmouse->model = 1;
+	psmouse->protocol_handler = cypress_protocol_handler;
+	psmouse->set_rate = cypress_set_rate;
+	psmouse->disconnect = cypress_disconnect;
+	psmouse->reconnect = cypress_reconnect;
+	psmouse->cleanup = cypress_reset;
+	psmouse->resync_time = 0;
+
+	return 0;
+
+err_exit:
+	/*
+	 * Reset Cypress Trackpad as a standard mouse. Then
+	 * let psmouse driver commmunicating with it as default PS2 mouse.
+	 */
+	cypress_reset(psmouse);
+
+	psmouse->private = NULL;
+	kfree(cytp);
+
+	return -1;
+}
diff --git a/drivers/input/mouse/cypress_ps2.h b/drivers/input/mouse/cypress_ps2.h
new file mode 100644
index 0000000..81f68aa
--- /dev/null
+++ b/drivers/input/mouse/cypress_ps2.h
@@ -0,0 +1,186 @@
+#ifndef _CYPRESS_PS2_H
+#define _CYPRESS_PS2_H
+
+#include "psmouse.h"
+
+#define CMD_BITS_MASK 0x03
+#define COMPOSIT(x, s) (((x) & CMD_BITS_MASK) << (s))
+
+#define ENCODE_CMD(aa, bb, cc, dd) \
+	(COMPOSIT((aa), 6) | COMPOSIT((bb), 4) | COMPOSIT((cc), 2) | COMPOSIT((dd), 0))
+#define CYTP_CMD_ABS_NO_PRESSURE_MODE       ENCODE_CMD(0, 1, 0, 0)
+#define CYTP_CMD_ABS_WITH_PRESSURE_MODE     ENCODE_CMD(0, 1, 0, 1)
+#define CYTP_CMD_SMBUS_MODE                 ENCODE_CMD(0, 1, 1, 0)
+#define CYTP_CMD_STANDARD_MODE              ENCODE_CMD(0, 2, 0, 0)  /* not implemented yet. */
+#define CYTP_CMD_CYPRESS_REL_MODE           ENCODE_CMD(1, 1, 1, 1)  /* not implemented yet. */
+#define CYTP_CMD_READ_CYPRESS_ID            ENCODE_CMD(0, 0, 0, 0)
+#define CYTP_CMD_READ_TP_METRICS            ENCODE_CMD(0, 0, 0, 1)
+#define CYTP_CMD_SET_HSCROLL_WIDTH(w)       ENCODE_CMD(1, 1, 0, (w))
+#define     CYTP_CMD_SET_HSCROLL_MASK       ENCODE_CMD(1, 1, 0, 0)
+#define CYTP_CMD_SET_VSCROLL_WIDTH(w)       ENCODE_CMD(1, 2, 0, (w))
+#define     CYTP_CMD_SET_VSCROLL_MASK       ENCODE_CMD(1, 2, 0, 0)
+#define CYTP_CMD_SET_PALM_GEOMETRY(e)       ENCODE_CMD(1, 2, 1, (e))
+#define     CYTP_CMD_PALM_GEMMETRY_MASK     ENCODE_CMD(1, 2, 1, 0)
+#define CYTP_CMD_SET_PALM_SENSITIVITY(s)    ENCODE_CMD(1, 2, 2, (s))
+#define     CYTP_CMD_PALM_SENSITIVITY_MASK  ENCODE_CMD(1, 2, 2, 0)
+#define CYTP_CMD_SET_MOUSE_SENSITIVITY(s)   ENCODE_CMD(1, 3, ((s) >> 2), (s))
+#define     CYTP_CMD_MOUSE_SENSITIVITY_MASK ENCODE_CMD(1, 3, 0, 0)
+#define CYTP_CMD_REQUEST_BASELINE_STATUS    ENCODE_CMD(2, 0, 0, 1)
+#define CYTP_CMD_REQUEST_RECALIBRATION      ENCODE_CMD(2, 0, 0, 3)
+
+#define DECODE_CMD_AA(x) (((x) >> 6) & CMD_BITS_MASK)
+#define DECODE_CMD_BB(x) (((x) >> 4) & CMD_BITS_MASK)
+#define DECODE_CMD_CC(x) (((x) >> 2) & CMD_BITS_MASK)
+#define DECODE_CMD_DD(x) ((x) & CMD_BITS_MASK)
+
+/* Cypress trackpad working mode. */
+#define CYTP_BIT_ABS_PRESSURE    (1 << 3)
+#define CYTP_BIT_ABS_NO_PRESSURE (1 << 2)
+#define CYTP_BIT_CYPRESS_REL     (1 << 1)
+#define CYTP_BIT_STANDARD_REL    (1 << 0)
+#define CYTP_BIT_REL_MASK (CYTP_BIT_CYPRESS_REL | CYTP_BIT_STANDARD_REL)
+#define CYTP_BIT_ABS_MASK (CYTP_BIT_ABS_PRESSURE | CYTP_BIT_ABS_NO_PRESSURE)
+#define CYTP_BIT_ABS_REL_MASK (CYTP_BIT_ABS_MASK | CYTP_BIT_REL_MASK)
+
+#define CYTP_BIT_HIGH_RATE       (1 << 4)
+/*
+ * report mode bit is set, firmware working in Remote Mode.
+ * report mode bit is cleared, firmware working in Stream Mode.
+ */
+#define CYTP_BIT_REPORT_MODE     (1 << 5)
+
+/* scrolling width values for set HSCROLL and VSCROLL width command. */
+#define SCROLL_WIDTH_NARROW 1
+#define SCROLL_WIDTH_NORMAL 2
+#define SCROLL_WIDTH_WIDE   3
+
+#define PALM_GEOMETRY_ENABLE  1
+#define PALM_GEOMETRY_DISABLE 0
+
+#define TP_METRICS_MASK  0x80
+#define FW_VERSION_MASX    0x7f
+#define FW_VER_HIGH_MASK 0x70
+#define FW_VER_LOW_MASK  0x0f
+
+/* Times to retry a ps2_command and millisecond delay between tries. */
+#define CYTP_PS2_CMD_TRIES 3
+#define CYTP_PS2_CMD_DELAY 500
+
+/* time out for PS/2 command only in milliseconds. */
+#define CYTP_CMD_TIMEOUT  200
+#define CYTP_DATA_TIMEOUT 30
+
+#define CYTP_EXT_CMD   0xe8
+#define CYTP_PS2_RETRY 0xfe
+#define CYTP_PS2_ERROR 0xfc
+
+#define CYTP_RESP_RETRY 0x01
+#define CYTP_RESP_ERROR 0xfe
+
+
+#define CYTP_105001_WIDTH  97   /* Dell XPS 13 */
+#define CYTP_105001_HIGH   59
+#define CYTP_DEFAULT_WIDTH (CYTP_105001_WIDTH)
+#define CYTP_DEFAULT_HIGH  (CYTP_105001_HIGH)
+
+#define CYTP_ABS_MAX_X     1600
+#define CYTP_ABS_MAX_Y     900
+#define CYTP_MAX_PRESSURE  255
+#define CYTP_MIN_PRESSURE  0
+
+/* header byte bits of relative package. */
+#define BTN_LEFT_BIT   0x01
+#define BTN_RIGHT_BIT  0x02
+#define BTN_MIDDLE_BIT 0x04
+#define REL_X_SIGN_BIT 0x10
+#define REL_Y_SIGN_BIT 0x20
+
+/* header byte bits of absolute package. */
+#define ABS_VSCROLL_BIT 0x10
+#define ABS_HSCROLL_BIT 0x20
+#define ABS_MULTIFINGER_TAP 0x04
+#define ABS_EDGE_MOTION_MASK 0x80
+
+#define DFLT_RESP_BITS_VALID     0x88  /* SMBus bit should not be set. */
+#define DFLT_RESP_SMBUS_BIT      0x80
+#define   DFLT_SMBUS_MODE        0x80
+#define   DFLT_PS2_MODE          0x00
+#define DFLT_RESP_BIT_MODE       0x40
+#define   DFLT_RESP_REMOTE_MODE  0x40
+#define   DFLT_RESP_STREAM_MODE  0x00
+#define DFLT_RESP_BIT_REPORTING  0x20
+#define DFLT_RESP_BIT_SCALING    0x10
+
+#define TP_METRICS_BIT_PALM               0x80
+#define TP_METRICS_BIT_STUBBORN           0x40
+#define TP_METRICS_BIT_2F_JITTER          0x30
+#define TP_METRICS_BIT_1F_JITTER          0x0c
+#define TP_METRICS_BIT_APA                0x02
+#define TP_METRICS_BIT_MTG                0x01
+#define TP_METRICS_BIT_ABS_PKT_FORMAT_SET 0xf0
+#define TP_METRICS_BIT_2F_SPIKE           0x0c
+#define TP_METRICS_BIT_1F_SPIKE           0x03
+
+/* bits of first byte response of E9h-Status Request command. */
+#define RESP_BTN_RIGHT_BIT  0x01
+#define RESP_BTN_MIDDLE_BIT 0x02
+#define RESP_BTN_LEFT_BIT   0x04
+#define RESP_SCALING_BIT    0x10
+#define RESP_ENABLE_BIT     0x20
+#define RESP_REMOTE_BIT     0x40
+#define RESP_SMBUS_BIT      0x80
+
+#define CYTP_MAX_MT_SLOTS 2
+
+struct cytp_contact {
+	int x;
+	int y;
+	int z;  /* also named as touch pressure. */
+};
+
+/* The structure of Cypress Trackpad event data. */
+struct cytp_report_data {
+	int contact_cnt;
+	struct cytp_contact contacts[CYTP_MAX_MT_SLOTS];
+	unsigned int left:1;
+	unsigned int right:1;
+	unsigned int middle:1;
+	unsigned int tap:1;  /* multi-finger tap detected. */
+};
+
+/* The structure of Cypress Trackpad device private data. */
+struct cytp_data {
+	int fw_version;
+
+	int pkt_size;
+	int mode;
+
+	int tp_min_pressure;
+	int tp_max_pressure;
+	int tp_width;  /* X direction physical size in mm. */
+	int tp_high;  /* Y direction physical size in mm. */
+	int tp_max_abs_x;  /* Max X absolute units that can be reported. */
+	int tp_max_abs_y;  /* Max Y absolute units that can be reported. */
+
+	int tp_res_x;  /* X resolution in units/mm. */
+	int tp_res_y;  /* Y resolution in units/mm. */
+
+	int tp_metrics_supported;
+};
+
+
+#ifdef CONFIG_MOUSE_PS2_CYPRESS
+int cypress_detect(struct psmouse *psmouse, bool set_properties);
+int cypress_init(struct psmouse *psmouse);
+#else
+inline int cypress_detect(struct psmouse *psmouse, bool set_properties)
+{
+	return -ENOSYS;
+}
+inline int cypress_init(struct psmouse *psmouse)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_CYPRESS */
+
+#endif  /* _CYPRESS_PS2_H */
diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h
new file mode 100644
index 0000000..c0ec261
--- /dev/null
+++ b/drivers/input/mouse/elan_i2c.h
@@ -0,0 +1,89 @@
+/*
+ * Elan I2C/SMBus Touchpad driver
+ *
+ * Copyright (c) 2013 ELAN Microelectronics Corp.
+ *
+ * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
+ *
+ * Based on cyapa driver:
+ * copyright (c) 2011-2012 Cypress Semiconductor, Inc.
+ * copyright (c) 2011-2012 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#ifndef _ELAN_I2C_H
+#define _ELAN_I2C_H
+
+#include <linux/types.h>
+
+#define ETP_ENABLE_ABS		0x0001
+#define ETP_ENABLE_CALIBRATE	0x0002
+#define ETP_DISABLE_CALIBRATE	0x0000
+#define ETP_DISABLE_POWER	0x0001
+#define ETP_PRESSURE_OFFSET	25
+
+/* IAP Firmware handling */
+#define ETP_PRODUCT_ID_FORMAT_STRING	"%d.0"
+#define ETP_FW_NAME		"elan_i2c_" ETP_PRODUCT_ID_FORMAT_STRING ".bin"
+#define ETP_IAP_START_ADDR	0x0083
+#define ETP_FW_IAP_PAGE_ERR	(1 << 5)
+#define ETP_FW_IAP_INTF_ERR	(1 << 4)
+#define ETP_FW_PAGE_SIZE	64
+#define ETP_FW_SIGNATURE_SIZE	6
+
+struct i2c_client;
+struct completion;
+
+enum tp_mode {
+	IAP_MODE = 1,
+	MAIN_MODE
+};
+
+struct elan_transport_ops {
+	int (*initialize)(struct i2c_client *client);
+	int (*sleep_control)(struct i2c_client *, bool sleep);
+	int (*power_control)(struct i2c_client *, bool enable);
+	int (*set_mode)(struct i2c_client *client, u8 mode);
+
+	int (*calibrate)(struct i2c_client *client);
+	int (*calibrate_result)(struct i2c_client *client, u8 *val);
+
+	int (*get_baseline_data)(struct i2c_client *client,
+				 bool max_baseliune, u8 *value);
+
+	int (*get_version)(struct i2c_client *client, bool iap, u8 *version);
+	int (*get_sm_version)(struct i2c_client *client,
+			      u8* ic_type, u8 *version);
+	int (*get_checksum)(struct i2c_client *client, bool iap, u16 *csum);
+	int (*get_product_id)(struct i2c_client *client, u16 *id);
+
+	int (*get_max)(struct i2c_client *client,
+		       unsigned int *max_x, unsigned int *max_y);
+	int (*get_resolution)(struct i2c_client *client,
+			      u8 *hw_res_x, u8 *hw_res_y);
+	int (*get_num_traces)(struct i2c_client *client,
+			      unsigned int *x_tracenum,
+			      unsigned int *y_tracenum);
+
+	int (*iap_get_mode)(struct i2c_client *client, enum tp_mode *mode);
+	int (*iap_reset)(struct i2c_client *client);
+
+	int (*prepare_fw_update)(struct i2c_client *client);
+	int (*write_fw_block)(struct i2c_client *client,
+			      const u8 *page, u16 checksum, int idx);
+	int (*finish_fw_update)(struct i2c_client *client,
+				struct completion *reset_done);
+
+	int (*get_report)(struct i2c_client *client, u8 *report);
+	int (*get_pressure_adjustment)(struct i2c_client *client,
+				       int *adjustment);
+};
+
+extern const struct elan_transport_ops elan_smbus_ops, elan_i2c_ops;
+
+#endif /* _ELAN_I2C_H */
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
new file mode 100644
index 0000000..c9d491b
--- /dev/null
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -0,0 +1,1276 @@
+/*
+ * Elan I2C/SMBus Touchpad driver
+ *
+ * Copyright (c) 2013 ELAN Microelectronics Corp.
+ *
+ * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
+ * Author: KT Liao <kt.liao@emc.com.tw>
+ * Version: 1.6.2
+ *
+ * Based on cyapa driver:
+ * copyright (c) 2011-2012 Cypress Semiconductor, Inc.
+ * copyright (c) 2011-2012 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/jiffies.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+
+#include "elan_i2c.h"
+
+#define DRIVER_NAME		"elan_i2c"
+#define ELAN_DRIVER_VERSION	"1.6.2"
+#define ELAN_VENDOR_ID		0x04f3
+#define ETP_MAX_PRESSURE	255
+#define ETP_FWIDTH_REDUCE	90
+#define ETP_FINGER_WIDTH	15
+#define ETP_RETRY_COUNT		3
+
+#define ETP_MAX_FINGERS		5
+#define ETP_FINGER_DATA_LEN	5
+#define ETP_REPORT_ID		0x5D
+#define ETP_REPORT_ID_OFFSET	2
+#define ETP_TOUCH_INFO_OFFSET	3
+#define ETP_FINGER_DATA_OFFSET	4
+#define ETP_HOVER_INFO_OFFSET	30
+#define ETP_MAX_REPORT_LEN	34
+
+/* The main device structure */
+struct elan_tp_data {
+	struct i2c_client	*client;
+	struct input_dev	*input;
+	struct regulator	*vcc;
+
+	const struct elan_transport_ops *ops;
+
+	/* for fw update */
+	struct completion	fw_completion;
+	bool			in_fw_update;
+
+	struct mutex		sysfs_mutex;
+
+	unsigned int		max_x;
+	unsigned int		max_y;
+	unsigned int		width_x;
+	unsigned int		width_y;
+	unsigned int		x_res;
+	unsigned int		y_res;
+
+	u16			product_id;
+	u8			fw_version;
+	u8			sm_version;
+	u8			iap_version;
+	u16			fw_checksum;
+	int			pressure_adjustment;
+	u8			mode;
+	u8			ic_type;
+	u16			fw_validpage_count;
+	u16			fw_signature_address;
+
+	bool			irq_wake;
+
+	u8			min_baseline;
+	u8			max_baseline;
+	bool			baseline_ready;
+};
+
+static int elan_get_fwinfo(u8 iap_version, u16 *validpage_count,
+			   u16 *signature_address)
+{
+	switch (iap_version) {
+	case 0x00:
+	case 0x06:
+	case 0x08:
+		*validpage_count = 512;
+		break;
+	case 0x03:
+	case 0x07:
+	case 0x09:
+	case 0x0A:
+	case 0x0B:
+	case 0x0C:
+		*validpage_count = 768;
+		break;
+	case 0x0D:
+		*validpage_count = 896;
+		break;
+	case 0x0E:
+		*validpage_count = 640;
+		break;
+	default:
+		/* unknown ic type clear value */
+		*validpage_count = 0;
+		*signature_address = 0;
+		return -ENXIO;
+	}
+
+	*signature_address =
+		(*validpage_count * ETP_FW_PAGE_SIZE) - ETP_FW_SIGNATURE_SIZE;
+
+	return 0;
+}
+
+static int elan_enable_power(struct elan_tp_data *data)
+{
+	int repeat = ETP_RETRY_COUNT;
+	int error;
+
+	error = regulator_enable(data->vcc);
+	if (error) {
+		dev_err(&data->client->dev,
+			"failed to enable regulator: %d\n", error);
+		return error;
+	}
+
+	do {
+		error = data->ops->power_control(data->client, true);
+		if (error >= 0)
+			return 0;
+
+		msleep(30);
+	} while (--repeat > 0);
+
+	dev_err(&data->client->dev, "failed to enable power: %d\n", error);
+	return error;
+}
+
+static int elan_disable_power(struct elan_tp_data *data)
+{
+	int repeat = ETP_RETRY_COUNT;
+	int error;
+
+	do {
+		error = data->ops->power_control(data->client, false);
+		if (!error) {
+			error = regulator_disable(data->vcc);
+			if (error) {
+				dev_err(&data->client->dev,
+					"failed to disable regulator: %d\n",
+					error);
+				/* Attempt to power the chip back up */
+				data->ops->power_control(data->client, true);
+				break;
+			}
+
+			return 0;
+		}
+
+		msleep(30);
+	} while (--repeat > 0);
+
+	dev_err(&data->client->dev, "failed to disable power: %d\n", error);
+	return error;
+}
+
+static int elan_sleep(struct elan_tp_data *data)
+{
+	int repeat = ETP_RETRY_COUNT;
+	int error;
+
+	do {
+		error = data->ops->sleep_control(data->client, true);
+		if (!error)
+			return 0;
+
+		msleep(30);
+	} while (--repeat > 0);
+
+	return error;
+}
+
+static int elan_query_product(struct elan_tp_data *data)
+{
+	int error;
+
+	error = data->ops->get_product_id(data->client, &data->product_id);
+	if (error)
+		return error;
+
+	error = data->ops->get_sm_version(data->client, &data->ic_type,
+					  &data->sm_version);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static int elan_check_ASUS_special_fw(struct elan_tp_data *data)
+{
+	if (data->ic_type == 0x0E) {
+		switch (data->product_id) {
+		case 0x05 ... 0x07:
+		case 0x09:
+		case 0x13:
+			return true;
+		}
+	} else if (data->ic_type == 0x08 && data->product_id == 0x26) {
+		/* ASUS EeeBook X205TA */
+		return true;
+	}
+
+	return false;
+}
+
+static int __elan_initialize(struct elan_tp_data *data)
+{
+	struct i2c_client *client = data->client;
+	bool woken_up = false;
+	int error;
+
+	error = data->ops->initialize(client);
+	if (error) {
+		dev_err(&client->dev, "device initialize failed: %d\n", error);
+		return error;
+	}
+
+	error = elan_query_product(data);
+	if (error)
+		return error;
+
+	/*
+	 * Some ASUS devices were shipped with firmware that requires
+	 * touchpads to be woken up first, before attempting to switch
+	 * them into absolute reporting mode.
+	 */
+	if (elan_check_ASUS_special_fw(data)) {
+		error = data->ops->sleep_control(client, false);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to wake device up: %d\n", error);
+			return error;
+		}
+
+		msleep(200);
+		woken_up = true;
+	}
+
+	data->mode |= ETP_ENABLE_ABS;
+	error = data->ops->set_mode(client, data->mode);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to switch to absolute mode: %d\n", error);
+		return error;
+	}
+
+	if (!woken_up) {
+		error = data->ops->sleep_control(client, false);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to wake device up: %d\n", error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static int elan_initialize(struct elan_tp_data *data)
+{
+	int repeat = ETP_RETRY_COUNT;
+	int error;
+
+	do {
+		error = __elan_initialize(data);
+		if (!error)
+			return 0;
+
+		msleep(30);
+	} while (--repeat > 0);
+
+	return error;
+}
+
+static int elan_query_device_info(struct elan_tp_data *data)
+{
+	int error;
+
+	error = data->ops->get_version(data->client, false, &data->fw_version);
+	if (error)
+		return error;
+
+	error = data->ops->get_checksum(data->client, false,
+					&data->fw_checksum);
+	if (error)
+		return error;
+
+	error = data->ops->get_version(data->client, true, &data->iap_version);
+	if (error)
+		return error;
+
+	error = data->ops->get_pressure_adjustment(data->client,
+						   &data->pressure_adjustment);
+	if (error)
+		return error;
+
+	error = elan_get_fwinfo(data->iap_version, &data->fw_validpage_count,
+				&data->fw_signature_address);
+	if (error)
+		dev_warn(&data->client->dev,
+			 "unexpected iap version %#04x (ic type: %#04x), firmware update will not work\n",
+			 data->iap_version, data->ic_type);
+
+	return 0;
+}
+
+static unsigned int elan_convert_resolution(u8 val)
+{
+	/*
+	 * (value from firmware) * 10 + 790 = dpi
+	 *
+	 * We also have to convert dpi to dots/mm (*10/254 to avoid floating
+	 * point).
+	 */
+
+	return ((int)(char)val * 10 + 790) * 10 / 254;
+}
+
+static int elan_query_device_parameters(struct elan_tp_data *data)
+{
+	unsigned int x_traces, y_traces;
+	u8 hw_x_res, hw_y_res;
+	int error;
+
+	error = data->ops->get_max(data->client, &data->max_x, &data->max_y);
+	if (error)
+		return error;
+
+	error = data->ops->get_num_traces(data->client, &x_traces, &y_traces);
+	if (error)
+		return error;
+
+	data->width_x = data->max_x / x_traces;
+	data->width_y = data->max_y / y_traces;
+
+	error = data->ops->get_resolution(data->client, &hw_x_res, &hw_y_res);
+	if (error)
+		return error;
+
+	data->x_res = elan_convert_resolution(hw_x_res);
+	data->y_res = elan_convert_resolution(hw_y_res);
+
+	return 0;
+}
+
+/*
+ **********************************************************
+ * IAP firmware updater related routines
+ **********************************************************
+ */
+static int elan_write_fw_block(struct elan_tp_data *data,
+			       const u8 *page, u16 checksum, int idx)
+{
+	int retry = ETP_RETRY_COUNT;
+	int error;
+
+	do {
+		error = data->ops->write_fw_block(data->client,
+						  page, checksum, idx);
+		if (!error)
+			return 0;
+
+		dev_dbg(&data->client->dev,
+			"IAP retrying page %d (error: %d)\n", idx, error);
+	} while (--retry > 0);
+
+	return error;
+}
+
+static int __elan_update_firmware(struct elan_tp_data *data,
+				  const struct firmware *fw)
+{
+	struct i2c_client *client = data->client;
+	struct device *dev = &client->dev;
+	int i, j;
+	int error;
+	u16 iap_start_addr;
+	u16 boot_page_count;
+	u16 sw_checksum = 0, fw_checksum = 0;
+
+	error = data->ops->prepare_fw_update(client);
+	if (error)
+		return error;
+
+	iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]);
+
+	boot_page_count = (iap_start_addr * 2) / ETP_FW_PAGE_SIZE;
+	for (i = boot_page_count; i < data->fw_validpage_count; i++) {
+		u16 checksum = 0;
+		const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE];
+
+		for (j = 0; j < ETP_FW_PAGE_SIZE; j += 2)
+			checksum += ((page[j + 1] << 8) | page[j]);
+
+		error = elan_write_fw_block(data, page, checksum, i);
+		if (error) {
+			dev_err(dev, "write page %d fail: %d\n", i, error);
+			return error;
+		}
+
+		sw_checksum += checksum;
+	}
+
+	/* Wait WDT reset and power on reset */
+	msleep(600);
+
+	error = data->ops->finish_fw_update(client, &data->fw_completion);
+	if (error)
+		return error;
+
+	error = data->ops->get_checksum(client, true, &fw_checksum);
+	if (error)
+		return error;
+
+	if (sw_checksum != fw_checksum) {
+		dev_err(dev, "checksum diff sw=[%04X], fw=[%04X]\n",
+			sw_checksum, fw_checksum);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int elan_update_firmware(struct elan_tp_data *data,
+				const struct firmware *fw)
+{
+	struct i2c_client *client = data->client;
+	int retval;
+
+	dev_dbg(&client->dev, "Starting firmware update....\n");
+
+	disable_irq(client->irq);
+	data->in_fw_update = true;
+
+	retval = __elan_update_firmware(data, fw);
+	if (retval) {
+		dev_err(&client->dev, "firmware update failed: %d\n", retval);
+		data->ops->iap_reset(client);
+	} else {
+		/* Reinitialize TP after fw is updated */
+		elan_initialize(data);
+		elan_query_device_info(data);
+	}
+
+	data->in_fw_update = false;
+	enable_irq(client->irq);
+
+	return retval;
+}
+
+/*
+ *******************************************************************
+ * SYSFS attributes
+ *******************************************************************
+ */
+static ssize_t elan_sysfs_read_fw_checksum(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+
+	return sprintf(buf, "0x%04x\n", data->fw_checksum);
+}
+
+static ssize_t elan_sysfs_read_product_id(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+
+	return sprintf(buf, ETP_PRODUCT_ID_FORMAT_STRING "\n",
+		       data->product_id);
+}
+
+static ssize_t elan_sysfs_read_fw_ver(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%d.0\n", data->fw_version);
+}
+
+static ssize_t elan_sysfs_read_sm_ver(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%d.0\n", data->sm_version);
+}
+
+static ssize_t elan_sysfs_read_iap_ver(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%d.0\n", data->iap_version);
+}
+
+static ssize_t elan_sysfs_update_fw(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+	const struct firmware *fw;
+	char *fw_name;
+	int error;
+	const u8 *fw_signature;
+	static const u8 signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF};
+
+	if (data->fw_validpage_count == 0)
+		return -EINVAL;
+
+	/* Look for a firmware with the product id appended. */
+	fw_name = kasprintf(GFP_KERNEL, ETP_FW_NAME, data->product_id);
+	if (!fw_name) {
+		dev_err(dev, "failed to allocate memory for firmware name\n");
+		return -ENOMEM;
+	}
+
+	dev_info(dev, "requesting fw '%s'\n", fw_name);
+	error = request_firmware(&fw, fw_name, dev);
+	kfree(fw_name);
+	if (error) {
+		dev_err(dev, "failed to request firmware: %d\n", error);
+		return error;
+	}
+
+	/* Firmware file must match signature data */
+	fw_signature = &fw->data[data->fw_signature_address];
+	if (memcmp(fw_signature, signature, sizeof(signature)) != 0) {
+		dev_err(dev, "signature mismatch (expected %*ph, got %*ph)\n",
+			(int)sizeof(signature), signature,
+			(int)sizeof(signature), fw_signature);
+		error = -EBADF;
+		goto out_release_fw;
+	}
+
+	error = mutex_lock_interruptible(&data->sysfs_mutex);
+	if (error)
+		goto out_release_fw;
+
+	error = elan_update_firmware(data, fw);
+
+	mutex_unlock(&data->sysfs_mutex);
+
+out_release_fw:
+	release_firmware(fw);
+	return error ?: count;
+}
+
+static ssize_t calibrate_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+	int tries = 20;
+	int retval;
+	int error;
+	u8 val[3];
+
+	retval = mutex_lock_interruptible(&data->sysfs_mutex);
+	if (retval)
+		return retval;
+
+	disable_irq(client->irq);
+
+	data->mode |= ETP_ENABLE_CALIBRATE;
+	retval = data->ops->set_mode(client, data->mode);
+	if (retval) {
+		dev_err(dev, "failed to enable calibration mode: %d\n",
+			retval);
+		goto out;
+	}
+
+	retval = data->ops->calibrate(client);
+	if (retval) {
+		dev_err(dev, "failed to start calibration: %d\n",
+			retval);
+		goto out_disable_calibrate;
+	}
+
+	val[0] = 0xff;
+	do {
+		/* Wait 250ms before checking if calibration has completed. */
+		msleep(250);
+
+		retval = data->ops->calibrate_result(client, val);
+		if (retval)
+			dev_err(dev, "failed to check calibration result: %d\n",
+				retval);
+		else if (val[0] == 0)
+			break; /* calibration done */
+
+	} while (--tries);
+
+	if (tries == 0) {
+		dev_err(dev, "failed to calibrate. Timeout.\n");
+		retval = -ETIMEDOUT;
+	}
+
+out_disable_calibrate:
+	data->mode &= ~ETP_ENABLE_CALIBRATE;
+	error = data->ops->set_mode(data->client, data->mode);
+	if (error) {
+		dev_err(dev, "failed to disable calibration mode: %d\n",
+			error);
+		if (!retval)
+			retval = error;
+	}
+out:
+	enable_irq(client->irq);
+	mutex_unlock(&data->sysfs_mutex);
+	return retval ?: count;
+}
+
+static ssize_t elan_sysfs_read_mode(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+	int error;
+	enum tp_mode mode;
+
+	error = mutex_lock_interruptible(&data->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = data->ops->iap_get_mode(data->client, &mode);
+
+	mutex_unlock(&data->sysfs_mutex);
+
+	if (error)
+		return error;
+
+	return sprintf(buf, "%d\n", (int)mode);
+}
+
+static DEVICE_ATTR(product_id, S_IRUGO, elan_sysfs_read_product_id, NULL);
+static DEVICE_ATTR(firmware_version, S_IRUGO, elan_sysfs_read_fw_ver, NULL);
+static DEVICE_ATTR(sample_version, S_IRUGO, elan_sysfs_read_sm_ver, NULL);
+static DEVICE_ATTR(iap_version, S_IRUGO, elan_sysfs_read_iap_ver, NULL);
+static DEVICE_ATTR(fw_checksum, S_IRUGO, elan_sysfs_read_fw_checksum, NULL);
+static DEVICE_ATTR(mode, S_IRUGO, elan_sysfs_read_mode, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, elan_sysfs_update_fw);
+
+static DEVICE_ATTR_WO(calibrate);
+
+static struct attribute *elan_sysfs_entries[] = {
+	&dev_attr_product_id.attr,
+	&dev_attr_firmware_version.attr,
+	&dev_attr_sample_version.attr,
+	&dev_attr_iap_version.attr,
+	&dev_attr_fw_checksum.attr,
+	&dev_attr_calibrate.attr,
+	&dev_attr_mode.attr,
+	&dev_attr_update_fw.attr,
+	NULL,
+};
+
+static const struct attribute_group elan_sysfs_group = {
+	.attrs = elan_sysfs_entries,
+};
+
+static ssize_t acquire_store(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+	int error;
+	int retval;
+
+	retval = mutex_lock_interruptible(&data->sysfs_mutex);
+	if (retval)
+		return retval;
+
+	disable_irq(client->irq);
+
+	data->baseline_ready = false;
+
+	data->mode |= ETP_ENABLE_CALIBRATE;
+	retval = data->ops->set_mode(data->client, data->mode);
+	if (retval) {
+		dev_err(dev, "Failed to enable calibration mode to get baseline: %d\n",
+			retval);
+		goto out;
+	}
+
+	msleep(250);
+
+	retval = data->ops->get_baseline_data(data->client, true,
+					      &data->max_baseline);
+	if (retval) {
+		dev_err(dev, "Failed to read max baseline form device: %d\n",
+			retval);
+		goto out_disable_calibrate;
+	}
+
+	retval = data->ops->get_baseline_data(data->client, false,
+					      &data->min_baseline);
+	if (retval) {
+		dev_err(dev, "Failed to read min baseline form device: %d\n",
+			retval);
+		goto out_disable_calibrate;
+	}
+
+	data->baseline_ready = true;
+
+out_disable_calibrate:
+	data->mode &= ~ETP_ENABLE_CALIBRATE;
+	error = data->ops->set_mode(data->client, data->mode);
+	if (error) {
+		dev_err(dev, "Failed to disable calibration mode after acquiring baseline: %d\n",
+			error);
+		if (!retval)
+			retval = error;
+	}
+out:
+	enable_irq(client->irq);
+	mutex_unlock(&data->sysfs_mutex);
+	return retval ?: count;
+}
+
+static ssize_t min_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+	int retval;
+
+	retval = mutex_lock_interruptible(&data->sysfs_mutex);
+	if (retval)
+		return retval;
+
+	if (!data->baseline_ready) {
+		retval = -ENODATA;
+		goto out;
+	}
+
+	retval = snprintf(buf, PAGE_SIZE, "%d", data->min_baseline);
+
+out:
+	mutex_unlock(&data->sysfs_mutex);
+	return retval;
+}
+
+static ssize_t max_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+	int retval;
+
+	retval = mutex_lock_interruptible(&data->sysfs_mutex);
+	if (retval)
+		return retval;
+
+	if (!data->baseline_ready) {
+		retval = -ENODATA;
+		goto out;
+	}
+
+	retval = snprintf(buf, PAGE_SIZE, "%d", data->max_baseline);
+
+out:
+	mutex_unlock(&data->sysfs_mutex);
+	return retval;
+}
+
+
+static DEVICE_ATTR_WO(acquire);
+static DEVICE_ATTR_RO(min);
+static DEVICE_ATTR_RO(max);
+
+static struct attribute *elan_baseline_sysfs_entries[] = {
+	&dev_attr_acquire.attr,
+	&dev_attr_min.attr,
+	&dev_attr_max.attr,
+	NULL,
+};
+
+static const struct attribute_group elan_baseline_sysfs_group = {
+	.name = "baseline",
+	.attrs = elan_baseline_sysfs_entries,
+};
+
+static const struct attribute_group *elan_sysfs_groups[] = {
+	&elan_sysfs_group,
+	&elan_baseline_sysfs_group,
+	NULL
+};
+
+/*
+ ******************************************************************
+ * Elan isr functions
+ ******************************************************************
+ */
+static void elan_report_contact(struct elan_tp_data *data,
+				int contact_num, bool contact_valid,
+				u8 *finger_data)
+{
+	struct input_dev *input = data->input;
+	unsigned int pos_x, pos_y;
+	unsigned int pressure, mk_x, mk_y;
+	unsigned int area_x, area_y, major, minor;
+	unsigned int scaled_pressure;
+
+	if (contact_valid) {
+		pos_x = ((finger_data[0] & 0xf0) << 4) |
+						finger_data[1];
+		pos_y = ((finger_data[0] & 0x0f) << 8) |
+						finger_data[2];
+		mk_x = (finger_data[3] & 0x0f);
+		mk_y = (finger_data[3] >> 4);
+		pressure = finger_data[4];
+
+		if (pos_x > data->max_x || pos_y > data->max_y) {
+			dev_dbg(input->dev.parent,
+				"[%d] x=%d y=%d over max (%d, %d)",
+				contact_num, pos_x, pos_y,
+				data->max_x, data->max_y);
+			return;
+		}
+
+		/*
+		 * To avoid treating large finger as palm, let's reduce the
+		 * width x and y per trace.
+		 */
+		area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE);
+		area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE);
+
+		major = max(area_x, area_y);
+		minor = min(area_x, area_y);
+
+		scaled_pressure = pressure + data->pressure_adjustment;
+
+		if (scaled_pressure > ETP_MAX_PRESSURE)
+			scaled_pressure = ETP_MAX_PRESSURE;
+
+		input_mt_slot(input, contact_num);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+		input_report_abs(input, ABS_MT_POSITION_X, pos_x);
+		input_report_abs(input, ABS_MT_POSITION_Y, data->max_y - pos_y);
+		input_report_abs(input, ABS_MT_PRESSURE, scaled_pressure);
+		input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
+		input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
+		input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
+	} else {
+		input_mt_slot(input, contact_num);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+	}
+}
+
+static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
+{
+	struct input_dev *input = data->input;
+	u8 *finger_data = &packet[ETP_FINGER_DATA_OFFSET];
+	int i;
+	u8 tp_info = packet[ETP_TOUCH_INFO_OFFSET];
+	u8 hover_info = packet[ETP_HOVER_INFO_OFFSET];
+	bool contact_valid, hover_event;
+
+	hover_event = hover_info & 0x40;
+	for (i = 0; i < ETP_MAX_FINGERS; i++) {
+		contact_valid = tp_info & (1U << (3 + i));
+		elan_report_contact(data, i, contact_valid, finger_data);
+
+		if (contact_valid)
+			finger_data += ETP_FINGER_DATA_LEN;
+	}
+
+	input_report_key(input, BTN_LEFT, tp_info & 0x01);
+	input_report_abs(input, ABS_DISTANCE, hover_event != 0);
+	input_mt_report_pointer_emulation(input, true);
+	input_sync(input);
+}
+
+static irqreturn_t elan_isr(int irq, void *dev_id)
+{
+	struct elan_tp_data *data = dev_id;
+	struct device *dev = &data->client->dev;
+	int error;
+	u8 report[ETP_MAX_REPORT_LEN];
+
+	/*
+	 * When device is connected to i2c bus, when all IAP page writes
+	 * complete, the driver will receive interrupt and must read
+	 * 0000 to confirm that IAP is finished.
+	*/
+	if (data->in_fw_update) {
+		complete(&data->fw_completion);
+		goto out;
+	}
+
+	error = data->ops->get_report(data->client, report);
+	if (error)
+		goto out;
+
+	if (report[ETP_REPORT_ID_OFFSET] != ETP_REPORT_ID)
+		dev_err(dev, "invalid report id data (%x)\n",
+			report[ETP_REPORT_ID_OFFSET]);
+	else
+		elan_report_absolute(data, report);
+
+out:
+	return IRQ_HANDLED;
+}
+
+/*
+ ******************************************************************
+ * Elan initialization functions
+ ******************************************************************
+ */
+static int elan_setup_input_device(struct elan_tp_data *data)
+{
+	struct device *dev = &data->client->dev;
+	struct input_dev *input;
+	unsigned int max_width = max(data->width_x, data->width_y);
+	unsigned int min_width = min(data->width_x, data->width_y);
+	int error;
+
+	input = devm_input_allocate_device(dev);
+	if (!input)
+		return -ENOMEM;
+
+	input->name = "Elan Touchpad";
+	input->id.bustype = BUS_I2C;
+	input->id.vendor = ELAN_VENDOR_ID;
+	input->id.product = data->product_id;
+	input_set_drvdata(input, data);
+
+	error = input_mt_init_slots(input, ETP_MAX_FINGERS,
+				    INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(dev, "failed to initialize MT slots: %d\n", error);
+		return error;
+	}
+
+	__set_bit(EV_ABS, input->evbit);
+	__set_bit(INPUT_PROP_POINTER, input->propbit);
+	__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+	__set_bit(BTN_LEFT, input->keybit);
+
+	/* Set up ST parameters */
+	input_set_abs_params(input, ABS_X, 0, data->max_x, 0, 0);
+	input_set_abs_params(input, ABS_Y, 0, data->max_y, 0, 0);
+	input_abs_set_res(input, ABS_X, data->x_res);
+	input_abs_set_res(input, ABS_Y, data->y_res);
+	input_set_abs_params(input, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0);
+	input_set_abs_params(input, ABS_TOOL_WIDTH, 0, ETP_FINGER_WIDTH, 0, 0);
+	input_set_abs_params(input, ABS_DISTANCE, 0, 1, 0, 0);
+
+	/* And MT parameters */
+	input_set_abs_params(input, ABS_MT_POSITION_X, 0, data->max_x, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, data->max_y, 0, 0);
+	input_abs_set_res(input, ABS_MT_POSITION_X, data->x_res);
+	input_abs_set_res(input, ABS_MT_POSITION_Y, data->y_res);
+	input_set_abs_params(input, ABS_MT_PRESSURE, 0,
+			     ETP_MAX_PRESSURE, 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
+			     ETP_FINGER_WIDTH * max_width, 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0,
+			     ETP_FINGER_WIDTH * min_width, 0, 0);
+
+	data->input = input;
+
+	return 0;
+}
+
+static void elan_disable_regulator(void *_data)
+{
+	struct elan_tp_data *data = _data;
+
+	regulator_disable(data->vcc);
+}
+
+static void elan_remove_sysfs_groups(void *_data)
+{
+	struct elan_tp_data *data = _data;
+
+	sysfs_remove_groups(&data->client->dev.kobj, elan_sysfs_groups);
+}
+
+static int elan_probe(struct i2c_client *client,
+		      const struct i2c_device_id *dev_id)
+{
+	const struct elan_transport_ops *transport_ops;
+	struct device *dev = &client->dev;
+	struct elan_tp_data *data;
+	unsigned long irqflags;
+	int error;
+
+	if (IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_I2C) &&
+	    i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		transport_ops = &elan_i2c_ops;
+	} else if (IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) &&
+		   i2c_check_functionality(client->adapter,
+					   I2C_FUNC_SMBUS_BYTE_DATA |
+						I2C_FUNC_SMBUS_BLOCK_DATA |
+						I2C_FUNC_SMBUS_I2C_BLOCK)) {
+		transport_ops = &elan_smbus_ops;
+	} else {
+		dev_err(dev, "not a supported I2C/SMBus adapter\n");
+		return -EIO;
+	}
+
+	data = devm_kzalloc(&client->dev, sizeof(struct elan_tp_data),
+			    GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, data);
+
+	data->ops = transport_ops;
+	data->client = client;
+	init_completion(&data->fw_completion);
+	mutex_init(&data->sysfs_mutex);
+
+	data->vcc = devm_regulator_get(&client->dev, "vcc");
+	if (IS_ERR(data->vcc)) {
+		error = PTR_ERR(data->vcc);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vcc' regulator: %d\n",
+				error);
+		return error;
+	}
+
+	error = regulator_enable(data->vcc);
+	if (error) {
+		dev_err(&client->dev,
+			"Failed to enable regulator: %d\n", error);
+		return error;
+	}
+
+	error = devm_add_action(&client->dev,
+				elan_disable_regulator, data);
+	if (error) {
+		regulator_disable(data->vcc);
+		dev_err(&client->dev,
+			"Failed to add disable regulator action: %d\n",
+			error);
+		return error;
+	}
+
+	/* Initialize the touchpad. */
+	error = elan_initialize(data);
+	if (error)
+		return error;
+
+	error = elan_query_device_info(data);
+	if (error)
+		return error;
+
+	error = elan_query_device_parameters(data);
+	if (error)
+		return error;
+
+	dev_dbg(&client->dev,
+		"Elan Touchpad Information:\n"
+		"    Module product ID:  0x%04x\n"
+		"    Firmware Version:  0x%04x\n"
+		"    Sample Version:  0x%04x\n"
+		"    IAP Version:  0x%04x\n"
+		"    Max ABS X,Y:   %d,%d\n"
+		"    Width X,Y:   %d,%d\n"
+		"    Resolution X,Y:   %d,%d (dots/mm)\n",
+		data->product_id,
+		data->fw_version,
+		data->sm_version,
+		data->iap_version,
+		data->max_x, data->max_y,
+		data->width_x, data->width_y,
+		data->x_res, data->y_res);
+
+	/* Set up input device properties based on queried parameters. */
+	error = elan_setup_input_device(data);
+	if (error)
+		return error;
+
+	/*
+	 * Systems using device tree should set up interrupt via DTS,
+	 * the rest will use the default falling edge interrupts.
+	 */
+	irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING;
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, elan_isr,
+					  irqflags | IRQF_ONESHOT,
+					  client->name, data);
+	if (error) {
+		dev_err(&client->dev, "cannot register irq=%d\n", client->irq);
+		return error;
+	}
+
+	error = sysfs_create_groups(&client->dev.kobj, elan_sysfs_groups);
+	if (error) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			error);
+		return error;
+	}
+
+	error = devm_add_action(&client->dev,
+				elan_remove_sysfs_groups, data);
+	if (error) {
+		elan_remove_sysfs_groups(data);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n",
+			error);
+		return error;
+	}
+
+	error = input_register_device(data->input);
+	if (error) {
+		dev_err(&client->dev, "failed to register input device: %d\n",
+			error);
+		return error;
+	}
+
+	/*
+	 * Systems using device tree should set up wakeup via DTS,
+	 * the rest will configure device as wakeup source by default.
+	 */
+	if (!client->dev.of_node)
+		device_init_wakeup(&client->dev, true);
+
+	return 0;
+}
+
+static int __maybe_unused elan_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+	int ret;
+
+	/*
+	 * We are taking the mutex to make sure sysfs operations are
+	 * complete before we attempt to bring the device into low[er]
+	 * power mode.
+	 */
+	ret = mutex_lock_interruptible(&data->sysfs_mutex);
+	if (ret)
+		return ret;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(dev)) {
+		ret = elan_sleep(data);
+		/* Enable wake from IRQ */
+		data->irq_wake = (enable_irq_wake(client->irq) == 0);
+	} else {
+		ret = elan_disable_power(data);
+	}
+
+	mutex_unlock(&data->sysfs_mutex);
+	return ret;
+}
+
+static int __maybe_unused elan_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+	int error;
+
+	if (device_may_wakeup(dev) && data->irq_wake) {
+		disable_irq_wake(client->irq);
+		data->irq_wake = false;
+	}
+
+	error = elan_enable_power(data);
+	if (error) {
+		dev_err(dev, "power up when resuming failed: %d\n", error);
+		goto err;
+	}
+
+	error = elan_initialize(data);
+	if (error)
+		dev_err(dev, "initialize when resuming failed: %d\n", error);
+
+err:
+	enable_irq(data->client->irq);
+	return error;
+}
+
+static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);
+
+static const struct i2c_device_id elan_id[] = {
+	{ DRIVER_NAME, 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, elan_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id elan_acpi_id[] = {
+	{ "ELAN0000", 0 },
+	{ "ELAN0100", 0 },
+	{ "ELAN0600", 0 },
+	{ "ELAN0602", 0 },
+	{ "ELAN0605", 0 },
+	{ "ELAN0608", 0 },
+	{ "ELAN0605", 0 },
+	{ "ELAN0609", 0 },
+	{ "ELAN060B", 0 },
+	{ "ELAN060C", 0 },
+	{ "ELAN0611", 0 },
+	{ "ELAN1000", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, elan_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id elan_of_match[] = {
+	{ .compatible = "elan,ekth3000" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, elan_of_match);
+#endif
+
+static struct i2c_driver elan_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.pm	= &elan_pm_ops,
+		.acpi_match_table = ACPI_PTR(elan_acpi_id),
+		.of_match_table = of_match_ptr(elan_of_match),
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+	.probe		= elan_probe,
+	.id_table	= elan_id,
+};
+
+module_i2c_driver(elan_driver);
+
+MODULE_AUTHOR("Duson Lin <dusonlin@emc.com.tw>");
+MODULE_DESCRIPTION("Elan I2C/SMBus Touchpad driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ELAN_DRIVER_VERSION);
diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c
new file mode 100644
index 0000000..a679e56
--- /dev/null
+++ b/drivers/input/mouse/elan_i2c_i2c.c
@@ -0,0 +1,643 @@
+/*
+ * Elan I2C/SMBus Touchpad driver - I2C interface
+ *
+ * Copyright (c) 2013 ELAN Microelectronics Corp.
+ *
+ * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
+ *
+ * Based on cyapa driver:
+ * copyright (c) 2011-2012 Cypress Semiconductor, Inc.
+ * copyright (c) 2011-2012 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/unaligned.h>
+
+#include "elan_i2c.h"
+
+/* Elan i2c commands */
+#define ETP_I2C_RESET			0x0100
+#define ETP_I2C_WAKE_UP			0x0800
+#define ETP_I2C_SLEEP			0x0801
+#define ETP_I2C_DESC_CMD		0x0001
+#define ETP_I2C_REPORT_DESC_CMD		0x0002
+#define ETP_I2C_STAND_CMD		0x0005
+#define ETP_I2C_UNIQUEID_CMD		0x0101
+#define ETP_I2C_FW_VERSION_CMD		0x0102
+#define ETP_I2C_SM_VERSION_CMD		0x0103
+#define ETP_I2C_XY_TRACENUM_CMD		0x0105
+#define ETP_I2C_MAX_X_AXIS_CMD		0x0106
+#define ETP_I2C_MAX_Y_AXIS_CMD		0x0107
+#define ETP_I2C_RESOLUTION_CMD		0x0108
+#define ETP_I2C_PRESSURE_CMD		0x010A
+#define ETP_I2C_IAP_VERSION_CMD		0x0110
+#define ETP_I2C_SET_CMD			0x0300
+#define ETP_I2C_POWER_CMD		0x0307
+#define ETP_I2C_FW_CHECKSUM_CMD		0x030F
+#define ETP_I2C_IAP_CTRL_CMD		0x0310
+#define ETP_I2C_IAP_CMD			0x0311
+#define ETP_I2C_IAP_RESET_CMD		0x0314
+#define ETP_I2C_IAP_CHECKSUM_CMD	0x0315
+#define ETP_I2C_CALIBRATE_CMD		0x0316
+#define ETP_I2C_MAX_BASELINE_CMD	0x0317
+#define ETP_I2C_MIN_BASELINE_CMD	0x0318
+
+#define ETP_I2C_REPORT_LEN		34
+#define ETP_I2C_DESC_LENGTH		30
+#define ETP_I2C_REPORT_DESC_LENGTH	158
+#define ETP_I2C_INF_LENGTH		2
+#define ETP_I2C_IAP_PASSWORD		0x1EA5
+#define ETP_I2C_IAP_RESET		0xF0F0
+#define ETP_I2C_MAIN_MODE_ON		(1 << 9)
+#define ETP_I2C_IAP_REG_L		0x01
+#define ETP_I2C_IAP_REG_H		0x06
+
+static int elan_i2c_read_block(struct i2c_client *client,
+			       u16 reg, u8 *val, u16 len)
+{
+	__le16 buf[] = {
+		cpu_to_le16(reg),
+	};
+	struct i2c_msg msgs[] = {
+		{
+			.addr = client->addr,
+			.flags = client->flags & I2C_M_TEN,
+			.len = sizeof(buf),
+			.buf = (u8 *)buf,
+		},
+		{
+			.addr = client->addr,
+			.flags = (client->flags & I2C_M_TEN) | I2C_M_RD,
+			.len = len,
+			.buf = val,
+		}
+	};
+	int ret;
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	return ret == ARRAY_SIZE(msgs) ? 0 : (ret < 0 ? ret : -EIO);
+}
+
+static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 *val)
+{
+	int retval;
+
+	retval = elan_i2c_read_block(client, reg, val, ETP_I2C_INF_LENGTH);
+	if (retval < 0) {
+		dev_err(&client->dev, "reading cmd (0x%04x) fail.\n", reg);
+		return retval;
+	}
+
+	return 0;
+}
+
+static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd)
+{
+	__le16 buf[] = {
+		cpu_to_le16(reg),
+		cpu_to_le16(cmd),
+	};
+	struct i2c_msg msg = {
+		.addr = client->addr,
+		.flags = client->flags & I2C_M_TEN,
+		.len = sizeof(buf),
+		.buf = (u8 *)buf,
+	};
+	int ret;
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret != 1) {
+		if (ret >= 0)
+			ret = -EIO;
+		dev_err(&client->dev, "writing cmd (0x%04x) failed: %d\n",
+			reg, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int elan_i2c_initialize(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	int error;
+	u8 val[256];
+
+	error = elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD, ETP_I2C_RESET);
+	if (error) {
+		dev_err(dev, "device reset failed: %d\n", error);
+		return error;
+	}
+
+	/* Wait for the device to reset */
+	msleep(100);
+
+	/* get reset acknowledgement 0000 */
+	error = i2c_master_recv(client, val, ETP_I2C_INF_LENGTH);
+	if (error < 0) {
+		dev_err(dev, "failed to read reset response: %d\n", error);
+		return error;
+	}
+
+	error = elan_i2c_read_block(client, ETP_I2C_DESC_CMD,
+				    val, ETP_I2C_DESC_LENGTH);
+	if (error) {
+		dev_err(dev, "cannot get device descriptor: %d\n", error);
+		return error;
+	}
+
+	error = elan_i2c_read_block(client, ETP_I2C_REPORT_DESC_CMD,
+				    val, ETP_I2C_REPORT_DESC_LENGTH);
+	if (error) {
+		dev_err(dev, "fetching report descriptor failed.: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int elan_i2c_sleep_control(struct i2c_client *client, bool sleep)
+{
+	return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
+				  sleep ? ETP_I2C_SLEEP : ETP_I2C_WAKE_UP);
+}
+
+static int elan_i2c_power_control(struct i2c_client *client, bool enable)
+{
+	u8 val[2];
+	u16 reg;
+	int error;
+
+	error = elan_i2c_read_cmd(client, ETP_I2C_POWER_CMD, val);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to read current power state: %d\n",
+			error);
+		return error;
+	}
+
+	reg = le16_to_cpup((__le16 *)val);
+	if (enable)
+		reg &= ~ETP_DISABLE_POWER;
+	else
+		reg |= ETP_DISABLE_POWER;
+
+	error = elan_i2c_write_cmd(client, ETP_I2C_POWER_CMD, reg);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to write current power state: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int elan_i2c_set_mode(struct i2c_client *client, u8 mode)
+{
+	return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD, mode);
+}
+
+
+static int elan_i2c_calibrate(struct i2c_client *client)
+{
+	return elan_i2c_write_cmd(client, ETP_I2C_CALIBRATE_CMD, 1);
+}
+
+static int elan_i2c_calibrate_result(struct i2c_client *client, u8 *val)
+{
+	return elan_i2c_read_block(client, ETP_I2C_CALIBRATE_CMD, val, 1);
+}
+
+static int elan_i2c_get_baseline_data(struct i2c_client *client,
+				      bool max_baseline, u8 *value)
+{
+	int error;
+	u8 val[3];
+
+	error = elan_i2c_read_cmd(client,
+				  max_baseline ? ETP_I2C_MAX_BASELINE_CMD :
+						 ETP_I2C_MIN_BASELINE_CMD,
+				  val);
+	if (error)
+		return error;
+
+	*value = le16_to_cpup((__le16 *)val);
+
+	return 0;
+}
+
+static int elan_i2c_get_version(struct i2c_client *client,
+				bool iap, u8 *version)
+{
+	int error;
+	u8 val[3];
+
+	error = elan_i2c_read_cmd(client,
+				  iap ? ETP_I2C_IAP_VERSION_CMD :
+					ETP_I2C_FW_VERSION_CMD,
+				  val);
+	if (error) {
+		dev_err(&client->dev, "failed to get %s version: %d\n",
+			iap ? "IAP" : "FW", error);
+		return error;
+	}
+
+	*version = val[0];
+	return 0;
+}
+
+static int elan_i2c_get_sm_version(struct i2c_client *client,
+				   u8 *ic_type, u8 *version)
+{
+	int error;
+	u8 val[3];
+
+	error = elan_i2c_read_cmd(client, ETP_I2C_SM_VERSION_CMD, val);
+	if (error) {
+		dev_err(&client->dev, "failed to get SM version: %d\n", error);
+		return error;
+	}
+
+	*version = val[0];
+	*ic_type = val[1];
+	return 0;
+}
+
+static int elan_i2c_get_product_id(struct i2c_client *client, u16 *id)
+{
+	int error;
+	u8 val[3];
+
+	error = elan_i2c_read_cmd(client, ETP_I2C_UNIQUEID_CMD, val);
+	if (error) {
+		dev_err(&client->dev, "failed to get product ID: %d\n", error);
+		return error;
+	}
+
+	*id = le16_to_cpup((__le16 *)val);
+	return 0;
+}
+
+static int elan_i2c_get_checksum(struct i2c_client *client,
+				 bool iap, u16 *csum)
+{
+	int error;
+	u8 val[3];
+
+	error = elan_i2c_read_cmd(client,
+				  iap ? ETP_I2C_IAP_CHECKSUM_CMD :
+					ETP_I2C_FW_CHECKSUM_CMD,
+				  val);
+	if (error) {
+		dev_err(&client->dev, "failed to get %s checksum: %d\n",
+			iap ? "IAP" : "FW", error);
+		return error;
+	}
+
+	*csum = le16_to_cpup((__le16 *)val);
+	return 0;
+}
+
+static int elan_i2c_get_max(struct i2c_client *client,
+			    unsigned int *max_x, unsigned int *max_y)
+{
+	int error;
+	u8 val[3];
+
+	error = elan_i2c_read_cmd(client, ETP_I2C_MAX_X_AXIS_CMD, val);
+	if (error) {
+		dev_err(&client->dev, "failed to get X dimension: %d\n", error);
+		return error;
+	}
+
+	*max_x = le16_to_cpup((__le16 *)val) & 0x0fff;
+
+	error = elan_i2c_read_cmd(client, ETP_I2C_MAX_Y_AXIS_CMD, val);
+	if (error) {
+		dev_err(&client->dev, "failed to get Y dimension: %d\n", error);
+		return error;
+	}
+
+	*max_y = le16_to_cpup((__le16 *)val) & 0x0fff;
+
+	return 0;
+}
+
+static int elan_i2c_get_resolution(struct i2c_client *client,
+				   u8 *hw_res_x, u8 *hw_res_y)
+{
+	int error;
+	u8 val[3];
+
+	error = elan_i2c_read_cmd(client, ETP_I2C_RESOLUTION_CMD, val);
+	if (error) {
+		dev_err(&client->dev, "failed to get resolution: %d\n", error);
+		return error;
+	}
+
+	*hw_res_x = val[0];
+	*hw_res_y = val[1];
+
+	return 0;
+}
+
+static int elan_i2c_get_num_traces(struct i2c_client *client,
+				   unsigned int *x_traces,
+				   unsigned int *y_traces)
+{
+	int error;
+	u8 val[3];
+
+	error = elan_i2c_read_cmd(client, ETP_I2C_XY_TRACENUM_CMD, val);
+	if (error) {
+		dev_err(&client->dev, "failed to get trace info: %d\n", error);
+		return error;
+	}
+
+	*x_traces = val[0];
+	*y_traces = val[1];
+
+	return 0;
+}
+
+static int elan_i2c_get_pressure_adjustment(struct i2c_client *client,
+					    int *adjustment)
+{
+	int error;
+	u8 val[3];
+
+	error = elan_i2c_read_cmd(client, ETP_I2C_PRESSURE_CMD, val);
+	if (error) {
+		dev_err(&client->dev, "failed to get pressure format: %d\n",
+			error);
+		return error;
+	}
+
+	if ((val[0] >> 4) & 0x1)
+		*adjustment = 0;
+	else
+		*adjustment = ETP_PRESSURE_OFFSET;
+
+	return 0;
+}
+
+static int elan_i2c_iap_get_mode(struct i2c_client *client, enum tp_mode *mode)
+{
+	int error;
+	u16 constant;
+	u8 val[3];
+
+	error = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to read iap control register: %d\n",
+			error);
+		return error;
+	}
+
+	constant = le16_to_cpup((__le16 *)val);
+	dev_dbg(&client->dev, "iap control reg: 0x%04x.\n", constant);
+
+	*mode = (constant & ETP_I2C_MAIN_MODE_ON) ? MAIN_MODE : IAP_MODE;
+
+	return 0;
+}
+
+static int elan_i2c_iap_reset(struct i2c_client *client)
+{
+	int error;
+
+	error = elan_i2c_write_cmd(client, ETP_I2C_IAP_RESET_CMD,
+				   ETP_I2C_IAP_RESET);
+	if (error) {
+		dev_err(&client->dev, "cannot reset IC: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int elan_i2c_set_flash_key(struct i2c_client *client)
+{
+	int error;
+
+	error = elan_i2c_write_cmd(client, ETP_I2C_IAP_CMD,
+				   ETP_I2C_IAP_PASSWORD);
+	if (error) {
+		dev_err(&client->dev, "cannot set flash key: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int elan_i2c_prepare_fw_update(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	int error;
+	enum tp_mode mode;
+	u8 val[3];
+	u16 password;
+
+	/* Get FW in which mode	(IAP_MODE/MAIN_MODE)  */
+	error = elan_i2c_iap_get_mode(client, &mode);
+	if (error)
+		return error;
+
+	if (mode == IAP_MODE) {
+		/* Reset IC */
+		error = elan_i2c_iap_reset(client);
+		if (error)
+			return error;
+
+		msleep(30);
+	}
+
+	/* Set flash key*/
+	error = elan_i2c_set_flash_key(client);
+	if (error)
+		return error;
+
+	/* Wait for F/W IAP initialization */
+	msleep(mode == MAIN_MODE ? 100 : 30);
+
+	/* Check if we are in IAP mode or not */
+	error = elan_i2c_iap_get_mode(client, &mode);
+	if (error)
+		return error;
+
+	if (mode == MAIN_MODE) {
+		dev_err(dev, "wrong mode: %d\n", mode);
+		return -EIO;
+	}
+
+	/* Set flash key again */
+	error = elan_i2c_set_flash_key(client);
+	if (error)
+		return error;
+
+	/* Wait for F/W IAP initialization */
+	msleep(30);
+
+	/* read back to check we actually enabled successfully. */
+	error = elan_i2c_read_cmd(client, ETP_I2C_IAP_CMD, val);
+	if (error) {
+		dev_err(dev, "cannot read iap password: %d\n",
+			error);
+		return error;
+	}
+
+	password = le16_to_cpup((__le16 *)val);
+	if (password != ETP_I2C_IAP_PASSWORD) {
+		dev_err(dev, "wrong iap password: 0x%X\n", password);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int elan_i2c_write_fw_block(struct i2c_client *client,
+				   const u8 *page, u16 checksum, int idx)
+{
+	struct device *dev = &client->dev;
+	u8 page_store[ETP_FW_PAGE_SIZE + 4];
+	u8 val[3];
+	u16 result;
+	int ret, error;
+
+	page_store[0] = ETP_I2C_IAP_REG_L;
+	page_store[1] = ETP_I2C_IAP_REG_H;
+	memcpy(&page_store[2], page, ETP_FW_PAGE_SIZE);
+	/* recode checksum at last two bytes */
+	put_unaligned_le16(checksum, &page_store[ETP_FW_PAGE_SIZE + 2]);
+
+	ret = i2c_master_send(client, page_store, sizeof(page_store));
+	if (ret != sizeof(page_store)) {
+		error = ret < 0 ? ret : -EIO;
+		dev_err(dev, "Failed to write page %d: %d\n", idx, error);
+		return error;
+	}
+
+	/* Wait for F/W to update one page ROM data. */
+	msleep(20);
+
+	error = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val);
+	if (error) {
+		dev_err(dev, "Failed to read IAP write result: %d\n", error);
+		return error;
+	}
+
+	result = le16_to_cpup((__le16 *)val);
+	if (result & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) {
+		dev_err(dev, "IAP reports failed write: %04hx\n",
+			result);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int elan_i2c_finish_fw_update(struct i2c_client *client,
+				     struct completion *completion)
+{
+	struct device *dev = &client->dev;
+	long ret;
+	int error;
+	int len;
+	u8 buffer[ETP_I2C_INF_LENGTH];
+
+	reinit_completion(completion);
+	enable_irq(client->irq);
+
+	error = elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD, ETP_I2C_RESET);
+	if (!error)
+		ret = wait_for_completion_interruptible_timeout(completion,
+							msecs_to_jiffies(300));
+	disable_irq(client->irq);
+
+	if (error) {
+		dev_err(dev, "device reset failed: %d\n", error);
+		return error;
+	} else if (ret == 0) {
+		dev_err(dev, "timeout waiting for device reset\n");
+		return -ETIMEDOUT;
+	} else if (ret < 0) {
+		error = ret;
+		dev_err(dev, "error waiting for device reset: %d\n", error);
+		return error;
+	}
+
+	len = i2c_master_recv(client, buffer, ETP_I2C_INF_LENGTH);
+	if (len != ETP_I2C_INF_LENGTH) {
+		error = len < 0 ? len : -EIO;
+		dev_err(dev, "failed to read INT signal: %d (%d)\n",
+			error, len);
+		return error;
+	}
+
+	return 0;
+}
+
+static int elan_i2c_get_report(struct i2c_client *client, u8 *report)
+{
+	int len;
+
+	len = i2c_master_recv(client, report, ETP_I2C_REPORT_LEN);
+	if (len < 0) {
+		dev_err(&client->dev, "failed to read report data: %d\n", len);
+		return len;
+	}
+
+	if (len != ETP_I2C_REPORT_LEN) {
+		dev_err(&client->dev,
+			"wrong report length (%d vs %d expected)\n",
+			len, ETP_I2C_REPORT_LEN);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+const struct elan_transport_ops elan_i2c_ops = {
+	.initialize		= elan_i2c_initialize,
+	.sleep_control		= elan_i2c_sleep_control,
+	.power_control		= elan_i2c_power_control,
+	.set_mode		= elan_i2c_set_mode,
+
+	.calibrate		= elan_i2c_calibrate,
+	.calibrate_result	= elan_i2c_calibrate_result,
+
+	.get_baseline_data	= elan_i2c_get_baseline_data,
+
+	.get_version		= elan_i2c_get_version,
+	.get_sm_version		= elan_i2c_get_sm_version,
+	.get_product_id		= elan_i2c_get_product_id,
+	.get_checksum		= elan_i2c_get_checksum,
+	.get_pressure_adjustment = elan_i2c_get_pressure_adjustment,
+
+	.get_max		= elan_i2c_get_max,
+	.get_resolution		= elan_i2c_get_resolution,
+	.get_num_traces		= elan_i2c_get_num_traces,
+
+	.iap_get_mode		= elan_i2c_iap_get_mode,
+	.iap_reset		= elan_i2c_iap_reset,
+
+	.prepare_fw_update	= elan_i2c_prepare_fw_update,
+	.write_fw_block		= elan_i2c_write_fw_block,
+	.finish_fw_update	= elan_i2c_finish_fw_update,
+
+	.get_report		= elan_i2c_get_report,
+};
diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c
new file mode 100644
index 0000000..cb6aecb
--- /dev/null
+++ b/drivers/input/mouse/elan_i2c_smbus.c
@@ -0,0 +1,523 @@
+/*
+ * Elan I2C/SMBus Touchpad driver - SMBus interface
+ *
+ * Copyright (c) 2013 ELAN Microelectronics Corp.
+ *
+ * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
+ *
+ * Based on cyapa driver:
+ * copyright (c) 2011-2012 Cypress Semiconductor, Inc.
+ * copyright (c) 2011-2012 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include "elan_i2c.h"
+
+/* Elan SMbus commands */
+#define ETP_SMBUS_IAP_CMD		0x00
+#define ETP_SMBUS_ENABLE_TP		0x20
+#define ETP_SMBUS_SLEEP_CMD		0x21
+#define ETP_SMBUS_IAP_PASSWORD_WRITE	0x29
+#define ETP_SMBUS_IAP_PASSWORD_READ	0x80
+#define ETP_SMBUS_WRITE_FW_BLOCK	0x2A
+#define ETP_SMBUS_IAP_RESET_CMD		0x2B
+#define ETP_SMBUS_RANGE_CMD		0xA0
+#define ETP_SMBUS_FW_VERSION_CMD	0xA1
+#define ETP_SMBUS_XY_TRACENUM_CMD	0xA2
+#define ETP_SMBUS_SM_VERSION_CMD	0xA3
+#define ETP_SMBUS_UNIQUEID_CMD		0xA3
+#define ETP_SMBUS_RESOLUTION_CMD	0xA4
+#define ETP_SMBUS_HELLOPACKET_CMD	0xA7
+#define ETP_SMBUS_PACKET_QUERY		0xA8
+#define ETP_SMBUS_IAP_VERSION_CMD	0xAC
+#define ETP_SMBUS_IAP_CTRL_CMD		0xAD
+#define ETP_SMBUS_IAP_CHECKSUM_CMD	0xAE
+#define ETP_SMBUS_FW_CHECKSUM_CMD	0xAF
+#define ETP_SMBUS_MAX_BASELINE_CMD	0xC3
+#define ETP_SMBUS_MIN_BASELINE_CMD	0xC4
+#define ETP_SMBUS_CALIBRATE_QUERY	0xC5
+
+#define ETP_SMBUS_REPORT_LEN		32
+#define ETP_SMBUS_REPORT_OFFSET		2
+#define ETP_SMBUS_HELLOPACKET_LEN	5
+#define ETP_SMBUS_IAP_PASSWORD		0x1234
+#define ETP_SMBUS_IAP_MODE_ON		(1 << 6)
+
+static int elan_smbus_initialize(struct i2c_client *client)
+{
+	u8 check[ETP_SMBUS_HELLOPACKET_LEN] = { 0x55, 0x55, 0x55, 0x55, 0x55 };
+	u8 values[ETP_SMBUS_HELLOPACKET_LEN] = { 0, 0, 0, 0, 0 };
+	int len, error;
+
+	/* Get hello packet */
+	len = i2c_smbus_read_block_data(client,
+					ETP_SMBUS_HELLOPACKET_CMD, values);
+	if (len != ETP_SMBUS_HELLOPACKET_LEN) {
+		dev_err(&client->dev, "hello packet length fail: %d\n", len);
+		error = len < 0 ? len : -EIO;
+		return error;
+	}
+
+	/* compare hello packet */
+	if (memcmp(values, check, ETP_SMBUS_HELLOPACKET_LEN)) {
+		dev_err(&client->dev, "hello packet fail [%*ph]\n",
+			ETP_SMBUS_HELLOPACKET_LEN, values);
+		return -ENXIO;
+	}
+
+	/* enable tp */
+	error = i2c_smbus_write_byte(client, ETP_SMBUS_ENABLE_TP);
+	if (error) {
+		dev_err(&client->dev, "failed to enable touchpad: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int elan_smbus_set_mode(struct i2c_client *client, u8 mode)
+{
+	u8 cmd[4] = { 0x00, 0x07, 0x00, mode };
+
+	return i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD,
+					  sizeof(cmd), cmd);
+}
+
+static int elan_smbus_sleep_control(struct i2c_client *client, bool sleep)
+{
+	if (sleep)
+		return i2c_smbus_write_byte(client, ETP_SMBUS_SLEEP_CMD);
+	else
+		return 0; /* XXX should we send ETP_SMBUS_ENABLE_TP here? */
+}
+
+static int elan_smbus_power_control(struct i2c_client *client, bool enable)
+{
+	return 0; /* A no-op */
+}
+
+static int elan_smbus_calibrate(struct i2c_client *client)
+{
+	u8 cmd[4] = { 0x00, 0x08, 0x00, 0x01 };
+
+	return i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD,
+					  sizeof(cmd), cmd);
+}
+
+static int elan_smbus_calibrate_result(struct i2c_client *client, u8 *val)
+{
+	int error;
+
+	error = i2c_smbus_read_block_data(client,
+					  ETP_SMBUS_CALIBRATE_QUERY, val);
+	if (error < 0)
+		return error;
+
+	return 0;
+}
+
+static int elan_smbus_get_baseline_data(struct i2c_client *client,
+					bool max_baseline, u8 *value)
+{
+	int error;
+	u8 val[3];
+
+	error = i2c_smbus_read_block_data(client,
+					  max_baseline ?
+						ETP_SMBUS_MAX_BASELINE_CMD :
+						ETP_SMBUS_MIN_BASELINE_CMD,
+					  val);
+	if (error < 0)
+		return error;
+
+	*value = be16_to_cpup((__be16 *)val);
+
+	return 0;
+}
+
+static int elan_smbus_get_version(struct i2c_client *client,
+				  bool iap, u8 *version)
+{
+	int error;
+	u8 val[3];
+
+	error = i2c_smbus_read_block_data(client,
+					  iap ? ETP_SMBUS_IAP_VERSION_CMD :
+						ETP_SMBUS_FW_VERSION_CMD,
+					  val);
+	if (error < 0) {
+		dev_err(&client->dev, "failed to get %s version: %d\n",
+			iap ? "IAP" : "FW", error);
+		return error;
+	}
+
+	*version = val[2];
+	return 0;
+}
+
+static int elan_smbus_get_sm_version(struct i2c_client *client,
+				     u8 *ic_type, u8 *version)
+{
+	int error;
+	u8 val[3];
+
+	error = i2c_smbus_read_block_data(client,
+					  ETP_SMBUS_SM_VERSION_CMD, val);
+	if (error < 0) {
+		dev_err(&client->dev, "failed to get SM version: %d\n", error);
+		return error;
+	}
+
+	*version = val[0];
+	*ic_type = val[1];
+	return 0;
+}
+
+static int elan_smbus_get_product_id(struct i2c_client *client, u16 *id)
+{
+	int error;
+	u8 val[3];
+
+	error = i2c_smbus_read_block_data(client,
+					  ETP_SMBUS_UNIQUEID_CMD, val);
+	if (error < 0) {
+		dev_err(&client->dev, "failed to get product ID: %d\n", error);
+		return error;
+	}
+
+	*id = be16_to_cpup((__be16 *)val);
+	return 0;
+}
+
+static int elan_smbus_get_checksum(struct i2c_client *client,
+				   bool iap, u16 *csum)
+{
+	int error;
+	u8 val[3];
+
+	error = i2c_smbus_read_block_data(client,
+					  iap ? ETP_SMBUS_FW_CHECKSUM_CMD :
+						ETP_SMBUS_IAP_CHECKSUM_CMD,
+					  val);
+	if (error < 0) {
+		dev_err(&client->dev, "failed to get %s checksum: %d\n",
+			iap ? "IAP" : "FW", error);
+		return error;
+	}
+
+	*csum = be16_to_cpup((__be16 *)val);
+	return 0;
+}
+
+static int elan_smbus_get_max(struct i2c_client *client,
+			      unsigned int *max_x, unsigned int *max_y)
+{
+	int error;
+	u8 val[3];
+
+	error = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val);
+	if (error) {
+		dev_err(&client->dev, "failed to get dimensions: %d\n", error);
+		return error;
+	}
+
+	*max_x = (0x0f & val[0]) << 8 | val[1];
+	*max_y = (0xf0 & val[0]) << 4 | val[2];
+
+	return 0;
+}
+
+static int elan_smbus_get_resolution(struct i2c_client *client,
+				     u8 *hw_res_x, u8 *hw_res_y)
+{
+	int error;
+	u8 val[3];
+
+	error = i2c_smbus_read_block_data(client,
+					  ETP_SMBUS_RESOLUTION_CMD, val);
+	if (error) {
+		dev_err(&client->dev, "failed to get resolution: %d\n", error);
+		return error;
+	}
+
+	*hw_res_x = val[1] & 0x0F;
+	*hw_res_y = (val[1] & 0xF0) >> 4;
+
+	return 0;
+}
+
+static int elan_smbus_get_num_traces(struct i2c_client *client,
+				     unsigned int *x_traces,
+				     unsigned int *y_traces)
+{
+	int error;
+	u8 val[3];
+
+	error = i2c_smbus_read_block_data(client,
+					  ETP_SMBUS_XY_TRACENUM_CMD, val);
+	if (error) {
+		dev_err(&client->dev, "failed to get trace info: %d\n", error);
+		return error;
+	}
+
+	*x_traces = val[1];
+	*y_traces = val[2];
+
+	return 0;
+}
+
+static int elan_smbus_get_pressure_adjustment(struct i2c_client *client,
+					      int *adjustment)
+{
+	*adjustment = ETP_PRESSURE_OFFSET;
+	return 0;
+}
+
+static int elan_smbus_iap_get_mode(struct i2c_client *client,
+				   enum tp_mode *mode)
+{
+	int error;
+	u16 constant;
+	u8 val[3];
+
+	error = i2c_smbus_read_block_data(client, ETP_SMBUS_IAP_CTRL_CMD, val);
+	if (error < 0) {
+		dev_err(&client->dev, "failed to read iap ctrol register: %d\n",
+			error);
+		return error;
+	}
+
+	constant = be16_to_cpup((__be16 *)val);
+	dev_dbg(&client->dev, "iap control reg: 0x%04x.\n", constant);
+
+	*mode = (constant & ETP_SMBUS_IAP_MODE_ON) ? IAP_MODE : MAIN_MODE;
+
+	return 0;
+}
+
+static int elan_smbus_iap_reset(struct i2c_client *client)
+{
+	int error;
+
+	error = i2c_smbus_write_byte(client, ETP_SMBUS_IAP_RESET_CMD);
+	if (error) {
+		dev_err(&client->dev, "cannot reset IC: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int elan_smbus_set_flash_key(struct i2c_client *client)
+{
+	int error;
+	u8 cmd[4] = { 0x00, 0x0B, 0x00, 0x5A };
+
+	error = i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD,
+					   sizeof(cmd), cmd);
+	if (error) {
+		dev_err(&client->dev, "cannot set flash key: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int elan_smbus_prepare_fw_update(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	int len;
+	int error;
+	enum tp_mode mode;
+	u8 val[3];
+	u8 cmd[4] = {0x0F, 0x78, 0x00, 0x06};
+	u16 password;
+
+	/* Get FW in which mode	(IAP_MODE/MAIN_MODE)  */
+	error = elan_smbus_iap_get_mode(client, &mode);
+	if (error)
+		return error;
+
+	if (mode == MAIN_MODE) {
+
+		/* set flash key */
+		error = elan_smbus_set_flash_key(client);
+		if (error)
+			return error;
+
+		/* write iap password */
+		if (i2c_smbus_write_byte(client,
+					 ETP_SMBUS_IAP_PASSWORD_WRITE) < 0) {
+			dev_err(dev, "cannot write iap password\n");
+			return -EIO;
+		}
+
+		error = i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD,
+						   sizeof(cmd), cmd);
+		if (error) {
+			dev_err(dev, "failed to write iap password: %d\n",
+				error);
+			return error;
+		}
+
+		/*
+		 * Read back password to make sure we enabled flash
+		 * successfully.
+		 */
+		len = i2c_smbus_read_block_data(client,
+						ETP_SMBUS_IAP_PASSWORD_READ,
+						val);
+		if (len < sizeof(u16)) {
+			error = len < 0 ? len : -EIO;
+			dev_err(dev, "failed to read iap password: %d\n",
+				error);
+			return error;
+		}
+
+		password = be16_to_cpup((__be16 *)val);
+		if (password != ETP_SMBUS_IAP_PASSWORD) {
+			dev_err(dev, "wrong iap password = 0x%X\n", password);
+			return -EIO;
+		}
+
+		/* Wait 30ms for MAIN_MODE change to IAP_MODE */
+		msleep(30);
+	}
+
+	error = elan_smbus_set_flash_key(client);
+	if (error)
+		return error;
+
+	/* Reset IC */
+	error = elan_smbus_iap_reset(client);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+
+static int elan_smbus_write_fw_block(struct i2c_client *client,
+				     const u8 *page, u16 checksum, int idx)
+{
+	struct device *dev = &client->dev;
+	int error;
+	u16 result;
+	u8 val[3];
+
+	/*
+	 * Due to the limitation of smbus protocol limiting
+	 * transfer to 32 bytes at a time, we must split block
+	 * in 2 transfers.
+	 */
+	error = i2c_smbus_write_block_data(client,
+					   ETP_SMBUS_WRITE_FW_BLOCK,
+					   ETP_FW_PAGE_SIZE / 2,
+					   page);
+	if (error) {
+		dev_err(dev, "Failed to write page %d (part %d): %d\n",
+			idx, 1, error);
+		return error;
+	}
+
+	error = i2c_smbus_write_block_data(client,
+					   ETP_SMBUS_WRITE_FW_BLOCK,
+					   ETP_FW_PAGE_SIZE / 2,
+					   page + ETP_FW_PAGE_SIZE / 2);
+	if (error) {
+		dev_err(dev, "Failed to write page %d (part %d): %d\n",
+			idx, 2, error);
+		return error;
+	}
+
+
+	/* Wait for F/W to update one page ROM data. */
+	usleep_range(8000, 10000);
+
+	error = i2c_smbus_read_block_data(client,
+					  ETP_SMBUS_IAP_CTRL_CMD, val);
+	if (error < 0) {
+		dev_err(dev, "Failed to read IAP write result: %d\n",
+			error);
+		return error;
+	}
+
+	result = be16_to_cpup((__be16 *)val);
+	if (result & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) {
+		dev_err(dev, "IAP reports failed write: %04hx\n",
+			result);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int elan_smbus_get_report(struct i2c_client *client, u8 *report)
+{
+	int len;
+
+	len = i2c_smbus_read_block_data(client,
+					ETP_SMBUS_PACKET_QUERY,
+					&report[ETP_SMBUS_REPORT_OFFSET]);
+	if (len < 0) {
+		dev_err(&client->dev, "failed to read report data: %d\n", len);
+		return len;
+	}
+
+	if (len != ETP_SMBUS_REPORT_LEN) {
+		dev_err(&client->dev,
+			"wrong report length (%d vs %d expected)\n",
+			len, ETP_SMBUS_REPORT_LEN);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int elan_smbus_finish_fw_update(struct i2c_client *client,
+				       struct completion *fw_completion)
+{
+	/* No special handling unlike I2C transport */
+	return 0;
+}
+
+const struct elan_transport_ops elan_smbus_ops = {
+	.initialize		= elan_smbus_initialize,
+	.sleep_control		= elan_smbus_sleep_control,
+	.power_control		= elan_smbus_power_control,
+	.set_mode		= elan_smbus_set_mode,
+
+	.calibrate		= elan_smbus_calibrate,
+	.calibrate_result	= elan_smbus_calibrate_result,
+
+	.get_baseline_data	= elan_smbus_get_baseline_data,
+
+	.get_version		= elan_smbus_get_version,
+	.get_sm_version		= elan_smbus_get_sm_version,
+	.get_product_id		= elan_smbus_get_product_id,
+	.get_checksum		= elan_smbus_get_checksum,
+	.get_pressure_adjustment = elan_smbus_get_pressure_adjustment,
+
+	.get_max		= elan_smbus_get_max,
+	.get_resolution		= elan_smbus_get_resolution,
+	.get_num_traces		= elan_smbus_get_num_traces,
+
+	.iap_get_mode		= elan_smbus_iap_get_mode,
+	.iap_reset		= elan_smbus_iap_reset,
+
+	.prepare_fw_update	= elan_smbus_prepare_fw_update,
+	.write_fw_block		= elan_smbus_write_fw_block,
+	.finish_fw_update	= elan_smbus_finish_fw_update,
+
+	.get_report		= elan_smbus_get_report,
+};
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
new file mode 100644
index 0000000..6f4dc0f
--- /dev/null
+++ b/drivers/input/mouse/elantech.c
@@ -0,0 +1,1792 @@
+/*
+ * Elantech Touchpad driver (v6)
+ *
+ * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <asm/unaligned.h>
+#include "psmouse.h"
+#include "elantech.h"
+
+#define elantech_debug(fmt, ...)					\
+	do {								\
+		if (etd->debug)						\
+			psmouse_printk(KERN_DEBUG, psmouse,		\
+					fmt, ##__VA_ARGS__);		\
+	} while (0)
+
+/*
+ * Send a Synaptics style sliced query command
+ */
+static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c,
+				unsigned char *param)
+{
+	if (psmouse_sliced_command(psmouse, c) ||
+	    ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
+		psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * V3 and later support this fast command
+ */
+static int elantech_send_cmd(struct psmouse *psmouse, unsigned char c,
+				unsigned char *param)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+	if (ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+	    ps2_command(ps2dev, NULL, c) ||
+	    ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
+		psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * A retrying version of ps2_command
+ */
+static int elantech_ps2_command(struct psmouse *psmouse,
+				unsigned char *param, int command)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	struct elantech_data *etd = psmouse->private;
+	int rc;
+	int tries = ETP_PS2_COMMAND_TRIES;
+
+	do {
+		rc = ps2_command(ps2dev, param, command);
+		if (rc == 0)
+			break;
+		tries--;
+		elantech_debug("retrying ps2 command 0x%02x (%d).\n",
+				command, tries);
+		msleep(ETP_PS2_COMMAND_DELAY);
+	} while (tries > 0);
+
+	if (rc)
+		psmouse_err(psmouse, "ps2 command 0x%02x failed.\n", command);
+
+	return rc;
+}
+
+/*
+ * Send an Elantech style special command to read a value from a register
+ */
+static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
+				unsigned char *val)
+{
+	struct elantech_data *etd = psmouse->private;
+	unsigned char param[3];
+	int rc = 0;
+
+	if (reg < 0x07 || reg > 0x26)
+		return -1;
+
+	if (reg > 0x11 && reg < 0x20)
+		return -1;
+
+	switch (etd->hw_version) {
+	case 1:
+		if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) ||
+		    psmouse_sliced_command(psmouse, reg) ||
+		    ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
+			rc = -1;
+		}
+		break;
+
+	case 2:
+		if (elantech_ps2_command(psmouse,  NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse,  NULL, ETP_REGISTER_READ) ||
+		    elantech_ps2_command(psmouse,  NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse,  NULL, reg) ||
+		    elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) {
+			rc = -1;
+		}
+		break;
+
+	case 3 ... 4:
+		if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
+		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse, NULL, reg) ||
+		    elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) {
+			rc = -1;
+		}
+		break;
+	}
+
+	if (rc)
+		psmouse_err(psmouse, "failed to read register 0x%02x.\n", reg);
+	else if (etd->hw_version != 4)
+		*val = param[0];
+	else
+		*val = param[1];
+
+	return rc;
+}
+
+/*
+ * Send an Elantech style special command to write a register with a value
+ */
+static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
+				unsigned char val)
+{
+	struct elantech_data *etd = psmouse->private;
+	int rc = 0;
+
+	if (reg < 0x07 || reg > 0x26)
+		return -1;
+
+	if (reg > 0x11 && reg < 0x20)
+		return -1;
+
+	switch (etd->hw_version) {
+	case 1:
+		if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) ||
+		    psmouse_sliced_command(psmouse, reg) ||
+		    psmouse_sliced_command(psmouse, val) ||
+		    ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) {
+			rc = -1;
+		}
+		break;
+
+	case 2:
+		if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) ||
+		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse, NULL, reg) ||
+		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse, NULL, val) ||
+		    elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
+			rc = -1;
+		}
+		break;
+
+	case 3:
+		if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
+		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse, NULL, reg) ||
+		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse, NULL, val) ||
+		    elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
+			rc = -1;
+		}
+		break;
+
+	case 4:
+		if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
+		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse, NULL, reg) ||
+		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) ||
+		    elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+		    elantech_ps2_command(psmouse, NULL, val) ||
+		    elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
+			rc = -1;
+		}
+		break;
+	}
+
+	if (rc)
+		psmouse_err(psmouse,
+			    "failed to write register 0x%02x with value 0x%02x.\n",
+			    reg, val);
+
+	return rc;
+}
+
+/*
+ * Dump a complete mouse movement packet to the syslog
+ */
+static void elantech_packet_dump(struct psmouse *psmouse)
+{
+	int	i;
+
+	psmouse_printk(KERN_DEBUG, psmouse, "PS/2 packet [");
+	for (i = 0; i < psmouse->pktsize; i++)
+		printk("%s0x%02x ", i ? ", " : " ", psmouse->packet[i]);
+	printk("]\n");
+}
+
+/*
+ * Interpret complete data packets and report absolute mode input events for
+ * hardware version 1. (4 byte packets)
+ */
+static void elantech_report_absolute_v1(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct elantech_data *etd = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	int fingers;
+
+	if (etd->fw_version < 0x020000) {
+		/*
+		 * byte 0:  D   U  p1  p2   1  p3   R   L
+		 * byte 1:  f   0  th  tw  x9  x8  y9  y8
+		 */
+		fingers = ((packet[1] & 0x80) >> 7) +
+				((packet[1] & 0x30) >> 4);
+	} else {
+		/*
+		 * byte 0: n1  n0  p2  p1   1  p3   R   L
+		 * byte 1:  0   0   0   0  x9  x8  y9  y8
+		 */
+		fingers = (packet[0] & 0xc0) >> 6;
+	}
+
+	if (etd->jumpy_cursor) {
+		if (fingers != 1) {
+			etd->single_finger_reports = 0;
+		} else if (etd->single_finger_reports < 2) {
+			/* Discard first 2 reports of one finger, bogus */
+			etd->single_finger_reports++;
+			elantech_debug("discarding packet\n");
+			return;
+		}
+	}
+
+	input_report_key(dev, BTN_TOUCH, fingers != 0);
+
+	/*
+	 * byte 2: x7  x6  x5  x4  x3  x2  x1  x0
+	 * byte 3: y7  y6  y5  y4  y3  y2  y1  y0
+	 */
+	if (fingers) {
+		input_report_abs(dev, ABS_X,
+			((packet[1] & 0x0c) << 6) | packet[2]);
+		input_report_abs(dev, ABS_Y,
+			etd->y_max - (((packet[1] & 0x03) << 8) | packet[3]));
+	}
+
+	input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+	input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+	input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+	input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+
+	if (etd->fw_version < 0x020000 &&
+	    (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
+		/* rocker up */
+		input_report_key(dev, BTN_FORWARD, packet[0] & 0x40);
+		/* rocker down */
+		input_report_key(dev, BTN_BACK, packet[0] & 0x80);
+	}
+
+	input_sync(dev);
+}
+
+static void elantech_set_slot(struct input_dev *dev, int slot, bool active,
+			      unsigned int x, unsigned int y)
+{
+	input_mt_slot(dev, slot);
+	input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
+	if (active) {
+		input_report_abs(dev, ABS_MT_POSITION_X, x);
+		input_report_abs(dev, ABS_MT_POSITION_Y, y);
+	}
+}
+
+/* x1 < x2 and y1 < y2 when two fingers, x = y = 0 when not pressed */
+static void elantech_report_semi_mt_data(struct input_dev *dev,
+					 unsigned int num_fingers,
+					 unsigned int x1, unsigned int y1,
+					 unsigned int x2, unsigned int y2)
+{
+	elantech_set_slot(dev, 0, num_fingers != 0, x1, y1);
+	elantech_set_slot(dev, 1, num_fingers >= 2, x2, y2);
+}
+
+/*
+ * Interpret complete data packets and report absolute mode input events for
+ * hardware version 2. (6 byte packets)
+ */
+static void elantech_report_absolute_v2(struct psmouse *psmouse)
+{
+	struct elantech_data *etd = psmouse->private;
+	struct input_dev *dev = psmouse->dev;
+	unsigned char *packet = psmouse->packet;
+	unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+	unsigned int width = 0, pres = 0;
+
+	/* byte 0: n1  n0   .   .   .   .   R   L */
+	fingers = (packet[0] & 0xc0) >> 6;
+
+	switch (fingers) {
+	case 3:
+		/*
+		 * Same as one finger, except report of more than 3 fingers:
+		 * byte 3:  n4  .   w1  w0   .   .   .   .
+		 */
+		if (packet[3] & 0x80)
+			fingers = 4;
+		/* pass through... */
+	case 1:
+		/*
+		 * byte 1:  .   .   .   .  x11 x10 x9  x8
+		 * byte 2: x7  x6  x5  x4  x4  x2  x1  x0
+		 */
+		x1 = ((packet[1] & 0x0f) << 8) | packet[2];
+		/*
+		 * byte 4:  .   .   .   .  y11 y10 y9  y8
+		 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0
+		 */
+		y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
+
+		pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
+		width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4);
+		break;
+
+	case 2:
+		/*
+		 * The coordinate of each finger is reported separately
+		 * with a lower resolution for two finger touches:
+		 * byte 0:  .   .  ay8 ax8  .   .   .   .
+		 * byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0
+		 */
+		x1 = (((packet[0] & 0x10) << 4) | packet[1]) << 2;
+		/* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */
+		y1 = etd->y_max -
+			((((packet[0] & 0x20) << 3) | packet[2]) << 2);
+		/*
+		 * byte 3:  .   .  by8 bx8  .   .   .   .
+		 * byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0
+		 */
+		x2 = (((packet[3] & 0x10) << 4) | packet[4]) << 2;
+		/* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */
+		y2 = etd->y_max -
+			((((packet[3] & 0x20) << 3) | packet[5]) << 2);
+
+		/* Unknown so just report sensible values */
+		pres = 127;
+		width = 7;
+		break;
+	}
+
+	input_report_key(dev, BTN_TOUCH, fingers != 0);
+	if (fingers != 0) {
+		input_report_abs(dev, ABS_X, x1);
+		input_report_abs(dev, ABS_Y, y1);
+	}
+	elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
+	input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+	input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+	input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+	input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
+	input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+	if (etd->reports_pressure) {
+		input_report_abs(dev, ABS_PRESSURE, pres);
+		input_report_abs(dev, ABS_TOOL_WIDTH, width);
+	}
+
+	input_sync(dev);
+}
+
+static void elantech_report_trackpoint(struct psmouse *psmouse,
+				       int packet_type)
+{
+	/*
+	 * byte 0:  0   0  sx  sy   0   M   R   L
+	 * byte 1:~sx   0   0   0   0   0   0   0
+	 * byte 2:~sy   0   0   0   0   0   0   0
+	 * byte 3:  0   0 ~sy ~sx   0   1   1   0
+	 * byte 4: x7  x6  x5  x4  x3  x2  x1  x0
+	 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0
+	 *
+	 * x and y are written in two's complement spread
+	 * over 9 bits with sx/sy the relative top bit and
+	 * x7..x0 and y7..y0 the lower bits.
+	 * The sign of y is opposite to what the input driver
+	 * expects for a relative movement
+	 */
+
+	struct elantech_data *etd = psmouse->private;
+	struct input_dev *tp_dev = etd->tp_dev;
+	unsigned char *packet = psmouse->packet;
+	int x, y;
+	u32 t;
+
+	t = get_unaligned_le32(&packet[0]);
+
+	switch (t & ~7U) {
+	case 0x06000030U:
+	case 0x16008020U:
+	case 0x26800010U:
+	case 0x36808000U:
+		x = packet[4] - (int)((packet[1]^0x80) << 1);
+		y = (int)((packet[2]^0x80) << 1) - packet[5];
+
+		input_report_key(tp_dev, BTN_LEFT, packet[0] & 0x01);
+		input_report_key(tp_dev, BTN_RIGHT, packet[0] & 0x02);
+		input_report_key(tp_dev, BTN_MIDDLE, packet[0] & 0x04);
+
+		input_report_rel(tp_dev, REL_X, x);
+		input_report_rel(tp_dev, REL_Y, y);
+
+		input_sync(tp_dev);
+
+		break;
+
+	default:
+		/* Dump unexpected packet sequences if debug=1 (default) */
+		if (etd->debug == 1)
+			elantech_packet_dump(psmouse);
+
+		break;
+	}
+}
+
+/*
+ * Interpret complete data packets and report absolute mode input events for
+ * hardware version 3. (12 byte packets for two fingers)
+ */
+static void elantech_report_absolute_v3(struct psmouse *psmouse,
+					int packet_type)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct elantech_data *etd = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	unsigned int fingers = 0, x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+	unsigned int width = 0, pres = 0;
+
+	/* byte 0: n1  n0   .   .   .   .   R   L */
+	fingers = (packet[0] & 0xc0) >> 6;
+
+	switch (fingers) {
+	case 3:
+	case 1:
+		/*
+		 * byte 1:  .   .   .   .  x11 x10 x9  x8
+		 * byte 2: x7  x6  x5  x4  x4  x2  x1  x0
+		 */
+		x1 = ((packet[1] & 0x0f) << 8) | packet[2];
+		/*
+		 * byte 4:  .   .   .   .  y11 y10 y9  y8
+		 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0
+		 */
+		y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
+		break;
+
+	case 2:
+		if (packet_type == PACKET_V3_HEAD) {
+			/*
+			 * byte 1:   .    .    .    .  ax11 ax10 ax9  ax8
+			 * byte 2: ax7  ax6  ax5  ax4  ax3  ax2  ax1  ax0
+			 */
+			etd->mt[0].x = ((packet[1] & 0x0f) << 8) | packet[2];
+			/*
+			 * byte 4:   .    .    .    .  ay11 ay10 ay9  ay8
+			 * byte 5: ay7  ay6  ay5  ay4  ay3  ay2  ay1  ay0
+			 */
+			etd->mt[0].y = etd->y_max -
+				(((packet[4] & 0x0f) << 8) | packet[5]);
+			/*
+			 * wait for next packet
+			 */
+			return;
+		}
+
+		/* packet_type == PACKET_V3_TAIL */
+		x1 = etd->mt[0].x;
+		y1 = etd->mt[0].y;
+		x2 = ((packet[1] & 0x0f) << 8) | packet[2];
+		y2 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
+		break;
+	}
+
+	pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
+	width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4);
+
+	input_report_key(dev, BTN_TOUCH, fingers != 0);
+	if (fingers != 0) {
+		input_report_abs(dev, ABS_X, x1);
+		input_report_abs(dev, ABS_Y, y1);
+	}
+	elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
+	input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+	input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+	input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+
+	/* For clickpads map both buttons to BTN_LEFT */
+	if (etd->fw_version & 0x001000) {
+		input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
+	} else {
+		input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+		input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+	}
+
+	input_report_abs(dev, ABS_PRESSURE, pres);
+	input_report_abs(dev, ABS_TOOL_WIDTH, width);
+
+	input_sync(dev);
+}
+
+static void elantech_input_sync_v4(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct elantech_data *etd = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+
+	/* For clickpads map both buttons to BTN_LEFT */
+	if (etd->fw_version & 0x001000) {
+		input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
+	} else {
+		input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+		input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+		input_report_key(dev, BTN_MIDDLE, packet[0] & 0x04);
+	}
+
+	input_mt_report_pointer_emulation(dev, true);
+	input_sync(dev);
+}
+
+static void process_packet_status_v4(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	unsigned char *packet = psmouse->packet;
+	unsigned fingers;
+	int i;
+
+	/* notify finger state change */
+	fingers = packet[1] & 0x1f;
+	for (i = 0; i < ETP_MAX_FINGERS; i++) {
+		if ((fingers & (1 << i)) == 0) {
+			input_mt_slot(dev, i);
+			input_mt_report_slot_state(dev, MT_TOOL_FINGER, false);
+		}
+	}
+
+	elantech_input_sync_v4(psmouse);
+}
+
+static void process_packet_head_v4(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct elantech_data *etd = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	int id = ((packet[3] & 0xe0) >> 5) - 1;
+	int pres, traces;
+
+	if (id < 0)
+		return;
+
+	etd->mt[id].x = ((packet[1] & 0x0f) << 8) | packet[2];
+	etd->mt[id].y = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);
+	pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
+	traces = (packet[0] & 0xf0) >> 4;
+
+	input_mt_slot(dev, id);
+	input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+
+	input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x);
+	input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y);
+	input_report_abs(dev, ABS_MT_PRESSURE, pres);
+	input_report_abs(dev, ABS_MT_TOUCH_MAJOR, traces * etd->width);
+	/* report this for backwards compatibility */
+	input_report_abs(dev, ABS_TOOL_WIDTH, traces);
+
+	elantech_input_sync_v4(psmouse);
+}
+
+static void process_packet_motion_v4(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct elantech_data *etd = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	int weight, delta_x1 = 0, delta_y1 = 0, delta_x2 = 0, delta_y2 = 0;
+	int id, sid;
+
+	id = ((packet[0] & 0xe0) >> 5) - 1;
+	if (id < 0)
+		return;
+
+	sid = ((packet[3] & 0xe0) >> 5) - 1;
+	weight = (packet[0] & 0x10) ? ETP_WEIGHT_VALUE : 1;
+	/*
+	 * Motion packets give us the delta of x, y values of specific fingers,
+	 * but in two's complement. Let the compiler do the conversion for us.
+	 * Also _enlarge_ the numbers to int, in case of overflow.
+	 */
+	delta_x1 = (signed char)packet[1];
+	delta_y1 = (signed char)packet[2];
+	delta_x2 = (signed char)packet[4];
+	delta_y2 = (signed char)packet[5];
+
+	etd->mt[id].x += delta_x1 * weight;
+	etd->mt[id].y -= delta_y1 * weight;
+	input_mt_slot(dev, id);
+	input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x);
+	input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y);
+
+	if (sid >= 0) {
+		etd->mt[sid].x += delta_x2 * weight;
+		etd->mt[sid].y -= delta_y2 * weight;
+		input_mt_slot(dev, sid);
+		input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[sid].x);
+		input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[sid].y);
+	}
+
+	elantech_input_sync_v4(psmouse);
+}
+
+static void elantech_report_absolute_v4(struct psmouse *psmouse,
+					int packet_type)
+{
+	switch (packet_type) {
+	case PACKET_V4_STATUS:
+		process_packet_status_v4(psmouse);
+		break;
+
+	case PACKET_V4_HEAD:
+		process_packet_head_v4(psmouse);
+		break;
+
+	case PACKET_V4_MOTION:
+		process_packet_motion_v4(psmouse);
+		break;
+
+	case PACKET_UNKNOWN:
+	default:
+		/* impossible to get here */
+		break;
+	}
+}
+
+static int elantech_packet_check_v1(struct psmouse *psmouse)
+{
+	struct elantech_data *etd = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	unsigned char p1, p2, p3;
+
+	/* Parity bits are placed differently */
+	if (etd->fw_version < 0x020000) {
+		/* byte 0:  D   U  p1  p2   1  p3   R   L */
+		p1 = (packet[0] & 0x20) >> 5;
+		p2 = (packet[0] & 0x10) >> 4;
+	} else {
+		/* byte 0: n1  n0  p2  p1   1  p3   R   L */
+		p1 = (packet[0] & 0x10) >> 4;
+		p2 = (packet[0] & 0x20) >> 5;
+	}
+
+	p3 = (packet[0] & 0x04) >> 2;
+
+	return etd->parity[packet[1]] == p1 &&
+	       etd->parity[packet[2]] == p2 &&
+	       etd->parity[packet[3]] == p3;
+}
+
+static int elantech_debounce_check_v2(struct psmouse *psmouse)
+{
+        /*
+         * When we encounter packet that matches this exactly, it means the
+         * hardware is in debounce status. Just ignore the whole packet.
+         */
+        const u8 debounce_packet[] = { 0x84, 0xff, 0xff, 0x02, 0xff, 0xff };
+        unsigned char *packet = psmouse->packet;
+
+        return !memcmp(packet, debounce_packet, sizeof(debounce_packet));
+}
+
+static int elantech_packet_check_v2(struct psmouse *psmouse)
+{
+	struct elantech_data *etd = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+
+	/*
+	 * V2 hardware has two flavors. Older ones that do not report pressure,
+	 * and newer ones that reports pressure and width. With newer ones, all
+	 * packets (1, 2, 3 finger touch) have the same constant bits. With
+	 * older ones, 1/3 finger touch packets and 2 finger touch packets
+	 * have different constant bits.
+	 * With all three cases, if the constant bits are not exactly what I
+	 * expected, I consider them invalid.
+	 */
+	if (etd->reports_pressure)
+		return (packet[0] & 0x0c) == 0x04 &&
+		       (packet[3] & 0x0f) == 0x02;
+
+	if ((packet[0] & 0xc0) == 0x80)
+		return (packet[0] & 0x0c) == 0x0c &&
+		       (packet[3] & 0x0e) == 0x08;
+
+	return (packet[0] & 0x3c) == 0x3c &&
+	       (packet[1] & 0xf0) == 0x00 &&
+	       (packet[3] & 0x3e) == 0x38 &&
+	       (packet[4] & 0xf0) == 0x00;
+}
+
+/*
+ * We check the constant bits to determine what packet type we get,
+ * so packet checking is mandatory for v3 and later hardware.
+ */
+static int elantech_packet_check_v3(struct psmouse *psmouse)
+{
+	struct elantech_data *etd = psmouse->private;
+	const u8 debounce_packet[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff };
+	unsigned char *packet = psmouse->packet;
+
+	/*
+	 * check debounce first, it has the same signature in byte 0
+	 * and byte 3 as PACKET_V3_HEAD.
+	 */
+	if (!memcmp(packet, debounce_packet, sizeof(debounce_packet)))
+		return PACKET_DEBOUNCE;
+
+	/*
+	 * If the hardware flag 'crc_enabled' is set the packets have
+	 * different signatures.
+	 */
+	if (etd->crc_enabled) {
+		if ((packet[3] & 0x09) == 0x08)
+			return PACKET_V3_HEAD;
+
+		if ((packet[3] & 0x09) == 0x09)
+			return PACKET_V3_TAIL;
+	} else {
+		if ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0xcf) == 0x02)
+			return PACKET_V3_HEAD;
+
+		if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c)
+			return PACKET_V3_TAIL;
+		if ((packet[3] & 0x0f) == 0x06)
+			return PACKET_TRACKPOINT;
+	}
+
+	return PACKET_UNKNOWN;
+}
+
+static int elantech_packet_check_v4(struct psmouse *psmouse)
+{
+	struct elantech_data *etd = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	unsigned char packet_type = packet[3] & 0x03;
+	unsigned int ic_version;
+	bool sanity_check;
+
+	if (etd->tp_dev && (packet[3] & 0x0f) == 0x06)
+		return PACKET_TRACKPOINT;
+
+	/* This represents the version of IC body. */
+	ic_version = (etd->fw_version & 0x0f0000) >> 16;
+
+	/*
+	 * Sanity check based on the constant bits of a packet.
+	 * The constant bits change depending on the value of
+	 * the hardware flag 'crc_enabled' and the version of
+	 * the IC body, but are the same for every packet,
+	 * regardless of the type.
+	 */
+	if (etd->crc_enabled)
+		sanity_check = ((packet[3] & 0x08) == 0x00);
+	else if (ic_version == 7 && etd->samples[1] == 0x2A)
+		sanity_check = ((packet[3] & 0x1c) == 0x10);
+	else
+		sanity_check = ((packet[0] & 0x0c) == 0x04 &&
+				(packet[3] & 0x1c) == 0x10);
+
+	if (!sanity_check)
+		return PACKET_UNKNOWN;
+
+	switch (packet_type) {
+	case 0:
+		return PACKET_V4_STATUS;
+
+	case 1:
+		return PACKET_V4_HEAD;
+
+	case 2:
+		return PACKET_V4_MOTION;
+	}
+
+	return PACKET_UNKNOWN;
+}
+
+/*
+ * Process byte stream from mouse and handle complete packets
+ */
+static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
+{
+	struct elantech_data *etd = psmouse->private;
+	int packet_type;
+
+	if (psmouse->pktcnt < psmouse->pktsize)
+		return PSMOUSE_GOOD_DATA;
+
+	if (etd->debug > 1)
+		elantech_packet_dump(psmouse);
+
+	switch (etd->hw_version) {
+	case 1:
+		if (etd->paritycheck && !elantech_packet_check_v1(psmouse))
+			return PSMOUSE_BAD_DATA;
+
+		elantech_report_absolute_v1(psmouse);
+		break;
+
+	case 2:
+		/* ignore debounce */
+		if (elantech_debounce_check_v2(psmouse))
+			return PSMOUSE_FULL_PACKET;
+
+		if (etd->paritycheck && !elantech_packet_check_v2(psmouse))
+			return PSMOUSE_BAD_DATA;
+
+		elantech_report_absolute_v2(psmouse);
+		break;
+
+	case 3:
+		packet_type = elantech_packet_check_v3(psmouse);
+		switch (packet_type) {
+		case PACKET_UNKNOWN:
+			return PSMOUSE_BAD_DATA;
+
+		case PACKET_DEBOUNCE:
+			/* ignore debounce */
+			break;
+
+		case PACKET_TRACKPOINT:
+			elantech_report_trackpoint(psmouse, packet_type);
+			break;
+
+		default:
+			elantech_report_absolute_v3(psmouse, packet_type);
+			break;
+		}
+
+		break;
+
+	case 4:
+		packet_type = elantech_packet_check_v4(psmouse);
+		switch (packet_type) {
+		case PACKET_UNKNOWN:
+			return PSMOUSE_BAD_DATA;
+
+		case PACKET_TRACKPOINT:
+			elantech_report_trackpoint(psmouse, packet_type);
+			break;
+
+		default:
+			elantech_report_absolute_v4(psmouse, packet_type);
+			break;
+		}
+
+		break;
+	}
+
+	return PSMOUSE_FULL_PACKET;
+}
+
+/*
+ * This writes the reg_07 value again to the hardware at the end of every
+ * set_rate call because the register loses its value. reg_07 allows setting
+ * absolute mode on v4 hardware
+ */
+static void elantech_set_rate_restore_reg_07(struct psmouse *psmouse,
+		unsigned int rate)
+{
+	struct elantech_data *etd = psmouse->private;
+
+	etd->original_set_rate(psmouse, rate);
+	if (elantech_write_reg(psmouse, 0x07, etd->reg_07))
+		psmouse_err(psmouse, "restoring reg_07 failed\n");
+}
+
+/*
+ * Put the touchpad into absolute mode
+ */
+static int elantech_set_absolute_mode(struct psmouse *psmouse)
+{
+	struct elantech_data *etd = psmouse->private;
+	unsigned char val;
+	int tries = ETP_READ_BACK_TRIES;
+	int rc = 0;
+
+	switch (etd->hw_version) {
+	case 1:
+		etd->reg_10 = 0x16;
+		etd->reg_11 = 0x8f;
+		if (elantech_write_reg(psmouse, 0x10, etd->reg_10) ||
+		    elantech_write_reg(psmouse, 0x11, etd->reg_11)) {
+			rc = -1;
+		}
+		break;
+
+	case 2:
+					/* Windows driver values */
+		etd->reg_10 = 0x54;
+		etd->reg_11 = 0x88;	/* 0x8a */
+		etd->reg_21 = 0x60;	/* 0x00 */
+		if (elantech_write_reg(psmouse, 0x10, etd->reg_10) ||
+		    elantech_write_reg(psmouse, 0x11, etd->reg_11) ||
+		    elantech_write_reg(psmouse, 0x21, etd->reg_21)) {
+			rc = -1;
+		}
+		break;
+
+	case 3:
+		if (etd->set_hw_resolution)
+			etd->reg_10 = 0x0b;
+		else
+			etd->reg_10 = 0x01;
+
+		if (elantech_write_reg(psmouse, 0x10, etd->reg_10))
+			rc = -1;
+
+		break;
+
+	case 4:
+		etd->reg_07 = 0x01;
+		if (elantech_write_reg(psmouse, 0x07, etd->reg_07))
+			rc = -1;
+
+		goto skip_readback_reg_10; /* v4 has no reg 0x10 to read */
+	}
+
+	if (rc == 0) {
+		/*
+		 * Read back reg 0x10. For hardware version 1 we must make
+		 * sure the absolute mode bit is set. For hardware version 2
+		 * the touchpad is probably initializing and not ready until
+		 * we read back the value we just wrote.
+		 */
+		do {
+			rc = elantech_read_reg(psmouse, 0x10, &val);
+			if (rc == 0)
+				break;
+			tries--;
+			elantech_debug("retrying read (%d).\n", tries);
+			msleep(ETP_READ_BACK_DELAY);
+		} while (tries > 0);
+
+		if (rc) {
+			psmouse_err(psmouse,
+				    "failed to read back register 0x10.\n");
+		} else if (etd->hw_version == 1 &&
+			   !(val & ETP_R10_ABSOLUTE_MODE)) {
+			psmouse_err(psmouse,
+				    "touchpad refuses to switch to absolute mode.\n");
+			rc = -1;
+		}
+	}
+
+ skip_readback_reg_10:
+	if (rc)
+		psmouse_err(psmouse, "failed to initialise registers.\n");
+
+	return rc;
+}
+
+static int elantech_set_range(struct psmouse *psmouse,
+			      unsigned int *x_min, unsigned int *y_min,
+			      unsigned int *x_max, unsigned int *y_max,
+			      unsigned int *width)
+{
+	struct elantech_data *etd = psmouse->private;
+	unsigned char param[3];
+	unsigned char traces;
+
+	switch (etd->hw_version) {
+	case 1:
+		*x_min = ETP_XMIN_V1;
+		*y_min = ETP_YMIN_V1;
+		*x_max = ETP_XMAX_V1;
+		*y_max = ETP_YMAX_V1;
+		break;
+
+	case 2:
+		if (etd->fw_version == 0x020800 ||
+		    etd->fw_version == 0x020b00 ||
+		    etd->fw_version == 0x020030) {
+			*x_min = ETP_XMIN_V2;
+			*y_min = ETP_YMIN_V2;
+			*x_max = ETP_XMAX_V2;
+			*y_max = ETP_YMAX_V2;
+		} else {
+			int i;
+			int fixed_dpi;
+
+			i = (etd->fw_version > 0x020800 &&
+			     etd->fw_version < 0x020900) ? 1 : 2;
+
+			if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+				return -1;
+
+			fixed_dpi = param[1] & 0x10;
+
+			if (((etd->fw_version >> 16) == 0x14) && fixed_dpi) {
+				if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
+					return -1;
+
+				*x_max = (etd->capabilities[1] - i) * param[1] / 2;
+				*y_max = (etd->capabilities[2] - i) * param[2] / 2;
+			} else if (etd->fw_version == 0x040216) {
+				*x_max = 819;
+				*y_max = 405;
+			} else if (etd->fw_version == 0x040219 || etd->fw_version == 0x040215) {
+				*x_max = 900;
+				*y_max = 500;
+			} else {
+				*x_max = (etd->capabilities[1] - i) * 64;
+				*y_max = (etd->capabilities[2] - i) * 64;
+			}
+		}
+		break;
+
+	case 3:
+		if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+			return -1;
+
+		*x_max = (0x0f & param[0]) << 8 | param[1];
+		*y_max = (0xf0 & param[0]) << 4 | param[2];
+		break;
+
+	case 4:
+		if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+			return -1;
+
+		*x_max = (0x0f & param[0]) << 8 | param[1];
+		*y_max = (0xf0 & param[0]) << 4 | param[2];
+		traces = etd->capabilities[1];
+		if ((traces < 2) || (traces > *x_max))
+			return -1;
+
+		*width = *x_max / (traces - 1);
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * (value from firmware) * 10 + 790 = dpi
+ * we also have to convert dpi to dots/mm (*10/254 to avoid floating point)
+ */
+static unsigned int elantech_convert_res(unsigned int val)
+{
+	return (val * 10 + 790) * 10 / 254;
+}
+
+static int elantech_get_resolution_v4(struct psmouse *psmouse,
+				      unsigned int *x_res,
+				      unsigned int *y_res)
+{
+	unsigned char param[3];
+
+	if (elantech_send_cmd(psmouse, ETP_RESOLUTION_QUERY, param))
+		return -1;
+
+	*x_res = elantech_convert_res(param[1] & 0x0f);
+	*y_res = elantech_convert_res((param[1] & 0xf0) >> 4);
+
+	return 0;
+}
+
+/*
+ * Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in
+ * fw_version for this is based on the following fw_version & caps table:
+ *
+ * Laptop-model:           fw_version:     caps:           buttons:
+ * Acer S3                 0x461f00        10, 13, 0e      clickpad
+ * Acer S7-392             0x581f01        50, 17, 0d      clickpad
+ * Acer V5-131             0x461f02        01, 16, 0c      clickpad
+ * Acer V5-551             0x461f00        ?               clickpad
+ * Asus K53SV              0x450f01        78, 15, 0c      2 hw buttons
+ * Asus G46VW              0x460f02        00, 18, 0c      2 hw buttons
+ * Asus G750JX             0x360f00        00, 16, 0c      2 hw buttons
+ * Asus TP500LN            0x381f17        10, 14, 0e      clickpad
+ * Asus X750JN             0x381f17        10, 14, 0e      clickpad
+ * Asus UX31               0x361f00        20, 15, 0e      clickpad
+ * Asus UX32VD             0x361f02        00, 15, 0e      clickpad
+ * Avatar AVIU-145A2       0x361f00        ?               clickpad
+ * Fujitsu LIFEBOOK E544   0x470f00        d0, 12, 09      2 hw buttons
+ * Fujitsu LIFEBOOK E546   0x470f00        50, 12, 09      2 hw buttons
+ * Fujitsu LIFEBOOK E547   0x470f00        50, 12, 09      2 hw buttons
+ * Fujitsu LIFEBOOK E554   0x570f01        40, 14, 0c      2 hw buttons
+ * Fujitsu LIFEBOOK E557   0x570f01        40, 14, 0c      2 hw buttons
+ * Fujitsu T725            0x470f01        05, 12, 09      2 hw buttons
+ * Fujitsu H730            0x570f00        c0, 14, 0c      3 hw buttons (**)
+ * Gigabyte U2442          0x450f01        58, 17, 0c      2 hw buttons
+ * Lenovo L430             0x350f02        b9, 15, 0c      2 hw buttons (*)
+ * Lenovo L530             0x350f02        b9, 15, 0c      2 hw buttons (*)
+ * Samsung NF210           0x150b00        78, 14, 0a      2 hw buttons
+ * Samsung NP770Z5E        0x575f01        10, 15, 0f      clickpad
+ * Samsung NP700Z5B        0x361f06        21, 15, 0f      clickpad
+ * Samsung NP900X3E-A02    0x575f03        ?               clickpad
+ * Samsung NP-QX410        0x851b00        19, 14, 0c      clickpad
+ * Samsung RC512           0x450f00        08, 15, 0c      2 hw buttons
+ * Samsung RF710           0x450f00        ?               2 hw buttons
+ * System76 Pangolin       0x250f01        ?               2 hw buttons
+ * (*) + 3 trackpoint buttons
+ * (**) + 0 trackpoint buttons
+ * Note: Lenovo L430 and Lenovo L430 have the same fw_version/caps
+ */
+static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct elantech_data *etd = psmouse->private;
+
+	if (etd->fw_version & 0x001000) {
+		__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
+		__clear_bit(BTN_RIGHT, dev->keybit);
+	}
+}
+
+/*
+ * Some hw_version 4 models do have a middle button
+ */
+static const struct dmi_system_id elantech_dmi_has_middle_button[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+	{
+		/* Fujitsu H730 has a middle button */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
+		},
+	},
+	{
+		/* Fujitsu H760 also has a middle button */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
+		},
+	},
+#endif
+	{ }
+};
+
+/*
+ * Set the appropriate event bits for the input subsystem
+ */
+static int elantech_set_input_params(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct elantech_data *etd = psmouse->private;
+	unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0;
+	unsigned int x_res = 31, y_res = 31;
+
+	if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width))
+		return -1;
+
+	__set_bit(INPUT_PROP_POINTER, dev->propbit);
+	__set_bit(EV_KEY, dev->evbit);
+	__set_bit(EV_ABS, dev->evbit);
+	__clear_bit(EV_REL, dev->evbit);
+
+	__set_bit(BTN_LEFT, dev->keybit);
+	if (dmi_check_system(elantech_dmi_has_middle_button))
+		__set_bit(BTN_MIDDLE, dev->keybit);
+	__set_bit(BTN_RIGHT, dev->keybit);
+
+	__set_bit(BTN_TOUCH, dev->keybit);
+	__set_bit(BTN_TOOL_FINGER, dev->keybit);
+	__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+	__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+
+	switch (etd->hw_version) {
+	case 1:
+		/* Rocker button */
+		if (etd->fw_version < 0x020000 &&
+		    (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
+			__set_bit(BTN_FORWARD, dev->keybit);
+			__set_bit(BTN_BACK, dev->keybit);
+		}
+		input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
+		input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
+		break;
+
+	case 2:
+		__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
+		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
+		/* fall through */
+	case 3:
+		if (etd->hw_version == 3)
+			elantech_set_buttonpad_prop(psmouse);
+		input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
+		input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
+		if (etd->reports_pressure) {
+			input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
+					     ETP_PMAX_V2, 0, 0);
+			input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
+					     ETP_WMAX_V2, 0, 0);
+		}
+		input_mt_init_slots(dev, 2, INPUT_MT_SEMI_MT);
+		input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
+		input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+		break;
+
+	case 4:
+		if (elantech_get_resolution_v4(psmouse, &x_res, &y_res)) {
+			/*
+			 * if query failed, print a warning and leave the values
+			 * zero to resemble synaptics.c behavior.
+			 */
+			psmouse_warn(psmouse, "couldn't query resolution data.\n");
+		}
+		elantech_set_buttonpad_prop(psmouse);
+		__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
+		/* For X to recognize me as touchpad. */
+		input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
+		input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
+		/*
+		 * range of pressure and width is the same as v2,
+		 * report ABS_PRESSURE, ABS_TOOL_WIDTH for compatibility.
+		 */
+		input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
+				     ETP_PMAX_V2, 0, 0);
+		input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
+				     ETP_WMAX_V2, 0, 0);
+		/* Multitouch capable pad, up to 5 fingers. */
+		input_mt_init_slots(dev, ETP_MAX_FINGERS, 0);
+		input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
+		input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+		input_set_abs_params(dev, ABS_MT_PRESSURE, ETP_PMIN_V2,
+				     ETP_PMAX_V2, 0, 0);
+		/*
+		 * The firmware reports how many trace lines the finger spans,
+		 * convert to surface unit as Protocol-B requires.
+		 */
+		input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0,
+				     ETP_WMAX_V2 * width, 0, 0);
+		break;
+	}
+
+	input_abs_set_res(dev, ABS_X, x_res);
+	input_abs_set_res(dev, ABS_Y, y_res);
+	if (etd->hw_version > 1) {
+		input_abs_set_res(dev, ABS_MT_POSITION_X, x_res);
+		input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res);
+	}
+
+	etd->y_max = y_max;
+	etd->width = width;
+
+	return 0;
+}
+
+struct elantech_attr_data {
+	size_t		field_offset;
+	unsigned char	reg;
+};
+
+/*
+ * Display a register value by reading a sysfs entry
+ */
+static ssize_t elantech_show_int_attr(struct psmouse *psmouse, void *data,
+					char *buf)
+{
+	struct elantech_data *etd = psmouse->private;
+	struct elantech_attr_data *attr = data;
+	unsigned char *reg = (unsigned char *) etd + attr->field_offset;
+	int rc = 0;
+
+	if (attr->reg)
+		rc = elantech_read_reg(psmouse, attr->reg, reg);
+
+	return sprintf(buf, "0x%02x\n", (attr->reg && rc) ? -1 : *reg);
+}
+
+/*
+ * Write a register value by writing a sysfs entry
+ */
+static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
+				     void *data, const char *buf, size_t count)
+{
+	struct elantech_data *etd = psmouse->private;
+	struct elantech_attr_data *attr = data;
+	unsigned char *reg = (unsigned char *) etd + attr->field_offset;
+	unsigned char value;
+	int err;
+
+	err = kstrtou8(buf, 16, &value);
+	if (err)
+		return err;
+
+	/* Do we need to preserve some bits for version 2 hardware too? */
+	if (etd->hw_version == 1) {
+		if (attr->reg == 0x10)
+			/* Force absolute mode always on */
+			value |= ETP_R10_ABSOLUTE_MODE;
+		else if (attr->reg == 0x11)
+			/* Force 4 byte mode always on */
+			value |= ETP_R11_4_BYTE_MODE;
+	}
+
+	if (!attr->reg || elantech_write_reg(psmouse, attr->reg, value) == 0)
+		*reg = value;
+
+	return count;
+}
+
+#define ELANTECH_INT_ATTR(_name, _register)				\
+	static struct elantech_attr_data elantech_attr_##_name = {	\
+		.field_offset = offsetof(struct elantech_data, _name),	\
+		.reg = _register,					\
+	};								\
+	PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO,			\
+			    &elantech_attr_##_name,			\
+			    elantech_show_int_attr,			\
+			    elantech_set_int_attr)
+
+ELANTECH_INT_ATTR(reg_07, 0x07);
+ELANTECH_INT_ATTR(reg_10, 0x10);
+ELANTECH_INT_ATTR(reg_11, 0x11);
+ELANTECH_INT_ATTR(reg_20, 0x20);
+ELANTECH_INT_ATTR(reg_21, 0x21);
+ELANTECH_INT_ATTR(reg_22, 0x22);
+ELANTECH_INT_ATTR(reg_23, 0x23);
+ELANTECH_INT_ATTR(reg_24, 0x24);
+ELANTECH_INT_ATTR(reg_25, 0x25);
+ELANTECH_INT_ATTR(reg_26, 0x26);
+ELANTECH_INT_ATTR(debug, 0);
+ELANTECH_INT_ATTR(paritycheck, 0);
+ELANTECH_INT_ATTR(crc_enabled, 0);
+
+static struct attribute *elantech_attrs[] = {
+	&psmouse_attr_reg_07.dattr.attr,
+	&psmouse_attr_reg_10.dattr.attr,
+	&psmouse_attr_reg_11.dattr.attr,
+	&psmouse_attr_reg_20.dattr.attr,
+	&psmouse_attr_reg_21.dattr.attr,
+	&psmouse_attr_reg_22.dattr.attr,
+	&psmouse_attr_reg_23.dattr.attr,
+	&psmouse_attr_reg_24.dattr.attr,
+	&psmouse_attr_reg_25.dattr.attr,
+	&psmouse_attr_reg_26.dattr.attr,
+	&psmouse_attr_debug.dattr.attr,
+	&psmouse_attr_paritycheck.dattr.attr,
+	&psmouse_attr_crc_enabled.dattr.attr,
+	NULL
+};
+
+static struct attribute_group elantech_attr_group = {
+	.attrs = elantech_attrs,
+};
+
+static bool elantech_is_signature_valid(const unsigned char *param)
+{
+	static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10 };
+	int i;
+
+	if (param[0] == 0)
+		return false;
+
+	if (param[1] == 0)
+		return true;
+
+	/*
+	 * Some hw_version >= 4 models have a revision higher then 20. Meaning
+	 * that param[2] may be 10 or 20, skip the rates check for these.
+	 */
+	if ((param[0] & 0x0f) >= 0x06 && (param[1] & 0xaf) == 0x0f &&
+	    param[2] < 40)
+		return true;
+
+	for (i = 0; i < ARRAY_SIZE(rates); i++)
+		if (param[2] == rates[i])
+			return false;
+
+	return true;
+}
+
+/*
+ * Use magic knock to detect Elantech touchpad
+ */
+int elantech_detect(struct psmouse *psmouse, bool set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[3];
+
+	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+
+	if (ps2_command(ps2dev,  NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
+	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
+	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
+	    ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
+		psmouse_dbg(psmouse, "sending Elantech magic knock failed.\n");
+		return -1;
+	}
+
+	/*
+	 * Report this in case there are Elantech models that use a different
+	 * set of magic numbers
+	 */
+	if (param[0] != 0x3c || param[1] != 0x03 ||
+	    (param[2] != 0xc8 && param[2] != 0x00)) {
+		psmouse_dbg(psmouse,
+			    "unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n",
+			    param[0], param[1], param[2]);
+		return -1;
+	}
+
+	/*
+	 * Query touchpad's firmware version and see if it reports known
+	 * value to avoid mis-detection. Logitech mice are known to respond
+	 * to Elantech magic knock and there might be more.
+	 */
+	if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
+		psmouse_dbg(psmouse, "failed to query firmware version.\n");
+		return -1;
+	}
+
+	psmouse_dbg(psmouse,
+		    "Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n",
+		    param[0], param[1], param[2]);
+
+	if (!elantech_is_signature_valid(param)) {
+		psmouse_dbg(psmouse,
+			    "Probably not a real Elantech touchpad. Aborting.\n");
+		return -1;
+	}
+
+	if (set_properties) {
+		psmouse->vendor = "Elantech";
+		psmouse->name = "Touchpad";
+	}
+
+	return 0;
+}
+
+/*
+ * Clean up sysfs entries when disconnecting
+ */
+static void elantech_disconnect(struct psmouse *psmouse)
+{
+	struct elantech_data *etd = psmouse->private;
+
+	if (etd->tp_dev)
+		input_unregister_device(etd->tp_dev);
+	sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
+			   &elantech_attr_group);
+	kfree(psmouse->private);
+	psmouse->private = NULL;
+}
+
+/*
+ * Put the touchpad back into absolute mode when reconnecting
+ */
+static int elantech_reconnect(struct psmouse *psmouse)
+{
+	psmouse_reset(psmouse);
+
+	if (elantech_detect(psmouse, 0))
+		return -1;
+
+	if (elantech_set_absolute_mode(psmouse)) {
+		psmouse_err(psmouse,
+			    "failed to put touchpad back into absolute mode.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Some hw_version 4 models do not work with crc_disabled
+ */
+static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+	{
+		/* Fujitsu H730 does not work with crc_enabled == 0 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
+		},
+	},
+	{
+		/* Fujitsu H760 does not work with crc_enabled == 0 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
+		},
+	},
+	{
+		/* Fujitsu LIFEBOOK E544  does not work with crc_enabled == 0 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E544"),
+		},
+	},
+	{
+		/* Fujitsu LIFEBOOK E546  does not work with crc_enabled == 0 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E546"),
+		},
+	},
+	{
+		/* Fujitsu LIFEBOOK E547 does not work with crc_enabled == 0 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E547"),
+		},
+	},
+	{
+		/* Fujitsu LIFEBOOK E554  does not work with crc_enabled == 0 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"),
+		},
+	},
+	{
+		/* Fujitsu LIFEBOOK E556 does not work with crc_enabled == 0 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E556"),
+		},
+	},
+	{
+		/* Fujitsu LIFEBOOK E557 does not work with crc_enabled == 0 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E557"),
+		},
+	},
+	{
+		/* Fujitsu LIFEBOOK U745 does not work with crc_enabled == 0 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"),
+		},
+	},
+#endif
+	{ }
+};
+
+/*
+ * Some hw_version 3 models go into error state when we try to set
+ * bit 3 and/or bit 1 of r10.
+ */
+static const struct dmi_system_id no_hw_res_dmi_table[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+	{
+		/* Gigabyte U2442 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "U2442"),
+		},
+	},
+#endif
+	{ }
+};
+
+/*
+ * determine hardware version and set some properties according to it.
+ */
+static int elantech_set_properties(struct elantech_data *etd)
+{
+	/* This represents the version of IC body. */
+	int ver = (etd->fw_version & 0x0f0000) >> 16;
+
+	/* Early version of Elan touchpads doesn't obey the rule. */
+	if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600)
+		etd->hw_version = 1;
+	else {
+		switch (ver) {
+		case 2:
+		case 4:
+			etd->hw_version = 2;
+			break;
+		case 5:
+			etd->hw_version = 3;
+			break;
+		case 6 ... 14:
+			etd->hw_version = 4;
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	/* decide which send_cmd we're gonna use early */
+	etd->send_cmd = etd->hw_version >= 3 ? elantech_send_cmd :
+					       synaptics_send_cmd;
+
+	/* Turn on packet checking by default */
+	etd->paritycheck = 1;
+
+	/*
+	 * This firmware suffers from misreporting coordinates when
+	 * a touch action starts causing the mouse cursor or scrolled page
+	 * to jump. Enable a workaround.
+	 */
+	etd->jumpy_cursor =
+		(etd->fw_version == 0x020022 || etd->fw_version == 0x020600);
+
+	if (etd->hw_version > 1) {
+		/* For now show extra debug information */
+		etd->debug = 1;
+
+		if (etd->fw_version >= 0x020800)
+			etd->reports_pressure = true;
+	}
+
+	/*
+	 * The signatures of v3 and v4 packets change depending on the
+	 * value of this hardware flag.
+	 */
+	etd->crc_enabled = (etd->fw_version & 0x4000) == 0x4000 ||
+			   dmi_check_system(elantech_dmi_force_crc_enabled);
+
+	/* Enable real hardware resolution on hw_version 3 ? */
+	etd->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
+
+	return 0;
+}
+
+/*
+ * Initialize the touchpad and create sysfs entries
+ */
+int elantech_init(struct psmouse *psmouse)
+{
+	struct elantech_data *etd;
+	int i;
+	int error = -EINVAL;
+	unsigned char param[3];
+	struct input_dev *tp_dev;
+
+	psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
+	if (!etd)
+		return -ENOMEM;
+
+	psmouse_reset(psmouse);
+
+	etd->parity[0] = 1;
+	for (i = 1; i < 256; i++)
+		etd->parity[i] = etd->parity[i & (i - 1)] ^ 1;
+
+	/*
+	 * Do the version query again so we can store the result
+	 */
+	if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
+		psmouse_err(psmouse, "failed to query firmware version.\n");
+		goto init_fail;
+	}
+	etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
+
+	if (elantech_set_properties(etd)) {
+		psmouse_err(psmouse, "unknown hardware version, aborting...\n");
+		goto init_fail;
+	}
+	psmouse_info(psmouse,
+		     "assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n",
+		     etd->hw_version, param[0], param[1], param[2]);
+
+	if (etd->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
+	    etd->capabilities)) {
+		psmouse_err(psmouse, "failed to query capabilities.\n");
+		goto init_fail;
+	}
+	psmouse_info(psmouse,
+		     "Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
+		     etd->capabilities[0], etd->capabilities[1],
+		     etd->capabilities[2]);
+
+	if (etd->hw_version != 1) {
+		if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, etd->samples)) {
+			psmouse_err(psmouse, "failed to query sample data\n");
+			goto init_fail;
+		}
+		psmouse_info(psmouse,
+			     "Elan sample query result %02x, %02x, %02x\n",
+			     etd->samples[0], etd->samples[1], etd->samples[2]);
+	}
+
+	if (elantech_set_absolute_mode(psmouse)) {
+		psmouse_err(psmouse,
+			    "failed to put touchpad into absolute mode.\n");
+		goto init_fail;
+	}
+
+	if (etd->fw_version == 0x381f17) {
+		etd->original_set_rate = psmouse->set_rate;
+		psmouse->set_rate = elantech_set_rate_restore_reg_07;
+	}
+
+	if (elantech_set_input_params(psmouse)) {
+		psmouse_err(psmouse, "failed to query touchpad range.\n");
+		goto init_fail;
+	}
+
+	error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
+				   &elantech_attr_group);
+	if (error) {
+		psmouse_err(psmouse,
+			    "failed to create sysfs attributes, error: %d.\n",
+			    error);
+		goto init_fail;
+	}
+
+	/* The MSB indicates the presence of the trackpoint */
+	if ((etd->capabilities[0] & 0x80) == 0x80) {
+		tp_dev = input_allocate_device();
+
+		if (!tp_dev) {
+			error = -ENOMEM;
+			goto init_fail_tp_alloc;
+		}
+
+		etd->tp_dev = tp_dev;
+		snprintf(etd->tp_phys, sizeof(etd->tp_phys), "%s/input1",
+			psmouse->ps2dev.serio->phys);
+		tp_dev->phys = etd->tp_phys;
+		tp_dev->name = "Elantech PS/2 TrackPoint";
+		tp_dev->id.bustype = BUS_I8042;
+		tp_dev->id.vendor  = 0x0002;
+		tp_dev->id.product = PSMOUSE_ELANTECH;
+		tp_dev->id.version = 0x0000;
+		tp_dev->dev.parent = &psmouse->ps2dev.serio->dev;
+		tp_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+		tp_dev->relbit[BIT_WORD(REL_X)] =
+			BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+		tp_dev->keybit[BIT_WORD(BTN_LEFT)] =
+			BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) |
+			BIT_MASK(BTN_RIGHT);
+
+		__set_bit(INPUT_PROP_POINTER, tp_dev->propbit);
+		__set_bit(INPUT_PROP_POINTING_STICK, tp_dev->propbit);
+
+		error = input_register_device(etd->tp_dev);
+		if (error < 0)
+			goto init_fail_tp_reg;
+	}
+
+	psmouse->protocol_handler = elantech_process_byte;
+	psmouse->disconnect = elantech_disconnect;
+	psmouse->reconnect = elantech_reconnect;
+	psmouse->pktsize = etd->hw_version > 1 ? 6 : 4;
+
+	return 0;
+ init_fail_tp_reg:
+	input_free_device(tp_dev);
+ init_fail_tp_alloc:
+	sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
+			   &elantech_attr_group);
+ init_fail:
+	psmouse_reset(psmouse);
+	kfree(etd);
+	return error;
+}
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
new file mode 100644
index 0000000..e1cbf40
--- /dev/null
+++ b/drivers/input/mouse/elantech.h
@@ -0,0 +1,163 @@
+/*
+ * Elantech Touchpad driver (v6)
+ *
+ * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#ifndef _ELANTECH_H
+#define _ELANTECH_H
+
+/*
+ * Command values for Synaptics style queries
+ */
+#define ETP_FW_ID_QUERY			0x00
+#define ETP_FW_VERSION_QUERY		0x01
+#define ETP_CAPABILITIES_QUERY		0x02
+#define ETP_SAMPLE_QUERY		0x03
+#define ETP_RESOLUTION_QUERY		0x04
+
+/*
+ * Command values for register reading or writing
+ */
+#define ETP_REGISTER_READ		0x10
+#define ETP_REGISTER_WRITE		0x11
+#define ETP_REGISTER_READWRITE		0x00
+
+/*
+ * Hardware version 2 custom PS/2 command value
+ */
+#define ETP_PS2_CUSTOM_COMMAND		0xf8
+
+/*
+ * Times to retry a ps2_command and millisecond delay between tries
+ */
+#define ETP_PS2_COMMAND_TRIES		3
+#define ETP_PS2_COMMAND_DELAY		500
+
+/*
+ * Times to try to read back a register and millisecond delay between tries
+ */
+#define ETP_READ_BACK_TRIES		5
+#define ETP_READ_BACK_DELAY		2000
+
+/*
+ * Register bitmasks for hardware version 1
+ */
+#define ETP_R10_ABSOLUTE_MODE		0x04
+#define ETP_R11_4_BYTE_MODE		0x02
+
+/*
+ * Capability bitmasks
+ */
+#define ETP_CAP_HAS_ROCKER		0x04
+
+/*
+ * One hard to find application note states that X axis range is 0 to 576
+ * and Y axis range is 0 to 384 for harware version 1.
+ * Edge fuzz might be necessary because of bezel around the touchpad
+ */
+#define ETP_EDGE_FUZZ_V1		32
+
+#define ETP_XMIN_V1			(  0 + ETP_EDGE_FUZZ_V1)
+#define ETP_XMAX_V1			(576 - ETP_EDGE_FUZZ_V1)
+#define ETP_YMIN_V1			(  0 + ETP_EDGE_FUZZ_V1)
+#define ETP_YMAX_V1			(384 - ETP_EDGE_FUZZ_V1)
+
+/*
+ * The resolution for older v2 hardware doubled.
+ * (newer v2's firmware provides command so we can query)
+ */
+#define ETP_XMIN_V2			0
+#define ETP_XMAX_V2			1152
+#define ETP_YMIN_V2			0
+#define ETP_YMAX_V2			768
+
+#define ETP_PMIN_V2			0
+#define ETP_PMAX_V2			255
+#define ETP_WMIN_V2			0
+#define ETP_WMAX_V2			15
+
+/*
+ * v3 hardware has 2 kinds of packet types,
+ * v4 hardware has 3.
+ */
+#define PACKET_UNKNOWN			0x01
+#define PACKET_DEBOUNCE			0x02
+#define PACKET_V3_HEAD			0x03
+#define PACKET_V3_TAIL			0x04
+#define PACKET_V4_HEAD			0x05
+#define PACKET_V4_MOTION		0x06
+#define PACKET_V4_STATUS		0x07
+#define PACKET_TRACKPOINT		0x08
+
+/*
+ * track up to 5 fingers for v4 hardware
+ */
+#define ETP_MAX_FINGERS			5
+
+/*
+ * weight value for v4 hardware
+ */
+#define ETP_WEIGHT_VALUE		5
+
+/*
+ * The base position for one finger, v4 hardware
+ */
+struct finger_pos {
+	unsigned int x;
+	unsigned int y;
+};
+
+struct elantech_data {
+	struct input_dev *tp_dev;	/* Relative device for trackpoint */
+	char tp_phys[32];
+	unsigned char reg_07;
+	unsigned char reg_10;
+	unsigned char reg_11;
+	unsigned char reg_20;
+	unsigned char reg_21;
+	unsigned char reg_22;
+	unsigned char reg_23;
+	unsigned char reg_24;
+	unsigned char reg_25;
+	unsigned char reg_26;
+	unsigned char debug;
+	unsigned char capabilities[3];
+	unsigned char samples[3];
+	bool paritycheck;
+	bool jumpy_cursor;
+	bool reports_pressure;
+	bool crc_enabled;
+	bool set_hw_resolution;
+	unsigned char hw_version;
+	unsigned int fw_version;
+	unsigned int single_finger_reports;
+	unsigned int y_max;
+	unsigned int width;
+	struct finger_pos mt[ETP_MAX_FINGERS];
+	unsigned char parity[256];
+	int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param);
+	void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate);
+};
+
+#ifdef CONFIG_MOUSE_PS2_ELANTECH
+int elantech_detect(struct psmouse *psmouse, bool set_properties);
+int elantech_init(struct psmouse *psmouse);
+#else
+static inline int elantech_detect(struct psmouse *psmouse, bool set_properties)
+{
+	return -ENOSYS;
+}
+static inline int elantech_init(struct psmouse *psmouse)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_ELANTECH */
+
+#endif
diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c
new file mode 100644
index 0000000..4d5576d
--- /dev/null
+++ b/drivers/input/mouse/focaltech.c
@@ -0,0 +1,469 @@
+/*
+ * Focaltech TouchPad PS/2 mouse driver
+ *
+ * Copyright (c) 2014 Red Hat Inc.
+ * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
+ *
+ * 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.
+ *
+ * Red Hat authors:
+ *
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+
+#include <linux/device.h>
+#include <linux/libps2.h>
+#include <linux/input/mt.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+#include "psmouse.h"
+#include "focaltech.h"
+
+static const char * const focaltech_pnp_ids[] = {
+	"FLT0101",
+	"FLT0102",
+	"FLT0103",
+	NULL
+};
+
+/*
+ * Even if the kernel is built without support for Focaltech PS/2 touchpads (or
+ * when the real driver fails to recognize the device), we still have to detect
+ * them in order to avoid further detection attempts confusing the touchpad.
+ * This way it at least works in PS/2 mouse compatibility mode.
+ */
+int focaltech_detect(struct psmouse *psmouse, bool set_properties)
+{
+	if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
+		return -ENODEV;
+
+	if (set_properties) {
+		psmouse->vendor = "FocalTech";
+		psmouse->name = "FocalTech Touchpad";
+	}
+
+	return 0;
+}
+
+static void focaltech_reset(struct psmouse *psmouse)
+{
+	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+	psmouse_reset(psmouse);
+}
+
+#ifdef CONFIG_MOUSE_PS2_FOCALTECH
+
+/*
+ * Packet types - the numbers are not consecutive, so we might be missing
+ * something here.
+ */
+#define FOC_TOUCH 0x3 /* bitmap of active fingers */
+#define FOC_ABS 0x6 /* absolute position of one finger */
+#define FOC_REL 0x9 /* relative position of 1-2 fingers */
+
+#define FOC_MAX_FINGERS 5
+
+/*
+ * Current state of a single finger on the touchpad.
+ */
+struct focaltech_finger_state {
+	/* The touchpad has generated a touch event for the finger */
+	bool active;
+
+	/*
+	 * The touchpad has sent position data for the finger. The
+	 * flag is 0 when the finger is not active, and there is a
+	 * time between the first touch event for the finger and the
+	 * following absolute position packet for the finger where the
+	 * touchpad has declared the finger to be valid, but we do not
+	 * have any valid position yet.
+	 */
+	bool valid;
+
+	/*
+	 * Absolute position (from the bottom left corner) of the
+	 * finger.
+	 */
+	unsigned int x;
+	unsigned int y;
+};
+
+/*
+ * Description of the current state of the touchpad hardware.
+ */
+struct focaltech_hw_state {
+	/*
+	 * The touchpad tracks the positions of the fingers for us,
+	 * the array indices correspond to the finger indices returned
+	 * in the report packages.
+	 */
+	struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
+
+	/*
+	 * Finger width 0-7 and 15 for a very big contact area.
+	 * 15 value stays until the finger is released.
+	 * Width is reported only in absolute packets.
+	 * Since hardware reports width only for last touching finger,
+	 * there is no need to store width for every specific finger,
+	 * so we keep only last value reported.
+	 */
+	unsigned int width;
+
+	/* True if the clickpad has been pressed. */
+	bool pressed;
+};
+
+struct focaltech_data {
+	unsigned int x_max, y_max;
+	struct focaltech_hw_state state;
+};
+
+static void focaltech_report_state(struct psmouse *psmouse)
+{
+	struct focaltech_data *priv = psmouse->private;
+	struct focaltech_hw_state *state = &priv->state;
+	struct input_dev *dev = psmouse->dev;
+	int i;
+
+	for (i = 0; i < FOC_MAX_FINGERS; i++) {
+		struct focaltech_finger_state *finger = &state->fingers[i];
+		bool active = finger->active && finger->valid;
+
+		input_mt_slot(dev, i);
+		input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
+		if (active) {
+			unsigned int clamped_x, clamped_y;
+			/*
+			 * The touchpad might report invalid data, so we clamp
+			 * the resulting values so that we do not confuse
+			 * userspace.
+			 */
+			clamped_x = clamp(finger->x, 0U, priv->x_max);
+			clamped_y = clamp(finger->y, 0U, priv->y_max);
+			input_report_abs(dev, ABS_MT_POSITION_X, clamped_x);
+			input_report_abs(dev, ABS_MT_POSITION_Y,
+					 priv->y_max - clamped_y);
+			input_report_abs(dev, ABS_TOOL_WIDTH, state->width);
+		}
+	}
+	input_mt_report_pointer_emulation(dev, true);
+
+	input_report_key(psmouse->dev, BTN_LEFT, state->pressed);
+	input_sync(psmouse->dev);
+}
+
+static void focaltech_process_touch_packet(struct psmouse *psmouse,
+					   unsigned char *packet)
+{
+	struct focaltech_data *priv = psmouse->private;
+	struct focaltech_hw_state *state = &priv->state;
+	unsigned char fingers = packet[1];
+	int i;
+
+	state->pressed = (packet[0] >> 4) & 1;
+
+	/* the second byte contains a bitmap of all fingers touching the pad */
+	for (i = 0; i < FOC_MAX_FINGERS; i++) {
+		state->fingers[i].active = fingers & 0x1;
+		if (!state->fingers[i].active) {
+			/*
+			 * Even when the finger becomes active again, we still
+			 * will have to wait for the first valid position.
+			 */
+			state->fingers[i].valid = false;
+		}
+		fingers >>= 1;
+	}
+}
+
+static void focaltech_process_abs_packet(struct psmouse *psmouse,
+					 unsigned char *packet)
+{
+	struct focaltech_data *priv = psmouse->private;
+	struct focaltech_hw_state *state = &priv->state;
+	unsigned int finger;
+
+	finger = (packet[1] >> 4) - 1;
+	if (finger >= FOC_MAX_FINGERS) {
+		psmouse_err(psmouse, "Invalid finger in abs packet: %d\n",
+			    finger);
+		return;
+	}
+
+	state->pressed = (packet[0] >> 4) & 1;
+
+	state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
+	state->fingers[finger].y = (packet[3] << 8) | packet[4];
+	state->width = packet[5] >> 4;
+	state->fingers[finger].valid = true;
+}
+
+static void focaltech_process_rel_packet(struct psmouse *psmouse,
+					 unsigned char *packet)
+{
+	struct focaltech_data *priv = psmouse->private;
+	struct focaltech_hw_state *state = &priv->state;
+	int finger1, finger2;
+
+	state->pressed = packet[0] >> 7;
+	finger1 = ((packet[0] >> 4) & 0x7) - 1;
+	if (finger1 < FOC_MAX_FINGERS) {
+		state->fingers[finger1].x += (char)packet[1];
+		state->fingers[finger1].y += (char)packet[2];
+	} else {
+		psmouse_err(psmouse, "First finger in rel packet invalid: %d\n",
+			    finger1);
+	}
+
+	/*
+	 * If there is an odd number of fingers, the last relative
+	 * packet only contains one finger. In this case, the second
+	 * finger index in the packet is 0 (we subtract 1 in the lines
+	 * above to create array indices, so the finger will overflow
+	 * and be above FOC_MAX_FINGERS).
+	 */
+	finger2 = ((packet[3] >> 4) & 0x7) - 1;
+	if (finger2 < FOC_MAX_FINGERS) {
+		state->fingers[finger2].x += (char)packet[4];
+		state->fingers[finger2].y += (char)packet[5];
+	}
+}
+
+static void focaltech_process_packet(struct psmouse *psmouse)
+{
+	unsigned char *packet = psmouse->packet;
+
+	switch (packet[0] & 0xf) {
+	case FOC_TOUCH:
+		focaltech_process_touch_packet(psmouse, packet);
+		break;
+
+	case FOC_ABS:
+		focaltech_process_abs_packet(psmouse, packet);
+		break;
+
+	case FOC_REL:
+		focaltech_process_rel_packet(psmouse, packet);
+		break;
+
+	default:
+		psmouse_err(psmouse, "Unknown packet type: %02x\n", packet[0]);
+		break;
+	}
+
+	focaltech_report_state(psmouse);
+}
+
+static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
+{
+	if (psmouse->pktcnt >= 6) { /* Full packet received */
+		focaltech_process_packet(psmouse);
+		return PSMOUSE_FULL_PACKET;
+	}
+
+	/*
+	 * We might want to do some validation of the data here, but
+	 * we do not know the protocol well enough
+	 */
+	return PSMOUSE_GOOD_DATA;
+}
+
+static int focaltech_switch_protocol(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[3];
+
+	param[0] = 0;
+	if (ps2_command(ps2dev, param, 0x10f8))
+		return -EIO;
+
+	if (ps2_command(ps2dev, param, 0x10f8))
+		return -EIO;
+
+	if (ps2_command(ps2dev, param, 0x10f8))
+		return -EIO;
+
+	param[0] = 1;
+	if (ps2_command(ps2dev, param, 0x10f8))
+		return -EIO;
+
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
+		return -EIO;
+
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
+		return -EIO;
+
+	return 0;
+}
+
+static void focaltech_disconnect(struct psmouse *psmouse)
+{
+	focaltech_reset(psmouse);
+	kfree(psmouse->private);
+	psmouse->private = NULL;
+}
+
+static int focaltech_reconnect(struct psmouse *psmouse)
+{
+	int error;
+
+	focaltech_reset(psmouse);
+
+	error = focaltech_switch_protocol(psmouse);
+	if (error) {
+		psmouse_err(psmouse, "Unable to initialize the device\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static void focaltech_set_input_params(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct focaltech_data *priv = psmouse->private;
+
+	/*
+	 * Undo part of setup done for us by psmouse core since touchpad
+	 * is not a relative device.
+	 */
+	__clear_bit(EV_REL, dev->evbit);
+	__clear_bit(REL_X, dev->relbit);
+	__clear_bit(REL_Y, dev->relbit);
+	__clear_bit(BTN_RIGHT, dev->keybit);
+	__clear_bit(BTN_MIDDLE, dev->keybit);
+
+	/*
+	 * Now set up our capabilities.
+	 */
+	__set_bit(EV_ABS, dev->evbit);
+	input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
+	input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
+	input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
+	input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
+	__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
+}
+
+static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
+				   unsigned char *param)
+{
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
+		return -EIO;
+
+	param[0] = 0;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+		return -EIO;
+
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+		return -EIO;
+
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+		return -EIO;
+
+	param[0] = reg;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+		return -EIO;
+
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+		return -EIO;
+
+	return 0;
+}
+
+static int focaltech_read_size(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	struct focaltech_data *priv = psmouse->private;
+	char param[3];
+
+	if (focaltech_read_register(ps2dev, 2, param))
+		return -EIO;
+
+	/* not sure whether this is 100% correct */
+	priv->x_max = (unsigned char)param[1] * 128;
+	priv->y_max = (unsigned char)param[2] * 128;
+
+	return 0;
+}
+
+void focaltech_set_resolution(struct psmouse *psmouse, unsigned int resolution)
+{
+	/* not supported yet */
+}
+
+static void focaltech_set_rate(struct psmouse *psmouse, unsigned int rate)
+{
+	/* not supported yet */
+}
+
+static void focaltech_set_scale(struct psmouse *psmouse,
+				enum psmouse_scale scale)
+{
+	/* not supported yet */
+}
+
+int focaltech_init(struct psmouse *psmouse)
+{
+	struct focaltech_data *priv;
+	int error;
+
+	psmouse->private = priv = kzalloc(sizeof(struct focaltech_data),
+					  GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	focaltech_reset(psmouse);
+
+	error = focaltech_read_size(psmouse);
+	if (error) {
+		psmouse_err(psmouse,
+			    "Unable to read the size of the touchpad\n");
+		goto fail;
+	}
+
+	error = focaltech_switch_protocol(psmouse);
+	if (error) {
+		psmouse_err(psmouse, "Unable to initialize the device\n");
+		goto fail;
+	}
+
+	focaltech_set_input_params(psmouse);
+
+	psmouse->protocol_handler = focaltech_process_byte;
+	psmouse->pktsize = 6;
+	psmouse->disconnect = focaltech_disconnect;
+	psmouse->reconnect = focaltech_reconnect;
+	psmouse->cleanup = focaltech_reset;
+	/* resync is not supported yet */
+	psmouse->resync_time = 0;
+	/*
+	 * rate/resolution/scale changes are not supported yet, and
+	 * the generic implementations of these functions seem to
+	 * confuse some touchpads
+	 */
+	psmouse->set_resolution = focaltech_set_resolution;
+	psmouse->set_rate = focaltech_set_rate;
+	psmouse->set_scale = focaltech_set_scale;
+
+	return 0;
+
+fail:
+	focaltech_reset(psmouse);
+	kfree(priv);
+	return error;
+}
+
+#else /* CONFIG_MOUSE_PS2_FOCALTECH */
+
+int focaltech_init(struct psmouse *psmouse)
+{
+	focaltech_reset(psmouse);
+
+	return 0;
+}
+
+#endif /* CONFIG_MOUSE_PS2_FOCALTECH */
diff --git a/drivers/input/mouse/focaltech.h b/drivers/input/mouse/focaltech.h
new file mode 100644
index 0000000..ca61ebf
--- /dev/null
+++ b/drivers/input/mouse/focaltech.h
@@ -0,0 +1,23 @@
+/*
+ * Focaltech TouchPad PS/2 mouse driver
+ *
+ * Copyright (c) 2014 Red Hat Inc.
+ * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
+ *
+ * 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.
+ *
+ * Red Hat authors:
+ *
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#ifndef _FOCALTECH_H
+#define _FOCALTECH_H
+
+int focaltech_detect(struct psmouse *psmouse, bool set_properties);
+int focaltech_init(struct psmouse *psmouse);
+
+#endif
diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c
new file mode 100644
index 0000000..ced0739
--- /dev/null
+++ b/drivers/input/mouse/gpio_mouse.c
@@ -0,0 +1,182 @@
+/*
+ * Driver for simulating a mouse on GPIO lines.
+ *
+ * Copyright (C) 2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input-polldev.h>
+#include <linux/gpio.h>
+#include <linux/gpio_mouse.h>
+
+
+/*
+ * Timer function which is run every scan_ms ms when the device is opened.
+ * The dev input variable is set to the the input_dev pointer.
+ */
+static void gpio_mouse_scan(struct input_polled_dev *dev)
+{
+	struct gpio_mouse_platform_data *gpio = dev->private;
+	struct input_dev *input = dev->input;
+	int x, y;
+
+	if (gpio->bleft >= 0)
+		input_report_key(input, BTN_LEFT,
+				gpio_get_value(gpio->bleft) ^ gpio->polarity);
+	if (gpio->bmiddle >= 0)
+		input_report_key(input, BTN_MIDDLE,
+				gpio_get_value(gpio->bmiddle) ^ gpio->polarity);
+	if (gpio->bright >= 0)
+		input_report_key(input, BTN_RIGHT,
+				gpio_get_value(gpio->bright) ^ gpio->polarity);
+
+	x = (gpio_get_value(gpio->right) ^ gpio->polarity)
+		- (gpio_get_value(gpio->left) ^ gpio->polarity);
+	y = (gpio_get_value(gpio->down) ^ gpio->polarity)
+		- (gpio_get_value(gpio->up) ^ gpio->polarity);
+
+	input_report_rel(input, REL_X, x);
+	input_report_rel(input, REL_Y, y);
+	input_sync(input);
+}
+
+static int gpio_mouse_probe(struct platform_device *pdev)
+{
+	struct gpio_mouse_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct input_polled_dev *input_poll;
+	struct input_dev *input;
+	int pin, i;
+	int error;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data\n");
+		error = -ENXIO;
+		goto out;
+	}
+
+	if (pdata->scan_ms < 0) {
+		dev_err(&pdev->dev, "invalid scan time\n");
+		error = -EINVAL;
+		goto out;
+	}
+
+	for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) {
+		pin = pdata->pins[i];
+
+		if (pin < 0) {
+
+			if (i <= GPIO_MOUSE_PIN_RIGHT) {
+				/* Mouse direction is required. */
+				dev_err(&pdev->dev,
+					"missing GPIO for directions\n");
+				error = -EINVAL;
+				goto out_free_gpios;
+			}
+
+			if (i == GPIO_MOUSE_PIN_BLEFT)
+				dev_dbg(&pdev->dev, "no left button defined\n");
+
+		} else {
+			error = gpio_request(pin, "gpio_mouse");
+			if (error) {
+				dev_err(&pdev->dev, "fail %d pin (%d idx)\n",
+					pin, i);
+				goto out_free_gpios;
+			}
+
+			gpio_direction_input(pin);
+		}
+	}
+
+	input_poll = input_allocate_polled_device();
+	if (!input_poll) {
+		dev_err(&pdev->dev, "not enough memory for input device\n");
+		error = -ENOMEM;
+		goto out_free_gpios;
+	}
+
+	platform_set_drvdata(pdev, input_poll);
+
+	/* set input-polldev handlers */
+	input_poll->private = pdata;
+	input_poll->poll = gpio_mouse_scan;
+	input_poll->poll_interval = pdata->scan_ms;
+
+	input = input_poll->input;
+	input->name = pdev->name;
+	input->id.bustype = BUS_HOST;
+	input->dev.parent = &pdev->dev;
+
+	input_set_capability(input, EV_REL, REL_X);
+	input_set_capability(input, EV_REL, REL_Y);
+	if (pdata->bleft >= 0)
+		input_set_capability(input, EV_KEY, BTN_LEFT);
+	if (pdata->bmiddle >= 0)
+		input_set_capability(input, EV_KEY, BTN_MIDDLE);
+	if (pdata->bright >= 0)
+		input_set_capability(input, EV_KEY, BTN_RIGHT);
+
+	error = input_register_polled_device(input_poll);
+	if (error) {
+		dev_err(&pdev->dev, "could not register input device\n");
+		goto out_free_polldev;
+	}
+
+	dev_dbg(&pdev->dev, "%d ms scan time, buttons: %s%s%s\n",
+			pdata->scan_ms,
+			pdata->bleft < 0 ? "" : "left ",
+			pdata->bmiddle < 0 ? "" : "middle ",
+			pdata->bright < 0 ? "" : "right");
+
+	return 0;
+
+ out_free_polldev:
+	input_free_polled_device(input_poll);
+
+ out_free_gpios:
+	while (--i >= 0) {
+		pin = pdata->pins[i];
+		if (pin)
+			gpio_free(pin);
+	}
+ out:
+	return error;
+}
+
+static int gpio_mouse_remove(struct platform_device *pdev)
+{
+	struct input_polled_dev *input = platform_get_drvdata(pdev);
+	struct gpio_mouse_platform_data *pdata = input->private;
+	int pin, i;
+
+	input_unregister_polled_device(input);
+	input_free_polled_device(input);
+
+	for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) {
+		pin = pdata->pins[i];
+		if (pin >= 0)
+			gpio_free(pin);
+	}
+
+	return 0;
+}
+
+static struct platform_driver gpio_mouse_device_driver = {
+	.probe		= gpio_mouse_probe,
+	.remove		= gpio_mouse_remove,
+	.driver		= {
+		.name	= "gpio_mouse",
+	}
+};
+module_platform_driver(gpio_mouse_device_driver);
+
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
+MODULE_DESCRIPTION("GPIO mouse driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio_mouse"); /* work with hotplug and coldplug */
+
diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c
new file mode 100644
index 0000000..62be888
--- /dev/null
+++ b/drivers/input/mouse/hgpk.c
@@ -0,0 +1,1067 @@
+/*
+ * OLPC HGPK (XO-1) touchpad PS/2 mouse driver
+ *
+ * Copyright (c) 2006-2008 One Laptop Per Child
+ * Authors:
+ *   Zephaniah E. Hull
+ *   Andres Salomon <dilinger@debian.org>
+ *
+ * This driver is partly based on the ALPS driver, which is:
+ *
+ * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com>
+ * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * The spec from ALPS is available from
+ * <http://wiki.laptop.org/go/Touch_Pad/Tablet>.  It refers to this
+ * device as HGPK (Hybrid GS, PT, and Keymatrix).
+ *
+ * The earliest versions of the device had simultaneous reporting; that
+ * was removed.  After that, the device used the Advanced Mode GS/PT streaming
+ * stuff.  That turned out to be too buggy to support, so we've finally
+ * switched to Mouse Mode (which utilizes only the center 1/3 of the touchpad).
+ */
+
+#define DEBUG
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/delay.h>
+#include <asm/olpc.h>
+
+#include "psmouse.h"
+#include "hgpk.h"
+
+#define ILLEGAL_XY 999999
+
+static bool tpdebug;
+module_param(tpdebug, bool, 0644);
+MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG.");
+
+static int recalib_delta = 100;
+module_param(recalib_delta, int, 0644);
+MODULE_PARM_DESC(recalib_delta,
+	"packets containing a delta this large will be discarded, and a "
+	"recalibration may be scheduled.");
+
+static int jumpy_delay = 20;
+module_param(jumpy_delay, int, 0644);
+MODULE_PARM_DESC(jumpy_delay,
+	"delay (ms) before recal after jumpiness detected");
+
+static int spew_delay = 1;
+module_param(spew_delay, int, 0644);
+MODULE_PARM_DESC(spew_delay,
+	"delay (ms) before recal after packet spew detected");
+
+static int recal_guard_time;
+module_param(recal_guard_time, int, 0644);
+MODULE_PARM_DESC(recal_guard_time,
+	"interval (ms) during which recal will be restarted if packet received");
+
+static int post_interrupt_delay = 40;
+module_param(post_interrupt_delay, int, 0644);
+MODULE_PARM_DESC(post_interrupt_delay,
+	"delay (ms) before recal after recal interrupt detected");
+
+static bool autorecal = true;
+module_param(autorecal, bool, 0644);
+MODULE_PARM_DESC(autorecal, "enable recalibration in the driver");
+
+static char hgpk_mode_name[16];
+module_param_string(hgpk_mode, hgpk_mode_name, sizeof(hgpk_mode_name), 0644);
+MODULE_PARM_DESC(hgpk_mode,
+	"default hgpk mode: mouse, glidesensor or pentablet");
+
+static int hgpk_default_mode = HGPK_MODE_MOUSE;
+
+static const char * const hgpk_mode_names[] = {
+	[HGPK_MODE_MOUSE] = "Mouse",
+	[HGPK_MODE_GLIDESENSOR] = "GlideSensor",
+	[HGPK_MODE_PENTABLET] = "PenTablet",
+};
+
+static int hgpk_mode_from_name(const char *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hgpk_mode_names); i++) {
+		const char *name = hgpk_mode_names[i];
+		if (strlen(name) == len && !strncasecmp(name, buf, len))
+			return i;
+	}
+
+	return HGPK_MODE_INVALID;
+}
+
+/*
+ * see if new value is within 20% of half of old value
+ */
+static int approx_half(int curr, int prev)
+{
+	int belowhalf, abovehalf;
+
+	if (curr < 5 || prev < 5)
+		return 0;
+
+	belowhalf = (prev * 8) / 20;
+	abovehalf = (prev * 12) / 20;
+
+	return belowhalf < curr && curr <= abovehalf;
+}
+
+/*
+ * Throw out oddly large delta packets, and any that immediately follow whose
+ * values are each approximately half of the previous.  It seems that the ALPS
+ * firmware emits errant packets, and they get averaged out slowly.
+ */
+static int hgpk_discard_decay_hack(struct psmouse *psmouse, int x, int y)
+{
+	struct hgpk_data *priv = psmouse->private;
+	int avx, avy;
+	bool do_recal = false;
+
+	avx = abs(x);
+	avy = abs(y);
+
+	/* discard if too big, or half that but > 4 times the prev delta */
+	if (avx > recalib_delta ||
+		(avx > recalib_delta / 2 && ((avx / 4) > priv->xlast))) {
+		psmouse_warn(psmouse, "detected %dpx jump in x\n", x);
+		priv->xbigj = avx;
+	} else if (approx_half(avx, priv->xbigj)) {
+		psmouse_warn(psmouse, "detected secondary %dpx jump in x\n", x);
+		priv->xbigj = avx;
+		priv->xsaw_secondary++;
+	} else {
+		if (priv->xbigj && priv->xsaw_secondary > 1)
+			do_recal = true;
+		priv->xbigj = 0;
+		priv->xsaw_secondary = 0;
+	}
+
+	if (avy > recalib_delta ||
+		(avy > recalib_delta / 2 && ((avy / 4) > priv->ylast))) {
+		psmouse_warn(psmouse, "detected %dpx jump in y\n", y);
+		priv->ybigj = avy;
+	} else if (approx_half(avy, priv->ybigj)) {
+		psmouse_warn(psmouse, "detected secondary %dpx jump in y\n", y);
+		priv->ybigj = avy;
+		priv->ysaw_secondary++;
+	} else {
+		if (priv->ybigj && priv->ysaw_secondary > 1)
+			do_recal = true;
+		priv->ybigj = 0;
+		priv->ysaw_secondary = 0;
+	}
+
+	priv->xlast = avx;
+	priv->ylast = avy;
+
+	if (do_recal && jumpy_delay) {
+		psmouse_warn(psmouse, "scheduling recalibration\n");
+		psmouse_queue_work(psmouse, &priv->recalib_wq,
+				msecs_to_jiffies(jumpy_delay));
+	}
+
+	return priv->xbigj || priv->ybigj;
+}
+
+static void hgpk_reset_spew_detection(struct hgpk_data *priv)
+{
+	priv->spew_count = 0;
+	priv->dupe_count = 0;
+	priv->x_tally = 0;
+	priv->y_tally = 0;
+	priv->spew_flag = NO_SPEW;
+}
+
+static void hgpk_reset_hack_state(struct psmouse *psmouse)
+{
+	struct hgpk_data *priv = psmouse->private;
+
+	priv->abs_x = priv->abs_y = -1;
+	priv->xlast = priv->ylast = ILLEGAL_XY;
+	priv->xbigj = priv->ybigj = 0;
+	priv->xsaw_secondary = priv->ysaw_secondary = 0;
+	hgpk_reset_spew_detection(priv);
+}
+
+/*
+ * We have no idea why this particular hardware bug occurs.  The touchpad
+ * will randomly start spewing packets without anything touching the
+ * pad.  This wouldn't necessarily be bad, but it's indicative of a
+ * severely miscalibrated pad; attempting to use the touchpad while it's
+ * spewing means the cursor will jump all over the place, and act "drunk".
+ *
+ * The packets that are spewed tend to all have deltas between -2 and 2, and
+ * the cursor will move around without really going very far.  It will
+ * tend to end up in the same location; if we tally up the changes over
+ * 100 packets, we end up w/ a final delta of close to 0.  This happens
+ * pretty regularly when the touchpad is spewing, and is pretty hard to
+ * manually trigger (at least for *my* fingers).  So, it makes a perfect
+ * scheme for detecting spews.
+ */
+static void hgpk_spewing_hack(struct psmouse *psmouse,
+			      int l, int r, int x, int y)
+{
+	struct hgpk_data *priv = psmouse->private;
+
+	/* ignore button press packets; many in a row could trigger
+	 * a false-positive! */
+	if (l || r)
+		return;
+
+	/* don't track spew if the workaround feature has been turned off */
+	if (!spew_delay)
+		return;
+
+	if (abs(x) > 3 || abs(y) > 3) {
+		/* no spew, or spew ended */
+		hgpk_reset_spew_detection(priv);
+		return;
+	}
+
+	/* Keep a tally of the overall delta to the cursor position caused by
+	 * the spew */
+	priv->x_tally += x;
+	priv->y_tally += y;
+
+	switch (priv->spew_flag) {
+	case NO_SPEW:
+		/* we're not spewing, but this packet might be the start */
+		priv->spew_flag = MAYBE_SPEWING;
+
+		/* fall-through */
+
+	case MAYBE_SPEWING:
+		priv->spew_count++;
+
+		if (priv->spew_count < SPEW_WATCH_COUNT)
+			break;
+
+		/* excessive spew detected, request recalibration */
+		priv->spew_flag = SPEW_DETECTED;
+
+		/* fall-through */
+
+	case SPEW_DETECTED:
+		/* only recalibrate when the overall delta to the cursor
+		 * is really small. if the spew is causing significant cursor
+		 * movement, it is probably a case of the user moving the
+		 * cursor very slowly across the screen. */
+		if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) {
+			psmouse_warn(psmouse, "packet spew detected (%d,%d)\n",
+				     priv->x_tally, priv->y_tally);
+			priv->spew_flag = RECALIBRATING;
+			psmouse_queue_work(psmouse, &priv->recalib_wq,
+					   msecs_to_jiffies(spew_delay));
+		}
+
+		break;
+	case RECALIBRATING:
+		/* we already detected a spew and requested a recalibration,
+		 * just wait for the queue to kick into action. */
+		break;
+	}
+}
+
+/*
+ * HGPK Mouse Mode format (standard mouse format, sans middle button)
+ *
+ * byte 0:	y-over	x-over	y-neg	x-neg	1	0	swr	swl
+ * byte 1:	x7	x6	x5	x4	x3	x2	x1	x0
+ * byte 2:	y7	y6	y5	y4	y3	y2	y1	y0
+ *
+ * swr/swl are the left/right buttons.
+ * x-neg/y-neg are the x and y delta negative bits
+ * x-over/y-over are the x and y overflow bits
+ *
+ * ---
+ *
+ * HGPK Advanced Mode - single-mode format
+ *
+ * byte 0(PT):  1    1    0    0    1    1     1     1
+ * byte 0(GS):  1    1    1    1    1    1     1     1
+ * byte 1:      0   x6   x5   x4   x3   x2    x1    x0
+ * byte 2(PT):  0    0   x9   x8   x7    ? pt-dsw    0
+ * byte 2(GS):  0  x10   x9   x8   x7    ? gs-dsw pt-dsw
+ * byte 3:      0   y9   y8   y7    1    0   swr   swl
+ * byte 4:      0   y6   y5   y4   y3   y2    y1    y0
+ * byte 5:      0   z6   z5   z4   z3   z2    z1    z0
+ *
+ * ?'s are not defined in the protocol spec, may vary between models.
+ *
+ * swr/swl are the left/right buttons.
+ *
+ * pt-dsw/gs-dsw indicate that the pt/gs sensor is detecting a
+ * pen/finger
+ */
+static bool hgpk_is_byte_valid(struct psmouse *psmouse, unsigned char *packet)
+{
+	struct hgpk_data *priv = psmouse->private;
+	int pktcnt = psmouse->pktcnt;
+	bool valid;
+
+	switch (priv->mode) {
+	case HGPK_MODE_MOUSE:
+		valid = (packet[0] & 0x0C) == 0x08;
+		break;
+
+	case HGPK_MODE_GLIDESENSOR:
+		valid = pktcnt == 1 ?
+			packet[0] == HGPK_GS : !(packet[pktcnt - 1] & 0x80);
+		break;
+
+	case HGPK_MODE_PENTABLET:
+		valid = pktcnt == 1 ?
+			packet[0] == HGPK_PT : !(packet[pktcnt - 1] & 0x80);
+		break;
+
+	default:
+		valid = false;
+		break;
+	}
+
+	if (!valid)
+		psmouse_dbg(psmouse,
+			    "bad data, mode %d (%d) %*ph\n",
+			    priv->mode, pktcnt, 6, psmouse->packet);
+
+	return valid;
+}
+
+static void hgpk_process_advanced_packet(struct psmouse *psmouse)
+{
+	struct hgpk_data *priv = psmouse->private;
+	struct input_dev *idev = psmouse->dev;
+	unsigned char *packet = psmouse->packet;
+	int down = !!(packet[2] & 2);
+	int left = !!(packet[3] & 1);
+	int right = !!(packet[3] & 2);
+	int x = packet[1] | ((packet[2] & 0x78) << 4);
+	int y = packet[4] | ((packet[3] & 0x70) << 3);
+
+	if (priv->mode == HGPK_MODE_GLIDESENSOR) {
+		int pt_down = !!(packet[2] & 1);
+		int finger_down = !!(packet[2] & 2);
+		int z = packet[5];
+
+		input_report_abs(idev, ABS_PRESSURE, z);
+		if (tpdebug)
+			psmouse_dbg(psmouse, "pd=%d fd=%d z=%d",
+				    pt_down, finger_down, z);
+	} else {
+		/*
+		 * PenTablet mode does not report pressure, so we don't
+		 * report it here
+		 */
+		if (tpdebug)
+			psmouse_dbg(psmouse, "pd=%d ", down);
+	}
+
+	if (tpdebug)
+		psmouse_dbg(psmouse, "l=%d r=%d x=%d y=%d\n",
+			    left, right, x, y);
+
+	input_report_key(idev, BTN_TOUCH, down);
+	input_report_key(idev, BTN_LEFT, left);
+	input_report_key(idev, BTN_RIGHT, right);
+
+	/*
+	 * If this packet says that the finger was removed, reset our position
+	 * tracking so that we don't erroneously detect a jump on next press.
+	 */
+	if (!down) {
+		hgpk_reset_hack_state(psmouse);
+		goto done;
+	}
+
+	/*
+	 * Weed out duplicate packets (we get quite a few, and they mess up
+	 * our jump detection)
+	 */
+	if (x == priv->abs_x && y == priv->abs_y) {
+		if (++priv->dupe_count > SPEW_WATCH_COUNT) {
+			if (tpdebug)
+				psmouse_dbg(psmouse, "hard spew detected\n");
+			priv->spew_flag = RECALIBRATING;
+			psmouse_queue_work(psmouse, &priv->recalib_wq,
+					   msecs_to_jiffies(spew_delay));
+		}
+		goto done;
+	}
+
+	/* not a duplicate, continue with position reporting */
+	priv->dupe_count = 0;
+
+	/* Don't apply hacks in PT mode, it seems reliable */
+	if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) {
+		int x_diff = priv->abs_x - x;
+		int y_diff = priv->abs_y - y;
+		if (hgpk_discard_decay_hack(psmouse, x_diff, y_diff)) {
+			if (tpdebug)
+				psmouse_dbg(psmouse, "discarding\n");
+			goto done;
+		}
+		hgpk_spewing_hack(psmouse, left, right, x_diff, y_diff);
+	}
+
+	input_report_abs(idev, ABS_X, x);
+	input_report_abs(idev, ABS_Y, y);
+	priv->abs_x = x;
+	priv->abs_y = y;
+
+done:
+	input_sync(idev);
+}
+
+static void hgpk_process_simple_packet(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	unsigned char *packet = psmouse->packet;
+	int left = packet[0] & 1;
+	int right = (packet[0] >> 1) & 1;
+	int x = packet[1] - ((packet[0] << 4) & 0x100);
+	int y = ((packet[0] << 3) & 0x100) - packet[2];
+
+	if (packet[0] & 0xc0)
+		psmouse_dbg(psmouse,
+			    "overflow -- 0x%02x 0x%02x 0x%02x\n",
+			    packet[0], packet[1], packet[2]);
+
+	if (hgpk_discard_decay_hack(psmouse, x, y)) {
+		if (tpdebug)
+			psmouse_dbg(psmouse, "discarding\n");
+		return;
+	}
+
+	hgpk_spewing_hack(psmouse, left, right, x, y);
+
+	if (tpdebug)
+		psmouse_dbg(psmouse, "l=%d r=%d x=%d y=%d\n",
+			    left, right, x, y);
+
+	input_report_key(dev, BTN_LEFT, left);
+	input_report_key(dev, BTN_RIGHT, right);
+
+	input_report_rel(dev, REL_X, x);
+	input_report_rel(dev, REL_Y, y);
+
+	input_sync(dev);
+}
+
+static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)
+{
+	struct hgpk_data *priv = psmouse->private;
+
+	if (!hgpk_is_byte_valid(psmouse, psmouse->packet))
+		return PSMOUSE_BAD_DATA;
+
+	if (psmouse->pktcnt >= psmouse->pktsize) {
+		if (priv->mode == HGPK_MODE_MOUSE)
+			hgpk_process_simple_packet(psmouse);
+		else
+			hgpk_process_advanced_packet(psmouse);
+		return PSMOUSE_FULL_PACKET;
+	}
+
+	if (priv->recalib_window) {
+		if (time_before(jiffies, priv->recalib_window)) {
+			/*
+			 * ugh, got a packet inside our recalibration
+			 * window, schedule another recalibration.
+			 */
+			psmouse_dbg(psmouse,
+				    "packet inside calibration window, queueing another recalibration\n");
+			psmouse_queue_work(psmouse, &priv->recalib_wq,
+					msecs_to_jiffies(post_interrupt_delay));
+		}
+		priv->recalib_window = 0;
+	}
+
+	return PSMOUSE_GOOD_DATA;
+}
+
+static int hgpk_select_mode(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	struct hgpk_data *priv = psmouse->private;
+	int i;
+	int cmd;
+
+	/*
+	 * 4 disables to enable advanced mode
+	 * then 3 0xf2 bytes as the preamble for GS/PT selection
+	 */
+	const int advanced_init[] = {
+		PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE,
+		PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE,
+		0xf2, 0xf2, 0xf2,
+	};
+
+	switch (priv->mode) {
+	case HGPK_MODE_MOUSE:
+		psmouse->pktsize = 3;
+		break;
+
+	case HGPK_MODE_GLIDESENSOR:
+	case HGPK_MODE_PENTABLET:
+		psmouse->pktsize = 6;
+
+		/* Switch to 'Advanced mode.', four disables in a row. */
+		for (i = 0; i < ARRAY_SIZE(advanced_init); i++)
+			if (ps2_command(ps2dev, NULL, advanced_init[i]))
+				return -EIO;
+
+		/* select between GlideSensor (mouse) or PenTablet */
+		cmd = priv->mode == HGPK_MODE_GLIDESENSOR ?
+			PSMOUSE_CMD_SETSCALE11 : PSMOUSE_CMD_SETSCALE21;
+
+		if (ps2_command(ps2dev, NULL, cmd))
+			return -EIO;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void hgpk_setup_input_device(struct input_dev *input,
+				    struct input_dev *old_input,
+				    enum hgpk_mode mode)
+{
+	if (old_input) {
+		input->name = old_input->name;
+		input->phys = old_input->phys;
+		input->id = old_input->id;
+		input->dev.parent = old_input->dev.parent;
+	}
+
+	memset(input->evbit, 0, sizeof(input->evbit));
+	memset(input->relbit, 0, sizeof(input->relbit));
+	memset(input->keybit, 0, sizeof(input->keybit));
+
+	/* All modes report left and right buttons */
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(BTN_LEFT, input->keybit);
+	__set_bit(BTN_RIGHT, input->keybit);
+
+	switch (mode) {
+	case HGPK_MODE_MOUSE:
+		__set_bit(EV_REL, input->evbit);
+		__set_bit(REL_X, input->relbit);
+		__set_bit(REL_Y, input->relbit);
+		break;
+
+	case HGPK_MODE_GLIDESENSOR:
+		__set_bit(BTN_TOUCH, input->keybit);
+		__set_bit(BTN_TOOL_FINGER, input->keybit);
+
+		__set_bit(EV_ABS, input->evbit);
+
+		/* GlideSensor has pressure sensor, PenTablet does not */
+		input_set_abs_params(input, ABS_PRESSURE, 0, 15, 0, 0);
+
+		/* From device specs */
+		input_set_abs_params(input, ABS_X, 0, 399, 0, 0);
+		input_set_abs_params(input, ABS_Y, 0, 290, 0, 0);
+
+		/* Calculated by hand based on usable size (52mm x 38mm) */
+		input_abs_set_res(input, ABS_X, 8);
+		input_abs_set_res(input, ABS_Y, 8);
+		break;
+
+	case HGPK_MODE_PENTABLET:
+		__set_bit(BTN_TOUCH, input->keybit);
+		__set_bit(BTN_TOOL_FINGER, input->keybit);
+
+		__set_bit(EV_ABS, input->evbit);
+
+		/* From device specs */
+		input_set_abs_params(input, ABS_X, 0, 999, 0, 0);
+		input_set_abs_params(input, ABS_Y, 5, 239, 0, 0);
+
+		/* Calculated by hand based on usable size (156mm x 38mm) */
+		input_abs_set_res(input, ABS_X, 6);
+		input_abs_set_res(input, ABS_Y, 8);
+		break;
+
+	default:
+		BUG();
+	}
+}
+
+static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate)
+{
+	int err;
+
+	psmouse_reset(psmouse);
+
+	if (recalibrate) {
+		struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+		/* send the recalibrate request */
+		if (ps2_command(ps2dev, NULL, 0xf5) ||
+		    ps2_command(ps2dev, NULL, 0xf5) ||
+		    ps2_command(ps2dev, NULL, 0xe6) ||
+		    ps2_command(ps2dev, NULL, 0xf5)) {
+			return -1;
+		}
+
+		/* according to ALPS, 150mS is required for recalibration */
+		msleep(150);
+	}
+
+	err = hgpk_select_mode(psmouse);
+	if (err) {
+		psmouse_err(psmouse, "failed to select mode\n");
+		return err;
+	}
+
+	hgpk_reset_hack_state(psmouse);
+
+	return 0;
+}
+
+static int hgpk_force_recalibrate(struct psmouse *psmouse)
+{
+	struct hgpk_data *priv = psmouse->private;
+	int err;
+
+	/* C-series touchpads added the recalibrate command */
+	if (psmouse->model < HGPK_MODEL_C)
+		return 0;
+
+	if (!autorecal) {
+		psmouse_dbg(psmouse, "recalibration disabled, ignoring\n");
+		return 0;
+	}
+
+	psmouse_dbg(psmouse, "recalibrating touchpad..\n");
+
+	/* we don't want to race with the irq handler, nor with resyncs */
+	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+	/* start by resetting the device */
+	err = hgpk_reset_device(psmouse, true);
+	if (err)
+		return err;
+
+	/*
+	 * XXX: If a finger is down during this delay, recalibration will
+	 * detect capacitance incorrectly.  This is a hardware bug, and
+	 * we don't have a good way to deal with it.  The 2s window stuff
+	 * (below) is our best option for now.
+	 */
+	if (psmouse_activate(psmouse))
+		return -1;
+
+	if (tpdebug)
+		psmouse_dbg(psmouse, "touchpad reactivated\n");
+
+	/*
+	 * If we get packets right away after recalibrating, it's likely
+	 * that a finger was on the touchpad.  If so, it's probably
+	 * miscalibrated, so we optionally schedule another.
+	 */
+	if (recal_guard_time)
+		priv->recalib_window = jiffies +
+			msecs_to_jiffies(recal_guard_time);
+
+	return 0;
+}
+
+/*
+ * This puts the touchpad in a power saving mode; according to ALPS, current
+ * consumption goes down to 50uA after running this.  To turn power back on,
+ * we drive MS-DAT low.  Measuring with a 1mA resolution ammeter says that
+ * the current on the SUS_3.3V rail drops from 3mA or 4mA to 0 when we do this.
+ *
+ * We have no formal spec that details this operation -- the low-power
+ * sequence came from a long-lost email trail.
+ */
+static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	int timeo;
+	int err;
+
+	/* Added on D-series touchpads */
+	if (psmouse->model < HGPK_MODEL_D)
+		return 0;
+
+	if (enable) {
+		psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+		/*
+		 * Sending a byte will drive MS-DAT low; this will wake up
+		 * the controller.  Once we get an ACK back from it, it
+		 * means we can continue with the touchpad re-init.  ALPS
+		 * tells us that 1s should be long enough, so set that as
+		 * the upper bound. (in practice, it takes about 3 loops.)
+		 */
+		for (timeo = 20; timeo > 0; timeo--) {
+			if (!ps2_sendbyte(&psmouse->ps2dev,
+					PSMOUSE_CMD_DISABLE, 20))
+				break;
+			msleep(25);
+		}
+
+		err = hgpk_reset_device(psmouse, false);
+		if (err) {
+			psmouse_err(psmouse, "Failed to reset device!\n");
+			return err;
+		}
+
+		/* should be all set, enable the touchpad */
+		psmouse_activate(psmouse);
+		psmouse_dbg(psmouse, "Touchpad powered up.\n");
+	} else {
+		psmouse_dbg(psmouse, "Powering off touchpad.\n");
+
+		if (ps2_command(ps2dev, NULL, 0xec) ||
+		    ps2_command(ps2dev, NULL, 0xec) ||
+		    ps2_command(ps2dev, NULL, 0xea)) {
+			return -1;
+		}
+
+		psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+
+		/* probably won't see an ACK, the touchpad will be off */
+		ps2_sendbyte(&psmouse->ps2dev, 0xec, 20);
+	}
+
+	return 0;
+}
+
+static int hgpk_poll(struct psmouse *psmouse)
+{
+	/* We can't poll, so always return failure. */
+	return -1;
+}
+
+static int hgpk_reconnect(struct psmouse *psmouse)
+{
+	struct hgpk_data *priv = psmouse->private;
+
+	/*
+	 * During suspend/resume the ps2 rails remain powered.  We don't want
+	 * to do a reset because it's flush data out of buffers; however,
+	 * earlier prototypes (B1) had some brokenness that required a reset.
+	 */
+	if (olpc_board_at_least(olpc_board(0xb2)))
+		if (psmouse->ps2dev.serio->dev.power.power_state.event !=
+				PM_EVENT_ON)
+			return 0;
+
+	priv->powered = 1;
+	return hgpk_reset_device(psmouse, false);
+}
+
+static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf)
+{
+	struct hgpk_data *priv = psmouse->private;
+
+	return sprintf(buf, "%d\n", priv->powered);
+}
+
+static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,
+				const char *buf, size_t count)
+{
+	struct hgpk_data *priv = psmouse->private;
+	unsigned int value;
+	int err;
+
+	err = kstrtouint(buf, 10, &value);
+	if (err)
+		return err;
+
+	if (value > 1)
+		return -EINVAL;
+
+	if (value != priv->powered) {
+		/*
+		 * hgpk_toggle_power will deal w/ state so
+		 * we're not racing w/ irq
+		 */
+		err = hgpk_toggle_powersave(psmouse, value);
+		if (!err)
+			priv->powered = value;
+	}
+
+	return err ? err : count;
+}
+
+__PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL,
+		      hgpk_show_powered, hgpk_set_powered, false);
+
+static ssize_t attr_show_mode(struct psmouse *psmouse, void *data, char *buf)
+{
+	struct hgpk_data *priv = psmouse->private;
+
+	return sprintf(buf, "%s\n", hgpk_mode_names[priv->mode]);
+}
+
+static ssize_t attr_set_mode(struct psmouse *psmouse, void *data,
+			     const char *buf, size_t len)
+{
+	struct hgpk_data *priv = psmouse->private;
+	enum hgpk_mode old_mode = priv->mode;
+	enum hgpk_mode new_mode = hgpk_mode_from_name(buf, len);
+	struct input_dev *old_dev = psmouse->dev;
+	struct input_dev *new_dev;
+	int err;
+
+	if (new_mode == HGPK_MODE_INVALID)
+		return -EINVAL;
+
+	if (old_mode == new_mode)
+		return len;
+
+	new_dev = input_allocate_device();
+	if (!new_dev)
+		return -ENOMEM;
+
+	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+	/* Switch device into the new mode */
+	priv->mode = new_mode;
+	err = hgpk_reset_device(psmouse, false);
+	if (err)
+		goto err_try_restore;
+
+	hgpk_setup_input_device(new_dev, old_dev, new_mode);
+
+	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+	err = input_register_device(new_dev);
+	if (err)
+		goto err_try_restore;
+
+	psmouse->dev = new_dev;
+	input_unregister_device(old_dev);
+
+	return len;
+
+err_try_restore:
+	input_free_device(new_dev);
+	priv->mode = old_mode;
+	hgpk_reset_device(psmouse, false);
+
+	return err;
+}
+
+PSMOUSE_DEFINE_ATTR(hgpk_mode, S_IWUSR | S_IRUGO, NULL,
+		    attr_show_mode, attr_set_mode);
+
+static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse,
+		void *data, char *buf)
+{
+	return -EINVAL;
+}
+
+static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data,
+				const char *buf, size_t count)
+{
+	struct hgpk_data *priv = psmouse->private;
+	unsigned int value;
+	int err;
+
+	err = kstrtouint(buf, 10, &value);
+	if (err)
+		return err;
+
+	if (value != 1)
+		return -EINVAL;
+
+	/*
+	 * We queue work instead of doing recalibration right here
+	 * to avoid adding locking to to hgpk_force_recalibrate()
+	 * since workqueue provides serialization.
+	 */
+	psmouse_queue_work(psmouse, &priv->recalib_wq, 0);
+	return count;
+}
+
+__PSMOUSE_DEFINE_ATTR(recalibrate, S_IWUSR | S_IRUGO, NULL,
+		      hgpk_trigger_recal_show, hgpk_trigger_recal, false);
+
+static void hgpk_disconnect(struct psmouse *psmouse)
+{
+	struct hgpk_data *priv = psmouse->private;
+
+	device_remove_file(&psmouse->ps2dev.serio->dev,
+			   &psmouse_attr_powered.dattr);
+	device_remove_file(&psmouse->ps2dev.serio->dev,
+			   &psmouse_attr_hgpk_mode.dattr);
+
+	if (psmouse->model >= HGPK_MODEL_C)
+		device_remove_file(&psmouse->ps2dev.serio->dev,
+				   &psmouse_attr_recalibrate.dattr);
+
+	psmouse_reset(psmouse);
+	kfree(priv);
+}
+
+static void hgpk_recalib_work(struct work_struct *work)
+{
+	struct delayed_work *w = to_delayed_work(work);
+	struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq);
+	struct psmouse *psmouse = priv->psmouse;
+
+	if (hgpk_force_recalibrate(psmouse))
+		psmouse_err(psmouse, "recalibration failed!\n");
+}
+
+static int hgpk_register(struct psmouse *psmouse)
+{
+	struct hgpk_data *priv = psmouse->private;
+	int err;
+
+	/* register handlers */
+	psmouse->protocol_handler = hgpk_process_byte;
+	psmouse->poll = hgpk_poll;
+	psmouse->disconnect = hgpk_disconnect;
+	psmouse->reconnect = hgpk_reconnect;
+
+	/* Disable the idle resync. */
+	psmouse->resync_time = 0;
+	/* Reset after a lot of bad bytes. */
+	psmouse->resetafter = 1024;
+
+	hgpk_setup_input_device(psmouse->dev, NULL, priv->mode);
+
+	err = device_create_file(&psmouse->ps2dev.serio->dev,
+				 &psmouse_attr_powered.dattr);
+	if (err) {
+		psmouse_err(psmouse, "Failed creating 'powered' sysfs node\n");
+		return err;
+	}
+
+	err = device_create_file(&psmouse->ps2dev.serio->dev,
+				 &psmouse_attr_hgpk_mode.dattr);
+	if (err) {
+		psmouse_err(psmouse,
+			    "Failed creating 'hgpk_mode' sysfs node\n");
+		goto err_remove_powered;
+	}
+
+	/* C-series touchpads added the recalibrate command */
+	if (psmouse->model >= HGPK_MODEL_C) {
+		err = device_create_file(&psmouse->ps2dev.serio->dev,
+					 &psmouse_attr_recalibrate.dattr);
+		if (err) {
+			psmouse_err(psmouse,
+				    "Failed creating 'recalibrate' sysfs node\n");
+			goto err_remove_mode;
+		}
+	}
+
+	return 0;
+
+err_remove_mode:
+	device_remove_file(&psmouse->ps2dev.serio->dev,
+			   &psmouse_attr_hgpk_mode.dattr);
+err_remove_powered:
+	device_remove_file(&psmouse->ps2dev.serio->dev,
+			   &psmouse_attr_powered.dattr);
+	return err;
+}
+
+int hgpk_init(struct psmouse *psmouse)
+{
+	struct hgpk_data *priv;
+	int err;
+
+	priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL);
+	if (!priv) {
+		err = -ENOMEM;
+		goto alloc_fail;
+	}
+
+	psmouse->private = priv;
+
+	priv->psmouse = psmouse;
+	priv->powered = true;
+	priv->mode = hgpk_default_mode;
+	INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work);
+
+	err = hgpk_reset_device(psmouse, false);
+	if (err)
+		goto init_fail;
+
+	err = hgpk_register(psmouse);
+	if (err)
+		goto init_fail;
+
+	return 0;
+
+init_fail:
+	kfree(priv);
+alloc_fail:
+	return err;
+}
+
+static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[3];
+
+	/* E7, E7, E7, E9 gets us a 3 byte identifier */
+	if (ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
+	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
+	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
+	    ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
+		return -EIO;
+	}
+
+	psmouse_dbg(psmouse, "ID: %*ph\n", 3, param);
+
+	/* HGPK signature: 0x67, 0x00, 0x<model> */
+	if (param[0] != 0x67 || param[1] != 0x00)
+		return -ENODEV;
+
+	psmouse_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]);
+
+	return param[2];
+}
+
+int hgpk_detect(struct psmouse *psmouse, bool set_properties)
+{
+	int version;
+
+	version = hgpk_get_model(psmouse);
+	if (version < 0)
+		return version;
+
+	if (set_properties) {
+		psmouse->vendor = "ALPS";
+		psmouse->name = "HGPK";
+		psmouse->model = version;
+	}
+
+	return 0;
+}
+
+void hgpk_module_init(void)
+{
+	hgpk_default_mode = hgpk_mode_from_name(hgpk_mode_name,
+						strlen(hgpk_mode_name));
+	if (hgpk_default_mode == HGPK_MODE_INVALID) {
+		hgpk_default_mode = HGPK_MODE_MOUSE;
+		strlcpy(hgpk_mode_name, hgpk_mode_names[HGPK_MODE_MOUSE],
+			sizeof(hgpk_mode_name));
+	}
+}
diff --git a/drivers/input/mouse/hgpk.h b/drivers/input/mouse/hgpk.h
new file mode 100644
index 0000000..dd68677
--- /dev/null
+++ b/drivers/input/mouse/hgpk.h
@@ -0,0 +1,67 @@
+/*
+ * OLPC HGPK (XO-1) touchpad PS/2 mouse driver
+ */
+
+#ifndef _HGPK_H
+#define _HGPK_H
+
+#define HGPK_GS		0xff       /* The GlideSensor */
+#define HGPK_PT		0xcf       /* The PenTablet */
+
+enum hgpk_model_t {
+	HGPK_MODEL_PREA = 0x0a,	/* pre-B1s */
+	HGPK_MODEL_A = 0x14,	/* found on B1s, PT disabled in hardware */
+	HGPK_MODEL_B = 0x28,	/* B2s, has capacitance issues */
+	HGPK_MODEL_C = 0x3c,
+	HGPK_MODEL_D = 0x50,	/* C1, mass production */
+};
+
+enum hgpk_spew_flag {
+	NO_SPEW,
+	MAYBE_SPEWING,
+	SPEW_DETECTED,
+	RECALIBRATING,
+};
+
+#define SPEW_WATCH_COUNT 42  /* at 12ms/packet, this is 1/2 second */
+
+enum hgpk_mode {
+	HGPK_MODE_MOUSE,
+	HGPK_MODE_GLIDESENSOR,
+	HGPK_MODE_PENTABLET,
+	HGPK_MODE_INVALID
+};
+
+struct hgpk_data {
+	struct psmouse *psmouse;
+	enum hgpk_mode mode;
+	bool powered;
+	enum hgpk_spew_flag spew_flag;
+	int spew_count, x_tally, y_tally;	/* spew detection */
+	unsigned long recalib_window;
+	struct delayed_work recalib_wq;
+	int abs_x, abs_y;
+	int dupe_count;
+	int xbigj, ybigj, xlast, ylast; /* jumpiness detection */
+	int xsaw_secondary, ysaw_secondary; /* jumpiness detection */
+};
+
+#ifdef CONFIG_MOUSE_PS2_OLPC
+void hgpk_module_init(void);
+int hgpk_detect(struct psmouse *psmouse, bool set_properties);
+int hgpk_init(struct psmouse *psmouse);
+#else
+static inline void hgpk_module_init(void)
+{
+}
+static inline int hgpk_detect(struct psmouse *psmouse, bool set_properties)
+{
+	return -ENODEV;
+}
+static inline int hgpk_init(struct psmouse *psmouse)
+{
+	return -ENODEV;
+}
+#endif
+
+#endif
diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c
new file mode 100644
index 0000000..3827a22
--- /dev/null
+++ b/drivers/input/mouse/inport.c
@@ -0,0 +1,196 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Teemu Rantanen		Derrick Cole
+ *	Peter Cervasio		Christoph Niemann
+ *	Philip Blundell		Russell King
+ *	Bob Harris
+ */
+
+/*
+ * Inport (ATI XL and Microsoft) busmouse driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Inport (ATI XL and Microsoft) busmouse driver");
+MODULE_LICENSE("GPL");
+
+#define INPORT_BASE		0x23c
+#define INPORT_EXTENT		4
+
+#define INPORT_CONTROL_PORT	INPORT_BASE + 0
+#define INPORT_DATA_PORT	INPORT_BASE + 1
+#define INPORT_SIGNATURE_PORT	INPORT_BASE + 2
+
+#define INPORT_REG_BTNS	0x00
+#define INPORT_REG_X		0x01
+#define INPORT_REG_Y		0x02
+#define INPORT_REG_MODE		0x07
+#define INPORT_RESET		0x80
+
+#ifdef CONFIG_MOUSE_ATIXL
+#define INPORT_NAME		"ATI XL Mouse"
+#define INPORT_VENDOR		0x0002
+#define INPORT_SPEED_30HZ	0x01
+#define INPORT_SPEED_50HZ	0x02
+#define INPORT_SPEED_100HZ	0x03
+#define INPORT_SPEED_200HZ	0x04
+#define INPORT_MODE_BASE	INPORT_SPEED_100HZ
+#define INPORT_MODE_IRQ		0x08
+#else
+#define INPORT_NAME		"Microsoft InPort Mouse"
+#define INPORT_VENDOR		0x0001
+#define INPORT_MODE_BASE	0x10
+#define INPORT_MODE_IRQ		0x01
+#endif
+#define INPORT_MODE_HOLD	0x20
+
+#define INPORT_IRQ		5
+
+static int inport_irq = INPORT_IRQ;
+module_param_named(irq, inport_irq, uint, 0);
+MODULE_PARM_DESC(irq, "IRQ number (5=default)");
+
+static struct input_dev *inport_dev;
+
+static irqreturn_t inport_interrupt(int irq, void *dev_id)
+{
+	unsigned char buttons;
+
+	outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+	outb(INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
+
+	outb(INPORT_REG_X, INPORT_CONTROL_PORT);
+	input_report_rel(inport_dev, REL_X, inb(INPORT_DATA_PORT));
+
+	outb(INPORT_REG_Y, INPORT_CONTROL_PORT);
+	input_report_rel(inport_dev, REL_Y, inb(INPORT_DATA_PORT));
+
+	outb(INPORT_REG_BTNS, INPORT_CONTROL_PORT);
+	buttons = inb(INPORT_DATA_PORT);
+
+	input_report_key(inport_dev, BTN_MIDDLE, buttons & 1);
+	input_report_key(inport_dev, BTN_LEFT,   buttons & 2);
+	input_report_key(inport_dev, BTN_RIGHT,  buttons & 4);
+
+	outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+	outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
+
+	input_sync(inport_dev);
+	return IRQ_HANDLED;
+}
+
+static int inport_open(struct input_dev *dev)
+{
+	if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL))
+		return -EBUSY;
+	outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+	outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
+
+	return 0;
+}
+
+static void inport_close(struct input_dev *dev)
+{
+	outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+	outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
+	free_irq(inport_irq, NULL);
+}
+
+static int __init inport_init(void)
+{
+	unsigned char a, b, c;
+	int err;
+
+	if (!request_region(INPORT_BASE, INPORT_EXTENT, "inport")) {
+		printk(KERN_ERR "inport.c: Can't allocate ports at %#x\n", INPORT_BASE);
+		return -EBUSY;
+	}
+
+	a = inb(INPORT_SIGNATURE_PORT);
+	b = inb(INPORT_SIGNATURE_PORT);
+	c = inb(INPORT_SIGNATURE_PORT);
+	if (a == b || a != c) {
+		printk(KERN_INFO "inport.c: Didn't find InPort mouse at %#x\n", INPORT_BASE);
+		err = -ENODEV;
+		goto err_release_region;
+	}
+
+	inport_dev = input_allocate_device();
+	if (!inport_dev) {
+		printk(KERN_ERR "inport.c: Not enough memory for input device\n");
+		err = -ENOMEM;
+		goto err_release_region;
+	}
+
+	inport_dev->name = INPORT_NAME;
+	inport_dev->phys = "isa023c/input0";
+	inport_dev->id.bustype = BUS_ISA;
+	inport_dev->id.vendor  = INPORT_VENDOR;
+	inport_dev->id.product = 0x0001;
+	inport_dev->id.version = 0x0100;
+
+	inport_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+	inport_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+		BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+	inport_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+	inport_dev->open  = inport_open;
+	inport_dev->close = inport_close;
+
+	outb(INPORT_RESET, INPORT_CONTROL_PORT);
+	outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+	outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
+
+	err = input_register_device(inport_dev);
+	if (err)
+		goto err_free_dev;
+
+	return 0;
+
+ err_free_dev:
+	input_free_device(inport_dev);
+ err_release_region:
+	release_region(INPORT_BASE, INPORT_EXTENT);
+
+	return err;
+}
+
+static void __exit inport_exit(void)
+{
+	input_unregister_device(inport_dev);
+	release_region(INPORT_BASE, INPORT_EXTENT);
+}
+
+module_init(inport_init);
+module_exit(inport_exit);
diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c
new file mode 100644
index 0000000..e5ed216
--- /dev/null
+++ b/drivers/input/mouse/lifebook.c
@@ -0,0 +1,352 @@
+/*
+ * Fujitsu B-series Lifebook PS/2 TouchScreen driver
+ *
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2005 Kenan Esau <kenan.esau@conan.de>
+ *
+ * TouchScreen detection, absolute mode setting and packet layout is taken from
+ * Harald Hoyer's description of the device.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/dmi.h>
+#include <linux/slab.h>
+
+#include "psmouse.h"
+#include "lifebook.h"
+
+struct lifebook_data {
+	struct input_dev *dev2;		/* Relative device */
+	char phys[32];
+};
+
+static bool lifebook_present;
+
+static const char *desired_serio_phys;
+
+static int lifebook_limit_serio3(const struct dmi_system_id *d)
+{
+	desired_serio_phys = "isa0060/serio3";
+	return 1;
+}
+
+static bool lifebook_use_6byte_proto;
+
+static int lifebook_set_6byte_proto(const struct dmi_system_id *d)
+{
+	lifebook_use_6byte_proto = true;
+	return 1;
+}
+
+static const struct dmi_system_id lifebook_dmi_table[] __initconst = {
+	{
+		/* FLORA-ie 55mi */
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "FLORA-ie 55mi"),
+		},
+	},
+	{
+		/* LifeBook B */
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "Lifebook B Series"),
+		},
+	},
+	{
+		/* LifeBook B */
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B Series"),
+		},
+	},
+	{
+		/* Lifebook B */
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"),
+		},
+	},
+	{
+		/* Lifebook B-2130 */
+		.matches = {
+			DMI_MATCH(DMI_BOARD_NAME, "ZEPHYR"),
+		},
+	},
+	{
+		/* Lifebook B213x/B2150 */
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"),
+		},
+	},
+	{
+		/* Zephyr */
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "ZEPHYR"),
+		},
+	},
+	{
+		/* Panasonic CF-18 */
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"),
+		},
+		.callback = lifebook_limit_serio3,
+	},
+	{
+		/* Panasonic CF-28 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "CF-28"),
+		},
+		.callback = lifebook_set_6byte_proto,
+	},
+	{
+		/* Panasonic CF-29 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
+		},
+		.callback = lifebook_set_6byte_proto,
+	},
+	{
+		/* Panasonic CF-72 */
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "CF-72"),
+		},
+		.callback = lifebook_set_6byte_proto,
+	},
+	{
+		/* Lifebook B142 */
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"),
+		},
+	},
+	{ }
+};
+
+void __init lifebook_module_init(void)
+{
+	lifebook_present = dmi_check_system(lifebook_dmi_table);
+}
+
+static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
+{
+	struct lifebook_data *priv = psmouse->private;
+	struct input_dev *dev1 = psmouse->dev;
+	struct input_dev *dev2 = priv ? priv->dev2 : NULL;
+	unsigned char *packet = psmouse->packet;
+	bool relative_packet = packet[0] & 0x08;
+
+	if (relative_packet || !lifebook_use_6byte_proto) {
+		if (psmouse->pktcnt != 3)
+			return PSMOUSE_GOOD_DATA;
+	} else {
+		switch (psmouse->pktcnt) {
+		case 1:
+			return (packet[0] & 0xf8) == 0x00 ?
+				PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+		case 2:
+			return PSMOUSE_GOOD_DATA;
+		case 3:
+			return ((packet[2] & 0x30) << 2) == (packet[2] & 0xc0) ?
+				PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+		case 4:
+			return (packet[3] & 0xf8) == 0xc0 ?
+				PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+		case 5:
+			return (packet[4] & 0xc0) == (packet[2] & 0xc0) ?
+				PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+		case 6:
+			if (((packet[5] & 0x30) << 2) != (packet[5] & 0xc0))
+				return PSMOUSE_BAD_DATA;
+			if ((packet[5] & 0xc0) != (packet[1] & 0xc0))
+				return PSMOUSE_BAD_DATA;
+			break; /* report data */
+		}
+	}
+
+	if (relative_packet) {
+		if (!dev2)
+			psmouse_warn(psmouse,
+				     "got relative packet but no relative device set up\n");
+	} else {
+		if (lifebook_use_6byte_proto) {
+			input_report_abs(dev1, ABS_X,
+				((packet[1] & 0x3f) << 6) | (packet[2] & 0x3f));
+			input_report_abs(dev1, ABS_Y,
+				4096 - (((packet[4] & 0x3f) << 6) | (packet[5] & 0x3f)));
+		} else {
+			input_report_abs(dev1, ABS_X,
+				(packet[1] | ((packet[0] & 0x30) << 4)));
+			input_report_abs(dev1, ABS_Y,
+				1024 - (packet[2] | ((packet[0] & 0xC0) << 2)));
+		}
+		input_report_key(dev1, BTN_TOUCH, packet[0] & 0x04);
+		input_sync(dev1);
+	}
+
+	if (dev2) {
+		if (relative_packet) {
+			input_report_rel(dev2, REL_X,
+				((packet[0] & 0x10) ? packet[1] - 256 : packet[1]));
+			input_report_rel(dev2, REL_Y,
+				 -(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2]));
+		}
+		input_report_key(dev2, BTN_LEFT, packet[0] & 0x01);
+		input_report_key(dev2, BTN_RIGHT, packet[0] & 0x02);
+		input_sync(dev2);
+	}
+
+	return PSMOUSE_FULL_PACKET;
+}
+
+static int lifebook_absolute_mode(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param;
+
+	if (psmouse_reset(psmouse))
+		return -1;
+
+	/*
+	 * Enable absolute output -- ps2_command fails always but if
+	 * you leave this call out the touchscreen will never send
+	 * absolute coordinates
+	 */
+	param = lifebook_use_6byte_proto ? 0x08 : 0x07;
+	ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
+
+	return 0;
+}
+
+static void lifebook_relative_mode(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param = 0x06;
+
+	ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
+}
+
+static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution)
+{
+	static const unsigned char params[] = { 0, 1, 2, 2, 3 };
+	unsigned char p;
+
+	if (resolution == 0 || resolution > 400)
+		resolution = 400;
+
+	p = params[resolution / 100];
+	ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES);
+	psmouse->resolution = 50 << p;
+}
+
+static void lifebook_disconnect(struct psmouse *psmouse)
+{
+	struct lifebook_data *priv = psmouse->private;
+
+	psmouse_reset(psmouse);
+	if (priv) {
+		input_unregister_device(priv->dev2);
+		kfree(priv);
+	}
+	psmouse->private = NULL;
+}
+
+int lifebook_detect(struct psmouse *psmouse, bool set_properties)
+{
+	if (!lifebook_present)
+		return -1;
+
+	if (desired_serio_phys &&
+	    strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys))
+		return -1;
+
+	if (set_properties) {
+		psmouse->vendor = "Fujitsu";
+		psmouse->name = "Lifebook TouchScreen";
+	}
+
+	return 0;
+}
+
+static int lifebook_create_relative_device(struct psmouse *psmouse)
+{
+	struct input_dev *dev2;
+	struct lifebook_data *priv;
+	int error = -ENOMEM;
+
+	priv = kzalloc(sizeof(struct lifebook_data), GFP_KERNEL);
+	dev2 = input_allocate_device();
+	if (!priv || !dev2)
+		goto err_out;
+
+	priv->dev2 = dev2;
+	snprintf(priv->phys, sizeof(priv->phys),
+		 "%s/input1", psmouse->ps2dev.serio->phys);
+
+	dev2->phys = priv->phys;
+	dev2->name = "PS/2 Touchpad";
+	dev2->id.bustype = BUS_I8042;
+	dev2->id.vendor  = 0x0002;
+	dev2->id.product = PSMOUSE_LIFEBOOK;
+	dev2->id.version = 0x0000;
+	dev2->dev.parent = &psmouse->ps2dev.serio->dev;
+
+	dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+	dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+	dev2->keybit[BIT_WORD(BTN_LEFT)] =
+				BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
+
+	error = input_register_device(priv->dev2);
+	if (error)
+		goto err_out;
+
+	psmouse->private = priv;
+	return 0;
+
+ err_out:
+	input_free_device(dev2);
+	kfree(priv);
+	return error;
+}
+
+int lifebook_init(struct psmouse *psmouse)
+{
+	struct input_dev *dev1 = psmouse->dev;
+	int max_coord = lifebook_use_6byte_proto ? 4096 : 1024;
+
+	if (lifebook_absolute_mode(psmouse))
+		return -1;
+
+	dev1->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
+	dev1->relbit[0] = 0;
+	dev1->keybit[BIT_WORD(BTN_MOUSE)] = 0;
+	dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0);
+	input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0);
+
+	if (!desired_serio_phys) {
+		if (lifebook_create_relative_device(psmouse)) {
+			lifebook_relative_mode(psmouse);
+			return -1;
+		}
+	}
+
+	psmouse->protocol_handler = lifebook_process_byte;
+	psmouse->set_resolution = lifebook_set_resolution;
+	psmouse->disconnect = lifebook_disconnect;
+	psmouse->reconnect  = lifebook_absolute_mode;
+
+	psmouse->model = lifebook_use_6byte_proto ? 6 : 3;
+
+	/*
+	 * Use packet size = 3 even when using 6-byte protocol because
+	 * that's what POLL will return on Lifebooks (according to spec).
+	 */
+	psmouse->pktsize = 3;
+
+	return 0;
+}
+
diff --git a/drivers/input/mouse/lifebook.h b/drivers/input/mouse/lifebook.h
new file mode 100644
index 0000000..0baf02a
--- /dev/null
+++ b/drivers/input/mouse/lifebook.h
@@ -0,0 +1,32 @@
+/*
+ * Fujitsu B-series Lifebook PS/2 TouchScreen driver
+ *
+ * Copyright (c) 2005 Vojtech Pavlik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _LIFEBOOK_H
+#define _LIFEBOOK_H
+
+#ifdef CONFIG_MOUSE_PS2_LIFEBOOK
+void lifebook_module_init(void);
+int lifebook_detect(struct psmouse *psmouse, bool set_properties);
+int lifebook_init(struct psmouse *psmouse);
+#else
+static inline void lifebook_module_init(void)
+{
+}
+static inline int lifebook_detect(struct psmouse *psmouse, bool set_properties)
+{
+	return -ENOSYS;
+}
+static inline int lifebook_init(struct psmouse *psmouse)
+{
+	return -ENOSYS;
+}
+#endif
+
+#endif
diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c
new file mode 100644
index 0000000..e241311
--- /dev/null
+++ b/drivers/input/mouse/logibm.c
@@ -0,0 +1,185 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	James Banks		Matthew Dillon
+ *	David Giller		Nathan Laredo
+ *	Linus Torvalds		Johan Myreen
+ *	Cliff Matthews		Philip Blundell
+ *	Russell King
+ */
+
+/*
+ * Logitech Bus Mouse Driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Logitech busmouse driver");
+MODULE_LICENSE("GPL");
+
+#define	LOGIBM_BASE		0x23c
+#define	LOGIBM_EXTENT		4
+
+#define	LOGIBM_DATA_PORT	LOGIBM_BASE + 0
+#define	LOGIBM_SIGNATURE_PORT	LOGIBM_BASE + 1
+#define	LOGIBM_CONTROL_PORT	LOGIBM_BASE + 2
+#define	LOGIBM_CONFIG_PORT	LOGIBM_BASE + 3
+
+#define	LOGIBM_ENABLE_IRQ	0x00
+#define	LOGIBM_DISABLE_IRQ	0x10
+#define	LOGIBM_READ_X_LOW	0x80
+#define	LOGIBM_READ_X_HIGH	0xa0
+#define	LOGIBM_READ_Y_LOW	0xc0
+#define	LOGIBM_READ_Y_HIGH	0xe0
+
+#define LOGIBM_DEFAULT_MODE	0x90
+#define LOGIBM_CONFIG_BYTE	0x91
+#define LOGIBM_SIGNATURE_BYTE	0xa5
+
+#define LOGIBM_IRQ		5
+
+static int logibm_irq = LOGIBM_IRQ;
+module_param_named(irq, logibm_irq, uint, 0);
+MODULE_PARM_DESC(irq, "IRQ number (5=default)");
+
+static struct input_dev *logibm_dev;
+
+static irqreturn_t logibm_interrupt(int irq, void *dev_id)
+{
+	char dx, dy;
+	unsigned char buttons;
+
+	outb(LOGIBM_READ_X_LOW, LOGIBM_CONTROL_PORT);
+	dx = (inb(LOGIBM_DATA_PORT) & 0xf);
+	outb(LOGIBM_READ_X_HIGH, LOGIBM_CONTROL_PORT);
+	dx |= (inb(LOGIBM_DATA_PORT) & 0xf) << 4;
+	outb(LOGIBM_READ_Y_LOW, LOGIBM_CONTROL_PORT);
+	dy = (inb(LOGIBM_DATA_PORT) & 0xf);
+	outb(LOGIBM_READ_Y_HIGH, LOGIBM_CONTROL_PORT);
+	buttons = inb(LOGIBM_DATA_PORT);
+	dy |= (buttons & 0xf) << 4;
+	buttons = ~buttons >> 5;
+
+	input_report_rel(logibm_dev, REL_X, dx);
+	input_report_rel(logibm_dev, REL_Y, dy);
+	input_report_key(logibm_dev, BTN_RIGHT,  buttons & 1);
+	input_report_key(logibm_dev, BTN_MIDDLE, buttons & 2);
+	input_report_key(logibm_dev, BTN_LEFT,   buttons & 4);
+	input_sync(logibm_dev);
+
+	outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT);
+	return IRQ_HANDLED;
+}
+
+static int logibm_open(struct input_dev *dev)
+{
+	if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) {
+		printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq);
+		return -EBUSY;
+	}
+	outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT);
+	return 0;
+}
+
+static void logibm_close(struct input_dev *dev)
+{
+	outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT);
+	free_irq(logibm_irq, NULL);
+}
+
+static int __init logibm_init(void)
+{
+	int err;
+
+	if (!request_region(LOGIBM_BASE, LOGIBM_EXTENT, "logibm")) {
+		printk(KERN_ERR "logibm.c: Can't allocate ports at %#x\n", LOGIBM_BASE);
+		return -EBUSY;
+	}
+
+	outb(LOGIBM_CONFIG_BYTE, LOGIBM_CONFIG_PORT);
+	outb(LOGIBM_SIGNATURE_BYTE, LOGIBM_SIGNATURE_PORT);
+	udelay(100);
+
+	if (inb(LOGIBM_SIGNATURE_PORT) != LOGIBM_SIGNATURE_BYTE) {
+		printk(KERN_INFO "logibm.c: Didn't find Logitech busmouse at %#x\n", LOGIBM_BASE);
+		err = -ENODEV;
+		goto err_release_region;
+	}
+
+	outb(LOGIBM_DEFAULT_MODE, LOGIBM_CONFIG_PORT);
+	outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT);
+
+	logibm_dev = input_allocate_device();
+	if (!logibm_dev) {
+		printk(KERN_ERR "logibm.c: Not enough memory for input device\n");
+		err = -ENOMEM;
+		goto err_release_region;
+	}
+
+	logibm_dev->name = "Logitech bus mouse";
+	logibm_dev->phys = "isa023c/input0";
+	logibm_dev->id.bustype = BUS_ISA;
+	logibm_dev->id.vendor  = 0x0003;
+	logibm_dev->id.product = 0x0001;
+	logibm_dev->id.version = 0x0100;
+
+	logibm_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+	logibm_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+		BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+	logibm_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+	logibm_dev->open  = logibm_open;
+	logibm_dev->close = logibm_close;
+
+	err = input_register_device(logibm_dev);
+	if (err)
+		goto err_free_dev;
+
+	return 0;
+
+ err_free_dev:
+	input_free_device(logibm_dev);
+ err_release_region:
+	release_region(LOGIBM_BASE, LOGIBM_EXTENT);
+
+	return err;
+}
+
+static void __exit logibm_exit(void)
+{
+	input_unregister_device(logibm_dev);
+	release_region(LOGIBM_BASE, LOGIBM_EXTENT);
+}
+
+module_init(logibm_init);
+module_exit(logibm_exit);
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
new file mode 100644
index 0000000..136e222
--- /dev/null
+++ b/drivers/input/mouse/logips2pp.c
@@ -0,0 +1,425 @@
+/*
+ * Logitech PS/2++ mouse driver
+ *
+ * Copyright (c) 1999-2003 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2003 Eric Wong <eric@yhbt.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include "psmouse.h"
+#include "logips2pp.h"
+
+/* Logitech mouse types */
+#define PS2PP_KIND_WHEEL	1
+#define PS2PP_KIND_MX		2
+#define PS2PP_KIND_TP3		3
+#define PS2PP_KIND_TRACKMAN	4
+
+/* Logitech mouse features */
+#define PS2PP_WHEEL		0x01
+#define PS2PP_HWHEEL		0x02
+#define PS2PP_SIDE_BTN		0x04
+#define PS2PP_EXTRA_BTN		0x08
+#define PS2PP_TASK_BTN		0x10
+#define PS2PP_NAV_BTN		0x20
+
+struct ps2pp_info {
+	u8 model;
+	u8 kind;
+	u16 features;
+};
+
+/*
+ * Process a PS2++ or PS2T++ packet.
+ */
+
+static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	unsigned char *packet = psmouse->packet;
+
+	if (psmouse->pktcnt < 3)
+		return PSMOUSE_GOOD_DATA;
+
+/*
+ * Full packet accumulated, process it
+ */
+
+	if ((packet[0] & 0x48) == 0x48 && (packet[1] & 0x02) == 0x02) {
+
+		/* Logitech extended packet */
+		switch ((packet[1] >> 4) | (packet[0] & 0x30)) {
+
+		case 0x0d: /* Mouse extra info */
+
+			input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
+				(int) (packet[2] & 8) - (int) (packet[2] & 7));
+			input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1);
+			input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1);
+
+			break;
+
+		case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */
+
+			input_report_key(dev, BTN_SIDE, (packet[2]) & 1);
+			input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1);
+			input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1);
+			input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1);
+			input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1);
+
+			break;
+
+		case 0x0f: /* TouchPad extra info */
+
+			input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
+				(int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7));
+			packet[0] = packet[2] | 0x08;
+			break;
+
+		default:
+			psmouse_dbg(psmouse,
+				    "Received PS2++ packet #%x, but don't know how to handle.\n",
+				    (packet[1] >> 4) | (packet[0] & 0x30));
+			break;
+		}
+	} else {
+		/* Standard PS/2 motion data */
+		input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
+		input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
+	}
+
+	input_report_key(dev, BTN_LEFT,    packet[0]       & 1);
+	input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
+	input_report_key(dev, BTN_RIGHT,  (packet[0] >> 1) & 1);
+
+	input_sync(dev);
+
+	return PSMOUSE_FULL_PACKET;
+
+}
+
+/*
+ * ps2pp_cmd() sends a PS2++ command, sliced into two bit
+ * pieces through the SETRES command. This is needed to send extended
+ * commands to mice on notebooks that try to understand the PS/2 protocol
+ * Ugly.
+ */
+
+static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command)
+{
+	if (psmouse_sliced_command(psmouse, command))
+		return -1;
+
+	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300))
+		return -1;
+
+	return 0;
+}
+
+/*
+ * SmartScroll / CruiseControl for some newer Logitech mice Defaults to
+ * enabled if we do nothing to it. Of course I put this in because I want it
+ * disabled :P
+ * 1 - enabled (if previously disabled, also default)
+ * 0 - disabled
+ */
+
+static void ps2pp_set_smartscroll(struct psmouse *psmouse, bool smartscroll)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4];
+
+	ps2pp_cmd(psmouse, param, 0x32);
+
+	param[0] = 0;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+
+	param[0] = smartscroll;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+}
+
+static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse,
+					   void *data, char *buf)
+{
+	return sprintf(buf, "%d\n", psmouse->smartscroll);
+}
+
+static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data,
+					  const char *buf, size_t count)
+{
+	unsigned int value;
+	int err;
+
+	err = kstrtouint(buf, 10, &value);
+	if (err)
+		return err;
+
+	if (value > 1)
+		return -EINVAL;
+
+	ps2pp_set_smartscroll(psmouse, value);
+	psmouse->smartscroll = value;
+	return count;
+}
+
+PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL,
+			ps2pp_attr_show_smartscroll, ps2pp_attr_set_smartscroll);
+
+/*
+ * Support 800 dpi resolution _only_ if the user wants it (there are good
+ * reasons to not use it even if the mouse supports it, and of course there are
+ * also good reasons to use it, let the user decide).
+ */
+
+static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolution)
+{
+	if (resolution > 400) {
+		struct ps2dev *ps2dev = &psmouse->ps2dev;
+		unsigned char param = 3;
+
+		ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+		ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+		ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+		ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
+		psmouse->resolution = 800;
+	} else
+		psmouse_set_resolution(psmouse, resolution);
+}
+
+static void ps2pp_disconnect(struct psmouse *psmouse)
+{
+	device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll.dattr);
+}
+
+static const struct ps2pp_info *get_model_info(unsigned char model)
+{
+	static const struct ps2pp_info ps2pp_list[] = {
+		{  1,	0,			0 },	/* Simple 2-button mouse */
+		{ 12,	0,			PS2PP_SIDE_BTN},
+		{ 13,	0,			0 },
+		{ 15,	PS2PP_KIND_MX,					/* MX1000 */
+				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL },
+		{ 40,	0,			PS2PP_SIDE_BTN },
+		{ 41,	0,			PS2PP_SIDE_BTN },
+		{ 42,	0,			PS2PP_SIDE_BTN },
+		{ 43,	0,			PS2PP_SIDE_BTN },
+		{ 50,	0,			0 },
+		{ 51,	0,			0 },
+		{ 52,	PS2PP_KIND_WHEEL,	PS2PP_SIDE_BTN | PS2PP_WHEEL },
+		{ 53,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 56,	PS2PP_KIND_WHEEL,	PS2PP_SIDE_BTN | PS2PP_WHEEL }, /* Cordless MouseMan Wheel */
+		{ 61,	PS2PP_KIND_MX,					/* MX700 */
+				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
+		{ 66,	PS2PP_KIND_MX,					/* MX3100 receiver */
+				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL },
+		{ 72,	PS2PP_KIND_TRACKMAN,	0 },			/* T-CH11: TrackMan Marble */
+		{ 73,	PS2PP_KIND_TRACKMAN,	PS2PP_SIDE_BTN },	/* TrackMan FX */
+		{ 75,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 76,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 79,	PS2PP_KIND_TRACKMAN,	PS2PP_WHEEL },		/* TrackMan with wheel */
+		{ 80,	PS2PP_KIND_WHEEL,	PS2PP_SIDE_BTN | PS2PP_WHEEL },
+		{ 81,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 83,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 85,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 86,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 87,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 88,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 96,	0,			0 },
+		{ 97,	PS2PP_KIND_TP3,		PS2PP_WHEEL | PS2PP_HWHEEL },
+		{ 99,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 100,	PS2PP_KIND_MX,					/* MX510 */
+				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
+		{ 111,  PS2PP_KIND_MX,	PS2PP_WHEEL | PS2PP_SIDE_BTN },	/* MX300 reports task button as side */
+		{ 112,	PS2PP_KIND_MX,					/* MX500 */
+				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
+		{ 114,	PS2PP_KIND_MX,					/* MX310 */
+				PS2PP_WHEEL | PS2PP_SIDE_BTN |
+				PS2PP_TASK_BTN | PS2PP_EXTRA_BTN }
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ps2pp_list); i++)
+		if (model == ps2pp_list[i].model)
+			return &ps2pp_list[i];
+
+	return NULL;
+}
+
+/*
+ * Set up input device's properties based on the detected mouse model.
+ */
+
+static void ps2pp_set_model_properties(struct psmouse *psmouse,
+				       const struct ps2pp_info *model_info,
+				       bool using_ps2pp)
+{
+	struct input_dev *input_dev = psmouse->dev;
+
+	if (model_info->features & PS2PP_SIDE_BTN)
+		__set_bit(BTN_SIDE, input_dev->keybit);
+
+	if (model_info->features & PS2PP_EXTRA_BTN)
+		__set_bit(BTN_EXTRA, input_dev->keybit);
+
+	if (model_info->features & PS2PP_TASK_BTN)
+		__set_bit(BTN_TASK, input_dev->keybit);
+
+	if (model_info->features & PS2PP_NAV_BTN) {
+		__set_bit(BTN_FORWARD, input_dev->keybit);
+		__set_bit(BTN_BACK, input_dev->keybit);
+	}
+
+	if (model_info->features & PS2PP_WHEEL)
+		__set_bit(REL_WHEEL, input_dev->relbit);
+
+	if (model_info->features & PS2PP_HWHEEL)
+		__set_bit(REL_HWHEEL, input_dev->relbit);
+
+	switch (model_info->kind) {
+
+	case PS2PP_KIND_WHEEL:
+		psmouse->name = "Wheel Mouse";
+		break;
+
+	case PS2PP_KIND_MX:
+		psmouse->name = "MX Mouse";
+		break;
+
+	case PS2PP_KIND_TP3:
+		psmouse->name = "TouchPad 3";
+		break;
+
+	case PS2PP_KIND_TRACKMAN:
+		psmouse->name = "TrackMan";
+		break;
+
+	default:
+		/*
+		 * Set name to "Mouse" only when using PS2++,
+		 * otherwise let other protocols define suitable
+		 * name
+		 */
+		if (using_ps2pp)
+			psmouse->name = "Mouse";
+		break;
+	}
+}
+
+
+/*
+ * Logitech magic init. Detect whether the mouse is a Logitech one
+ * and its exact model and try turning on extended protocol for ones
+ * that support it.
+ */
+
+int ps2pp_init(struct psmouse *psmouse, bool set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4];
+	unsigned char model, buttons;
+	const struct ps2pp_info *model_info;
+	bool use_ps2pp = false;
+	int error;
+
+	param[0] = 0;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	param[1] = 0;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
+
+	model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78);
+	buttons = param[1];
+
+	if (!model || !buttons)
+		return -1;
+
+	model_info = get_model_info(model);
+	if (model_info) {
+
+/*
+ * Do Logitech PS2++ / PS2T++ magic init.
+ */
+		if (model_info->kind == PS2PP_KIND_TP3) { /* Touch Pad 3 */
+
+			/* Unprotect RAM */
+			param[0] = 0x11; param[1] = 0x04; param[2] = 0x68;
+			ps2_command(ps2dev, param, 0x30d1);
+			/* Enable features */
+			param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b;
+			ps2_command(ps2dev, param, 0x30d1);
+			/* Enable PS2++ */
+			param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3;
+			ps2_command(ps2dev, param, 0x30d1);
+
+			param[0] = 0;
+			if (!ps2_command(ps2dev, param, 0x13d1) &&
+			    param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) {
+				use_ps2pp = true;
+			}
+
+		} else {
+
+			param[0] = param[1] = param[2] = 0;
+			ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */
+			ps2pp_cmd(psmouse, param, 0xDB);
+
+			if ((param[0] & 0x78) == 0x48 &&
+			    (param[1] & 0xf3) == 0xc2 &&
+			    (param[2] & 0x03) == ((param[1] >> 2) & 3)) {
+				ps2pp_set_smartscroll(psmouse, false);
+				use_ps2pp = true;
+			}
+		}
+
+	} else {
+		psmouse_warn(psmouse, "Detected unknown Logitech mouse model %d\n", model);
+	}
+
+	if (set_properties) {
+		psmouse->vendor = "Logitech";
+		psmouse->model = model;
+
+		if (use_ps2pp) {
+			psmouse->protocol_handler = ps2pp_process_byte;
+			psmouse->pktsize = 3;
+
+			if (model_info->kind != PS2PP_KIND_TP3) {
+				psmouse->set_resolution = ps2pp_set_resolution;
+				psmouse->disconnect = ps2pp_disconnect;
+
+				error = device_create_file(&psmouse->ps2dev.serio->dev,
+							   &psmouse_attr_smartscroll.dattr);
+				if (error) {
+					psmouse_err(psmouse,
+						    "failed to create smartscroll sysfs attribute, error: %d\n",
+						    error);
+					return -1;
+				}
+			}
+		}
+
+		if (buttons >= 3)
+			__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+
+		if (model_info)
+			ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
+	}
+
+	return use_ps2pp ? 0 : -1;
+}
+
diff --git a/drivers/input/mouse/logips2pp.h b/drivers/input/mouse/logips2pp.h
new file mode 100644
index 0000000..0c186f0
--- /dev/null
+++ b/drivers/input/mouse/logips2pp.h
@@ -0,0 +1,23 @@
+/*
+ * Logitech PS/2++ mouse driver header
+ *
+ * Copyright (c) 2003 Vojtech Pavlik <vojtech@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _LOGIPS2PP_H
+#define _LOGIPS2PP_H
+
+#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
+int ps2pp_init(struct psmouse *psmouse, bool set_properties);
+#else
+inline int ps2pp_init(struct psmouse *psmouse, bool set_properties)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_LOGIPS2PP */
+
+#endif
diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c
new file mode 100644
index 0000000..0a60717
--- /dev/null
+++ b/drivers/input/mouse/maplemouse.c
@@ -0,0 +1,150 @@
+/*
+ *	SEGA Dreamcast mouse driver
+ *	Based on drivers/usb/usbmouse.c
+ *
+ *	Copyright (c) Yaegashi Takeshi, 2001
+ *	Copyright (c) Adrian McMenamin, 2008 - 2009
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/maple.h>
+
+MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
+MODULE_DESCRIPTION("SEGA Dreamcast mouse driver");
+MODULE_LICENSE("GPL");
+
+struct dc_mouse {
+	struct input_dev *dev;
+	struct maple_device *mdev;
+};
+
+static void dc_mouse_callback(struct mapleq *mq)
+{
+	int buttons, relx, rely, relz;
+	struct maple_device *mapledev = mq->dev;
+	struct dc_mouse *mse = maple_get_drvdata(mapledev);
+	struct input_dev *dev = mse->dev;
+	unsigned char *res = mq->recvbuf->buf;
+
+	buttons = ~res[8];
+	relx = *(unsigned short *)(res + 12) - 512;
+	rely = *(unsigned short *)(res + 14) - 512;
+	relz = *(unsigned short *)(res + 16) - 512;
+
+	input_report_key(dev, BTN_LEFT,   buttons & 4);
+	input_report_key(dev, BTN_MIDDLE, buttons & 9);
+	input_report_key(dev, BTN_RIGHT,  buttons & 2);
+	input_report_rel(dev, REL_X,      relx);
+	input_report_rel(dev, REL_Y,      rely);
+	input_report_rel(dev, REL_WHEEL,  relz);
+	input_sync(dev);
+}
+
+static int dc_mouse_open(struct input_dev *dev)
+{
+	struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev));
+
+	maple_getcond_callback(mse->mdev, dc_mouse_callback, HZ/50,
+		MAPLE_FUNC_MOUSE);
+
+	return 0;
+}
+
+static void dc_mouse_close(struct input_dev *dev)
+{
+	struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev));
+
+	maple_getcond_callback(mse->mdev, dc_mouse_callback, 0,
+		MAPLE_FUNC_MOUSE);
+}
+
+/* allow the mouse to be used */
+static int probe_maple_mouse(struct device *dev)
+{
+	struct maple_device *mdev = to_maple_dev(dev);
+	struct maple_driver *mdrv = to_maple_driver(dev->driver);
+	int error;
+	struct input_dev *input_dev;
+	struct dc_mouse *mse;
+
+	mse = kzalloc(sizeof(struct dc_mouse), GFP_KERNEL);
+	if (!mse) {
+		error = -ENOMEM;
+		goto fail;
+	}
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		error = -ENOMEM;
+		goto fail_nomem;
+	}
+
+	mse->dev = input_dev;
+	mse->mdev = mdev;
+
+	input_set_drvdata(input_dev, mse);
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+	input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+		BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
+	input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) |
+		BIT_MASK(REL_WHEEL);
+	input_dev->open = dc_mouse_open;
+	input_dev->close = dc_mouse_close;
+	input_dev->name = mdev->product_name;
+	input_dev->id.bustype = BUS_HOST;
+	error =	input_register_device(input_dev);
+	if (error)
+		goto fail_register;
+
+	mdev->driver = mdrv;
+	maple_set_drvdata(mdev, mse);
+
+	return error;
+
+fail_register:
+	input_free_device(input_dev);
+fail_nomem:
+	kfree(mse);
+fail:
+	return error;
+}
+
+static int remove_maple_mouse(struct device *dev)
+{
+	struct maple_device *mdev = to_maple_dev(dev);
+	struct dc_mouse *mse = maple_get_drvdata(mdev);
+
+	mdev->callback = NULL;
+	input_unregister_device(mse->dev);
+	maple_set_drvdata(mdev, NULL);
+	kfree(mse);
+
+	return 0;
+}
+
+static struct maple_driver dc_mouse_driver = {
+	.function =	MAPLE_FUNC_MOUSE,
+	.drv = {
+		.name = "Dreamcast_mouse",
+		.probe = probe_maple_mouse,
+		.remove = remove_maple_mouse,
+	},
+};
+
+static int __init dc_mouse_init(void)
+{
+	return maple_driver_register(&dc_mouse_driver);
+}
+
+static void __exit dc_mouse_exit(void)
+{
+	maple_driver_unregister(&dc_mouse_driver);
+}
+
+module_init(dc_mouse_init);
+module_exit(dc_mouse_exit);
diff --git a/drivers/input/mouse/navpoint.c b/drivers/input/mouse/navpoint.c
new file mode 100644
index 0000000..d6e8f58
--- /dev/null
+++ b/drivers/input/mouse/navpoint.c
@@ -0,0 +1,365 @@
+/*
+ * Synaptics NavPoint (PXA27x SSP/SPI) driver.
+ *
+ * Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/input/navpoint.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/pxa2xx_ssp.h>
+#include <linux/slab.h>
+
+/*
+ * Synaptics Modular Embedded Protocol: Module Packet Format.
+ * Module header byte 2:0 = Length (# bytes that follow)
+ * Module header byte 4:3 = Control
+ * Module header byte 7:5 = Module Address
+ */
+#define HEADER_LENGTH(byte)	((byte) & 0x07)
+#define HEADER_CONTROL(byte)	(((byte) >> 3) & 0x03)
+#define HEADER_ADDRESS(byte)	((byte) >> 5)
+
+struct navpoint {
+	struct ssp_device	*ssp;
+	struct input_dev	*input;
+	struct device		*dev;
+	int			gpio;
+	int			index;
+	u8			data[1 + HEADER_LENGTH(0xff)];
+};
+
+/*
+ * Initialization values for SSCR0_x, SSCR1_x, SSSR_x.
+ */
+static const u32 sscr0 = 0
+	| SSCR0_TUM		/* TIM = 1; No TUR interrupts */
+	| SSCR0_RIM		/* RIM = 1; No ROR interrupts */
+	| SSCR0_SSE		/* SSE = 1; SSP enabled */
+	| SSCR0_Motorola	/* FRF = 0; Motorola SPI */
+	| SSCR0_DataSize(16)	/* DSS = 15; Data size = 16-bit */
+	;
+static const u32 sscr1 = 0
+	| SSCR1_SCFR		/* SCFR = 1; SSPSCLK only during transfers */
+	| SSCR1_SCLKDIR		/* SCLKDIR = 1; Slave mode */
+	| SSCR1_SFRMDIR		/* SFRMDIR = 1; Slave mode */
+	| SSCR1_RWOT		/* RWOT = 1; Receive without transmit mode */
+	| SSCR1_RxTresh(1)	/* RFT = 0; Receive FIFO threshold = 1 */
+	| SSCR1_SPH		/* SPH = 1; SSPSCLK inactive 0.5 + 1 cycles */
+	| SSCR1_RIE		/* RIE = 1; Receive FIFO interrupt enabled */
+	;
+static const u32 sssr = 0
+	| SSSR_BCE		/* BCE = 1; Clear BCE */
+	| SSSR_TUR		/* TUR = 1; Clear TUR */
+	| SSSR_EOC		/* EOC = 1; Clear EOC */
+	| SSSR_TINT		/* TINT = 1; Clear TINT */
+	| SSSR_PINT		/* PINT = 1; Clear PINT */
+	| SSSR_ROR		/* ROR = 1; Clear ROR */
+	;
+
+/*
+ * MEP Query $22: Touchpad Coordinate Range Query is not supported by
+ * the NavPoint module, so sampled values provide the default limits.
+ */
+#define NAVPOINT_X_MIN		1278
+#define NAVPOINT_X_MAX		5340
+#define NAVPOINT_Y_MIN		1572
+#define NAVPOINT_Y_MAX		4396
+#define NAVPOINT_PRESSURE_MIN	0
+#define NAVPOINT_PRESSURE_MAX	255
+
+static void navpoint_packet(struct navpoint *navpoint)
+{
+	int finger;
+	int gesture;
+	int x, y, z;
+
+	switch (navpoint->data[0]) {
+	case 0xff:	/* Garbage (packet?) between reset and Hello packet */
+	case 0x00:	/* Module 0, NULL packet */
+		break;
+
+	case 0x0e:	/* Module 0, Absolute packet */
+		finger = (navpoint->data[1] & 0x01);
+		gesture = (navpoint->data[1] & 0x02);
+		x = ((navpoint->data[2] & 0x1f) << 8) | navpoint->data[3];
+		y = ((navpoint->data[4] & 0x1f) << 8) | navpoint->data[5];
+		z = navpoint->data[6];
+		input_report_key(navpoint->input, BTN_TOUCH, finger);
+		input_report_abs(navpoint->input, ABS_X, x);
+		input_report_abs(navpoint->input, ABS_Y, y);
+		input_report_abs(navpoint->input, ABS_PRESSURE, z);
+		input_report_key(navpoint->input, BTN_TOOL_FINGER, finger);
+		input_report_key(navpoint->input, BTN_LEFT, gesture);
+		input_sync(navpoint->input);
+		break;
+
+	case 0x19:	/* Module 0, Hello packet */
+		if ((navpoint->data[1] & 0xf0) == 0x10)
+			break;
+		/* FALLTHROUGH */
+	default:
+		dev_warn(navpoint->dev,
+			 "spurious packet: data=0x%02x,0x%02x,...\n",
+			 navpoint->data[0], navpoint->data[1]);
+		break;
+	}
+}
+
+static irqreturn_t navpoint_irq(int irq, void *dev_id)
+{
+	struct navpoint *navpoint = dev_id;
+	struct ssp_device *ssp = navpoint->ssp;
+	irqreturn_t ret = IRQ_NONE;
+	u32 status;
+
+	status = pxa_ssp_read_reg(ssp, SSSR);
+	if (status & sssr) {
+		dev_warn(navpoint->dev,
+			 "unexpected interrupt: status=0x%08x\n", status);
+		pxa_ssp_write_reg(ssp, SSSR, (status & sssr));
+		ret = IRQ_HANDLED;
+	}
+
+	while (status & SSSR_RNE) {
+		u32 data;
+
+		data = pxa_ssp_read_reg(ssp, SSDR);
+		navpoint->data[navpoint->index + 0] = (data >> 8);
+		navpoint->data[navpoint->index + 1] = data;
+		navpoint->index += 2;
+		if (HEADER_LENGTH(navpoint->data[0]) < navpoint->index) {
+			navpoint_packet(navpoint);
+			navpoint->index = 0;
+		}
+		status = pxa_ssp_read_reg(ssp, SSSR);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+static void navpoint_up(struct navpoint *navpoint)
+{
+	struct ssp_device *ssp = navpoint->ssp;
+	int timeout;
+
+	clk_prepare_enable(ssp->clk);
+
+	pxa_ssp_write_reg(ssp, SSCR1, sscr1);
+	pxa_ssp_write_reg(ssp, SSSR, sssr);
+	pxa_ssp_write_reg(ssp, SSTO, 0);
+	pxa_ssp_write_reg(ssp, SSCR0, sscr0);	/* SSCR0_SSE written last */
+
+	/* Wait until SSP port is ready for slave clock operations */
+	for (timeout = 100; timeout != 0; --timeout) {
+		if (!(pxa_ssp_read_reg(ssp, SSSR) & SSSR_CSS))
+			break;
+		msleep(1);
+	}
+
+	if (timeout == 0)
+		dev_err(navpoint->dev,
+			"timeout waiting for SSSR[CSS] to clear\n");
+
+	if (gpio_is_valid(navpoint->gpio))
+		gpio_set_value(navpoint->gpio, 1);
+}
+
+static void navpoint_down(struct navpoint *navpoint)
+{
+	struct ssp_device *ssp = navpoint->ssp;
+
+	if (gpio_is_valid(navpoint->gpio))
+		gpio_set_value(navpoint->gpio, 0);
+
+	pxa_ssp_write_reg(ssp, SSCR0, 0);
+
+	clk_disable_unprepare(ssp->clk);
+}
+
+static int navpoint_open(struct input_dev *input)
+{
+	struct navpoint *navpoint = input_get_drvdata(input);
+
+	navpoint_up(navpoint);
+
+	return 0;
+}
+
+static void navpoint_close(struct input_dev *input)
+{
+	struct navpoint *navpoint = input_get_drvdata(input);
+
+	navpoint_down(navpoint);
+}
+
+static int navpoint_probe(struct platform_device *pdev)
+{
+	const struct navpoint_platform_data *pdata =
+					dev_get_platdata(&pdev->dev);
+	struct ssp_device *ssp;
+	struct input_dev *input;
+	struct navpoint *navpoint;
+	int error;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data\n");
+		return -EINVAL;
+	}
+
+	if (gpio_is_valid(pdata->gpio)) {
+		error = gpio_request_one(pdata->gpio, GPIOF_OUT_INIT_LOW,
+					 "SYNAPTICS_ON");
+		if (error)
+			return error;
+	}
+
+	ssp = pxa_ssp_request(pdata->port, pdev->name);
+	if (!ssp) {
+		error = -ENODEV;
+		goto err_free_gpio;
+	}
+
+	/* HaRET does not disable devices before jumping into Linux */
+	if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) {
+		pxa_ssp_write_reg(ssp, SSCR0, 0);
+		dev_warn(&pdev->dev, "ssp%d already enabled\n", pdata->port);
+	}
+
+	navpoint = kzalloc(sizeof(*navpoint), GFP_KERNEL);
+	input = input_allocate_device();
+	if (!navpoint || !input) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	navpoint->ssp = ssp;
+	navpoint->input = input;
+	navpoint->dev = &pdev->dev;
+	navpoint->gpio = pdata->gpio;
+
+	input->name = pdev->name;
+	input->dev.parent = &pdev->dev;
+
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(EV_ABS, input->evbit);
+	__set_bit(BTN_LEFT, input->keybit);
+	__set_bit(BTN_TOUCH, input->keybit);
+	__set_bit(BTN_TOOL_FINGER, input->keybit);
+
+	input_set_abs_params(input, ABS_X,
+			     NAVPOINT_X_MIN, NAVPOINT_X_MAX, 0, 0);
+	input_set_abs_params(input, ABS_Y,
+			     NAVPOINT_Y_MIN, NAVPOINT_Y_MAX, 0, 0);
+	input_set_abs_params(input, ABS_PRESSURE,
+			     NAVPOINT_PRESSURE_MIN, NAVPOINT_PRESSURE_MAX,
+			     0, 0);
+
+	input->open = navpoint_open;
+	input->close = navpoint_close;
+
+	input_set_drvdata(input, navpoint);
+
+	error = request_irq(ssp->irq, navpoint_irq, 0, pdev->name, navpoint);
+	if (error)
+		goto err_free_mem;
+
+	error = input_register_device(input);
+	if (error)
+		goto err_free_irq;
+
+	platform_set_drvdata(pdev, navpoint);
+	dev_dbg(&pdev->dev, "ssp%d, irq %d\n", pdata->port, ssp->irq);
+
+	return 0;
+
+err_free_irq:
+	free_irq(ssp->irq, navpoint);
+err_free_mem:
+	input_free_device(input);
+	kfree(navpoint);
+	pxa_ssp_free(ssp);
+err_free_gpio:
+	if (gpio_is_valid(pdata->gpio))
+		gpio_free(pdata->gpio);
+
+	return error;
+}
+
+static int navpoint_remove(struct platform_device *pdev)
+{
+	const struct navpoint_platform_data *pdata =
+					dev_get_platdata(&pdev->dev);
+	struct navpoint *navpoint = platform_get_drvdata(pdev);
+	struct ssp_device *ssp = navpoint->ssp;
+
+	free_irq(ssp->irq, navpoint);
+
+	input_unregister_device(navpoint->input);
+	kfree(navpoint);
+
+	pxa_ssp_free(ssp);
+
+	if (gpio_is_valid(pdata->gpio))
+		gpio_free(pdata->gpio);
+
+	return 0;
+}
+
+static int __maybe_unused navpoint_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct navpoint *navpoint = platform_get_drvdata(pdev);
+	struct input_dev *input = navpoint->input;
+
+	mutex_lock(&input->mutex);
+	if (input->users)
+		navpoint_down(navpoint);
+	mutex_unlock(&input->mutex);
+
+	return 0;
+}
+
+static int __maybe_unused navpoint_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct navpoint *navpoint = platform_get_drvdata(pdev);
+	struct input_dev *input = navpoint->input;
+
+	mutex_lock(&input->mutex);
+	if (input->users)
+		navpoint_up(navpoint);
+	mutex_unlock(&input->mutex);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(navpoint_pm_ops, navpoint_suspend, navpoint_resume);
+
+static struct platform_driver navpoint_driver = {
+	.probe		= navpoint_probe,
+	.remove		= navpoint_remove,
+	.driver = {
+		.name	= "navpoint",
+		.pm	= &navpoint_pm_ops,
+	},
+};
+
+module_platform_driver(navpoint_driver);
+
+MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>");
+MODULE_DESCRIPTION("Synaptics NavPoint (PXA27x SSP/SPI) driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:navpoint");
diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c
new file mode 100644
index 0000000..7b02b65
--- /dev/null
+++ b/drivers/input/mouse/pc110pad.c
@@ -0,0 +1,179 @@
+/*
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Alan Cox	Robin O'Leary
+ */
+
+/*
+ * IBM PC110 touchpad driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("IBM PC110 touchpad driver");
+MODULE_LICENSE("GPL");
+
+#define PC110PAD_OFF	0x30
+#define PC110PAD_ON	0x38
+
+static int pc110pad_irq = 10;
+static int pc110pad_io = 0x15e0;
+
+static struct input_dev *pc110pad_dev;
+static int pc110pad_data[3];
+static int pc110pad_count;
+
+static irqreturn_t pc110pad_interrupt(int irq, void *ptr)
+{
+	int value     = inb_p(pc110pad_io);
+	int handshake = inb_p(pc110pad_io + 2);
+
+	outb(handshake |  1, pc110pad_io + 2);
+	udelay(2);
+	outb(handshake & ~1, pc110pad_io + 2);
+	udelay(2);
+	inb_p(0x64);
+
+	pc110pad_data[pc110pad_count++] = value;
+
+	if (pc110pad_count < 3)
+		return IRQ_HANDLED;
+
+	input_report_key(pc110pad_dev, BTN_TOUCH,
+		pc110pad_data[0] & 0x01);
+	input_report_abs(pc110pad_dev, ABS_X,
+		pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100));
+	input_report_abs(pc110pad_dev, ABS_Y,
+		pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80));
+	input_sync(pc110pad_dev);
+
+	pc110pad_count = 0;
+	return IRQ_HANDLED;
+}
+
+static void pc110pad_close(struct input_dev *dev)
+{
+	outb(PC110PAD_OFF, pc110pad_io + 2);
+}
+
+static int pc110pad_open(struct input_dev *dev)
+{
+	pc110pad_interrupt(0, NULL);
+	pc110pad_interrupt(0, NULL);
+	pc110pad_interrupt(0, NULL);
+	outb(PC110PAD_ON, pc110pad_io + 2);
+	pc110pad_count = 0;
+
+	return 0;
+}
+
+/*
+ * We try to avoid enabling the hardware if it's not
+ * there, but we don't know how to test. But we do know
+ * that the PC110 is not a PCI system. So if we find any
+ * PCI devices in the machine, we don't have a PC110.
+ */
+static int __init pc110pad_init(void)
+{
+	int err;
+
+	if (!no_pci_devices())
+		return -ENODEV;
+
+	if (!request_region(pc110pad_io, 4, "pc110pad")) {
+		printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n",
+				pc110pad_io, pc110pad_io + 4);
+		return -EBUSY;
+	}
+
+	outb(PC110PAD_OFF, pc110pad_io + 2);
+
+	if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL)) {
+		printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq);
+		err = -EBUSY;
+		goto err_release_region;
+	}
+
+	pc110pad_dev = input_allocate_device();
+	if (!pc110pad_dev) {
+		printk(KERN_ERR "pc110pad: Not enough memory.\n");
+		err = -ENOMEM;
+		goto err_free_irq;
+	}
+
+	pc110pad_dev->name = "IBM PC110 TouchPad";
+	pc110pad_dev->phys = "isa15e0/input0";
+	pc110pad_dev->id.bustype = BUS_ISA;
+	pc110pad_dev->id.vendor = 0x0003;
+	pc110pad_dev->id.product = 0x0001;
+	pc110pad_dev->id.version = 0x0100;
+
+	pc110pad_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	pc110pad_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y);
+	pc110pad_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_abs_set_max(pc110pad_dev, ABS_X, 0x1ff);
+	input_abs_set_max(pc110pad_dev, ABS_Y, 0x0ff);
+
+	pc110pad_dev->open = pc110pad_open;
+	pc110pad_dev->close = pc110pad_close;
+
+	err = input_register_device(pc110pad_dev);
+	if (err)
+		goto err_free_dev;
+
+	return 0;
+
+ err_free_dev:
+	input_free_device(pc110pad_dev);
+ err_free_irq:
+	free_irq(pc110pad_irq, NULL);
+ err_release_region:
+	release_region(pc110pad_io, 4);
+
+	return err;
+}
+
+static void __exit pc110pad_exit(void)
+{
+	outb(PC110PAD_OFF, pc110pad_io + 2);
+	free_irq(pc110pad_irq, NULL);
+	input_unregister_device(pc110pad_dev);
+	release_region(pc110pad_io, 4);
+}
+
+module_init(pc110pad_init);
+module_exit(pc110pad_exit);
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
new file mode 100644
index 0000000..ad18dab
--- /dev/null
+++ b/drivers/input/mouse/psmouse-base.c
@@ -0,0 +1,1945 @@
+/*
+ * PS/2 mouse driver
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 2003-2004 Dmitry Torokhov
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt
+#define psmouse_fmt(fmt)	fmt
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/libps2.h>
+#include <linux/mutex.h>
+
+#include "psmouse.h"
+#include "synaptics.h"
+#include "logips2pp.h"
+#include "alps.h"
+#include "hgpk.h"
+#include "lifebook.h"
+#include "trackpoint.h"
+#include "touchkit_ps2.h"
+#include "elantech.h"
+#include "sentelic.h"
+#include "cypress_ps2.h"
+#include "focaltech.h"
+#include "vmmouse.h"
+
+#define DRIVER_DESC	"PS/2 mouse driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static unsigned int psmouse_max_proto = PSMOUSE_AUTO;
+static int psmouse_set_maxproto(const char *val, const struct kernel_param *);
+static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp);
+static const struct kernel_param_ops param_ops_proto_abbrev = {
+	.set = psmouse_set_maxproto,
+	.get = psmouse_get_maxproto,
+};
+#define param_check_proto_abbrev(name, p)	__param_check(name, p, unsigned int)
+module_param_named(proto, psmouse_max_proto, proto_abbrev, 0644);
+MODULE_PARM_DESC(proto, "Highest protocol extension to probe (bare, imps, exps, any). Useful for KVM switches.");
+
+static unsigned int psmouse_resolution = 200;
+module_param_named(resolution, psmouse_resolution, uint, 0644);
+MODULE_PARM_DESC(resolution, "Resolution, in dpi.");
+
+static unsigned int psmouse_rate = 100;
+module_param_named(rate, psmouse_rate, uint, 0644);
+MODULE_PARM_DESC(rate, "Report rate, in reports per second.");
+
+static bool psmouse_smartscroll = true;
+module_param_named(smartscroll, psmouse_smartscroll, bool, 0644);
+MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled.");
+
+static unsigned int psmouse_resetafter = 5;
+module_param_named(resetafter, psmouse_resetafter, uint, 0644);
+MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never).");
+
+static unsigned int psmouse_resync_time;
+module_param_named(resync_time, psmouse_resync_time, uint, 0644);
+MODULE_PARM_DESC(resync_time, "How long can mouse stay idle before forcing resync (in seconds, 0 = never).");
+
+PSMOUSE_DEFINE_ATTR(protocol, S_IWUSR | S_IRUGO,
+			NULL,
+			psmouse_attr_show_protocol, psmouse_attr_set_protocol);
+PSMOUSE_DEFINE_ATTR(rate, S_IWUSR | S_IRUGO,
+			(void *) offsetof(struct psmouse, rate),
+			psmouse_show_int_attr, psmouse_attr_set_rate);
+PSMOUSE_DEFINE_ATTR(resolution, S_IWUSR | S_IRUGO,
+			(void *) offsetof(struct psmouse, resolution),
+			psmouse_show_int_attr, psmouse_attr_set_resolution);
+PSMOUSE_DEFINE_ATTR(resetafter, S_IWUSR | S_IRUGO,
+			(void *) offsetof(struct psmouse, resetafter),
+			psmouse_show_int_attr, psmouse_set_int_attr);
+PSMOUSE_DEFINE_ATTR(resync_time, S_IWUSR | S_IRUGO,
+			(void *) offsetof(struct psmouse, resync_time),
+			psmouse_show_int_attr, psmouse_set_int_attr);
+
+static struct attribute *psmouse_attributes[] = {
+	&psmouse_attr_protocol.dattr.attr,
+	&psmouse_attr_rate.dattr.attr,
+	&psmouse_attr_resolution.dattr.attr,
+	&psmouse_attr_resetafter.dattr.attr,
+	&psmouse_attr_resync_time.dattr.attr,
+	NULL
+};
+
+static struct attribute_group psmouse_attribute_group = {
+	.attrs	= psmouse_attributes,
+};
+
+/*
+ * psmouse_mutex protects all operations changing state of mouse
+ * (connecting, disconnecting, changing rate or resolution via
+ * sysfs). We could use a per-device semaphore but since there
+ * rarely more than one PS/2 mouse connected and since semaphore
+ * is taken in "slow" paths it is not worth it.
+ */
+static DEFINE_MUTEX(psmouse_mutex);
+
+static struct workqueue_struct *kpsmoused_wq;
+
+struct psmouse_protocol {
+	enum psmouse_type type;
+	bool maxproto;
+	bool ignore_parity; /* Protocol should ignore parity errors from KBC */
+	const char *name;
+	const char *alias;
+	int (*detect)(struct psmouse *, bool);
+	int (*init)(struct psmouse *);
+};
+
+/*
+ * psmouse_process_byte() analyzes the PS/2 data stream and reports
+ * relevant events to the input module once full packet has arrived.
+ */
+
+psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	unsigned char *packet = psmouse->packet;
+
+	if (psmouse->pktcnt < psmouse->pktsize)
+		return PSMOUSE_GOOD_DATA;
+
+/*
+ * Full packet accumulated, process it
+ */
+
+/*
+ * Scroll wheel on IntelliMice, scroll buttons on NetMice
+ */
+
+	if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS)
+		input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
+
+/*
+ * Scroll wheel and buttons on IntelliMouse Explorer
+ */
+
+	if (psmouse->type == PSMOUSE_IMEX) {
+		switch (packet[3] & 0xC0) {
+		case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */
+			input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
+			break;
+		case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */
+			input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
+			break;
+		case 0x00:
+		case 0xC0:
+			input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7));
+			input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1);
+			input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
+			break;
+		}
+	}
+
+/*
+ * Extra buttons on Genius NewNet 3D
+ */
+
+	if (psmouse->type == PSMOUSE_GENPS) {
+		input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1);
+		input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1);
+	}
+
+/*
+ * Extra button on ThinkingMouse
+ */
+	if (psmouse->type == PSMOUSE_THINKPS) {
+		input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1);
+		/* Without this bit of weirdness moving up gives wildly high Y changes. */
+		packet[1] |= (packet[0] & 0x40) << 1;
+	}
+
+/*
+ * Cortron PS2 Trackball reports SIDE button on the 4th bit of the first
+ * byte.
+ */
+	if (psmouse->type == PSMOUSE_CORTRON) {
+		input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1);
+		packet[0] |= 0x08;
+	}
+
+/*
+ * Generic PS/2 Mouse
+ */
+
+	input_report_key(dev, BTN_LEFT,    packet[0]       & 1);
+	input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
+	input_report_key(dev, BTN_RIGHT,  (packet[0] >> 1) & 1);
+
+	input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
+	input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
+
+	input_sync(dev);
+
+	return PSMOUSE_FULL_PACKET;
+}
+
+void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
+		unsigned long delay)
+{
+	queue_delayed_work(kpsmoused_wq, work, delay);
+}
+
+/*
+ * __psmouse_set_state() sets new psmouse state and resets all flags.
+ */
+
+static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
+{
+	psmouse->state = new_state;
+	psmouse->pktcnt = psmouse->out_of_sync_cnt = 0;
+	psmouse->ps2dev.flags = 0;
+	psmouse->last = jiffies;
+}
+
+
+/*
+ * psmouse_set_state() sets new psmouse state and resets all flags and
+ * counters while holding serio lock so fighting with interrupt handler
+ * is not a concern.
+ */
+
+void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
+{
+	serio_pause_rx(psmouse->ps2dev.serio);
+	__psmouse_set_state(psmouse, new_state);
+	serio_continue_rx(psmouse->ps2dev.serio);
+}
+
+/*
+ * psmouse_handle_byte() processes one byte of the input data stream
+ * by calling corresponding protocol handler.
+ */
+
+static int psmouse_handle_byte(struct psmouse *psmouse)
+{
+	psmouse_ret_t rc = psmouse->protocol_handler(psmouse);
+
+	switch (rc) {
+	case PSMOUSE_BAD_DATA:
+		if (psmouse->state == PSMOUSE_ACTIVATED) {
+			psmouse_warn(psmouse,
+				     "%s at %s lost sync at byte %d\n",
+				     psmouse->name, psmouse->phys,
+				     psmouse->pktcnt);
+			if (++psmouse->out_of_sync_cnt == psmouse->resetafter) {
+				__psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+				psmouse_notice(psmouse,
+						"issuing reconnect request\n");
+				serio_reconnect(psmouse->ps2dev.serio);
+				return -1;
+			}
+		}
+		psmouse->pktcnt = 0;
+		break;
+
+	case PSMOUSE_FULL_PACKET:
+		psmouse->pktcnt = 0;
+		if (psmouse->out_of_sync_cnt) {
+			psmouse->out_of_sync_cnt = 0;
+			psmouse_notice(psmouse,
+					"%s at %s - driver resynced.\n",
+					psmouse->name, psmouse->phys);
+		}
+		break;
+
+	case PSMOUSE_GOOD_DATA:
+		break;
+	}
+	return 0;
+}
+
+/*
+ * psmouse_interrupt() handles incoming characters, either passing them
+ * for normal processing or gathering them as command response.
+ */
+
+static irqreturn_t psmouse_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct psmouse *psmouse = serio_get_drvdata(serio);
+
+	if (psmouse->state == PSMOUSE_IGNORE)
+		goto out;
+
+	if (unlikely((flags & SERIO_TIMEOUT) ||
+		     ((flags & SERIO_PARITY) && !psmouse->ignore_parity))) {
+
+		if (psmouse->state == PSMOUSE_ACTIVATED)
+			psmouse_warn(psmouse,
+				     "bad data from KBC -%s%s\n",
+				     flags & SERIO_TIMEOUT ? " timeout" : "",
+				     flags & SERIO_PARITY ? " bad parity" : "");
+		ps2_cmd_aborted(&psmouse->ps2dev);
+		goto out;
+	}
+
+	if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_ACK))
+		if  (ps2_handle_ack(&psmouse->ps2dev, data))
+			goto out;
+
+	if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_CMD))
+		if  (ps2_handle_response(&psmouse->ps2dev, data))
+			goto out;
+
+	if (psmouse->state <= PSMOUSE_RESYNCING)
+		goto out;
+
+	if (psmouse->state == PSMOUSE_ACTIVATED &&
+	    psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
+		psmouse_info(psmouse, "%s at %s lost synchronization, throwing %d bytes away.\n",
+			     psmouse->name, psmouse->phys, psmouse->pktcnt);
+		psmouse->badbyte = psmouse->packet[0];
+		__psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
+		psmouse_queue_work(psmouse, &psmouse->resync_work, 0);
+		goto out;
+	}
+
+	psmouse->packet[psmouse->pktcnt++] = data;
+/*
+ * Check if this is a new device announcement (0xAA 0x00)
+ */
+	if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) {
+		if (psmouse->pktcnt == 1) {
+			psmouse->last = jiffies;
+			goto out;
+		}
+
+		if (psmouse->packet[1] == PSMOUSE_RET_ID ||
+		    (psmouse->type == PSMOUSE_HGPK &&
+		     psmouse->packet[1] == PSMOUSE_RET_BAT)) {
+			__psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+			serio_reconnect(serio);
+			goto out;
+		}
+/*
+ * Not a new device, try processing first byte normally
+ */
+		psmouse->pktcnt = 1;
+		if (psmouse_handle_byte(psmouse))
+			goto out;
+
+		psmouse->packet[psmouse->pktcnt++] = data;
+	}
+
+/*
+ * See if we need to force resync because mouse was idle for too long
+ */
+	if (psmouse->state == PSMOUSE_ACTIVATED &&
+	    psmouse->pktcnt == 1 && psmouse->resync_time &&
+	    time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) {
+		psmouse->badbyte = psmouse->packet[0];
+		__psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
+		psmouse_queue_work(psmouse, &psmouse->resync_work, 0);
+		goto out;
+	}
+
+	psmouse->last = jiffies;
+	psmouse_handle_byte(psmouse);
+
+ out:
+	return IRQ_HANDLED;
+}
+
+
+/*
+ * psmouse_sliced_command() sends an extended PS/2 command to the mouse
+ * using sliced syntax, understood by advanced devices, such as Logitech
+ * or Synaptics touchpads. The command is encoded as:
+ * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
+ * is the command.
+ */
+int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command)
+{
+	int i;
+
+	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
+		return -1;
+
+	for (i = 6; i >= 0; i -= 2) {
+		unsigned char d = (command >> i) & 3;
+		if (ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES))
+			return -1;
+	}
+
+	return 0;
+}
+
+
+/*
+ * psmouse_reset() resets the mouse into power-on state.
+ */
+int psmouse_reset(struct psmouse *psmouse)
+{
+	unsigned char param[2];
+
+	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT))
+		return -1;
+
+	if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID)
+		return -1;
+
+	return 0;
+}
+
+/*
+ * Here we set the mouse resolution.
+ */
+
+void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
+{
+	static const unsigned char params[] = { 0, 1, 2, 2, 3 };
+	unsigned char p;
+
+	if (resolution == 0 || resolution > 200)
+		resolution = 200;
+
+	p = params[resolution / 50];
+	ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES);
+	psmouse->resolution = 25 << p;
+}
+
+/*
+ * Here we set the mouse report rate.
+ */
+
+static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
+{
+	static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
+	unsigned char r;
+	int i = 0;
+
+	while (rates[i] > rate) i++;
+	r = rates[i];
+	ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE);
+	psmouse->rate = r;
+}
+
+/*
+ * Here we set the mouse scaling.
+ */
+
+static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale)
+{
+	ps2_command(&psmouse->ps2dev, NULL,
+		    scale == PSMOUSE_SCALE21 ? PSMOUSE_CMD_SETSCALE21 :
+					       PSMOUSE_CMD_SETSCALE11);
+}
+
+/*
+ * psmouse_poll() - default poll handler. Everyone except for ALPS uses it.
+ */
+
+static int psmouse_poll(struct psmouse *psmouse)
+{
+	return ps2_command(&psmouse->ps2dev, psmouse->packet,
+			   PSMOUSE_CMD_POLL | (psmouse->pktsize << 8));
+}
+
+static bool psmouse_check_pnp_id(const char *id, const char * const ids[])
+{
+	int i;
+
+	for (i = 0; ids[i]; i++)
+		if (!strcasecmp(id, ids[i]))
+			return true;
+
+	return false;
+}
+
+/*
+ * psmouse_matches_pnp_id - check if psmouse matches one of the passed in ids.
+ */
+bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[])
+{
+	struct serio *serio = psmouse->ps2dev.serio;
+	char *p, *fw_id_copy, *save_ptr;
+	bool found = false;
+
+	if (strncmp(serio->firmware_id, "PNP: ", 5))
+		return false;
+
+	fw_id_copy = kstrndup(&serio->firmware_id[5],
+			      sizeof(serio->firmware_id) - 5,
+			      GFP_KERNEL);
+	if (!fw_id_copy)
+		return false;
+
+	save_ptr = fw_id_copy;
+	while ((p = strsep(&fw_id_copy, " ")) != NULL) {
+		if (psmouse_check_pnp_id(p, ids)) {
+			found = true;
+			break;
+		}
+	}
+
+	kfree(save_ptr);
+	return found;
+}
+
+/*
+ * Genius NetMouse magic init.
+ */
+static int genius_detect(struct psmouse *psmouse, bool set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4];
+
+	param[0] = 3;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
+
+	if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55)
+		return -1;
+
+	if (set_properties) {
+		__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+		__set_bit(BTN_EXTRA, psmouse->dev->keybit);
+		__set_bit(BTN_SIDE, psmouse->dev->keybit);
+		__set_bit(REL_WHEEL, psmouse->dev->relbit);
+
+		psmouse->vendor = "Genius";
+		psmouse->name = "Mouse";
+		psmouse->pktsize = 4;
+	}
+
+	return 0;
+}
+
+/*
+ * IntelliMouse magic init.
+ */
+static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[2];
+
+	param[0] = 200;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] = 100;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] =  80;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
+
+	if (param[0] != 3)
+		return -1;
+
+	if (set_properties) {
+		__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+		__set_bit(REL_WHEEL, psmouse->dev->relbit);
+
+		if (!psmouse->vendor)
+			psmouse->vendor = "Generic";
+		if (!psmouse->name)
+			psmouse->name = "Wheel Mouse";
+		psmouse->pktsize = 4;
+	}
+
+	return 0;
+}
+
+/*
+ * Try IntelliMouse/Explorer magic init.
+ */
+static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[2];
+
+	intellimouse_detect(psmouse, 0);
+
+	param[0] = 200;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] = 200;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] =  80;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
+
+	if (param[0] != 4)
+		return -1;
+
+/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
+	param[0] = 200;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] =  80;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] =  40;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+
+	if (set_properties) {
+		__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+		__set_bit(REL_WHEEL, psmouse->dev->relbit);
+		__set_bit(REL_HWHEEL, psmouse->dev->relbit);
+		__set_bit(BTN_SIDE, psmouse->dev->keybit);
+		__set_bit(BTN_EXTRA, psmouse->dev->keybit);
+
+		if (!psmouse->vendor)
+			psmouse->vendor = "Generic";
+		if (!psmouse->name)
+			psmouse->name = "Explorer Mouse";
+		psmouse->pktsize = 4;
+	}
+
+	return 0;
+}
+
+/*
+ * Kensington ThinkingMouse / ExpertMouse magic init.
+ */
+static int thinking_detect(struct psmouse *psmouse, bool set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[2];
+	static const unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
+	int i;
+
+	param[0] = 10;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] = 0;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	for (i = 0; i < ARRAY_SIZE(seq); i++) {
+		param[0] = seq[i];
+		ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	}
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
+
+	if (param[0] != 2)
+		return -1;
+
+	if (set_properties) {
+		__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+		__set_bit(BTN_EXTRA, psmouse->dev->keybit);
+
+		psmouse->vendor = "Kensington";
+		psmouse->name = "ThinkingMouse";
+	}
+
+	return 0;
+}
+
+/*
+ * Bare PS/2 protocol "detection". Always succeeds.
+ */
+static int ps2bare_detect(struct psmouse *psmouse, bool set_properties)
+{
+	if (set_properties) {
+		if (!psmouse->vendor)
+			psmouse->vendor = "Generic";
+		if (!psmouse->name)
+			psmouse->name = "Mouse";
+
+/*
+ * We have no way of figuring true number of buttons so let's
+ * assume that the device has 3.
+ */
+		__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+	}
+
+	return 0;
+}
+
+/*
+ * Cortron PS/2 protocol detection. There's no special way to detect it, so it
+ * must be forced by sysfs protocol writing.
+ */
+static int cortron_detect(struct psmouse *psmouse, bool set_properties)
+{
+	if (set_properties) {
+		psmouse->vendor = "Cortron";
+		psmouse->name = "PS/2 Trackball";
+
+		__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+		__set_bit(BTN_SIDE, psmouse->dev->keybit);
+	}
+
+	return 0;
+}
+
+/*
+ * Apply default settings to the psmouse structure. Most of them will
+ * be overridden by individual protocol initialization routines.
+ */
+
+static void psmouse_apply_defaults(struct psmouse *psmouse)
+{
+	struct input_dev *input_dev = psmouse->dev;
+
+	memset(input_dev->evbit, 0, sizeof(input_dev->evbit));
+	memset(input_dev->keybit, 0, sizeof(input_dev->keybit));
+	memset(input_dev->relbit, 0, sizeof(input_dev->relbit));
+	memset(input_dev->absbit, 0, sizeof(input_dev->absbit));
+	memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit));
+
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(EV_REL, input_dev->evbit);
+
+	__set_bit(BTN_LEFT, input_dev->keybit);
+	__set_bit(BTN_RIGHT, input_dev->keybit);
+
+	__set_bit(REL_X, input_dev->relbit);
+	__set_bit(REL_Y, input_dev->relbit);
+
+	__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+	psmouse->set_rate = psmouse_set_rate;
+	psmouse->set_resolution = psmouse_set_resolution;
+	psmouse->set_scale = psmouse_set_scale;
+	psmouse->poll = psmouse_poll;
+	psmouse->protocol_handler = psmouse_process_byte;
+	psmouse->pktsize = 3;
+	psmouse->reconnect = NULL;
+	psmouse->disconnect = NULL;
+	psmouse->cleanup = NULL;
+	psmouse->pt_activate = NULL;
+	psmouse->pt_deactivate = NULL;
+}
+
+/*
+ * Apply default settings to the psmouse structure and call specified
+ * protocol detection or initialization routine.
+ */
+static int psmouse_do_detect(int (*detect)(struct psmouse *psmouse,
+					   bool set_properties),
+			     struct psmouse *psmouse, bool set_properties)
+{
+	if (set_properties)
+		psmouse_apply_defaults(psmouse);
+
+	return detect(psmouse, set_properties);
+}
+
+/*
+ * psmouse_extensions() probes for any extensions to the basic PS/2 protocol
+ * the mouse may have.
+ */
+
+static int psmouse_extensions(struct psmouse *psmouse,
+			      unsigned int max_proto, bool set_properties)
+{
+	bool synaptics_hardware = false;
+
+/* Always check for focaltech, this is safe as it uses pnp-id matching */
+	if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
+		if (max_proto > PSMOUSE_IMEX) {
+			if (!set_properties || focaltech_init(psmouse) == 0) {
+				if (IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH))
+					return PSMOUSE_FOCALTECH;
+				/*
+				 * Note that we need to also restrict
+				 * psmouse_max_proto so that psmouse_initialize()
+				 * does not try to reset rate and resolution,
+				 * because even that upsets the device.
+				 */
+				psmouse_max_proto = PSMOUSE_PS2;
+				return PSMOUSE_PS2;
+			}
+		}
+	}
+
+/*
+ * We always check for lifebook because it does not disturb mouse
+ * (it only checks DMI information).
+ */
+	if (psmouse_do_detect(lifebook_detect, psmouse, set_properties) == 0) {
+		if (max_proto > PSMOUSE_IMEX) {
+			if (!set_properties || lifebook_init(psmouse) == 0)
+				return PSMOUSE_LIFEBOOK;
+		}
+	}
+
+	if (psmouse_do_detect(vmmouse_detect, psmouse, set_properties) == 0) {
+		if (max_proto > PSMOUSE_IMEX) {
+			if (!set_properties || vmmouse_init(psmouse) == 0)
+				return PSMOUSE_VMMOUSE;
+		}
+	}
+
+/*
+ * Try Kensington ThinkingMouse (we try first, because synaptics probe
+ * upsets the thinkingmouse).
+ */
+
+	if (max_proto > PSMOUSE_IMEX &&
+	    psmouse_do_detect(thinking_detect, psmouse, set_properties) == 0) {
+		return PSMOUSE_THINKPS;
+	}
+
+/*
+ * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol
+ * support is disabled in config - we need to know if it is synaptics so we
+ * can reset it properly after probing for intellimouse.
+ */
+	if (max_proto > PSMOUSE_PS2 &&
+	    psmouse_do_detect(synaptics_detect, psmouse, set_properties) == 0) {
+		synaptics_hardware = true;
+
+		if (max_proto > PSMOUSE_IMEX) {
+/*
+ * Try activating protocol, but check if support is enabled first, since
+ * we try detecting Synaptics even when protocol is disabled.
+ */
+			if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) &&
+			    (!set_properties || synaptics_init(psmouse) == 0)) {
+				return PSMOUSE_SYNAPTICS;
+			}
+
+/*
+ * Some Synaptics touchpads can emulate extended protocols (like IMPS/2).
+ * Unfortunately Logitech/Genius probes confuse some firmware versions so
+ * we'll have to skip them.
+ */
+			max_proto = PSMOUSE_IMEX;
+		}
+/*
+ * Make sure that touchpad is in relative mode, gestures (taps) are enabled
+ */
+		synaptics_reset(psmouse);
+	}
+
+/*
+ * Try Cypress Trackpad.
+ * Must try it before Finger Sensing Pad because Finger Sensing Pad probe
+ * upsets some modules of Cypress Trackpads.
+ */
+	if (max_proto > PSMOUSE_IMEX &&
+			cypress_detect(psmouse, set_properties) == 0) {
+		if (IS_ENABLED(CONFIG_MOUSE_PS2_CYPRESS)) {
+			if (cypress_init(psmouse) == 0)
+				return PSMOUSE_CYPRESS;
+
+			/*
+			 * Finger Sensing Pad probe upsets some modules of
+			 * Cypress Trackpad, must avoid Finger Sensing Pad
+			 * probe if Cypress Trackpad device detected.
+			 */
+			return PSMOUSE_PS2;
+		}
+
+		max_proto = PSMOUSE_IMEX;
+	}
+
+/*
+ * Try ALPS TouchPad
+ */
+	if (max_proto > PSMOUSE_IMEX) {
+		ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+		if (psmouse_do_detect(alps_detect,
+				      psmouse, set_properties) == 0) {
+			if (!set_properties || alps_init(psmouse) == 0)
+				return PSMOUSE_ALPS;
+/*
+ * Init failed, try basic relative protocols
+ */
+			max_proto = PSMOUSE_IMEX;
+		}
+	}
+
+/*
+ * Try OLPC HGPK touchpad.
+ */
+	if (max_proto > PSMOUSE_IMEX &&
+	    psmouse_do_detect(hgpk_detect, psmouse, set_properties) == 0) {
+		if (!set_properties || hgpk_init(psmouse) == 0)
+			return PSMOUSE_HGPK;
+/*
+ * Init failed, try basic relative protocols
+ */
+		max_proto = PSMOUSE_IMEX;
+	}
+
+/*
+ * Try Elantech touchpad.
+ */
+	if (max_proto > PSMOUSE_IMEX &&
+	    psmouse_do_detect(elantech_detect, psmouse, set_properties) == 0) {
+		if (!set_properties || elantech_init(psmouse) == 0)
+			return PSMOUSE_ELANTECH;
+/*
+ * Init failed, try basic relative protocols
+ */
+		max_proto = PSMOUSE_IMEX;
+	}
+
+	if (max_proto > PSMOUSE_IMEX) {
+		if (psmouse_do_detect(genius_detect,
+				      psmouse, set_properties) == 0)
+			return PSMOUSE_GENPS;
+
+		if (psmouse_do_detect(ps2pp_init,
+				      psmouse, set_properties) == 0)
+			return PSMOUSE_PS2PP;
+
+		if (psmouse_do_detect(trackpoint_detect,
+				      psmouse, set_properties) == 0)
+			return PSMOUSE_TRACKPOINT;
+
+		if (psmouse_do_detect(touchkit_ps2_detect,
+				      psmouse, set_properties) == 0)
+			return PSMOUSE_TOUCHKIT_PS2;
+	}
+
+/*
+ * Try Finger Sensing Pad. We do it here because its probe upsets
+ * Trackpoint devices (causing TP_READ_ID command to time out).
+ */
+	if (max_proto > PSMOUSE_IMEX) {
+		if (psmouse_do_detect(fsp_detect,
+				      psmouse, set_properties) == 0) {
+			if (!set_properties || fsp_init(psmouse) == 0)
+				return PSMOUSE_FSP;
+/*
+ * Init failed, try basic relative protocols
+ */
+			max_proto = PSMOUSE_IMEX;
+		}
+	}
+
+/*
+ * Reset to defaults in case the device got confused by extended
+ * protocol probes. Note that we follow up with full reset because
+ * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
+ */
+	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+	psmouse_reset(psmouse);
+
+	if (max_proto >= PSMOUSE_IMEX &&
+	    psmouse_do_detect(im_explorer_detect,
+			      psmouse, set_properties) == 0) {
+		return PSMOUSE_IMEX;
+	}
+
+	if (max_proto >= PSMOUSE_IMPS &&
+	    psmouse_do_detect(intellimouse_detect,
+			      psmouse, set_properties) == 0) {
+		return PSMOUSE_IMPS;
+	}
+
+/*
+ * Okay, all failed, we have a standard mouse here. The number of the buttons
+ * is still a question, though. We assume 3.
+ */
+	psmouse_do_detect(ps2bare_detect, psmouse, set_properties);
+
+	if (synaptics_hardware) {
+/*
+ * We detected Synaptics hardware but it did not respond to IMPS/2 probes.
+ * We need to reset the touchpad because if there is a track point on the
+ * pass through port it could get disabled while probing for protocol
+ * extensions.
+ */
+		psmouse_reset(psmouse);
+	}
+
+	return PSMOUSE_PS2;
+}
+
+static const struct psmouse_protocol psmouse_protocols[] = {
+	{
+		.type		= PSMOUSE_PS2,
+		.name		= "PS/2",
+		.alias		= "bare",
+		.maxproto	= true,
+		.ignore_parity	= true,
+		.detect		= ps2bare_detect,
+	},
+#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
+	{
+		.type		= PSMOUSE_PS2PP,
+		.name		= "PS2++",
+		.alias		= "logitech",
+		.detect		= ps2pp_init,
+	},
+#endif
+	{
+		.type		= PSMOUSE_THINKPS,
+		.name		= "ThinkPS/2",
+		.alias		= "thinkps",
+		.detect		= thinking_detect,
+	},
+#ifdef CONFIG_MOUSE_PS2_CYPRESS
+	{
+		.type		= PSMOUSE_CYPRESS,
+		.name		= "CyPS/2",
+		.alias		= "cypress",
+		.detect		= cypress_detect,
+		.init		= cypress_init,
+	},
+#endif
+	{
+		.type		= PSMOUSE_GENPS,
+		.name		= "GenPS/2",
+		.alias		= "genius",
+		.detect		= genius_detect,
+	},
+	{
+		.type		= PSMOUSE_IMPS,
+		.name		= "ImPS/2",
+		.alias		= "imps",
+		.maxproto	= true,
+		.ignore_parity	= true,
+		.detect		= intellimouse_detect,
+	},
+	{
+		.type		= PSMOUSE_IMEX,
+		.name		= "ImExPS/2",
+		.alias		= "exps",
+		.maxproto	= true,
+		.ignore_parity	= true,
+		.detect		= im_explorer_detect,
+	},
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
+	{
+		.type		= PSMOUSE_SYNAPTICS,
+		.name		= "SynPS/2",
+		.alias		= "synaptics",
+		.detect		= synaptics_detect,
+		.init		= synaptics_init,
+	},
+	{
+		.type		= PSMOUSE_SYNAPTICS_RELATIVE,
+		.name		= "SynRelPS/2",
+		.alias		= "synaptics-relative",
+		.detect		= synaptics_detect,
+		.init		= synaptics_init_relative,
+	},
+#endif
+#ifdef CONFIG_MOUSE_PS2_ALPS
+	{
+		.type		= PSMOUSE_ALPS,
+		.name		= "AlpsPS/2",
+		.alias		= "alps",
+		.detect		= alps_detect,
+		.init		= alps_init,
+	},
+#endif
+#ifdef CONFIG_MOUSE_PS2_LIFEBOOK
+	{
+		.type		= PSMOUSE_LIFEBOOK,
+		.name		= "LBPS/2",
+		.alias		= "lifebook",
+		.init		= lifebook_init,
+	},
+#endif
+#ifdef CONFIG_MOUSE_PS2_TRACKPOINT
+	{
+		.type		= PSMOUSE_TRACKPOINT,
+		.name		= "TPPS/2",
+		.alias		= "trackpoint",
+		.detect		= trackpoint_detect,
+	},
+#endif
+#ifdef CONFIG_MOUSE_PS2_TOUCHKIT
+	{
+		.type		= PSMOUSE_TOUCHKIT_PS2,
+		.name		= "touchkitPS/2",
+		.alias		= "touchkit",
+		.detect		= touchkit_ps2_detect,
+	},
+#endif
+#ifdef CONFIG_MOUSE_PS2_OLPC
+	{
+		.type		= PSMOUSE_HGPK,
+		.name		= "OLPC HGPK",
+		.alias		= "hgpk",
+		.detect		= hgpk_detect,
+	},
+#endif
+#ifdef CONFIG_MOUSE_PS2_ELANTECH
+	{
+		.type		= PSMOUSE_ELANTECH,
+		.name		= "ETPS/2",
+		.alias		= "elantech",
+		.detect		= elantech_detect,
+		.init		= elantech_init,
+	},
+#endif
+#ifdef CONFIG_MOUSE_PS2_SENTELIC
+	{
+		.type		= PSMOUSE_FSP,
+		.name		= "FSPPS/2",
+		.alias		= "fsp",
+		.detect		= fsp_detect,
+		.init		= fsp_init,
+	},
+#endif
+	{
+		.type		= PSMOUSE_CORTRON,
+		.name		= "CortronPS/2",
+		.alias		= "cortps",
+		.detect		= cortron_detect,
+	},
+#ifdef CONFIG_MOUSE_PS2_FOCALTECH
+	{
+		.type		= PSMOUSE_FOCALTECH,
+		.name		= "FocalTechPS/2",
+		.alias		= "focaltech",
+		.detect		= focaltech_detect,
+		.init		= focaltech_init,
+	},
+#endif
+#ifdef CONFIG_MOUSE_PS2_VMMOUSE
+	{
+		.type		= PSMOUSE_VMMOUSE,
+		.name		= VMMOUSE_PSNAME,
+		.alias		= "vmmouse",
+		.detect		= vmmouse_detect,
+		.init		= vmmouse_init,
+	},
+#endif
+	{
+		.type		= PSMOUSE_AUTO,
+		.name		= "auto",
+		.alias		= "any",
+		.maxproto	= true,
+	},
+};
+
+static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++)
+		if (psmouse_protocols[i].type == type)
+			return &psmouse_protocols[i];
+
+	WARN_ON(1);
+	return &psmouse_protocols[0];
+}
+
+static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len)
+{
+	const struct psmouse_protocol *p;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) {
+		p = &psmouse_protocols[i];
+
+		if ((strlen(p->name) == len && !strncmp(p->name, name, len)) ||
+		    (strlen(p->alias) == len && !strncmp(p->alias, name, len)))
+			return &psmouse_protocols[i];
+	}
+
+	return NULL;
+}
+
+
+/*
+ * psmouse_probe() probes for a PS/2 mouse.
+ */
+
+static int psmouse_probe(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[2];
+
+/*
+ * First, we check if it's a mouse. It should send 0x00 or 0x03
+ * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
+ * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and subsequent
+ * ID queries, probably due to a firmware bug.
+ */
+
+	param[0] = 0xa5;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID))
+		return -1;
+
+	if (param[0] != 0x00 && param[0] != 0x03 &&
+	    param[0] != 0x04 && param[0] != 0xff)
+		return -1;
+
+/*
+ * Then we reset and disable the mouse so that it doesn't generate events.
+ */
+
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS))
+		psmouse_warn(psmouse, "Failed to reset mouse on %s\n",
+			     ps2dev->serio->phys);
+
+	return 0;
+}
+
+/*
+ * psmouse_initialize() initializes the mouse to a sane state.
+ */
+
+static void psmouse_initialize(struct psmouse *psmouse)
+{
+/*
+ * We set the mouse report rate, resolution and scaling.
+ */
+
+	if (psmouse_max_proto != PSMOUSE_PS2) {
+		psmouse->set_rate(psmouse, psmouse->rate);
+		psmouse->set_resolution(psmouse, psmouse->resolution);
+		psmouse->set_scale(psmouse, PSMOUSE_SCALE11);
+	}
+}
+
+/*
+ * psmouse_activate() enables the mouse so that we get motion reports from it.
+ */
+
+int psmouse_activate(struct psmouse *psmouse)
+{
+	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
+		psmouse_warn(psmouse, "Failed to enable mouse on %s\n",
+			     psmouse->ps2dev.serio->phys);
+		return -1;
+	}
+
+	psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+	return 0;
+}
+
+/*
+ * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion
+ * reports from it unless we explicitly request it.
+ */
+
+int psmouse_deactivate(struct psmouse *psmouse)
+{
+	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) {
+		psmouse_warn(psmouse, "Failed to deactivate mouse on %s\n",
+			     psmouse->ps2dev.serio->phys);
+		return -1;
+	}
+
+	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+	return 0;
+}
+
+
+/*
+ * psmouse_resync() attempts to re-validate current protocol.
+ */
+
+static void psmouse_resync(struct work_struct *work)
+{
+	struct psmouse *parent = NULL, *psmouse =
+		container_of(work, struct psmouse, resync_work.work);
+	struct serio *serio = psmouse->ps2dev.serio;
+	psmouse_ret_t rc = PSMOUSE_GOOD_DATA;
+	bool failed = false, enabled = false;
+	int i;
+
+	mutex_lock(&psmouse_mutex);
+
+	if (psmouse->state != PSMOUSE_RESYNCING)
+		goto out;
+
+	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+		parent = serio_get_drvdata(serio->parent);
+		psmouse_deactivate(parent);
+	}
+
+/*
+ * Some mice don't ACK commands sent while they are in the middle of
+ * transmitting motion packet. To avoid delay we use ps2_sendbyte()
+ * instead of ps2_command() which would wait for 200ms for an ACK
+ * that may never come.
+ * As an additional quirk ALPS touchpads may not only forget to ACK
+ * disable command but will stop reporting taps, so if we see that
+ * mouse at least once ACKs disable we will do full reconnect if ACK
+ * is missing.
+ */
+	psmouse->num_resyncs++;
+
+	if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) {
+		if (psmouse->num_resyncs < 3 || psmouse->acks_disable_command)
+			failed = true;
+	} else
+		psmouse->acks_disable_command = true;
+
+/*
+ * Poll the mouse. If it was reset the packet will be shorter than
+ * psmouse->pktsize and ps2_command will fail. We do not expect and
+ * do not handle scenario when mouse "upgrades" its protocol while
+ * disconnected since it would require additional delay. If we ever
+ * see a mouse that does it we'll adjust the code.
+ */
+	if (!failed) {
+		if (psmouse->poll(psmouse))
+			failed = true;
+		else {
+			psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+			for (i = 0; i < psmouse->pktsize; i++) {
+				psmouse->pktcnt++;
+				rc = psmouse->protocol_handler(psmouse);
+				if (rc != PSMOUSE_GOOD_DATA)
+					break;
+			}
+			if (rc != PSMOUSE_FULL_PACKET)
+				failed = true;
+			psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
+		}
+	}
+/*
+ * Now try to enable mouse. We try to do that even if poll failed and also
+ * repeat our attempts 5 times, otherwise we may be left out with disabled
+ * mouse.
+ */
+	for (i = 0; i < 5; i++) {
+		if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
+			enabled = true;
+			break;
+		}
+		msleep(200);
+	}
+
+	if (!enabled) {
+		psmouse_warn(psmouse, "failed to re-enable mouse on %s\n",
+			     psmouse->ps2dev.serio->phys);
+		failed = true;
+	}
+
+	if (failed) {
+		psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+		psmouse_info(psmouse,
+			     "resync failed, issuing reconnect request\n");
+		serio_reconnect(serio);
+	} else
+		psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+
+	if (parent)
+		psmouse_activate(parent);
+ out:
+	mutex_unlock(&psmouse_mutex);
+}
+
+/*
+ * psmouse_cleanup() resets the mouse into power-on state.
+ */
+
+static void psmouse_cleanup(struct serio *serio)
+{
+	struct psmouse *psmouse = serio_get_drvdata(serio);
+	struct psmouse *parent = NULL;
+
+	mutex_lock(&psmouse_mutex);
+
+	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+		parent = serio_get_drvdata(serio->parent);
+		psmouse_deactivate(parent);
+	}
+
+	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+	/*
+	 * Disable stream mode so cleanup routine can proceed undisturbed.
+	 */
+	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE))
+		psmouse_warn(psmouse, "Failed to disable mouse on %s\n",
+			     psmouse->ps2dev.serio->phys);
+
+	if (psmouse->cleanup)
+		psmouse->cleanup(psmouse);
+
+/*
+ * Reset the mouse to defaults (bare PS/2 protocol).
+ */
+	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+
+/*
+ * Some boxes, such as HP nx7400, get terribly confused if mouse
+ * is not fully enabled before suspending/shutting down.
+ */
+	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+	if (parent) {
+		if (parent->pt_deactivate)
+			parent->pt_deactivate(parent);
+
+		psmouse_activate(parent);
+	}
+
+	mutex_unlock(&psmouse_mutex);
+}
+
+/*
+ * psmouse_disconnect() closes and frees.
+ */
+
+static void psmouse_disconnect(struct serio *serio)
+{
+	struct psmouse *psmouse, *parent = NULL;
+
+	psmouse = serio_get_drvdata(serio);
+
+	sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group);
+
+	mutex_lock(&psmouse_mutex);
+
+	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+	/* make sure we don't have a resync in progress */
+	mutex_unlock(&psmouse_mutex);
+	flush_workqueue(kpsmoused_wq);
+	mutex_lock(&psmouse_mutex);
+
+	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+		parent = serio_get_drvdata(serio->parent);
+		psmouse_deactivate(parent);
+	}
+
+	if (psmouse->disconnect)
+		psmouse->disconnect(psmouse);
+
+	if (parent && parent->pt_deactivate)
+		parent->pt_deactivate(parent);
+
+	psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(psmouse->dev);
+	kfree(psmouse);
+
+	if (parent)
+		psmouse_activate(parent);
+
+	mutex_unlock(&psmouse_mutex);
+}
+
+static int psmouse_switch_protocol(struct psmouse *psmouse,
+				   const struct psmouse_protocol *proto)
+{
+	const struct psmouse_protocol *selected_proto;
+	struct input_dev *input_dev = psmouse->dev;
+
+	input_dev->dev.parent = &psmouse->ps2dev.serio->dev;
+
+	if (proto && (proto->detect || proto->init)) {
+		psmouse_apply_defaults(psmouse);
+
+		if (proto->detect && proto->detect(psmouse, true) < 0)
+			return -1;
+
+		if (proto->init && proto->init(psmouse) < 0)
+			return -1;
+
+		psmouse->type = proto->type;
+		selected_proto = proto;
+	} else {
+		psmouse->type = psmouse_extensions(psmouse,
+						   psmouse_max_proto, true);
+		selected_proto = psmouse_protocol_by_type(psmouse->type);
+	}
+
+	psmouse->ignore_parity = selected_proto->ignore_parity;
+
+	/*
+	 * If mouse's packet size is 3 there is no point in polling the
+	 * device in hopes to detect protocol reset - we won't get less
+	 * than 3 bytes response anyhow.
+	 */
+	if (psmouse->pktsize == 3)
+		psmouse->resync_time = 0;
+
+	/*
+	 * Some smart KVMs fake response to POLL command returning just
+	 * 3 bytes and messing up our resync logic, so if initial poll
+	 * fails we won't try polling the device anymore. Hopefully
+	 * such KVM will maintain initially selected protocol.
+	 */
+	if (psmouse->resync_time && psmouse->poll(psmouse))
+		psmouse->resync_time = 0;
+
+	snprintf(psmouse->devname, sizeof(psmouse->devname), "%s %s %s",
+		 selected_proto->name, psmouse->vendor, psmouse->name);
+
+	input_dev->name = psmouse->devname;
+	input_dev->phys = psmouse->phys;
+	input_dev->id.bustype = BUS_I8042;
+	input_dev->id.vendor = 0x0002;
+	input_dev->id.product = psmouse->type;
+	input_dev->id.version = psmouse->model;
+
+	return 0;
+}
+
+/*
+ * psmouse_connect() is a callback from the serio module when
+ * an unhandled serio port is found.
+ */
+static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct psmouse *psmouse, *parent = NULL;
+	struct input_dev *input_dev;
+	int retval = 0, error = -ENOMEM;
+
+	mutex_lock(&psmouse_mutex);
+
+	/*
+	 * If this is a pass-through port deactivate parent so the device
+	 * connected to this port can be successfully identified
+	 */
+	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+		parent = serio_get_drvdata(serio->parent);
+		psmouse_deactivate(parent);
+	}
+
+	psmouse = kzalloc(sizeof(struct psmouse), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!psmouse || !input_dev)
+		goto err_free;
+
+	ps2_init(&psmouse->ps2dev, serio);
+	INIT_DELAYED_WORK(&psmouse->resync_work, psmouse_resync);
+	psmouse->dev = input_dev;
+	snprintf(psmouse->phys, sizeof(psmouse->phys), "%s/input0", serio->phys);
+
+	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+	serio_set_drvdata(serio, psmouse);
+
+	error = serio_open(serio, drv);
+	if (error)
+		goto err_clear_drvdata;
+
+	/* give PT device some time to settle down before probing */
+	if (serio->id.type == SERIO_PS_PSTHRU)
+		usleep_range(10000, 15000);
+
+	if (psmouse_probe(psmouse) < 0) {
+		error = -ENODEV;
+		goto err_close_serio;
+	}
+
+	psmouse->rate = psmouse_rate;
+	psmouse->resolution = psmouse_resolution;
+	psmouse->resetafter = psmouse_resetafter;
+	psmouse->resync_time = parent ? 0 : psmouse_resync_time;
+	psmouse->smartscroll = psmouse_smartscroll;
+
+	psmouse_switch_protocol(psmouse, NULL);
+
+	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+	psmouse_initialize(psmouse);
+
+	error = input_register_device(psmouse->dev);
+	if (error)
+		goto err_protocol_disconnect;
+
+	if (parent && parent->pt_activate)
+		parent->pt_activate(parent);
+
+	error = sysfs_create_group(&serio->dev.kobj, &psmouse_attribute_group);
+	if (error)
+		goto err_pt_deactivate;
+
+	psmouse_activate(psmouse);
+
+ out:
+	/* If this is a pass-through port the parent needs to be re-activated */
+	if (parent)
+		psmouse_activate(parent);
+
+	mutex_unlock(&psmouse_mutex);
+	return retval;
+
+ err_pt_deactivate:
+	if (parent && parent->pt_deactivate)
+		parent->pt_deactivate(parent);
+	input_unregister_device(psmouse->dev);
+	input_dev = NULL; /* so we don't try to free it below */
+ err_protocol_disconnect:
+	if (psmouse->disconnect)
+		psmouse->disconnect(psmouse);
+	psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+ err_close_serio:
+	serio_close(serio);
+ err_clear_drvdata:
+	serio_set_drvdata(serio, NULL);
+ err_free:
+	input_free_device(input_dev);
+	kfree(psmouse);
+
+	retval = error;
+	goto out;
+}
+
+
+static int psmouse_reconnect(struct serio *serio)
+{
+	struct psmouse *psmouse = serio_get_drvdata(serio);
+	struct psmouse *parent = NULL;
+	unsigned char type;
+	int rc = -1;
+
+	mutex_lock(&psmouse_mutex);
+
+	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+		parent = serio_get_drvdata(serio->parent);
+		psmouse_deactivate(parent);
+	}
+
+	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+	if (psmouse->reconnect) {
+		if (psmouse->reconnect(psmouse))
+			goto out;
+	} else {
+		psmouse_reset(psmouse);
+
+		if (psmouse_probe(psmouse) < 0)
+			goto out;
+
+		type = psmouse_extensions(psmouse, psmouse_max_proto, false);
+		if (psmouse->type != type)
+			goto out;
+	}
+
+	/*
+	 * OK, the device type (and capabilities) match the old one,
+	 * we can continue using it, complete initialization
+	 */
+	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+	psmouse_initialize(psmouse);
+
+	if (parent && parent->pt_activate)
+		parent->pt_activate(parent);
+
+	psmouse_activate(psmouse);
+	rc = 0;
+
+out:
+	/* If this is a pass-through port the parent waits to be activated */
+	if (parent)
+		psmouse_activate(parent);
+
+	mutex_unlock(&psmouse_mutex);
+	return rc;
+}
+
+static struct serio_device_id psmouse_serio_ids[] = {
+	{
+		.type	= SERIO_8042,
+		.proto	= SERIO_ANY,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_PS_PSTHRU,
+		.proto	= SERIO_ANY,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, psmouse_serio_ids);
+
+static struct serio_driver psmouse_drv = {
+	.driver		= {
+		.name	= "psmouse",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= psmouse_serio_ids,
+	.interrupt	= psmouse_interrupt,
+	.connect	= psmouse_connect,
+	.reconnect	= psmouse_reconnect,
+	.disconnect	= psmouse_disconnect,
+	.cleanup	= psmouse_cleanup,
+};
+
+ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *devattr,
+				 char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	struct psmouse_attribute *attr = to_psmouse_attr(devattr);
+	struct psmouse *psmouse;
+
+	psmouse = serio_get_drvdata(serio);
+
+	return attr->show(psmouse, attr->data, buf);
+}
+
+ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *devattr,
+				const char *buf, size_t count)
+{
+	struct serio *serio = to_serio_port(dev);
+	struct psmouse_attribute *attr = to_psmouse_attr(devattr);
+	struct psmouse *psmouse, *parent = NULL;
+	int retval;
+
+	retval = mutex_lock_interruptible(&psmouse_mutex);
+	if (retval)
+		goto out;
+
+	psmouse = serio_get_drvdata(serio);
+
+	if (attr->protect) {
+		if (psmouse->state == PSMOUSE_IGNORE) {
+			retval = -ENODEV;
+			goto out_unlock;
+		}
+
+		if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+			parent = serio_get_drvdata(serio->parent);
+			psmouse_deactivate(parent);
+		}
+
+		psmouse_deactivate(psmouse);
+	}
+
+	retval = attr->set(psmouse, attr->data, buf, count);
+
+	if (attr->protect) {
+		if (retval != -ENODEV)
+			psmouse_activate(psmouse);
+
+		if (parent)
+			psmouse_activate(parent);
+	}
+
+ out_unlock:
+	mutex_unlock(&psmouse_mutex);
+ out:
+	return retval;
+}
+
+static ssize_t psmouse_show_int_attr(struct psmouse *psmouse, void *offset, char *buf)
+{
+	unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset);
+
+	return sprintf(buf, "%u\n", *field);
+}
+
+static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const char *buf, size_t count)
+{
+	unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset);
+	unsigned int value;
+	int err;
+
+	err = kstrtouint(buf, 10, &value);
+	if (err)
+		return err;
+
+	*field = value;
+
+	return count;
+}
+
+static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, void *data, char *buf)
+{
+	return sprintf(buf, "%s\n", psmouse_protocol_by_type(psmouse->type)->name);
+}
+
+static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, const char *buf, size_t count)
+{
+	struct serio *serio = psmouse->ps2dev.serio;
+	struct psmouse *parent = NULL;
+	struct input_dev *old_dev, *new_dev;
+	const struct psmouse_protocol *proto, *old_proto;
+	int error;
+	int retry = 0;
+
+	proto = psmouse_protocol_by_name(buf, count);
+	if (!proto)
+		return -EINVAL;
+
+	if (psmouse->type == proto->type)
+		return count;
+
+	new_dev = input_allocate_device();
+	if (!new_dev)
+		return -ENOMEM;
+
+	while (!list_empty(&serio->children)) {
+		if (++retry > 3) {
+			psmouse_warn(psmouse,
+				     "failed to destroy children ports, protocol change aborted.\n");
+			input_free_device(new_dev);
+			return -EIO;
+		}
+
+		mutex_unlock(&psmouse_mutex);
+		serio_unregister_child_port(serio);
+		mutex_lock(&psmouse_mutex);
+
+		if (serio->drv != &psmouse_drv) {
+			input_free_device(new_dev);
+			return -ENODEV;
+		}
+
+		if (psmouse->type == proto->type) {
+			input_free_device(new_dev);
+			return count; /* switched by other thread */
+		}
+	}
+
+	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+		parent = serio_get_drvdata(serio->parent);
+		if (parent->pt_deactivate)
+			parent->pt_deactivate(parent);
+	}
+
+	old_dev = psmouse->dev;
+	old_proto = psmouse_protocol_by_type(psmouse->type);
+
+	if (psmouse->disconnect)
+		psmouse->disconnect(psmouse);
+
+	psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+
+	psmouse->dev = new_dev;
+	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+	if (psmouse_switch_protocol(psmouse, proto) < 0) {
+		psmouse_reset(psmouse);
+		/* default to PSMOUSE_PS2 */
+		psmouse_switch_protocol(psmouse, &psmouse_protocols[0]);
+	}
+
+	psmouse_initialize(psmouse);
+	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+	error = input_register_device(psmouse->dev);
+	if (error) {
+		if (psmouse->disconnect)
+			psmouse->disconnect(psmouse);
+
+		psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+		input_free_device(new_dev);
+		psmouse->dev = old_dev;
+		psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+		psmouse_switch_protocol(psmouse, old_proto);
+		psmouse_initialize(psmouse);
+		psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+		return error;
+	}
+
+	input_unregister_device(old_dev);
+
+	if (parent && parent->pt_activate)
+		parent->pt_activate(parent);
+
+	return count;
+}
+
+static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const char *buf, size_t count)
+{
+	unsigned int value;
+	int err;
+
+	err = kstrtouint(buf, 10, &value);
+	if (err)
+		return err;
+
+	psmouse->set_rate(psmouse, value);
+	return count;
+}
+
+static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, const char *buf, size_t count)
+{
+	unsigned int value;
+	int err;
+
+	err = kstrtouint(buf, 10, &value);
+	if (err)
+		return err;
+
+	psmouse->set_resolution(psmouse, value);
+	return count;
+}
+
+
+static int psmouse_set_maxproto(const char *val, const struct kernel_param *kp)
+{
+	const struct psmouse_protocol *proto;
+
+	if (!val)
+		return -EINVAL;
+
+	proto = psmouse_protocol_by_name(val, strlen(val));
+
+	if (!proto || !proto->maxproto)
+		return -EINVAL;
+
+	*((unsigned int *)kp->arg) = proto->type;
+
+	return 0;
+}
+
+static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp)
+{
+	int type = *((unsigned int *)kp->arg);
+
+	return sprintf(buffer, "%s", psmouse_protocol_by_type(type)->name);
+}
+
+static int __init psmouse_init(void)
+{
+	int err;
+
+	lifebook_module_init();
+	synaptics_module_init();
+	hgpk_module_init();
+
+	kpsmoused_wq = create_singlethread_workqueue("kpsmoused");
+	if (!kpsmoused_wq) {
+		pr_err("failed to create kpsmoused workqueue\n");
+		return -ENOMEM;
+	}
+
+	err = serio_register_driver(&psmouse_drv);
+	if (err)
+		destroy_workqueue(kpsmoused_wq);
+
+	return err;
+}
+
+static void __exit psmouse_exit(void)
+{
+	serio_unregister_driver(&psmouse_drv);
+	destroy_workqueue(kpsmoused_wq);
+}
+
+module_init(psmouse_init);
+module_exit(psmouse_exit);
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
new file mode 100644
index 0000000..ad5a5a1
--- /dev/null
+++ b/drivers/input/mouse/psmouse.h
@@ -0,0 +1,193 @@
+#ifndef _PSMOUSE_H
+#define _PSMOUSE_H
+
+#define PSMOUSE_CMD_SETSCALE11	0x00e6
+#define PSMOUSE_CMD_SETSCALE21	0x00e7
+#define PSMOUSE_CMD_SETRES	0x10e8
+#define PSMOUSE_CMD_GETINFO	0x03e9
+#define PSMOUSE_CMD_SETSTREAM	0x00ea
+#define PSMOUSE_CMD_SETPOLL	0x00f0
+#define PSMOUSE_CMD_POLL	0x00eb	/* caller sets number of bytes to receive */
+#define PSMOUSE_CMD_RESET_WRAP	0x00ec
+#define PSMOUSE_CMD_GETID	0x02f2
+#define PSMOUSE_CMD_SETRATE	0x10f3
+#define PSMOUSE_CMD_ENABLE	0x00f4
+#define PSMOUSE_CMD_DISABLE	0x00f5
+#define PSMOUSE_CMD_RESET_DIS	0x00f6
+#define PSMOUSE_CMD_RESET_BAT	0x02ff
+
+#define PSMOUSE_RET_BAT		0xaa
+#define PSMOUSE_RET_ID		0x00
+#define PSMOUSE_RET_ACK		0xfa
+#define PSMOUSE_RET_NAK		0xfe
+
+enum psmouse_state {
+	PSMOUSE_IGNORE,
+	PSMOUSE_INITIALIZING,
+	PSMOUSE_RESYNCING,
+	PSMOUSE_CMD_MODE,
+	PSMOUSE_ACTIVATED,
+};
+
+/* psmouse protocol handler return codes */
+typedef enum {
+	PSMOUSE_BAD_DATA,
+	PSMOUSE_GOOD_DATA,
+	PSMOUSE_FULL_PACKET
+} psmouse_ret_t;
+
+enum psmouse_scale {
+	PSMOUSE_SCALE11,
+	PSMOUSE_SCALE21
+};
+
+struct psmouse {
+	void *private;
+	struct input_dev *dev;
+	struct ps2dev ps2dev;
+	struct delayed_work resync_work;
+	char *vendor;
+	char *name;
+	unsigned char packet[8];
+	unsigned char badbyte;
+	unsigned char pktcnt;
+	unsigned char pktsize;
+	unsigned char type;
+	bool ignore_parity;
+	bool acks_disable_command;
+	unsigned int model;
+	unsigned long last;
+	unsigned long out_of_sync_cnt;
+	unsigned long num_resyncs;
+	enum psmouse_state state;
+	char devname[64];
+	char phys[32];
+
+	unsigned int rate;
+	unsigned int resolution;
+	unsigned int resetafter;
+	unsigned int resync_time;
+	bool smartscroll;	/* Logitech only */
+
+	psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse);
+	void (*set_rate)(struct psmouse *psmouse, unsigned int rate);
+	void (*set_resolution)(struct psmouse *psmouse, unsigned int resolution);
+	void (*set_scale)(struct psmouse *psmouse, enum psmouse_scale scale);
+
+	int (*reconnect)(struct psmouse *psmouse);
+	void (*disconnect)(struct psmouse *psmouse);
+	void (*cleanup)(struct psmouse *psmouse);
+	int (*poll)(struct psmouse *psmouse);
+
+	void (*pt_activate)(struct psmouse *psmouse);
+	void (*pt_deactivate)(struct psmouse *psmouse);
+};
+
+enum psmouse_type {
+	PSMOUSE_NONE,
+	PSMOUSE_PS2,
+	PSMOUSE_PS2PP,
+	PSMOUSE_THINKPS,
+	PSMOUSE_GENPS,
+	PSMOUSE_IMPS,
+	PSMOUSE_IMEX,
+	PSMOUSE_SYNAPTICS,
+	PSMOUSE_ALPS,
+	PSMOUSE_LIFEBOOK,
+	PSMOUSE_TRACKPOINT,
+	PSMOUSE_TOUCHKIT_PS2,
+	PSMOUSE_CORTRON,
+	PSMOUSE_HGPK,
+	PSMOUSE_ELANTECH,
+	PSMOUSE_FSP,
+	PSMOUSE_SYNAPTICS_RELATIVE,
+	PSMOUSE_CYPRESS,
+	PSMOUSE_FOCALTECH,
+	PSMOUSE_VMMOUSE,
+	PSMOUSE_AUTO		/* This one should always be last */
+};
+
+void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
+		unsigned long delay);
+int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
+int psmouse_reset(struct psmouse *psmouse);
+void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state);
+void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution);
+psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse);
+int psmouse_activate(struct psmouse *psmouse);
+int psmouse_deactivate(struct psmouse *psmouse);
+bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]);
+
+struct psmouse_attribute {
+	struct device_attribute dattr;
+	void *data;
+	ssize_t (*show)(struct psmouse *psmouse, void *data, char *buf);
+	ssize_t (*set)(struct psmouse *psmouse, void *data,
+			const char *buf, size_t count);
+	bool protect;
+};
+#define to_psmouse_attr(a)	container_of((a), struct psmouse_attribute, dattr)
+
+ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *attr,
+				 char *buf);
+ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t count);
+
+#define __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, _set, _protect)	\
+static struct psmouse_attribute psmouse_attr_##_name = {			\
+	.dattr	= {								\
+		.attr	= {							\
+			.name	= __stringify(_name),				\
+			.mode	= _mode,					\
+		},								\
+		.show	= psmouse_attr_show_helper,				\
+		.store	= psmouse_attr_set_helper,				\
+	},									\
+	.data	= _data,							\
+	.show	= _show,							\
+	.set	= _set,								\
+	.protect = _protect,							\
+}
+
+#define __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, _protect)	\
+	static ssize_t _show(struct psmouse *, void *, char *);			\
+	static ssize_t _set(struct psmouse *, void *, const char *, size_t);	\
+	__PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, _set, _protect)
+
+#define PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set)			\
+	__PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, true)
+
+#define PSMOUSE_DEFINE_RO_ATTR(_name, _mode, _data, _show)			\
+	static ssize_t _show(struct psmouse *, void *, char *);			\
+	__PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, NULL, true)
+
+#define PSMOUSE_DEFINE_WO_ATTR(_name, _mode, _data, _set)			\
+	static ssize_t _set(struct psmouse *, void *, const char *, size_t);	\
+	__PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, NULL, _set, true)
+
+#ifndef psmouse_fmt
+#define psmouse_fmt(fmt)	KBUILD_BASENAME ": " fmt
+#endif
+
+#define psmouse_dbg(psmouse, format, ...)		\
+	dev_dbg(&(psmouse)->ps2dev.serio->dev,		\
+		psmouse_fmt(format), ##__VA_ARGS__)
+#define psmouse_info(psmouse, format, ...)		\
+	dev_info(&(psmouse)->ps2dev.serio->dev,		\
+		 psmouse_fmt(format), ##__VA_ARGS__)
+#define psmouse_warn(psmouse, format, ...)		\
+	dev_warn(&(psmouse)->ps2dev.serio->dev,		\
+		 psmouse_fmt(format), ##__VA_ARGS__)
+#define psmouse_err(psmouse, format, ...)		\
+	dev_err(&(psmouse)->ps2dev.serio->dev,		\
+		psmouse_fmt(format), ##__VA_ARGS__)
+#define psmouse_notice(psmouse, format, ...)		\
+	dev_notice(&(psmouse)->ps2dev.serio->dev,	\
+		   psmouse_fmt(format), ##__VA_ARGS__)
+#define psmouse_printk(level, psmouse, format, ...)	\
+	dev_printk(level,				\
+		   &(psmouse)->ps2dev.serio->dev,	\
+		   psmouse_fmt(format), ##__VA_ARGS__)
+
+
+#endif /* _PSMOUSE_H */
diff --git a/drivers/input/mouse/pxa930_trkball.c b/drivers/input/mouse/pxa930_trkball.c
new file mode 100644
index 0000000..9b4d9a5
--- /dev/null
+++ b/drivers/input/mouse/pxa930_trkball.c
@@ -0,0 +1,256 @@
+/*
+ * PXA930 track ball mouse driver
+ *
+ * Copyright (C) 2007 Marvell International Ltd.
+ * 2008-02-28: Yong Yao <yaoyong@marvell.com>
+ *             initial version
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+#include <linux/platform_data/mouse-pxa930_trkball.h>
+
+/* Trackball Controller Register Definitions */
+#define TBCR		(0x000C)
+#define TBCNTR		(0x0010)
+#define TBSBC		(0x0014)
+
+#define TBCR_TBRST	(1 << 1)
+#define TBCR_TBSB	(1 << 10)
+
+#define TBCR_Y_FLT(n)	(((n) & 0xf) << 6)
+#define TBCR_X_FLT(n)	(((n) & 0xf) << 2)
+
+#define TBCNTR_YM(n)	(((n) >> 24) & 0xff)
+#define TBCNTR_YP(n)	(((n) >> 16) & 0xff)
+#define TBCNTR_XM(n)	(((n) >> 8) & 0xff)
+#define TBCNTR_XP(n)	((n) & 0xff)
+
+#define TBSBC_TBSBC	(0x1)
+
+struct pxa930_trkball {
+	struct pxa930_trkball_platform_data *pdata;
+
+	/* Memory Mapped Register */
+	struct resource *mem;
+	void __iomem *mmio_base;
+
+	struct input_dev *input;
+};
+
+static irqreturn_t pxa930_trkball_interrupt(int irq, void *dev_id)
+{
+	struct pxa930_trkball *trkball = dev_id;
+	struct input_dev *input = trkball->input;
+	int tbcntr, x, y;
+
+	/* According to the spec software must read TBCNTR twice:
+	 * if the read value is the same, the reading is valid
+	 */
+	tbcntr = __raw_readl(trkball->mmio_base + TBCNTR);
+
+	if (tbcntr == __raw_readl(trkball->mmio_base + TBCNTR)) {
+		x = (TBCNTR_XP(tbcntr) - TBCNTR_XM(tbcntr)) / 2;
+		y = (TBCNTR_YP(tbcntr) - TBCNTR_YM(tbcntr)) / 2;
+
+		input_report_rel(input, REL_X, x);
+		input_report_rel(input, REL_Y, y);
+		input_sync(input);
+	}
+
+	__raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC);
+	__raw_writel(0, trkball->mmio_base + TBSBC);
+
+	return IRQ_HANDLED;
+}
+
+/* For TBCR, we need to wait for a while to make sure it has been modified. */
+static int write_tbcr(struct pxa930_trkball *trkball, int v)
+{
+	int i = 100;
+
+	__raw_writel(v, trkball->mmio_base + TBCR);
+
+	while (--i) {
+		if (__raw_readl(trkball->mmio_base + TBCR) == v)
+			break;
+		msleep(1);
+	}
+
+	if (i == 0) {
+		pr_err("%s: timed out writing TBCR(%x)!\n", __func__, v);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static void pxa930_trkball_config(struct pxa930_trkball *trkball)
+{
+	uint32_t tbcr;
+
+	/* According to spec, need to write the filters of x,y to 0xf first! */
+	tbcr = __raw_readl(trkball->mmio_base + TBCR);
+	write_tbcr(trkball, tbcr | TBCR_X_FLT(0xf) | TBCR_Y_FLT(0xf));
+	write_tbcr(trkball, TBCR_X_FLT(trkball->pdata->x_filter) |
+			    TBCR_Y_FLT(trkball->pdata->y_filter));
+
+	/* According to spec, set TBCR_TBRST first, before clearing it! */
+	tbcr = __raw_readl(trkball->mmio_base + TBCR);
+	write_tbcr(trkball, tbcr | TBCR_TBRST);
+	write_tbcr(trkball, tbcr & ~TBCR_TBRST);
+
+	__raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC);
+	__raw_writel(0, trkball->mmio_base + TBSBC);
+
+	pr_debug("%s: final TBCR=%x!\n", __func__,
+		 __raw_readl(trkball->mmio_base + TBCR));
+}
+
+static int pxa930_trkball_open(struct input_dev *dev)
+{
+	struct pxa930_trkball *trkball = input_get_drvdata(dev);
+
+	pxa930_trkball_config(trkball);
+
+	return 0;
+}
+
+static void pxa930_trkball_disable(struct pxa930_trkball *trkball)
+{
+	uint32_t tbcr = __raw_readl(trkball->mmio_base + TBCR);
+
+	/* Held in reset, gate the 32-KHz input clock off */
+	write_tbcr(trkball, tbcr | TBCR_TBRST);
+}
+
+static void pxa930_trkball_close(struct input_dev *dev)
+{
+	struct pxa930_trkball *trkball = input_get_drvdata(dev);
+
+	pxa930_trkball_disable(trkball);
+}
+
+static int pxa930_trkball_probe(struct platform_device *pdev)
+{
+	struct pxa930_trkball *trkball;
+	struct input_dev *input;
+	struct resource *res;
+	int irq, error;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to get trkball irq\n");
+		return -ENXIO;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get register memory\n");
+		return -ENXIO;
+	}
+
+	trkball = kzalloc(sizeof(struct pxa930_trkball), GFP_KERNEL);
+	if (!trkball)
+		return -ENOMEM;
+
+	trkball->pdata = dev_get_platdata(&pdev->dev);
+	if (!trkball->pdata) {
+		dev_err(&pdev->dev, "no platform data defined\n");
+		error = -EINVAL;
+		goto failed;
+	}
+
+	trkball->mmio_base = ioremap_nocache(res->start, resource_size(res));
+	if (!trkball->mmio_base) {
+		dev_err(&pdev->dev, "failed to ioremap registers\n");
+		error = -ENXIO;
+		goto failed;
+	}
+
+	/* held the module in reset, will be enabled in open() */
+	pxa930_trkball_disable(trkball);
+
+	error = request_irq(irq, pxa930_trkball_interrupt, 0,
+			    pdev->name, trkball);
+	if (error) {
+		dev_err(&pdev->dev, "failed to request irq: %d\n", error);
+		goto failed_free_io;
+	}
+
+	platform_set_drvdata(pdev, trkball);
+
+	input = input_allocate_device();
+	if (!input) {
+		dev_err(&pdev->dev, "failed to allocate input device\n");
+		error = -ENOMEM;
+		goto failed_free_irq;
+	}
+
+	input->name = pdev->name;
+	input->id.bustype = BUS_HOST;
+	input->open = pxa930_trkball_open;
+	input->close = pxa930_trkball_close;
+	input->dev.parent = &pdev->dev;
+	input_set_drvdata(input, trkball);
+
+	trkball->input = input;
+
+	input_set_capability(input, EV_REL, REL_X);
+	input_set_capability(input, EV_REL, REL_Y);
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(&pdev->dev, "unable to register input device\n");
+		goto failed_free_input;
+	}
+
+	return 0;
+
+failed_free_input:
+	input_free_device(input);
+failed_free_irq:
+	free_irq(irq, trkball);
+failed_free_io:
+	iounmap(trkball->mmio_base);
+failed:
+	kfree(trkball);
+	return error;
+}
+
+static int pxa930_trkball_remove(struct platform_device *pdev)
+{
+	struct pxa930_trkball *trkball = platform_get_drvdata(pdev);
+	int irq = platform_get_irq(pdev, 0);
+
+	input_unregister_device(trkball->input);
+	free_irq(irq, trkball);
+	iounmap(trkball->mmio_base);
+	kfree(trkball);
+
+	return 0;
+}
+
+static struct platform_driver pxa930_trkball_driver = {
+	.driver		= {
+		.name	= "pxa930-trkball",
+	},
+	.probe		= pxa930_trkball_probe,
+	.remove		= pxa930_trkball_remove,
+};
+module_platform_driver(pxa930_trkball_driver);
+
+MODULE_AUTHOR("Yong Yao <yaoyong@marvell.com>");
+MODULE_DESCRIPTION("PXA930 Trackball Mouse Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/mouse/rpcmouse.c b/drivers/input/mouse/rpcmouse.c
new file mode 100644
index 0000000..21c60fe
--- /dev/null
+++ b/drivers/input/mouse/rpcmouse.c
@@ -0,0 +1,116 @@
+/*
+ *  Acorn RiscPC mouse driver for Linux/ARM
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik
+ *  Copyright (C) 1996-2002 Russell King
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This handles the Acorn RiscPCs mouse.  We basically have a couple of
+ * hardware registers that track the sensor count for the X-Y movement and
+ * another register holding the button state.  On every VSYNC interrupt we read
+ * the complete state and then work out if something has changed.
+ */
+
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/hardware/iomd.h>
+
+MODULE_AUTHOR("Vojtech Pavlik, Russell King");
+MODULE_DESCRIPTION("Acorn RiscPC mouse driver");
+MODULE_LICENSE("GPL");
+
+static short rpcmouse_lastx, rpcmouse_lasty;
+static struct input_dev *rpcmouse_dev;
+
+static irqreturn_t rpcmouse_irq(int irq, void *dev_id)
+{
+	struct input_dev *dev = dev_id;
+	short x, y, dx, dy, b;
+
+	x = (short) iomd_readl(IOMD_MOUSEX);
+	y = (short) iomd_readl(IOMD_MOUSEY);
+	b = (short) (__raw_readl(IOMEM(0xe0310000)) ^ 0x70);
+
+	dx = x - rpcmouse_lastx;
+	dy = y - rpcmouse_lasty;
+
+	rpcmouse_lastx = x;
+	rpcmouse_lasty = y;
+
+	input_report_rel(dev, REL_X, dx);
+	input_report_rel(dev, REL_Y, -dy);
+
+	input_report_key(dev, BTN_LEFT,   b & 0x40);
+	input_report_key(dev, BTN_MIDDLE, b & 0x20);
+	input_report_key(dev, BTN_RIGHT,  b & 0x10);
+
+	input_sync(dev);
+
+	return IRQ_HANDLED;
+}
+
+
+static int __init rpcmouse_init(void)
+{
+	int err;
+
+	rpcmouse_dev = input_allocate_device();
+	if (!rpcmouse_dev)
+		return -ENOMEM;
+
+	rpcmouse_dev->name = "Acorn RiscPC Mouse";
+	rpcmouse_dev->phys = "rpcmouse/input0";
+	rpcmouse_dev->id.bustype = BUS_HOST;
+	rpcmouse_dev->id.vendor  = 0x0005;
+	rpcmouse_dev->id.product = 0x0001;
+	rpcmouse_dev->id.version = 0x0100;
+
+	rpcmouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+	rpcmouse_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+		BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+	rpcmouse_dev->relbit[0]	= BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+	rpcmouse_lastx = (short) iomd_readl(IOMD_MOUSEX);
+	rpcmouse_lasty = (short) iomd_readl(IOMD_MOUSEY);
+
+	if (request_irq(IRQ_VSYNCPULSE, rpcmouse_irq, IRQF_SHARED, "rpcmouse", rpcmouse_dev)) {
+		printk(KERN_ERR "rpcmouse: unable to allocate VSYNC interrupt\n");
+		err = -EBUSY;
+		goto err_free_dev;
+	}
+
+	err = input_register_device(rpcmouse_dev);
+	if (err)
+		goto err_free_irq;
+
+	return 0;
+
+ err_free_irq:
+	free_irq(IRQ_VSYNCPULSE, rpcmouse_dev);
+ err_free_dev:
+	input_free_device(rpcmouse_dev);
+
+	return err;
+}
+
+static void __exit rpcmouse_exit(void)
+{
+	free_irq(IRQ_VSYNCPULSE, rpcmouse_dev);
+	input_unregister_device(rpcmouse_dev);
+}
+
+module_init(rpcmouse_init);
+module_exit(rpcmouse_exit);
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
new file mode 100644
index 0000000..11c32ac
--- /dev/null
+++ b/drivers/input/mouse/sentelic.c
@@ -0,0 +1,1089 @@
+/*-
+ * Finger Sensing Pad PS/2 mouse driver.
+ *
+ * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
+ * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation.
+ *
+ *   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.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/ctype.h>
+#include <linux/libps2.h>
+#include <linux/serio.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+
+#include "psmouse.h"
+#include "sentelic.h"
+
+/*
+ * Timeout for FSP PS/2 command only (in milliseconds).
+ */
+#define	FSP_CMD_TIMEOUT		200
+#define	FSP_CMD_TIMEOUT2	30
+
+#define	GET_ABS_X(packet)	((packet[1] << 2) | ((packet[3] >> 2) & 0x03))
+#define	GET_ABS_Y(packet)	((packet[2] << 2) | (packet[3] & 0x03))
+
+/** Driver version. */
+static const char fsp_drv_ver[] = "1.1.0-K";
+
+/*
+ * Make sure that the value being sent to FSP will not conflict with
+ * possible sample rate values.
+ */
+static unsigned char fsp_test_swap_cmd(unsigned char reg_val)
+{
+	switch (reg_val) {
+	case 10: case 20: case 40: case 60: case 80: case 100: case 200:
+		/*
+		 * The requested value being sent to FSP matched to possible
+		 * sample rates, swap the given value such that the hardware
+		 * wouldn't get confused.
+		 */
+		return (reg_val >> 4) | (reg_val << 4);
+	default:
+		return reg_val;	/* swap isn't necessary */
+	}
+}
+
+/*
+ * Make sure that the value being sent to FSP will not conflict with certain
+ * commands.
+ */
+static unsigned char fsp_test_invert_cmd(unsigned char reg_val)
+{
+	switch (reg_val) {
+	case 0xe9: case 0xee: case 0xf2: case 0xff:
+		/*
+		 * The requested value being sent to FSP matched to certain
+		 * commands, inverse the given value such that the hardware
+		 * wouldn't get confused.
+		 */
+		return ~reg_val;
+	default:
+		return reg_val;	/* inversion isn't necessary */
+	}
+}
+
+static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[3];
+	unsigned char addr;
+	int rc = -1;
+
+	/*
+	 * We need to shut off the device and switch it into command
+	 * mode so we don't confuse our protocol handler. We don't need
+	 * to do that for writes because sysfs set helper does this for
+	 * us.
+	 */
+	psmouse_deactivate(psmouse);
+
+	ps2_begin_command(ps2dev);
+
+	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+		goto out;
+
+	/* should return 0xfe(request for resending) */
+	ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
+	/* should return 0xfc(failed) */
+	ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
+
+	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+		goto out;
+
+	if ((addr = fsp_test_invert_cmd(reg_addr)) != reg_addr) {
+		ps2_sendbyte(ps2dev, 0x68, FSP_CMD_TIMEOUT2);
+	} else if ((addr = fsp_test_swap_cmd(reg_addr)) != reg_addr) {
+		/* swapping is required */
+		ps2_sendbyte(ps2dev, 0xcc, FSP_CMD_TIMEOUT2);
+		/* expect 0xfe */
+	} else {
+		/* swapping isn't necessary */
+		ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
+		/* expect 0xfe */
+	}
+	/* should return 0xfc(failed) */
+	ps2_sendbyte(ps2dev, addr, FSP_CMD_TIMEOUT);
+
+	if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) < 0)
+		goto out;
+
+	*reg_val = param[2];
+	rc = 0;
+
+ out:
+	ps2_end_command(ps2dev);
+	psmouse_activate(psmouse);
+	psmouse_dbg(psmouse,
+		    "READ REG: 0x%02x is 0x%02x (rc = %d)\n",
+		    reg_addr, *reg_val, rc);
+	return rc;
+}
+
+static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char v;
+	int rc = -1;
+
+	ps2_begin_command(ps2dev);
+
+	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+		goto out;
+
+	if ((v = fsp_test_invert_cmd(reg_addr)) != reg_addr) {
+		/* inversion is required */
+		ps2_sendbyte(ps2dev, 0x74, FSP_CMD_TIMEOUT2);
+	} else {
+		if ((v = fsp_test_swap_cmd(reg_addr)) != reg_addr) {
+			/* swapping is required */
+			ps2_sendbyte(ps2dev, 0x77, FSP_CMD_TIMEOUT2);
+		} else {
+			/* swapping isn't necessary */
+			ps2_sendbyte(ps2dev, 0x55, FSP_CMD_TIMEOUT2);
+		}
+	}
+	/* write the register address in correct order */
+	ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);
+
+	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+		goto out;
+
+	if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {
+		/* inversion is required */
+		ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2);
+	} else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) {
+		/* swapping is required */
+		ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2);
+	} else {
+		/* swapping isn't necessary */
+		ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2);
+	}
+
+	/* write the register value in correct order */
+	ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);
+	rc = 0;
+
+ out:
+	ps2_end_command(ps2dev);
+	psmouse_dbg(psmouse,
+		    "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n",
+		    reg_addr, reg_val, rc);
+	return rc;
+}
+
+/* Enable register clock gating for writing certain registers */
+static int fsp_reg_write_enable(struct psmouse *psmouse, bool enable)
+{
+	int v, nv;
+
+	if (fsp_reg_read(psmouse, FSP_REG_SYSCTL1, &v) == -1)
+		return -1;
+
+	if (enable)
+		nv = v | FSP_BIT_EN_REG_CLK;
+	else
+		nv = v & ~FSP_BIT_EN_REG_CLK;
+
+	/* only write if necessary */
+	if (nv != v)
+		if (fsp_reg_write(psmouse, FSP_REG_SYSCTL1, nv) == -1)
+			return -1;
+
+	return 0;
+}
+
+static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[3];
+	int rc = -1;
+
+	psmouse_deactivate(psmouse);
+
+	ps2_begin_command(ps2dev);
+
+	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+		goto out;
+
+	ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
+	ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
+
+	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+		goto out;
+
+	ps2_sendbyte(ps2dev, 0x83, FSP_CMD_TIMEOUT2);
+	ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
+
+	/* get the returned result */
+	if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+		goto out;
+
+	*reg_val = param[2];
+	rc = 0;
+
+ out:
+	ps2_end_command(ps2dev);
+	psmouse_activate(psmouse);
+	psmouse_dbg(psmouse,
+		    "READ PAGE REG: 0x%02x (rc = %d)\n",
+		    *reg_val, rc);
+	return rc;
+}
+
+static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char v;
+	int rc = -1;
+
+	ps2_begin_command(ps2dev);
+
+	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+		goto out;
+
+	ps2_sendbyte(ps2dev, 0x38, FSP_CMD_TIMEOUT2);
+	ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
+
+	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+		goto out;
+
+	if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {
+		ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2);
+	} else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) {
+		/* swapping is required */
+		ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2);
+	} else {
+		/* swapping isn't necessary */
+		ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2);
+	}
+
+	ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);
+	rc = 0;
+
+ out:
+	ps2_end_command(ps2dev);
+	psmouse_dbg(psmouse,
+		    "WRITE PAGE REG: to 0x%02x (rc = %d)\n",
+		    reg_val, rc);
+	return rc;
+}
+
+static int fsp_get_version(struct psmouse *psmouse, int *version)
+{
+	if (fsp_reg_read(psmouse, FSP_REG_VERSION, version))
+		return -EIO;
+
+	return 0;
+}
+
+static int fsp_get_revision(struct psmouse *psmouse, int *rev)
+{
+	if (fsp_reg_read(psmouse, FSP_REG_REVISION, rev))
+		return -EIO;
+
+	return 0;
+}
+
+static int fsp_get_sn(struct psmouse *psmouse, int *sn)
+{
+	int v0, v1, v2;
+	int rc = -EIO;
+
+	/* production number since Cx is available at: 0x0b40 ~ 0x0b42 */
+	if (fsp_page_reg_write(psmouse, FSP_PAGE_0B))
+		goto out;
+	if (fsp_reg_read(psmouse, FSP_REG_SN0, &v0))
+		goto out;
+	if (fsp_reg_read(psmouse, FSP_REG_SN1, &v1))
+		goto out;
+	if (fsp_reg_read(psmouse, FSP_REG_SN2, &v2))
+		goto out;
+	*sn = (v0 << 16) | (v1 << 8) | v2;
+	rc = 0;
+out:
+	fsp_page_reg_write(psmouse, FSP_PAGE_DEFAULT);
+	return rc;
+}
+
+static int fsp_get_buttons(struct psmouse *psmouse, int *btn)
+{
+	static const int buttons[] = {
+		0x16, /* Left/Middle/Right/Forward/Backward & Scroll Up/Down */
+		0x06, /* Left/Middle/Right & Scroll Up/Down/Right/Left */
+		0x04, /* Left/Middle/Right & Scroll Up/Down */
+		0x02, /* Left/Middle/Right */
+	};
+	int val;
+
+	if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS, &val) == -1)
+		return -EIO;
+
+	*btn = buttons[(val & 0x30) >> 4];
+	return 0;
+}
+
+/* Enable on-pad command tag output */
+static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable)
+{
+	int v, nv;
+	int res = 0;
+
+	if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) {
+		psmouse_err(psmouse, "Unable get OPC state.\n");
+		return -EIO;
+	}
+
+	if (enable)
+		nv = v | FSP_BIT_EN_OPC_TAG;
+	else
+		nv = v & ~FSP_BIT_EN_OPC_TAG;
+
+	/* only write if necessary */
+	if (nv != v) {
+		fsp_reg_write_enable(psmouse, true);
+		res = fsp_reg_write(psmouse, FSP_REG_OPC_QDOWN, nv);
+		fsp_reg_write_enable(psmouse, false);
+	}
+
+	if (res != 0) {
+		psmouse_err(psmouse, "Unable to enable OPC tag.\n");
+		res = -EIO;
+	}
+
+	return res;
+}
+
+static int fsp_onpad_vscr(struct psmouse *psmouse, bool enable)
+{
+	struct fsp_data *pad = psmouse->private;
+	int val;
+
+	if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val))
+		return -EIO;
+
+	pad->vscroll = enable;
+
+	if (enable)
+		val |= (FSP_BIT_FIX_VSCR | FSP_BIT_ONPAD_ENABLE);
+	else
+		val &= ~FSP_BIT_FIX_VSCR;
+
+	if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val))
+		return -EIO;
+
+	return 0;
+}
+
+static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable)
+{
+	struct fsp_data *pad = psmouse->private;
+	int val, v2;
+
+	if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val))
+		return -EIO;
+
+	if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &v2))
+		return -EIO;
+
+	pad->hscroll = enable;
+
+	if (enable) {
+		val |= (FSP_BIT_FIX_HSCR | FSP_BIT_ONPAD_ENABLE);
+		v2 |= FSP_BIT_EN_MSID6;
+	} else {
+		val &= ~FSP_BIT_FIX_HSCR;
+		v2 &= ~(FSP_BIT_EN_MSID6 | FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8);
+	}
+
+	if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val))
+		return -EIO;
+
+	/* reconfigure horizontal scrolling packet output */
+	if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, v2))
+		return -EIO;
+
+	return 0;
+}
+
+/*
+ * Write device specific initial parameters.
+ *
+ * ex: 0xab 0xcd - write oxcd into register 0xab
+ */
+static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,
+				   const char *buf, size_t count)
+{
+	unsigned int reg, val;
+	char *rest;
+	ssize_t retval;
+
+	reg = simple_strtoul(buf, &rest, 16);
+	if (rest == buf || *rest != ' ' || reg > 0xff)
+		return -EINVAL;
+
+	retval = kstrtouint(rest + 1, 16, &val);
+	if (retval)
+		return retval;
+
+	if (val > 0xff)
+		return -EINVAL;
+
+	if (fsp_reg_write_enable(psmouse, true))
+		return -EIO;
+
+	retval = fsp_reg_write(psmouse, reg, val) < 0 ? -EIO : count;
+
+	fsp_reg_write_enable(psmouse, false);
+
+	return count;
+}
+
+PSMOUSE_DEFINE_WO_ATTR(setreg, S_IWUSR, NULL, fsp_attr_set_setreg);
+
+static ssize_t fsp_attr_show_getreg(struct psmouse *psmouse,
+					void *data, char *buf)
+{
+	struct fsp_data *pad = psmouse->private;
+
+	return sprintf(buf, "%02x%02x\n", pad->last_reg, pad->last_val);
+}
+
+/*
+ * Read a register from device.
+ *
+ * ex: 0xab -- read content from register 0xab
+ */
+static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data,
+					const char *buf, size_t count)
+{
+	struct fsp_data *pad = psmouse->private;
+	unsigned int reg, val;
+	int err;
+
+	err = kstrtouint(buf, 16, &reg);
+	if (err)
+		return err;
+
+	if (reg > 0xff)
+		return -EINVAL;
+
+	if (fsp_reg_read(psmouse, reg, &val))
+		return -EIO;
+
+	pad->last_reg = reg;
+	pad->last_val = val;
+
+	return count;
+}
+
+PSMOUSE_DEFINE_ATTR(getreg, S_IWUSR | S_IRUGO, NULL,
+			fsp_attr_show_getreg, fsp_attr_set_getreg);
+
+static ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse,
+					void *data, char *buf)
+{
+	int val = 0;
+
+	if (fsp_page_reg_read(psmouse, &val))
+		return -EIO;
+
+	return sprintf(buf, "%02x\n", val);
+}
+
+static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data,
+					const char *buf, size_t count)
+{
+	unsigned int val;
+	int err;
+
+	err = kstrtouint(buf, 16, &val);
+	if (err)
+		return err;
+
+	if (val > 0xff)
+		return -EINVAL;
+
+	if (fsp_page_reg_write(psmouse, val))
+		return -EIO;
+
+	return count;
+}
+
+PSMOUSE_DEFINE_ATTR(page, S_IWUSR | S_IRUGO, NULL,
+			fsp_attr_show_pagereg, fsp_attr_set_pagereg);
+
+static ssize_t fsp_attr_show_vscroll(struct psmouse *psmouse,
+					void *data, char *buf)
+{
+	struct fsp_data *pad = psmouse->private;
+
+	return sprintf(buf, "%d\n", pad->vscroll);
+}
+
+static ssize_t fsp_attr_set_vscroll(struct psmouse *psmouse, void *data,
+					const char *buf, size_t count)
+{
+	unsigned int val;
+	int err;
+
+	err = kstrtouint(buf, 10, &val);
+	if (err)
+		return err;
+
+	if (val > 1)
+		return -EINVAL;
+
+	fsp_onpad_vscr(psmouse, val);
+
+	return count;
+}
+
+PSMOUSE_DEFINE_ATTR(vscroll, S_IWUSR | S_IRUGO, NULL,
+			fsp_attr_show_vscroll, fsp_attr_set_vscroll);
+
+static ssize_t fsp_attr_show_hscroll(struct psmouse *psmouse,
+					void *data, char *buf)
+{
+	struct fsp_data *pad = psmouse->private;
+
+	return sprintf(buf, "%d\n", pad->hscroll);
+}
+
+static ssize_t fsp_attr_set_hscroll(struct psmouse *psmouse, void *data,
+					const char *buf, size_t count)
+{
+	unsigned int val;
+	int err;
+
+	err = kstrtouint(buf, 10, &val);
+	if (err)
+		return err;
+
+	if (val > 1)
+		return -EINVAL;
+
+	fsp_onpad_hscr(psmouse, val);
+
+	return count;
+}
+
+PSMOUSE_DEFINE_ATTR(hscroll, S_IWUSR | S_IRUGO, NULL,
+			fsp_attr_show_hscroll, fsp_attr_set_hscroll);
+
+static ssize_t fsp_attr_show_flags(struct psmouse *psmouse,
+					void *data, char *buf)
+{
+	struct fsp_data *pad = psmouse->private;
+
+	return sprintf(buf, "%c\n",
+			pad->flags & FSPDRV_FLAG_EN_OPC ? 'C' : 'c');
+}
+
+static ssize_t fsp_attr_set_flags(struct psmouse *psmouse, void *data,
+					const char *buf, size_t count)
+{
+	struct fsp_data *pad = psmouse->private;
+	size_t i;
+
+	for (i = 0; i < count; i++) {
+		switch (buf[i]) {
+		case 'C':
+			pad->flags |= FSPDRV_FLAG_EN_OPC;
+			break;
+		case 'c':
+			pad->flags &= ~FSPDRV_FLAG_EN_OPC;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	return count;
+}
+
+PSMOUSE_DEFINE_ATTR(flags, S_IWUSR | S_IRUGO, NULL,
+			fsp_attr_show_flags, fsp_attr_set_flags);
+
+static ssize_t fsp_attr_show_ver(struct psmouse *psmouse,
+					void *data, char *buf)
+{
+	return sprintf(buf, "Sentelic FSP kernel module %s\n", fsp_drv_ver);
+}
+
+PSMOUSE_DEFINE_RO_ATTR(ver, S_IRUGO, NULL, fsp_attr_show_ver);
+
+static struct attribute *fsp_attributes[] = {
+	&psmouse_attr_setreg.dattr.attr,
+	&psmouse_attr_getreg.dattr.attr,
+	&psmouse_attr_page.dattr.attr,
+	&psmouse_attr_vscroll.dattr.attr,
+	&psmouse_attr_hscroll.dattr.attr,
+	&psmouse_attr_flags.dattr.attr,
+	&psmouse_attr_ver.dattr.attr,
+	NULL
+};
+
+static struct attribute_group fsp_attribute_group = {
+	.attrs = fsp_attributes,
+};
+
+#ifdef	FSP_DEBUG
+static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[])
+{
+	static unsigned int ps2_packet_cnt;
+	static unsigned int ps2_last_second;
+	unsigned int jiffies_msec;
+	const char *packet_type = "UNKNOWN";
+	unsigned short abs_x = 0, abs_y = 0;
+
+	/* Interpret & dump the packet data. */
+	switch (packet[0] >> FSP_PKT_TYPE_SHIFT) {
+	case FSP_PKT_TYPE_ABS:
+		packet_type = "Absolute";
+		abs_x = GET_ABS_X(packet);
+		abs_y = GET_ABS_Y(packet);
+		break;
+	case FSP_PKT_TYPE_NORMAL:
+		packet_type = "Normal";
+		break;
+	case FSP_PKT_TYPE_NOTIFY:
+		packet_type = "Notify";
+		break;
+	case FSP_PKT_TYPE_NORMAL_OPC:
+		packet_type = "Normal-OPC";
+		break;
+	}
+
+	ps2_packet_cnt++;
+	jiffies_msec = jiffies_to_msecs(jiffies);
+	psmouse_dbg(psmouse,
+		    "%08dms %s packets: %02x, %02x, %02x, %02x; "
+		    "abs_x: %d, abs_y: %d\n",
+		    jiffies_msec, packet_type,
+		    packet[0], packet[1], packet[2], packet[3], abs_x, abs_y);
+
+	if (jiffies_msec - ps2_last_second > 1000) {
+		psmouse_dbg(psmouse, "PS/2 packets/sec = %d\n", ps2_packet_cnt);
+		ps2_packet_cnt = 0;
+		ps2_last_second = jiffies_msec;
+	}
+}
+#else
+static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[])
+{
+}
+#endif
+
+static void fsp_set_slot(struct input_dev *dev, int slot, bool active,
+			 unsigned int x, unsigned int y)
+{
+	input_mt_slot(dev, slot);
+	input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
+	if (active) {
+		input_report_abs(dev, ABS_MT_POSITION_X, x);
+		input_report_abs(dev, ABS_MT_POSITION_Y, y);
+	}
+}
+
+static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct fsp_data *ad = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	unsigned char button_status = 0, lscroll = 0, rscroll = 0;
+	unsigned short abs_x, abs_y, fgrs = 0;
+	int rel_x, rel_y;
+
+	if (psmouse->pktcnt < 4)
+		return PSMOUSE_GOOD_DATA;
+
+	/*
+	 * Full packet accumulated, process it
+	 */
+
+	fsp_packet_debug(psmouse, packet);
+
+	switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) {
+	case FSP_PKT_TYPE_ABS:
+
+		if ((packet[0] == 0x48 || packet[0] == 0x49) &&
+		    packet[1] == 0 && packet[2] == 0) {
+			/*
+			 * Ignore coordinate noise when finger leaving the
+			 * surface, otherwise cursor may jump to upper-left
+			 * corner.
+			 */
+			packet[3] &= 0xf0;
+		}
+
+		abs_x = GET_ABS_X(packet);
+		abs_y = GET_ABS_Y(packet);
+
+		if (packet[0] & FSP_PB0_MFMC) {
+			/*
+			 * MFMC packet: assume that there are two fingers on
+			 * pad
+			 */
+			fgrs = 2;
+
+			/* MFMC packet */
+			if (packet[0] & FSP_PB0_MFMC_FGR2) {
+				/* 2nd finger */
+				if (ad->last_mt_fgr == 2) {
+					/*
+					 * workaround for buggy firmware
+					 * which doesn't clear MFMC bit if
+					 * the 1st finger is up
+					 */
+					fgrs = 1;
+					fsp_set_slot(dev, 0, false, 0, 0);
+				}
+				ad->last_mt_fgr = 2;
+
+				fsp_set_slot(dev, 1, fgrs == 2, abs_x, abs_y);
+			} else {
+				/* 1st finger */
+				if (ad->last_mt_fgr == 1) {
+					/*
+					 * workaround for buggy firmware
+					 * which doesn't clear MFMC bit if
+					 * the 2nd finger is up
+					 */
+					fgrs = 1;
+					fsp_set_slot(dev, 1, false, 0, 0);
+				}
+				ad->last_mt_fgr = 1;
+				fsp_set_slot(dev, 0, fgrs != 0, abs_x, abs_y);
+			}
+		} else {
+			/* SFAC packet */
+			if ((packet[0] & (FSP_PB0_LBTN|FSP_PB0_PHY_BTN)) ==
+				FSP_PB0_LBTN) {
+				/* On-pad click in SFAC mode should be handled
+				 * by userspace.  On-pad clicks in MFMC mode
+				 * are real clickpad clicks, and not ignored.
+				 */
+				packet[0] &= ~FSP_PB0_LBTN;
+			}
+
+			/* no multi-finger information */
+			ad->last_mt_fgr = 0;
+
+			if (abs_x != 0 && abs_y != 0)
+				fgrs = 1;
+
+			fsp_set_slot(dev, 0, fgrs > 0, abs_x, abs_y);
+			fsp_set_slot(dev, 1, false, 0, 0);
+		}
+		if (fgrs == 1 || (fgrs == 2 && !(packet[0] & FSP_PB0_MFMC_FGR2))) {
+			input_report_abs(dev, ABS_X, abs_x);
+			input_report_abs(dev, ABS_Y, abs_y);
+		}
+		input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+		input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+		input_report_key(dev, BTN_TOUCH, fgrs);
+		input_report_key(dev, BTN_TOOL_FINGER, fgrs == 1);
+		input_report_key(dev, BTN_TOOL_DOUBLETAP, fgrs == 2);
+		break;
+
+	case FSP_PKT_TYPE_NORMAL_OPC:
+		/* on-pad click, filter it if necessary */
+		if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC)
+			packet[0] &= ~FSP_PB0_LBTN;
+		/* fall through */
+
+	case FSP_PKT_TYPE_NORMAL:
+		/* normal packet */
+		/* special packet data translation from on-pad packets */
+		if (packet[3] != 0) {
+			if (packet[3] & BIT(0))
+				button_status |= 0x01;	/* wheel down */
+			if (packet[3] & BIT(1))
+				button_status |= 0x0f;	/* wheel up */
+			if (packet[3] & BIT(2))
+				button_status |= BIT(4);/* horizontal left */
+			if (packet[3] & BIT(3))
+				button_status |= BIT(5);/* horizontal right */
+			/* push back to packet queue */
+			if (button_status != 0)
+				packet[3] = button_status;
+			rscroll = (packet[3] >> 4) & 1;
+			lscroll = (packet[3] >> 5) & 1;
+		}
+		/*
+		 * Processing wheel up/down and extra button events
+		 */
+		input_report_rel(dev, REL_WHEEL,
+				 (int)(packet[3] & 8) - (int)(packet[3] & 7));
+		input_report_rel(dev, REL_HWHEEL, lscroll - rscroll);
+		input_report_key(dev, BTN_BACK, lscroll);
+		input_report_key(dev, BTN_FORWARD, rscroll);
+
+		/*
+		 * Standard PS/2 Mouse
+		 */
+		input_report_key(dev, BTN_LEFT, packet[0] & 1);
+		input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
+		input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
+
+		rel_x = packet[1] ? (int)packet[1] - (int)((packet[0] << 4) & 0x100) : 0;
+		rel_y = packet[2] ? (int)((packet[0] << 3) & 0x100) - (int)packet[2] : 0;
+
+		input_report_rel(dev, REL_X, rel_x);
+		input_report_rel(dev, REL_Y, rel_y);
+		break;
+	}
+
+	input_sync(dev);
+
+	return PSMOUSE_FULL_PACKET;
+}
+
+static int fsp_activate_protocol(struct psmouse *psmouse)
+{
+	struct fsp_data *pad = psmouse->private;
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[2];
+	int val;
+
+	/*
+	 * Standard procedure to enter FSP Intellimouse mode
+	 * (scrolling wheel, 4th and 5th buttons)
+	 */
+	param[0] = 200;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] = 200;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] =  80;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
+	if (param[0] != 0x04) {
+		psmouse_err(psmouse,
+			    "Unable to enable 4 bytes packet format.\n");
+		return -EIO;
+	}
+
+	if (pad->ver < FSP_VER_STL3888_C0) {
+		/* Preparing relative coordinates output for older hardware */
+		if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) {
+			psmouse_err(psmouse,
+				    "Unable to read SYSCTL5 register.\n");
+			return -EIO;
+		}
+
+		if (fsp_get_buttons(psmouse, &pad->buttons)) {
+			psmouse_err(psmouse,
+				    "Unable to retrieve number of buttons.\n");
+			return -EIO;
+		}
+
+		val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8);
+		/* Ensure we are not in absolute mode */
+		val &= ~FSP_BIT_EN_PKT_G0;
+		if (pad->buttons == 0x06) {
+			/* Left/Middle/Right & Scroll Up/Down/Right/Left */
+			val |= FSP_BIT_EN_MSID6;
+		}
+
+		if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) {
+			psmouse_err(psmouse,
+				    "Unable to set up required mode bits.\n");
+			return -EIO;
+		}
+
+		/*
+		 * Enable OPC tags such that driver can tell the difference
+		 * between on-pad and real button click
+		 */
+		if (fsp_opc_tag_enable(psmouse, true))
+			psmouse_warn(psmouse,
+				     "Failed to enable OPC tag mode.\n");
+		/* enable on-pad click by default */
+		pad->flags |= FSPDRV_FLAG_EN_OPC;
+
+		/* Enable on-pad vertical and horizontal scrolling */
+		fsp_onpad_vscr(psmouse, true);
+		fsp_onpad_hscr(psmouse, true);
+	} else {
+		/* Enable absolute coordinates output for Cx/Dx hardware */
+		if (fsp_reg_write(psmouse, FSP_REG_SWC1,
+				  FSP_BIT_SWC1_EN_ABS_1F |
+				  FSP_BIT_SWC1_EN_ABS_2F |
+				  FSP_BIT_SWC1_EN_FUP_OUT |
+				  FSP_BIT_SWC1_EN_ABS_CON)) {
+			psmouse_err(psmouse,
+				    "Unable to enable absolute coordinates output.\n");
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+static int fsp_set_input_params(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct fsp_data *pad = psmouse->private;
+
+	if (pad->ver < FSP_VER_STL3888_C0) {
+		__set_bit(BTN_MIDDLE, dev->keybit);
+		__set_bit(BTN_BACK, dev->keybit);
+		__set_bit(BTN_FORWARD, dev->keybit);
+		__set_bit(REL_WHEEL, dev->relbit);
+		__set_bit(REL_HWHEEL, dev->relbit);
+	} else {
+		/*
+		 * Hardware prior to Cx performs much better in relative mode;
+		 * hence, only enable absolute coordinates output as well as
+		 * multi-touch output for the newer hardware.
+		 *
+		 * Maximum coordinates can be computed as:
+		 *
+		 *	number of scanlines * 64 - 57
+		 *
+		 * where number of X/Y scanline lines are 16/12.
+		 */
+		int abs_x = 967, abs_y = 711;
+
+		__set_bit(EV_ABS, dev->evbit);
+		__clear_bit(EV_REL, dev->evbit);
+		__set_bit(BTN_TOUCH, dev->keybit);
+		__set_bit(BTN_TOOL_FINGER, dev->keybit);
+		__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
+
+		input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0);
+		input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0);
+		input_mt_init_slots(dev, 2, 0);
+		input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0);
+		input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0);
+	}
+
+	return 0;
+}
+
+int fsp_detect(struct psmouse *psmouse, bool set_properties)
+{
+	int id;
+
+	if (fsp_reg_read(psmouse, FSP_REG_DEVICE_ID, &id))
+		return -EIO;
+
+	if (id != 0x01)
+		return -ENODEV;
+
+	if (set_properties) {
+		psmouse->vendor = "Sentelic";
+		psmouse->name = "FingerSensingPad";
+	}
+
+	return 0;
+}
+
+static void fsp_reset(struct psmouse *psmouse)
+{
+	fsp_opc_tag_enable(psmouse, false);
+	fsp_onpad_vscr(psmouse, false);
+	fsp_onpad_hscr(psmouse, false);
+}
+
+static void fsp_disconnect(struct psmouse *psmouse)
+{
+	sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
+			   &fsp_attribute_group);
+
+	fsp_reset(psmouse);
+	kfree(psmouse->private);
+}
+
+static int fsp_reconnect(struct psmouse *psmouse)
+{
+	int version;
+
+	if (fsp_detect(psmouse, 0))
+		return -ENODEV;
+
+	if (fsp_get_version(psmouse, &version))
+		return -ENODEV;
+
+	if (fsp_activate_protocol(psmouse))
+		return -EIO;
+
+	return 0;
+}
+
+int fsp_init(struct psmouse *psmouse)
+{
+	struct fsp_data *priv;
+	int ver, rev, sn = 0;
+	int error;
+
+	if (fsp_get_version(psmouse, &ver) ||
+	    fsp_get_revision(psmouse, &rev)) {
+		return -ENODEV;
+	}
+	if (ver >= FSP_VER_STL3888_C0) {
+		/* firmware information is only available since C0 */
+		fsp_get_sn(psmouse, &sn);
+	}
+
+	psmouse_info(psmouse,
+		     "Finger Sensing Pad, hw: %d.%d.%d, sn: %x, sw: %s\n",
+		     ver >> 4, ver & 0x0F, rev, sn, fsp_drv_ver);
+
+	psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->ver = ver;
+	priv->rev = rev;
+
+	psmouse->protocol_handler = fsp_process_byte;
+	psmouse->disconnect = fsp_disconnect;
+	psmouse->reconnect = fsp_reconnect;
+	psmouse->cleanup = fsp_reset;
+	psmouse->pktsize = 4;
+
+	error = fsp_activate_protocol(psmouse);
+	if (error)
+		goto err_out;
+
+	/* Set up various supported input event bits */
+	error = fsp_set_input_params(psmouse);
+	if (error)
+		goto err_out;
+
+	error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
+				   &fsp_attribute_group);
+	if (error) {
+		psmouse_err(psmouse,
+			    "Failed to create sysfs attributes (%d)", error);
+		goto err_out;
+	}
+
+	return 0;
+
+ err_out:
+	kfree(psmouse->private);
+	psmouse->private = NULL;
+	return error;
+}
diff --git a/drivers/input/mouse/sentelic.h b/drivers/input/mouse/sentelic.h
new file mode 100644
index 0000000..42df9e3
--- /dev/null
+++ b/drivers/input/mouse/sentelic.h
@@ -0,0 +1,138 @@
+/*-
+ * Finger Sensing Pad PS/2 mouse driver.
+ *
+ * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
+ * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation.
+ *
+ *   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.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef	__SENTELIC_H
+#define	__SENTELIC_H
+
+/* Finger-sensing Pad information registers */
+#define	FSP_REG_DEVICE_ID	0x00
+#define	FSP_REG_VERSION		0x01
+#define	FSP_REG_REVISION	0x04
+#define	FSP_REG_TMOD_STATUS1	0x0B
+#define	FSP_BIT_NO_ROTATION	BIT(3)
+#define	FSP_REG_PAGE_CTRL	0x0F
+
+/* Finger-sensing Pad control registers */
+#define	FSP_REG_SYSCTL1		0x10
+#define	FSP_BIT_EN_REG_CLK	BIT(5)
+#define	FSP_REG_TMOD_STATUS	0x20
+#define	FSP_REG_OPC_QDOWN	0x31
+#define	FSP_BIT_EN_OPC_TAG	BIT(7)
+#define	FSP_REG_OPTZ_XLO	0x34
+#define	FSP_REG_OPTZ_XHI	0x35
+#define	FSP_REG_OPTZ_YLO	0x36
+#define	FSP_REG_OPTZ_YHI	0x37
+#define	FSP_REG_SYSCTL5		0x40
+#define	FSP_BIT_90_DEGREE	BIT(0)
+#define	FSP_BIT_EN_MSID6	BIT(1)
+#define	FSP_BIT_EN_MSID7	BIT(2)
+#define	FSP_BIT_EN_MSID8	BIT(3)
+#define	FSP_BIT_EN_AUTO_MSID8	BIT(5)
+#define	FSP_BIT_EN_PKT_G0	BIT(6)
+
+#define	FSP_REG_ONPAD_CTL	0x43
+#define	FSP_BIT_ONPAD_ENABLE	BIT(0)
+#define	FSP_BIT_ONPAD_FBBB	BIT(1)
+#define	FSP_BIT_FIX_VSCR	BIT(3)
+#define	FSP_BIT_FIX_HSCR	BIT(5)
+#define	FSP_BIT_DRAG_LOCK	BIT(6)
+
+#define	FSP_REG_SWC1		(0x90)
+#define	FSP_BIT_SWC1_EN_ABS_1F	BIT(0)
+#define	FSP_BIT_SWC1_EN_GID	BIT(1)
+#define	FSP_BIT_SWC1_EN_ABS_2F	BIT(2)
+#define	FSP_BIT_SWC1_EN_FUP_OUT	BIT(3)
+#define	FSP_BIT_SWC1_EN_ABS_CON	BIT(4)
+#define	FSP_BIT_SWC1_GST_GRP0	BIT(5)
+#define	FSP_BIT_SWC1_GST_GRP1	BIT(6)
+#define	FSP_BIT_SWC1_BX_COMPAT	BIT(7)
+
+#define	FSP_PAGE_0B		(0x0b)
+#define	FSP_PAGE_82		(0x82)
+#define	FSP_PAGE_DEFAULT	FSP_PAGE_82
+
+#define	FSP_REG_SN0		(0x40)
+#define	FSP_REG_SN1		(0x41)
+#define	FSP_REG_SN2		(0x42)
+
+/* Finger-sensing Pad packet formating related definitions */
+
+/* absolute packet type */
+#define	FSP_PKT_TYPE_NORMAL	(0x00)
+#define	FSP_PKT_TYPE_ABS	(0x01)
+#define	FSP_PKT_TYPE_NOTIFY	(0x02)
+#define	FSP_PKT_TYPE_NORMAL_OPC	(0x03)
+#define	FSP_PKT_TYPE_SHIFT	(6)
+
+/* bit definitions for the first byte of report packet */
+#define	FSP_PB0_LBTN		BIT(0)
+#define	FSP_PB0_RBTN		BIT(1)
+#define	FSP_PB0_MBTN		BIT(2)
+#define	FSP_PB0_MFMC_FGR2	FSP_PB0_MBTN
+#define	FSP_PB0_MUST_SET	BIT(3)
+#define	FSP_PB0_PHY_BTN		BIT(4)
+#define	FSP_PB0_MFMC		BIT(5)
+
+/* hardware revisions */
+#define	FSP_VER_STL3888_A4	(0xC1)
+#define	FSP_VER_STL3888_B0	(0xD0)
+#define	FSP_VER_STL3888_B1	(0xD1)
+#define	FSP_VER_STL3888_B2	(0xD2)
+#define	FSP_VER_STL3888_C0	(0xE0)
+#define	FSP_VER_STL3888_C1	(0xE1)
+#define	FSP_VER_STL3888_D0	(0xE2)
+#define	FSP_VER_STL3888_D1	(0xE3)
+#define	FSP_VER_STL3888_E0	(0xE4)
+
+#ifdef __KERNEL__
+
+struct fsp_data {
+	unsigned char	ver;		/* hardware version */
+	unsigned char	rev;		/* hardware revison */
+	unsigned int	buttons;	/* Number of buttons */
+	unsigned int	flags;
+#define	FSPDRV_FLAG_EN_OPC	(0x001)	/* enable on-pad clicking */
+
+	bool		vscroll;	/* Vertical scroll zone enabled */
+	bool		hscroll;	/* Horizontal scroll zone enabled */
+
+	unsigned char	last_reg;	/* Last register we requested read from */
+	unsigned char	last_val;
+	unsigned int	last_mt_fgr;	/* Last seen finger(multitouch) */
+};
+
+#ifdef CONFIG_MOUSE_PS2_SENTELIC
+extern int fsp_detect(struct psmouse *psmouse, bool set_properties);
+extern int fsp_init(struct psmouse *psmouse);
+#else
+static inline int fsp_detect(struct psmouse *psmouse, bool set_properties)
+{
+	return -ENOSYS;
+}
+static inline int fsp_init(struct psmouse *psmouse)
+{
+	return -ENOSYS;
+}
+#endif
+
+#endif	/* __KERNEL__ */
+
+#endif	/* !__SENTELIC_H */
diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c
new file mode 100644
index 0000000..8df5266
--- /dev/null
+++ b/drivers/input/mouse/sermouse.c
@@ -0,0 +1,357 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ *  Serial mouse driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Serial mouse driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static const char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse",
+					"Logitech M+ Mouse", "Microsoft MZ Mouse", "Logitech MZ+ Mouse",
+					"Logitech MZ++ Mouse"};
+
+struct sermouse {
+	struct input_dev *dev;
+	signed char buf[8];
+	unsigned char count;
+	unsigned char type;
+	unsigned long last;
+	char phys[32];
+};
+
+/*
+ * sermouse_process_msc() analyzes the incoming MSC/Sun bytestream and
+ * applies some prediction to the data, resulting in 96 updates per
+ * second, which is as good as a PS/2 or USB mouse.
+ */
+
+static void sermouse_process_msc(struct sermouse *sermouse, signed char data)
+{
+	struct input_dev *dev = sermouse->dev;
+	signed char *buf = sermouse->buf;
+
+	switch (sermouse->count) {
+
+		case 0:
+			if ((data & 0xf8) != 0x80)
+				return;
+			input_report_key(dev, BTN_LEFT,   !(data & 4));
+			input_report_key(dev, BTN_RIGHT,  !(data & 1));
+			input_report_key(dev, BTN_MIDDLE, !(data & 2));
+			break;
+
+		case 1:
+		case 3:
+			input_report_rel(dev, REL_X, data / 2);
+			input_report_rel(dev, REL_Y, -buf[1]);
+			buf[0] = data - data / 2;
+			break;
+
+		case 2:
+		case 4:
+			input_report_rel(dev, REL_X, buf[0]);
+			input_report_rel(dev, REL_Y, buf[1] - data);
+			buf[1] = data / 2;
+			break;
+	}
+
+	input_sync(dev);
+
+	if (++sermouse->count == 5)
+		sermouse->count = 0;
+}
+
+/*
+ * sermouse_process_ms() anlyzes the incoming MS(Z/+/++) bytestream and
+ * generates events. With prediction it gets 80 updates/sec, assuming
+ * standard 3-byte packets and 1200 bps.
+ */
+
+static void sermouse_process_ms(struct sermouse *sermouse, signed char data)
+{
+	struct input_dev *dev = sermouse->dev;
+	signed char *buf = sermouse->buf;
+
+	if (data & 0x40)
+		sermouse->count = 0;
+	else if (sermouse->count == 0)
+		return;
+
+	switch (sermouse->count) {
+
+		case 0:
+			buf[1] = data;
+			input_report_key(dev, BTN_LEFT,   (data >> 5) & 1);
+			input_report_key(dev, BTN_RIGHT,  (data >> 4) & 1);
+			break;
+
+		case 1:
+			buf[2] = data;
+			data = (signed char) (((buf[1] << 6) & 0xc0) | (data & 0x3f));
+			input_report_rel(dev, REL_X, data / 2);
+			input_report_rel(dev, REL_Y, buf[4]);
+			buf[3] = data - data / 2;
+			break;
+
+		case 2:
+			/* Guessing the state of the middle button on 3-button MS-protocol mice - ugly. */
+			if ((sermouse->type == SERIO_MS) && !data && !buf[2] && !((buf[0] & 0xf0) ^ buf[1]))
+				input_report_key(dev, BTN_MIDDLE, !test_bit(BTN_MIDDLE, dev->key));
+			buf[0] = buf[1];
+
+			data = (signed char) (((buf[1] << 4) & 0xc0) | (data & 0x3f));
+			input_report_rel(dev, REL_X, buf[3]);
+			input_report_rel(dev, REL_Y, data - buf[4]);
+			buf[4] = data / 2;
+			break;
+
+		case 3:
+
+			switch (sermouse->type) {
+
+				case SERIO_MS:
+					 sermouse->type = SERIO_MP;
+
+				case SERIO_MP:
+					if ((data >> 2) & 3) break;	/* M++ Wireless Extension packet. */
+					input_report_key(dev, BTN_MIDDLE, (data >> 5) & 1);
+					input_report_key(dev, BTN_SIDE,   (data >> 4) & 1);
+					break;
+
+				case SERIO_MZP:
+				case SERIO_MZPP:
+					input_report_key(dev, BTN_SIDE,   (data >> 5) & 1);
+
+				case SERIO_MZ:
+					input_report_key(dev, BTN_MIDDLE, (data >> 4) & 1);
+					input_report_rel(dev, REL_WHEEL,  (data & 8) - (data & 7));
+					break;
+			}
+
+			break;
+
+		case 4:
+		case 6:	/* MZ++ packet type. We can get these bytes for M++ too but we ignore them later. */
+			buf[1] = (data >> 2) & 0x0f;
+			break;
+
+		case 5:
+		case 7: /* Ignore anything besides MZ++ */
+			if (sermouse->type != SERIO_MZPP)
+				break;
+
+			switch (buf[1]) {
+
+				case 1: /* Extra mouse info */
+
+					input_report_key(dev, BTN_SIDE, (data >> 4) & 1);
+					input_report_key(dev, BTN_EXTRA, (data >> 5) & 1);
+					input_report_rel(dev, data & 0x80 ? REL_HWHEEL : REL_WHEEL, (data & 7) - (data & 8));
+
+					break;
+
+				default: /* We don't decode anything else yet. */
+
+					printk(KERN_WARNING
+						"sermouse.c: Received MZ++ packet %x, don't know how to handle.\n", buf[1]);
+					break;
+			}
+
+			break;
+	}
+
+	input_sync(dev);
+
+	sermouse->count++;
+}
+
+/*
+ * sermouse_interrupt() handles incoming characters, either gathering them into
+ * packets or passing them to the command routine as command output.
+ */
+
+static irqreturn_t sermouse_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct sermouse *sermouse = serio_get_drvdata(serio);
+
+	if (time_after(jiffies, sermouse->last + HZ/10))
+		sermouse->count = 0;
+
+	sermouse->last = jiffies;
+
+	if (sermouse->type > SERIO_SUN)
+		sermouse_process_ms(sermouse, data);
+	else
+		sermouse_process_msc(sermouse, data);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * sermouse_disconnect() cleans up after we don't want talk
+ * to the mouse anymore.
+ */
+
+static void sermouse_disconnect(struct serio *serio)
+{
+	struct sermouse *sermouse = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(sermouse->dev);
+	kfree(sermouse);
+}
+
+/*
+ * sermouse_connect() is a callback form the serio module when
+ * an unhandled serio port is found.
+ */
+
+static int sermouse_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct sermouse *sermouse;
+	struct input_dev *input_dev;
+	unsigned char c = serio->id.extra;
+	int err = -ENOMEM;
+
+	sermouse = kzalloc(sizeof(struct sermouse), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!sermouse || !input_dev)
+		goto fail1;
+
+	sermouse->dev = input_dev;
+	snprintf(sermouse->phys, sizeof(sermouse->phys), "%s/input0", serio->phys);
+	sermouse->type = serio->id.proto;
+
+	input_dev->name = sermouse_protocols[sermouse->type];
+	input_dev->phys = sermouse->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor  = sermouse->type;
+	input_dev->id.product = c;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+	input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+		BIT_MASK(BTN_RIGHT);
+	input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+	if (c & 0x01) set_bit(BTN_MIDDLE, input_dev->keybit);
+	if (c & 0x02) set_bit(BTN_SIDE, input_dev->keybit);
+	if (c & 0x04) set_bit(BTN_EXTRA, input_dev->keybit);
+	if (c & 0x10) set_bit(REL_WHEEL, input_dev->relbit);
+	if (c & 0x20) set_bit(REL_HWHEEL, input_dev->relbit);
+
+	serio_set_drvdata(serio, sermouse);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(sermouse->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(sermouse);
+	return err;
+}
+
+static struct serio_device_id sermouse_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MSC,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_SUN,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MS,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MP,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MZ,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MZP,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MZPP,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, sermouse_serio_ids);
+
+static struct serio_driver sermouse_drv = {
+	.driver		= {
+		.name	= "sermouse",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= sermouse_serio_ids,
+	.interrupt	= sermouse_interrupt,
+	.connect	= sermouse_connect,
+	.disconnect	= sermouse_disconnect,
+};
+
+module_serio_driver(sermouse_drv);
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
new file mode 100644
index 0000000..a41d832
--- /dev/null
+++ b/drivers/input/mouse/synaptics.c
@@ -0,0 +1,1577 @@
+/*
+ * Synaptics TouchPad PS/2 mouse driver
+ *
+ *   2003 Dmitry Torokhov <dtor@mail.ru>
+ *     Added support for pass-through port. Special thanks to Peter Berg Larsen
+ *     for explaining various Synaptics quirks.
+ *
+ *   2003 Peter Osterlund <petero2@telia.com>
+ *     Ported to 2.5 input device infrastructure.
+ *
+ *   Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch>
+ *     start merging tpconfig and gpm code to a xfree-input module
+ *     adding some changes and extensions (ex. 3rd and 4th button)
+ *
+ *   Copyright (c) 1997 C. Scott Ananian <cananian@alumni.priceton.edu>
+ *   Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com>
+ *     code for the special synaptics commands (from the tpconfig-source)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/input/mt.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/slab.h>
+#include "psmouse.h"
+#include "synaptics.h"
+
+/*
+ * The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
+ * section 2.3.2, which says that they should be valid regardless of the
+ * actual size of the sensor.
+ * Note that newer firmware allows querying device for maximum useable
+ * coordinates.
+ */
+#define XMIN 0
+#define XMAX 6143
+#define YMIN 0
+#define YMAX 6143
+#define XMIN_NOMINAL 1472
+#define XMAX_NOMINAL 5472
+#define YMIN_NOMINAL 1408
+#define YMAX_NOMINAL 4448
+
+/* Size in bits of absolute position values reported by the hardware */
+#define ABS_POS_BITS 13
+
+/*
+ * These values should represent the absolute maximum value that will
+ * be reported for a positive position value. Some Synaptics firmware
+ * uses this value to indicate a finger near the edge of the touchpad
+ * whose precise position cannot be determined.
+ *
+ * At least one touchpad is known to report positions in excess of this
+ * value which are actually negative values truncated to the 13-bit
+ * reporting range. These values have never been observed to be lower
+ * than 8184 (i.e. -8), so we treat all values greater than 8176 as
+ * negative and any other value as positive.
+ */
+#define X_MAX_POSITIVE 8176
+#define Y_MAX_POSITIVE 8176
+
+/* maximum ABS_MT_POSITION displacement (in mm) */
+#define DMAX 10
+
+/*****************************************************************************
+ *	Stuff we need even when we do not want native Synaptics support
+ ****************************************************************************/
+
+/*
+ * Set the synaptics touchpad mode byte by special commands
+ */
+static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode)
+{
+	unsigned char param[1];
+
+	if (psmouse_sliced_command(psmouse, mode))
+		return -1;
+	param[0] = SYN_PS_SET_MODE2;
+	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE))
+		return -1;
+	return 0;
+}
+
+int synaptics_detect(struct psmouse *psmouse, bool set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4];
+
+	param[0] = 0;
+
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
+
+	if (param[1] != 0x47)
+		return -ENODEV;
+
+	if (set_properties) {
+		psmouse->vendor = "Synaptics";
+		psmouse->name = "TouchPad";
+	}
+
+	return 0;
+}
+
+void synaptics_reset(struct psmouse *psmouse)
+{
+	/* reset touchpad back to relative mode, gestures enabled */
+	synaptics_mode_cmd(psmouse, 0);
+}
+
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
+
+static bool cr48_profile_sensor;
+
+#define ANY_BOARD_ID 0
+struct min_max_quirk {
+	const char * const *pnp_ids;
+	struct {
+		unsigned long int min, max;
+	} board_id;
+	int x_min, x_max, y_min, y_max;
+};
+
+static const struct min_max_quirk min_max_pnpid_table[] = {
+	{
+		(const char * const []){"LEN0033", NULL},
+		{ANY_BOARD_ID, ANY_BOARD_ID},
+		1024, 5052, 2258, 4832
+	},
+	{
+		(const char * const []){"LEN0042", NULL},
+		{ANY_BOARD_ID, ANY_BOARD_ID},
+		1232, 5710, 1156, 4696
+	},
+	{
+		(const char * const []){"LEN0034", "LEN0036", "LEN0037",
+					"LEN0039", "LEN2002", "LEN2004",
+					NULL},
+		{ANY_BOARD_ID, 2961},
+		1024, 5112, 2024, 4832
+	},
+	{
+		(const char * const []){"LEN2000", NULL},
+		{ANY_BOARD_ID, ANY_BOARD_ID},
+		1024, 5113, 2021, 4832
+	},
+	{
+		(const char * const []){"LEN2001", NULL},
+		{ANY_BOARD_ID, ANY_BOARD_ID},
+		1024, 5022, 2508, 4832
+	},
+	{
+		(const char * const []){"LEN2006", NULL},
+		{2691, 2691},
+		1024, 5045, 2457, 4832
+	},
+	{
+		(const char * const []){"LEN2006", NULL},
+		{ANY_BOARD_ID, ANY_BOARD_ID},
+		1264, 5675, 1171, 4688
+	},
+	{ }
+};
+
+/* This list has been kindly provided by Synaptics. */
+static const char * const topbuttonpad_pnp_ids[] = {
+	"LEN0017",
+	"LEN0018",
+	"LEN0019",
+	"LEN0023",
+	"LEN002A",
+	"LEN002B",
+	"LEN002C",
+	"LEN002D",
+	"LEN002E",
+	"LEN0033", /* Helix */
+	"LEN0034", /* T431s, L440, L540, T540, W540, X1 Carbon 2nd */
+	"LEN0035", /* X240 */
+	"LEN0036", /* T440 */
+	"LEN0037", /* X1 Carbon 2nd */
+	"LEN0038",
+	"LEN0039", /* T440s */
+	"LEN0041",
+	"LEN0042", /* Yoga */
+	"LEN0045",
+	"LEN0047",
+	"LEN0049",
+	"LEN2000", /* S540 */
+	"LEN2001", /* Edge E431 */
+	"LEN2002", /* Edge E531 */
+	"LEN2003",
+	"LEN2004", /* L440 */
+	"LEN2005",
+	"LEN2006", /* Edge E440/E540 */
+	"LEN2007",
+	"LEN2008",
+	"LEN2009",
+	"LEN200A",
+	"LEN200B",
+	NULL
+};
+
+/* This list has been kindly provided by Synaptics. */
+static const char * const forcepad_pnp_ids[] = {
+	"SYN300D",
+	"SYN3014",
+	NULL
+};
+
+/*****************************************************************************
+ *	Synaptics communications functions
+ ****************************************************************************/
+
+/*
+ * Synaptics touchpads report the y coordinate from bottom to top, which is
+ * opposite from what userspace expects.
+ * This function is used to invert y before reporting.
+ */
+static int synaptics_invert_y(int y)
+{
+	return YMAX_NOMINAL + YMIN_NOMINAL - y;
+}
+
+/*
+ * Send a command to the synpatics touchpad by special commands
+ */
+static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
+{
+	if (psmouse_sliced_command(psmouse, c))
+		return -1;
+	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
+		return -1;
+	return 0;
+}
+
+/*
+ * Read the model-id bytes from the touchpad
+ * see also SYN_MODEL_* macros
+ */
+static int synaptics_model_id(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	unsigned char mi[3];
+
+	if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi))
+		return -1;
+	priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2];
+	return 0;
+}
+
+static int synaptics_more_extended_queries(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	unsigned char buf[3];
+
+	if (synaptics_send_cmd(psmouse, SYN_QUE_MEXT_CAPAB_10, buf))
+		return -1;
+
+	priv->ext_cap_10 = (buf[0]<<16) | (buf[1]<<8) | buf[2];
+
+	return 0;
+}
+
+/*
+ * Read the board id and the "More Extended Queries" from the touchpad
+ * The board id is encoded in the "QUERY MODES" response
+ */
+static int synaptics_query_modes(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	unsigned char bid[3];
+
+	/* firmwares prior 7.5 have no board_id encoded */
+	if (SYN_ID_FULL(priv->identity) < 0x705)
+		return 0;
+
+	if (synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid))
+		return -1;
+	priv->board_id = ((bid[0] & 0xfc) << 6) | bid[1];
+
+	if (SYN_MEXT_CAP_BIT(bid[0]))
+		return synaptics_more_extended_queries(psmouse);
+
+	return 0;
+}
+
+/*
+ * Read the firmware id from the touchpad
+ */
+static int synaptics_firmware_id(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	unsigned char fwid[3];
+
+	if (synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid))
+		return -1;
+	priv->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2];
+	return 0;
+}
+
+/*
+ * Read the capability-bits from the touchpad
+ * see also the SYN_CAP_* macros
+ */
+static int synaptics_capability(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	unsigned char cap[3];
+
+	if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap))
+		return -1;
+	priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+	priv->ext_cap = priv->ext_cap_0c = 0;
+
+	/*
+	 * Older firmwares had submodel ID fixed to 0x47
+	 */
+	if (SYN_ID_FULL(priv->identity) < 0x705 &&
+	    SYN_CAP_SUBMODEL_ID(priv->capabilities) != 0x47) {
+		return -1;
+	}
+
+	/*
+	 * Unless capExtended is set the rest of the flags should be ignored
+	 */
+	if (!SYN_CAP_EXTENDED(priv->capabilities))
+		priv->capabilities = 0;
+
+	if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {
+		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) {
+			psmouse_warn(psmouse,
+				     "device claims to have extended capabilities, but I'm not able to read them.\n");
+		} else {
+			priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+
+			/*
+			 * if nExtBtn is greater than 8 it should be considered
+			 * invalid and treated as 0
+			 */
+			if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8)
+				priv->ext_cap &= 0xff0fff;
+		}
+	}
+
+	if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 4) {
+		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap)) {
+			psmouse_warn(psmouse,
+				     "device claims to have extended capability 0x0c, but I'm not able to read it.\n");
+		} else {
+			priv->ext_cap_0c = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Identify Touchpad
+ * See also the SYN_ID_* macros
+ */
+static int synaptics_identify(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	unsigned char id[3];
+
+	if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id))
+		return -1;
+	priv->identity = (id[0]<<16) | (id[1]<<8) | id[2];
+	if (SYN_ID_IS_SYNAPTICS(priv->identity))
+		return 0;
+	return -1;
+}
+
+/*
+ * Read touchpad resolution and maximum reported coordinates
+ * Resolution is left zero if touchpad does not support the query
+ */
+
+static int synaptics_resolution(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	unsigned char resp[3];
+
+	if (SYN_ID_MAJOR(priv->identity) < 4)
+		return 0;
+
+	if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp) == 0) {
+		if (resp[0] != 0 && (resp[1] & 0x80) && resp[2] != 0) {
+			priv->x_res = resp[0]; /* x resolution in units/mm */
+			priv->y_res = resp[2]; /* y resolution in units/mm */
+		}
+	}
+
+	if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 &&
+	    SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) {
+		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) {
+			psmouse_warn(psmouse,
+				     "device claims to have max coordinates query, but I'm not able to read it.\n");
+		} else {
+			priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+			priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+			psmouse_info(psmouse,
+				     "queried max coordinates: x [..%d], y [..%d]\n",
+				     priv->x_max, priv->y_max);
+		}
+	}
+
+	if (SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c) &&
+	    (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 ||
+	     /*
+	      * Firmware v8.1 does not report proper number of extended
+	      * capabilities, but has been proven to report correct min
+	      * coordinates.
+	      */
+	     SYN_ID_FULL(priv->identity) == 0x801)) {
+		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) {
+			psmouse_warn(psmouse,
+				     "device claims to have min coordinates query, but I'm not able to read it.\n");
+		} else {
+			priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+			priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+			psmouse_info(psmouse,
+				     "queried min coordinates: x [%d..], y [%d..]\n",
+				     priv->x_min, priv->y_min);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Apply quirk(s) if the hardware matches
+ */
+
+static void synaptics_apply_quirks(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	int i;
+
+	for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) {
+		if (!psmouse_matches_pnp_id(psmouse,
+					    min_max_pnpid_table[i].pnp_ids))
+			continue;
+
+		if (min_max_pnpid_table[i].board_id.min != ANY_BOARD_ID &&
+		    priv->board_id < min_max_pnpid_table[i].board_id.min)
+			continue;
+
+		if (min_max_pnpid_table[i].board_id.max != ANY_BOARD_ID &&
+		    priv->board_id > min_max_pnpid_table[i].board_id.max)
+			continue;
+
+		priv->x_min = min_max_pnpid_table[i].x_min;
+		priv->x_max = min_max_pnpid_table[i].x_max;
+		priv->y_min = min_max_pnpid_table[i].y_min;
+		priv->y_max = min_max_pnpid_table[i].y_max;
+		psmouse_info(psmouse,
+			     "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n",
+			     priv->x_min, priv->x_max,
+			     priv->y_min, priv->y_max);
+		break;
+	}
+}
+
+static int synaptics_query_hardware(struct psmouse *psmouse)
+{
+	if (synaptics_identify(psmouse))
+		return -1;
+	if (synaptics_model_id(psmouse))
+		return -1;
+	if (synaptics_firmware_id(psmouse))
+		return -1;
+	if (synaptics_query_modes(psmouse))
+		return -1;
+	if (synaptics_capability(psmouse))
+		return -1;
+	if (synaptics_resolution(psmouse))
+		return -1;
+
+	synaptics_apply_quirks(psmouse);
+
+	return 0;
+}
+
+static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
+{
+	static unsigned char param = 0xc8;
+	struct synaptics_data *priv = psmouse->private;
+
+	if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
+	      SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)))
+		return 0;
+
+	if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
+		return -1;
+
+	if (ps2_command(&psmouse->ps2dev, &param, PSMOUSE_CMD_SETRATE))
+		return -1;
+
+	/* Advanced gesture mode also sends multi finger data */
+	priv->capabilities |= BIT(1);
+
+	return 0;
+}
+
+static int synaptics_set_mode(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+
+	priv->mode = 0;
+	if (priv->absolute_mode)
+		priv->mode |= SYN_BIT_ABSOLUTE_MODE;
+	if (priv->disable_gesture)
+		priv->mode |= SYN_BIT_DISABLE_GESTURE;
+	if (psmouse->rate >= 80)
+		priv->mode |= SYN_BIT_HIGH_RATE;
+	if (SYN_CAP_EXTENDED(priv->capabilities))
+		priv->mode |= SYN_BIT_W_MODE;
+
+	if (synaptics_mode_cmd(psmouse, priv->mode))
+		return -1;
+
+	if (priv->absolute_mode &&
+	    synaptics_set_advanced_gesture_mode(psmouse)) {
+		psmouse_err(psmouse, "Advanced gesture mode init failed.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
+{
+	struct synaptics_data *priv = psmouse->private;
+
+	if (rate >= 80) {
+		priv->mode |= SYN_BIT_HIGH_RATE;
+		psmouse->rate = 80;
+	} else {
+		priv->mode &= ~SYN_BIT_HIGH_RATE;
+		psmouse->rate = 40;
+	}
+
+	synaptics_mode_cmd(psmouse, priv->mode);
+}
+
+/*****************************************************************************
+ *	Synaptics pass-through PS/2 port support
+ ****************************************************************************/
+static int synaptics_pt_write(struct serio *serio, unsigned char c)
+{
+	struct psmouse *parent = serio_get_drvdata(serio->parent);
+	char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
+
+	if (psmouse_sliced_command(parent, c))
+		return -1;
+	if (ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE))
+		return -1;
+	return 0;
+}
+
+static int synaptics_pt_start(struct serio *serio)
+{
+	struct psmouse *parent = serio_get_drvdata(serio->parent);
+	struct synaptics_data *priv = parent->private;
+
+	serio_pause_rx(parent->ps2dev.serio);
+	priv->pt_port = serio;
+	serio_continue_rx(parent->ps2dev.serio);
+
+	return 0;
+}
+
+static void synaptics_pt_stop(struct serio *serio)
+{
+	struct psmouse *parent = serio_get_drvdata(serio->parent);
+	struct synaptics_data *priv = parent->private;
+
+	serio_pause_rx(parent->ps2dev.serio);
+	priv->pt_port = NULL;
+	serio_continue_rx(parent->ps2dev.serio);
+}
+
+static int synaptics_is_pt_packet(unsigned char *buf)
+{
+	return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
+}
+
+static void synaptics_pass_pt_packet(struct psmouse *psmouse,
+				     struct serio *ptport,
+				     unsigned char *packet)
+{
+	struct synaptics_data *priv = psmouse->private;
+	struct psmouse *child = serio_get_drvdata(ptport);
+
+	if (child && child->state == PSMOUSE_ACTIVATED) {
+		serio_interrupt(ptport, packet[1] | priv->pt_buttons, 0);
+		serio_interrupt(ptport, packet[4], 0);
+		serio_interrupt(ptport, packet[5], 0);
+		if (child->pktsize == 4)
+			serio_interrupt(ptport, packet[2], 0);
+	} else {
+		serio_interrupt(ptport, packet[1], 0);
+	}
+}
+
+static void synaptics_pt_activate(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	struct psmouse *child = serio_get_drvdata(priv->pt_port);
+
+	/* adjust the touchpad to child's choice of protocol */
+	if (child) {
+		if (child->pktsize == 4)
+			priv->mode |= SYN_BIT_FOUR_BYTE_CLIENT;
+		else
+			priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT;
+
+		if (synaptics_mode_cmd(psmouse, priv->mode))
+			psmouse_warn(psmouse,
+				     "failed to switch guest protocol\n");
+	}
+}
+
+static void synaptics_pt_create(struct psmouse *psmouse)
+{
+	struct serio *serio;
+
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!serio) {
+		psmouse_err(psmouse,
+			    "not enough memory for pass-through port\n");
+		return;
+	}
+
+	serio->id.type = SERIO_PS_PSTHRU;
+	strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
+	strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
+	serio->write = synaptics_pt_write;
+	serio->start = synaptics_pt_start;
+	serio->stop = synaptics_pt_stop;
+	serio->parent = psmouse->ps2dev.serio;
+
+	psmouse->pt_activate = synaptics_pt_activate;
+
+	psmouse_info(psmouse, "serio: %s port at %s\n",
+		     serio->name, psmouse->phys);
+	serio_register_port(serio);
+}
+
+/*****************************************************************************
+ *	Functions to interpret the absolute mode packets
+ ****************************************************************************/
+
+static void synaptics_parse_agm(const unsigned char buf[],
+				struct synaptics_data *priv,
+				struct synaptics_hw_state *hw)
+{
+	struct synaptics_hw_state *agm = &priv->agm;
+	int agm_packet_type;
+
+	agm_packet_type = (buf[5] & 0x30) >> 4;
+	switch (agm_packet_type) {
+	case 1:
+		/* Gesture packet: (x, y, z) half resolution */
+		agm->w = hw->w;
+		agm->x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
+		agm->y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
+		agm->z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
+		break;
+
+	case 2:
+		/* AGM-CONTACT packet: we are only interested in the count */
+		priv->agm_count = buf[1];
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void synaptics_parse_ext_buttons(const unsigned char buf[],
+					struct synaptics_data *priv,
+					struct synaptics_hw_state *hw)
+{
+	unsigned int ext_bits =
+		(SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
+	unsigned int ext_mask = GENMASK(ext_bits - 1, 0);
+
+	hw->ext_buttons = buf[4] & ext_mask;
+	hw->ext_buttons |= (buf[5] & ext_mask) << ext_bits;
+}
+
+static int synaptics_parse_hw_state(const unsigned char buf[],
+				    struct synaptics_data *priv,
+				    struct synaptics_hw_state *hw)
+{
+	memset(hw, 0, sizeof(struct synaptics_hw_state));
+
+	if (SYN_MODEL_NEWABS(priv->model_id)) {
+		hw->w = (((buf[0] & 0x30) >> 2) |
+			 ((buf[0] & 0x04) >> 1) |
+			 ((buf[3] & 0x04) >> 2));
+
+		if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
+			SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) &&
+		    hw->w == 2) {
+			synaptics_parse_agm(buf, priv, hw);
+			return 1;
+		}
+
+		hw->x = (((buf[3] & 0x10) << 8) |
+			 ((buf[1] & 0x0f) << 8) |
+			 buf[4]);
+		hw->y = (((buf[3] & 0x20) << 7) |
+			 ((buf[1] & 0xf0) << 4) |
+			 buf[5]);
+		hw->z = buf[2];
+
+		hw->left  = (buf[0] & 0x01) ? 1 : 0;
+		hw->right = (buf[0] & 0x02) ? 1 : 0;
+
+		if (priv->is_forcepad) {
+			/*
+			 * ForcePads, like Clickpads, use middle button
+			 * bits to report primary button clicks.
+			 * Unfortunately they report primary button not
+			 * only when user presses on the pad above certain
+			 * threshold, but also when there are more than one
+			 * finger on the touchpad, which interferes with
+			 * out multi-finger gestures.
+			 */
+			if (hw->z == 0) {
+				/* No contacts */
+				priv->press = priv->report_press = false;
+			} else if (hw->w >= 4 && ((buf[0] ^ buf[3]) & 0x01)) {
+				/*
+				 * Single-finger touch with pressure above
+				 * the threshold. If pressure stays long
+				 * enough, we'll start reporting primary
+				 * button. We rely on the device continuing
+				 * sending data even if finger does not
+				 * move.
+				 */
+				if  (!priv->press) {
+					priv->press_start = jiffies;
+					priv->press = true;
+				} else if (time_after(jiffies,
+						priv->press_start +
+							msecs_to_jiffies(50))) {
+					priv->report_press = true;
+				}
+			} else {
+				priv->press = false;
+			}
+
+			hw->left = priv->report_press;
+
+		} else if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
+			/*
+			 * Clickpad's button is transmitted as middle button,
+			 * however, since it is primary button, we will report
+			 * it as BTN_LEFT.
+			 */
+			hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
+
+		} else if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
+			hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
+			if (hw->w == 2)
+				hw->scroll = (signed char)(buf[1]);
+		}
+
+		if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+			hw->up   = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
+			hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
+		}
+
+		if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 0 &&
+		    ((buf[0] ^ buf[3]) & 0x02)) {
+			synaptics_parse_ext_buttons(buf, priv, hw);
+		}
+	} else {
+		hw->x = (((buf[1] & 0x1f) << 8) | buf[2]);
+		hw->y = (((buf[4] & 0x1f) << 8) | buf[5]);
+
+		hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F));
+		hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1));
+
+		hw->left  = (buf[0] & 0x01) ? 1 : 0;
+		hw->right = (buf[0] & 0x02) ? 1 : 0;
+	}
+
+	/*
+	 * Convert wrap-around values to negative. (X|Y)_MAX_POSITIVE
+	 * is used by some firmware to indicate a finger at the edge of
+	 * the touchpad whose precise position cannot be determined, so
+	 * convert these values to the maximum axis value.
+	 */
+	if (hw->x > X_MAX_POSITIVE)
+		hw->x -= 1 << ABS_POS_BITS;
+	else if (hw->x == X_MAX_POSITIVE)
+		hw->x = XMAX;
+
+	if (hw->y > Y_MAX_POSITIVE)
+		hw->y -= 1 << ABS_POS_BITS;
+	else if (hw->y == Y_MAX_POSITIVE)
+		hw->y = YMAX;
+
+	return 0;
+}
+
+static void synaptics_report_semi_mt_slot(struct input_dev *dev, int slot,
+					  bool active, int x, int y)
+{
+	input_mt_slot(dev, slot);
+	input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
+	if (active) {
+		input_report_abs(dev, ABS_MT_POSITION_X, x);
+		input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(y));
+	}
+}
+
+static void synaptics_report_semi_mt_data(struct input_dev *dev,
+					  const struct synaptics_hw_state *a,
+					  const struct synaptics_hw_state *b,
+					  int num_fingers)
+{
+	if (num_fingers >= 2) {
+		synaptics_report_semi_mt_slot(dev, 0, true, min(a->x, b->x),
+					      min(a->y, b->y));
+		synaptics_report_semi_mt_slot(dev, 1, true, max(a->x, b->x),
+					      max(a->y, b->y));
+	} else if (num_fingers == 1) {
+		synaptics_report_semi_mt_slot(dev, 0, true, a->x, a->y);
+		synaptics_report_semi_mt_slot(dev, 1, false, 0, 0);
+	} else {
+		synaptics_report_semi_mt_slot(dev, 0, false, 0, 0);
+		synaptics_report_semi_mt_slot(dev, 1, false, 0, 0);
+	}
+}
+
+static void synaptics_report_ext_buttons(struct psmouse *psmouse,
+					 const struct synaptics_hw_state *hw)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct synaptics_data *priv = psmouse->private;
+	int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
+	char buf[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	int i;
+
+	if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
+		return;
+
+	/* Bug in FW 8.1 & 8.2, buttons are reported only when ExtBit is 1 */
+	if ((SYN_ID_FULL(priv->identity) == 0x801 ||
+	     SYN_ID_FULL(priv->identity) == 0x802) &&
+	    !((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02))
+		return;
+
+	if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) {
+		for (i = 0; i < ext_bits; i++) {
+			input_report_key(dev, BTN_0 + 2 * i,
+				hw->ext_buttons & (1 << i));
+			input_report_key(dev, BTN_1 + 2 * i,
+				hw->ext_buttons & (1 << (i + ext_bits)));
+		}
+		return;
+	}
+
+	/*
+	 * This generation of touchpads has the trackstick buttons
+	 * physically wired to the touchpad. Re-route them through
+	 * the pass-through interface.
+	 */
+	if (!priv->pt_port)
+		return;
+
+	/* The trackstick expects at most 3 buttons */
+	priv->pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons)      |
+			   SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
+			   SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
+
+	synaptics_pass_pt_packet(psmouse, priv->pt_port, buf);
+}
+
+static void synaptics_report_buttons(struct psmouse *psmouse,
+				     const struct synaptics_hw_state *hw)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct synaptics_data *priv = psmouse->private;
+
+	input_report_key(dev, BTN_LEFT, hw->left);
+	input_report_key(dev, BTN_RIGHT, hw->right);
+
+	if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+		input_report_key(dev, BTN_MIDDLE, hw->middle);
+
+	if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+		input_report_key(dev, BTN_FORWARD, hw->up);
+		input_report_key(dev, BTN_BACK, hw->down);
+	}
+
+	synaptics_report_ext_buttons(psmouse, hw);
+}
+
+static void synaptics_report_mt_data(struct psmouse *psmouse,
+				     const struct synaptics_hw_state *sgm,
+				     int num_fingers)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct synaptics_data *priv = psmouse->private;
+	const struct synaptics_hw_state *hw[2] = { sgm, &priv->agm };
+	struct input_mt_pos pos[2];
+	int slot[2], nsemi, i;
+
+	nsemi = clamp_val(num_fingers, 0, 2);
+
+	for (i = 0; i < nsemi; i++) {
+		pos[i].x = hw[i]->x;
+		pos[i].y = synaptics_invert_y(hw[i]->y);
+	}
+
+	input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res);
+
+	for (i = 0; i < nsemi; i++) {
+		input_mt_slot(dev, slot[i]);
+		input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+		input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x);
+		input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y);
+		input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z);
+	}
+
+	input_mt_drop_unused(dev);
+
+	/* Don't use active slot count to generate BTN_TOOL events. */
+	input_mt_report_pointer_emulation(dev, false);
+
+	/* Send the number of fingers reported by touchpad itself. */
+	input_mt_report_finger_count(dev, num_fingers);
+
+	synaptics_report_buttons(psmouse, sgm);
+
+	input_sync(dev);
+}
+
+static void synaptics_image_sensor_process(struct psmouse *psmouse,
+					   struct synaptics_hw_state *sgm)
+{
+	struct synaptics_data *priv = psmouse->private;
+	int num_fingers;
+
+	/*
+	 * Update mt_state using the new finger count and current mt_state.
+	 */
+	if (sgm->z == 0)
+		num_fingers = 0;
+	else if (sgm->w >= 4)
+		num_fingers = 1;
+	else if (sgm->w == 0)
+		num_fingers = 2;
+	else if (sgm->w == 1)
+		num_fingers = priv->agm_count ? priv->agm_count : 3;
+	else
+		num_fingers = 4;
+
+	/* Send resulting input events to user space */
+	synaptics_report_mt_data(psmouse, sgm, num_fingers);
+}
+
+/*
+ *  called for each full received packet from the touchpad
+ */
+static void synaptics_process_packet(struct psmouse *psmouse)
+{
+	struct input_dev *dev = psmouse->dev;
+	struct synaptics_data *priv = psmouse->private;
+	struct synaptics_hw_state hw;
+	int num_fingers;
+	int finger_width;
+
+	if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
+		return;
+
+	if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+		synaptics_image_sensor_process(psmouse, &hw);
+		return;
+	}
+
+	if (hw.scroll) {
+		priv->scroll += hw.scroll;
+
+		while (priv->scroll >= 4) {
+			input_report_key(dev, BTN_BACK, !hw.down);
+			input_sync(dev);
+			input_report_key(dev, BTN_BACK, hw.down);
+			input_sync(dev);
+			priv->scroll -= 4;
+		}
+		while (priv->scroll <= -4) {
+			input_report_key(dev, BTN_FORWARD, !hw.up);
+			input_sync(dev);
+			input_report_key(dev, BTN_FORWARD, hw.up);
+			input_sync(dev);
+			priv->scroll += 4;
+		}
+		return;
+	}
+
+	if (hw.z > 0 && hw.x > 1) {
+		num_fingers = 1;
+		finger_width = 5;
+		if (SYN_CAP_EXTENDED(priv->capabilities)) {
+			switch (hw.w) {
+			case 0 ... 1:
+				if (SYN_CAP_MULTIFINGER(priv->capabilities))
+					num_fingers = hw.w + 2;
+				break;
+			case 2:
+				if (SYN_MODEL_PEN(priv->model_id))
+					;   /* Nothing, treat a pen as a single finger */
+				break;
+			case 4 ... 15:
+				if (SYN_CAP_PALMDETECT(priv->capabilities))
+					finger_width = hw.w;
+				break;
+			}
+		}
+	} else {
+		num_fingers = 0;
+		finger_width = 0;
+	}
+
+	if (cr48_profile_sensor) {
+		synaptics_report_mt_data(psmouse, &hw, num_fingers);
+		return;
+	}
+
+	if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
+		synaptics_report_semi_mt_data(dev, &hw, &priv->agm,
+					      num_fingers);
+
+	/* Post events
+	 * BTN_TOUCH has to be first as mousedev relies on it when doing
+	 * absolute -> relative conversion
+	 */
+	if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1);
+	if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0);
+
+	if (num_fingers > 0) {
+		input_report_abs(dev, ABS_X, hw.x);
+		input_report_abs(dev, ABS_Y, synaptics_invert_y(hw.y));
+	}
+	input_report_abs(dev, ABS_PRESSURE, hw.z);
+
+	if (SYN_CAP_PALMDETECT(priv->capabilities))
+		input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
+
+	input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
+	if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
+		input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
+		input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
+	}
+
+	synaptics_report_buttons(psmouse, &hw);
+
+	input_sync(dev);
+}
+
+static int synaptics_validate_byte(struct psmouse *psmouse,
+				   int idx, unsigned char pkt_type)
+{
+	static const unsigned char newabs_mask[]	= { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
+	static const unsigned char newabs_rel_mask[]	= { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
+	static const unsigned char newabs_rslt[]	= { 0x80, 0x00, 0x00, 0xC0, 0x00 };
+	static const unsigned char oldabs_mask[]	= { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
+	static const unsigned char oldabs_rslt[]	= { 0xC0, 0x00, 0x00, 0x80, 0x00 };
+	const char *packet = psmouse->packet;
+
+	if (idx < 0 || idx > 4)
+		return 0;
+
+	switch (pkt_type) {
+
+	case SYN_NEWABS:
+	case SYN_NEWABS_RELAXED:
+		return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx];
+
+	case SYN_NEWABS_STRICT:
+		return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx];
+
+	case SYN_OLDABS:
+		return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx];
+
+	default:
+		psmouse_err(psmouse, "unknown packet type %d\n", pkt_type);
+		return 0;
+	}
+}
+
+static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse)
+{
+	int i;
+
+	for (i = 0; i < 5; i++)
+		if (!synaptics_validate_byte(psmouse, i, SYN_NEWABS_STRICT)) {
+			psmouse_info(psmouse, "using relaxed packet validation\n");
+			return SYN_NEWABS_RELAXED;
+		}
+
+	return SYN_NEWABS_STRICT;
+}
+
+static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+
+	if (psmouse->pktcnt >= 6) { /* Full packet received */
+		if (unlikely(priv->pkt_type == SYN_NEWABS))
+			priv->pkt_type = synaptics_detect_pkt_type(psmouse);
+
+		if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
+		    synaptics_is_pt_packet(psmouse->packet)) {
+			if (priv->pt_port)
+				synaptics_pass_pt_packet(psmouse, priv->pt_port,
+							 psmouse->packet);
+		} else
+			synaptics_process_packet(psmouse);
+
+		return PSMOUSE_FULL_PACKET;
+	}
+
+	return synaptics_validate_byte(psmouse, psmouse->pktcnt - 1, priv->pkt_type) ?
+		PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+}
+
+/*****************************************************************************
+ *	Driver initialization/cleanup functions
+ ****************************************************************************/
+static void set_abs_position_params(struct input_dev *dev,
+				    struct synaptics_data *priv, int x_code,
+				    int y_code)
+{
+	int x_min = priv->x_min ?: XMIN_NOMINAL;
+	int x_max = priv->x_max ?: XMAX_NOMINAL;
+	int y_min = priv->y_min ?: YMIN_NOMINAL;
+	int y_max = priv->y_max ?: YMAX_NOMINAL;
+	int fuzz = SYN_CAP_REDUCED_FILTERING(priv->ext_cap_0c) ?
+			SYN_REDUCED_FILTER_FUZZ : 0;
+
+	input_set_abs_params(dev, x_code, x_min, x_max, fuzz, 0);
+	input_set_abs_params(dev, y_code, y_min, y_max, fuzz, 0);
+	input_abs_set_res(dev, x_code, priv->x_res);
+	input_abs_set_res(dev, y_code, priv->y_res);
+}
+
+static void set_input_params(struct psmouse *psmouse,
+			     struct synaptics_data *priv)
+{
+	struct input_dev *dev = psmouse->dev;
+	int i;
+
+	/* Things that apply to both modes */
+	__set_bit(INPUT_PROP_POINTER, dev->propbit);
+	__set_bit(EV_KEY, dev->evbit);
+	__set_bit(BTN_LEFT, dev->keybit);
+	__set_bit(BTN_RIGHT, dev->keybit);
+
+	if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+		__set_bit(BTN_MIDDLE, dev->keybit);
+
+	if (!priv->absolute_mode) {
+		/* Relative mode */
+		__set_bit(EV_REL, dev->evbit);
+		__set_bit(REL_X, dev->relbit);
+		__set_bit(REL_Y, dev->relbit);
+		return;
+	}
+
+	/* Absolute mode */
+	__set_bit(EV_ABS, dev->evbit);
+	set_abs_position_params(dev, priv, ABS_X, ABS_Y);
+	input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+
+	if (cr48_profile_sensor)
+		input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+	if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+		set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
+					ABS_MT_POSITION_Y);
+		/* Image sensors can report per-contact pressure */
+		input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+		input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK);
+
+		/* Image sensors can signal 4 and 5 finger clicks */
+		__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
+		__set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
+	} else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
+		set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
+					ABS_MT_POSITION_Y);
+		/*
+		 * Profile sensor in CR-48 tracks contacts reasonably well,
+		 * other non-image sensors with AGM use semi-mt.
+		 */
+		input_mt_init_slots(dev, 2,
+				    INPUT_MT_POINTER |
+				    (cr48_profile_sensor ?
+					INPUT_MT_TRACK : INPUT_MT_SEMI_MT));
+	}
+
+	if (SYN_CAP_PALMDETECT(priv->capabilities))
+		input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
+
+	__set_bit(BTN_TOUCH, dev->keybit);
+	__set_bit(BTN_TOOL_FINGER, dev->keybit);
+
+	if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
+		__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+		__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+	}
+
+	if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||
+	    SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
+		__set_bit(BTN_FORWARD, dev->keybit);
+		__set_bit(BTN_BACK, dev->keybit);
+	}
+
+	if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10))
+		for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+			__set_bit(BTN_0 + i, dev->keybit);
+
+	__clear_bit(EV_REL, dev->evbit);
+	__clear_bit(REL_X, dev->relbit);
+	__clear_bit(REL_Y, dev->relbit);
+
+	if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
+		__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
+		if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
+		    !SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10))
+			__set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit);
+		/* Clickpads report only left button */
+		__clear_bit(BTN_RIGHT, dev->keybit);
+		__clear_bit(BTN_MIDDLE, dev->keybit);
+	}
+}
+
+static ssize_t synaptics_show_disable_gesture(struct psmouse *psmouse,
+					      void *data, char *buf)
+{
+	struct synaptics_data *priv = psmouse->private;
+
+	return sprintf(buf, "%c\n", priv->disable_gesture ? '1' : '0');
+}
+
+static ssize_t synaptics_set_disable_gesture(struct psmouse *psmouse,
+					     void *data, const char *buf,
+					     size_t len)
+{
+	struct synaptics_data *priv = psmouse->private;
+	unsigned int value;
+	int err;
+
+	err = kstrtouint(buf, 10, &value);
+	if (err)
+		return err;
+
+	if (value > 1)
+		return -EINVAL;
+
+	if (value == priv->disable_gesture)
+		return len;
+
+	priv->disable_gesture = value;
+	if (value)
+		priv->mode |= SYN_BIT_DISABLE_GESTURE;
+	else
+		priv->mode &= ~SYN_BIT_DISABLE_GESTURE;
+
+	if (synaptics_mode_cmd(psmouse, priv->mode))
+		return -EIO;
+
+	return len;
+}
+
+PSMOUSE_DEFINE_ATTR(disable_gesture, S_IWUSR | S_IRUGO, NULL,
+		    synaptics_show_disable_gesture,
+		    synaptics_set_disable_gesture);
+
+static void synaptics_disconnect(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+
+	if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity))
+		device_remove_file(&psmouse->ps2dev.serio->dev,
+				   &psmouse_attr_disable_gesture.dattr);
+
+	synaptics_reset(psmouse);
+	kfree(priv);
+	psmouse->private = NULL;
+}
+
+static int synaptics_reconnect(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	struct synaptics_data old_priv = *priv;
+	unsigned char param[2];
+	int retry = 0;
+	int error;
+
+	do {
+		psmouse_reset(psmouse);
+		if (retry) {
+			/*
+			 * On some boxes, right after resuming, the touchpad
+			 * needs some time to finish initializing (I assume
+			 * it needs time to calibrate) and start responding
+			 * to Synaptics-specific queries, so let's wait a
+			 * bit.
+			 */
+			ssleep(1);
+		}
+		ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETID);
+		error = synaptics_detect(psmouse, 0);
+	} while (error && ++retry < 3);
+
+	if (error)
+		return -1;
+
+	if (retry > 1)
+		psmouse_dbg(psmouse, "reconnected after %d tries\n", retry);
+
+	if (synaptics_query_hardware(psmouse)) {
+		psmouse_err(psmouse, "Unable to query device.\n");
+		return -1;
+	}
+
+	if (synaptics_set_mode(psmouse)) {
+		psmouse_err(psmouse, "Unable to initialize device.\n");
+		return -1;
+	}
+
+	if (old_priv.identity != priv->identity ||
+	    old_priv.model_id != priv->model_id ||
+	    old_priv.capabilities != priv->capabilities ||
+	    old_priv.ext_cap != priv->ext_cap) {
+		psmouse_err(psmouse,
+			    "hardware appears to be different: id(%ld-%ld), model(%ld-%ld), caps(%lx-%lx), ext(%lx-%lx).\n",
+			    old_priv.identity, priv->identity,
+			    old_priv.model_id, priv->model_id,
+			    old_priv.capabilities, priv->capabilities,
+			    old_priv.ext_cap, priv->ext_cap);
+		return -1;
+	}
+
+	return 0;
+}
+
+static bool impaired_toshiba_kbc;
+
+static const struct dmi_system_id toshiba_dmi_table[] __initconst = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+	{
+		/* Toshiba Satellite */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"),
+		},
+	},
+	{
+		/* Toshiba Dynabook */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"),
+		},
+	},
+	{
+		/* Toshiba Portege M300 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"),
+		},
+
+	},
+	{
+		/* Toshiba Portege M300 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "Version 1.0"),
+		},
+
+	},
+#endif
+	{ }
+};
+
+static bool broken_olpc_ec;
+
+static const struct dmi_system_id olpc_dmi_table[] __initconst = {
+#if defined(CONFIG_DMI) && defined(CONFIG_OLPC)
+	{
+		/* OLPC XO-1 or XO-1.5 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "OLPC"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "XO"),
+		},
+	},
+#endif
+	{ }
+};
+
+static const struct dmi_system_id __initconst cr48_dmi_table[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+	{
+		/* Cr-48 Chromebook (Codename Mario) */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "IEC"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
+		},
+	},
+#endif
+	{ }
+};
+
+void __init synaptics_module_init(void)
+{
+	impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
+	broken_olpc_ec = dmi_check_system(olpc_dmi_table);
+	cr48_profile_sensor = dmi_check_system(cr48_dmi_table);
+}
+
+static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+{
+	struct synaptics_data *priv;
+	int err = -1;
+
+	/*
+	 * The OLPC XO has issues with Synaptics' absolute mode; the constant
+	 * packet spew overloads the EC such that key presses on the keyboard
+	 * are missed.  Given that, don't even attempt to use Absolute mode.
+	 * Relative mode seems to work just fine.
+	 */
+	if (absolute_mode && broken_olpc_ec) {
+		psmouse_info(psmouse,
+			     "OLPC XO detected, not enabling Synaptics protocol.\n");
+		return -ENODEV;
+	}
+
+	psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	psmouse_reset(psmouse);
+
+	if (synaptics_query_hardware(psmouse)) {
+		psmouse_err(psmouse, "Unable to query device.\n");
+		goto init_fail;
+	}
+
+	priv->absolute_mode = absolute_mode;
+	if (SYN_ID_DISGEST_SUPPORTED(priv->identity))
+		priv->disable_gesture = true;
+
+	/*
+	 * Unfortunately ForcePad capability is not exported over PS/2,
+	 * so we have to resort to checking PNP IDs.
+	 */
+	priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids);
+
+	if (synaptics_set_mode(psmouse)) {
+		psmouse_err(psmouse, "Unable to initialize device.\n");
+		goto init_fail;
+	}
+
+	priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
+
+	psmouse_info(psmouse,
+		     "Touchpad model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx/%#lx, board id: %lu, fw id: %lu\n",
+		     SYN_ID_MODEL(priv->identity),
+		     SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
+		     priv->model_id,
+		     priv->capabilities, priv->ext_cap, priv->ext_cap_0c,
+		     priv->ext_cap_10, priv->board_id, priv->firmware_id);
+
+	set_input_params(psmouse, priv);
+
+	/*
+	 * Encode touchpad model so that it can be used to set
+	 * input device->id.version and be visible to userspace.
+	 * Because version is __u16 we have to drop something.
+	 * Hardware info bits seem to be good candidates as they
+	 * are documented to be for Synaptics corp. internal use.
+	 */
+	psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) |
+			  (priv->model_id & 0x000000ff);
+
+	if (absolute_mode) {
+		psmouse->protocol_handler = synaptics_process_byte;
+		psmouse->pktsize = 6;
+	} else {
+		/* Relative mode follows standard PS/2 mouse protocol */
+		psmouse->protocol_handler = psmouse_process_byte;
+		psmouse->pktsize = 3;
+	}
+
+	psmouse->set_rate = synaptics_set_rate;
+	psmouse->disconnect = synaptics_disconnect;
+	psmouse->reconnect = synaptics_reconnect;
+	psmouse->cleanup = synaptics_reset;
+	/* Synaptics can usually stay in sync without extra help */
+	psmouse->resync_time = 0;
+
+	if (SYN_CAP_PASS_THROUGH(priv->capabilities))
+		synaptics_pt_create(psmouse);
+
+	/*
+	 * Toshiba's KBC seems to have trouble handling data from
+	 * Synaptics at full rate.  Switch to a lower rate (roughly
+	 * the same rate as a standard PS/2 mouse).
+	 */
+	if (psmouse->rate >= 80 && impaired_toshiba_kbc) {
+		psmouse_info(psmouse,
+			     "Toshiba %s detected, limiting rate to 40pps.\n",
+			     dmi_get_system_info(DMI_PRODUCT_NAME));
+		psmouse->rate = 40;
+	}
+
+	if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) {
+		err = device_create_file(&psmouse->ps2dev.serio->dev,
+					 &psmouse_attr_disable_gesture.dattr);
+		if (err) {
+			psmouse_err(psmouse,
+				    "Failed to create disable_gesture attribute (%d)",
+				    err);
+			goto init_fail;
+		}
+	}
+
+	return 0;
+
+ init_fail:
+	kfree(priv);
+	return err;
+}
+
+int synaptics_init(struct psmouse *psmouse)
+{
+	return __synaptics_init(psmouse, true);
+}
+
+int synaptics_init_relative(struct psmouse *psmouse)
+{
+	return __synaptics_init(psmouse, false);
+}
+
+#else /* CONFIG_MOUSE_PS2_SYNAPTICS */
+
+void __init synaptics_module_init(void)
+{
+}
+
+int synaptics_init(struct psmouse *psmouse)
+{
+	return -ENOSYS;
+}
+
+#endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
new file mode 100644
index 0000000..56faa7e
--- /dev/null
+++ b/drivers/input/mouse/synaptics.h
@@ -0,0 +1,208 @@
+/*
+ * Synaptics TouchPad PS/2 mouse driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _SYNAPTICS_H
+#define _SYNAPTICS_H
+
+/* synaptics queries */
+#define SYN_QUE_IDENTIFY		0x00
+#define SYN_QUE_MODES			0x01
+#define SYN_QUE_CAPABILITIES		0x02
+#define SYN_QUE_MODEL			0x03
+#define SYN_QUE_SERIAL_NUMBER_PREFIX	0x06
+#define SYN_QUE_SERIAL_NUMBER_SUFFIX	0x07
+#define SYN_QUE_RESOLUTION		0x08
+#define SYN_QUE_EXT_CAPAB		0x09
+#define SYN_QUE_FIRMWARE_ID		0x0a
+#define SYN_QUE_EXT_CAPAB_0C		0x0c
+#define SYN_QUE_EXT_MAX_COORDS		0x0d
+#define SYN_QUE_EXT_MIN_COORDS		0x0f
+#define SYN_QUE_MEXT_CAPAB_10		0x10
+
+/* synatics modes */
+#define SYN_BIT_ABSOLUTE_MODE		(1 << 7)
+#define SYN_BIT_HIGH_RATE		(1 << 6)
+#define SYN_BIT_SLEEP_MODE		(1 << 3)
+#define SYN_BIT_DISABLE_GESTURE		(1 << 2)
+#define SYN_BIT_FOUR_BYTE_CLIENT	(1 << 1)
+#define SYN_BIT_W_MODE			(1 << 0)
+
+/* synaptics model ID bits */
+#define SYN_MODEL_ROT180(m)		((m) & (1 << 23))
+#define SYN_MODEL_PORTRAIT(m)		((m) & (1 << 22))
+#define SYN_MODEL_SENSOR(m)		(((m) >> 16) & 0x3f)
+#define SYN_MODEL_HARDWARE(m)		(((m) >> 9) & 0x7f)
+#define SYN_MODEL_NEWABS(m)		((m) & (1 << 7))
+#define SYN_MODEL_PEN(m)		((m) & (1 << 6))
+#define SYN_MODEL_SIMPLIC(m)		((m) & (1 << 5))
+#define SYN_MODEL_GEOMETRY(m)		((m) & 0x0f)
+
+/* synaptics capability bits */
+#define SYN_CAP_EXTENDED(c)		((c) & (1 << 23))
+#define SYN_CAP_MIDDLE_BUTTON(c)	((c) & (1 << 18))
+#define SYN_CAP_PASS_THROUGH(c)		((c) & (1 << 7))
+#define SYN_CAP_SLEEP(c)		((c) & (1 << 4))
+#define SYN_CAP_FOUR_BUTTON(c)		((c) & (1 << 3))
+#define SYN_CAP_MULTIFINGER(c)		((c) & (1 << 1))
+#define SYN_CAP_PALMDETECT(c)		((c) & (1 << 0))
+#define SYN_CAP_SUBMODEL_ID(c)		(((c) & 0x00ff00) >> 8)
+#define SYN_EXT_CAP_REQUESTS(c)		(((c) & 0x700000) >> 20)
+#define SYN_CAP_MULTI_BUTTON_NO(ec)	(((ec) & 0x00f000) >> 12)
+#define SYN_CAP_PRODUCT_ID(ec)		(((ec) & 0xff0000) >> 16)
+#define SYN_MEXT_CAP_BIT(m)		((m) & (1 << 1))
+
+/*
+ * The following describes response for the 0x0c query.
+ *
+ * byte	mask	name			meaning
+ * ----	----	-------			------------
+ * 1	0x01	adjustable threshold	capacitive button sensitivity
+ *					can be adjusted
+ * 1	0x02	report max		query 0x0d gives max coord reported
+ * 1	0x04	clearpad		sensor is ClearPad product
+ * 1	0x08	advanced gesture	not particularly meaningful
+ * 1	0x10	clickpad bit 0		1-button ClickPad
+ * 1	0x60	multifinger mode	identifies firmware finger counting
+ *					(not reporting!) algorithm.
+ *					Not particularly meaningful
+ * 1	0x80	covered pad		W clipped to 14, 15 == pad mostly covered
+ * 2	0x01	clickpad bit 1		2-button ClickPad
+ * 2	0x02	deluxe LED controls	touchpad support LED commands
+ *					ala multimedia control bar
+ * 2	0x04	reduced filtering	firmware does less filtering on
+ *					position data, driver should watch
+ *					for noise.
+ * 2	0x08	image sensor		image sensor tracks 5 fingers, but only
+ *					reports 2.
+ * 2	0x01	uniform clickpad	whole clickpad moves instead of being
+ *					hinged at the top.
+ * 2	0x20	report min		query 0x0f gives min coord reported
+ */
+#define SYN_CAP_CLICKPAD(ex0c)		((ex0c) & 0x100000) /* 1-button ClickPad */
+#define SYN_CAP_CLICKPAD2BTN(ex0c)	((ex0c) & 0x000100) /* 2-button ClickPad */
+#define SYN_CAP_MAX_DIMENSIONS(ex0c)	((ex0c) & 0x020000)
+#define SYN_CAP_MIN_DIMENSIONS(ex0c)	((ex0c) & 0x002000)
+#define SYN_CAP_ADV_GESTURE(ex0c)	((ex0c) & 0x080000)
+#define SYN_CAP_REDUCED_FILTERING(ex0c)	((ex0c) & 0x000400)
+#define SYN_CAP_IMAGE_SENSOR(ex0c)	((ex0c) & 0x000800)
+
+/*
+ * The following descibes response for the 0x10 query.
+ *
+ * byte	mask	name			meaning
+ * ----	----	-------			------------
+ * 1	0x01	ext buttons are stick	buttons exported in the extended
+ *					capability are actually meant to be used
+ *					by the tracktick (pass-through).
+ * 1	0x02	SecurePad		the touchpad is a SecurePad, so it
+ *					contains a built-in fingerprint reader.
+ * 1	0xe0	more ext count		how many more extented queries are
+ *					available after this one.
+ * 2	0xff	SecurePad width		the width of the SecurePad fingerprint
+ *					reader.
+ * 3	0xff	SecurePad height	the height of the SecurePad fingerprint
+ *					reader.
+ */
+#define SYN_CAP_EXT_BUTTONS_STICK(ex10)	((ex10) & 0x010000)
+#define SYN_CAP_SECUREPAD(ex10)		((ex10) & 0x020000)
+
+#define SYN_CAP_EXT_BUTTON_STICK_L(eb)	(!!((eb) & 0x01))
+#define SYN_CAP_EXT_BUTTON_STICK_M(eb)	(!!((eb) & 0x02))
+#define SYN_CAP_EXT_BUTTON_STICK_R(eb)	(!!((eb) & 0x04))
+
+/* synaptics modes query bits */
+#define SYN_MODE_ABSOLUTE(m)		((m) & (1 << 7))
+#define SYN_MODE_RATE(m)		((m) & (1 << 6))
+#define SYN_MODE_BAUD_SLEEP(m)		((m) & (1 << 3))
+#define SYN_MODE_DISABLE_GESTURE(m)	((m) & (1 << 2))
+#define SYN_MODE_PACKSIZE(m)		((m) & (1 << 1))
+#define SYN_MODE_WMODE(m)		((m) & (1 << 0))
+
+/* synaptics identify query bits */
+#define SYN_ID_MODEL(i)			(((i) >> 4) & 0x0f)
+#define SYN_ID_MAJOR(i)			((i) & 0x0f)
+#define SYN_ID_MINOR(i)			(((i) >> 16) & 0xff)
+#define SYN_ID_FULL(i)			((SYN_ID_MAJOR(i) << 8) | SYN_ID_MINOR(i))
+#define SYN_ID_IS_SYNAPTICS(i)		((((i) >> 8) & 0xff) == 0x47)
+#define SYN_ID_DISGEST_SUPPORTED(i)	(SYN_ID_MAJOR(i) >= 4)
+
+/* synaptics special commands */
+#define SYN_PS_SET_MODE2		0x14
+#define SYN_PS_CLIENT_CMD		0x28
+
+/* synaptics packet types */
+#define SYN_NEWABS			0
+#define SYN_NEWABS_STRICT		1
+#define SYN_NEWABS_RELAXED		2
+#define SYN_OLDABS			3
+
+/* amount to fuzz position data when touchpad reports reduced filtering */
+#define SYN_REDUCED_FILTER_FUZZ		8
+
+/*
+ * A structure to describe the state of the touchpad hardware (buttons and pad)
+ */
+struct synaptics_hw_state {
+	int x;
+	int y;
+	int z;
+	int w;
+	unsigned int left:1;
+	unsigned int right:1;
+	unsigned int middle:1;
+	unsigned int up:1;
+	unsigned int down:1;
+	unsigned char ext_buttons;
+	signed char scroll;
+};
+
+struct synaptics_data {
+	/* Data read from the touchpad */
+	unsigned long int model_id;		/* Model-ID */
+	unsigned long int firmware_id;		/* Firmware-ID */
+	unsigned long int board_id;		/* Board-ID */
+	unsigned long int capabilities;		/* Capabilities */
+	unsigned long int ext_cap;		/* Extended Capabilities */
+	unsigned long int ext_cap_0c;		/* Ext Caps from 0x0c query */
+	unsigned long int ext_cap_10;		/* Ext Caps from 0x10 query */
+	unsigned long int identity;		/* Identification */
+	unsigned int x_res, y_res;		/* X/Y resolution in units/mm */
+	unsigned int x_max, y_max;		/* Max coordinates (from FW) */
+	unsigned int x_min, y_min;		/* Min coordinates (from FW) */
+
+	unsigned char pkt_type;			/* packet type - old, new, etc */
+	unsigned char mode;			/* current mode byte */
+	int scroll;
+
+	bool absolute_mode;			/* run in Absolute mode */
+	bool disable_gesture;			/* disable gestures */
+
+	struct serio *pt_port;			/* Pass-through serio port */
+	unsigned char pt_buttons;		/* Pass-through buttons */
+
+	/*
+	 * Last received Advanced Gesture Mode (AGM) packet. An AGM packet
+	 * contains position data for a second contact, at half resolution.
+	 */
+	struct synaptics_hw_state agm;
+	unsigned int agm_count;			/* finger count reported by agm */
+
+	/* ForcePad handling */
+	unsigned long				press_start;
+	bool					press;
+	bool					report_press;
+	bool					is_forcepad;
+};
+
+void synaptics_module_init(void);
+int synaptics_detect(struct psmouse *psmouse, bool set_properties);
+int synaptics_init(struct psmouse *psmouse);
+int synaptics_init_relative(struct psmouse *psmouse);
+void synaptics_reset(struct psmouse *psmouse);
+
+#endif /* _SYNAPTICS_H */
diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c
new file mode 100644
index 0000000..aa7c5da
--- /dev/null
+++ b/drivers/input/mouse/synaptics_i2c.c
@@ -0,0 +1,672 @@
+/*
+ * Synaptics touchpad with I2C interface
+ *
+ * Copyright (C) 2009 Compulab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ * Igor Grinberg <grinberg@compulab.co.il>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+
+#define DRIVER_NAME		"synaptics_i2c"
+/* maximum product id is 15 characters */
+#define PRODUCT_ID_LENGTH	15
+#define REGISTER_LENGTH		8
+
+/*
+ * after soft reset, we should wait for 1 ms
+ * before the device becomes operational
+ */
+#define SOFT_RESET_DELAY_MS	3
+/* and after hard reset, we should wait for max 500ms */
+#define HARD_RESET_DELAY_MS	500
+
+/* Registers by SMBus address */
+#define PAGE_SEL_REG		0xff
+#define DEVICE_STATUS_REG	0x09
+
+/* Registers by RMI address */
+#define DEV_CONTROL_REG		0x0000
+#define INTERRUPT_EN_REG	0x0001
+#define ERR_STAT_REG		0x0002
+#define INT_REQ_STAT_REG	0x0003
+#define DEV_COMMAND_REG		0x0004
+
+#define RMI_PROT_VER_REG	0x0200
+#define MANUFACT_ID_REG		0x0201
+#define PHYS_INT_VER_REG	0x0202
+#define PROD_PROPERTY_REG	0x0203
+#define INFO_QUERY_REG0		0x0204
+#define INFO_QUERY_REG1		(INFO_QUERY_REG0 + 1)
+#define INFO_QUERY_REG2		(INFO_QUERY_REG0 + 2)
+#define INFO_QUERY_REG3		(INFO_QUERY_REG0 + 3)
+
+#define PRODUCT_ID_REG0		0x0210
+#define PRODUCT_ID_REG1		(PRODUCT_ID_REG0 + 1)
+#define PRODUCT_ID_REG2		(PRODUCT_ID_REG0 + 2)
+#define PRODUCT_ID_REG3		(PRODUCT_ID_REG0 + 3)
+#define PRODUCT_ID_REG4		(PRODUCT_ID_REG0 + 4)
+#define PRODUCT_ID_REG5		(PRODUCT_ID_REG0 + 5)
+#define PRODUCT_ID_REG6		(PRODUCT_ID_REG0 + 6)
+#define PRODUCT_ID_REG7		(PRODUCT_ID_REG0 + 7)
+#define PRODUCT_ID_REG8		(PRODUCT_ID_REG0 + 8)
+#define PRODUCT_ID_REG9		(PRODUCT_ID_REG0 + 9)
+#define PRODUCT_ID_REG10	(PRODUCT_ID_REG0 + 10)
+#define PRODUCT_ID_REG11	(PRODUCT_ID_REG0 + 11)
+#define PRODUCT_ID_REG12	(PRODUCT_ID_REG0 + 12)
+#define PRODUCT_ID_REG13	(PRODUCT_ID_REG0 + 13)
+#define PRODUCT_ID_REG14	(PRODUCT_ID_REG0 + 14)
+#define PRODUCT_ID_REG15	(PRODUCT_ID_REG0 + 15)
+
+#define DATA_REG0		0x0400
+#define ABS_PRESSURE_REG	0x0401
+#define ABS_MSB_X_REG		0x0402
+#define ABS_LSB_X_REG		(ABS_MSB_X_REG + 1)
+#define ABS_MSB_Y_REG		0x0404
+#define ABS_LSB_Y_REG		(ABS_MSB_Y_REG + 1)
+#define REL_X_REG		0x0406
+#define REL_Y_REG		0x0407
+
+#define DEV_QUERY_REG0		0x1000
+#define DEV_QUERY_REG1		(DEV_QUERY_REG0 + 1)
+#define DEV_QUERY_REG2		(DEV_QUERY_REG0 + 2)
+#define DEV_QUERY_REG3		(DEV_QUERY_REG0 + 3)
+#define DEV_QUERY_REG4		(DEV_QUERY_REG0 + 4)
+#define DEV_QUERY_REG5		(DEV_QUERY_REG0 + 5)
+#define DEV_QUERY_REG6		(DEV_QUERY_REG0 + 6)
+#define DEV_QUERY_REG7		(DEV_QUERY_REG0 + 7)
+#define DEV_QUERY_REG8		(DEV_QUERY_REG0 + 8)
+
+#define GENERAL_2D_CONTROL_REG	0x1041
+#define SENSOR_SENSITIVITY_REG	0x1044
+#define SENS_MAX_POS_MSB_REG	0x1046
+#define SENS_MAX_POS_LSB_REG	(SENS_MAX_POS_UPPER_REG + 1)
+
+/* Register bits */
+/* Device Control Register Bits */
+#define REPORT_RATE_1ST_BIT	6
+
+/* Interrupt Enable Register Bits (INTERRUPT_EN_REG) */
+#define F10_ABS_INT_ENA		0
+#define F10_REL_INT_ENA		1
+#define F20_INT_ENA		2
+
+/* Interrupt Request Register Bits (INT_REQ_STAT_REG | DEVICE_STATUS_REG) */
+#define F10_ABS_INT_REQ		0
+#define F10_REL_INT_REQ		1
+#define F20_INT_REQ		2
+/* Device Status Register Bits (DEVICE_STATUS_REG) */
+#define STAT_CONFIGURED		6
+#define STAT_ERROR		7
+
+/* Device Command Register Bits (DEV_COMMAND_REG) */
+#define RESET_COMMAND		0x01
+#define REZERO_COMMAND		0x02
+
+/* Data Register 0 Bits (DATA_REG0) */
+#define GESTURE			3
+
+/* Device Query Registers Bits */
+/* DEV_QUERY_REG3 */
+#define HAS_PALM_DETECT		1
+#define HAS_MULTI_FING		2
+#define HAS_SCROLLER		4
+#define HAS_2D_SCROLL		5
+
+/* General 2D Control Register Bits (GENERAL_2D_CONTROL_REG) */
+#define NO_DECELERATION		1
+#define REDUCE_REPORTING	3
+#define NO_FILTER		5
+
+/* Function Masks */
+/* Device Control Register Masks (DEV_CONTROL_REG) */
+#define REPORT_RATE_MSK		0xc0
+#define SLEEP_MODE_MSK		0x07
+
+/* Device Sleep Modes */
+#define FULL_AWAKE		0x0
+#define NORMAL_OP		0x1
+#define LOW_PWR_OP		0x2
+#define VERY_LOW_PWR_OP		0x3
+#define SENS_SLEEP		0x4
+#define SLEEP_MOD		0x5
+#define DEEP_SLEEP		0x6
+#define HIBERNATE		0x7
+
+/* Interrupt Register Mask */
+/* (INT_REQ_STAT_REG | DEVICE_STATUS_REG | INTERRUPT_EN_REG) */
+#define INT_ENA_REQ_MSK		0x07
+#define INT_ENA_ABS_MSK		0x01
+#define INT_ENA_REL_MSK		0x02
+#define INT_ENA_F20_MSK		0x04
+
+/* Device Status Register Masks (DEVICE_STATUS_REG) */
+#define CONFIGURED_MSK		0x40
+#define ERROR_MSK		0x80
+
+/* Data Register 0 Masks */
+#define FINGER_WIDTH_MSK	0xf0
+#define GESTURE_MSK		0x08
+#define SENSOR_STATUS_MSK	0x07
+
+/*
+ * MSB Position Register Masks
+ * ABS_MSB_X_REG | ABS_MSB_Y_REG | SENS_MAX_POS_MSB_REG |
+ * DEV_QUERY_REG3 | DEV_QUERY_REG5
+ */
+#define MSB_POSITION_MSK	0x1f
+
+/* Device Query Registers Masks */
+
+/* DEV_QUERY_REG2 */
+#define NUM_EXTRA_POS_MSK	0x07
+
+/* When in IRQ mode read the device every THREAD_IRQ_SLEEP_SECS */
+#define THREAD_IRQ_SLEEP_SECS	2
+#define THREAD_IRQ_SLEEP_MSECS	(THREAD_IRQ_SLEEP_SECS * MSEC_PER_SEC)
+
+/*
+ * When in Polling mode and no data received for NO_DATA_THRES msecs
+ * reduce the polling rate to NO_DATA_SLEEP_MSECS
+ */
+#define NO_DATA_THRES		(MSEC_PER_SEC)
+#define NO_DATA_SLEEP_MSECS	(MSEC_PER_SEC / 4)
+
+/* Control touchpad's No Deceleration option */
+static bool no_decel = true;
+module_param(no_decel, bool, 0644);
+MODULE_PARM_DESC(no_decel, "No Deceleration. Default = 1 (on)");
+
+/* Control touchpad's Reduced Reporting option */
+static bool reduce_report;
+module_param(reduce_report, bool, 0644);
+MODULE_PARM_DESC(reduce_report, "Reduced Reporting. Default = 0 (off)");
+
+/* Control touchpad's No Filter option */
+static bool no_filter;
+module_param(no_filter, bool, 0644);
+MODULE_PARM_DESC(no_filter, "No Filter. Default = 0 (off)");
+
+/*
+ * touchpad Attention line is Active Low and Open Drain,
+ * therefore should be connected to pulled up line
+ * and the irq configuration should be set to Falling Edge Trigger
+ */
+/* Control IRQ / Polling option */
+static bool polling_req;
+module_param(polling_req, bool, 0444);
+MODULE_PARM_DESC(polling_req, "Request Polling. Default = 0 (use irq)");
+
+/* Control Polling Rate */
+static int scan_rate = 80;
+module_param(scan_rate, int, 0644);
+MODULE_PARM_DESC(scan_rate, "Polling rate in times/sec. Default = 80");
+
+/* The main device structure */
+struct synaptics_i2c {
+	struct i2c_client	*client;
+	struct input_dev	*input;
+	struct delayed_work	dwork;
+	spinlock_t		lock;
+	int			no_data_count;
+	int			no_decel_param;
+	int			reduce_report_param;
+	int			no_filter_param;
+	int			scan_rate_param;
+	int			scan_ms;
+};
+
+static inline void set_scan_rate(struct synaptics_i2c *touch, int scan_rate)
+{
+	touch->scan_ms = MSEC_PER_SEC / scan_rate;
+	touch->scan_rate_param = scan_rate;
+}
+
+/*
+ * Driver's initial design makes no race condition possible on i2c bus,
+ * so there is no need in any locking.
+ * Keep it in mind, while playing with the code.
+ */
+static s32 synaptics_i2c_reg_get(struct i2c_client *client, u16 reg)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8);
+	if (ret == 0)
+		ret = i2c_smbus_read_byte_data(client, reg & 0xff);
+
+	return ret;
+}
+
+static s32 synaptics_i2c_reg_set(struct i2c_client *client, u16 reg, u8 val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8);
+	if (ret == 0)
+		ret = i2c_smbus_write_byte_data(client, reg & 0xff, val);
+
+	return ret;
+}
+
+static s32 synaptics_i2c_word_get(struct i2c_client *client, u16 reg)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8);
+	if (ret == 0)
+		ret = i2c_smbus_read_word_data(client, reg & 0xff);
+
+	return ret;
+}
+
+static int synaptics_i2c_config(struct i2c_client *client)
+{
+	int ret, control;
+	u8 int_en;
+
+	/* set Report Rate to Device Highest (>=80) and Sleep to normal */
+	ret = synaptics_i2c_reg_set(client, DEV_CONTROL_REG, 0xc1);
+	if (ret)
+		return ret;
+
+	/* set Interrupt Disable to Func20 / Enable to Func10) */
+	int_en = (polling_req) ? 0 : INT_ENA_ABS_MSK | INT_ENA_REL_MSK;
+	ret = synaptics_i2c_reg_set(client, INTERRUPT_EN_REG, int_en);
+	if (ret)
+		return ret;
+
+	control = synaptics_i2c_reg_get(client, GENERAL_2D_CONTROL_REG);
+	/* No Deceleration */
+	control |= no_decel ? 1 << NO_DECELERATION : 0;
+	/* Reduced Reporting */
+	control |= reduce_report ? 1 << REDUCE_REPORTING : 0;
+	/* No Filter */
+	control |= no_filter ? 1 << NO_FILTER : 0;
+	ret = synaptics_i2c_reg_set(client, GENERAL_2D_CONTROL_REG, control);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int synaptics_i2c_reset_config(struct i2c_client *client)
+{
+	int ret;
+
+	/* Reset the Touchpad */
+	ret = synaptics_i2c_reg_set(client, DEV_COMMAND_REG, RESET_COMMAND);
+	if (ret) {
+		dev_err(&client->dev, "Unable to reset device\n");
+	} else {
+		msleep(SOFT_RESET_DELAY_MS);
+		ret = synaptics_i2c_config(client);
+		if (ret)
+			dev_err(&client->dev, "Unable to config device\n");
+	}
+
+	return ret;
+}
+
+static int synaptics_i2c_check_error(struct i2c_client *client)
+{
+	int status, ret = 0;
+
+	status = i2c_smbus_read_byte_data(client, DEVICE_STATUS_REG) &
+		(CONFIGURED_MSK | ERROR_MSK);
+
+	if (status != CONFIGURED_MSK)
+		ret = synaptics_i2c_reset_config(client);
+
+	return ret;
+}
+
+static bool synaptics_i2c_get_input(struct synaptics_i2c *touch)
+{
+	struct input_dev *input = touch->input;
+	int xy_delta, gesture;
+	s32 data;
+	s8 x_delta, y_delta;
+
+	/* Deal with spontaneous resets and errors */
+	if (synaptics_i2c_check_error(touch->client))
+		return false;
+
+	/* Get Gesture Bit */
+	data = synaptics_i2c_reg_get(touch->client, DATA_REG0);
+	gesture = (data >> GESTURE) & 0x1;
+
+	/*
+	 * Get Relative axes. we have to get them in one shot,
+	 * so we get 2 bytes starting from REL_X_REG.
+	 */
+	xy_delta = synaptics_i2c_word_get(touch->client, REL_X_REG) & 0xffff;
+
+	/* Separate X from Y */
+	x_delta = xy_delta & 0xff;
+	y_delta = (xy_delta >> REGISTER_LENGTH) & 0xff;
+
+	/* Report the button event */
+	input_report_key(input, BTN_LEFT, gesture);
+
+	/* Report the deltas */
+	input_report_rel(input, REL_X, x_delta);
+	input_report_rel(input, REL_Y, -y_delta);
+	input_sync(input);
+
+	return xy_delta || gesture;
+}
+
+static void synaptics_i2c_reschedule_work(struct synaptics_i2c *touch,
+					  unsigned long delay)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&touch->lock, flags);
+
+	mod_delayed_work(system_wq, &touch->dwork, delay);
+
+	spin_unlock_irqrestore(&touch->lock, flags);
+}
+
+static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id)
+{
+	struct synaptics_i2c *touch = dev_id;
+
+	synaptics_i2c_reschedule_work(touch, 0);
+
+	return IRQ_HANDLED;
+}
+
+static void synaptics_i2c_check_params(struct synaptics_i2c *touch)
+{
+	bool reset = false;
+
+	if (scan_rate != touch->scan_rate_param)
+		set_scan_rate(touch, scan_rate);
+
+	if (no_decel != touch->no_decel_param) {
+		touch->no_decel_param = no_decel;
+		reset = true;
+	}
+
+	if (no_filter != touch->no_filter_param) {
+		touch->no_filter_param = no_filter;
+		reset = true;
+	}
+
+	if (reduce_report != touch->reduce_report_param) {
+		touch->reduce_report_param = reduce_report;
+		reset = true;
+	}
+
+	if (reset)
+		synaptics_i2c_reset_config(touch->client);
+}
+
+/* Control the Device polling rate / Work Handler sleep time */
+static unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch,
+						bool have_data)
+{
+	unsigned long delay, nodata_count_thres;
+
+	if (polling_req) {
+		delay = touch->scan_ms;
+		if (have_data) {
+			touch->no_data_count = 0;
+		} else {
+			nodata_count_thres = NO_DATA_THRES / touch->scan_ms;
+			if (touch->no_data_count < nodata_count_thres)
+				touch->no_data_count++;
+			else
+				delay = NO_DATA_SLEEP_MSECS;
+		}
+		return msecs_to_jiffies(delay);
+	} else {
+		delay = msecs_to_jiffies(THREAD_IRQ_SLEEP_MSECS);
+		return round_jiffies_relative(delay);
+	}
+}
+
+/* Work Handler */
+static void synaptics_i2c_work_handler(struct work_struct *work)
+{
+	bool have_data;
+	struct synaptics_i2c *touch =
+			container_of(work, struct synaptics_i2c, dwork.work);
+	unsigned long delay;
+
+	synaptics_i2c_check_params(touch);
+
+	have_data = synaptics_i2c_get_input(touch);
+	delay = synaptics_i2c_adjust_delay(touch, have_data);
+
+	/*
+	 * While interrupt driven, there is no real need to poll the device.
+	 * But touchpads are very sensitive, so there could be errors
+	 * related to physical environment and the attention line isn't
+	 * necessarily asserted. In such case we can lose the touchpad.
+	 * We poll the device once in THREAD_IRQ_SLEEP_SECS and
+	 * if error is detected, we try to reset and reconfigure the touchpad.
+	 */
+	synaptics_i2c_reschedule_work(touch, delay);
+}
+
+static int synaptics_i2c_open(struct input_dev *input)
+{
+	struct synaptics_i2c *touch = input_get_drvdata(input);
+	int ret;
+
+	ret = synaptics_i2c_reset_config(touch->client);
+	if (ret)
+		return ret;
+
+	if (polling_req)
+		synaptics_i2c_reschedule_work(touch,
+				msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
+
+	return 0;
+}
+
+static void synaptics_i2c_close(struct input_dev *input)
+{
+	struct synaptics_i2c *touch = input_get_drvdata(input);
+
+	if (!polling_req)
+		synaptics_i2c_reg_set(touch->client, INTERRUPT_EN_REG, 0);
+
+	cancel_delayed_work_sync(&touch->dwork);
+
+	/* Save some power */
+	synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP);
+}
+
+static void synaptics_i2c_set_input_params(struct synaptics_i2c *touch)
+{
+	struct input_dev *input = touch->input;
+
+	input->name = touch->client->name;
+	input->phys = touch->client->adapter->name;
+	input->id.bustype = BUS_I2C;
+	input->id.version = synaptics_i2c_word_get(touch->client,
+						   INFO_QUERY_REG0);
+	input->dev.parent = &touch->client->dev;
+	input->open = synaptics_i2c_open;
+	input->close = synaptics_i2c_close;
+	input_set_drvdata(input, touch);
+
+	/* Register the device as mouse */
+	__set_bit(EV_REL, input->evbit);
+	__set_bit(REL_X, input->relbit);
+	__set_bit(REL_Y, input->relbit);
+
+	/* Register device's buttons and keys */
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(BTN_LEFT, input->keybit);
+}
+
+static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client)
+{
+	struct synaptics_i2c *touch;
+
+	touch = kzalloc(sizeof(struct synaptics_i2c), GFP_KERNEL);
+	if (!touch)
+		return NULL;
+
+	touch->client = client;
+	touch->no_decel_param = no_decel;
+	touch->scan_rate_param = scan_rate;
+	set_scan_rate(touch, scan_rate);
+	INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler);
+	spin_lock_init(&touch->lock);
+
+	return touch;
+}
+
+static int synaptics_i2c_probe(struct i2c_client *client,
+			       const struct i2c_device_id *dev_id)
+{
+	int ret;
+	struct synaptics_i2c *touch;
+
+	touch = synaptics_i2c_touch_create(client);
+	if (!touch)
+		return -ENOMEM;
+
+	ret = synaptics_i2c_reset_config(client);
+	if (ret)
+		goto err_mem_free;
+
+	if (client->irq < 1)
+		polling_req = true;
+
+	touch->input = input_allocate_device();
+	if (!touch->input) {
+		ret = -ENOMEM;
+		goto err_mem_free;
+	}
+
+	synaptics_i2c_set_input_params(touch);
+
+	if (!polling_req) {
+		dev_dbg(&touch->client->dev,
+			 "Requesting IRQ: %d\n", touch->client->irq);
+
+		ret = request_irq(touch->client->irq, synaptics_i2c_irq,
+				  IRQ_TYPE_EDGE_FALLING,
+				  DRIVER_NAME, touch);
+		if (ret) {
+			dev_warn(&touch->client->dev,
+				  "IRQ request failed: %d, "
+				  "falling back to polling\n", ret);
+			polling_req = true;
+			synaptics_i2c_reg_set(touch->client,
+					      INTERRUPT_EN_REG, 0);
+		}
+	}
+
+	if (polling_req)
+		dev_dbg(&touch->client->dev,
+			 "Using polling at rate: %d times/sec\n", scan_rate);
+
+	/* Register the device in input subsystem */
+	ret = input_register_device(touch->input);
+	if (ret) {
+		dev_err(&client->dev,
+			 "Input device register failed: %d\n", ret);
+		goto err_input_free;
+	}
+
+	i2c_set_clientdata(client, touch);
+
+	return 0;
+
+err_input_free:
+	input_free_device(touch->input);
+err_mem_free:
+	kfree(touch);
+
+	return ret;
+}
+
+static int synaptics_i2c_remove(struct i2c_client *client)
+{
+	struct synaptics_i2c *touch = i2c_get_clientdata(client);
+
+	if (!polling_req)
+		free_irq(client->irq, touch);
+
+	input_unregister_device(touch->input);
+	kfree(touch);
+
+	return 0;
+}
+
+static int __maybe_unused synaptics_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct synaptics_i2c *touch = i2c_get_clientdata(client);
+
+	cancel_delayed_work_sync(&touch->dwork);
+
+	/* Save some power */
+	synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP);
+
+	return 0;
+}
+
+static int __maybe_unused synaptics_i2c_resume(struct device *dev)
+{
+	int ret;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct synaptics_i2c *touch = i2c_get_clientdata(client);
+
+	ret = synaptics_i2c_reset_config(client);
+	if (ret)
+		return ret;
+
+	synaptics_i2c_reschedule_work(touch,
+				msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(synaptics_i2c_pm, synaptics_i2c_suspend,
+			 synaptics_i2c_resume);
+
+static const struct i2c_device_id synaptics_i2c_id_table[] = {
+	{ "synaptics_i2c", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table);
+
+static struct i2c_driver synaptics_i2c_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.pm	= &synaptics_i2c_pm,
+	},
+
+	.probe		= synaptics_i2c_probe,
+	.remove		= synaptics_i2c_remove,
+
+	.id_table	= synaptics_i2c_id_table,
+};
+
+module_i2c_driver(synaptics_i2c_driver);
+
+MODULE_DESCRIPTION("Synaptics I2C touchpad driver");
+MODULE_AUTHOR("Mike Rapoport, Igor Grinberg, Compulab");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/mouse/synaptics_usb.c b/drivers/input/mouse/synaptics_usb.c
new file mode 100644
index 0000000..6bcc018
--- /dev/null
+++ b/drivers/input/mouse/synaptics_usb.c
@@ -0,0 +1,562 @@
+/*
+ * USB Synaptics device driver
+ *
+ *  Copyright (c) 2002 Rob Miller (rob@inpharmatica . co . uk)
+ *  Copyright (c) 2003 Ron Lee (ron@debian.org)
+ *	cPad driver for kernel 2.4
+ *
+ *  Copyright (c) 2004 Jan Steinhoff (cpad@jan-steinhoff . de)
+ *  Copyright (c) 2004 Ron Lee (ron@debian.org)
+ *	rewritten for kernel 2.6
+ *
+ *  cPad display character device part is not included. It can be found at
+ *  http://jan-steinhoff.de/linux/synaptics-usb.html
+ *
+ * Bases on:	usb_skeleton.c v2.2 by Greg Kroah-Hartman
+ *		drivers/hid/usbhid/usbmouse.c by Vojtech Pavlik
+ *		drivers/input/mouse/synaptics.c by Peter Osterlund
+ *
+ * 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.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+/*
+ * There are three different types of Synaptics USB devices: Touchpads,
+ * touchsticks (or trackpoints), and touchscreens. Touchpads are well supported
+ * by this driver, touchstick support has not been tested much yet, and
+ * touchscreens have not been tested at all.
+ *
+ * Up to three alternate settings are possible:
+ *	setting 0: one int endpoint for relative movement (used by usbhid.ko)
+ *	setting 1: one int endpoint for absolute finger position
+ *	setting 2 (cPad only): one int endpoint for absolute finger position and
+ *		   two bulk endpoints for the display (in/out)
+ * This driver uses setting 1.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/usb.h>
+#include <linux/input.h>
+#include <linux/usb/input.h>
+
+#define USB_VENDOR_ID_SYNAPTICS	0x06cb
+#define USB_DEVICE_ID_SYNAPTICS_TP	0x0001	/* Synaptics USB TouchPad */
+#define USB_DEVICE_ID_SYNAPTICS_INT_TP	0x0002	/* Integrated USB TouchPad */
+#define USB_DEVICE_ID_SYNAPTICS_CPAD	0x0003	/* Synaptics cPad */
+#define USB_DEVICE_ID_SYNAPTICS_TS	0x0006	/* Synaptics TouchScreen */
+#define USB_DEVICE_ID_SYNAPTICS_STICK	0x0007	/* Synaptics USB Styk */
+#define USB_DEVICE_ID_SYNAPTICS_WP	0x0008	/* Synaptics USB WheelPad */
+#define USB_DEVICE_ID_SYNAPTICS_COMP_TP	0x0009	/* Composite USB TouchPad */
+#define USB_DEVICE_ID_SYNAPTICS_WTP	0x0010	/* Wireless TouchPad */
+#define USB_DEVICE_ID_SYNAPTICS_DPAD	0x0013	/* DisplayPad */
+
+#define SYNUSB_TOUCHPAD			(1 << 0)
+#define SYNUSB_STICK			(1 << 1)
+#define SYNUSB_TOUCHSCREEN		(1 << 2)
+#define SYNUSB_AUXDISPLAY		(1 << 3) /* For cPad */
+#define SYNUSB_COMBO			(1 << 4) /* Composite device (TP + stick) */
+#define SYNUSB_IO_ALWAYS		(1 << 5)
+
+#define USB_DEVICE_SYNAPTICS(prod, kind)		\
+	USB_DEVICE(USB_VENDOR_ID_SYNAPTICS,		\
+		   USB_DEVICE_ID_SYNAPTICS_##prod),	\
+	.driver_info = (kind),
+
+#define SYNUSB_RECV_SIZE	8
+
+#define XMIN_NOMINAL		1472
+#define XMAX_NOMINAL		5472
+#define YMIN_NOMINAL		1408
+#define YMAX_NOMINAL		4448
+
+struct synusb {
+	struct usb_device *udev;
+	struct usb_interface *intf;
+	struct urb *urb;
+	unsigned char *data;
+
+	/* input device related data structures */
+	struct input_dev *input;
+	char name[128];
+	char phys[64];
+
+	/* characteristics of the device */
+	unsigned long flags;
+};
+
+static void synusb_report_buttons(struct synusb *synusb)
+{
+	struct input_dev *input_dev = synusb->input;
+
+	input_report_key(input_dev, BTN_LEFT, synusb->data[1] & 0x04);
+	input_report_key(input_dev, BTN_RIGHT, synusb->data[1] & 0x01);
+	input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x02);
+}
+
+static void synusb_report_stick(struct synusb *synusb)
+{
+	struct input_dev *input_dev = synusb->input;
+	int x, y;
+	unsigned int pressure;
+
+	pressure = synusb->data[6];
+	x = (s16)(be16_to_cpup((__be16 *)&synusb->data[2]) << 3) >> 7;
+	y = (s16)(be16_to_cpup((__be16 *)&synusb->data[4]) << 3) >> 7;
+
+	if (pressure > 0) {
+		input_report_rel(input_dev, REL_X, x);
+		input_report_rel(input_dev, REL_Y, -y);
+	}
+
+	input_report_abs(input_dev, ABS_PRESSURE, pressure);
+
+	synusb_report_buttons(synusb);
+
+	input_sync(input_dev);
+}
+
+static void synusb_report_touchpad(struct synusb *synusb)
+{
+	struct input_dev *input_dev = synusb->input;
+	unsigned int num_fingers, tool_width;
+	unsigned int x, y;
+	unsigned int pressure, w;
+
+	pressure = synusb->data[6];
+	x = be16_to_cpup((__be16 *)&synusb->data[2]);
+	y = be16_to_cpup((__be16 *)&synusb->data[4]);
+	w = synusb->data[0] & 0x0f;
+
+	if (pressure > 0) {
+		num_fingers = 1;
+		tool_width = 5;
+		switch (w) {
+		case 0 ... 1:
+			num_fingers = 2 + w;
+			break;
+
+		case 2:	                /* pen, pretend its a finger */
+			break;
+
+		case 4 ... 15:
+			tool_width = w;
+			break;
+		}
+	} else {
+		num_fingers = 0;
+		tool_width = 0;
+	}
+
+	/*
+	 * Post events
+	 * BTN_TOUCH has to be first as mousedev relies on it when doing
+	 * absolute -> relative conversion
+	 */
+
+	if (pressure > 30)
+		input_report_key(input_dev, BTN_TOUCH, 1);
+	if (pressure < 25)
+		input_report_key(input_dev, BTN_TOUCH, 0);
+
+	if (num_fingers > 0) {
+		input_report_abs(input_dev, ABS_X, x);
+		input_report_abs(input_dev, ABS_Y,
+				 YMAX_NOMINAL + YMIN_NOMINAL - y);
+	}
+
+	input_report_abs(input_dev, ABS_PRESSURE, pressure);
+	input_report_abs(input_dev, ABS_TOOL_WIDTH, tool_width);
+
+	input_report_key(input_dev, BTN_TOOL_FINGER, num_fingers == 1);
+	input_report_key(input_dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
+	input_report_key(input_dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
+
+	synusb_report_buttons(synusb);
+	if (synusb->flags & SYNUSB_AUXDISPLAY)
+		input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x08);
+
+	input_sync(input_dev);
+}
+
+static void synusb_irq(struct urb *urb)
+{
+	struct synusb *synusb = urb->context;
+	int error;
+
+	/* Check our status in case we need to bail out early. */
+	switch (urb->status) {
+	case 0:
+		usb_mark_last_busy(synusb->udev);
+		break;
+
+	/* Device went away so don't keep trying to read from it. */
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+
+	default:
+		goto resubmit;
+		break;
+	}
+
+	if (synusb->flags & SYNUSB_STICK)
+		synusb_report_stick(synusb);
+	else
+		synusb_report_touchpad(synusb);
+
+resubmit:
+	error = usb_submit_urb(urb, GFP_ATOMIC);
+	if (error && error != -EPERM)
+		dev_err(&synusb->intf->dev,
+			"%s - usb_submit_urb failed with result: %d",
+			__func__, error);
+}
+
+static struct usb_endpoint_descriptor *
+synusb_get_in_endpoint(struct usb_host_interface *iface)
+{
+
+	struct usb_endpoint_descriptor *endpoint;
+	int i;
+
+	for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
+		endpoint = &iface->endpoint[i].desc;
+
+		if (usb_endpoint_is_int_in(endpoint)) {
+			/* we found our interrupt in endpoint */
+			return endpoint;
+		}
+	}
+
+	return NULL;
+}
+
+static int synusb_open(struct input_dev *dev)
+{
+	struct synusb *synusb = input_get_drvdata(dev);
+	int retval;
+
+	retval = usb_autopm_get_interface(synusb->intf);
+	if (retval) {
+		dev_err(&synusb->intf->dev,
+			"%s - usb_autopm_get_interface failed, error: %d\n",
+			__func__, retval);
+		return retval;
+	}
+
+	retval = usb_submit_urb(synusb->urb, GFP_KERNEL);
+	if (retval) {
+		dev_err(&synusb->intf->dev,
+			"%s - usb_submit_urb failed, error: %d\n",
+			__func__, retval);
+		retval = -EIO;
+		goto out;
+	}
+
+	synusb->intf->needs_remote_wakeup = 1;
+
+out:
+	usb_autopm_put_interface(synusb->intf);
+	return retval;
+}
+
+static void synusb_close(struct input_dev *dev)
+{
+	struct synusb *synusb = input_get_drvdata(dev);
+	int autopm_error;
+
+	autopm_error = usb_autopm_get_interface(synusb->intf);
+
+	usb_kill_urb(synusb->urb);
+	synusb->intf->needs_remote_wakeup = 0;
+
+	if (!autopm_error)
+		usb_autopm_put_interface(synusb->intf);
+}
+
+static int synusb_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_endpoint_descriptor *ep;
+	struct synusb *synusb;
+	struct input_dev *input_dev;
+	unsigned int intf_num = intf->cur_altsetting->desc.bInterfaceNumber;
+	unsigned int altsetting = min(intf->num_altsetting, 1U);
+	int error;
+
+	error = usb_set_interface(udev, intf_num, altsetting);
+	if (error) {
+		dev_err(&udev->dev,
+			"Can not set alternate setting to %i, error: %i",
+			altsetting, error);
+		return error;
+	}
+
+	ep = synusb_get_in_endpoint(intf->cur_altsetting);
+	if (!ep)
+		return -ENODEV;
+
+	synusb = kzalloc(sizeof(*synusb), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!synusb || !input_dev) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	synusb->udev = udev;
+	synusb->intf = intf;
+	synusb->input = input_dev;
+
+	synusb->flags = id->driver_info;
+	if (synusb->flags & SYNUSB_COMBO) {
+		/*
+		 * This is a combo device, we need to set proper
+		 * capability, depending on the interface.
+		 */
+		synusb->flags |= intf_num == 1 ?
+					SYNUSB_STICK : SYNUSB_TOUCHPAD;
+	}
+
+	synusb->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!synusb->urb) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	synusb->data = usb_alloc_coherent(udev, SYNUSB_RECV_SIZE, GFP_KERNEL,
+					  &synusb->urb->transfer_dma);
+	if (!synusb->data) {
+		error = -ENOMEM;
+		goto err_free_urb;
+	}
+
+	usb_fill_int_urb(synusb->urb, udev,
+			 usb_rcvintpipe(udev, ep->bEndpointAddress),
+			 synusb->data, SYNUSB_RECV_SIZE,
+			 synusb_irq, synusb,
+			 ep->bInterval);
+	synusb->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	if (udev->manufacturer)
+		strlcpy(synusb->name, udev->manufacturer,
+			sizeof(synusb->name));
+
+	if (udev->product) {
+		if (udev->manufacturer)
+			strlcat(synusb->name, " ", sizeof(synusb->name));
+		strlcat(synusb->name, udev->product, sizeof(synusb->name));
+	}
+
+	if (!strlen(synusb->name))
+		snprintf(synusb->name, sizeof(synusb->name),
+			 "USB Synaptics Device %04x:%04x",
+			 le16_to_cpu(udev->descriptor.idVendor),
+			 le16_to_cpu(udev->descriptor.idProduct));
+
+	if (synusb->flags & SYNUSB_STICK)
+		strlcat(synusb->name, " (Stick)", sizeof(synusb->name));
+
+	usb_make_path(udev, synusb->phys, sizeof(synusb->phys));
+	strlcat(synusb->phys, "/input0", sizeof(synusb->phys));
+
+	input_dev->name = synusb->name;
+	input_dev->phys = synusb->phys;
+	usb_to_input_id(udev, &input_dev->id);
+	input_dev->dev.parent = &synusb->intf->dev;
+
+	if (!(synusb->flags & SYNUSB_IO_ALWAYS)) {
+		input_dev->open = synusb_open;
+		input_dev->close = synusb_close;
+	}
+
+	input_set_drvdata(input_dev, synusb);
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+
+	if (synusb->flags & SYNUSB_STICK) {
+		__set_bit(EV_REL, input_dev->evbit);
+		__set_bit(REL_X, input_dev->relbit);
+		__set_bit(REL_Y, input_dev->relbit);
+		__set_bit(INPUT_PROP_POINTING_STICK, input_dev->propbit);
+		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 127, 0, 0);
+	} else {
+		input_set_abs_params(input_dev, ABS_X,
+				     XMIN_NOMINAL, XMAX_NOMINAL, 0, 0);
+		input_set_abs_params(input_dev, ABS_Y,
+				     YMIN_NOMINAL, YMAX_NOMINAL, 0, 0);
+		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
+		input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
+		__set_bit(BTN_TOUCH, input_dev->keybit);
+		__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+		__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+		__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
+	}
+
+	if (synusb->flags & SYNUSB_TOUCHSCREEN)
+		__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+	else
+		__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+	__set_bit(BTN_LEFT, input_dev->keybit);
+	__set_bit(BTN_RIGHT, input_dev->keybit);
+	__set_bit(BTN_MIDDLE, input_dev->keybit);
+
+	usb_set_intfdata(intf, synusb);
+
+	if (synusb->flags & SYNUSB_IO_ALWAYS) {
+		error = synusb_open(input_dev);
+		if (error)
+			goto err_free_dma;
+	}
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(&udev->dev,
+			"Failed to register input device, error %d\n",
+			error);
+		goto err_stop_io;
+	}
+
+	return 0;
+
+err_stop_io:
+	if (synusb->flags & SYNUSB_IO_ALWAYS)
+		synusb_close(synusb->input);
+err_free_dma:
+	usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data,
+			  synusb->urb->transfer_dma);
+err_free_urb:
+	usb_free_urb(synusb->urb);
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(synusb);
+	usb_set_intfdata(intf, NULL);
+
+	return error;
+}
+
+static void synusb_disconnect(struct usb_interface *intf)
+{
+	struct synusb *synusb = usb_get_intfdata(intf);
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	if (synusb->flags & SYNUSB_IO_ALWAYS)
+		synusb_close(synusb->input);
+
+	input_unregister_device(synusb->input);
+
+	usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data,
+			  synusb->urb->transfer_dma);
+	usb_free_urb(synusb->urb);
+	kfree(synusb);
+
+	usb_set_intfdata(intf, NULL);
+}
+
+static int synusb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct synusb *synusb = usb_get_intfdata(intf);
+	struct input_dev *input_dev = synusb->input;
+
+	mutex_lock(&input_dev->mutex);
+	usb_kill_urb(synusb->urb);
+	mutex_unlock(&input_dev->mutex);
+
+	return 0;
+}
+
+static int synusb_resume(struct usb_interface *intf)
+{
+	struct synusb *synusb = usb_get_intfdata(intf);
+	struct input_dev *input_dev = synusb->input;
+	int retval = 0;
+
+	mutex_lock(&input_dev->mutex);
+
+	if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
+	    usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
+		retval = -EIO;
+	}
+
+	mutex_unlock(&input_dev->mutex);
+
+	return retval;
+}
+
+static int synusb_pre_reset(struct usb_interface *intf)
+{
+	struct synusb *synusb = usb_get_intfdata(intf);
+	struct input_dev *input_dev = synusb->input;
+
+	mutex_lock(&input_dev->mutex);
+	usb_kill_urb(synusb->urb);
+
+	return 0;
+}
+
+static int synusb_post_reset(struct usb_interface *intf)
+{
+	struct synusb *synusb = usb_get_intfdata(intf);
+	struct input_dev *input_dev = synusb->input;
+	int retval = 0;
+
+	if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
+	    usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
+		retval = -EIO;
+	}
+
+	mutex_unlock(&input_dev->mutex);
+
+	return retval;
+}
+
+static int synusb_reset_resume(struct usb_interface *intf)
+{
+	return synusb_resume(intf);
+}
+
+static struct usb_device_id synusb_idtable[] = {
+	{ USB_DEVICE_SYNAPTICS(TP, SYNUSB_TOUCHPAD) },
+	{ USB_DEVICE_SYNAPTICS(INT_TP, SYNUSB_TOUCHPAD) },
+	{ USB_DEVICE_SYNAPTICS(CPAD,
+		SYNUSB_TOUCHPAD | SYNUSB_AUXDISPLAY | SYNUSB_IO_ALWAYS) },
+	{ USB_DEVICE_SYNAPTICS(TS, SYNUSB_TOUCHSCREEN) },
+	{ USB_DEVICE_SYNAPTICS(STICK, SYNUSB_STICK) },
+	{ USB_DEVICE_SYNAPTICS(WP, SYNUSB_TOUCHPAD) },
+	{ USB_DEVICE_SYNAPTICS(COMP_TP, SYNUSB_COMBO) },
+	{ USB_DEVICE_SYNAPTICS(WTP, SYNUSB_TOUCHPAD) },
+	{ USB_DEVICE_SYNAPTICS(DPAD, SYNUSB_TOUCHPAD) },
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, synusb_idtable);
+
+static struct usb_driver synusb_driver = {
+	.name		= "synaptics_usb",
+	.probe		= synusb_probe,
+	.disconnect	= synusb_disconnect,
+	.id_table	= synusb_idtable,
+	.suspend	= synusb_suspend,
+	.resume		= synusb_resume,
+	.pre_reset	= synusb_pre_reset,
+	.post_reset	= synusb_post_reset,
+	.reset_resume	= synusb_reset_resume,
+	.supports_autosuspend = 1,
+};
+
+module_usb_driver(synusb_driver);
+
+MODULE_AUTHOR("Rob Miller <rob@inpharmatica.co.uk>, "
+              "Ron Lee <ron@debian.org>, "
+              "Jan Steinhoff <cpad@jan-steinhoff.de>");
+MODULE_DESCRIPTION("Synaptics USB device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/mouse/touchkit_ps2.c b/drivers/input/mouse/touchkit_ps2.c
new file mode 100644
index 0000000..1fd8f5e
--- /dev/null
+++ b/drivers/input/mouse/touchkit_ps2.c
@@ -0,0 +1,100 @@
+/* ----------------------------------------------------------------------------
+ * touchkit_ps2.c  --  Driver for eGalax TouchKit PS/2 Touchscreens
+ *
+ * Copyright (C) 2005 by Stefan Lucke
+ * Copyright (C) 2004 by Daniel Ritz
+ * Copyright (C) by Todd E. Johnson (mtouchusb.c)
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Based upon touchkitusb.c
+ *
+ * Vendor documentation is available at:
+ * http://home.eeti.com.tw/web20/drivers/Software%20Programming%20Guide_v2.0.pdf 
+ */
+
+#include <linux/kernel.h>
+
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+
+#include "psmouse.h"
+#include "touchkit_ps2.h"
+
+#define TOUCHKIT_MAX_XC			0x07ff
+#define TOUCHKIT_MAX_YC			0x07ff
+
+#define TOUCHKIT_CMD			0x0a
+#define TOUCHKIT_CMD_LENGTH		1
+
+#define TOUCHKIT_CMD_ACTIVE		'A'
+#define TOUCHKIT_CMD_FIRMWARE_VERSION	'D'
+#define TOUCHKIT_CMD_CONTROLLER_TYPE	'E'
+
+#define TOUCHKIT_SEND_PARMS(s, r, c)	((s) << 12 | (r) << 8 | (c))
+
+#define TOUCHKIT_GET_TOUCHED(packet)	(((packet)[0]) & 0x01)
+#define TOUCHKIT_GET_X(packet)		(((packet)[1] << 7) | (packet)[2])
+#define TOUCHKIT_GET_Y(packet)		(((packet)[3] << 7) | (packet)[4])
+
+static psmouse_ret_t touchkit_ps2_process_byte(struct psmouse *psmouse)
+{
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev = psmouse->dev;
+
+	if (psmouse->pktcnt != 5)
+		return PSMOUSE_GOOD_DATA;
+
+	input_report_abs(dev, ABS_X, TOUCHKIT_GET_X(packet));
+	input_report_abs(dev, ABS_Y, TOUCHKIT_GET_Y(packet));
+	input_report_key(dev, BTN_TOUCH, TOUCHKIT_GET_TOUCHED(packet));
+	input_sync(dev);
+
+	return PSMOUSE_FULL_PACKET;
+}
+
+int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties)
+{
+	struct input_dev *dev = psmouse->dev;
+	unsigned char param[3];
+	int command;
+
+	param[0] = TOUCHKIT_CMD_LENGTH;
+	param[1] = TOUCHKIT_CMD_ACTIVE;
+	command = TOUCHKIT_SEND_PARMS(2, 3, TOUCHKIT_CMD);
+
+	if (ps2_command(&psmouse->ps2dev, param, command))
+		return -ENODEV;
+
+	if (param[0] != TOUCHKIT_CMD || param[1] != 0x01 ||
+	    param[2] != TOUCHKIT_CMD_ACTIVE)
+		return -ENODEV;
+
+	if (set_properties) {
+		dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+		dev->keybit[BIT_WORD(BTN_MOUSE)] = 0;
+		dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+		input_set_abs_params(dev, ABS_X, 0, TOUCHKIT_MAX_XC, 0, 0);
+		input_set_abs_params(dev, ABS_Y, 0, TOUCHKIT_MAX_YC, 0, 0);
+
+		psmouse->vendor = "eGalax";
+		psmouse->name = "Touchscreen";
+		psmouse->protocol_handler = touchkit_ps2_process_byte;
+		psmouse->pktsize = 5;
+	}
+
+	return 0;
+}
diff --git a/drivers/input/mouse/touchkit_ps2.h b/drivers/input/mouse/touchkit_ps2.h
new file mode 100644
index 0000000..2efe9ea
--- /dev/null
+++ b/drivers/input/mouse/touchkit_ps2.h
@@ -0,0 +1,25 @@
+/* ----------------------------------------------------------------------------
+ * touchkit_ps2.h  --  Driver for eGalax TouchKit PS/2 Touchscreens
+ *
+ * Copyright (C) 2005 by Stefan Lucke
+ * Copyright (c) 2005 Vojtech Pavlik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _TOUCHKIT_PS2_H
+#define _TOUCHKIT_PS2_H
+
+#ifdef CONFIG_MOUSE_PS2_TOUCHKIT
+int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties);
+#else
+static inline int touchkit_ps2_detect(struct psmouse *psmouse,
+				      bool set_properties)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_TOUCHKIT */
+
+#endif
diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c
new file mode 100644
index 0000000..7e2dc5e
--- /dev/null
+++ b/drivers/input/mouse/trackpoint.c
@@ -0,0 +1,429 @@
+/*
+ * Stephen Evanchik <evanchsa@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/serio.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/libps2.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include "psmouse.h"
+#include "trackpoint.h"
+
+/*
+ * Power-on Reset: Resets all trackpoint parameters, including RAM values,
+ * to defaults.
+ * Returns zero on success, non-zero on failure.
+ */
+static int trackpoint_power_on_reset(struct ps2dev *ps2dev)
+{
+	unsigned char results[2];
+	int tries = 0;
+
+	/* Issue POR command, and repeat up to once if 0xFC00 received */
+	do {
+		if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
+		    ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 2, TP_POR)))
+			return -1;
+	} while (results[0] == 0xFC && results[1] == 0x00 && ++tries < 2);
+
+	/* Check for success response -- 0xAA00 */
+	if (results[0] != 0xAA || results[1] != 0x00)
+		return -1;
+
+	return 0;
+}
+
+/*
+ * Device IO: read, write and toggle bit
+ */
+static int trackpoint_read(struct ps2dev *ps2dev,
+			   unsigned char loc, unsigned char *results)
+{
+	if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
+	    ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) {
+		return -1;
+	}
+
+	return 0;
+}
+
+static int trackpoint_write(struct ps2dev *ps2dev,
+			    unsigned char loc, unsigned char val)
+{
+	if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
+	    ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) ||
+	    ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
+	    ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, val))) {
+		return -1;
+	}
+
+	return 0;
+}
+
+static int trackpoint_toggle_bit(struct ps2dev *ps2dev,
+				 unsigned char loc, unsigned char mask)
+{
+	/* Bad things will happen if the loc param isn't in this range */
+	if (loc < 0x20 || loc >= 0x2F)
+		return -1;
+
+	if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
+	    ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_TOGGLE)) ||
+	    ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
+	    ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, mask))) {
+		return -1;
+	}
+
+	return 0;
+}
+
+static int trackpoint_update_bit(struct ps2dev *ps2dev, unsigned char loc,
+				 unsigned char mask, unsigned char value)
+{
+	int retval = 0;
+	unsigned char data;
+
+	trackpoint_read(ps2dev, loc, &data);
+	if (((data & mask) == mask) != !!value)
+		retval = trackpoint_toggle_bit(ps2dev, loc, mask);
+
+	return retval;
+}
+
+/*
+ * Trackpoint-specific attributes
+ */
+struct trackpoint_attr_data {
+	size_t field_offset;
+	unsigned char command;
+	unsigned char mask;
+	unsigned char inverted;
+	unsigned char power_on_default;
+};
+
+static ssize_t trackpoint_show_int_attr(struct psmouse *psmouse, void *data, char *buf)
+{
+	struct trackpoint_data *tp = psmouse->private;
+	struct trackpoint_attr_data *attr = data;
+	unsigned char value = *(unsigned char *)((char *)tp + attr->field_offset);
+
+	if (attr->inverted)
+		value = !value;
+
+	return sprintf(buf, "%u\n", value);
+}
+
+static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,
+					const char *buf, size_t count)
+{
+	struct trackpoint_data *tp = psmouse->private;
+	struct trackpoint_attr_data *attr = data;
+	unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
+	unsigned char value;
+	int err;
+
+	err = kstrtou8(buf, 10, &value);
+	if (err)
+		return err;
+
+	*field = value;
+	trackpoint_write(&psmouse->ps2dev, attr->command, value);
+
+	return count;
+}
+
+#define TRACKPOINT_INT_ATTR(_name, _command, _default)				\
+	static struct trackpoint_attr_data trackpoint_attr_##_name = {		\
+		.field_offset = offsetof(struct trackpoint_data, _name),	\
+		.command = _command,						\
+		.power_on_default = _default,					\
+	};									\
+	PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO,				\
+			    &trackpoint_attr_##_name,				\
+			    trackpoint_show_int_attr, trackpoint_set_int_attr)
+
+static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,
+					const char *buf, size_t count)
+{
+	struct trackpoint_data *tp = psmouse->private;
+	struct trackpoint_attr_data *attr = data;
+	unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
+	unsigned int value;
+	int err;
+
+	err = kstrtouint(buf, 10, &value);
+	if (err)
+		return err;
+
+	if (value > 1)
+		return -EINVAL;
+
+	if (attr->inverted)
+		value = !value;
+
+	if (*field != value) {
+		*field = value;
+		trackpoint_toggle_bit(&psmouse->ps2dev, attr->command, attr->mask);
+	}
+
+	return count;
+}
+
+
+#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv, _default)	\
+static struct trackpoint_attr_data trackpoint_attr_##_name = {		\
+	.field_offset		= offsetof(struct trackpoint_data,	\
+					   _name),			\
+	.command		= _command,				\
+	.mask			= _mask,				\
+	.inverted		= _inv,					\
+	.power_on_default	= _default,				\
+	};								\
+PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO,				\
+		    &trackpoint_attr_##_name,				\
+		    trackpoint_show_int_attr, trackpoint_set_bit_attr)
+
+#define TRACKPOINT_UPDATE_BIT(_psmouse, _tp, _name)			\
+do {									\
+	struct trackpoint_attr_data *_attr = &trackpoint_attr_##_name;	\
+									\
+	trackpoint_update_bit(&_psmouse->ps2dev,			\
+			_attr->command, _attr->mask, _tp->_name);	\
+} while (0)
+
+#define TRACKPOINT_UPDATE(_power_on, _psmouse, _tp, _name)		\
+do {									\
+	if (!_power_on ||						\
+	    _tp->_name != trackpoint_attr_##_name.power_on_default) {	\
+		if (!trackpoint_attr_##_name.mask)			\
+			trackpoint_write(&_psmouse->ps2dev,		\
+				 trackpoint_attr_##_name.command,	\
+				 _tp->_name);				\
+		else							\
+			TRACKPOINT_UPDATE_BIT(_psmouse, _tp, _name);	\
+	}								\
+} while (0)
+
+#define TRACKPOINT_SET_POWER_ON_DEFAULT(_tp, _name)				\
+	(_tp->_name = trackpoint_attr_##_name.power_on_default)
+
+TRACKPOINT_INT_ATTR(sensitivity, TP_SENS, TP_DEF_SENS);
+TRACKPOINT_INT_ATTR(speed, TP_SPEED, TP_DEF_SPEED);
+TRACKPOINT_INT_ATTR(inertia, TP_INERTIA, TP_DEF_INERTIA);
+TRACKPOINT_INT_ATTR(reach, TP_REACH, TP_DEF_REACH);
+TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS, TP_DEF_DRAGHYS);
+TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG, TP_DEF_MINDRAG);
+TRACKPOINT_INT_ATTR(thresh, TP_THRESH, TP_DEF_THRESH);
+TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH, TP_DEF_UP_THRESH);
+TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME, TP_DEF_Z_TIME);
+TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV, TP_DEF_JENKS_CURV);
+TRACKPOINT_INT_ATTR(drift_time, TP_DRIFT_TIME, TP_DEF_DRIFT_TIME);
+
+TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0,
+		    TP_DEF_PTSON);
+TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0,
+		    TP_DEF_SKIPBACK);
+TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1,
+		    TP_DEF_EXT_DEV);
+
+static struct attribute *trackpoint_attrs[] = {
+	&psmouse_attr_sensitivity.dattr.attr,
+	&psmouse_attr_speed.dattr.attr,
+	&psmouse_attr_inertia.dattr.attr,
+	&psmouse_attr_reach.dattr.attr,
+	&psmouse_attr_draghys.dattr.attr,
+	&psmouse_attr_mindrag.dattr.attr,
+	&psmouse_attr_thresh.dattr.attr,
+	&psmouse_attr_upthresh.dattr.attr,
+	&psmouse_attr_ztime.dattr.attr,
+	&psmouse_attr_jenks.dattr.attr,
+	&psmouse_attr_drift_time.dattr.attr,
+	&psmouse_attr_press_to_select.dattr.attr,
+	&psmouse_attr_skipback.dattr.attr,
+	&psmouse_attr_ext_dev.dattr.attr,
+	NULL
+};
+
+static struct attribute_group trackpoint_attr_group = {
+	.attrs = trackpoint_attrs,
+};
+
+static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *firmware_id)
+{
+	unsigned char param[2] = { 0 };
+
+	if (ps2_command(&psmouse->ps2dev, param, MAKE_PS2_CMD(0, 2, TP_READ_ID)))
+		return -1;
+
+	/* add new TP ID. */
+	if (!(param[0] & TP_MAGIC_IDENT))
+		return -1;
+
+	if (firmware_id)
+		*firmware_id = param[1];
+
+	return 0;
+}
+
+/*
+ * Write parameters to trackpad.
+ * in_power_on_state: Set to true if TP is in default / power-on state (ex. if
+ *		      power-on reset was run). If so, values will only be
+ *		      written to TP if they differ from power-on default.
+ */
+static int trackpoint_sync(struct psmouse *psmouse, bool in_power_on_state)
+{
+	struct trackpoint_data *tp = psmouse->private;
+
+	if (!in_power_on_state) {
+		/*
+		 * Disable features that may make device unusable
+		 * with this driver.
+		 */
+		trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND,
+				      TP_MASK_TWOHAND, TP_DEF_TWOHAND);
+
+		trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG,
+				      TP_MASK_SOURCE_TAG, TP_DEF_SOURCE_TAG);
+
+		trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_MB,
+				      TP_MASK_MB, TP_DEF_MB);
+	}
+
+	/*
+	 * These properties can be changed in this driver. Only
+	 * configure them if the values are non-default or if the TP is in
+	 * an unknown state.
+	 */
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, sensitivity);
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, inertia);
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, speed);
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, reach);
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, draghys);
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, mindrag);
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, thresh);
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, upthresh);
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ztime);
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, jenks);
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, drift_time);
+
+	/* toggles */
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, press_to_select);
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, skipback);
+	TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ext_dev);
+
+	return 0;
+}
+
+static void trackpoint_defaults(struct trackpoint_data *tp)
+{
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, sensitivity);
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, speed);
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, reach);
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, draghys);
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, mindrag);
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, thresh);
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, upthresh);
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ztime);
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, jenks);
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, drift_time);
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, inertia);
+
+	/* toggles */
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, press_to_select);
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, skipback);
+	TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ext_dev);
+}
+
+static void trackpoint_disconnect(struct psmouse *psmouse)
+{
+	sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, &trackpoint_attr_group);
+
+	kfree(psmouse->private);
+	psmouse->private = NULL;
+}
+
+static int trackpoint_reconnect(struct psmouse *psmouse)
+{
+	int reset_fail;
+
+	if (trackpoint_start_protocol(psmouse, NULL))
+		return -1;
+
+	reset_fail = trackpoint_power_on_reset(&psmouse->ps2dev);
+	if (trackpoint_sync(psmouse, !reset_fail))
+		return -1;
+
+	return 0;
+}
+
+int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char firmware_id;
+	unsigned char button_info;
+	int error;
+
+	if (trackpoint_start_protocol(psmouse, &firmware_id))
+		return -1;
+
+	if (!set_properties)
+		return 0;
+
+	if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) {
+		psmouse_warn(psmouse, "failed to get extended button data, assuming 3 buttons\n");
+		button_info = 0x33;
+	}
+
+	psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
+	if (!psmouse->private)
+		return -ENOMEM;
+
+	psmouse->vendor = "IBM";
+	psmouse->name = "TrackPoint";
+
+	psmouse->reconnect = trackpoint_reconnect;
+	psmouse->disconnect = trackpoint_disconnect;
+
+	if ((button_info & 0x0f) >= 3)
+		__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+
+	__set_bit(INPUT_PROP_POINTER, psmouse->dev->propbit);
+	__set_bit(INPUT_PROP_POINTING_STICK, psmouse->dev->propbit);
+
+	trackpoint_defaults(psmouse->private);
+
+	error = trackpoint_power_on_reset(&psmouse->ps2dev);
+
+	/* Write defaults to TP only if reset fails. */
+	if (error)
+		trackpoint_sync(psmouse, false);
+
+	error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group);
+	if (error) {
+		psmouse_err(psmouse,
+			    "failed to create sysfs attributes, error: %d\n",
+			    error);
+		kfree(psmouse->private);
+		psmouse->private = NULL;
+		return -1;
+	}
+
+	psmouse_info(psmouse,
+		     "IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n",
+		     firmware_id,
+		     (button_info & 0xf0) >> 4, button_info & 0x0f);
+
+	return 0;
+}
+
diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h
new file mode 100644
index 0000000..8805575
--- /dev/null
+++ b/drivers/input/mouse/trackpoint.h
@@ -0,0 +1,162 @@
+/*
+ * IBM TrackPoint PS/2 mouse driver
+ *
+ * Stephen Evanchik <evanchsa@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _TRACKPOINT_H
+#define _TRACKPOINT_H
+
+/*
+ * These constants are from the TrackPoint System
+ * Engineering documentation Version 4 from IBM Watson
+ * research:
+ *	http://wwwcssrv.almaden.ibm.com/trackpoint/download.html
+ */
+
+#define TP_COMMAND		0xE2	/* Commands start with this */
+
+#define TP_READ_ID		0xE1	/* Sent for device identification */
+#define TP_MAGIC_IDENT		0x03	/* Sent after a TP_READ_ID followed */
+					/* by the firmware ID */
+					/* Firmware ID includes 0x1, 0x2, 0x3 */
+
+
+/*
+ * Commands
+ */
+#define TP_RECALIB		0x51	/* Recalibrate */
+#define TP_POWER_DOWN		0x44	/* Can only be undone through HW reset */
+#define TP_EXT_DEV		0x21	/* Determines if external device is connected (RO) */
+#define TP_EXT_BTN		0x4B	/* Read extended button status */
+#define TP_POR			0x7F	/* Execute Power on Reset */
+#define TP_POR_RESULTS		0x25	/* Read Power on Self test results */
+#define TP_DISABLE_EXT		0x40	/* Disable external pointing device */
+#define TP_ENABLE_EXT		0x41	/* Enable external pointing device */
+
+/*
+ * Mode manipulation
+ */
+#define TP_SET_SOFT_TRANS	0x4E	/* Set mode */
+#define TP_CANCEL_SOFT_TRANS	0xB9	/* Cancel mode */
+#define TP_SET_HARD_TRANS	0x45	/* Mode can only be set */
+
+
+/*
+ * Register oriented commands/properties
+ */
+#define TP_WRITE_MEM		0x81
+#define TP_READ_MEM		0x80	/* Not used in this implementation */
+
+/*
+* RAM Locations for properties
+ */
+#define TP_SENS			0x4A	/* Sensitivity */
+#define TP_MB			0x4C	/* Read Middle Button Status (RO) */
+#define TP_INERTIA		0x4D	/* Negative Inertia */
+#define TP_SPEED		0x60	/* Speed of TP Cursor */
+#define TP_REACH		0x57	/* Backup for Z-axis press */
+#define TP_DRAGHYS		0x58	/* Drag Hysteresis */
+					/* (how hard it is to drag */
+					/* with Z-axis pressed) */
+
+#define TP_MINDRAG		0x59	/* Minimum amount of force needed */
+					/* to trigger dragging */
+
+#define TP_THRESH		0x5C	/* Minimum value for a Z-axis press */
+#define TP_UP_THRESH		0x5A	/* Used to generate a 'click' on Z-axis */
+#define TP_Z_TIME		0x5E	/* How sharp of a press */
+#define TP_JENKS_CURV		0x5D	/* Minimum curvature for double click */
+#define TP_DRIFT_TIME		0x5F	/* How long a 'hands off' condition */
+					/* must last (x*107ms) for drift */
+					/* correction to occur */
+
+/*
+ * Toggling Flag bits
+ */
+#define TP_TOGGLE		0x47	/* Toggle command */
+
+#define TP_TOGGLE_MB		0x23	/* Disable/Enable Middle Button */
+#define TP_MASK_MB			0x01
+#define TP_TOGGLE_EXT_DEV	0x23	/* Disable external device */
+#define TP_MASK_EXT_DEV			0x02
+#define TP_TOGGLE_DRIFT		0x23	/* Drift Correction */
+#define TP_MASK_DRIFT			0x80
+#define TP_TOGGLE_BURST		0x28	/* Burst Mode */
+#define TP_MASK_BURST			0x80
+#define TP_TOGGLE_PTSON		0x2C	/* Press to Select */
+#define TP_MASK_PTSON			0x01
+#define TP_TOGGLE_HARD_TRANS	0x2C	/* Alternate method to set Hard Transparency */
+#define TP_MASK_HARD_TRANS		0x80
+#define TP_TOGGLE_TWOHAND	0x2D	/* Two handed */
+#define TP_MASK_TWOHAND			0x01
+#define TP_TOGGLE_STICKY_TWO	0x2D	/* Sticky two handed */
+#define TP_MASK_STICKY_TWO		0x04
+#define TP_TOGGLE_SKIPBACK	0x2D	/* Suppress movement after drag release */
+#define TP_MASK_SKIPBACK		0x08
+#define TP_TOGGLE_SOURCE_TAG	0x20	/* Bit 3 of the first packet will be set to
+					   to the origin of the packet (external or TP) */
+#define TP_MASK_SOURCE_TAG		0x80
+#define TP_TOGGLE_EXT_TAG	0x22	/* Bit 3 of the first packet coming from the
+					   external device will be forced to 1 */
+#define TP_MASK_EXT_TAG			0x04
+
+
+/* Power on Self Test Results */
+#define TP_POR_SUCCESS		0x3B
+
+/*
+ * Default power on values
+ */
+#define TP_DEF_SENS		0x80
+#define TP_DEF_INERTIA		0x06
+#define TP_DEF_SPEED		0x61
+#define TP_DEF_REACH		0x0A
+
+#define TP_DEF_DRAGHYS		0xFF
+#define TP_DEF_MINDRAG		0x14
+
+#define TP_DEF_THRESH		0x08
+#define TP_DEF_UP_THRESH	0xFF
+#define TP_DEF_Z_TIME		0x26
+#define TP_DEF_JENKS_CURV	0x87
+#define TP_DEF_DRIFT_TIME	0x05
+
+/* Toggles */
+#define TP_DEF_MB		0x00
+#define TP_DEF_PTSON		0x00
+#define TP_DEF_SKIPBACK		0x00
+#define TP_DEF_EXT_DEV		0x00	/* 0 means enabled */
+#define TP_DEF_TWOHAND		0x00
+#define TP_DEF_SOURCE_TAG	0x00
+
+#define MAKE_PS2_CMD(params, results, cmd) ((params<<12) | (results<<8) | (cmd))
+
+struct trackpoint_data
+{
+	unsigned char sensitivity, speed, inertia, reach;
+	unsigned char draghys, mindrag;
+	unsigned char thresh, upthresh;
+	unsigned char ztime, jenks;
+	unsigned char drift_time;
+
+	/* toggles */
+	unsigned char press_to_select;
+	unsigned char skipback;
+	unsigned char ext_dev;
+};
+
+#ifdef CONFIG_MOUSE_PS2_TRACKPOINT
+int trackpoint_detect(struct psmouse *psmouse, bool set_properties);
+#else
+inline int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_TRACKPOINT */
+
+#endif /* _TRACKPOINT_H */
diff --git a/drivers/input/mouse/vmmouse.c b/drivers/input/mouse/vmmouse.c
new file mode 100644
index 0000000..0f58678
--- /dev/null
+++ b/drivers/input/mouse/vmmouse.c
@@ -0,0 +1,491 @@
+/*
+ * Driver for Virtual PS/2 Mouse on VMware and QEMU hypervisors.
+ *
+ * Copyright (C) 2014, VMware, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Twin device code is hugely inspired by the ALPS driver.
+ * Authors:
+ *   Dmitry Torokhov <dmitry.torokhov@gmail.com>
+ *   Thomas Hellstrom <thellstrom@vmware.com>
+ */
+
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <asm/hypervisor.h>
+
+#include "psmouse.h"
+#include "vmmouse.h"
+
+#define VMMOUSE_PROTO_MAGIC			0x564D5868U
+#define VMMOUSE_PROTO_PORT			0x5658
+
+/*
+ * Main commands supported by the vmmouse hypervisor port.
+ */
+#define VMMOUSE_PROTO_CMD_GETVERSION		10
+#define VMMOUSE_PROTO_CMD_ABSPOINTER_DATA	39
+#define VMMOUSE_PROTO_CMD_ABSPOINTER_STATUS	40
+#define VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND	41
+#define VMMOUSE_PROTO_CMD_ABSPOINTER_RESTRICT   86
+
+/*
+ * Subcommands for VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND
+ */
+#define VMMOUSE_CMD_ENABLE			0x45414552U
+#define VMMOUSE_CMD_DISABLE			0x000000f5U
+#define VMMOUSE_CMD_REQUEST_RELATIVE		0x4c455252U
+#define VMMOUSE_CMD_REQUEST_ABSOLUTE		0x53424152U
+
+#define VMMOUSE_ERROR				0xffff0000U
+
+#define VMMOUSE_VERSION_ID			0x3442554aU
+
+#define VMMOUSE_RELATIVE_PACKET			0x00010000U
+
+#define VMMOUSE_LEFT_BUTTON			0x20
+#define VMMOUSE_RIGHT_BUTTON			0x10
+#define VMMOUSE_MIDDLE_BUTTON			0x08
+
+/*
+ * VMMouse Restrict command
+ */
+#define VMMOUSE_RESTRICT_ANY                    0x00
+#define VMMOUSE_RESTRICT_CPL0                   0x01
+#define VMMOUSE_RESTRICT_IOPL                   0x02
+
+#define VMMOUSE_MAX_X                           0xFFFF
+#define VMMOUSE_MAX_Y                           0xFFFF
+
+#define VMMOUSE_VENDOR "VMware"
+#define VMMOUSE_NAME   "VMMouse"
+
+/**
+ * struct vmmouse_data - private data structure for the vmmouse driver
+ *
+ * @abs_dev: "Absolute" device used to report absolute mouse movement.
+ * @phys: Physical path for the absolute device.
+ * @dev_name: Name attribute name for the absolute device.
+ */
+struct vmmouse_data {
+	struct input_dev *abs_dev;
+	char phys[32];
+	char dev_name[128];
+};
+
+/**
+ * Hypervisor-specific bi-directional communication channel
+ * implementing the vmmouse protocol. Should never execute on
+ * bare metal hardware.
+ */
+#define VMMOUSE_CMD(cmd, in1, out1, out2, out3, out4)	\
+({							\
+	unsigned long __dummy1, __dummy2;		\
+	__asm__ __volatile__ ("inl %%dx" :		\
+		"=a"(out1),				\
+		"=b"(out2),				\
+		"=c"(out3),				\
+		"=d"(out4),				\
+		"=S"(__dummy1),				\
+		"=D"(__dummy2) :			\
+		"a"(VMMOUSE_PROTO_MAGIC),		\
+		"b"(in1),				\
+		"c"(VMMOUSE_PROTO_CMD_##cmd),		\
+		"d"(VMMOUSE_PROTO_PORT) :		\
+		"memory");		                \
+})
+
+/**
+ * vmmouse_report_button - report button state on the correct input device
+ *
+ * @psmouse:  Pointer to the psmouse struct
+ * @abs_dev:  The absolute input device
+ * @rel_dev:  The relative input device
+ * @pref_dev: The preferred device for reporting
+ * @code:     Button code
+ * @value:    Button value
+ *
+ * Report @value and @code on @pref_dev, unless the button is already
+ * pressed on the other device, in which case the state is reported on that
+ * device.
+ */
+static void vmmouse_report_button(struct psmouse *psmouse,
+				  struct input_dev *abs_dev,
+				  struct input_dev *rel_dev,
+				  struct input_dev *pref_dev,
+				  unsigned int code, int value)
+{
+	if (test_bit(code, abs_dev->key))
+		pref_dev = abs_dev;
+	else if (test_bit(code, rel_dev->key))
+		pref_dev = rel_dev;
+
+	input_report_key(pref_dev, code, value);
+}
+
+/**
+ * vmmouse_report_events - process events on the vmmouse communications channel
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * This function pulls events from the vmmouse communications channel and
+ * reports them on the correct (absolute or relative) input device. When the
+ * communications channel is drained, or if we've processed more than 255
+ * psmouse commands, the function returns PSMOUSE_FULL_PACKET. If there is a
+ * host- or synchronization error, the function returns PSMOUSE_BAD_DATA in
+ * the hope that the caller will reset the communications channel.
+ */
+static psmouse_ret_t vmmouse_report_events(struct psmouse *psmouse)
+{
+	struct input_dev *rel_dev = psmouse->dev;
+	struct vmmouse_data *priv = psmouse->private;
+	struct input_dev *abs_dev = priv->abs_dev;
+	struct input_dev *pref_dev;
+	u32 status, x, y, z;
+	u32 dummy1, dummy2, dummy3;
+	unsigned int queue_length;
+	unsigned int count = 255;
+
+	while (count--) {
+		/* See if we have motion data. */
+		VMMOUSE_CMD(ABSPOINTER_STATUS, 0,
+			    status, dummy1, dummy2, dummy3);
+		if ((status & VMMOUSE_ERROR) == VMMOUSE_ERROR) {
+			psmouse_err(psmouse, "failed to fetch status data\n");
+			/*
+			 * After a few attempts this will result in
+			 * reconnect.
+			 */
+			return PSMOUSE_BAD_DATA;
+		}
+
+		queue_length = status & 0xffff;
+		if (queue_length == 0)
+			break;
+
+		if (queue_length % 4) {
+			psmouse_err(psmouse, "invalid queue length\n");
+			return PSMOUSE_BAD_DATA;
+		}
+
+		/* Now get it */
+		VMMOUSE_CMD(ABSPOINTER_DATA, 4, status, x, y, z);
+
+		/*
+		 * And report what we've got. Prefer to report button
+		 * events on the same device where we report motion events.
+		 * This doesn't work well with the mouse wheel, though. See
+		 * below. Ideally we would want to report that on the
+		 * preferred device as well.
+		 */
+		if (status & VMMOUSE_RELATIVE_PACKET) {
+			pref_dev = rel_dev;
+			input_report_rel(rel_dev, REL_X, (s32)x);
+			input_report_rel(rel_dev, REL_Y, -(s32)y);
+		} else {
+			pref_dev = abs_dev;
+			input_report_abs(abs_dev, ABS_X, x);
+			input_report_abs(abs_dev, ABS_Y, y);
+		}
+
+		/* Xorg seems to ignore wheel events on absolute devices */
+		input_report_rel(rel_dev, REL_WHEEL, -(s8)((u8) z));
+
+		vmmouse_report_button(psmouse, abs_dev, rel_dev,
+				      pref_dev, BTN_LEFT,
+				      status & VMMOUSE_LEFT_BUTTON);
+		vmmouse_report_button(psmouse, abs_dev, rel_dev,
+				      pref_dev, BTN_RIGHT,
+				      status & VMMOUSE_RIGHT_BUTTON);
+		vmmouse_report_button(psmouse, abs_dev, rel_dev,
+				      pref_dev, BTN_MIDDLE,
+				      status & VMMOUSE_MIDDLE_BUTTON);
+		input_sync(abs_dev);
+		input_sync(rel_dev);
+	}
+
+	return PSMOUSE_FULL_PACKET;
+}
+
+/**
+ * vmmouse_process_byte - process data on the ps/2 channel
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * When the ps/2 channel indicates that there is vmmouse data available,
+ * call vmmouse channel processing. Otherwise, continue to accept bytes. If
+ * there is a synchronization or communication data error, return
+ * PSMOUSE_BAD_DATA in the hope that the caller will reset the mouse.
+ */
+static psmouse_ret_t vmmouse_process_byte(struct psmouse *psmouse)
+{
+	unsigned char *packet = psmouse->packet;
+
+	switch (psmouse->pktcnt) {
+	case 1:
+		return (packet[0] & 0x8) == 0x8 ?
+			PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+
+	case 2:
+		return PSMOUSE_GOOD_DATA;
+
+	default:
+		return vmmouse_report_events(psmouse);
+	}
+}
+
+/**
+ * vmmouse_disable - Disable vmmouse
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Tries to disable vmmouse mode.
+ */
+static void vmmouse_disable(struct psmouse *psmouse)
+{
+	u32 status;
+	u32 dummy1, dummy2, dummy3, dummy4;
+
+	VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_DISABLE,
+		    dummy1, dummy2, dummy3, dummy4);
+
+	VMMOUSE_CMD(ABSPOINTER_STATUS, 0,
+		    status, dummy1, dummy2, dummy3);
+
+	if ((status & VMMOUSE_ERROR) != VMMOUSE_ERROR)
+		psmouse_warn(psmouse, "failed to disable vmmouse device\n");
+}
+
+/**
+ * vmmouse_enable - Enable vmmouse and request absolute mode.
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Tries to enable vmmouse mode. Performs basic checks and requests
+ * absolute vmmouse mode.
+ * Returns 0 on success, -ENODEV on failure.
+ */
+static int vmmouse_enable(struct psmouse *psmouse)
+{
+	u32 status, version;
+	u32 dummy1, dummy2, dummy3, dummy4;
+
+	/*
+	 * Try enabling the device. If successful, we should be able to
+	 * read valid version ID back from it.
+	 */
+	VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_ENABLE,
+		    dummy1, dummy2, dummy3, dummy4);
+
+	/*
+	 * See if version ID can be retrieved.
+	 */
+	VMMOUSE_CMD(ABSPOINTER_STATUS, 0, status, dummy1, dummy2, dummy3);
+	if ((status & 0x0000ffff) == 0) {
+		psmouse_dbg(psmouse, "empty flags - assuming no device\n");
+		return -ENXIO;
+	}
+
+	VMMOUSE_CMD(ABSPOINTER_DATA, 1 /* single item */,
+		    version, dummy1, dummy2, dummy3);
+	if (version != VMMOUSE_VERSION_ID) {
+		psmouse_dbg(psmouse, "Unexpected version value: %u vs %u\n",
+			    (unsigned) version, VMMOUSE_VERSION_ID);
+		vmmouse_disable(psmouse);
+		return -ENXIO;
+	}
+
+	/*
+	 * Restrict ioport access, if possible.
+	 */
+	VMMOUSE_CMD(ABSPOINTER_RESTRICT, VMMOUSE_RESTRICT_CPL0,
+		    dummy1, dummy2, dummy3, dummy4);
+
+	VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_REQUEST_ABSOLUTE,
+		    dummy1, dummy2, dummy3, dummy4);
+
+	return 0;
+}
+
+/*
+ * Array of supported hypervisors.
+ */
+static const struct hypervisor_x86 *vmmouse_supported_hypervisors[] = {
+	&x86_hyper_vmware,
+#ifdef CONFIG_KVM_GUEST
+	&x86_hyper_kvm,
+#endif
+};
+
+/**
+ * vmmouse_check_hypervisor - Check if we're running on a supported hypervisor
+ */
+static bool vmmouse_check_hypervisor(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(vmmouse_supported_hypervisors); i++)
+		if (vmmouse_supported_hypervisors[i] == x86_hyper)
+			return true;
+
+	return false;
+}
+
+/**
+ * vmmouse_detect - Probe whether vmmouse is available
+ *
+ * @psmouse: Pointer to the psmouse struct
+ * @set_properties: Whether to set psmouse name and vendor
+ *
+ * Returns 0 if vmmouse channel is available. Negative error code if not.
+ */
+int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
+{
+	u32 response, version, dummy1, dummy2;
+
+	if (!vmmouse_check_hypervisor()) {
+		psmouse_dbg(psmouse,
+			    "VMMouse not running on supported hypervisor.\n");
+		return -ENXIO;
+	}
+
+	/* Check if the device is present */
+	response = ~VMMOUSE_PROTO_MAGIC;
+	VMMOUSE_CMD(GETVERSION, 0, version, response, dummy1, dummy2);
+	if (response != VMMOUSE_PROTO_MAGIC || version == 0xffffffffU)
+		return -ENXIO;
+
+	if (set_properties) {
+		psmouse->vendor = VMMOUSE_VENDOR;
+		psmouse->name = VMMOUSE_NAME;
+		psmouse->model = version;
+	}
+
+	return 0;
+}
+
+/**
+ * vmmouse_disconnect - Take down vmmouse driver
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Takes down vmmouse driver and frees resources set up in vmmouse_init().
+ */
+static void vmmouse_disconnect(struct psmouse *psmouse)
+{
+	struct vmmouse_data *priv = psmouse->private;
+
+	vmmouse_disable(psmouse);
+	psmouse_reset(psmouse);
+	input_unregister_device(priv->abs_dev);
+	kfree(priv);
+}
+
+/**
+ * vmmouse_reconnect - Reset the ps/2 - and vmmouse connections
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Attempts to reset the mouse connections. Returns 0 on success and
+ * -1 on failure.
+ */
+static int vmmouse_reconnect(struct psmouse *psmouse)
+{
+	int error;
+
+	psmouse_reset(psmouse);
+	vmmouse_disable(psmouse);
+	error = vmmouse_enable(psmouse);
+	if (error) {
+		psmouse_err(psmouse,
+			    "Unable to re-enable mouse when reconnecting, err: %d\n",
+			    error);
+		return error;
+	}
+
+	return 0;
+}
+
+/**
+ * vmmouse_init - Initialize the vmmouse driver
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Requests the device and tries to enable vmmouse mode.
+ * If successful, sets up the input device for relative movement events.
+ * It also allocates another input device and sets it up for absolute motion
+ * events. Returns 0 on success and -1 on failure.
+ */
+int vmmouse_init(struct psmouse *psmouse)
+{
+	struct vmmouse_data *priv;
+	struct input_dev *rel_dev = psmouse->dev, *abs_dev;
+	int error;
+
+	psmouse_reset(psmouse);
+	error = vmmouse_enable(psmouse);
+	if (error)
+		return error;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	abs_dev = input_allocate_device();
+	if (!priv || !abs_dev) {
+		error = -ENOMEM;
+		goto init_fail;
+	}
+
+	priv->abs_dev = abs_dev;
+	psmouse->private = priv;
+
+	/* Set up and register absolute device */
+	snprintf(priv->phys, sizeof(priv->phys), "%s/input1",
+		 psmouse->ps2dev.serio->phys);
+
+	/* Mimic name setup for relative device in psmouse-base.c */
+	snprintf(priv->dev_name, sizeof(priv->dev_name), "%s %s %s",
+		 VMMOUSE_PSNAME, VMMOUSE_VENDOR, VMMOUSE_NAME);
+	abs_dev->phys = priv->phys;
+	abs_dev->name = priv->dev_name;
+	abs_dev->id.bustype = BUS_I8042;
+	abs_dev->id.vendor = 0x0002;
+	abs_dev->id.product = PSMOUSE_VMMOUSE;
+	abs_dev->id.version = psmouse->model;
+	abs_dev->dev.parent = &psmouse->ps2dev.serio->dev;
+
+	/* Set absolute device capabilities */
+	input_set_capability(abs_dev, EV_KEY, BTN_LEFT);
+	input_set_capability(abs_dev, EV_KEY, BTN_RIGHT);
+	input_set_capability(abs_dev, EV_KEY, BTN_MIDDLE);
+	input_set_capability(abs_dev, EV_ABS, ABS_X);
+	input_set_capability(abs_dev, EV_ABS, ABS_Y);
+	input_set_abs_params(abs_dev, ABS_X, 0, VMMOUSE_MAX_X, 0, 0);
+	input_set_abs_params(abs_dev, ABS_Y, 0, VMMOUSE_MAX_Y, 0, 0);
+
+	error = input_register_device(priv->abs_dev);
+	if (error)
+		goto init_fail;
+
+	/* Add wheel capability to the relative device */
+	input_set_capability(rel_dev, EV_REL, REL_WHEEL);
+
+	psmouse->protocol_handler = vmmouse_process_byte;
+	psmouse->disconnect = vmmouse_disconnect;
+	psmouse->reconnect = vmmouse_reconnect;
+
+	return 0;
+
+init_fail:
+	vmmouse_disable(psmouse);
+	psmouse_reset(psmouse);
+	input_free_device(abs_dev);
+	kfree(priv);
+	psmouse->private = NULL;
+
+	return error;
+}
diff --git a/drivers/input/mouse/vmmouse.h b/drivers/input/mouse/vmmouse.h
new file mode 100644
index 0000000..6f12601
--- /dev/null
+++ b/drivers/input/mouse/vmmouse.h
@@ -0,0 +1,30 @@
+/*
+ * Driver for Virtual PS/2 Mouse on VMware and QEMU hypervisors.
+ *
+ * Copyright (C) 2014, VMware, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _VMMOUSE_H
+#define _VMMOUSE_H
+
+#ifdef CONFIG_MOUSE_PS2_VMMOUSE
+#define VMMOUSE_PSNAME  "VirtualPS/2"
+
+int vmmouse_detect(struct psmouse *psmouse, bool set_properties);
+int vmmouse_init(struct psmouse *psmouse);
+#else
+static inline int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
+{
+	return -ENOSYS;
+}
+static inline int vmmouse_init(struct psmouse *psmouse)
+{
+	return -ENOSYS;
+}
+#endif
+
+#endif
diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c
new file mode 100644
index 0000000..abd4944
--- /dev/null
+++ b/drivers/input/mouse/vsxxxaa.c
@@ -0,0 +1,550 @@
+/*
+ * Driver for	DEC VSXXX-AA mouse (hockey-puck mouse, ball or two rollers)
+ *		DEC VSXXX-GA mouse (rectangular mouse, with ball)
+ *		DEC VSXXX-AB tablet (digitizer with hair cross or stylus)
+ *
+ * Copyright (C) 2003-2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
+ *
+ * The packet format was initially taken from a patch to GPM which is (C) 2001
+ * by	Karsten Merker <merker@linuxtag.org>
+ * and	Maciej W. Rozycki <macro@ds2.pg.gda.pl>
+ * Later on, I had access to the device's documentation (referenced below).
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Building an adaptor to DE9 / DB25 RS232
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * DISCLAIMER: Use this description AT YOUR OWN RISK! I'll not pay for
+ * anything if you break your mouse, your computer or whatever!
+ *
+ * In theory, this mouse is a simple RS232 device. In practice, it has got
+ * a quite uncommon plug and the requirement to additionally get a power
+ * supply at +5V and -12V.
+ *
+ * If you look at the socket/jack (_not_ at the plug), we use this pin
+ * numbering:
+ *    _______
+ *   / 7 6 5 \
+ *  | 4 --- 3 |
+ *   \  2 1  /
+ *    -------
+ *
+ *	DEC socket	DE9	DB25	Note
+ *	1 (GND)		5	7	-
+ *	2 (RxD)		2	3	-
+ *	3 (TxD)		3	2	-
+ *	4 (-12V)	-	-	Somewhere from the PSU. At ATX, it's
+ *					the thin blue wire at pin 12 of the
+ *					ATX power connector. Only required for
+ *					VSXXX-AA/-GA mice.
+ *	5 (+5V)		-	-	PSU (red wires of ATX power connector
+ *					on pin 4, 6, 19 or 20) or HDD power
+ *					connector (also red wire).
+ *	6 (+12V)	-	-	HDD power connector, yellow wire. Only
+ *					required for VSXXX-AB digitizer.
+ *	7 (dev. avail.)	-	-	The mouse shorts this one to pin 1.
+ *					This way, the host computer can detect
+ *					the mouse. To use it with the adaptor,
+ *					simply don't connect this pin.
+ *
+ * So to get a working adaptor, you need to connect the mouse with three
+ * wires to a RS232 port and two or three additional wires for +5V, +12V and
+ * -12V to the PSU.
+ *
+ * Flow specification for the link is 4800, 8o1.
+ *
+ * The mice and tablet are described in "VCB02 Video Subsystem - Technical
+ * Manual", DEC EK-104AA-TM-001. You'll find it at MANX, a search engine
+ * specific for DEC documentation. Try
+ * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet"
+
+MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#undef VSXXXAA_DEBUG
+#ifdef VSXXXAA_DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...) do {} while (0)
+#endif
+
+#define VSXXXAA_INTRO_MASK	0x80
+#define VSXXXAA_INTRO_HEAD	0x80
+#define IS_HDR_BYTE(x)			\
+	(((x) & VSXXXAA_INTRO_MASK) == VSXXXAA_INTRO_HEAD)
+
+#define VSXXXAA_PACKET_MASK	0xe0
+#define VSXXXAA_PACKET_REL	0x80
+#define VSXXXAA_PACKET_ABS	0xc0
+#define VSXXXAA_PACKET_POR	0xa0
+#define MATCH_PACKET_TYPE(data, type)	\
+	(((data) & VSXXXAA_PACKET_MASK) == (type))
+
+
+
+struct vsxxxaa {
+	struct input_dev *dev;
+	struct serio *serio;
+#define BUFLEN 15 /* At least 5 is needed for a full tablet packet */
+	unsigned char buf[BUFLEN];
+	unsigned char count;
+	unsigned char version;
+	unsigned char country;
+	unsigned char type;
+	char name[64];
+	char phys[32];
+};
+
+static void vsxxxaa_drop_bytes(struct vsxxxaa *mouse, int num)
+{
+	if (num >= mouse->count) {
+		mouse->count = 0;
+	} else {
+		memmove(mouse->buf, mouse->buf + num, BUFLEN - num);
+		mouse->count -= num;
+	}
+}
+
+static void vsxxxaa_queue_byte(struct vsxxxaa *mouse, unsigned char byte)
+{
+	if (mouse->count == BUFLEN) {
+		printk(KERN_ERR "%s on %s: Dropping a byte of full buffer.\n",
+			mouse->name, mouse->phys);
+		vsxxxaa_drop_bytes(mouse, 1);
+	}
+
+	DBG(KERN_INFO "Queueing byte 0x%02x\n", byte);
+
+	mouse->buf[mouse->count++] = byte;
+}
+
+static void vsxxxaa_detection_done(struct vsxxxaa *mouse)
+{
+	switch (mouse->type) {
+	case 0x02:
+		strlcpy(mouse->name, "DEC VSXXX-AA/-GA mouse",
+			sizeof(mouse->name));
+		break;
+
+	case 0x04:
+		strlcpy(mouse->name, "DEC VSXXX-AB digitizer",
+			sizeof(mouse->name));
+		break;
+
+	default:
+		snprintf(mouse->name, sizeof(mouse->name),
+			 "unknown DEC pointer device (type = 0x%02x)",
+			 mouse->type);
+		break;
+	}
+
+	printk(KERN_INFO
+		"Found %s version 0x%02x from country 0x%02x on port %s\n",
+		mouse->name, mouse->version, mouse->country, mouse->phys);
+}
+
+/*
+ * Returns number of bytes to be dropped, 0 if packet is okay.
+ */
+static int vsxxxaa_check_packet(struct vsxxxaa *mouse, int packet_len)
+{
+	int i;
+
+	/* First byte must be a header byte */
+	if (!IS_HDR_BYTE(mouse->buf[0])) {
+		DBG("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]);
+		return 1;
+	}
+
+	/* Check all following bytes */
+	for (i = 1; i < packet_len; i++) {
+		if (IS_HDR_BYTE(mouse->buf[i])) {
+			printk(KERN_ERR
+				"Need to drop %d bytes of a broken packet.\n",
+				i - 1);
+			DBG(KERN_INFO "check: len=%d, b[%d]=0x%02x\n",
+			    packet_len, i, mouse->buf[i]);
+			return i - 1;
+		}
+	}
+
+	return 0;
+}
+
+static inline int vsxxxaa_smells_like_packet(struct vsxxxaa *mouse,
+					     unsigned char type, size_t len)
+{
+	return mouse->count >= len && MATCH_PACKET_TYPE(mouse->buf[0], type);
+}
+
+static void vsxxxaa_handle_REL_packet(struct vsxxxaa *mouse)
+{
+	struct input_dev *dev = mouse->dev;
+	unsigned char *buf = mouse->buf;
+	int left, middle, right;
+	int dx, dy;
+
+	/*
+	 * Check for normal stream packets. This is three bytes,
+	 * with the first byte's 3 MSB set to 100.
+	 *
+	 * [0]:	1	0	0	SignX	SignY	Left	Middle	Right
+	 * [1]: 0	dx	dx	dx	dx	dx	dx	dx
+	 * [2]:	0	dy	dy	dy	dy	dy	dy	dy
+	 */
+
+	/*
+	 * Low 7 bit of byte 1 are abs(dx), bit 7 is
+	 * 0, bit 4 of byte 0 is direction.
+	 */
+	dx = buf[1] & 0x7f;
+	dx *= ((buf[0] >> 4) & 0x01) ? 1 : -1;
+
+	/*
+	 * Low 7 bit of byte 2 are abs(dy), bit 7 is
+	 * 0, bit 3 of byte 0 is direction.
+	 */
+	dy = buf[2] & 0x7f;
+	dy *= ((buf[0] >> 3) & 0x01) ? -1 : 1;
+
+	/*
+	 * Get button state. It's the low three bits
+	 * (for three buttons) of byte 0.
+	 */
+	left	= buf[0] & 0x04;
+	middle	= buf[0] & 0x02;
+	right	= buf[0] & 0x01;
+
+	vsxxxaa_drop_bytes(mouse, 3);
+
+	DBG(KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n",
+	    mouse->name, mouse->phys, dx, dy,
+	    left ? "L" : "l", middle ? "M" : "m", right ? "R" : "r");
+
+	/*
+	 * Report what we've found so far...
+	 */
+	input_report_key(dev, BTN_LEFT, left);
+	input_report_key(dev, BTN_MIDDLE, middle);
+	input_report_key(dev, BTN_RIGHT, right);
+	input_report_key(dev, BTN_TOUCH, 0);
+	input_report_rel(dev, REL_X, dx);
+	input_report_rel(dev, REL_Y, dy);
+	input_sync(dev);
+}
+
+static void vsxxxaa_handle_ABS_packet(struct vsxxxaa *mouse)
+{
+	struct input_dev *dev = mouse->dev;
+	unsigned char *buf = mouse->buf;
+	int left, middle, right, touch;
+	int x, y;
+
+	/*
+	 * Tablet position / button packet
+	 *
+	 * [0]:	1	1	0	B4	B3	B2	B1	Pr
+	 * [1]:	0	0	X5	X4	X3	X2	X1	X0
+	 * [2]:	0	0	X11	X10	X9	X8	X7	X6
+	 * [3]:	0	0	Y5	Y4	Y3	Y2	Y1	Y0
+	 * [4]:	0	0	Y11	Y10	Y9	Y8	Y7	Y6
+	 */
+
+	/*
+	 * Get X/Y position. Y axis needs to be inverted since VSXXX-AB
+	 * counts down->top while monitor counts top->bottom.
+	 */
+	x = ((buf[2] & 0x3f) << 6) | (buf[1] & 0x3f);
+	y = ((buf[4] & 0x3f) << 6) | (buf[3] & 0x3f);
+	y = 1023 - y;
+
+	/*
+	 * Get button state. It's bits <4..1> of byte 0.
+	 */
+	left	= buf[0] & 0x02;
+	middle	= buf[0] & 0x04;
+	right	= buf[0] & 0x08;
+	touch	= buf[0] & 0x10;
+
+	vsxxxaa_drop_bytes(mouse, 5);
+
+	DBG(KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n",
+	    mouse->name, mouse->phys, x, y,
+	    left ? "L" : "l", middle ? "M" : "m",
+	    right ? "R" : "r", touch ? "T" : "t");
+
+	/*
+	 * Report what we've found so far...
+	 */
+	input_report_key(dev, BTN_LEFT, left);
+	input_report_key(dev, BTN_MIDDLE, middle);
+	input_report_key(dev, BTN_RIGHT, right);
+	input_report_key(dev, BTN_TOUCH, touch);
+	input_report_abs(dev, ABS_X, x);
+	input_report_abs(dev, ABS_Y, y);
+	input_sync(dev);
+}
+
+static void vsxxxaa_handle_POR_packet(struct vsxxxaa *mouse)
+{
+	struct input_dev *dev = mouse->dev;
+	unsigned char *buf = mouse->buf;
+	int left, middle, right;
+	unsigned char error;
+
+	/*
+	 * Check for Power-On-Reset packets. These are sent out
+	 * after plugging the mouse in, or when explicitly
+	 * requested by sending 'T'.
+	 *
+	 * [0]:	1	0	1	0	R3	R2	R1	R0
+	 * [1]:	0	M2	M1	M0	D3	D2	D1	D0
+	 * [2]:	0	E6	E5	E4	E3	E2	E1	E0
+	 * [3]:	0	0	0	0	0	Left	Middle	Right
+	 *
+	 * M: manufacturer location code
+	 * R: revision code
+	 * E: Error code. If it's in the range of 0x00..0x1f, only some
+	 *    minor problem occurred. Errors >= 0x20 are considered bad
+	 *    and the device may not work properly...
+	 * D: <0010> == mouse, <0100> == tablet
+	 */
+
+	mouse->version = buf[0] & 0x0f;
+	mouse->country = (buf[1] >> 4) & 0x07;
+	mouse->type = buf[1] & 0x0f;
+	error = buf[2] & 0x7f;
+
+	/*
+	 * Get button state. It's the low three bits
+	 * (for three buttons) of byte 0. Maybe even the bit <3>
+	 * has some meaning if a tablet is attached.
+	 */
+	left	= buf[0] & 0x04;
+	middle	= buf[0] & 0x02;
+	right	= buf[0] & 0x01;
+
+	vsxxxaa_drop_bytes(mouse, 4);
+	vsxxxaa_detection_done(mouse);
+
+	if (error <= 0x1f) {
+		/* No (serious) error. Report buttons */
+		input_report_key(dev, BTN_LEFT, left);
+		input_report_key(dev, BTN_MIDDLE, middle);
+		input_report_key(dev, BTN_RIGHT, right);
+		input_report_key(dev, BTN_TOUCH, 0);
+		input_sync(dev);
+
+		if (error != 0)
+			printk(KERN_INFO "Your %s on %s reports error=0x%02x\n",
+				mouse->name, mouse->phys, error);
+
+	}
+
+	/*
+	 * If the mouse was hot-plugged, we need to force differential mode
+	 * now... However, give it a second to recover from it's reset.
+	 */
+	printk(KERN_NOTICE
+		"%s on %s: Forcing standard packet format, "
+		"incremental streaming mode and 72 samples/sec\n",
+		mouse->name, mouse->phys);
+	serio_write(mouse->serio, 'S');	/* Standard format */
+	mdelay(50);
+	serio_write(mouse->serio, 'R');	/* Incremental */
+	mdelay(50);
+	serio_write(mouse->serio, 'L');	/* 72 samples/sec */
+}
+
+static void vsxxxaa_parse_buffer(struct vsxxxaa *mouse)
+{
+	unsigned char *buf = mouse->buf;
+	int stray_bytes;
+
+	/*
+	 * Parse buffer to death...
+	 */
+	do {
+		/*
+		 * Out of sync? Throw away what we don't understand. Each
+		 * packet starts with a byte whose bit 7 is set. Unhandled
+		 * packets (ie. which we don't know about or simply b0rk3d
+		 * data...) will get shifted out of the buffer after some
+		 * activity on the mouse.
+		 */
+		while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) {
+			printk(KERN_ERR "%s on %s: Dropping a byte to regain "
+				"sync with mouse data stream...\n",
+				mouse->name, mouse->phys);
+			vsxxxaa_drop_bytes(mouse, 1);
+		}
+
+		/*
+		 * Check for packets we know about.
+		 */
+
+		if (vsxxxaa_smells_like_packet(mouse, VSXXXAA_PACKET_REL, 3)) {
+			/* Check for broken packet */
+			stray_bytes = vsxxxaa_check_packet(mouse, 3);
+			if (!stray_bytes)
+				vsxxxaa_handle_REL_packet(mouse);
+
+		} else if (vsxxxaa_smells_like_packet(mouse,
+						      VSXXXAA_PACKET_ABS, 5)) {
+			/* Check for broken packet */
+			stray_bytes = vsxxxaa_check_packet(mouse, 5);
+			if (!stray_bytes)
+				vsxxxaa_handle_ABS_packet(mouse);
+
+		} else if (vsxxxaa_smells_like_packet(mouse,
+						      VSXXXAA_PACKET_POR, 4)) {
+			/* Check for broken packet */
+			stray_bytes = vsxxxaa_check_packet(mouse, 4);
+			if (!stray_bytes)
+				vsxxxaa_handle_POR_packet(mouse);
+
+		} else {
+			break; /* No REL, ABS or POR packet found */
+		}
+
+		if (stray_bytes > 0) {
+			printk(KERN_ERR "Dropping %d bytes now...\n",
+				stray_bytes);
+			vsxxxaa_drop_bytes(mouse, stray_bytes);
+		}
+
+	} while (1);
+}
+
+static irqreturn_t vsxxxaa_interrupt(struct serio *serio,
+				     unsigned char data, unsigned int flags)
+{
+	struct vsxxxaa *mouse = serio_get_drvdata(serio);
+
+	vsxxxaa_queue_byte(mouse, data);
+	vsxxxaa_parse_buffer(mouse);
+
+	return IRQ_HANDLED;
+}
+
+static void vsxxxaa_disconnect(struct serio *serio)
+{
+	struct vsxxxaa *mouse = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(mouse->dev);
+	kfree(mouse);
+}
+
+static int vsxxxaa_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct vsxxxaa *mouse;
+	struct input_dev *input_dev;
+	int err = -ENOMEM;
+
+	mouse = kzalloc(sizeof(struct vsxxxaa), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!mouse || !input_dev)
+		goto fail1;
+
+	mouse->dev = input_dev;
+	mouse->serio = serio;
+	strlcat(mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer",
+		 sizeof(mouse->name));
+	snprintf(mouse->phys, sizeof(mouse->phys), "%s/input0", serio->phys);
+
+	input_dev->name = mouse->name;
+	input_dev->phys = mouse->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->dev.parent = &serio->dev;
+
+	__set_bit(EV_KEY, input_dev->evbit);		/* We have buttons */
+	__set_bit(EV_REL, input_dev->evbit);
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(BTN_LEFT, input_dev->keybit);		/* We have 3 buttons */
+	__set_bit(BTN_MIDDLE, input_dev->keybit);
+	__set_bit(BTN_RIGHT, input_dev->keybit);
+	__set_bit(BTN_TOUCH, input_dev->keybit);	/* ...and Tablet */
+	__set_bit(REL_X, input_dev->relbit);
+	__set_bit(REL_Y, input_dev->relbit);
+	input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
+
+	serio_set_drvdata(serio, mouse);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	/*
+	 * Request selftest. Standard packet format and differential
+	 * mode will be requested after the device ID'ed successfully.
+	 */
+	serio_write(serio, 'T'); /* Test */
+
+	err = input_register_device(input_dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(mouse);
+	return err;
+}
+
+static struct serio_device_id vsxxaa_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_VSXXXAA,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, vsxxaa_serio_ids);
+
+static struct serio_driver vsxxxaa_drv = {
+	.driver		= {
+		.name	= "vsxxxaa",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= vsxxaa_serio_ids,
+	.connect	= vsxxxaa_connect,
+	.interrupt	= vsxxxaa_interrupt,
+	.disconnect	= vsxxxaa_disconnect,
+};
+
+module_serio_driver(vsxxxaa_drv);
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
new file mode 100644
index 0000000..b604564
--- /dev/null
+++ b/drivers/input/mousedev.c
@@ -0,0 +1,1128 @@
+/*
+ * Input driver to ExplorerPS/2 device driver module.
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 2004      Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MOUSEDEV_MINOR_BASE	32
+#define MOUSEDEV_MINORS		31
+#define MOUSEDEV_MIX		63
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/random.h>
+#include <linux/major.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/kernel.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Mouse (ExplorerPS/2) device interfaces");
+MODULE_LICENSE("GPL");
+
+#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X
+#define CONFIG_INPUT_MOUSEDEV_SCREEN_X	1024
+#endif
+#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y
+#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y	768
+#endif
+
+static int xres = CONFIG_INPUT_MOUSEDEV_SCREEN_X;
+module_param(xres, uint, 0644);
+MODULE_PARM_DESC(xres, "Horizontal screen resolution");
+
+static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y;
+module_param(yres, uint, 0644);
+MODULE_PARM_DESC(yres, "Vertical screen resolution");
+
+static unsigned tap_time = 200;
+module_param(tap_time, uint, 0644);
+MODULE_PARM_DESC(tap_time, "Tap time for touchpads in absolute mode (msecs)");
+
+struct mousedev_hw_data {
+	int dx, dy, dz;
+	int x, y;
+	int abs_event;
+	unsigned long buttons;
+};
+
+struct mousedev {
+	int open;
+	struct input_handle handle;
+	wait_queue_head_t wait;
+	struct list_head client_list;
+	spinlock_t client_lock; /* protects client_list */
+	struct mutex mutex;
+	struct device dev;
+	struct cdev cdev;
+	bool exist;
+
+	struct list_head mixdev_node;
+	bool opened_by_mixdev;
+
+	struct mousedev_hw_data packet;
+	unsigned int pkt_count;
+	int old_x[4], old_y[4];
+	int frac_dx, frac_dy;
+	unsigned long touch;
+
+	int (*open_device)(struct mousedev *mousedev);
+	void (*close_device)(struct mousedev *mousedev);
+};
+
+enum mousedev_emul {
+	MOUSEDEV_EMUL_PS2,
+	MOUSEDEV_EMUL_IMPS,
+	MOUSEDEV_EMUL_EXPS
+};
+
+struct mousedev_motion {
+	int dx, dy, dz;
+	unsigned long buttons;
+};
+
+#define PACKET_QUEUE_LEN	16
+struct mousedev_client {
+	struct fasync_struct *fasync;
+	struct mousedev *mousedev;
+	struct list_head node;
+
+	struct mousedev_motion packets[PACKET_QUEUE_LEN];
+	unsigned int head, tail;
+	spinlock_t packet_lock;
+	int pos_x, pos_y;
+
+	signed char ps2[6];
+	unsigned char ready, buffer, bufsiz;
+	unsigned char imexseq, impsseq;
+	enum mousedev_emul mode;
+	unsigned long last_buttons;
+};
+
+#define MOUSEDEV_SEQ_LEN	6
+
+static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
+static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
+
+static struct mousedev *mousedev_mix;
+static LIST_HEAD(mousedev_mix_list);
+
+#define fx(i)  (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
+#define fy(i)  (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
+
+static void mousedev_touchpad_event(struct input_dev *dev,
+				    struct mousedev *mousedev,
+				    unsigned int code, int value)
+{
+	int size, tmp;
+	enum { FRACTION_DENOM = 128 };
+
+	switch (code) {
+
+	case ABS_X:
+
+		fx(0) = value;
+		if (mousedev->touch && mousedev->pkt_count >= 2) {
+			size = input_abs_get_max(dev, ABS_X) -
+					input_abs_get_min(dev, ABS_X);
+			if (size == 0)
+				size = 256 * 2;
+
+			tmp = ((value - fx(2)) * 256 * FRACTION_DENOM) / size;
+			tmp += mousedev->frac_dx;
+			mousedev->packet.dx = tmp / FRACTION_DENOM;
+			mousedev->frac_dx =
+				tmp - mousedev->packet.dx * FRACTION_DENOM;
+		}
+		break;
+
+	case ABS_Y:
+		fy(0) = value;
+		if (mousedev->touch && mousedev->pkt_count >= 2) {
+			/* use X size for ABS_Y to keep the same scale */
+			size = input_abs_get_max(dev, ABS_X) -
+					input_abs_get_min(dev, ABS_X);
+			if (size == 0)
+				size = 256 * 2;
+
+			tmp = -((value - fy(2)) * 256 * FRACTION_DENOM) / size;
+			tmp += mousedev->frac_dy;
+			mousedev->packet.dy = tmp / FRACTION_DENOM;
+			mousedev->frac_dy = tmp -
+				mousedev->packet.dy * FRACTION_DENOM;
+		}
+		break;
+	}
+}
+
+static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
+				unsigned int code, int value)
+{
+	int min, max, size;
+
+	switch (code) {
+
+	case ABS_X:
+		min = input_abs_get_min(dev, ABS_X);
+		max = input_abs_get_max(dev, ABS_X);
+
+		size = max - min;
+		if (size == 0)
+			size = xres ? : 1;
+
+		value = clamp(value, min, max);
+
+		mousedev->packet.x = ((value - min) * xres) / size;
+		mousedev->packet.abs_event = 1;
+		break;
+
+	case ABS_Y:
+		min = input_abs_get_min(dev, ABS_Y);
+		max = input_abs_get_max(dev, ABS_Y);
+
+		size = max - min;
+		if (size == 0)
+			size = yres ? : 1;
+
+		value = clamp(value, min, max);
+
+		mousedev->packet.y = yres - ((value - min) * yres) / size;
+		mousedev->packet.abs_event = 1;
+		break;
+	}
+}
+
+static void mousedev_rel_event(struct mousedev *mousedev,
+				unsigned int code, int value)
+{
+	switch (code) {
+	case REL_X:
+		mousedev->packet.dx += value;
+		break;
+
+	case REL_Y:
+		mousedev->packet.dy -= value;
+		break;
+
+	case REL_WHEEL:
+		mousedev->packet.dz -= value;
+		break;
+	}
+}
+
+static void mousedev_key_event(struct mousedev *mousedev,
+				unsigned int code, int value)
+{
+	int index;
+
+	switch (code) {
+
+	case BTN_TOUCH:
+	case BTN_0:
+	case BTN_LEFT:		index = 0; break;
+
+	case BTN_STYLUS:
+	case BTN_1:
+	case BTN_RIGHT:		index = 1; break;
+
+	case BTN_2:
+	case BTN_FORWARD:
+	case BTN_STYLUS2:
+	case BTN_MIDDLE:	index = 2; break;
+
+	case BTN_3:
+	case BTN_BACK:
+	case BTN_SIDE:		index = 3; break;
+
+	case BTN_4:
+	case BTN_EXTRA:		index = 4; break;
+
+	default:		return;
+	}
+
+	if (value) {
+		set_bit(index, &mousedev->packet.buttons);
+		set_bit(index, &mousedev_mix->packet.buttons);
+	} else {
+		clear_bit(index, &mousedev->packet.buttons);
+		clear_bit(index, &mousedev_mix->packet.buttons);
+	}
+}
+
+static void mousedev_notify_readers(struct mousedev *mousedev,
+				    struct mousedev_hw_data *packet)
+{
+	struct mousedev_client *client;
+	struct mousedev_motion *p;
+	unsigned int new_head;
+	int wake_readers = 0;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(client, &mousedev->client_list, node) {
+
+		/* Just acquire the lock, interrupts already disabled */
+		spin_lock(&client->packet_lock);
+
+		p = &client->packets[client->head];
+		if (client->ready && p->buttons != mousedev->packet.buttons) {
+			new_head = (client->head + 1) % PACKET_QUEUE_LEN;
+			if (new_head != client->tail) {
+				p = &client->packets[client->head = new_head];
+				memset(p, 0, sizeof(struct mousedev_motion));
+			}
+		}
+
+		if (packet->abs_event) {
+			p->dx += packet->x - client->pos_x;
+			p->dy += packet->y - client->pos_y;
+			client->pos_x = packet->x;
+			client->pos_y = packet->y;
+		}
+
+		client->pos_x += packet->dx;
+		client->pos_x = client->pos_x < 0 ?
+			0 : (client->pos_x >= xres ? xres : client->pos_x);
+		client->pos_y += packet->dy;
+		client->pos_y = client->pos_y < 0 ?
+			0 : (client->pos_y >= yres ? yres : client->pos_y);
+
+		p->dx += packet->dx;
+		p->dy += packet->dy;
+		p->dz += packet->dz;
+		p->buttons = mousedev->packet.buttons;
+
+		if (p->dx || p->dy || p->dz ||
+		    p->buttons != client->last_buttons)
+			client->ready = 1;
+
+		spin_unlock(&client->packet_lock);
+
+		if (client->ready) {
+			kill_fasync(&client->fasync, SIGIO, POLL_IN);
+			wake_readers = 1;
+		}
+	}
+	rcu_read_unlock();
+
+	if (wake_readers)
+		wake_up_interruptible(&mousedev->wait);
+}
+
+static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
+{
+	if (!value) {
+		if (mousedev->touch &&
+		    time_before(jiffies,
+				mousedev->touch + msecs_to_jiffies(tap_time))) {
+			/*
+			 * Toggle left button to emulate tap.
+			 * We rely on the fact that mousedev_mix always has 0
+			 * motion packet so we won't mess current position.
+			 */
+			set_bit(0, &mousedev->packet.buttons);
+			set_bit(0, &mousedev_mix->packet.buttons);
+			mousedev_notify_readers(mousedev, &mousedev_mix->packet);
+			mousedev_notify_readers(mousedev_mix,
+						&mousedev_mix->packet);
+			clear_bit(0, &mousedev->packet.buttons);
+			clear_bit(0, &mousedev_mix->packet.buttons);
+		}
+		mousedev->touch = mousedev->pkt_count = 0;
+		mousedev->frac_dx = 0;
+		mousedev->frac_dy = 0;
+
+	} else if (!mousedev->touch)
+		mousedev->touch = jiffies;
+}
+
+static void mousedev_event(struct input_handle *handle,
+			   unsigned int type, unsigned int code, int value)
+{
+	struct mousedev *mousedev = handle->private;
+
+	switch (type) {
+
+	case EV_ABS:
+		/* Ignore joysticks */
+		if (test_bit(BTN_TRIGGER, handle->dev->keybit))
+			return;
+
+		if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
+			mousedev_touchpad_event(handle->dev,
+						mousedev, code, value);
+		else
+			mousedev_abs_event(handle->dev, mousedev, code, value);
+
+		break;
+
+	case EV_REL:
+		mousedev_rel_event(mousedev, code, value);
+		break;
+
+	case EV_KEY:
+		if (value != 2) {
+			if (code == BTN_TOUCH &&
+			    test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
+				mousedev_touchpad_touch(mousedev, value);
+			else
+				mousedev_key_event(mousedev, code, value);
+		}
+		break;
+
+	case EV_SYN:
+		if (code == SYN_REPORT) {
+			if (mousedev->touch) {
+				mousedev->pkt_count++;
+				/*
+				 * Input system eats duplicate events,
+				 * but we need all of them to do correct
+				 * averaging so apply present one forward
+				 */
+				fx(0) = fx(1);
+				fy(0) = fy(1);
+			}
+
+			mousedev_notify_readers(mousedev, &mousedev->packet);
+			mousedev_notify_readers(mousedev_mix, &mousedev->packet);
+
+			mousedev->packet.dx = mousedev->packet.dy =
+				mousedev->packet.dz = 0;
+			mousedev->packet.abs_event = 0;
+		}
+		break;
+	}
+}
+
+static int mousedev_fasync(int fd, struct file *file, int on)
+{
+	struct mousedev_client *client = file->private_data;
+
+	return fasync_helper(fd, file, on, &client->fasync);
+}
+
+static void mousedev_free(struct device *dev)
+{
+	struct mousedev *mousedev = container_of(dev, struct mousedev, dev);
+
+	input_put_device(mousedev->handle.dev);
+	kfree(mousedev);
+}
+
+static int mousedev_open_device(struct mousedev *mousedev)
+{
+	int retval;
+
+	retval = mutex_lock_interruptible(&mousedev->mutex);
+	if (retval)
+		return retval;
+
+	if (!mousedev->exist)
+		retval = -ENODEV;
+	else if (!mousedev->open++) {
+		retval = input_open_device(&mousedev->handle);
+		if (retval)
+			mousedev->open--;
+	}
+
+	mutex_unlock(&mousedev->mutex);
+	return retval;
+}
+
+static void mousedev_close_device(struct mousedev *mousedev)
+{
+	mutex_lock(&mousedev->mutex);
+
+	if (mousedev->exist && !--mousedev->open)
+		input_close_device(&mousedev->handle);
+
+	mutex_unlock(&mousedev->mutex);
+}
+
+/*
+ * Open all available devices so they can all be multiplexed in one.
+ * stream. Note that this function is called with mousedev_mix->mutex
+ * held.
+ */
+static int mixdev_open_devices(struct mousedev *mixdev)
+{
+	int error;
+
+	error = mutex_lock_interruptible(&mixdev->mutex);
+	if (error)
+		return error;
+
+	if (!mixdev->open++) {
+		struct mousedev *mousedev;
+
+		list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
+			if (!mousedev->opened_by_mixdev) {
+				if (mousedev_open_device(mousedev))
+					continue;
+
+				mousedev->opened_by_mixdev = true;
+			}
+		}
+	}
+
+	mutex_unlock(&mixdev->mutex);
+	return 0;
+}
+
+/*
+ * Close all devices that were opened as part of multiplexed
+ * device. Note that this function is called with mousedev_mix->mutex
+ * held.
+ */
+static void mixdev_close_devices(struct mousedev *mixdev)
+{
+	mutex_lock(&mixdev->mutex);
+
+	if (!--mixdev->open) {
+		struct mousedev *mousedev;
+
+		list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
+			if (mousedev->opened_by_mixdev) {
+				mousedev->opened_by_mixdev = false;
+				mousedev_close_device(mousedev);
+			}
+		}
+	}
+
+	mutex_unlock(&mixdev->mutex);
+}
+
+
+static void mousedev_attach_client(struct mousedev *mousedev,
+				   struct mousedev_client *client)
+{
+	spin_lock(&mousedev->client_lock);
+	list_add_tail_rcu(&client->node, &mousedev->client_list);
+	spin_unlock(&mousedev->client_lock);
+}
+
+static void mousedev_detach_client(struct mousedev *mousedev,
+				   struct mousedev_client *client)
+{
+	spin_lock(&mousedev->client_lock);
+	list_del_rcu(&client->node);
+	spin_unlock(&mousedev->client_lock);
+	synchronize_rcu();
+}
+
+static int mousedev_release(struct inode *inode, struct file *file)
+{
+	struct mousedev_client *client = file->private_data;
+	struct mousedev *mousedev = client->mousedev;
+
+	mousedev_detach_client(mousedev, client);
+	kfree(client);
+
+	mousedev->close_device(mousedev);
+
+	return 0;
+}
+
+static int mousedev_open(struct inode *inode, struct file *file)
+{
+	struct mousedev_client *client;
+	struct mousedev *mousedev;
+	int error;
+
+#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
+	if (imajor(inode) == MISC_MAJOR)
+		mousedev = mousedev_mix;
+	else
+#endif
+		mousedev = container_of(inode->i_cdev, struct mousedev, cdev);
+
+	client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL);
+	if (!client)
+		return -ENOMEM;
+
+	spin_lock_init(&client->packet_lock);
+	client->pos_x = xres / 2;
+	client->pos_y = yres / 2;
+	client->mousedev = mousedev;
+	mousedev_attach_client(mousedev, client);
+
+	error = mousedev->open_device(mousedev);
+	if (error)
+		goto err_free_client;
+
+	file->private_data = client;
+	nonseekable_open(inode, file);
+
+	return 0;
+
+ err_free_client:
+	mousedev_detach_client(mousedev, client);
+	kfree(client);
+	return error;
+}
+
+static inline int mousedev_limit_delta(int delta, int limit)
+{
+	return delta > limit ? limit : (delta < -limit ? -limit : delta);
+}
+
+static void mousedev_packet(struct mousedev_client *client,
+			    signed char *ps2_data)
+{
+	struct mousedev_motion *p = &client->packets[client->tail];
+
+	ps2_data[0] = 0x08 |
+		((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
+	ps2_data[1] = mousedev_limit_delta(p->dx, 127);
+	ps2_data[2] = mousedev_limit_delta(p->dy, 127);
+	p->dx -= ps2_data[1];
+	p->dy -= ps2_data[2];
+
+	switch (client->mode) {
+	case MOUSEDEV_EMUL_EXPS:
+		ps2_data[3] = mousedev_limit_delta(p->dz, 7);
+		p->dz -= ps2_data[3];
+		ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
+		client->bufsiz = 4;
+		break;
+
+	case MOUSEDEV_EMUL_IMPS:
+		ps2_data[0] |=
+			((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
+		ps2_data[3] = mousedev_limit_delta(p->dz, 127);
+		p->dz -= ps2_data[3];
+		client->bufsiz = 4;
+		break;
+
+	case MOUSEDEV_EMUL_PS2:
+	default:
+		ps2_data[0] |=
+			((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
+		p->dz = 0;
+		client->bufsiz = 3;
+		break;
+	}
+
+	if (!p->dx && !p->dy && !p->dz) {
+		if (client->tail == client->head) {
+			client->ready = 0;
+			client->last_buttons = p->buttons;
+		} else
+			client->tail = (client->tail + 1) % PACKET_QUEUE_LEN;
+	}
+}
+
+static void mousedev_generate_response(struct mousedev_client *client,
+					int command)
+{
+	client->ps2[0] = 0xfa; /* ACK */
+
+	switch (command) {
+
+	case 0xeb: /* Poll */
+		mousedev_packet(client, &client->ps2[1]);
+		client->bufsiz++; /* account for leading ACK */
+		break;
+
+	case 0xf2: /* Get ID */
+		switch (client->mode) {
+		case MOUSEDEV_EMUL_PS2:
+			client->ps2[1] = 0;
+			break;
+		case MOUSEDEV_EMUL_IMPS:
+			client->ps2[1] = 3;
+			break;
+		case MOUSEDEV_EMUL_EXPS:
+			client->ps2[1] = 4;
+			break;
+		}
+		client->bufsiz = 2;
+		break;
+
+	case 0xe9: /* Get info */
+		client->ps2[1] = 0x60; client->ps2[2] = 3; client->ps2[3] = 200;
+		client->bufsiz = 4;
+		break;
+
+	case 0xff: /* Reset */
+		client->impsseq = client->imexseq = 0;
+		client->mode = MOUSEDEV_EMUL_PS2;
+		client->ps2[1] = 0xaa; client->ps2[2] = 0x00;
+		client->bufsiz = 3;
+		break;
+
+	default:
+		client->bufsiz = 1;
+		break;
+	}
+	client->buffer = client->bufsiz;
+}
+
+static ssize_t mousedev_write(struct file *file, const char __user *buffer,
+				size_t count, loff_t *ppos)
+{
+	struct mousedev_client *client = file->private_data;
+	unsigned char c;
+	unsigned int i;
+
+	for (i = 0; i < count; i++) {
+
+		if (get_user(c, buffer + i))
+			return -EFAULT;
+
+		spin_lock_irq(&client->packet_lock);
+
+		if (c == mousedev_imex_seq[client->imexseq]) {
+			if (++client->imexseq == MOUSEDEV_SEQ_LEN) {
+				client->imexseq = 0;
+				client->mode = MOUSEDEV_EMUL_EXPS;
+			}
+		} else
+			client->imexseq = 0;
+
+		if (c == mousedev_imps_seq[client->impsseq]) {
+			if (++client->impsseq == MOUSEDEV_SEQ_LEN) {
+				client->impsseq = 0;
+				client->mode = MOUSEDEV_EMUL_IMPS;
+			}
+		} else
+			client->impsseq = 0;
+
+		mousedev_generate_response(client, c);
+
+		spin_unlock_irq(&client->packet_lock);
+	}
+
+	kill_fasync(&client->fasync, SIGIO, POLL_IN);
+	wake_up_interruptible(&client->mousedev->wait);
+
+	return count;
+}
+
+static ssize_t mousedev_read(struct file *file, char __user *buffer,
+			     size_t count, loff_t *ppos)
+{
+	struct mousedev_client *client = file->private_data;
+	struct mousedev *mousedev = client->mousedev;
+	signed char data[sizeof(client->ps2)];
+	int retval = 0;
+
+	if (!client->ready && !client->buffer && mousedev->exist &&
+	    (file->f_flags & O_NONBLOCK))
+		return -EAGAIN;
+
+	retval = wait_event_interruptible(mousedev->wait,
+			!mousedev->exist || client->ready || client->buffer);
+	if (retval)
+		return retval;
+
+	if (!mousedev->exist)
+		return -ENODEV;
+
+	spin_lock_irq(&client->packet_lock);
+
+	if (!client->buffer && client->ready) {
+		mousedev_packet(client, client->ps2);
+		client->buffer = client->bufsiz;
+	}
+
+	if (count > client->buffer)
+		count = client->buffer;
+
+	memcpy(data, client->ps2 + client->bufsiz - client->buffer, count);
+	client->buffer -= count;
+
+	spin_unlock_irq(&client->packet_lock);
+
+	if (copy_to_user(buffer, data, count))
+		return -EFAULT;
+
+	return count;
+}
+
+/* No kernel lock - fine */
+static unsigned int mousedev_poll(struct file *file, poll_table *wait)
+{
+	struct mousedev_client *client = file->private_data;
+	struct mousedev *mousedev = client->mousedev;
+	unsigned int mask;
+
+	poll_wait(file, &mousedev->wait, wait);
+
+	mask = mousedev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR;
+	if (client->ready || client->buffer)
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+static const struct file_operations mousedev_fops = {
+	.owner		= THIS_MODULE,
+	.read		= mousedev_read,
+	.write		= mousedev_write,
+	.poll		= mousedev_poll,
+	.open		= mousedev_open,
+	.release	= mousedev_release,
+	.fasync		= mousedev_fasync,
+	.llseek		= noop_llseek,
+};
+
+/*
+ * Mark device non-existent. This disables writes, ioctls and
+ * prevents new users from opening the device. Already posted
+ * blocking reads will stay, however new ones will fail.
+ */
+static void mousedev_mark_dead(struct mousedev *mousedev)
+{
+	mutex_lock(&mousedev->mutex);
+	mousedev->exist = false;
+	mutex_unlock(&mousedev->mutex);
+}
+
+/*
+ * Wake up users waiting for IO so they can disconnect from
+ * dead device.
+ */
+static void mousedev_hangup(struct mousedev *mousedev)
+{
+	struct mousedev_client *client;
+
+	spin_lock(&mousedev->client_lock);
+	list_for_each_entry(client, &mousedev->client_list, node)
+		kill_fasync(&client->fasync, SIGIO, POLL_HUP);
+	spin_unlock(&mousedev->client_lock);
+
+	wake_up_interruptible(&mousedev->wait);
+}
+
+static void mousedev_cleanup(struct mousedev *mousedev)
+{
+	struct input_handle *handle = &mousedev->handle;
+
+	mousedev_mark_dead(mousedev);
+	mousedev_hangup(mousedev);
+
+	cdev_del(&mousedev->cdev);
+
+	/* mousedev is marked dead so no one else accesses mousedev->open */
+	if (mousedev->open)
+		input_close_device(handle);
+}
+
+static int mousedev_reserve_minor(bool mixdev)
+{
+	int minor;
+
+	if (mixdev) {
+		minor = input_get_new_minor(MOUSEDEV_MIX, 1, false);
+		if (minor < 0)
+			pr_err("failed to reserve mixdev minor: %d\n", minor);
+	} else {
+		minor = input_get_new_minor(MOUSEDEV_MINOR_BASE,
+					    MOUSEDEV_MINORS, true);
+		if (minor < 0)
+			pr_err("failed to reserve new minor: %d\n", minor);
+	}
+
+	return minor;
+}
+
+static struct mousedev *mousedev_create(struct input_dev *dev,
+					struct input_handler *handler,
+					bool mixdev)
+{
+	struct mousedev *mousedev;
+	int minor;
+	int error;
+
+	minor = mousedev_reserve_minor(mixdev);
+	if (minor < 0) {
+		error = minor;
+		goto err_out;
+	}
+
+	mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL);
+	if (!mousedev) {
+		error = -ENOMEM;
+		goto err_free_minor;
+	}
+
+	INIT_LIST_HEAD(&mousedev->client_list);
+	INIT_LIST_HEAD(&mousedev->mixdev_node);
+	spin_lock_init(&mousedev->client_lock);
+	mutex_init(&mousedev->mutex);
+	lockdep_set_subclass(&mousedev->mutex,
+			     mixdev ? SINGLE_DEPTH_NESTING : 0);
+	init_waitqueue_head(&mousedev->wait);
+
+	if (mixdev) {
+		dev_set_name(&mousedev->dev, "mice");
+
+		mousedev->open_device = mixdev_open_devices;
+		mousedev->close_device = mixdev_close_devices;
+	} else {
+		int dev_no = minor;
+		/* Normalize device number if it falls into legacy range */
+		if (dev_no < MOUSEDEV_MINOR_BASE + MOUSEDEV_MINORS)
+			dev_no -= MOUSEDEV_MINOR_BASE;
+		dev_set_name(&mousedev->dev, "mouse%d", dev_no);
+
+		mousedev->open_device = mousedev_open_device;
+		mousedev->close_device = mousedev_close_device;
+	}
+
+	mousedev->exist = true;
+	mousedev->handle.dev = input_get_device(dev);
+	mousedev->handle.name = dev_name(&mousedev->dev);
+	mousedev->handle.handler = handler;
+	mousedev->handle.private = mousedev;
+
+	mousedev->dev.class = &input_class;
+	if (dev)
+		mousedev->dev.parent = &dev->dev;
+	mousedev->dev.devt = MKDEV(INPUT_MAJOR, minor);
+	mousedev->dev.release = mousedev_free;
+	device_initialize(&mousedev->dev);
+
+	if (!mixdev) {
+		error = input_register_handle(&mousedev->handle);
+		if (error)
+			goto err_free_mousedev;
+	}
+
+	cdev_init(&mousedev->cdev, &mousedev_fops);
+	mousedev->cdev.kobj.parent = &mousedev->dev.kobj;
+	error = cdev_add(&mousedev->cdev, mousedev->dev.devt, 1);
+	if (error)
+		goto err_unregister_handle;
+
+	error = device_add(&mousedev->dev);
+	if (error)
+		goto err_cleanup_mousedev;
+
+	return mousedev;
+
+ err_cleanup_mousedev:
+	mousedev_cleanup(mousedev);
+ err_unregister_handle:
+	if (!mixdev)
+		input_unregister_handle(&mousedev->handle);
+ err_free_mousedev:
+	put_device(&mousedev->dev);
+ err_free_minor:
+	input_free_minor(minor);
+ err_out:
+	return ERR_PTR(error);
+}
+
+static void mousedev_destroy(struct mousedev *mousedev)
+{
+	device_del(&mousedev->dev);
+	mousedev_cleanup(mousedev);
+	input_free_minor(MINOR(mousedev->dev.devt));
+	if (mousedev != mousedev_mix)
+		input_unregister_handle(&mousedev->handle);
+	put_device(&mousedev->dev);
+}
+
+static int mixdev_add_device(struct mousedev *mousedev)
+{
+	int retval;
+
+	retval = mutex_lock_interruptible(&mousedev_mix->mutex);
+	if (retval)
+		return retval;
+
+	if (mousedev_mix->open) {
+		retval = mousedev_open_device(mousedev);
+		if (retval)
+			goto out;
+
+		mousedev->opened_by_mixdev = true;
+	}
+
+	get_device(&mousedev->dev);
+	list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list);
+
+ out:
+	mutex_unlock(&mousedev_mix->mutex);
+	return retval;
+}
+
+static void mixdev_remove_device(struct mousedev *mousedev)
+{
+	mutex_lock(&mousedev_mix->mutex);
+
+	if (mousedev->opened_by_mixdev) {
+		mousedev->opened_by_mixdev = false;
+		mousedev_close_device(mousedev);
+	}
+
+	list_del_init(&mousedev->mixdev_node);
+	mutex_unlock(&mousedev_mix->mutex);
+
+	put_device(&mousedev->dev);
+}
+
+static int mousedev_connect(struct input_handler *handler,
+			    struct input_dev *dev,
+			    const struct input_device_id *id)
+{
+	struct mousedev *mousedev;
+	int error;
+
+	mousedev = mousedev_create(dev, handler, false);
+	if (IS_ERR(mousedev))
+		return PTR_ERR(mousedev);
+
+	error = mixdev_add_device(mousedev);
+	if (error) {
+		mousedev_destroy(mousedev);
+		return error;
+	}
+
+	return 0;
+}
+
+static void mousedev_disconnect(struct input_handle *handle)
+{
+	struct mousedev *mousedev = handle->private;
+
+	mixdev_remove_device(mousedev);
+	mousedev_destroy(mousedev);
+}
+
+static const struct input_device_id mousedev_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+				INPUT_DEVICE_ID_MATCH_KEYBIT |
+				INPUT_DEVICE_ID_MATCH_RELBIT,
+		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) },
+		.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
+		.relbit = { BIT_MASK(REL_X) | BIT_MASK(REL_Y) },
+	},	/* A mouse like device, at least one button,
+		   two relative axes */
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+				INPUT_DEVICE_ID_MATCH_RELBIT,
+		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) },
+		.relbit = { BIT_MASK(REL_WHEEL) },
+	},	/* A separate scrollwheel */
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+				INPUT_DEVICE_ID_MATCH_KEYBIT |
+				INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
+		.keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
+		.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
+	},	/* A tablet like device, at least touch detection,
+		   two absolute axes */
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+				INPUT_DEVICE_ID_MATCH_KEYBIT |
+				INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
+		.keybit = { [BIT_WORD(BTN_TOOL_FINGER)] =
+				BIT_MASK(BTN_TOOL_FINGER) },
+		.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
+				BIT_MASK(ABS_PRESSURE) |
+				BIT_MASK(ABS_TOOL_WIDTH) },
+	},	/* A touchpad */
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+			INPUT_DEVICE_ID_MATCH_KEYBIT |
+			INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
+		.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
+		.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
+	},	/* Mouse-like device with absolute X and Y but ordinary
+		   clicks, like hp ILO2 High Performance mouse */
+
+	{ },	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, mousedev_ids);
+
+static struct input_handler mousedev_handler = {
+	.event		= mousedev_event,
+	.connect	= mousedev_connect,
+	.disconnect	= mousedev_disconnect,
+	.legacy_minors	= true,
+	.minor		= MOUSEDEV_MINOR_BASE,
+	.name		= "mousedev",
+	.id_table	= mousedev_ids,
+};
+
+#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
+#include <linux/miscdevice.h>
+
+static struct miscdevice psaux_mouse = {
+	.minor	= PSMOUSE_MINOR,
+	.name	= "psaux",
+	.fops	= &mousedev_fops,
+};
+
+static bool psaux_registered;
+
+static void __init mousedev_psaux_register(void)
+{
+	int error;
+
+	error = misc_register(&psaux_mouse);
+	if (error)
+		pr_warn("could not register psaux device, error: %d\n",
+			   error);
+	else
+		psaux_registered = true;
+}
+
+static void __exit mousedev_psaux_unregister(void)
+{
+	if (psaux_registered)
+		misc_deregister(&psaux_mouse);
+}
+#else
+static inline void mousedev_psaux_register(void) { }
+static inline void mousedev_psaux_unregister(void) { }
+#endif
+
+static int __init mousedev_init(void)
+{
+	int error;
+
+	mousedev_mix = mousedev_create(NULL, &mousedev_handler, true);
+	if (IS_ERR(mousedev_mix))
+		return PTR_ERR(mousedev_mix);
+
+	error = input_register_handler(&mousedev_handler);
+	if (error) {
+		mousedev_destroy(mousedev_mix);
+		return error;
+	}
+
+	mousedev_psaux_register();
+
+	pr_info("PS/2 mouse device common for all mice\n");
+
+	return 0;
+}
+
+static void __exit mousedev_exit(void)
+{
+	mousedev_psaux_unregister();
+	input_unregister_handler(&mousedev_handler);
+	mousedev_destroy(mousedev_mix);
+}
+
+module_init(mousedev_init);
+module_exit(mousedev_exit);
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
new file mode 100644
index 0000000..c3d05b4
--- /dev/null
+++ b/drivers/input/serio/Kconfig
@@ -0,0 +1,309 @@
+#
+# Input core configuration
+#
+config SERIO
+	tristate "Serial I/O support"
+	default y
+	help
+	  Say Yes here if you have any input device that uses serial I/O to
+	  communicate with the system. This includes the
+	  		* standard AT keyboard and PS/2 mouse *
+	  as well as serial mice, Sun keyboards, some joysticks and 6dof
+	  devices and more.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called serio.
+
+config ARCH_MIGHT_HAVE_PC_SERIO
+	bool
+	help
+	  Select this config option from the architecture Kconfig if
+	  the architecture might use a PC serio device (i8042) to
+	  communicate with keyboard, mouse, etc.
+
+if SERIO
+
+config SERIO_I8042
+	tristate "i8042 PC Keyboard controller"
+	default y
+	depends on ARCH_MIGHT_HAVE_PC_SERIO
+	help
+	  i8042 is the chip over which the standard AT keyboard and PS/2
+	  mouse are connected to the computer. If you use these devices,
+	  you'll need to say Y here.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called i8042.
+
+config SERIO_SERPORT
+	tristate "Serial port line discipline"
+	default y
+	depends on TTY
+	help
+	  Say Y here if you plan to use an input device (mouse, joystick,
+	  tablet, 6dof) that communicates over the RS232 serial (COM) port.
+
+	  More information is available: <file:Documentation/input/input.txt>
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called serport.
+
+config SERIO_CT82C710
+	tristate "ct82c710 Aux port controller"
+	depends on X86
+	help
+	  Say Y here if you have a Texas Instruments TravelMate notebook
+	  equipped with the ct82c710 chip and want to use a mouse connected
+	  to the "QuickPort".
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ct82c710.
+
+config SERIO_Q40KBD
+	tristate "Q40 keyboard controller"
+	depends on Q40
+
+config SERIO_PARKBD
+	tristate "Parallel port keyboard adapter"
+	depends on PARPORT
+	help
+	  Say Y here if you built a simple parallel port adapter to attach
+	  an additional AT keyboard, XT keyboard or PS/2 mouse.
+
+	  More information is available: <file:Documentation/input/input.txt>
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called parkbd.
+
+config SERIO_RPCKBD
+	tristate "Acorn RiscPC keyboard controller"
+	depends on ARCH_ACORN
+	default y
+	help
+	  Say Y here if you have the Acorn RiscPC and want to use an AT
+	  keyboard connected to its keyboard controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rpckbd.
+
+config SERIO_AT32PSIF
+	tristate "AVR32 PSIF PS/2 keyboard and mouse controller"
+	depends on AVR32
+	help
+	  Say Y here if you want to use the PSIF peripheral on AVR32 devices
+	  and connect a PS/2 keyboard and/or mouse to it.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called at32psif.
+
+config SERIO_AMBAKMI
+	tristate "AMBA KMI keyboard controller"
+	depends on ARM_AMBA
+
+config SERIO_SA1111
+	tristate "Intel SA1111 keyboard controller"
+	depends on SA1111
+
+config SERIO_GSCPS2
+	tristate "HP GSC PS/2 keyboard and PS/2 mouse controller"
+	depends on GSC
+	default y
+	help
+	  This driver provides support for the PS/2 ports on PA-RISC machines
+	  over which HP PS/2 keyboards and PS/2 mice may be connected.
+	  If you use these devices, you'll need to say Y here.
+
+	  It's safe to enable this driver, so if unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gscps2.
+
+config HP_SDC
+	tristate "HP System Device Controller i8042 Support"
+	depends on (GSC || HP300) && SERIO
+	default y
+	help
+	  This option enables support for the "System Device
+	  Controller", an i8042 carrying microcode to manage a
+	  few miscellaneous devices on some Hewlett Packard systems.
+	  The SDC itself contains a 10ms resolution timer/clock capable
+	  of delivering interrupts on a periodic and one-shot basis.
+	  The SDC may also be connected to a battery-backed real-time
+	  clock, a basic audio waveform generator, and an HP-HIL Master
+	  Link Controller serving up to seven input devices.
+
+	  By itself this option is rather useless, but enabling it will
+	  enable selection of drivers for the abovementioned devices.
+	  It is, however, incompatible with the old, reliable HIL keyboard
+	  driver, and the new HIL driver is experimental, so if you plan
+	  to use a HIL keyboard as your primary keyboard, you may wish
+	  to keep using that driver until the new HIL drivers have had
+	  more testing.
+
+config HIL_MLC
+	tristate "HIL MLC Support (needed for HIL input devices)"
+	depends on HP_SDC
+
+config SERIO_PCIPS2
+	tristate "PCI PS/2 keyboard and PS/2 mouse controller"
+	depends on PCI
+	help
+	  Say Y here if you have a Mobility Docking station with PS/2
+	  keyboard and mice ports.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pcips2.
+
+config SERIO_MACEPS2
+	tristate "SGI O2 MACE PS/2 controller"
+	depends on SGI_IP32
+	help
+	  Say Y here if you have SGI O2 workstation and want to use its
+	  PS/2 ports.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called maceps2.
+
+config SERIO_LIBPS2
+	tristate "PS/2 driver library"
+	depends on SERIO_I8042 || SERIO_I8042=n
+	help
+	  Say Y here if you are using a driver for device connected
+	  to a PS/2 port, such as PS/2 mouse or standard AT keyboard.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called libps2.
+
+config SERIO_RAW
+	tristate "Raw access to serio ports"
+	help
+	  Say Y here if you want to have raw access to serio ports, such as
+	  AUX ports on i8042 keyboard controller. Each serio port that is
+	  bound to this driver will be accessible via a char device with
+	  major 10 and dynamically allocated minor. The driver will try
+	  allocating minor 1 (that historically corresponds to /dev/psaux)
+	  first. To bind this driver to a serio port use sysfs interface:
+
+	      echo -n "serio_raw" > /sys/bus/serio/devices/serioX/drvctl
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called serio_raw.
+
+config SERIO_XILINX_XPS_PS2
+	tristate "Xilinx XPS PS/2 Controller Support"
+	depends on PPC || MICROBLAZE
+	help
+	  This driver supports XPS PS/2 IP from the Xilinx EDK on
+	  PowerPC platform.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called xilinx_ps2.
+
+config SERIO_ALTERA_PS2
+	tristate "Altera UP PS/2 controller"
+	depends on HAS_IOMEM
+	help
+	  Say Y here if you have Altera University Program PS/2 ports.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called altera_ps2.
+
+config SERIO_AMS_DELTA
+	tristate "Amstrad Delta (E3) mailboard support"
+	depends on MACH_AMS_DELTA
+	default y
+	---help---
+	  Say Y here if you have an E3 and want to use its mailboard,
+	  or any standard AT keyboard connected to the mailboard port.
+
+	  When used for the E3 mailboard, a non-standard key table
+	  must be loaded from userspace, possibly using udev extras
+	  provided keymap helper utility.
+
+	  To compile this driver as a module, choose M here;
+	  the module will be called ams_delta_serio.
+
+config SERIO_PS2MULT
+	tristate "TQC PS/2 multiplexer"
+	help
+	  Say Y here if you have the PS/2 line multiplexer like the one
+	  present on TQC boards.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ps2mult.
+
+config SERIO_ARC_PS2
+	tristate "ARC PS/2 support"
+	depends on HAS_IOMEM
+	help
+	  Say Y here if you have an ARC FPGA platform with a PS/2
+	  controller in it.
+
+	  To compile this driver as a module, choose M here; the module
+	  will be called arc_ps2.
+
+config SERIO_APBPS2
+	tristate "GRLIB APBPS2 PS/2 keyboard/mouse controller"
+	depends on OF
+	help
+	  Say Y here if you want support for GRLIB APBPS2 peripherals used
+	  to connect to PS/2 keyboard and/or mouse.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called apbps2.
+
+config SERIO_OLPC_APSP
+	tristate "OLPC AP-SP input support"
+	depends on OLPC || COMPILE_TEST
+	help
+	  Say Y here if you want support for the keyboard and touchpad included
+	  in the OLPC XO-1.75 and XO-4 laptops.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called olpc_apsp.
+
+config HYPERV_KEYBOARD
+	tristate "Microsoft Synthetic Keyboard driver"
+	depends on HYPERV
+	default HYPERV
+	help
+	  Select this option to enable the Hyper-V Keyboard driver.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called hyperv_keyboard.
+
+config SERIO_SUN4I_PS2
+	tristate "Allwinner A10 PS/2 controller support"
+	depends on ARCH_SUNXI || COMPILE_TEST
+	help
+	  This selects support for the PS/2 Host Controller on
+	  Allwinner A10.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sun4i-ps2.
+
+config USERIO
+	tristate "User space serio port driver support"
+	help
+	  Say Y here if you want to support user level drivers for serio
+	  subsystem accessible under char device 10:240 - /dev/userio. Using
+	  this facility userspace programs can implement serio ports that
+	  will be used by the standard in-kernel serio consumer drivers,
+	  such as psmouse and atkbd.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called userio.
+
+	  If you are unsure, say N.
+
+endif
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
new file mode 100644
index 0000000..2374ef9
--- /dev/null
+++ b/drivers/input/serio/Makefile
@@ -0,0 +1,33 @@
+#
+# Makefile for the input core drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_SERIO)		+= serio.o
+obj-$(CONFIG_SERIO_I8042)	+= i8042.o
+obj-$(CONFIG_SERIO_PARKBD)	+= parkbd.o
+obj-$(CONFIG_SERIO_SERPORT)	+= serport.o
+obj-$(CONFIG_SERIO_CT82C710)	+= ct82c710.o
+obj-$(CONFIG_SERIO_RPCKBD)	+= rpckbd.o
+obj-$(CONFIG_SERIO_SA1111)	+= sa1111ps2.o
+obj-$(CONFIG_SERIO_AMBAKMI)	+= ambakmi.o
+obj-$(CONFIG_SERIO_AT32PSIF)	+= at32psif.o
+obj-$(CONFIG_SERIO_Q40KBD)	+= q40kbd.o
+obj-$(CONFIG_SERIO_GSCPS2)	+= gscps2.o
+obj-$(CONFIG_HP_SDC)		+= hp_sdc.o
+obj-$(CONFIG_HIL_MLC)		+= hp_sdc_mlc.o hil_mlc.o
+obj-$(CONFIG_SERIO_PCIPS2)	+= pcips2.o
+obj-$(CONFIG_SERIO_PS2MULT)	+= ps2mult.o
+obj-$(CONFIG_SERIO_MACEPS2)	+= maceps2.o
+obj-$(CONFIG_SERIO_LIBPS2)	+= libps2.o
+obj-$(CONFIG_SERIO_RAW)		+= serio_raw.o
+obj-$(CONFIG_SERIO_AMS_DELTA)	+= ams_delta_serio.o
+obj-$(CONFIG_SERIO_XILINX_XPS_PS2)	+= xilinx_ps2.o
+obj-$(CONFIG_SERIO_ALTERA_PS2)	+= altera_ps2.o
+obj-$(CONFIG_SERIO_ARC_PS2)	+= arc_ps2.o
+obj-$(CONFIG_SERIO_APBPS2)	+= apbps2.o
+obj-$(CONFIG_SERIO_OLPC_APSP)	+= olpc_apsp.o
+obj-$(CONFIG_HYPERV_KEYBOARD)	+= hyperv-keyboard.o
+obj-$(CONFIG_SERIO_SUN4I_PS2)	+= sun4i-ps2.o
+obj-$(CONFIG_USERIO)		+= userio.o
diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c
new file mode 100644
index 0000000..131d782
--- /dev/null
+++ b/drivers/input/serio/altera_ps2.c
@@ -0,0 +1,167 @@
+/*
+ * Altera University Program PS2 controller driver
+ *
+ * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * Based on sa1111ps2.c, which is:
+ * Copyright (C) 2002 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+#define DRV_NAME "altera_ps2"
+
+struct ps2if {
+	struct serio *io;
+	void __iomem *base;
+};
+
+/*
+ * Read all bytes waiting in the PS2 port.  There should be
+ * at the most one, but we loop for safety.
+ */
+static irqreturn_t altera_ps2_rxint(int irq, void *dev_id)
+{
+	struct ps2if *ps2if = dev_id;
+	unsigned int status;
+	irqreturn_t handled = IRQ_NONE;
+
+	while ((status = readl(ps2if->base)) & 0xffff0000) {
+		serio_interrupt(ps2if->io, status & 0xff, 0);
+		handled = IRQ_HANDLED;
+	}
+
+	return handled;
+}
+
+/*
+ * Write a byte to the PS2 port.
+ */
+static int altera_ps2_write(struct serio *io, unsigned char val)
+{
+	struct ps2if *ps2if = io->port_data;
+
+	writel(val, ps2if->base);
+	return 0;
+}
+
+static int altera_ps2_open(struct serio *io)
+{
+	struct ps2if *ps2if = io->port_data;
+
+	/* clear fifo */
+	while (readl(ps2if->base) & 0xffff0000)
+		/* empty */;
+
+	writel(1, ps2if->base + 4); /* enable rx irq */
+	return 0;
+}
+
+static void altera_ps2_close(struct serio *io)
+{
+	struct ps2if *ps2if = io->port_data;
+
+	writel(0, ps2if->base + 4); /* disable rx irq */
+}
+
+/*
+ * Add one device to this driver.
+ */
+static int altera_ps2_probe(struct platform_device *pdev)
+{
+	struct ps2if *ps2if;
+	struct resource *res;
+	struct serio *serio;
+	int error, irq;
+
+	ps2if = devm_kzalloc(&pdev->dev, sizeof(struct ps2if), GFP_KERNEL);
+	if (!ps2if)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ps2if->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ps2if->base))
+		return PTR_ERR(ps2if->base);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return -ENXIO;
+
+	error = devm_request_irq(&pdev->dev, irq, altera_ps2_rxint, 0,
+				 pdev->name, ps2if);
+	if (error) {
+		dev_err(&pdev->dev, "could not request IRQ %d\n", irq);
+		return error;
+	}
+
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!serio)
+		return -ENOMEM;
+
+	serio->id.type		= SERIO_8042;
+	serio->write		= altera_ps2_write;
+	serio->open		= altera_ps2_open;
+	serio->close		= altera_ps2_close;
+	strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name));
+	strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys));
+	serio->port_data	= ps2if;
+	serio->dev.parent	= &pdev->dev;
+	ps2if->io		= serio;
+
+	dev_info(&pdev->dev, "base %p, irq %d\n", ps2if->base, irq);
+
+	serio_register_port(ps2if->io);
+	platform_set_drvdata(pdev, ps2if);
+
+	return 0;
+}
+
+/*
+ * Remove one device from this driver.
+ */
+static int altera_ps2_remove(struct platform_device *pdev)
+{
+	struct ps2if *ps2if = platform_get_drvdata(pdev);
+
+	serio_unregister_port(ps2if->io);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id altera_ps2_match[] = {
+	{ .compatible = "ALTR,ps2-1.0", },
+	{ .compatible = "altr,ps2-1.0", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, altera_ps2_match);
+#endif /* CONFIG_OF */
+
+/*
+ * Our device driver structure
+ */
+static struct platform_driver altera_ps2_driver = {
+	.probe		= altera_ps2_probe,
+	.remove		= altera_ps2_remove,
+	.driver	= {
+		.name	= DRV_NAME,
+		.of_match_table = of_match_ptr(altera_ps2_match),
+	},
+};
+module_platform_driver(altera_ps2_driver);
+
+MODULE_DESCRIPTION("Altera University Program PS2 controller driver");
+MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c
new file mode 100644
index 0000000..c6606ca
--- /dev/null
+++ b/drivers/input/serio/ambakmi.c
@@ -0,0 +1,215 @@
+/*
+ *  linux/drivers/input/serio/ambakmi.c
+ *
+ *  Copyright (C) 2000-2003 Deep Blue Solutions Ltd.
+ *  Copyright (C) 2002 Russell King.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/kmi.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#define KMI_BASE	(kmi->base)
+
+struct amba_kmi_port {
+	struct serio		*io;
+	struct clk		*clk;
+	void __iomem		*base;
+	unsigned int		irq;
+	unsigned int		divisor;
+	unsigned int		open;
+};
+
+static irqreturn_t amba_kmi_int(int irq, void *dev_id)
+{
+	struct amba_kmi_port *kmi = dev_id;
+	unsigned int status = readb(KMIIR);
+	int handled = IRQ_NONE;
+
+	while (status & KMIIR_RXINTR) {
+		serio_interrupt(kmi->io, readb(KMIDATA), 0);
+		status = readb(KMIIR);
+		handled = IRQ_HANDLED;
+	}
+
+	return handled;
+}
+
+static int amba_kmi_write(struct serio *io, unsigned char val)
+{
+	struct amba_kmi_port *kmi = io->port_data;
+	unsigned int timeleft = 10000; /* timeout in 100ms */
+
+	while ((readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && --timeleft)
+		udelay(10);
+
+	if (timeleft)
+		writeb(val, KMIDATA);
+
+	return timeleft ? 0 : SERIO_TIMEOUT;
+}
+
+static int amba_kmi_open(struct serio *io)
+{
+	struct amba_kmi_port *kmi = io->port_data;
+	unsigned int divisor;
+	int ret;
+
+	ret = clk_prepare_enable(kmi->clk);
+	if (ret)
+		goto out;
+
+	divisor = clk_get_rate(kmi->clk) / 8000000 - 1;
+	writeb(divisor, KMICLKDIV);
+	writeb(KMICR_EN, KMICR);
+
+	ret = request_irq(kmi->irq, amba_kmi_int, IRQF_SHARED, "kmi-pl050",
+			  kmi);
+	if (ret) {
+		printk(KERN_ERR "kmi: failed to claim IRQ%d\n", kmi->irq);
+		writeb(0, KMICR);
+		goto clk_disable;
+	}
+
+	writeb(KMICR_EN | KMICR_RXINTREN, KMICR);
+
+	return 0;
+
+ clk_disable:
+	clk_disable_unprepare(kmi->clk);
+ out:
+	return ret;
+}
+
+static void amba_kmi_close(struct serio *io)
+{
+	struct amba_kmi_port *kmi = io->port_data;
+
+	writeb(0, KMICR);
+
+	free_irq(kmi->irq, kmi);
+	clk_disable_unprepare(kmi->clk);
+}
+
+static int amba_kmi_probe(struct amba_device *dev,
+	const struct amba_id *id)
+{
+	struct amba_kmi_port *kmi;
+	struct serio *io;
+	int ret;
+
+	ret = amba_request_regions(dev, NULL);
+	if (ret)
+		return ret;
+
+	kmi = kzalloc(sizeof(struct amba_kmi_port), GFP_KERNEL);
+	io = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!kmi || !io) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+
+	io->id.type	= SERIO_8042;
+	io->write	= amba_kmi_write;
+	io->open	= amba_kmi_open;
+	io->close	= amba_kmi_close;
+	strlcpy(io->name, dev_name(&dev->dev), sizeof(io->name));
+	strlcpy(io->phys, dev_name(&dev->dev), sizeof(io->phys));
+	io->port_data	= kmi;
+	io->dev.parent	= &dev->dev;
+
+	kmi->io		= io;
+	kmi->base	= ioremap(dev->res.start, resource_size(&dev->res));
+	if (!kmi->base) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	kmi->clk = clk_get(&dev->dev, "KMIREFCLK");
+	if (IS_ERR(kmi->clk)) {
+		ret = PTR_ERR(kmi->clk);
+		goto unmap;
+	}
+
+	kmi->irq = dev->irq[0];
+	amba_set_drvdata(dev, kmi);
+
+	serio_register_port(kmi->io);
+	return 0;
+
+ unmap:
+	iounmap(kmi->base);
+ out:
+	kfree(kmi);
+	kfree(io);
+	amba_release_regions(dev);
+	return ret;
+}
+
+static int amba_kmi_remove(struct amba_device *dev)
+{
+	struct amba_kmi_port *kmi = amba_get_drvdata(dev);
+
+	serio_unregister_port(kmi->io);
+	clk_put(kmi->clk);
+	iounmap(kmi->base);
+	kfree(kmi);
+	amba_release_regions(dev);
+	return 0;
+}
+
+static int __maybe_unused amba_kmi_resume(struct device *dev)
+{
+	struct amba_kmi_port *kmi = dev_get_drvdata(dev);
+
+	/* kick the serio layer to rescan this port */
+	serio_reconnect(kmi->io);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(amba_kmi_dev_pm_ops, NULL, amba_kmi_resume);
+
+static struct amba_id amba_kmi_idtable[] = {
+	{
+		.id	= 0x00041050,
+		.mask	= 0x000fffff,
+	},
+	{ 0, 0 }
+};
+
+MODULE_DEVICE_TABLE(amba, amba_kmi_idtable);
+
+static struct amba_driver ambakmi_driver = {
+	.drv		= {
+		.name	= "kmi-pl050",
+		.owner	= THIS_MODULE,
+		.pm	= &amba_kmi_dev_pm_ops,
+	},
+	.id_table	= amba_kmi_idtable,
+	.probe		= amba_kmi_probe,
+	.remove		= amba_kmi_remove,
+};
+
+module_amba_driver(ambakmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("AMBA KMI controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/serio/ams_delta_serio.c b/drivers/input/serio/ams_delta_serio.c
new file mode 100644
index 0000000..45887e3
--- /dev/null
+++ b/drivers/input/serio/ams_delta_serio.c
@@ -0,0 +1,191 @@
+/*
+ *  Amstrad E3 (Delta) keyboard port driver
+ *
+ *  Copyright (c) 2006 Matt Callow
+ *  Copyright (c) 2010 Janusz Krzysztofik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Thanks to Cliff Lawson for his help
+ *
+ * The Amstrad Delta keyboard (aka mailboard) uses normal PC-AT style serial
+ * transmission.  The keyboard port is formed of two GPIO lines, for clock
+ * and data.  Due to strict timing requirements of the interface,
+ * the serial data stream is read and processed by a FIQ handler.
+ * The resulting words are fetched by this driver from a circular buffer.
+ *
+ * Standard AT keyboard driver (atkbd) is used for handling the keyboard data.
+ * However, when used with the E3 mailboard that producecs non-standard
+ * scancodes, a custom key table must be prepared and loaded from userspace.
+ */
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <asm/mach-types.h>
+#include <mach/board-ams-delta.h>
+
+#include <mach/ams-delta-fiq.h>
+
+MODULE_AUTHOR("Matt Callow");
+MODULE_DESCRIPTION("AMS Delta (E3) keyboard port driver");
+MODULE_LICENSE("GPL");
+
+static struct serio *ams_delta_serio;
+
+static int check_data(int data)
+{
+	int i, parity = 0;
+
+	/* check valid stop bit */
+	if (!(data & 0x400)) {
+		dev_warn(&ams_delta_serio->dev,
+				"invalid stop bit, data=0x%X\n",
+				data);
+		return SERIO_FRAME;
+	}
+	/* calculate the parity */
+	for (i = 1; i < 10; i++) {
+		if (data & (1 << i))
+			parity++;
+	}
+	/* it should be odd */
+	if (!(parity & 0x01)) {
+		dev_warn(&ams_delta_serio->dev,
+				"paritiy check failed, data=0x%X parity=0x%X\n",
+				data, parity);
+		return SERIO_PARITY;
+	}
+	return 0;
+}
+
+static irqreturn_t ams_delta_serio_interrupt(int irq, void *dev_id)
+{
+	int *circ_buff = &fiq_buffer[FIQ_CIRC_BUFF];
+	int data, dfl;
+	u8 scancode;
+
+	fiq_buffer[FIQ_IRQ_PEND] = 0;
+
+	/*
+	 * Read data from the circular buffer, check it
+	 * and then pass it on the serio
+	 */
+	while (fiq_buffer[FIQ_KEYS_CNT] > 0) {
+
+		data = circ_buff[fiq_buffer[FIQ_HEAD_OFFSET]++];
+		fiq_buffer[FIQ_KEYS_CNT]--;
+		if (fiq_buffer[FIQ_HEAD_OFFSET] == fiq_buffer[FIQ_BUF_LEN])
+			fiq_buffer[FIQ_HEAD_OFFSET] = 0;
+
+		dfl = check_data(data);
+		scancode = (u8) (data >> 1) & 0xFF;
+		serio_interrupt(ams_delta_serio, scancode, dfl);
+	}
+	return IRQ_HANDLED;
+}
+
+static int ams_delta_serio_open(struct serio *serio)
+{
+	/* enable keyboard */
+	gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 1);
+
+	return 0;
+}
+
+static void ams_delta_serio_close(struct serio *serio)
+{
+	/* disable keyboard */
+	gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 0);
+}
+
+static const struct gpio ams_delta_gpios[] __initconst_or_module = {
+	{
+		.gpio	= AMS_DELTA_GPIO_PIN_KEYBRD_DATA,
+		.flags	= GPIOF_DIR_IN,
+		.label	= "serio-data",
+	},
+	{
+		.gpio	= AMS_DELTA_GPIO_PIN_KEYBRD_CLK,
+		.flags	= GPIOF_DIR_IN,
+		.label	= "serio-clock",
+	},
+	{
+		.gpio	= AMS_DELTA_GPIO_PIN_KEYBRD_PWR,
+		.flags	= GPIOF_OUT_INIT_LOW,
+		.label	= "serio-power",
+	},
+	{
+		.gpio	= AMS_DELTA_GPIO_PIN_KEYBRD_DATAOUT,
+		.flags	= GPIOF_OUT_INIT_LOW,
+		.label	= "serio-dataout",
+	},
+};
+
+static int __init ams_delta_serio_init(void)
+{
+	int err;
+
+	if (!machine_is_ams_delta())
+		return -ENODEV;
+
+	ams_delta_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!ams_delta_serio)
+		return -ENOMEM;
+
+	ams_delta_serio->id.type = SERIO_8042;
+	ams_delta_serio->open = ams_delta_serio_open;
+	ams_delta_serio->close = ams_delta_serio_close;
+	strlcpy(ams_delta_serio->name, "AMS DELTA keyboard adapter",
+			sizeof(ams_delta_serio->name));
+	strlcpy(ams_delta_serio->phys, "GPIO/serio0",
+			sizeof(ams_delta_serio->phys));
+
+	err = gpio_request_array(ams_delta_gpios,
+				ARRAY_SIZE(ams_delta_gpios));
+	if (err) {
+		pr_err("ams_delta_serio: Couldn't request gpio pins\n");
+		goto serio;
+	}
+
+	err = request_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK),
+			ams_delta_serio_interrupt, IRQ_TYPE_EDGE_RISING,
+			"ams-delta-serio", 0);
+	if (err < 0) {
+		pr_err("ams_delta_serio: couldn't request gpio interrupt %d\n",
+				gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK));
+		goto gpio;
+	}
+	/*
+	 * Since GPIO register handling for keyboard clock pin is performed
+	 * at FIQ level, switch back from edge to simple interrupt handler
+	 * to avoid bad interaction.
+	 */
+	irq_set_handler(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK),
+			handle_simple_irq);
+
+	serio_register_port(ams_delta_serio);
+	dev_info(&ams_delta_serio->dev, "%s\n", ams_delta_serio->name);
+
+	return 0;
+gpio:
+	gpio_free_array(ams_delta_gpios,
+			ARRAY_SIZE(ams_delta_gpios));
+serio:
+	kfree(ams_delta_serio);
+	return err;
+}
+module_init(ams_delta_serio_init);
+
+static void __exit ams_delta_serio_exit(void)
+{
+	serio_unregister_port(ams_delta_serio);
+	free_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0);
+	gpio_free_array(ams_delta_gpios,
+			ARRAY_SIZE(ams_delta_gpios));
+}
+module_exit(ams_delta_serio_exit);
diff --git a/drivers/input/serio/apbps2.c b/drivers/input/serio/apbps2.c
new file mode 100644
index 0000000..45d4e08
--- /dev/null
+++ b/drivers/input/serio/apbps2.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2013 Aeroflex Gaisler
+ *
+ * This driver supports the APBPS2 PS/2 core available in the GRLIB
+ * VHDL IP core library.
+ *
+ * Full documentation of the APBPS2 core can be found here:
+ * http://www.gaisler.com/products/grlib/grip.pdf
+ *
+ * See "Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt" for
+ * information on open firmware properties.
+ *
+ * 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.
+ *
+ * Contributors: Daniel Hellstrom <daniel@gaisler.com>
+ */
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/of_irq.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+
+struct apbps2_regs {
+	u32 __iomem data;	/* 0x00 */
+	u32 __iomem status;	/* 0x04 */
+	u32 __iomem ctrl;	/* 0x08 */
+	u32 __iomem reload;	/* 0x0c */
+};
+
+#define APBPS2_STATUS_DR	(1<<0)
+#define APBPS2_STATUS_PE	(1<<1)
+#define APBPS2_STATUS_FE	(1<<2)
+#define APBPS2_STATUS_KI	(1<<3)
+#define APBPS2_STATUS_RF	(1<<4)
+#define APBPS2_STATUS_TF	(1<<5)
+#define APBPS2_STATUS_TCNT	(0x1f<<22)
+#define APBPS2_STATUS_RCNT	(0x1f<<27)
+
+#define APBPS2_CTRL_RE		(1<<0)
+#define APBPS2_CTRL_TE		(1<<1)
+#define APBPS2_CTRL_RI		(1<<2)
+#define APBPS2_CTRL_TI		(1<<3)
+
+struct apbps2_priv {
+	struct serio		*io;
+	struct apbps2_regs	*regs;
+};
+
+static int apbps2_idx;
+
+static irqreturn_t apbps2_isr(int irq, void *dev_id)
+{
+	struct apbps2_priv *priv = dev_id;
+	unsigned long status, data, rxflags;
+	irqreturn_t ret = IRQ_NONE;
+
+	while ((status = ioread32be(&priv->regs->status)) & APBPS2_STATUS_DR) {
+		data = ioread32be(&priv->regs->data);
+		rxflags = (status & APBPS2_STATUS_PE) ? SERIO_PARITY : 0;
+		rxflags |= (status & APBPS2_STATUS_FE) ? SERIO_FRAME : 0;
+
+		/* clear error bits? */
+		if (rxflags)
+			iowrite32be(0, &priv->regs->status);
+
+		serio_interrupt(priv->io, data, rxflags);
+
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+static int apbps2_write(struct serio *io, unsigned char val)
+{
+	struct apbps2_priv *priv = io->port_data;
+	unsigned int tleft = 10000; /* timeout in 100ms */
+
+	/* delay until PS/2 controller has room for more chars */
+	while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_TF) && tleft--)
+		udelay(10);
+
+	if ((ioread32be(&priv->regs->status) & APBPS2_STATUS_TF) == 0) {
+		iowrite32be(val, &priv->regs->data);
+
+		iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI | APBPS2_CTRL_TE,
+				&priv->regs->ctrl);
+		return 0;
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int apbps2_open(struct serio *io)
+{
+	struct apbps2_priv *priv = io->port_data;
+	int limit;
+	unsigned long tmp;
+
+	/* clear error flags */
+	iowrite32be(0, &priv->regs->status);
+
+	/* Clear old data if available (unlikely) */
+	limit = 1024;
+	while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_DR) && --limit)
+		tmp = ioread32be(&priv->regs->data);
+
+	/* Enable reciever and it's interrupt */
+	iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI, &priv->regs->ctrl);
+
+	return 0;
+}
+
+static void apbps2_close(struct serio *io)
+{
+	struct apbps2_priv *priv = io->port_data;
+
+	/* stop interrupts at PS/2 HW level */
+	iowrite32be(0, &priv->regs->ctrl);
+}
+
+/* Initialize one APBPS2 PS/2 core */
+static int apbps2_of_probe(struct platform_device *ofdev)
+{
+	struct apbps2_priv *priv;
+	int irq, err;
+	u32 freq_hz;
+	struct resource *res;
+
+	priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&ofdev->dev, "memory allocation failed\n");
+		return -ENOMEM;
+	}
+
+	/* Find Device Address */
+	res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
+	priv->regs = devm_ioremap_resource(&ofdev->dev, res);
+	if (IS_ERR(priv->regs))
+		return PTR_ERR(priv->regs);
+
+	/* Reset hardware, disable interrupt */
+	iowrite32be(0, &priv->regs->ctrl);
+
+	/* IRQ */
+	irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
+	err = devm_request_irq(&ofdev->dev, irq, apbps2_isr,
+				IRQF_SHARED, "apbps2", priv);
+	if (err) {
+		dev_err(&ofdev->dev, "request IRQ%d failed\n", irq);
+		return err;
+	}
+
+	/* Get core frequency */
+	if (of_property_read_u32(ofdev->dev.of_node, "freq", &freq_hz)) {
+		dev_err(&ofdev->dev, "unable to get core frequency\n");
+		return -EINVAL;
+	}
+
+	/* Set reload register to core freq in kHz/10 */
+	iowrite32be(freq_hz / 10000, &priv->regs->reload);
+
+	priv->io = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!priv->io)
+		return -ENOMEM;
+
+	priv->io->id.type = SERIO_8042;
+	priv->io->open = apbps2_open;
+	priv->io->close = apbps2_close;
+	priv->io->write = apbps2_write;
+	priv->io->port_data = priv;
+	strlcpy(priv->io->name, "APBPS2 PS/2", sizeof(priv->io->name));
+	snprintf(priv->io->phys, sizeof(priv->io->phys),
+		 "apbps2_%d", apbps2_idx++);
+
+	dev_info(&ofdev->dev, "irq = %d, base = 0x%p\n", irq, priv->regs);
+
+	serio_register_port(priv->io);
+
+	platform_set_drvdata(ofdev, priv);
+
+	return 0;
+}
+
+static int apbps2_of_remove(struct platform_device *of_dev)
+{
+	struct apbps2_priv *priv = platform_get_drvdata(of_dev);
+
+	serio_unregister_port(priv->io);
+
+	return 0;
+}
+
+static const struct of_device_id apbps2_of_match[] = {
+	{ .name = "GAISLER_APBPS2", },
+	{ .name = "01_060", },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, apbps2_of_match);
+
+static struct platform_driver apbps2_of_driver = {
+	.driver = {
+		.name = "grlib-apbps2",
+		.of_match_table = apbps2_of_match,
+	},
+	.probe = apbps2_of_probe,
+	.remove = apbps2_of_remove,
+};
+
+module_platform_driver(apbps2_of_driver);
+
+MODULE_AUTHOR("Aeroflex Gaisler AB.");
+MODULE_DESCRIPTION("GRLIB APBPS2 PS/2 serial I/O");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/serio/arc_ps2.c b/drivers/input/serio/arc_ps2.c
new file mode 100644
index 0000000..99e57a4
--- /dev/null
+++ b/drivers/input/serio/arc_ps2.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Driver is originally developed by Pavel Sokolov <psokolov@synopsys.com>
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#define ARC_PS2_PORTS                   2
+
+#define ARC_ARC_PS2_ID                  0x0001f609
+
+#define STAT_TIMEOUT                    128
+
+#define PS2_STAT_RX_FRM_ERR             (1)
+#define PS2_STAT_RX_BUF_OVER            (1 << 1)
+#define PS2_STAT_RX_INT_EN              (1 << 2)
+#define PS2_STAT_RX_VAL                 (1 << 3)
+#define PS2_STAT_TX_ISNOT_FUL           (1 << 4)
+#define PS2_STAT_TX_INT_EN              (1 << 5)
+
+struct arc_ps2_port {
+	void __iomem *data_addr;
+	void __iomem *status_addr;
+	struct serio *io;
+};
+
+struct arc_ps2_data {
+	struct arc_ps2_port port[ARC_PS2_PORTS];
+	void __iomem *addr;
+	unsigned int frame_error;
+	unsigned int buf_overflow;
+	unsigned int total_int;
+};
+
+static void arc_ps2_check_rx(struct arc_ps2_data *arc_ps2,
+			     struct arc_ps2_port *port)
+{
+	unsigned int timeout = 1000;
+	unsigned int flag, status;
+	unsigned char data;
+
+	do {
+		status = ioread32(port->status_addr);
+		if (!(status & PS2_STAT_RX_VAL))
+			return;
+
+		data = ioread32(port->data_addr) & 0xff;
+
+		flag = 0;
+		arc_ps2->total_int++;
+		if (status & PS2_STAT_RX_FRM_ERR) {
+			arc_ps2->frame_error++;
+			flag |= SERIO_PARITY;
+		} else if (status & PS2_STAT_RX_BUF_OVER) {
+			arc_ps2->buf_overflow++;
+			flag |= SERIO_FRAME;
+		}
+
+		serio_interrupt(port->io, data, flag);
+	} while (--timeout);
+
+	dev_err(&port->io->dev, "PS/2 hardware stuck\n");
+}
+
+static irqreturn_t arc_ps2_interrupt(int irq, void *dev)
+{
+	struct arc_ps2_data *arc_ps2 = dev;
+	int i;
+
+	for (i = 0; i < ARC_PS2_PORTS; i++)
+		arc_ps2_check_rx(arc_ps2, &arc_ps2->port[i]);
+
+	return IRQ_HANDLED;
+}
+
+static int arc_ps2_write(struct serio *io, unsigned char val)
+{
+	unsigned status;
+	struct arc_ps2_port *port = io->port_data;
+	int timeout = STAT_TIMEOUT;
+
+	do {
+		status = ioread32(port->status_addr);
+		cpu_relax();
+
+		if (status & PS2_STAT_TX_ISNOT_FUL) {
+			iowrite32(val & 0xff, port->data_addr);
+			return 0;
+		}
+
+	} while (--timeout);
+
+	dev_err(&io->dev, "write timeout\n");
+	return -ETIMEDOUT;
+}
+
+static int arc_ps2_open(struct serio *io)
+{
+	struct arc_ps2_port *port = io->port_data;
+
+	iowrite32(PS2_STAT_RX_INT_EN, port->status_addr);
+
+	return 0;
+}
+
+static void arc_ps2_close(struct serio *io)
+{
+	struct arc_ps2_port *port = io->port_data;
+
+	iowrite32(ioread32(port->status_addr) & ~PS2_STAT_RX_INT_EN,
+		  port->status_addr);
+}
+
+static void __iomem *arc_ps2_calc_addr(struct arc_ps2_data *arc_ps2,
+						  int index, bool status)
+{
+	void __iomem *addr;
+
+	addr = arc_ps2->addr + 4 + 4 * index;
+	if (status)
+		addr += ARC_PS2_PORTS * 4;
+
+	return addr;
+}
+
+static void arc_ps2_inhibit_ports(struct arc_ps2_data *arc_ps2)
+{
+	void __iomem *addr;
+	u32 val;
+	int i;
+
+	for (i = 0; i < ARC_PS2_PORTS; i++) {
+		addr = arc_ps2_calc_addr(arc_ps2, i, true);
+		val = ioread32(addr);
+		val &= ~(PS2_STAT_RX_INT_EN | PS2_STAT_TX_INT_EN);
+		iowrite32(val, addr);
+	}
+}
+
+static int arc_ps2_create_port(struct platform_device *pdev,
+					 struct arc_ps2_data *arc_ps2,
+					 int index)
+{
+	struct arc_ps2_port *port = &arc_ps2->port[index];
+	struct serio *io;
+
+	io = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!io)
+		return -ENOMEM;
+
+	io->id.type = SERIO_8042;
+	io->write = arc_ps2_write;
+	io->open = arc_ps2_open;
+	io->close = arc_ps2_close;
+	snprintf(io->name, sizeof(io->name), "ARC PS/2 port%d", index);
+	snprintf(io->phys, sizeof(io->phys), "arc/serio%d", index);
+	io->port_data = port;
+
+	port->io = io;
+
+	port->data_addr = arc_ps2_calc_addr(arc_ps2, index, false);
+	port->status_addr = arc_ps2_calc_addr(arc_ps2, index, true);
+
+	dev_dbg(&pdev->dev, "port%d is allocated (data = 0x%p, status = 0x%p)\n",
+		index, port->data_addr, port->status_addr);
+
+	serio_register_port(port->io);
+	return 0;
+}
+
+static int arc_ps2_probe(struct platform_device *pdev)
+{
+	struct arc_ps2_data *arc_ps2;
+	struct resource *res;
+	int irq;
+	int error, id, i;
+
+	irq = platform_get_irq_byname(pdev, "arc_ps2_irq");
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no IRQ defined\n");
+		return -EINVAL;
+	}
+
+	arc_ps2 = devm_kzalloc(&pdev->dev, sizeof(struct arc_ps2_data),
+				GFP_KERNEL);
+	if (!arc_ps2) {
+		dev_err(&pdev->dev, "out of memory\n");
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	arc_ps2->addr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(arc_ps2->addr))
+		return PTR_ERR(arc_ps2->addr);
+
+	dev_info(&pdev->dev, "irq = %d, address = 0x%p, ports = %i\n",
+		 irq, arc_ps2->addr, ARC_PS2_PORTS);
+
+	id = ioread32(arc_ps2->addr);
+	if (id != ARC_ARC_PS2_ID) {
+		dev_err(&pdev->dev, "device id does not match\n");
+		return -ENXIO;
+	}
+
+	arc_ps2_inhibit_ports(arc_ps2);
+
+	error = devm_request_irq(&pdev->dev, irq, arc_ps2_interrupt,
+				 0, "arc_ps2", arc_ps2);
+	if (error) {
+		dev_err(&pdev->dev, "Could not allocate IRQ\n");
+		return error;
+	}
+
+	for (i = 0; i < ARC_PS2_PORTS; i++) {
+		error = arc_ps2_create_port(pdev, arc_ps2, i);
+		if (error) {
+			while (--i >= 0)
+				serio_unregister_port(arc_ps2->port[i].io);
+			return error;
+		}
+	}
+
+	platform_set_drvdata(pdev, arc_ps2);
+
+	return 0;
+}
+
+static int arc_ps2_remove(struct platform_device *pdev)
+{
+	struct arc_ps2_data *arc_ps2 = platform_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < ARC_PS2_PORTS; i++)
+		serio_unregister_port(arc_ps2->port[i].io);
+
+	dev_dbg(&pdev->dev, "interrupt count = %i\n", arc_ps2->total_int);
+	dev_dbg(&pdev->dev, "frame error count = %i\n", arc_ps2->frame_error);
+	dev_dbg(&pdev->dev, "buffer overflow count = %i\n",
+		arc_ps2->buf_overflow);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id arc_ps2_match[] = {
+	{ .compatible = "snps,arc_ps2" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, arc_ps2_match);
+#endif
+
+static struct platform_driver arc_ps2_driver = {
+	.driver	= {
+		.name		= "arc_ps2",
+		.of_match_table	= of_match_ptr(arc_ps2_match),
+	},
+	.probe	= arc_ps2_probe,
+	.remove	= arc_ps2_remove,
+};
+
+module_platform_driver(arc_ps2_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pavel Sokolov <psokolov@synopsys.com>");
+MODULE_DESCRIPTION("ARC PS/2 Driver");
diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c
new file mode 100644
index 0000000..2e4ff5b
--- /dev/null
+++ b/drivers/input/serio/at32psif.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2007 Atmel Corporation
+ *
+ * Driver for the AT32AP700X PS/2 controller (PSIF).
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/* PSIF register offsets */
+#define PSIF_CR				0x00
+#define PSIF_RHR			0x04
+#define PSIF_THR			0x08
+#define PSIF_SR				0x10
+#define PSIF_IER			0x14
+#define PSIF_IDR			0x18
+#define PSIF_IMR			0x1c
+#define PSIF_PSR			0x24
+
+/* Bitfields in control register. */
+#define PSIF_CR_RXDIS_OFFSET		1
+#define PSIF_CR_RXDIS_SIZE		1
+#define PSIF_CR_RXEN_OFFSET		0
+#define PSIF_CR_RXEN_SIZE		1
+#define PSIF_CR_SWRST_OFFSET		15
+#define PSIF_CR_SWRST_SIZE		1
+#define PSIF_CR_TXDIS_OFFSET		9
+#define PSIF_CR_TXDIS_SIZE		1
+#define PSIF_CR_TXEN_OFFSET		8
+#define PSIF_CR_TXEN_SIZE		1
+
+/* Bitfields in interrupt disable, enable, mask and status register. */
+#define PSIF_NACK_OFFSET		8
+#define PSIF_NACK_SIZE			1
+#define PSIF_OVRUN_OFFSET		5
+#define PSIF_OVRUN_SIZE			1
+#define PSIF_PARITY_OFFSET		9
+#define PSIF_PARITY_SIZE		1
+#define PSIF_RXRDY_OFFSET		4
+#define PSIF_RXRDY_SIZE			1
+#define PSIF_TXEMPTY_OFFSET		1
+#define PSIF_TXEMPTY_SIZE		1
+#define PSIF_TXRDY_OFFSET		0
+#define PSIF_TXRDY_SIZE			1
+
+/* Bitfields in prescale register. */
+#define PSIF_PSR_PRSCV_OFFSET		0
+#define PSIF_PSR_PRSCV_SIZE		12
+
+/* Bitfields in receive hold register. */
+#define PSIF_RHR_RXDATA_OFFSET		0
+#define PSIF_RHR_RXDATA_SIZE		8
+
+/* Bitfields in transmit hold register. */
+#define PSIF_THR_TXDATA_OFFSET		0
+#define PSIF_THR_TXDATA_SIZE		8
+
+/* Bit manipulation macros */
+#define PSIF_BIT(name)					\
+	(1 << PSIF_##name##_OFFSET)
+
+#define PSIF_BF(name, value)				\
+	(((value) & ((1 << PSIF_##name##_SIZE) - 1))	\
+	 << PSIF_##name##_OFFSET)
+
+#define PSIF_BFEXT(name, value)				\
+	(((value) >> PSIF_##name##_OFFSET)		\
+	 & ((1 << PSIF_##name##_SIZE) - 1))
+
+#define PSIF_BFINS(name, value, old)			\
+	(((old) & ~(((1 << PSIF_##name##_SIZE) - 1)	\
+		    << PSIF_##name##_OFFSET))		\
+	 | PSIF_BF(name, value))
+
+/* Register access macros */
+#define psif_readl(port, reg)				\
+	__raw_readl((port)->regs + PSIF_##reg)
+
+#define psif_writel(port, reg, value)			\
+	__raw_writel((value), (port)->regs + PSIF_##reg)
+
+struct psif {
+	struct platform_device	*pdev;
+	struct clk		*pclk;
+	struct serio		*io;
+	void __iomem		*regs;
+	unsigned int		irq;
+	/* Prevent concurrent writes to PSIF THR. */
+	spinlock_t		lock;
+	bool			open;
+};
+
+static irqreturn_t psif_interrupt(int irq, void *_ptr)
+{
+	struct psif *psif = _ptr;
+	int retval = IRQ_NONE;
+	unsigned int io_flags = 0;
+	unsigned long status;
+
+	status = psif_readl(psif, SR);
+
+	if (status & PSIF_BIT(RXRDY)) {
+		unsigned char val = (unsigned char) psif_readl(psif, RHR);
+
+		if (status & PSIF_BIT(PARITY))
+			io_flags |= SERIO_PARITY;
+		if (status & PSIF_BIT(OVRUN))
+			dev_err(&psif->pdev->dev, "overrun read error\n");
+
+		serio_interrupt(psif->io, val, io_flags);
+
+		retval = IRQ_HANDLED;
+	}
+
+	return retval;
+}
+
+static int psif_write(struct serio *io, unsigned char val)
+{
+	struct psif *psif = io->port_data;
+	unsigned long flags;
+	int timeout = 10;
+	int retval = 0;
+
+	spin_lock_irqsave(&psif->lock, flags);
+
+	while (!(psif_readl(psif, SR) & PSIF_BIT(TXEMPTY)) && timeout--)
+		udelay(50);
+
+	if (timeout >= 0) {
+		psif_writel(psif, THR, val);
+	} else {
+		dev_dbg(&psif->pdev->dev, "timeout writing to THR\n");
+		retval = -EBUSY;
+	}
+
+	spin_unlock_irqrestore(&psif->lock, flags);
+
+	return retval;
+}
+
+static int psif_open(struct serio *io)
+{
+	struct psif *psif = io->port_data;
+	int retval;
+
+	retval = clk_enable(psif->pclk);
+	if (retval)
+		goto out;
+
+	psif_writel(psif, CR, PSIF_BIT(CR_TXEN) | PSIF_BIT(CR_RXEN));
+	psif_writel(psif, IER, PSIF_BIT(RXRDY));
+
+	psif->open = true;
+out:
+	return retval;
+}
+
+static void psif_close(struct serio *io)
+{
+	struct psif *psif = io->port_data;
+
+	psif->open = false;
+
+	psif_writel(psif, IDR, ~0UL);
+	psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS));
+
+	clk_disable(psif->pclk);
+}
+
+static void psif_set_prescaler(struct psif *psif)
+{
+	unsigned long prscv;
+	unsigned long rate = clk_get_rate(psif->pclk);
+
+	/* PRSCV = Pulse length (100 us) * PSIF module frequency. */
+	prscv = 100 * (rate / 1000000UL);
+
+	if (prscv > ((1<<PSIF_PSR_PRSCV_SIZE) - 1)) {
+		prscv = (1<<PSIF_PSR_PRSCV_SIZE) - 1;
+		dev_dbg(&psif->pdev->dev, "pclk too fast, "
+				"prescaler set to max\n");
+	}
+
+	clk_enable(psif->pclk);
+	psif_writel(psif, PSR, prscv);
+	clk_disable(psif->pclk);
+}
+
+static int __init psif_probe(struct platform_device *pdev)
+{
+	struct resource *regs;
+	struct psif *psif;
+	struct serio *io;
+	struct clk *pclk;
+	int irq;
+	int ret;
+
+	psif = kzalloc(sizeof(struct psif), GFP_KERNEL);
+	if (!psif) {
+		dev_dbg(&pdev->dev, "out of memory\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+	psif->pdev = pdev;
+
+	io = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!io) {
+		dev_dbg(&pdev->dev, "out of memory\n");
+		ret = -ENOMEM;
+		goto out_free_psif;
+	}
+	psif->io = io;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs) {
+		dev_dbg(&pdev->dev, "no mmio resources defined\n");
+		ret = -ENOMEM;
+		goto out_free_io;
+	}
+
+	psif->regs = ioremap(regs->start, resource_size(regs));
+	if (!psif->regs) {
+		ret = -ENOMEM;
+		dev_dbg(&pdev->dev, "could not map I/O memory\n");
+		goto out_free_io;
+	}
+
+	pclk = clk_get(&pdev->dev, "pclk");
+	if (IS_ERR(pclk)) {
+		dev_dbg(&pdev->dev, "could not get peripheral clock\n");
+		ret = PTR_ERR(pclk);
+		goto out_iounmap;
+	}
+	psif->pclk = pclk;
+
+	/* Reset the PSIF to enter at a known state. */
+	ret = clk_enable(pclk);
+	if (ret) {
+		dev_dbg(&pdev->dev, "could not enable pclk\n");
+		goto out_put_clk;
+	}
+	psif_writel(psif, CR, PSIF_BIT(CR_SWRST));
+	clk_disable(pclk);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_dbg(&pdev->dev, "could not get irq\n");
+		ret = -ENXIO;
+		goto out_put_clk;
+	}
+	ret = request_irq(irq, psif_interrupt, IRQF_SHARED, "at32psif", psif);
+	if (ret) {
+		dev_dbg(&pdev->dev, "could not request irq %d\n", irq);
+		goto out_put_clk;
+	}
+	psif->irq = irq;
+
+	io->id.type	= SERIO_8042;
+	io->write	= psif_write;
+	io->open	= psif_open;
+	io->close	= psif_close;
+	snprintf(io->name, sizeof(io->name), "AVR32 PS/2 port%d", pdev->id);
+	snprintf(io->phys, sizeof(io->phys), "at32psif/serio%d", pdev->id);
+	io->port_data	= psif;
+	io->dev.parent	= &pdev->dev;
+
+	psif_set_prescaler(psif);
+
+	spin_lock_init(&psif->lock);
+	serio_register_port(psif->io);
+	platform_set_drvdata(pdev, psif);
+
+	dev_info(&pdev->dev, "Atmel AVR32 PSIF PS/2 driver on 0x%08x irq %d\n",
+			(int)psif->regs, psif->irq);
+
+	return 0;
+
+out_put_clk:
+	clk_put(psif->pclk);
+out_iounmap:
+	iounmap(psif->regs);
+out_free_io:
+	kfree(io);
+out_free_psif:
+	kfree(psif);
+out:
+	return ret;
+}
+
+static int __exit psif_remove(struct platform_device *pdev)
+{
+	struct psif *psif = platform_get_drvdata(pdev);
+
+	psif_writel(psif, IDR, ~0UL);
+	psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS));
+
+	serio_unregister_port(psif->io);
+	iounmap(psif->regs);
+	free_irq(psif->irq, psif);
+	clk_put(psif->pclk);
+	kfree(psif);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int psif_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct psif *psif = platform_get_drvdata(pdev);
+
+	if (psif->open) {
+		psif_writel(psif, CR, PSIF_BIT(CR_RXDIS) | PSIF_BIT(CR_TXDIS));
+		clk_disable(psif->pclk);
+	}
+
+	return 0;
+}
+
+static int psif_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct psif *psif = platform_get_drvdata(pdev);
+
+	if (psif->open) {
+		clk_enable(psif->pclk);
+		psif_set_prescaler(psif);
+		psif_writel(psif, CR, PSIF_BIT(CR_RXEN) | PSIF_BIT(CR_TXEN));
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(psif_pm_ops, psif_suspend, psif_resume);
+
+static struct platform_driver psif_driver = {
+	.remove		= __exit_p(psif_remove),
+	.driver		= {
+		.name	= "atmel_psif",
+		.pm	= &psif_pm_ops,
+	},
+};
+
+module_platform_driver_probe(psif_driver, psif_probe);
+
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
+MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c
new file mode 100644
index 0000000..9c54c43
--- /dev/null
+++ b/drivers/input/serio/ct82c710.c
@@ -0,0 +1,260 @@
+/*
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ *  82C710 C&T mouse port chip driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("82C710 C&T mouse port chip driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * ct82c710 interface
+ */
+
+#define CT82C710_DEV_IDLE     0x01		/* Device Idle */
+#define CT82C710_RX_FULL      0x02		/* Device Char received */
+#define CT82C710_TX_IDLE      0x04		/* Device XMIT Idle */
+#define CT82C710_RESET        0x08		/* Device Reset */
+#define CT82C710_INTS_ON      0x10		/* Device Interrupt On */
+#define CT82C710_ERROR_FLAG   0x20		/* Device Error */
+#define CT82C710_CLEAR        0x40		/* Device Clear */
+#define CT82C710_ENABLE       0x80		/* Device Enable */
+
+#define CT82C710_IRQ          12
+
+#define CT82C710_DATA         ct82c710_iores.start
+#define CT82C710_STATUS       (ct82c710_iores.start + 1)
+
+static struct serio *ct82c710_port;
+static struct platform_device *ct82c710_device;
+static struct resource ct82c710_iores;
+
+/*
+ * Interrupt handler for the 82C710 mouse port. A character
+ * is waiting in the 82C710.
+ */
+
+static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id)
+{
+	return serio_interrupt(ct82c710_port, inb(CT82C710_DATA), 0);
+}
+
+/*
+ * Wait for device to send output char and flush any input char.
+ */
+
+static int ct82c170_wait(void)
+{
+	int timeout = 60000;
+
+	while ((inb(CT82C710_STATUS) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE))
+		       != (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) {
+
+		if (inb_p(CT82C710_STATUS) & CT82C710_RX_FULL) inb_p(CT82C710_DATA);
+
+		udelay(1);
+		timeout--;
+	}
+
+	return !timeout;
+}
+
+static void ct82c710_close(struct serio *serio)
+{
+	if (ct82c170_wait())
+		printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
+
+	outb_p(inb_p(CT82C710_STATUS) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), CT82C710_STATUS);
+
+	if (ct82c170_wait())
+		printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
+
+	free_irq(CT82C710_IRQ, NULL);
+}
+
+static int ct82c710_open(struct serio *serio)
+{
+	unsigned char status;
+	int err;
+
+	err = request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL);
+	if (err)
+		return err;
+
+	status = inb_p(CT82C710_STATUS);
+
+	status |= (CT82C710_ENABLE | CT82C710_RESET);
+	outb_p(status, CT82C710_STATUS);
+
+	status &= ~(CT82C710_RESET);
+	outb_p(status, CT82C710_STATUS);
+
+	status |= CT82C710_INTS_ON;
+	outb_p(status, CT82C710_STATUS);	/* Enable interrupts */
+
+	while (ct82c170_wait()) {
+		printk(KERN_ERR "ct82c710: Device busy in open()\n");
+		status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON);
+		outb_p(status, CT82C710_STATUS);
+		free_irq(CT82C710_IRQ, NULL);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+/*
+ * Write to the 82C710 mouse device.
+ */
+
+static int ct82c710_write(struct serio *port, unsigned char c)
+{
+	if (ct82c170_wait()) return -1;
+	outb_p(c, CT82C710_DATA);
+	return 0;
+}
+
+/*
+ * See if we can find a 82C710 device. Read mouse address.
+ */
+
+static int __init ct82c710_detect(void)
+{
+	outb_p(0x55, 0x2fa);				/* Any value except 9, ff or 36 */
+	outb_p(0xaa, 0x3fa);				/* Inverse of 55 */
+	outb_p(0x36, 0x3fa);				/* Address the chip */
+	outb_p(0xe4, 0x3fa);				/* 390/4; 390 = config address */
+	outb_p(0x1b, 0x2fa);				/* Inverse of e4 */
+	outb_p(0x0f, 0x390);				/* Write index */
+	if (inb_p(0x391) != 0xe4)			/* Config address found? */
+		return -ENODEV;				/* No: no 82C710 here */
+
+	outb_p(0x0d, 0x390);				/* Write index */
+	ct82c710_iores.start = inb_p(0x391) << 2;	/* Get mouse I/O address */
+	ct82c710_iores.end = ct82c710_iores.start + 1;
+	ct82c710_iores.flags = IORESOURCE_IO;
+	outb_p(0x0f, 0x390);
+	outb_p(0x0f, 0x391);				/* Close config mode */
+
+	return 0;
+}
+
+static int ct82c710_probe(struct platform_device *dev)
+{
+	ct82c710_port = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!ct82c710_port)
+		return -ENOMEM;
+
+	ct82c710_port->id.type = SERIO_8042;
+	ct82c710_port->dev.parent = &dev->dev;
+	ct82c710_port->open = ct82c710_open;
+	ct82c710_port->close = ct82c710_close;
+	ct82c710_port->write = ct82c710_write;
+	strlcpy(ct82c710_port->name, "C&T 82c710 mouse port",
+		sizeof(ct82c710_port->name));
+	snprintf(ct82c710_port->phys, sizeof(ct82c710_port->phys),
+		 "isa%16llx/serio0", (unsigned long long)CT82C710_DATA);
+
+	serio_register_port(ct82c710_port);
+
+	printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n",
+		(unsigned long long)CT82C710_DATA, CT82C710_IRQ);
+
+	return 0;
+}
+
+static int ct82c710_remove(struct platform_device *dev)
+{
+	serio_unregister_port(ct82c710_port);
+
+	return 0;
+}
+
+static struct platform_driver ct82c710_driver = {
+	.driver		= {
+		.name	= "ct82c710",
+	},
+	.probe		= ct82c710_probe,
+	.remove		= ct82c710_remove,
+};
+
+
+static int __init ct82c710_init(void)
+{
+	int error;
+
+	error = ct82c710_detect();
+	if (error)
+		return error;
+
+	error = platform_driver_register(&ct82c710_driver);
+	if (error)
+		return error;
+
+	ct82c710_device = platform_device_alloc("ct82c710", -1);
+	if (!ct82c710_device) {
+		error = -ENOMEM;
+		goto err_unregister_driver;
+	}
+
+	error = platform_device_add_resources(ct82c710_device, &ct82c710_iores, 1);
+	if (error)
+		goto err_free_device;
+
+	error = platform_device_add(ct82c710_device);
+	if (error)
+		goto err_free_device;
+
+	return 0;
+
+ err_free_device:
+	platform_device_put(ct82c710_device);
+ err_unregister_driver:
+	platform_driver_unregister(&ct82c710_driver);
+	return error;
+}
+
+static void __exit ct82c710_exit(void)
+{
+	platform_device_unregister(ct82c710_device);
+	platform_driver_unregister(&ct82c710_driver);
+}
+
+module_init(ct82c710_init);
+module_exit(ct82c710_exit);
diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c
new file mode 100644
index 0000000..ecba666
--- /dev/null
+++ b/drivers/input/serio/gscps2.c
@@ -0,0 +1,463 @@
+/*
+ * drivers/input/serio/gscps2.c
+ *
+ * Copyright (c) 2004-2006 Helge Deller <deller@gmx.de>
+ * Copyright (c) 2002 Laurent Canet <canetl@esiee.fr>
+ * Copyright (c) 2002 Thibaut Varene <varenet@parisc-linux.org>
+ *
+ * Pieces of code based on linux-2.4's hp_mouse.c & hp_keyb.c
+ *	Copyright (c) 1999 Alex deVries <alex@onefishtwo.ca>
+ *	Copyright (c) 1999-2000 Philipp Rumpf <prumpf@tux.org>
+ *	Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr>
+ *	Copyright (c) 2000-2001 Thomas Marteau <marteaut@esiee.fr>
+ *
+ * HP GSC PS/2 port driver, found in PA/RISC Workstations
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * TODO:
+ * - Dino testing (did HP ever shipped a machine on which this port
+ *                 was usable/enabled ?)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/serio.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/parisc-device.h>
+
+MODULE_AUTHOR("Laurent Canet <canetl@esiee.fr>, Thibaut Varene <varenet@parisc-linux.org>, Helge Deller <deller@gmx.de>");
+MODULE_DESCRIPTION("HP GSC PS2 port driver");
+MODULE_LICENSE("GPL");
+
+#define PFX "gscps2.c: "
+
+/*
+ * Driver constants
+ */
+
+/* various constants */
+#define ENABLE			1
+#define DISABLE			0
+
+#define GSC_DINO_OFFSET		0x0800	/* offset for DINO controller versus LASI one */
+
+/* PS/2 IO port offsets */
+#define GSC_ID			0x00	/* device ID offset (see: GSC_ID_XXX) */
+#define GSC_RESET		0x00	/* reset port offset */
+#define GSC_RCVDATA		0x04	/* receive port offset */
+#define GSC_XMTDATA		0x04	/* transmit port offset */
+#define GSC_CONTROL		0x08	/* see: Control register bits */
+#define GSC_STATUS		0x0C	/* see: Status register bits */
+
+/* Control register bits */
+#define GSC_CTRL_ENBL		0x01	/* enable interface */
+#define GSC_CTRL_LPBXR		0x02	/* loopback operation */
+#define GSC_CTRL_DIAG		0x20	/* directly control clock/data line */
+#define GSC_CTRL_DATDIR		0x40	/* data line direct control */
+#define GSC_CTRL_CLKDIR		0x80	/* clock line direct control */
+
+/* Status register bits */
+#define GSC_STAT_RBNE		0x01	/* Receive Buffer Not Empty */
+#define GSC_STAT_TBNE		0x02	/* Transmit Buffer Not Empty */
+#define GSC_STAT_TERR		0x04	/* Timeout Error */
+#define GSC_STAT_PERR		0x08	/* Parity Error */
+#define GSC_STAT_CMPINTR	0x10	/* Composite Interrupt = irq on any port */
+#define GSC_STAT_DATSHD		0x40	/* Data Line Shadow */
+#define GSC_STAT_CLKSHD		0x80	/* Clock Line Shadow */
+
+/* IDs returned by GSC_ID port register */
+#define GSC_ID_KEYBOARD		0	/* device ID values */
+#define GSC_ID_MOUSE		1
+
+
+static irqreturn_t gscps2_interrupt(int irq, void *dev);
+
+#define BUFFER_SIZE 0x0f
+
+/* GSC PS/2 port device struct */
+struct gscps2port {
+	struct list_head node;
+	struct parisc_device *padev;
+	struct serio *port;
+	spinlock_t lock;
+	char *addr;
+	u8 act, append; /* position in buffer[] */
+	struct {
+		u8 data;
+		u8 str;
+	} buffer[BUFFER_SIZE+1];
+	int id;
+};
+
+/*
+ * Various HW level routines
+ */
+
+#define gscps2_readb_input(x)		readb((x)+GSC_RCVDATA)
+#define gscps2_readb_control(x)		readb((x)+GSC_CONTROL)
+#define gscps2_readb_status(x)		readb((x)+GSC_STATUS)
+#define gscps2_writeb_control(x, y)	writeb((x), (y)+GSC_CONTROL)
+
+
+/*
+ * wait_TBE() - wait for Transmit Buffer Empty
+ */
+
+static int wait_TBE(char *addr)
+{
+	int timeout = 25000; /* device is expected to react within 250 msec */
+	while (gscps2_readb_status(addr) & GSC_STAT_TBNE) {
+		if (!--timeout)
+			return 0;	/* This should not happen */
+		udelay(10);
+	}
+	return 1;
+}
+
+
+/*
+ * gscps2_flush() - flush the receive buffer
+ */
+
+static void gscps2_flush(struct gscps2port *ps2port)
+{
+	while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE)
+		gscps2_readb_input(ps2port->addr);
+	ps2port->act = ps2port->append = 0;
+}
+
+/*
+ * gscps2_writeb_output() - write a byte to the port
+ *
+ * returns 1 on success, 0 on error
+ */
+
+static inline int gscps2_writeb_output(struct gscps2port *ps2port, u8 data)
+{
+	unsigned long flags;
+	char *addr = ps2port->addr;
+
+	if (!wait_TBE(addr)) {
+		printk(KERN_DEBUG PFX "timeout - could not write byte %#x\n", data);
+		return 0;
+	}
+
+	while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE)
+		/* wait */;
+
+	spin_lock_irqsave(&ps2port->lock, flags);
+	writeb(data, addr+GSC_XMTDATA);
+	spin_unlock_irqrestore(&ps2port->lock, flags);
+
+	/* this is ugly, but due to timing of the port it seems to be necessary. */
+	mdelay(6);
+
+	/* make sure any received data is returned as fast as possible */
+	/* this is important e.g. when we set the LEDs on the keyboard */
+	gscps2_interrupt(0, NULL);
+
+	return 1;
+}
+
+
+/*
+ * gscps2_enable() - enables or disables the port
+ */
+
+static void gscps2_enable(struct gscps2port *ps2port, int enable)
+{
+	unsigned long flags;
+	u8 data;
+
+	/* now enable/disable the port */
+	spin_lock_irqsave(&ps2port->lock, flags);
+	gscps2_flush(ps2port);
+	data = gscps2_readb_control(ps2port->addr);
+	if (enable)
+		data |= GSC_CTRL_ENBL;
+	else
+		data &= ~GSC_CTRL_ENBL;
+	gscps2_writeb_control(data, ps2port->addr);
+	spin_unlock_irqrestore(&ps2port->lock, flags);
+	wait_TBE(ps2port->addr);
+	gscps2_flush(ps2port);
+}
+
+/*
+ * gscps2_reset() - resets the PS/2 port
+ */
+
+static void gscps2_reset(struct gscps2port *ps2port)
+{
+	char *addr = ps2port->addr;
+	unsigned long flags;
+
+	/* reset the interface */
+	spin_lock_irqsave(&ps2port->lock, flags);
+	gscps2_flush(ps2port);
+	writeb(0xff, addr+GSC_RESET);
+	gscps2_flush(ps2port);
+	spin_unlock_irqrestore(&ps2port->lock, flags);
+}
+
+static LIST_HEAD(ps2port_list);
+
+/**
+ * gscps2_interrupt() - Interruption service routine
+ *
+ * This function reads received PS/2 bytes and processes them on
+ * all interfaces.
+ * The problematic part here is, that the keyboard and mouse PS/2 port
+ * share the same interrupt and it's not possible to send data if any
+ * one of them holds input data. To solve this problem we try to receive
+ * the data as fast as possible and handle the reporting to the upper layer
+ * later.
+ */
+
+static irqreturn_t gscps2_interrupt(int irq, void *dev)
+{
+	struct gscps2port *ps2port;
+
+	list_for_each_entry(ps2port, &ps2port_list, node) {
+
+	  unsigned long flags;
+	  spin_lock_irqsave(&ps2port->lock, flags);
+
+	  while ( (ps2port->buffer[ps2port->append].str =
+		   gscps2_readb_status(ps2port->addr)) & GSC_STAT_RBNE ) {
+		ps2port->buffer[ps2port->append].data =
+				gscps2_readb_input(ps2port->addr);
+		ps2port->append = ((ps2port->append+1) & BUFFER_SIZE);
+	  }
+
+	  spin_unlock_irqrestore(&ps2port->lock, flags);
+
+	} /* list_for_each_entry */
+
+	/* all data was read from the ports - now report the data to upper layer */
+
+	list_for_each_entry(ps2port, &ps2port_list, node) {
+
+	  while (ps2port->act != ps2port->append) {
+
+	    unsigned int rxflags;
+	    u8 data, status;
+
+	    /* Did new data arrived while we read existing data ?
+	       If yes, exit now and let the new irq handler start over again */
+	    if (gscps2_readb_status(ps2port->addr) & GSC_STAT_CMPINTR)
+		return IRQ_HANDLED;
+
+	    status = ps2port->buffer[ps2port->act].str;
+	    data   = ps2port->buffer[ps2port->act].data;
+
+	    ps2port->act = ((ps2port->act+1) & BUFFER_SIZE);
+	    rxflags =	((status & GSC_STAT_TERR) ? SERIO_TIMEOUT : 0 ) |
+			((status & GSC_STAT_PERR) ? SERIO_PARITY  : 0 );
+
+	    serio_interrupt(ps2port->port, data, rxflags);
+
+	  } /* while() */
+
+	} /* list_for_each_entry */
+
+	return IRQ_HANDLED;
+}
+
+
+/*
+ * gscps2_write() - send a byte out through the aux interface.
+ */
+
+static int gscps2_write(struct serio *port, unsigned char data)
+{
+	struct gscps2port *ps2port = port->port_data;
+
+	if (!gscps2_writeb_output(ps2port, data)) {
+		printk(KERN_DEBUG PFX "sending byte %#x failed.\n", data);
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * gscps2_open() is called when a port is opened by the higher layer.
+ * It resets and enables the port.
+ */
+
+static int gscps2_open(struct serio *port)
+{
+	struct gscps2port *ps2port = port->port_data;
+
+	gscps2_reset(ps2port);
+
+	/* enable it */
+	gscps2_enable(ps2port, ENABLE);
+
+	gscps2_interrupt(0, NULL);
+
+	return 0;
+}
+
+/*
+ * gscps2_close() disables the port
+ */
+
+static void gscps2_close(struct serio *port)
+{
+	struct gscps2port *ps2port = port->port_data;
+	gscps2_enable(ps2port, DISABLE);
+}
+
+/**
+ * gscps2_probe() - Probes PS2 devices
+ * @return: success/error report
+ */
+
+static int gscps2_probe(struct parisc_device *dev)
+{
+	struct gscps2port *ps2port;
+	struct serio *serio;
+	unsigned long hpa = dev->hpa.start;
+	int ret;
+
+	if (!dev->irq)
+		return -ENODEV;
+
+	/* Offset for DINO PS/2. Works with LASI even */
+	if (dev->id.sversion == 0x96)
+		hpa += GSC_DINO_OFFSET;
+
+	ps2port = kzalloc(sizeof(struct gscps2port), GFP_KERNEL);
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!ps2port || !serio) {
+		ret = -ENOMEM;
+		goto fail_nomem;
+	}
+
+	dev_set_drvdata(&dev->dev, ps2port);
+
+	ps2port->port = serio;
+	ps2port->padev = dev;
+	ps2port->addr = ioremap_nocache(hpa, GSC_STATUS + 4);
+	spin_lock_init(&ps2port->lock);
+
+	gscps2_reset(ps2port);
+	ps2port->id = readb(ps2port->addr + GSC_ID) & 0x0f;
+
+	snprintf(serio->name, sizeof(serio->name), "gsc-ps2-%s",
+		 (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse");
+	strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
+	serio->id.type		= SERIO_8042;
+	serio->write		= gscps2_write;
+	serio->open		= gscps2_open;
+	serio->close		= gscps2_close;
+	serio->port_data	= ps2port;
+	serio->dev.parent	= &dev->dev;
+
+	ret = -EBUSY;
+	if (request_irq(dev->irq, gscps2_interrupt, IRQF_SHARED, ps2port->port->name, ps2port))
+		goto fail_miserably;
+
+	if (ps2port->id != GSC_ID_KEYBOARD && ps2port->id != GSC_ID_MOUSE) {
+		printk(KERN_WARNING PFX "Unsupported PS/2 port at 0x%08lx (id=%d) ignored\n",
+				hpa, ps2port->id);
+		ret = -ENODEV;
+		goto fail;
+	}
+
+#if 0
+	if (!request_mem_region(hpa, GSC_STATUS + 4, ps2port->port.name))
+		goto fail;
+#endif
+
+	printk(KERN_INFO "serio: %s port at 0x%p irq %d @ %s\n",
+		ps2port->port->name,
+		ps2port->addr,
+		ps2port->padev->irq,
+		ps2port->port->phys);
+
+	serio_register_port(ps2port->port);
+
+	list_add_tail(&ps2port->node, &ps2port_list);
+
+	return 0;
+
+fail:
+	free_irq(dev->irq, ps2port);
+
+fail_miserably:
+	iounmap(ps2port->addr);
+	release_mem_region(dev->hpa.start, GSC_STATUS + 4);
+
+fail_nomem:
+	kfree(ps2port);
+	kfree(serio);
+	return ret;
+}
+
+/**
+ * gscps2_remove() - Removes PS2 devices
+ * @return: success/error report
+ */
+
+static int gscps2_remove(struct parisc_device *dev)
+{
+	struct gscps2port *ps2port = dev_get_drvdata(&dev->dev);
+
+	serio_unregister_port(ps2port->port);
+	free_irq(dev->irq, ps2port);
+	gscps2_flush(ps2port);
+	list_del(&ps2port->node);
+	iounmap(ps2port->addr);
+#if 0
+	release_mem_region(dev->hpa, GSC_STATUS + 4);
+#endif
+	dev_set_drvdata(&dev->dev, NULL);
+	kfree(ps2port);
+	return 0;
+}
+
+
+static struct parisc_device_id gscps2_device_tbl[] = {
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00084 }, /* LASI PS/2 */
+#ifdef DINO_TESTED
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00096 }, /* DINO PS/2 */
+#endif
+	{ 0, }	/* 0 terminated list */
+};
+MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl);
+
+static struct parisc_driver parisc_ps2_driver = {
+	.name		= "gsc_ps2",
+	.id_table	= gscps2_device_tbl,
+	.probe		= gscps2_probe,
+	.remove		= gscps2_remove,
+};
+
+static int __init gscps2_init(void)
+{
+	register_parisc_driver(&parisc_ps2_driver);
+	return 0;
+}
+
+static void __exit gscps2_exit(void)
+{
+	unregister_parisc_driver(&parisc_ps2_driver);
+}
+
+
+module_init(gscps2_init);
+module_exit(gscps2_exit);
+
diff --git a/drivers/input/serio/hil_mlc.c b/drivers/input/serio/hil_mlc.c
new file mode 100644
index 0000000..65605e4
--- /dev/null
+++ b/drivers/input/serio/hil_mlc.c
@@ -0,0 +1,1016 @@
+/*
+ * HIL MLC state machine and serio interface driver
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *
+ * References:
+ * HP-HIL Technical Reference Manual.  Hewlett Packard Product No. 45918A
+ *
+ *
+ *	Driver theory of operation:
+ *
+ *	Some access methods and an ISR is defined by the sub-driver
+ *	(e.g. hp_sdc_mlc.c).  These methods are expected to provide a
+ *	few bits of logic in addition to raw access to the HIL MLC,
+ *	specifically, the ISR, which is entirely registered by the
+ *	sub-driver and invoked directly, must check for record
+ *	termination or packet match, at which point a semaphore must
+ *	be cleared and then the hil_mlcs_tasklet must be scheduled.
+ *
+ *	The hil_mlcs_tasklet processes the state machine for all MLCs
+ *	each time it runs, checking each MLC's progress at the current
+ *	node in the state machine, and moving the MLC to subsequent nodes
+ *	in the state machine when appropriate.  It will reschedule
+ *	itself if output is pending.  (This rescheduling should be replaced
+ *	at some point with a sub-driver-specific mechanism.)
+ *
+ *	A timer task prods the tasklet once per second to prevent
+ *	hangups when attached devices do not return expected data
+ *	and to initiate probes of the loop for new devices.
+ */
+
+#include <linux/hil_mlc.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("HIL MLC serio");
+MODULE_LICENSE("Dual BSD/GPL");
+
+EXPORT_SYMBOL(hil_mlc_register);
+EXPORT_SYMBOL(hil_mlc_unregister);
+
+#define PREFIX "HIL MLC: "
+
+static LIST_HEAD(hil_mlcs);
+static DEFINE_RWLOCK(hil_mlcs_lock);
+static struct timer_list	hil_mlcs_kicker;
+static int			hil_mlcs_probe;
+
+static void hil_mlcs_process(unsigned long unused);
+static DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet, hil_mlcs_process, 0);
+
+
+/* #define HIL_MLC_DEBUG */
+
+/********************** Device info/instance management **********************/
+
+static void hil_mlc_clear_di_map(hil_mlc *mlc, int val)
+{
+	int j;
+
+	for (j = val; j < 7 ; j++)
+		mlc->di_map[j] = -1;
+}
+
+static void hil_mlc_clear_di_scratch(hil_mlc *mlc)
+{
+	memset(&mlc->di_scratch, 0, sizeof(mlc->di_scratch));
+}
+
+static void hil_mlc_copy_di_scratch(hil_mlc *mlc, int idx)
+{
+	memcpy(&mlc->di[idx], &mlc->di_scratch, sizeof(mlc->di_scratch));
+}
+
+static int hil_mlc_match_di_scratch(hil_mlc *mlc)
+{
+	int idx;
+
+	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
+		int j, found = 0;
+
+		/* In-use slots are not eligible. */
+		for (j = 0; j < 7 ; j++)
+			if (mlc->di_map[j] == idx)
+				found++;
+
+		if (found)
+			continue;
+
+		if (!memcmp(mlc->di + idx, &mlc->di_scratch,
+				sizeof(mlc->di_scratch)))
+			break;
+	}
+	return idx >= HIL_MLC_DEVMEM ? -1 : idx;
+}
+
+static int hil_mlc_find_free_di(hil_mlc *mlc)
+{
+	int idx;
+
+	/* TODO: Pick all-zero slots first, failing that,
+	 * randomize the slot picked among those eligible.
+	 */
+	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
+		int j, found = 0;
+
+		for (j = 0; j < 7 ; j++)
+			if (mlc->di_map[j] == idx)
+				found++;
+
+		if (!found)
+			break;
+	}
+
+	return idx; /* Note: It is guaranteed at least one above will match */
+}
+
+static inline void hil_mlc_clean_serio_map(hil_mlc *mlc)
+{
+	int idx;
+
+	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
+		int j, found = 0;
+
+		for (j = 0; j < 7 ; j++)
+			if (mlc->di_map[j] == idx)
+				found++;
+
+		if (!found)
+			mlc->serio_map[idx].di_revmap = -1;
+	}
+}
+
+static void hil_mlc_send_polls(hil_mlc *mlc)
+{
+	int did, i, cnt;
+	struct serio *serio;
+	struct serio_driver *drv;
+
+	i = cnt = 0;
+	did = (mlc->ipacket[0] & HIL_PKT_ADDR_MASK) >> 8;
+	serio = did ? mlc->serio[mlc->di_map[did - 1]] : NULL;
+	drv = (serio != NULL) ? serio->drv : NULL;
+
+	while (mlc->icount < 15 - i) {
+		hil_packet p;
+
+		p = mlc->ipacket[i];
+		if (did != (p & HIL_PKT_ADDR_MASK) >> 8) {
+			if (drv && drv->interrupt) {
+				drv->interrupt(serio, 0, 0);
+				drv->interrupt(serio, HIL_ERR_INT >> 16, 0);
+				drv->interrupt(serio, HIL_PKT_CMD >> 8,  0);
+				drv->interrupt(serio, HIL_CMD_POL + cnt, 0);
+			}
+
+			did = (p & HIL_PKT_ADDR_MASK) >> 8;
+			serio = did ? mlc->serio[mlc->di_map[did-1]] : NULL;
+			drv = (serio != NULL) ? serio->drv : NULL;
+			cnt = 0;
+		}
+
+		cnt++;
+		i++;
+
+		if (drv && drv->interrupt) {
+			drv->interrupt(serio, (p >> 24), 0);
+			drv->interrupt(serio, (p >> 16) & 0xff, 0);
+			drv->interrupt(serio, (p >> 8) & ~HIL_PKT_ADDR_MASK, 0);
+			drv->interrupt(serio, p & 0xff, 0);
+		}
+	}
+}
+
+/*************************** State engine *********************************/
+
+#define HILSEN_SCHED	0x000100	/* Schedule the tasklet		*/
+#define HILSEN_BREAK	0x000200	/* Wait until next pass		*/
+#define HILSEN_UP	0x000400	/* relative node#, decrement	*/
+#define HILSEN_DOWN	0x000800	/* relative node#, increment	*/
+#define HILSEN_FOLLOW	0x001000	/* use retval as next node#	*/
+
+#define HILSEN_MASK	0x0000ff
+#define HILSEN_START	0
+#define HILSEN_RESTART	1
+#define HILSEN_DHR	9
+#define HILSEN_DHR2	10
+#define HILSEN_IFC	14
+#define HILSEN_HEAL0	16
+#define HILSEN_HEAL	18
+#define HILSEN_ACF      21
+#define HILSEN_ACF2	22
+#define HILSEN_DISC0	25
+#define HILSEN_DISC	27
+#define HILSEN_MATCH	40
+#define HILSEN_OPERATE	41
+#define HILSEN_PROBE	44
+#define HILSEN_DSR	52
+#define HILSEN_REPOLL	55
+#define HILSEN_IFCACF	58
+#define HILSEN_END	60
+
+#define HILSEN_NEXT	(HILSEN_DOWN | 1)
+#define HILSEN_SAME	(HILSEN_DOWN | 0)
+#define HILSEN_LAST	(HILSEN_UP | 1)
+
+#define HILSEN_DOZE	(HILSEN_SAME | HILSEN_SCHED | HILSEN_BREAK)
+#define HILSEN_SLEEP	(HILSEN_SAME | HILSEN_BREAK)
+
+static int hilse_match(hil_mlc *mlc, int unused)
+{
+	int rc;
+
+	rc = hil_mlc_match_di_scratch(mlc);
+	if (rc == -1) {
+		rc = hil_mlc_find_free_di(mlc);
+		if (rc == -1)
+			goto err;
+
+#ifdef HIL_MLC_DEBUG
+		printk(KERN_DEBUG PREFIX "new in slot %i\n", rc);
+#endif
+		hil_mlc_copy_di_scratch(mlc, rc);
+		mlc->di_map[mlc->ddi] = rc;
+		mlc->serio_map[rc].di_revmap = mlc->ddi;
+		hil_mlc_clean_serio_map(mlc);
+		serio_rescan(mlc->serio[rc]);
+		return -1;
+	}
+
+	mlc->di_map[mlc->ddi] = rc;
+#ifdef HIL_MLC_DEBUG
+	printk(KERN_DEBUG PREFIX "same in slot %i\n", rc);
+#endif
+	mlc->serio_map[rc].di_revmap = mlc->ddi;
+	hil_mlc_clean_serio_map(mlc);
+	return 0;
+
+ err:
+	printk(KERN_ERR PREFIX "Residual device slots exhausted, close some serios!\n");
+	return 1;
+}
+
+/* An LCV used to prevent runaway loops, forces 5 second sleep when reset. */
+static int hilse_init_lcv(hil_mlc *mlc, int unused)
+{
+	struct timeval tv;
+
+	do_gettimeofday(&tv);
+
+	if (mlc->lcv && (tv.tv_sec - mlc->lcv_tv.tv_sec) < 5)
+		return -1;
+
+	mlc->lcv_tv = tv;
+	mlc->lcv = 0;
+
+	return 0;
+}
+
+static int hilse_inc_lcv(hil_mlc *mlc, int lim)
+{
+	return mlc->lcv++ >= lim ? -1 : 0;
+}
+
+#if 0
+static int hilse_set_lcv(hil_mlc *mlc, int val)
+{
+	mlc->lcv = val;
+
+	return 0;
+}
+#endif
+
+/* Management of the discovered device index (zero based, -1 means no devs) */
+static int hilse_set_ddi(hil_mlc *mlc, int val)
+{
+	mlc->ddi = val;
+	hil_mlc_clear_di_map(mlc, val + 1);
+
+	return 0;
+}
+
+static int hilse_dec_ddi(hil_mlc *mlc, int unused)
+{
+	mlc->ddi--;
+	if (mlc->ddi <= -1) {
+		mlc->ddi = -1;
+		hil_mlc_clear_di_map(mlc, 0);
+		return -1;
+	}
+	hil_mlc_clear_di_map(mlc, mlc->ddi + 1);
+
+	return 0;
+}
+
+static int hilse_inc_ddi(hil_mlc *mlc, int unused)
+{
+	BUG_ON(mlc->ddi >= 6);
+	mlc->ddi++;
+
+	return 0;
+}
+
+static int hilse_take_idd(hil_mlc *mlc, int unused)
+{
+	int i;
+
+	/* Help the state engine:
+	 * Is this a real IDD response or just an echo?
+	 *
+	 * Real IDD response does not start with a command.
+	 */
+	if (mlc->ipacket[0] & HIL_PKT_CMD)
+		goto bail;
+
+	/* Should have the command echoed further down. */
+	for (i = 1; i < 16; i++) {
+		if (((mlc->ipacket[i] & HIL_PKT_ADDR_MASK) ==
+		     (mlc->ipacket[0] & HIL_PKT_ADDR_MASK)) &&
+		    (mlc->ipacket[i] & HIL_PKT_CMD) &&
+		    ((mlc->ipacket[i] & HIL_PKT_DATA_MASK) == HIL_CMD_IDD))
+			break;
+	}
+	if (i > 15)
+		goto bail;
+
+	/* And the rest of the packets should still be clear. */
+	while (++i < 16)
+		if (mlc->ipacket[i])
+			break;
+
+	if (i < 16)
+		goto bail;
+
+	for (i = 0; i < 16; i++)
+		mlc->di_scratch.idd[i] =
+			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
+
+	/* Next step is to see if RSC supported */
+	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_RSC)
+		return HILSEN_NEXT;
+
+	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD)
+		return HILSEN_DOWN | 4;
+
+	return 0;
+
+ bail:
+	mlc->ddi--;
+
+	return -1; /* This should send us off to ACF */
+}
+
+static int hilse_take_rsc(hil_mlc *mlc, int unused)
+{
+	int i;
+
+	for (i = 0; i < 16; i++)
+		mlc->di_scratch.rsc[i] =
+			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
+
+	/* Next step is to see if EXD supported (IDD has already been read) */
+	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD)
+		return HILSEN_NEXT;
+
+	return 0;
+}
+
+static int hilse_take_exd(hil_mlc *mlc, int unused)
+{
+	int i;
+
+	for (i = 0; i < 16; i++)
+		mlc->di_scratch.exd[i] =
+			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
+
+	/* Next step is to see if RNM supported. */
+	if (mlc->di_scratch.exd[0] & HIL_EXD_HEADER_RNM)
+		return HILSEN_NEXT;
+
+	return 0;
+}
+
+static int hilse_take_rnm(hil_mlc *mlc, int unused)
+{
+	int i;
+
+	for (i = 0; i < 16; i++)
+		mlc->di_scratch.rnm[i] =
+			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
+
+	printk(KERN_INFO PREFIX "Device name gotten: %16s\n",
+			mlc->di_scratch.rnm);
+
+	return 0;
+}
+
+static int hilse_operate(hil_mlc *mlc, int repoll)
+{
+
+	if (mlc->opercnt == 0)
+		hil_mlcs_probe = 0;
+	mlc->opercnt = 1;
+
+	hil_mlc_send_polls(mlc);
+
+	if (!hil_mlcs_probe)
+		return 0;
+	hil_mlcs_probe = 0;
+	mlc->opercnt = 0;
+	return 1;
+}
+
+#define FUNC(funct, funct_arg, zero_rc, neg_rc, pos_rc) \
+{ HILSE_FUNC,		{ .func = funct }, funct_arg, zero_rc, neg_rc, pos_rc },
+#define OUT(pack) \
+{ HILSE_OUT,		{ .packet = pack }, 0, HILSEN_NEXT, HILSEN_DOZE, 0 },
+#define CTS \
+{ HILSE_CTS,		{ .packet = 0    }, 0, HILSEN_NEXT | HILSEN_SCHED | HILSEN_BREAK, HILSEN_DOZE, 0 },
+#define EXPECT(comp, to, got, got_wrong, timed_out) \
+{ HILSE_EXPECT,		{ .packet = comp }, to, got, got_wrong, timed_out },
+#define EXPECT_LAST(comp, to, got, got_wrong, timed_out) \
+{ HILSE_EXPECT_LAST,	{ .packet = comp }, to, got, got_wrong, timed_out },
+#define EXPECT_DISC(comp, to, got, got_wrong, timed_out) \
+{ HILSE_EXPECT_DISC,	{ .packet = comp }, to, got, got_wrong, timed_out },
+#define IN(to, got, got_error, timed_out) \
+{ HILSE_IN,		{ .packet = 0    }, to, got, got_error, timed_out },
+#define OUT_DISC(pack) \
+{ HILSE_OUT_DISC,	{ .packet = pack }, 0, 0, 0, 0 },
+#define OUT_LAST(pack) \
+{ HILSE_OUT_LAST,	{ .packet = pack }, 0, 0, 0, 0 },
+
+static const struct hilse_node hil_mlc_se[HILSEN_END] = {
+
+	/* 0  HILSEN_START */
+	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_SLEEP,	0)
+
+	/* 1  HILSEN_RESTART */
+	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,  0)
+	OUT(HIL_CTRL_ONLY)			/* Disable APE */
+	CTS
+
+#define TEST_PACKET(x) \
+(HIL_PKT_CMD | (x << HIL_PKT_ADDR_SHIFT) | x << 4 | x)
+
+	OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0x5))
+	EXPECT(HIL_ERR_INT | TEST_PACKET(0x5),
+	       2000,		HILSEN_NEXT,	HILSEN_RESTART,	HILSEN_RESTART)
+	OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0xa))
+	EXPECT(HIL_ERR_INT | TEST_PACKET(0xa),
+	       2000,		HILSEN_NEXT,	HILSEN_RESTART,	HILSEN_RESTART)
+	OUT(HIL_CTRL_ONLY | 0)			/* Disable test mode */
+
+	/* 9  HILSEN_DHR */
+	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_SLEEP,	0)
+
+	/* 10 HILSEN_DHR2 */
+	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,	0)
+	FUNC(hilse_set_ddi, -1,	HILSEN_NEXT,	0,		0)
+	OUT(HIL_PKT_CMD | HIL_CMD_DHR)
+	IN(300000,		HILSEN_DHR2,	HILSEN_DHR2,	HILSEN_NEXT)
+
+	/* 14 HILSEN_IFC */
+	OUT(HIL_PKT_CMD | HIL_CMD_IFC)
+	EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT,
+	       20000,		HILSEN_DISC,	HILSEN_DHR2,	HILSEN_NEXT )
+
+	/* If devices are there, they weren't in PUP or other loopback mode.
+	 * We're more concerned at this point with restoring operation
+	 * to devices than discovering new ones, so we try to salvage
+	 * the loop configuration by closing off the loop.
+	 */
+
+	/* 16 HILSEN_HEAL0 */
+	FUNC(hilse_dec_ddi, 0,	HILSEN_NEXT,	HILSEN_ACF,	0)
+	FUNC(hilse_inc_ddi, 0,	HILSEN_NEXT,	0,		0)
+
+	/* 18 HILSEN_HEAL */
+	OUT_LAST(HIL_CMD_ELB)
+	EXPECT_LAST(HIL_CMD_ELB | HIL_ERR_INT,
+		    20000,	HILSEN_REPOLL,	HILSEN_DSR,	HILSEN_NEXT)
+	FUNC(hilse_dec_ddi, 0,	HILSEN_HEAL,	HILSEN_NEXT,	0)
+
+	/* 21 HILSEN_ACF */
+	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_DOZE,	0)
+
+	/* 22 HILSEN_ACF2 */
+	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,	0)
+	OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1)
+	IN(20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_NEXT)
+
+	/* 25 HILSEN_DISC0 */
+	OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB)
+	EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_ELB | HIL_ERR_INT,
+	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
+
+	/* Only enter here if response just received */
+	/* 27 HILSEN_DISC */
+	OUT_DISC(HIL_PKT_CMD | HIL_CMD_IDD)
+	EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_IDD | HIL_ERR_INT,
+	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_START)
+	FUNC(hilse_inc_ddi,  0,	HILSEN_NEXT,	HILSEN_START,	0)
+	FUNC(hilse_take_idd, 0,	HILSEN_MATCH,	HILSEN_IFCACF,	HILSEN_FOLLOW)
+	OUT_LAST(HIL_PKT_CMD | HIL_CMD_RSC)
+	EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RSC | HIL_ERR_INT,
+	       30000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
+	FUNC(hilse_take_rsc, 0,	HILSEN_MATCH,	0,		HILSEN_FOLLOW)
+	OUT_LAST(HIL_PKT_CMD | HIL_CMD_EXD)
+	EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_EXD | HIL_ERR_INT,
+	       30000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
+	FUNC(hilse_take_exd, 0,	HILSEN_MATCH,	0,		HILSEN_FOLLOW)
+	OUT_LAST(HIL_PKT_CMD | HIL_CMD_RNM)
+	EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RNM | HIL_ERR_INT,
+	       30000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
+	FUNC(hilse_take_rnm, 0, HILSEN_MATCH,	0,		0)
+
+	/* 40 HILSEN_MATCH */
+	FUNC(hilse_match, 0,	HILSEN_NEXT,	HILSEN_NEXT,	/* TODO */ 0)
+
+	/* 41 HILSEN_OPERATE */
+	OUT(HIL_PKT_CMD | HIL_CMD_POL)
+	EXPECT(HIL_PKT_CMD | HIL_CMD_POL | HIL_ERR_INT,
+	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_NEXT)
+	FUNC(hilse_operate, 0,	HILSEN_OPERATE,	HILSEN_IFC,	HILSEN_NEXT)
+
+	/* 44 HILSEN_PROBE */
+	OUT_LAST(HIL_PKT_CMD | HIL_CMD_EPT)
+	IN(10000,		HILSEN_DISC,	HILSEN_DSR,	HILSEN_NEXT)
+	OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB)
+	IN(10000,		HILSEN_DISC,	HILSEN_DSR,	HILSEN_NEXT)
+	OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1)
+	IN(10000,		HILSEN_DISC0,	HILSEN_DSR,	HILSEN_NEXT)
+	OUT_LAST(HIL_PKT_CMD | HIL_CMD_ELB)
+	IN(10000,		HILSEN_OPERATE,	HILSEN_DSR,	HILSEN_DSR)
+
+	/* 52 HILSEN_DSR */
+	FUNC(hilse_set_ddi, -1,	HILSEN_NEXT,	0,		0)
+	OUT(HIL_PKT_CMD | HIL_CMD_DSR)
+	IN(20000,		HILSEN_DHR,	HILSEN_DHR,	HILSEN_IFC)
+
+	/* 55 HILSEN_REPOLL */
+	OUT(HIL_PKT_CMD | HIL_CMD_RPL)
+	EXPECT(HIL_PKT_CMD | HIL_CMD_RPL | HIL_ERR_INT,
+	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_NEXT)
+	FUNC(hilse_operate, 1,	HILSEN_OPERATE,	HILSEN_IFC,	HILSEN_PROBE)
+
+	/* 58 HILSEN_IFCACF */
+	OUT(HIL_PKT_CMD | HIL_CMD_IFC)
+	EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT,
+	       20000,		HILSEN_ACF2,	HILSEN_DHR2,	HILSEN_HEAL)
+
+	/* 60 HILSEN_END */
+};
+
+static inline void hilse_setup_input(hil_mlc *mlc, const struct hilse_node *node)
+{
+
+	switch (node->act) {
+	case HILSE_EXPECT_DISC:
+		mlc->imatch = node->object.packet;
+		mlc->imatch |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT);
+		break;
+	case HILSE_EXPECT_LAST:
+		mlc->imatch = node->object.packet;
+		mlc->imatch |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT);
+		break;
+	case HILSE_EXPECT:
+		mlc->imatch = node->object.packet;
+		break;
+	case HILSE_IN:
+		mlc->imatch = 0;
+		break;
+	default:
+		BUG();
+	}
+	mlc->istarted = 1;
+	mlc->intimeout = node->arg;
+	do_gettimeofday(&(mlc->instart));
+	mlc->icount = 15;
+	memset(mlc->ipacket, 0, 16 * sizeof(hil_packet));
+	BUG_ON(down_trylock(&mlc->isem));
+}
+
+#ifdef HIL_MLC_DEBUG
+static int doze;
+static int seidx; /* For debug */
+#endif
+
+static int hilse_donode(hil_mlc *mlc)
+{
+	const struct hilse_node *node;
+	int nextidx = 0;
+	int sched_long = 0;
+	unsigned long flags;
+
+#ifdef HIL_MLC_DEBUG
+	if (mlc->seidx && mlc->seidx != seidx &&
+	    mlc->seidx != 41 && mlc->seidx != 42 && mlc->seidx != 43) {
+		printk(KERN_DEBUG PREFIX "z%i \n {%i}", doze, mlc->seidx);
+		doze = 0;
+	}
+
+	seidx = mlc->seidx;
+#endif
+	node = hil_mlc_se + mlc->seidx;
+
+	switch (node->act) {
+		int rc;
+		hil_packet pack;
+
+	case HILSE_FUNC:
+		BUG_ON(node->object.func == NULL);
+		rc = node->object.func(mlc, node->arg);
+		nextidx = (rc > 0) ? node->ugly :
+			((rc < 0) ? node->bad : node->good);
+		if (nextidx == HILSEN_FOLLOW)
+			nextidx = rc;
+		break;
+
+	case HILSE_EXPECT_LAST:
+	case HILSE_EXPECT_DISC:
+	case HILSE_EXPECT:
+	case HILSE_IN:
+		/* Already set up from previous HILSE_OUT_* */
+		write_lock_irqsave(&mlc->lock, flags);
+		rc = mlc->in(mlc, node->arg);
+		if (rc == 2)  {
+			nextidx = HILSEN_DOZE;
+			sched_long = 1;
+			write_unlock_irqrestore(&mlc->lock, flags);
+			break;
+		}
+		if (rc == 1)
+			nextidx = node->ugly;
+		else if (rc == 0)
+			nextidx = node->good;
+		else
+			nextidx = node->bad;
+		mlc->istarted = 0;
+		write_unlock_irqrestore(&mlc->lock, flags);
+		break;
+
+	case HILSE_OUT_LAST:
+		write_lock_irqsave(&mlc->lock, flags);
+		pack = node->object.packet;
+		pack |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT);
+		goto out;
+
+	case HILSE_OUT_DISC:
+		write_lock_irqsave(&mlc->lock, flags);
+		pack = node->object.packet;
+		pack |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT);
+		goto out;
+
+	case HILSE_OUT:
+		write_lock_irqsave(&mlc->lock, flags);
+		pack = node->object.packet;
+	out:
+		if (!mlc->istarted) {
+			/* Prepare to receive input */
+			if ((node + 1)->act & HILSE_IN)
+				hilse_setup_input(mlc, node + 1);
+		}
+
+		write_unlock_irqrestore(&mlc->lock, flags);
+
+		if (down_trylock(&mlc->osem)) {
+			nextidx = HILSEN_DOZE;
+			break;
+		}
+		up(&mlc->osem);
+
+		write_lock_irqsave(&mlc->lock, flags);
+		if (!mlc->ostarted) {
+			mlc->ostarted = 1;
+			mlc->opacket = pack;
+			mlc->out(mlc);
+			nextidx = HILSEN_DOZE;
+			write_unlock_irqrestore(&mlc->lock, flags);
+			break;
+		}
+		mlc->ostarted = 0;
+		do_gettimeofday(&(mlc->instart));
+		write_unlock_irqrestore(&mlc->lock, flags);
+		nextidx = HILSEN_NEXT;
+		break;
+
+	case HILSE_CTS:
+		write_lock_irqsave(&mlc->lock, flags);
+		nextidx = mlc->cts(mlc) ? node->bad : node->good;
+		write_unlock_irqrestore(&mlc->lock, flags);
+		break;
+
+	default:
+		BUG();
+	}
+
+#ifdef HIL_MLC_DEBUG
+	if (nextidx == HILSEN_DOZE)
+		doze++;
+#endif
+
+	while (nextidx & HILSEN_SCHED) {
+		struct timeval tv;
+
+		if (!sched_long)
+			goto sched;
+
+		do_gettimeofday(&tv);
+		tv.tv_usec += USEC_PER_SEC * (tv.tv_sec - mlc->instart.tv_sec);
+		tv.tv_usec -= mlc->instart.tv_usec;
+		if (tv.tv_usec >= mlc->intimeout) goto sched;
+		tv.tv_usec = (mlc->intimeout - tv.tv_usec) * HZ / USEC_PER_SEC;
+		if (!tv.tv_usec) goto sched;
+		mod_timer(&hil_mlcs_kicker, jiffies + tv.tv_usec);
+		break;
+	sched:
+		tasklet_schedule(&hil_mlcs_tasklet);
+		break;
+	}
+
+	if (nextidx & HILSEN_DOWN)
+		mlc->seidx += nextidx & HILSEN_MASK;
+	else if (nextidx & HILSEN_UP)
+		mlc->seidx -= nextidx & HILSEN_MASK;
+	else
+		mlc->seidx = nextidx & HILSEN_MASK;
+
+	if (nextidx & HILSEN_BREAK)
+		return 1;
+
+	return 0;
+}
+
+/******************** tasklet context functions **************************/
+static void hil_mlcs_process(unsigned long unused)
+{
+	struct list_head *tmp;
+
+	read_lock(&hil_mlcs_lock);
+	list_for_each(tmp, &hil_mlcs) {
+		struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list);
+		while (hilse_donode(mlc) == 0) {
+#ifdef HIL_MLC_DEBUG
+			if (mlc->seidx != 41 &&
+			    mlc->seidx != 42 &&
+			    mlc->seidx != 43)
+				printk(KERN_DEBUG PREFIX " + ");
+#endif
+		}
+	}
+	read_unlock(&hil_mlcs_lock);
+}
+
+/************************* Keepalive timer task *********************/
+
+static void hil_mlcs_timer(unsigned long data)
+{
+	hil_mlcs_probe = 1;
+	tasklet_schedule(&hil_mlcs_tasklet);
+	/* Re-insert the periodic task. */
+	if (!timer_pending(&hil_mlcs_kicker))
+		mod_timer(&hil_mlcs_kicker, jiffies + HZ);
+}
+
+/******************** user/kernel context functions **********************/
+
+static int hil_mlc_serio_write(struct serio *serio, unsigned char c)
+{
+	struct hil_mlc_serio_map *map;
+	struct hil_mlc *mlc;
+	struct serio_driver *drv;
+	uint8_t *idx, *last;
+
+	map = serio->port_data;
+	BUG_ON(map == NULL);
+
+	mlc = map->mlc;
+	BUG_ON(mlc == NULL);
+
+	mlc->serio_opacket[map->didx] |=
+		((hil_packet)c) << (8 * (3 - mlc->serio_oidx[map->didx]));
+
+	if (mlc->serio_oidx[map->didx] >= 3) {
+		/* for now only commands */
+		if (!(mlc->serio_opacket[map->didx] & HIL_PKT_CMD))
+			return -EIO;
+		switch (mlc->serio_opacket[map->didx] & HIL_PKT_DATA_MASK) {
+		case HIL_CMD_IDD:
+			idx = mlc->di[map->didx].idd;
+			goto emu;
+		case HIL_CMD_RSC:
+			idx = mlc->di[map->didx].rsc;
+			goto emu;
+		case HIL_CMD_EXD:
+			idx = mlc->di[map->didx].exd;
+			goto emu;
+		case HIL_CMD_RNM:
+			idx = mlc->di[map->didx].rnm;
+			goto emu;
+		default:
+			break;
+		}
+		mlc->serio_oidx[map->didx] = 0;
+		mlc->serio_opacket[map->didx] = 0;
+	}
+
+	mlc->serio_oidx[map->didx]++;
+	return -EIO;
+ emu:
+	drv = serio->drv;
+	BUG_ON(drv == NULL);
+
+	last = idx + 15;
+	while ((last != idx) && (*last == 0))
+		last--;
+
+	while (idx != last) {
+		drv->interrupt(serio, 0, 0);
+		drv->interrupt(serio, HIL_ERR_INT >> 16, 0);
+		drv->interrupt(serio, 0, 0);
+		drv->interrupt(serio, *idx, 0);
+		idx++;
+	}
+	drv->interrupt(serio, 0, 0);
+	drv->interrupt(serio, HIL_ERR_INT >> 16, 0);
+	drv->interrupt(serio, HIL_PKT_CMD >> 8, 0);
+	drv->interrupt(serio, *idx, 0);
+
+	mlc->serio_oidx[map->didx] = 0;
+	mlc->serio_opacket[map->didx] = 0;
+
+	return 0;
+}
+
+static int hil_mlc_serio_open(struct serio *serio)
+{
+	struct hil_mlc_serio_map *map;
+	struct hil_mlc *mlc;
+
+	if (serio_get_drvdata(serio) != NULL)
+		return -EBUSY;
+
+	map = serio->port_data;
+	BUG_ON(map == NULL);
+
+	mlc = map->mlc;
+	BUG_ON(mlc == NULL);
+
+	return 0;
+}
+
+static void hil_mlc_serio_close(struct serio *serio)
+{
+	struct hil_mlc_serio_map *map;
+	struct hil_mlc *mlc;
+
+	map = serio->port_data;
+	BUG_ON(map == NULL);
+
+	mlc = map->mlc;
+	BUG_ON(mlc == NULL);
+
+	serio_set_drvdata(serio, NULL);
+	serio->drv = NULL;
+	/* TODO wake up interruptable */
+}
+
+static const struct serio_device_id hil_mlc_serio_id = {
+	.type = SERIO_HIL_MLC,
+	.proto = SERIO_HIL,
+	.extra = SERIO_ANY,
+	.id = SERIO_ANY,
+};
+
+int hil_mlc_register(hil_mlc *mlc)
+{
+	int i;
+	unsigned long flags;
+
+	BUG_ON(mlc == NULL);
+
+	mlc->istarted = 0;
+	mlc->ostarted = 0;
+
+	rwlock_init(&mlc->lock);
+	sema_init(&mlc->osem, 1);
+
+	sema_init(&mlc->isem, 1);
+	mlc->icount = -1;
+	mlc->imatch = 0;
+
+	mlc->opercnt = 0;
+
+	sema_init(&(mlc->csem), 0);
+
+	hil_mlc_clear_di_scratch(mlc);
+	hil_mlc_clear_di_map(mlc, 0);
+	for (i = 0; i < HIL_MLC_DEVMEM; i++) {
+		struct serio *mlc_serio;
+		hil_mlc_copy_di_scratch(mlc, i);
+		mlc_serio = kzalloc(sizeof(*mlc_serio), GFP_KERNEL);
+		mlc->serio[i] = mlc_serio;
+		if (!mlc->serio[i]) {
+			for (; i >= 0; i--)
+				kfree(mlc->serio[i]);
+			return -ENOMEM;
+		}
+		snprintf(mlc_serio->name, sizeof(mlc_serio->name)-1, "HIL_SERIO%d", i);
+		snprintf(mlc_serio->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i);
+		mlc_serio->id			= hil_mlc_serio_id;
+		mlc_serio->id.id		= i; /* HIL port no. */
+		mlc_serio->write		= hil_mlc_serio_write;
+		mlc_serio->open			= hil_mlc_serio_open;
+		mlc_serio->close		= hil_mlc_serio_close;
+		mlc_serio->port_data		= &(mlc->serio_map[i]);
+		mlc->serio_map[i].mlc		= mlc;
+		mlc->serio_map[i].didx		= i;
+		mlc->serio_map[i].di_revmap	= -1;
+		mlc->serio_opacket[i]		= 0;
+		mlc->serio_oidx[i]		= 0;
+		serio_register_port(mlc_serio);
+	}
+
+	mlc->tasklet = &hil_mlcs_tasklet;
+
+	write_lock_irqsave(&hil_mlcs_lock, flags);
+	list_add_tail(&mlc->list, &hil_mlcs);
+	mlc->seidx = HILSEN_START;
+	write_unlock_irqrestore(&hil_mlcs_lock, flags);
+
+	tasklet_schedule(&hil_mlcs_tasklet);
+	return 0;
+}
+
+int hil_mlc_unregister(hil_mlc *mlc)
+{
+	struct list_head *tmp;
+	unsigned long flags;
+	int i;
+
+	BUG_ON(mlc == NULL);
+
+	write_lock_irqsave(&hil_mlcs_lock, flags);
+	list_for_each(tmp, &hil_mlcs)
+		if (list_entry(tmp, hil_mlc, list) == mlc)
+			goto found;
+
+	/* not found in list */
+	write_unlock_irqrestore(&hil_mlcs_lock, flags);
+	tasklet_schedule(&hil_mlcs_tasklet);
+	return -ENODEV;
+
+ found:
+	list_del(tmp);
+	write_unlock_irqrestore(&hil_mlcs_lock, flags);
+
+	for (i = 0; i < HIL_MLC_DEVMEM; i++) {
+		serio_unregister_port(mlc->serio[i]);
+		mlc->serio[i] = NULL;
+	}
+
+	tasklet_schedule(&hil_mlcs_tasklet);
+	return 0;
+}
+
+/**************************** Module interface *************************/
+
+static int __init hil_mlc_init(void)
+{
+	setup_timer(&hil_mlcs_kicker, &hil_mlcs_timer, 0);
+	mod_timer(&hil_mlcs_kicker, jiffies + HZ);
+
+	tasklet_enable(&hil_mlcs_tasklet);
+
+	return 0;
+}
+
+static void __exit hil_mlc_exit(void)
+{
+	del_timer_sync(&hil_mlcs_kicker);
+	tasklet_kill(&hil_mlcs_tasklet);
+}
+
+module_init(hil_mlc_init);
+module_exit(hil_mlc_exit);
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
new file mode 100644
index 0000000..852858e
--- /dev/null
+++ b/drivers/input/serio/hp_sdc.c
@@ -0,0 +1,1135 @@
+/*
+ * HP i8042-based System Device Controller driver.
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *
+ * References:
+ * System Device Controller Microprocessor Firmware Theory of Operation
+ *      for Part Number 1820-4784 Revision B.  Dwg No. A-1820-4784-2
+ * Helge Deller's original hilkbd.c port for PA-RISC.
+ *
+ *
+ * Driver theory of operation:
+ *
+ * hp_sdc_put does all writing to the SDC.  ISR can run on a different
+ * CPU than hp_sdc_put, but only one CPU runs hp_sdc_put at a time
+ * (it cannot really benefit from SMP anyway.)  A tasket fit this perfectly.
+ *
+ * All data coming back from the SDC is sent via interrupt and can be read
+ * fully in the ISR, so there are no latency/throughput problems there.
+ * The problem is with output, due to the slow clock speed of the SDC
+ * compared to the CPU.  This should not be too horrible most of the time,
+ * but if used with HIL devices that support the multibyte transfer command,
+ * keeping outbound throughput flowing at the 6500KBps that the HIL is
+ * capable of is more than can be done at HZ=100.
+ *
+ * Busy polling for IBF clear wastes CPU cycles and bus cycles.  hp_sdc.ibf
+ * is set to 0 when the IBF flag in the status register has cleared.  ISR
+ * may do this, and may also access the parts of queued transactions related
+ * to reading data back from the SDC, but otherwise will not touch the
+ * hp_sdc state. Whenever a register is written hp_sdc.ibf is set to 1.
+ *
+ * The i8042 write index and the values in the 4-byte input buffer
+ * starting at 0x70 are kept track of in hp_sdc.wi, and .r7[], respectively,
+ * to minimize the amount of IO needed to the SDC.  However these values
+ * do not need to be locked since they are only ever accessed by hp_sdc_put.
+ *
+ * A timer task schedules the tasklet once per second just to make
+ * sure it doesn't freeze up and to allow for bad reads to time out.
+ */
+
+#include <linux/hp_sdc.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+#include <linux/hil.h>
+#include <asm/io.h>
+
+/* Machine-specific abstraction */
+
+#if defined(__hppa__)
+# include <asm/parisc-device.h>
+# define sdc_readb(p)		gsc_readb(p)
+# define sdc_writeb(v,p)	gsc_writeb((v),(p))
+#elif defined(__mc68000__)
+# include <asm/uaccess.h>
+# define sdc_readb(p)		in_8(p)
+# define sdc_writeb(v,p)	out_8((p),(v))
+#else
+# error "HIL is not supported on this platform"
+#endif
+
+#define PREFIX "HP SDC: "
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("HP i8042-based SDC Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+EXPORT_SYMBOL(hp_sdc_request_timer_irq);
+EXPORT_SYMBOL(hp_sdc_request_hil_irq);
+EXPORT_SYMBOL(hp_sdc_request_cooked_irq);
+
+EXPORT_SYMBOL(hp_sdc_release_timer_irq);
+EXPORT_SYMBOL(hp_sdc_release_hil_irq);
+EXPORT_SYMBOL(hp_sdc_release_cooked_irq);
+
+EXPORT_SYMBOL(__hp_sdc_enqueue_transaction);
+EXPORT_SYMBOL(hp_sdc_enqueue_transaction);
+EXPORT_SYMBOL(hp_sdc_dequeue_transaction);
+
+static bool hp_sdc_disabled;
+module_param_named(no_hpsdc, hp_sdc_disabled, bool, 0);
+MODULE_PARM_DESC(no_hpsdc, "Do not enable HP SDC driver.");
+
+static hp_i8042_sdc	hp_sdc;	/* All driver state is kept in here. */
+
+/*************** primitives for use in any context *********************/
+static inline uint8_t hp_sdc_status_in8(void)
+{
+	uint8_t status;
+	unsigned long flags;
+
+	write_lock_irqsave(&hp_sdc.ibf_lock, flags);
+	status = sdc_readb(hp_sdc.status_io);
+	if (!(status & HP_SDC_STATUS_IBF))
+		hp_sdc.ibf = 0;
+	write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
+
+	return status;
+}
+
+static inline uint8_t hp_sdc_data_in8(void)
+{
+	return sdc_readb(hp_sdc.data_io);
+}
+
+static inline void hp_sdc_status_out8(uint8_t val)
+{
+	unsigned long flags;
+
+	write_lock_irqsave(&hp_sdc.ibf_lock, flags);
+	hp_sdc.ibf = 1;
+	if ((val & 0xf0) == 0xe0)
+		hp_sdc.wi = 0xff;
+	sdc_writeb(val, hp_sdc.status_io);
+	write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
+}
+
+static inline void hp_sdc_data_out8(uint8_t val)
+{
+	unsigned long flags;
+
+	write_lock_irqsave(&hp_sdc.ibf_lock, flags);
+	hp_sdc.ibf = 1;
+	sdc_writeb(val, hp_sdc.data_io);
+	write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
+}
+
+/*	Care must be taken to only invoke hp_sdc_spin_ibf when
+ *	absolutely needed, or in rarely invoked subroutines.
+ *	Not only does it waste CPU cycles, it also wastes bus cycles.
+ */
+static inline void hp_sdc_spin_ibf(void)
+{
+	unsigned long flags;
+	rwlock_t *lock;
+
+	lock = &hp_sdc.ibf_lock;
+
+	read_lock_irqsave(lock, flags);
+	if (!hp_sdc.ibf) {
+		read_unlock_irqrestore(lock, flags);
+		return;
+	}
+	read_unlock(lock);
+	write_lock(lock);
+	while (sdc_readb(hp_sdc.status_io) & HP_SDC_STATUS_IBF)
+		{ }
+	hp_sdc.ibf = 0;
+	write_unlock_irqrestore(lock, flags);
+}
+
+
+/************************ Interrupt context functions ************************/
+static void hp_sdc_take(int irq, void *dev_id, uint8_t status, uint8_t data)
+{
+	hp_sdc_transaction *curr;
+
+	read_lock(&hp_sdc.rtq_lock);
+	if (hp_sdc.rcurr < 0) {
+		read_unlock(&hp_sdc.rtq_lock);
+		return;
+	}
+	curr = hp_sdc.tq[hp_sdc.rcurr];
+	read_unlock(&hp_sdc.rtq_lock);
+
+	curr->seq[curr->idx++] = status;
+	curr->seq[curr->idx++] = data;
+	hp_sdc.rqty -= 2;
+	do_gettimeofday(&hp_sdc.rtv);
+
+	if (hp_sdc.rqty <= 0) {
+		/* All data has been gathered. */
+		if (curr->seq[curr->actidx] & HP_SDC_ACT_SEMAPHORE)
+			if (curr->act.semaphore)
+				up(curr->act.semaphore);
+
+		if (curr->seq[curr->actidx] & HP_SDC_ACT_CALLBACK)
+			if (curr->act.irqhook)
+				curr->act.irqhook(irq, dev_id, status, data);
+
+		curr->actidx = curr->idx;
+		curr->idx++;
+		/* Return control of this transaction */
+		write_lock(&hp_sdc.rtq_lock);
+		hp_sdc.rcurr = -1;
+		hp_sdc.rqty = 0;
+		write_unlock(&hp_sdc.rtq_lock);
+		tasklet_schedule(&hp_sdc.task);
+	}
+}
+
+static irqreturn_t hp_sdc_isr(int irq, void *dev_id)
+{
+	uint8_t status, data;
+
+	status = hp_sdc_status_in8();
+	/* Read data unconditionally to advance i8042. */
+	data =   hp_sdc_data_in8();
+
+	/* For now we are ignoring these until we get the SDC to behave. */
+	if (((status & 0xf1) == 0x51) && data == 0x82)
+		return IRQ_HANDLED;
+
+	switch (status & HP_SDC_STATUS_IRQMASK) {
+	case 0: /* This case is not documented. */
+		break;
+
+	case HP_SDC_STATUS_USERTIMER:
+	case HP_SDC_STATUS_PERIODIC:
+	case HP_SDC_STATUS_TIMER:
+		read_lock(&hp_sdc.hook_lock);
+		if (hp_sdc.timer != NULL)
+			hp_sdc.timer(irq, dev_id, status, data);
+		read_unlock(&hp_sdc.hook_lock);
+		break;
+
+	case HP_SDC_STATUS_REG:
+		hp_sdc_take(irq, dev_id, status, data);
+		break;
+
+	case HP_SDC_STATUS_HILCMD:
+	case HP_SDC_STATUS_HILDATA:
+		read_lock(&hp_sdc.hook_lock);
+		if (hp_sdc.hil != NULL)
+			hp_sdc.hil(irq, dev_id, status, data);
+		read_unlock(&hp_sdc.hook_lock);
+		break;
+
+	case HP_SDC_STATUS_PUP:
+		read_lock(&hp_sdc.hook_lock);
+		if (hp_sdc.pup != NULL)
+			hp_sdc.pup(irq, dev_id, status, data);
+		else
+			printk(KERN_INFO PREFIX "HP SDC reports successful PUP.\n");
+		read_unlock(&hp_sdc.hook_lock);
+		break;
+
+	default:
+		read_lock(&hp_sdc.hook_lock);
+		if (hp_sdc.cooked != NULL)
+			hp_sdc.cooked(irq, dev_id, status, data);
+		read_unlock(&hp_sdc.hook_lock);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+static irqreturn_t hp_sdc_nmisr(int irq, void *dev_id)
+{
+	int status;
+
+	status = hp_sdc_status_in8();
+	printk(KERN_WARNING PREFIX "NMI !\n");
+
+#if 0
+	if (status & HP_SDC_NMISTATUS_FHS) {
+		read_lock(&hp_sdc.hook_lock);
+		if (hp_sdc.timer != NULL)
+			hp_sdc.timer(irq, dev_id, status, 0);
+		read_unlock(&hp_sdc.hook_lock);
+	} else {
+		/* TODO: pass this on to the HIL handler, or do SAK here? */
+		printk(KERN_WARNING PREFIX "HIL NMI\n");
+	}
+#endif
+
+	return IRQ_HANDLED;
+}
+
+
+/***************** Kernel (tasklet) context functions ****************/
+
+unsigned long hp_sdc_put(void);
+
+static void hp_sdc_tasklet(unsigned long foo)
+{
+	write_lock_irq(&hp_sdc.rtq_lock);
+
+	if (hp_sdc.rcurr >= 0) {
+		struct timeval tv;
+
+		do_gettimeofday(&tv);
+		if (tv.tv_sec > hp_sdc.rtv.tv_sec)
+			tv.tv_usec += USEC_PER_SEC;
+
+		if (tv.tv_usec - hp_sdc.rtv.tv_usec > HP_SDC_MAX_REG_DELAY) {
+			hp_sdc_transaction *curr;
+			uint8_t tmp;
+
+			curr = hp_sdc.tq[hp_sdc.rcurr];
+			/* If this turns out to be a normal failure mode
+			 * we'll need to figure out a way to communicate
+			 * it back to the application. and be less verbose.
+			 */
+			printk(KERN_WARNING PREFIX "read timeout (%ius)!\n",
+			       (int)(tv.tv_usec - hp_sdc.rtv.tv_usec));
+			curr->idx += hp_sdc.rqty;
+			hp_sdc.rqty = 0;
+			tmp = curr->seq[curr->actidx];
+			curr->seq[curr->actidx] |= HP_SDC_ACT_DEAD;
+			if (tmp & HP_SDC_ACT_SEMAPHORE)
+				if (curr->act.semaphore)
+					up(curr->act.semaphore);
+
+			if (tmp & HP_SDC_ACT_CALLBACK) {
+				/* Note this means that irqhooks may be called
+				 * in tasklet/bh context.
+				 */
+				if (curr->act.irqhook)
+					curr->act.irqhook(0, NULL, 0, 0);
+			}
+
+			curr->actidx = curr->idx;
+			curr->idx++;
+			hp_sdc.rcurr = -1;
+		}
+	}
+	write_unlock_irq(&hp_sdc.rtq_lock);
+	hp_sdc_put();
+}
+
+unsigned long hp_sdc_put(void)
+{
+	hp_sdc_transaction *curr;
+	uint8_t act;
+	int idx, curridx;
+
+	int limit = 0;
+
+	write_lock(&hp_sdc.lock);
+
+	/* If i8042 buffers are full, we cannot do anything that
+	   requires output, so we skip to the administrativa. */
+	if (hp_sdc.ibf) {
+		hp_sdc_status_in8();
+		if (hp_sdc.ibf)
+			goto finish;
+	}
+
+ anew:
+	/* See if we are in the middle of a sequence. */
+	if (hp_sdc.wcurr < 0)
+		hp_sdc.wcurr = 0;
+	read_lock_irq(&hp_sdc.rtq_lock);
+	if (hp_sdc.rcurr == hp_sdc.wcurr)
+		hp_sdc.wcurr++;
+	read_unlock_irq(&hp_sdc.rtq_lock);
+	if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN)
+		hp_sdc.wcurr = 0;
+	curridx = hp_sdc.wcurr;
+
+	if (hp_sdc.tq[curridx] != NULL)
+		goto start;
+
+	while (++curridx != hp_sdc.wcurr) {
+		if (curridx >= HP_SDC_QUEUE_LEN) {
+			curridx = -1; /* Wrap to top */
+			continue;
+		}
+		read_lock_irq(&hp_sdc.rtq_lock);
+		if (hp_sdc.rcurr == curridx) {
+			read_unlock_irq(&hp_sdc.rtq_lock);
+			continue;
+		}
+		read_unlock_irq(&hp_sdc.rtq_lock);
+		if (hp_sdc.tq[curridx] != NULL)
+			break; /* Found one. */
+	}
+	if (curridx == hp_sdc.wcurr) { /* There's nothing queued to do. */
+		curridx = -1;
+	}
+	hp_sdc.wcurr = curridx;
+
+ start:
+
+	/* Check to see if the interrupt mask needs to be set. */
+	if (hp_sdc.set_im) {
+		hp_sdc_status_out8(hp_sdc.im | HP_SDC_CMD_SET_IM);
+		hp_sdc.set_im = 0;
+		goto finish;
+	}
+
+	if (hp_sdc.wcurr == -1)
+		goto done;
+
+	curr = hp_sdc.tq[curridx];
+	idx = curr->actidx;
+
+	if (curr->actidx >= curr->endidx) {
+		hp_sdc.tq[curridx] = NULL;
+		/* Interleave outbound data between the transactions. */
+		hp_sdc.wcurr++;
+		if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN)
+			hp_sdc.wcurr = 0;
+		goto finish;
+	}
+
+	act = curr->seq[idx];
+	idx++;
+
+	if (curr->idx >= curr->endidx) {
+		if (act & HP_SDC_ACT_DEALLOC)
+			kfree(curr);
+		hp_sdc.tq[curridx] = NULL;
+		/* Interleave outbound data between the transactions. */
+		hp_sdc.wcurr++;
+		if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN)
+			hp_sdc.wcurr = 0;
+		goto finish;
+	}
+
+	while (act & HP_SDC_ACT_PRECMD) {
+		if (curr->idx != idx) {
+			idx++;
+			act &= ~HP_SDC_ACT_PRECMD;
+			break;
+		}
+		hp_sdc_status_out8(curr->seq[idx]);
+		curr->idx++;
+		/* act finished? */
+		if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_PRECMD)
+			goto actdone;
+		/* skip quantity field if data-out sequence follows. */
+		if (act & HP_SDC_ACT_DATAOUT)
+			curr->idx++;
+		goto finish;
+	}
+	if (act & HP_SDC_ACT_DATAOUT) {
+		int qty;
+
+		qty = curr->seq[idx];
+		idx++;
+		if (curr->idx - idx < qty) {
+			hp_sdc_data_out8(curr->seq[curr->idx]);
+			curr->idx++;
+			/* act finished? */
+			if (curr->idx - idx >= qty &&
+			    (act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAOUT)
+				goto actdone;
+			goto finish;
+		}
+		idx += qty;
+		act &= ~HP_SDC_ACT_DATAOUT;
+	} else
+	    while (act & HP_SDC_ACT_DATAREG) {
+		int mask;
+		uint8_t w7[4];
+
+		mask = curr->seq[idx];
+		if (idx != curr->idx) {
+			idx++;
+			idx += !!(mask & 1);
+			idx += !!(mask & 2);
+			idx += !!(mask & 4);
+			idx += !!(mask & 8);
+			act &= ~HP_SDC_ACT_DATAREG;
+			break;
+		}
+
+		w7[0] = (mask & 1) ? curr->seq[++idx] : hp_sdc.r7[0];
+		w7[1] = (mask & 2) ? curr->seq[++idx] : hp_sdc.r7[1];
+		w7[2] = (mask & 4) ? curr->seq[++idx] : hp_sdc.r7[2];
+		w7[3] = (mask & 8) ? curr->seq[++idx] : hp_sdc.r7[3];
+
+		if (hp_sdc.wi > 0x73 || hp_sdc.wi < 0x70 ||
+		    w7[hp_sdc.wi - 0x70] == hp_sdc.r7[hp_sdc.wi - 0x70]) {
+			int i = 0;
+
+			/* Need to point the write index register */
+			while (i < 4 && w7[i] == hp_sdc.r7[i])
+				i++;
+
+			if (i < 4) {
+				hp_sdc_status_out8(HP_SDC_CMD_SET_D0 + i);
+				hp_sdc.wi = 0x70 + i;
+				goto finish;
+			}
+
+			idx++;
+			if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAREG)
+				goto actdone;
+
+			curr->idx = idx;
+			act &= ~HP_SDC_ACT_DATAREG;
+			break;
+		}
+
+		hp_sdc_data_out8(w7[hp_sdc.wi - 0x70]);
+		hp_sdc.r7[hp_sdc.wi - 0x70] = w7[hp_sdc.wi - 0x70];
+		hp_sdc.wi++; /* write index register autoincrements */
+		{
+			int i = 0;
+
+			while ((i < 4) && w7[i] == hp_sdc.r7[i])
+				i++;
+			if (i >= 4) {
+				curr->idx = idx + 1;
+				if ((act & HP_SDC_ACT_DURING) ==
+				    HP_SDC_ACT_DATAREG)
+					goto actdone;
+			}
+		}
+		goto finish;
+	}
+	/* We don't go any further in the command if there is a pending read,
+	   because we don't want interleaved results. */
+	read_lock_irq(&hp_sdc.rtq_lock);
+	if (hp_sdc.rcurr >= 0) {
+		read_unlock_irq(&hp_sdc.rtq_lock);
+		goto finish;
+	}
+	read_unlock_irq(&hp_sdc.rtq_lock);
+
+
+	if (act & HP_SDC_ACT_POSTCMD) {
+		uint8_t postcmd;
+
+		/* curr->idx should == idx at this point. */
+		postcmd = curr->seq[idx];
+		curr->idx++;
+		if (act & HP_SDC_ACT_DATAIN) {
+
+			/* Start a new read */
+			hp_sdc.rqty = curr->seq[curr->idx];
+			do_gettimeofday(&hp_sdc.rtv);
+			curr->idx++;
+			/* Still need to lock here in case of spurious irq. */
+			write_lock_irq(&hp_sdc.rtq_lock);
+			hp_sdc.rcurr = curridx;
+			write_unlock_irq(&hp_sdc.rtq_lock);
+			hp_sdc_status_out8(postcmd);
+			goto finish;
+		}
+		hp_sdc_status_out8(postcmd);
+		goto actdone;
+	}
+
+ actdone:
+	if (act & HP_SDC_ACT_SEMAPHORE)
+		up(curr->act.semaphore);
+	else if (act & HP_SDC_ACT_CALLBACK)
+		curr->act.irqhook(0,NULL,0,0);
+
+	if (curr->idx >= curr->endidx) { /* This transaction is over. */
+		if (act & HP_SDC_ACT_DEALLOC)
+			kfree(curr);
+		hp_sdc.tq[curridx] = NULL;
+	} else {
+		curr->actidx = idx + 1;
+		curr->idx = idx + 2;
+	}
+	/* Interleave outbound data between the transactions. */
+	hp_sdc.wcurr++;
+	if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN)
+		hp_sdc.wcurr = 0;
+
+ finish:
+	/* If by some quirk IBF has cleared and our ISR has run to
+	   see that that has happened, do it all again. */
+	if (!hp_sdc.ibf && limit++ < 20)
+		goto anew;
+
+ done:
+	if (hp_sdc.wcurr >= 0)
+		tasklet_schedule(&hp_sdc.task);
+	write_unlock(&hp_sdc.lock);
+
+	return 0;
+}
+
+/******* Functions called in either user or kernel context ****/
+int __hp_sdc_enqueue_transaction(hp_sdc_transaction *this)
+{
+	int i;
+
+	if (this == NULL) {
+		BUG();
+		return -EINVAL;
+	}
+
+	/* Can't have same transaction on queue twice */
+	for (i = 0; i < HP_SDC_QUEUE_LEN; i++)
+		if (hp_sdc.tq[i] == this)
+			goto fail;
+
+	this->actidx = 0;
+	this->idx = 1;
+
+	/* Search for empty slot */
+	for (i = 0; i < HP_SDC_QUEUE_LEN; i++)
+		if (hp_sdc.tq[i] == NULL) {
+			hp_sdc.tq[i] = this;
+			tasklet_schedule(&hp_sdc.task);
+			return 0;
+		}
+
+	printk(KERN_WARNING PREFIX "No free slot to add transaction.\n");
+	return -EBUSY;
+
+ fail:
+	printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n");
+	return -EINVAL;
+}
+
+int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) {
+	unsigned long flags;
+	int ret;
+
+	write_lock_irqsave(&hp_sdc.lock, flags);
+	ret = __hp_sdc_enqueue_transaction(this);
+	write_unlock_irqrestore(&hp_sdc.lock,flags);
+
+	return ret;
+}
+
+int hp_sdc_dequeue_transaction(hp_sdc_transaction *this)
+{
+	unsigned long flags;
+	int i;
+
+	write_lock_irqsave(&hp_sdc.lock, flags);
+
+	/* TODO: don't remove it if it's not done. */
+
+	for (i = 0; i < HP_SDC_QUEUE_LEN; i++)
+		if (hp_sdc.tq[i] == this)
+			hp_sdc.tq[i] = NULL;
+
+	write_unlock_irqrestore(&hp_sdc.lock, flags);
+	return 0;
+}
+
+
+
+/********************** User context functions **************************/
+int hp_sdc_request_timer_irq(hp_sdc_irqhook *callback)
+{
+	if (callback == NULL || hp_sdc.dev == NULL)
+		return -EINVAL;
+
+	write_lock_irq(&hp_sdc.hook_lock);
+	if (hp_sdc.timer != NULL) {
+		write_unlock_irq(&hp_sdc.hook_lock);
+		return -EBUSY;
+	}
+
+	hp_sdc.timer = callback;
+	/* Enable interrupts from the timers */
+	hp_sdc.im &= ~HP_SDC_IM_FH;
+        hp_sdc.im &= ~HP_SDC_IM_PT;
+	hp_sdc.im &= ~HP_SDC_IM_TIMERS;
+	hp_sdc.set_im = 1;
+	write_unlock_irq(&hp_sdc.hook_lock);
+
+	tasklet_schedule(&hp_sdc.task);
+
+	return 0;
+}
+
+int hp_sdc_request_hil_irq(hp_sdc_irqhook *callback)
+{
+	if (callback == NULL || hp_sdc.dev == NULL)
+		return -EINVAL;
+
+	write_lock_irq(&hp_sdc.hook_lock);
+	if (hp_sdc.hil != NULL) {
+		write_unlock_irq(&hp_sdc.hook_lock);
+		return -EBUSY;
+	}
+
+	hp_sdc.hil = callback;
+	hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET);
+	hp_sdc.set_im = 1;
+	write_unlock_irq(&hp_sdc.hook_lock);
+
+	tasklet_schedule(&hp_sdc.task);
+
+	return 0;
+}
+
+int hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback)
+{
+	if (callback == NULL || hp_sdc.dev == NULL)
+		return -EINVAL;
+
+	write_lock_irq(&hp_sdc.hook_lock);
+	if (hp_sdc.cooked != NULL) {
+		write_unlock_irq(&hp_sdc.hook_lock);
+		return -EBUSY;
+	}
+
+	/* Enable interrupts from the HIL MLC */
+	hp_sdc.cooked = callback;
+	hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET);
+	hp_sdc.set_im = 1;
+	write_unlock_irq(&hp_sdc.hook_lock);
+
+	tasklet_schedule(&hp_sdc.task);
+
+	return 0;
+}
+
+int hp_sdc_release_timer_irq(hp_sdc_irqhook *callback)
+{
+	write_lock_irq(&hp_sdc.hook_lock);
+	if ((callback != hp_sdc.timer) ||
+	    (hp_sdc.timer == NULL)) {
+		write_unlock_irq(&hp_sdc.hook_lock);
+		return -EINVAL;
+	}
+
+	/* Disable interrupts from the timers */
+	hp_sdc.timer = NULL;
+	hp_sdc.im |= HP_SDC_IM_TIMERS;
+	hp_sdc.im |= HP_SDC_IM_FH;
+	hp_sdc.im |= HP_SDC_IM_PT;
+	hp_sdc.set_im = 1;
+	write_unlock_irq(&hp_sdc.hook_lock);
+	tasklet_schedule(&hp_sdc.task);
+
+	return 0;
+}
+
+int hp_sdc_release_hil_irq(hp_sdc_irqhook *callback)
+{
+	write_lock_irq(&hp_sdc.hook_lock);
+	if ((callback != hp_sdc.hil) ||
+	    (hp_sdc.hil == NULL)) {
+		write_unlock_irq(&hp_sdc.hook_lock);
+		return -EINVAL;
+	}
+
+	hp_sdc.hil = NULL;
+	/* Disable interrupts from HIL only if there is no cooked driver. */
+	if(hp_sdc.cooked == NULL) {
+		hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET);
+		hp_sdc.set_im = 1;
+	}
+	write_unlock_irq(&hp_sdc.hook_lock);
+	tasklet_schedule(&hp_sdc.task);
+
+	return 0;
+}
+
+int hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback)
+{
+	write_lock_irq(&hp_sdc.hook_lock);
+	if ((callback != hp_sdc.cooked) ||
+	    (hp_sdc.cooked == NULL)) {
+		write_unlock_irq(&hp_sdc.hook_lock);
+		return -EINVAL;
+	}
+
+	hp_sdc.cooked = NULL;
+	/* Disable interrupts from HIL only if there is no raw HIL driver. */
+	if(hp_sdc.hil == NULL) {
+		hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET);
+		hp_sdc.set_im = 1;
+	}
+	write_unlock_irq(&hp_sdc.hook_lock);
+	tasklet_schedule(&hp_sdc.task);
+
+	return 0;
+}
+
+/************************* Keepalive timer task *********************/
+
+static void hp_sdc_kicker(unsigned long data)
+{
+	tasklet_schedule(&hp_sdc.task);
+	/* Re-insert the periodic task. */
+	mod_timer(&hp_sdc.kicker, jiffies + HZ);
+}
+
+/************************** Module Initialization ***************************/
+
+#if defined(__hppa__)
+
+static const struct parisc_device_id hp_sdc_tbl[] = {
+	{
+		.hw_type =	HPHW_FIO,
+		.hversion_rev =	HVERSION_REV_ANY_ID,
+		.hversion =	HVERSION_ANY_ID,
+		.sversion =	0x73,
+	 },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, hp_sdc_tbl);
+
+static int __init hp_sdc_init_hppa(struct parisc_device *d);
+static struct delayed_work moduleloader_work;
+
+static struct parisc_driver hp_sdc_driver = {
+	.name =		"hp_sdc",
+	.id_table =	hp_sdc_tbl,
+	.probe =	hp_sdc_init_hppa,
+};
+
+#endif /* __hppa__ */
+
+static int __init hp_sdc_init(void)
+{
+	char *errstr;
+	hp_sdc_transaction t_sync;
+	uint8_t ts_sync[6];
+	struct semaphore s_sync;
+
+	rwlock_init(&hp_sdc.lock);
+	rwlock_init(&hp_sdc.ibf_lock);
+	rwlock_init(&hp_sdc.rtq_lock);
+	rwlock_init(&hp_sdc.hook_lock);
+
+	hp_sdc.timer		= NULL;
+	hp_sdc.hil		= NULL;
+	hp_sdc.pup		= NULL;
+	hp_sdc.cooked		= NULL;
+	hp_sdc.im		= HP_SDC_IM_MASK;  /* Mask maskable irqs */
+	hp_sdc.set_im		= 1;
+	hp_sdc.wi		= 0xff;
+	hp_sdc.r7[0]		= 0xff;
+	hp_sdc.r7[1]		= 0xff;
+	hp_sdc.r7[2]		= 0xff;
+	hp_sdc.r7[3]		= 0xff;
+	hp_sdc.ibf		= 1;
+
+	memset(&hp_sdc.tq, 0, sizeof(hp_sdc.tq));
+
+	hp_sdc.wcurr		= -1;
+        hp_sdc.rcurr		= -1;
+	hp_sdc.rqty		= 0;
+
+	hp_sdc.dev_err = -ENODEV;
+
+	errstr = "IO not found for";
+	if (!hp_sdc.base_io)
+		goto err0;
+
+	errstr = "IRQ not found for";
+	if (!hp_sdc.irq)
+		goto err0;
+
+	hp_sdc.dev_err = -EBUSY;
+
+#if defined(__hppa__)
+	errstr = "IO not available for";
+        if (request_region(hp_sdc.data_io, 2, hp_sdc_driver.name))
+		goto err0;
+#endif
+
+	errstr = "IRQ not available for";
+	if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED,
+			"HP SDC", &hp_sdc))
+		goto err1;
+
+	errstr = "NMI not available for";
+	if (request_irq(hp_sdc.nmi, &hp_sdc_nmisr, IRQF_SHARED,
+			"HP SDC NMI", &hp_sdc))
+		goto err2;
+
+	printk(KERN_INFO PREFIX "HP SDC at 0x%p, IRQ %d (NMI IRQ %d)\n",
+	       (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi);
+
+	hp_sdc_status_in8();
+	hp_sdc_data_in8();
+
+	tasklet_init(&hp_sdc.task, hp_sdc_tasklet, 0);
+
+	/* Sync the output buffer registers, thus scheduling hp_sdc_tasklet. */
+	t_sync.actidx	= 0;
+	t_sync.idx	= 1;
+	t_sync.endidx	= 6;
+	t_sync.seq	= ts_sync;
+	ts_sync[0]	= HP_SDC_ACT_DATAREG | HP_SDC_ACT_SEMAPHORE;
+	ts_sync[1]	= 0x0f;
+	ts_sync[2] = ts_sync[3]	= ts_sync[4] = ts_sync[5] = 0;
+	t_sync.act.semaphore = &s_sync;
+	sema_init(&s_sync, 0);
+	hp_sdc_enqueue_transaction(&t_sync);
+	down(&s_sync); /* Wait for t_sync to complete */
+
+	/* Create the keepalive task */
+	init_timer(&hp_sdc.kicker);
+	hp_sdc.kicker.expires = jiffies + HZ;
+	hp_sdc.kicker.function = &hp_sdc_kicker;
+	add_timer(&hp_sdc.kicker);
+
+	hp_sdc.dev_err = 0;
+	return 0;
+ err2:
+	free_irq(hp_sdc.irq, &hp_sdc);
+ err1:
+	release_region(hp_sdc.data_io, 2);
+ err0:
+	printk(KERN_WARNING PREFIX ": %s SDC IO=0x%p IRQ=0x%x NMI=0x%x\n",
+		errstr, (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi);
+	hp_sdc.dev = NULL;
+
+	return hp_sdc.dev_err;
+}
+
+#if defined(__hppa__)
+
+static void request_module_delayed(struct work_struct *work)
+{
+	request_module("hp_sdc_mlc");
+}
+
+static int __init hp_sdc_init_hppa(struct parisc_device *d)
+{
+	int ret;
+
+	if (!d)
+		return 1;
+	if (hp_sdc.dev != NULL)
+		return 1;	/* We only expect one SDC */
+
+	hp_sdc.dev		= d;
+	hp_sdc.irq		= d->irq;
+	hp_sdc.nmi		= d->aux_irq;
+	hp_sdc.base_io		= d->hpa.start;
+	hp_sdc.data_io		= d->hpa.start + 0x800;
+	hp_sdc.status_io	= d->hpa.start + 0x801;
+
+	INIT_DELAYED_WORK(&moduleloader_work, request_module_delayed);
+
+	ret = hp_sdc_init();
+	/* after successful initialization give SDC some time to settle
+	 * and then load the hp_sdc_mlc upper layer driver */
+	if (!ret)
+		schedule_delayed_work(&moduleloader_work,
+			msecs_to_jiffies(2000));
+
+	return ret;
+}
+
+#endif /* __hppa__ */
+
+static void hp_sdc_exit(void)
+{
+	/* do nothing if we don't have a SDC */
+	if (!hp_sdc.dev)
+		return;
+
+	write_lock_irq(&hp_sdc.lock);
+
+	/* Turn off all maskable "sub-function" irq's. */
+	hp_sdc_spin_ibf();
+	sdc_writeb(HP_SDC_CMD_SET_IM | HP_SDC_IM_MASK, hp_sdc.status_io);
+
+	/* Wait until we know this has been processed by the i8042 */
+	hp_sdc_spin_ibf();
+
+	free_irq(hp_sdc.nmi, &hp_sdc);
+	free_irq(hp_sdc.irq, &hp_sdc);
+	write_unlock_irq(&hp_sdc.lock);
+
+	del_timer_sync(&hp_sdc.kicker);
+
+	tasklet_kill(&hp_sdc.task);
+
+#if defined(__hppa__)
+	cancel_delayed_work_sync(&moduleloader_work);
+	if (unregister_parisc_driver(&hp_sdc_driver))
+		printk(KERN_WARNING PREFIX "Error unregistering HP SDC");
+#endif
+}
+
+static int __init hp_sdc_register(void)
+{
+	hp_sdc_transaction tq_init;
+	uint8_t tq_init_seq[5];
+	struct semaphore tq_init_sem;
+#if defined(__mc68000__)
+	mm_segment_t fs;
+	unsigned char i;
+#endif
+
+	if (hp_sdc_disabled) {
+		printk(KERN_WARNING PREFIX "HP SDC driver disabled by no_hpsdc=1.\n");
+		return -ENODEV;
+	}
+
+	hp_sdc.dev = NULL;
+	hp_sdc.dev_err = 0;
+#if defined(__hppa__)
+	if (register_parisc_driver(&hp_sdc_driver)) {
+		printk(KERN_WARNING PREFIX "Error registering SDC with system bus tree.\n");
+		return -ENODEV;
+	}
+#elif defined(__mc68000__)
+	if (!MACH_IS_HP300)
+	    return -ENODEV;
+
+	hp_sdc.irq	 = 1;
+	hp_sdc.nmi	 = 7;
+	hp_sdc.base_io	 = (unsigned long) 0xf0428000;
+	hp_sdc.data_io	 = (unsigned long) hp_sdc.base_io + 1;
+	hp_sdc.status_io = (unsigned long) hp_sdc.base_io + 3;
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	if (!get_user(i, (unsigned char *)hp_sdc.data_io))
+		hp_sdc.dev = (void *)1;
+	set_fs(fs);
+	hp_sdc.dev_err   = hp_sdc_init();
+#endif
+	if (hp_sdc.dev == NULL) {
+		printk(KERN_WARNING PREFIX "No SDC found.\n");
+		return hp_sdc.dev_err;
+	}
+
+	sema_init(&tq_init_sem, 0);
+
+	tq_init.actidx		= 0;
+	tq_init.idx		= 1;
+	tq_init.endidx		= 5;
+	tq_init.seq		= tq_init_seq;
+	tq_init.act.semaphore	= &tq_init_sem;
+
+	tq_init_seq[0] =
+		HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE;
+	tq_init_seq[1] = HP_SDC_CMD_READ_KCC;
+	tq_init_seq[2] = 1;
+	tq_init_seq[3] = 0;
+	tq_init_seq[4] = 0;
+
+	hp_sdc_enqueue_transaction(&tq_init);
+
+	down(&tq_init_sem);
+	up(&tq_init_sem);
+
+	if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) {
+		printk(KERN_WARNING PREFIX "Error reading config byte.\n");
+		hp_sdc_exit();
+		return -ENODEV;
+	}
+	hp_sdc.r11 = tq_init_seq[4];
+	if (hp_sdc.r11 & HP_SDC_CFG_NEW) {
+		const char *str;
+		printk(KERN_INFO PREFIX "New style SDC\n");
+		tq_init_seq[1] = HP_SDC_CMD_READ_XTD;
+		tq_init.actidx		= 0;
+		tq_init.idx		= 1;
+		down(&tq_init_sem);
+		hp_sdc_enqueue_transaction(&tq_init);
+		down(&tq_init_sem);
+		up(&tq_init_sem);
+		if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) {
+			printk(KERN_WARNING PREFIX "Error reading extended config byte.\n");
+			return -ENODEV;
+		}
+		hp_sdc.r7e = tq_init_seq[4];
+		HP_SDC_XTD_REV_STRINGS(hp_sdc.r7e & HP_SDC_XTD_REV, str)
+		printk(KERN_INFO PREFIX "Revision: %s\n", str);
+		if (hp_sdc.r7e & HP_SDC_XTD_BEEPER)
+			printk(KERN_INFO PREFIX "TI SN76494 beeper present\n");
+		if (hp_sdc.r7e & HP_SDC_XTD_BBRTC)
+			printk(KERN_INFO PREFIX "OKI MSM-58321 BBRTC present\n");
+		printk(KERN_INFO PREFIX "Spunking the self test register to force PUP "
+		       "on next firmware reset.\n");
+		tq_init_seq[0] = HP_SDC_ACT_PRECMD |
+			HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE;
+		tq_init_seq[1] = HP_SDC_CMD_SET_STR;
+		tq_init_seq[2] = 1;
+		tq_init_seq[3] = 0;
+		tq_init.actidx		= 0;
+		tq_init.idx		= 1;
+		tq_init.endidx		= 4;
+		down(&tq_init_sem);
+		hp_sdc_enqueue_transaction(&tq_init);
+		down(&tq_init_sem);
+		up(&tq_init_sem);
+	} else
+		printk(KERN_INFO PREFIX "Old style SDC (1820-%s).\n",
+		       (hp_sdc.r11 & HP_SDC_CFG_REV) ? "3300" : "2564/3087");
+
+        return 0;
+}
+
+module_init(hp_sdc_register);
+module_exit(hp_sdc_exit);
+
+/* Timing notes:  These measurements taken on my 64MHz 7100-LC (715/64)
+ *                                              cycles cycles-adj    time
+ * between two consecutive mfctl(16)'s:              4        n/a    63ns
+ * hp_sdc_spin_ibf when idle:                      119        115   1.7us
+ * gsc_writeb status register:                      83         79   1.2us
+ * IBF to clear after sending SET_IM:             6204       6006    93us
+ * IBF to clear after sending LOAD_RT:            4467       4352    68us
+ * IBF to clear after sending two LOAD_RTs:      18974      18859   295us
+ * READ_T1, read status/data, IRQ, call handler: 35564        n/a   556us
+ * cmd to ~IBF READ_T1 2nd time right after:   5158403        n/a    81ms
+ * between IRQ received and ~IBF for above:    2578877        n/a    40ms
+ *
+ * Performance stats after a run of this module configuring HIL and
+ * receiving a few mouse events:
+ *
+ * status in8  282508 cycles 7128 calls
+ * status out8   8404 cycles  341 calls
+ * data out8     1734 cycles   78 calls
+ * isr         174324 cycles  617 calls (includes take)
+ * take          1241 cycles    2 calls
+ * put        1411504 cycles 6937 calls
+ * task       1655209 cycles 6937 calls (includes put)
+ *
+ */
diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c
new file mode 100644
index 0000000..d50f067
--- /dev/null
+++ b/drivers/input/serio/hp_sdc_mlc.c
@@ -0,0 +1,358 @@
+/*
+ * Access to HP-HIL MLC through HP System Device Controller.
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *
+ * References:
+ * HP-HIL Technical Reference Manual.  Hewlett Packard Product No. 45918A
+ * System Device Controller Microprocessor Firmware Theory of Operation
+ *      for Part Number 1820-4784 Revision B.  Dwg No. A-1820-4784-2
+ *
+ */
+
+#include <linux/hil_mlc.h>
+#include <linux/hp_sdc.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/semaphore.h>
+
+#define PREFIX "HP SDC MLC: "
+
+static hil_mlc hp_sdc_mlc;
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("Glue for onboard HIL MLC in HP-PARISC machines");
+MODULE_LICENSE("Dual BSD/GPL");
+
+static struct hp_sdc_mlc_priv_s {
+	int emtestmode;
+	hp_sdc_transaction trans;
+	u8 tseq[16];
+	int got5x;
+} hp_sdc_mlc_priv;
+
+/************************* Interrupt context ******************************/
+static void hp_sdc_mlc_isr (int irq, void *dev_id,
+			    uint8_t status, uint8_t data)
+{
+	int idx;
+	hil_mlc *mlc = &hp_sdc_mlc;
+
+	write_lock(&mlc->lock);
+	if (mlc->icount < 0) {
+		printk(KERN_WARNING PREFIX "HIL Overflow!\n");
+		up(&mlc->isem);
+		goto out;
+	}
+	idx = 15 - mlc->icount;
+	if ((status & HP_SDC_STATUS_IRQMASK) == HP_SDC_STATUS_HILDATA) {
+		mlc->ipacket[idx] |= data | HIL_ERR_INT;
+		mlc->icount--;
+		if (hp_sdc_mlc_priv.got5x || !idx)
+			goto check;
+		if ((mlc->ipacket[idx - 1] & HIL_PKT_ADDR_MASK) !=
+		    (mlc->ipacket[idx] & HIL_PKT_ADDR_MASK)) {
+			mlc->ipacket[idx] &= ~HIL_PKT_ADDR_MASK;
+			mlc->ipacket[idx] |= (mlc->ipacket[idx - 1]
+						& HIL_PKT_ADDR_MASK);
+		}
+		goto check;
+	}
+	/* We know status is 5X */
+	if (data & HP_SDC_HIL_ISERR)
+		goto err;
+	mlc->ipacket[idx] =
+		(data & HP_SDC_HIL_R1MASK) << HIL_PKT_ADDR_SHIFT;
+	hp_sdc_mlc_priv.got5x = 1;
+	goto out;
+
+ check:
+	hp_sdc_mlc_priv.got5x = 0;
+	if (mlc->imatch == 0)
+		goto done;
+	if ((mlc->imatch == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
+	    && (mlc->ipacket[idx] == (mlc->imatch | idx)))
+		goto done;
+	if (mlc->ipacket[idx] == mlc->imatch)
+		goto done;
+	goto out;
+
+ err:
+	printk(KERN_DEBUG PREFIX "err code %x\n", data);
+
+	switch (data) {
+	case HP_SDC_HIL_RC_DONE:
+		printk(KERN_WARNING PREFIX "Bastard SDC reconfigured loop!\n");
+		break;
+
+	case HP_SDC_HIL_ERR:
+		mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_PERR |
+					HIL_ERR_FERR | HIL_ERR_FOF;
+		break;
+
+	case HP_SDC_HIL_TO:
+		mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_LERR;
+		break;
+
+	case HP_SDC_HIL_RC:
+		printk(KERN_WARNING PREFIX "Bastard SDC decided to reconfigure loop!\n");
+		break;
+
+	default:
+		printk(KERN_WARNING PREFIX "Unknown HIL Error status (%x)!\n", data);
+		break;
+	}
+
+	/* No more data will be coming due to an error. */
+ done:
+	tasklet_schedule(mlc->tasklet);
+	up(&mlc->isem);
+ out:
+	write_unlock(&mlc->lock);
+}
+
+
+/******************** Tasklet or userspace context functions ****************/
+
+static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout)
+{
+	struct hp_sdc_mlc_priv_s *priv;
+	int rc = 2;
+
+	priv = mlc->priv;
+
+	/* Try to down the semaphore */
+	if (down_trylock(&mlc->isem)) {
+		struct timeval tv;
+		if (priv->emtestmode) {
+			mlc->ipacket[0] =
+				HIL_ERR_INT | (mlc->opacket &
+					       (HIL_PKT_CMD |
+						HIL_PKT_ADDR_MASK |
+						HIL_PKT_DATA_MASK));
+			mlc->icount = 14;
+			/* printk(KERN_DEBUG PREFIX ">[%x]\n", mlc->ipacket[0]); */
+			goto wasup;
+		}
+		do_gettimeofday(&tv);
+		tv.tv_usec += USEC_PER_SEC * (tv.tv_sec - mlc->instart.tv_sec);
+		if (tv.tv_usec - mlc->instart.tv_usec > mlc->intimeout) {
+			/*	printk("!%i %i",
+				tv.tv_usec - mlc->instart.tv_usec,
+				mlc->intimeout);
+			 */
+			rc = 1;
+			up(&mlc->isem);
+		}
+		goto done;
+	}
+ wasup:
+	up(&mlc->isem);
+	rc = 0;
+ done:
+	return rc;
+}
+
+static int hp_sdc_mlc_cts(hil_mlc *mlc)
+{
+	struct hp_sdc_mlc_priv_s *priv;
+
+	priv = mlc->priv;
+
+	/* Try to down the semaphores -- they should be up. */
+	BUG_ON(down_trylock(&mlc->isem));
+	BUG_ON(down_trylock(&mlc->osem));
+
+	up(&mlc->isem);
+	up(&mlc->osem);
+
+	if (down_trylock(&mlc->csem)) {
+		if (priv->trans.act.semaphore != &mlc->csem)
+			goto poll;
+		else
+			goto busy;
+	}
+
+	if (!(priv->tseq[4] & HP_SDC_USE_LOOP))
+		goto done;
+
+ poll:
+	priv->trans.act.semaphore = &mlc->csem;
+	priv->trans.actidx = 0;
+	priv->trans.idx = 1;
+	priv->trans.endidx = 5;
+	priv->tseq[0] =
+		HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE;
+	priv->tseq[1] = HP_SDC_CMD_READ_USE;
+	priv->tseq[2] = 1;
+	priv->tseq[3] = 0;
+	priv->tseq[4] = 0;
+	__hp_sdc_enqueue_transaction(&priv->trans);
+ busy:
+	return 1;
+ done:
+	priv->trans.act.semaphore = &mlc->osem;
+	up(&mlc->csem);
+	return 0;
+}
+
+static void hp_sdc_mlc_out(hil_mlc *mlc)
+{
+	struct hp_sdc_mlc_priv_s *priv;
+
+	priv = mlc->priv;
+
+	/* Try to down the semaphore -- it should be up. */
+	BUG_ON(down_trylock(&mlc->osem));
+
+	if (mlc->opacket & HIL_DO_ALTER_CTRL)
+		goto do_control;
+
+ do_data:
+	if (priv->emtestmode) {
+		up(&mlc->osem);
+		return;
+	}
+	/* Shouldn't be sending commands when loop may be busy */
+	BUG_ON(down_trylock(&mlc->csem));
+	up(&mlc->csem);
+
+	priv->trans.actidx = 0;
+	priv->trans.idx = 1;
+	priv->trans.act.semaphore = &mlc->osem;
+	priv->trans.endidx = 6;
+	priv->tseq[0] =
+		HP_SDC_ACT_DATAREG | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_SEMAPHORE;
+	priv->tseq[1] = 0x7;
+	priv->tseq[2] =
+		(mlc->opacket &
+		 (HIL_PKT_ADDR_MASK | HIL_PKT_CMD))
+		   >> HIL_PKT_ADDR_SHIFT;
+	priv->tseq[3] =
+		(mlc->opacket & HIL_PKT_DATA_MASK)
+		  >> HIL_PKT_DATA_SHIFT;
+	priv->tseq[4] = 0;  /* No timeout */
+	if (priv->tseq[3] == HIL_CMD_DHR)
+		priv->tseq[4] = 1;
+	priv->tseq[5] = HP_SDC_CMD_DO_HIL;
+	goto enqueue;
+
+ do_control:
+	priv->emtestmode = mlc->opacket & HIL_CTRL_TEST;
+
+	/* we cannot emulate this, it should not be used. */
+	BUG_ON((mlc->opacket & (HIL_CTRL_APE | HIL_CTRL_IPF)) == HIL_CTRL_APE);
+
+	if ((mlc->opacket & HIL_CTRL_ONLY) == HIL_CTRL_ONLY)
+		goto control_only;
+
+	/* Should not send command/data after engaging APE */
+	BUG_ON(mlc->opacket & HIL_CTRL_APE);
+
+	/* Disengaging APE this way would not be valid either since
+	 * the loop must be allowed to idle.
+	 *
+	 * So, it works out that we really never actually send control
+	 * and data when using SDC, we just send the data.
+	 */
+	goto do_data;
+
+ control_only:
+	priv->trans.actidx = 0;
+	priv->trans.idx = 1;
+	priv->trans.act.semaphore = &mlc->osem;
+	priv->trans.endidx = 4;
+	priv->tseq[0] =
+	  HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE;
+	priv->tseq[1] = HP_SDC_CMD_SET_LPC;
+	priv->tseq[2] = 1;
+	/* priv->tseq[3] = (mlc->ddc + 1) | HP_SDC_LPS_ACSUCC; */
+	priv->tseq[3] = 0;
+	if (mlc->opacket & HIL_CTRL_APE) {
+		priv->tseq[3] |= HP_SDC_LPC_APE_IPF;
+		BUG_ON(down_trylock(&mlc->csem));
+	}
+ enqueue:
+	hp_sdc_enqueue_transaction(&priv->trans);
+}
+
+static int __init hp_sdc_mlc_init(void)
+{
+	hil_mlc *mlc = &hp_sdc_mlc;
+	int err;
+
+#ifdef __mc68000__
+	if (!MACH_IS_HP300)
+		return -ENODEV;
+#endif
+
+	printk(KERN_INFO PREFIX "Registering the System Domain Controller's HIL MLC.\n");
+
+	hp_sdc_mlc_priv.emtestmode = 0;
+	hp_sdc_mlc_priv.trans.seq = hp_sdc_mlc_priv.tseq;
+	hp_sdc_mlc_priv.trans.act.semaphore = &mlc->osem;
+	hp_sdc_mlc_priv.got5x = 0;
+
+	mlc->cts = &hp_sdc_mlc_cts;
+	mlc->in	= &hp_sdc_mlc_in;
+	mlc->out = &hp_sdc_mlc_out;
+	mlc->priv = &hp_sdc_mlc_priv;
+
+	err = hil_mlc_register(mlc);
+	if (err) {
+		printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n");
+		return err;
+	}
+
+	if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) {
+		printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n");
+		if (hil_mlc_unregister(mlc))
+			printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
+				"This is bad.  Could cause an oops.\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static void __exit hp_sdc_mlc_exit(void)
+{
+	hil_mlc *mlc = &hp_sdc_mlc;
+
+	if (hp_sdc_release_hil_irq(&hp_sdc_mlc_isr))
+		printk(KERN_ERR PREFIX "Failed to release the raw HIL ISR hook.\n"
+			"This is bad.  Could cause an oops.\n");
+
+	if (hil_mlc_unregister(mlc))
+		printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
+			"This is bad.  Could cause an oops.\n");
+}
+
+module_init(hp_sdc_mlc_init);
+module_exit(hp_sdc_mlc_exit);
diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c
new file mode 100644
index 0000000..e74e5d6
--- /dev/null
+++ b/drivers/input/serio/hyperv-keyboard.c
@@ -0,0 +1,452 @@
+/*
+ *  Copyright (c) 2013, Microsoft Corporation.
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms and conditions of the GNU General Public License,
+ *  version 2, as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ *  more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/completion.h>
+#include <linux/hyperv.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+
+/*
+ * Current version 1.0
+ *
+ */
+#define SYNTH_KBD_VERSION_MAJOR 1
+#define SYNTH_KBD_VERSION_MINOR	0
+#define SYNTH_KBD_VERSION		(SYNTH_KBD_VERSION_MINOR | \
+					 (SYNTH_KBD_VERSION_MAJOR << 16))
+
+
+/*
+ * Message types in the synthetic input protocol
+ */
+enum synth_kbd_msg_type {
+	SYNTH_KBD_PROTOCOL_REQUEST = 1,
+	SYNTH_KBD_PROTOCOL_RESPONSE = 2,
+	SYNTH_KBD_EVENT = 3,
+	SYNTH_KBD_LED_INDICATORS = 4,
+};
+
+/*
+ * Basic message structures.
+ */
+struct synth_kbd_msg_hdr {
+	__le32 type;
+};
+
+struct synth_kbd_msg {
+	struct synth_kbd_msg_hdr header;
+	char data[]; /* Enclosed message */
+};
+
+union synth_kbd_version {
+	__le32 version;
+};
+
+/*
+ * Protocol messages
+ */
+struct synth_kbd_protocol_request {
+	struct synth_kbd_msg_hdr header;
+	union synth_kbd_version version_requested;
+};
+
+#define PROTOCOL_ACCEPTED	BIT(0)
+struct synth_kbd_protocol_response {
+	struct synth_kbd_msg_hdr header;
+	__le32 proto_status;
+};
+
+#define IS_UNICODE	BIT(0)
+#define IS_BREAK	BIT(1)
+#define IS_E0		BIT(2)
+#define IS_E1		BIT(3)
+struct synth_kbd_keystroke {
+	struct synth_kbd_msg_hdr header;
+	__le16 make_code;
+	__le16 reserved0;
+	__le32 info; /* Additional information */
+};
+
+
+#define HK_MAXIMUM_MESSAGE_SIZE 256
+
+#define KBD_VSC_SEND_RING_BUFFER_SIZE		(10 * PAGE_SIZE)
+#define KBD_VSC_RECV_RING_BUFFER_SIZE		(10 * PAGE_SIZE)
+
+#define XTKBD_EMUL0     0xe0
+#define XTKBD_EMUL1     0xe1
+#define XTKBD_RELEASE   0x80
+
+
+/*
+ * Represents a keyboard device
+ */
+struct hv_kbd_dev {
+	struct hv_device *hv_dev;
+	struct serio *hv_serio;
+	struct synth_kbd_protocol_request protocol_req;
+	struct synth_kbd_protocol_response protocol_resp;
+	/* Synchronize the request/response if needed */
+	struct completion wait_event;
+	spinlock_t lock; /* protects 'started' field */
+	bool started;
+};
+
+static void hv_kbd_on_receive(struct hv_device *hv_dev,
+			      struct synth_kbd_msg *msg, u32 msg_length)
+{
+	struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);
+	struct synth_kbd_keystroke *ks_msg;
+	unsigned long flags;
+	u32 msg_type = __le32_to_cpu(msg->header.type);
+	u32 info;
+	u16 scan_code;
+
+	switch (msg_type) {
+	case SYNTH_KBD_PROTOCOL_RESPONSE:
+		/*
+		 * Validate the information provided by the host.
+		 * If the host is giving us a bogus packet,
+		 * drop the packet (hoping the problem
+		 * goes away).
+		 */
+		if (msg_length < sizeof(struct synth_kbd_protocol_response)) {
+			dev_err(&hv_dev->device,
+				"Illegal protocol response packet (len: %d)\n",
+				msg_length);
+			break;
+		}
+
+		memcpy(&kbd_dev->protocol_resp, msg,
+			sizeof(struct synth_kbd_protocol_response));
+		complete(&kbd_dev->wait_event);
+		break;
+
+	case SYNTH_KBD_EVENT:
+		/*
+		 * Validate the information provided by the host.
+		 * If the host is giving us a bogus packet,
+		 * drop the packet (hoping the problem
+		 * goes away).
+		 */
+		if (msg_length < sizeof(struct  synth_kbd_keystroke)) {
+			dev_err(&hv_dev->device,
+				"Illegal keyboard event packet (len: %d)\n",
+				msg_length);
+			break;
+		}
+
+		ks_msg = (struct synth_kbd_keystroke *)msg;
+		info = __le32_to_cpu(ks_msg->info);
+
+		/*
+		 * Inject the information through the serio interrupt.
+		 */
+		spin_lock_irqsave(&kbd_dev->lock, flags);
+		if (kbd_dev->started) {
+			if (info & IS_E0)
+				serio_interrupt(kbd_dev->hv_serio,
+						XTKBD_EMUL0, 0);
+			if (info & IS_E1)
+				serio_interrupt(kbd_dev->hv_serio,
+						XTKBD_EMUL1, 0);
+			scan_code = __le16_to_cpu(ks_msg->make_code);
+			if (info & IS_BREAK)
+				scan_code |= XTKBD_RELEASE;
+
+			serio_interrupt(kbd_dev->hv_serio, scan_code, 0);
+		}
+		spin_unlock_irqrestore(&kbd_dev->lock, flags);
+
+		/*
+		 * Only trigger a wakeup on key down, otherwise
+		 * "echo freeze > /sys/power/state" can't really enter the
+		 * state because the Enter-UP can trigger a wakeup at once.
+		 */
+		if (!(info & IS_BREAK))
+			pm_wakeup_event(&hv_dev->device, 0);
+
+		break;
+
+	default:
+		dev_err(&hv_dev->device,
+			"unhandled message type %d\n", msg_type);
+	}
+}
+
+static void hv_kbd_handle_received_packet(struct hv_device *hv_dev,
+					  struct vmpacket_descriptor *desc,
+					  u32 bytes_recvd,
+					  u64 req_id)
+{
+	struct synth_kbd_msg *msg;
+	u32 msg_sz;
+
+	switch (desc->type) {
+	case VM_PKT_COMP:
+		break;
+
+	case VM_PKT_DATA_INBAND:
+		/*
+		 * We have a packet that has "inband" data. The API used
+		 * for retrieving the packet guarantees that the complete
+		 * packet is read. So, minimally, we should be able to
+		 * parse the payload header safely (assuming that the host
+		 * can be trusted.  Trusting the host seems to be a
+		 * reasonable assumption because in a virtualized
+		 * environment there is not whole lot you can do if you
+		 * don't trust the host.
+		 *
+		 * Nonetheless, let us validate if the host can be trusted
+		 * (in a trivial way).  The interesting aspect of this
+		 * validation is how do you recover if we discover that the
+		 * host is not to be trusted? Simply dropping the packet, I
+		 * don't think is an appropriate recovery.  In the interest
+		 * of failing fast, it may be better to crash the guest.
+		 * For now, I will just drop the packet!
+		 */
+
+		msg_sz = bytes_recvd - (desc->offset8 << 3);
+		if (msg_sz <= sizeof(struct synth_kbd_msg_hdr)) {
+			/*
+			 * Drop the packet and hope
+			 * the problem magically goes away.
+			 */
+			dev_err(&hv_dev->device,
+				"Illegal packet (type: %d, tid: %llx, size: %d)\n",
+				desc->type, req_id, msg_sz);
+			break;
+		}
+
+		msg = (void *)desc + (desc->offset8 << 3);
+		hv_kbd_on_receive(hv_dev, msg, msg_sz);
+		break;
+
+	default:
+		dev_err(&hv_dev->device,
+			"unhandled packet type %d, tid %llx len %d\n",
+			desc->type, req_id, bytes_recvd);
+		break;
+	}
+}
+
+static void hv_kbd_on_channel_callback(void *context)
+{
+	struct hv_device *hv_dev = context;
+	void *buffer;
+	int bufferlen = 0x100; /* Start with sensible size */
+	u32 bytes_recvd;
+	u64 req_id;
+	int error;
+
+	buffer = kmalloc(bufferlen, GFP_ATOMIC);
+	if (!buffer)
+		return;
+
+	while (1) {
+		error = vmbus_recvpacket_raw(hv_dev->channel, buffer, bufferlen,
+					     &bytes_recvd, &req_id);
+		switch (error) {
+		case 0:
+			if (bytes_recvd == 0) {
+				kfree(buffer);
+				return;
+			}
+
+			hv_kbd_handle_received_packet(hv_dev, buffer,
+						      bytes_recvd, req_id);
+			break;
+
+		case -ENOBUFS:
+			kfree(buffer);
+			/* Handle large packet */
+			bufferlen = bytes_recvd;
+			buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
+			if (!buffer)
+				return;
+			break;
+		}
+	}
+}
+
+static int hv_kbd_connect_to_vsp(struct hv_device *hv_dev)
+{
+	struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);
+	struct synth_kbd_protocol_request *request;
+	struct synth_kbd_protocol_response *response;
+	u32 proto_status;
+	int error;
+
+	request = &kbd_dev->protocol_req;
+	memset(request, 0, sizeof(struct synth_kbd_protocol_request));
+	request->header.type = __cpu_to_le32(SYNTH_KBD_PROTOCOL_REQUEST);
+	request->version_requested.version = __cpu_to_le32(SYNTH_KBD_VERSION);
+
+	error = vmbus_sendpacket(hv_dev->channel, request,
+				 sizeof(struct synth_kbd_protocol_request),
+				 (unsigned long)request,
+				 VM_PKT_DATA_INBAND,
+				 VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+	if (error)
+		return error;
+
+	if (!wait_for_completion_timeout(&kbd_dev->wait_event, 10 * HZ))
+		return -ETIMEDOUT;
+
+	response = &kbd_dev->protocol_resp;
+	proto_status = __le32_to_cpu(response->proto_status);
+	if (!(proto_status & PROTOCOL_ACCEPTED)) {
+		dev_err(&hv_dev->device,
+			"synth_kbd protocol request failed (version %d)\n",
+		        SYNTH_KBD_VERSION);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int hv_kbd_start(struct serio *serio)
+{
+	struct hv_kbd_dev *kbd_dev = serio->port_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kbd_dev->lock, flags);
+	kbd_dev->started = true;
+	spin_unlock_irqrestore(&kbd_dev->lock, flags);
+
+	return 0;
+}
+
+static void hv_kbd_stop(struct serio *serio)
+{
+	struct hv_kbd_dev *kbd_dev = serio->port_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kbd_dev->lock, flags);
+	kbd_dev->started = false;
+	spin_unlock_irqrestore(&kbd_dev->lock, flags);
+}
+
+static int hv_kbd_probe(struct hv_device *hv_dev,
+			const struct hv_vmbus_device_id *dev_id)
+{
+	struct hv_kbd_dev *kbd_dev;
+	struct serio *hv_serio;
+	int error;
+
+	kbd_dev = kzalloc(sizeof(struct hv_kbd_dev), GFP_KERNEL);
+	hv_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!kbd_dev || !hv_serio) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	kbd_dev->hv_dev = hv_dev;
+	kbd_dev->hv_serio = hv_serio;
+	spin_lock_init(&kbd_dev->lock);
+	init_completion(&kbd_dev->wait_event);
+	hv_set_drvdata(hv_dev, kbd_dev);
+
+	hv_serio->dev.parent  = &hv_dev->device;
+	hv_serio->id.type = SERIO_8042_XL;
+	hv_serio->port_data = kbd_dev;
+	strlcpy(hv_serio->name, dev_name(&hv_dev->device),
+		sizeof(hv_serio->name));
+	strlcpy(hv_serio->phys, dev_name(&hv_dev->device),
+		sizeof(hv_serio->phys));
+
+	hv_serio->start = hv_kbd_start;
+	hv_serio->stop = hv_kbd_stop;
+
+	error = vmbus_open(hv_dev->channel,
+			   KBD_VSC_SEND_RING_BUFFER_SIZE,
+			   KBD_VSC_RECV_RING_BUFFER_SIZE,
+			   NULL, 0,
+			   hv_kbd_on_channel_callback,
+			   hv_dev);
+	if (error)
+		goto err_free_mem;
+
+	error = hv_kbd_connect_to_vsp(hv_dev);
+	if (error)
+		goto err_close_vmbus;
+
+	serio_register_port(kbd_dev->hv_serio);
+
+	device_init_wakeup(&hv_dev->device, true);
+
+	return 0;
+
+err_close_vmbus:
+	vmbus_close(hv_dev->channel);
+err_free_mem:
+	kfree(hv_serio);
+	kfree(kbd_dev);
+	return error;
+}
+
+static int hv_kbd_remove(struct hv_device *hv_dev)
+{
+	struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);
+
+	device_init_wakeup(&hv_dev->device, false);
+	serio_unregister_port(kbd_dev->hv_serio);
+	vmbus_close(hv_dev->channel);
+	kfree(kbd_dev);
+
+	hv_set_drvdata(hv_dev, NULL);
+
+	return 0;
+}
+
+/*
+ * Keyboard GUID
+ * {f912ad6d-2b17-48ea-bd65-f927a61c7684}
+ */
+#define HV_KBD_GUID \
+	.guid = { \
+			0x6d, 0xad, 0x12, 0xf9, 0x17, 0x2b, 0xea, 0x48, \
+			0xbd, 0x65, 0xf9, 0x27, 0xa6, 0x1c, 0x76, 0x84 \
+	}
+
+static const struct hv_vmbus_device_id id_table[] = {
+	/* Keyboard guid */
+	{ HV_KBD_GUID, },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(vmbus, id_table);
+
+static struct  hv_driver hv_kbd_drv = {
+	.name = KBUILD_MODNAME,
+	.id_table = id_table,
+	.probe = hv_kbd_probe,
+	.remove = hv_kbd_remove,
+};
+
+static int __init hv_kbd_init(void)
+{
+	return vmbus_driver_register(&hv_kbd_drv);
+}
+
+static void __exit hv_kbd_exit(void)
+{
+	vmbus_driver_unregister(&hv_kbd_drv);
+}
+
+MODULE_LICENSE("GPL");
+module_init(hv_kbd_init);
+module_exit(hv_kbd_exit);
diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h
new file mode 100644
index 0000000..34da81c
--- /dev/null
+++ b/drivers/input/serio/i8042-io.h
@@ -0,0 +1,95 @@
+#ifndef _I8042_IO_H
+#define _I8042_IO_H
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "isa0060/serio0"
+#define I8042_AUX_PHYS_DESC "isa0060/serio1"
+#define I8042_MUX_PHYS_DESC "isa0060/serio%d"
+
+/*
+ * IRQs.
+ */
+
+#ifdef __alpha__
+# define I8042_KBD_IRQ	1
+# define I8042_AUX_IRQ	(RTC_PORT(0) == 0x170 ? 9 : 12)	/* Jensen is special */
+#elif defined(__arm__)
+/* defined in include/asm-arm/arch-xxx/irqs.h */
+#include <asm/irq.h>
+#elif defined(CONFIG_SH_CAYMAN)
+#include <asm/irq.h>
+#elif defined(CONFIG_PPC)
+extern int of_i8042_kbd_irq;
+extern int of_i8042_aux_irq;
+# define I8042_KBD_IRQ  of_i8042_kbd_irq
+# define I8042_AUX_IRQ  of_i8042_aux_irq
+#else
+# define I8042_KBD_IRQ	1
+# define I8042_AUX_IRQ	12
+#endif
+
+
+/*
+ * Register numbers.
+ */
+
+#define I8042_COMMAND_REG	0x64
+#define I8042_STATUS_REG	0x64
+#define I8042_DATA_REG		0x60
+
+static inline int i8042_read_data(void)
+{
+	return inb(I8042_DATA_REG);
+}
+
+static inline int i8042_read_status(void)
+{
+	return inb(I8042_STATUS_REG);
+}
+
+static inline void i8042_write_data(int val)
+{
+	outb(val, I8042_DATA_REG);
+}
+
+static inline void i8042_write_command(int val)
+{
+	outb(val, I8042_COMMAND_REG);
+}
+
+static inline int i8042_platform_init(void)
+{
+/*
+ * On some platforms touching the i8042 data register region can do really
+ * bad things. Because of this the region is always reserved on such boxes.
+ */
+#if defined(CONFIG_PPC)
+	if (check_legacy_ioport(I8042_DATA_REG))
+		return -ENODEV;
+#endif
+#if !defined(__sh__) && !defined(__alpha__)
+	if (!request_region(I8042_DATA_REG, 16, "i8042"))
+		return -EBUSY;
+#endif
+
+	i8042_reset = I8042_RESET_ALWAYS;
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+#if !defined(__sh__) && !defined(__alpha__)
+	release_region(I8042_DATA_REG, 16);
+#endif
+}
+
+#endif /* _I8042_IO_H */
diff --git a/drivers/input/serio/i8042-ip22io.h b/drivers/input/serio/i8042-ip22io.h
new file mode 100644
index 0000000..08a1c10
--- /dev/null
+++ b/drivers/input/serio/i8042-ip22io.h
@@ -0,0 +1,76 @@
+#ifndef _I8042_IP22_H
+#define _I8042_IP22_H
+
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/ip22.h>
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "hpc3ps2/serio0"
+#define I8042_AUX_PHYS_DESC "hpc3ps2/serio1"
+#define I8042_MUX_PHYS_DESC "hpc3ps2/serio%d"
+
+/*
+ * IRQs.
+ */
+
+#define I8042_KBD_IRQ SGI_KEYBD_IRQ
+#define I8042_AUX_IRQ SGI_KEYBD_IRQ
+
+/*
+ * Register numbers.
+ */
+
+#define I8042_COMMAND_REG	((unsigned long)&sgioc->kbdmouse.command)
+#define I8042_STATUS_REG	((unsigned long)&sgioc->kbdmouse.command)
+#define I8042_DATA_REG		((unsigned long)&sgioc->kbdmouse.data)
+
+static inline int i8042_read_data(void)
+{
+	return sgioc->kbdmouse.data;
+}
+
+static inline int i8042_read_status(void)
+{
+	return sgioc->kbdmouse.command;
+}
+
+static inline void i8042_write_data(int val)
+{
+	sgioc->kbdmouse.data = val;
+}
+
+static inline void i8042_write_command(int val)
+{
+	sgioc->kbdmouse.command = val;
+}
+
+static inline int i8042_platform_init(void)
+{
+#if 0
+	/* XXX sgi_kh is a virtual address */
+	if (!request_mem_region(sgi_kh, sizeof(struct hpc_keyb), "i8042"))
+		return -EBUSY;
+#endif
+
+	i8042_reset = I8042_RESET_ALWAYS;
+
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+#if 0
+	release_mem_region(JAZZ_KEYBOARD_ADDRESS, sizeof(struct hpc_keyb));
+#endif
+}
+
+#endif /* _I8042_IP22_H */
diff --git a/drivers/input/serio/i8042-jazzio.h b/drivers/input/serio/i8042-jazzio.h
new file mode 100644
index 0000000..13fd710
--- /dev/null
+++ b/drivers/input/serio/i8042-jazzio.h
@@ -0,0 +1,69 @@
+#ifndef _I8042_JAZZ_H
+#define _I8042_JAZZ_H
+
+#include <asm/jazz.h>
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "R4030/serio0"
+#define I8042_AUX_PHYS_DESC "R4030/serio1"
+#define I8042_MUX_PHYS_DESC "R4030/serio%d"
+
+/*
+ * IRQs.
+ */
+
+#define I8042_KBD_IRQ JAZZ_KEYBOARD_IRQ
+#define I8042_AUX_IRQ JAZZ_MOUSE_IRQ
+
+#define I8042_COMMAND_REG	((unsigned long)&jazz_kh->command)
+#define I8042_STATUS_REG	((unsigned long)&jazz_kh->command)
+#define I8042_DATA_REG		((unsigned long)&jazz_kh->data)
+
+static inline int i8042_read_data(void)
+{
+	return jazz_kh->data;
+}
+
+static inline int i8042_read_status(void)
+{
+	return jazz_kh->command;
+}
+
+static inline void i8042_write_data(int val)
+{
+	jazz_kh->data = val;
+}
+
+static inline void i8042_write_command(int val)
+{
+	jazz_kh->command = val;
+}
+
+static inline int i8042_platform_init(void)
+{
+#if 0
+	/* XXX JAZZ_KEYBOARD_ADDRESS is a virtual address */
+	if (!request_mem_region(JAZZ_KEYBOARD_ADDRESS, 2, "i8042"))
+		return -EBUSY;
+#endif
+
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+#if 0
+	release_mem_region(JAZZ_KEYBOARD_ADDRESS, 2);
+#endif
+}
+
+#endif /* _I8042_JAZZ_H */
diff --git a/drivers/input/serio/i8042-ppcio.h b/drivers/input/serio/i8042-ppcio.h
new file mode 100644
index 0000000..1aabea4
--- /dev/null
+++ b/drivers/input/serio/i8042-ppcio.h
@@ -0,0 +1,61 @@
+#ifndef _I8042_PPCIO_H
+#define _I8042_PPCIO_H
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#if defined(CONFIG_WALNUT)
+
+#define I8042_KBD_IRQ 25
+#define I8042_AUX_IRQ 26
+
+#define I8042_KBD_PHYS_DESC "walnutps2/serio0"
+#define I8042_AUX_PHYS_DESC "walnutps2/serio1"
+#define I8042_MUX_PHYS_DESC "walnutps2/serio%d"
+
+extern void *kb_cs;
+extern void *kb_data;
+
+#define I8042_COMMAND_REG (*(int *)kb_cs)
+#define I8042_DATA_REG (*(int *)kb_data)
+
+static inline int i8042_read_data(void)
+{
+	return readb(kb_data);
+}
+
+static inline int i8042_read_status(void)
+{
+	return readb(kb_cs);
+}
+
+static inline void i8042_write_data(int val)
+{
+	writeb(val, kb_data);
+}
+
+static inline void i8042_write_command(int val)
+{
+	writeb(val, kb_cs);
+}
+
+static inline int i8042_platform_init(void)
+{
+	i8042_reset = I8042_RESET_ALWAYS;
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+}
+
+#else
+
+#include "i8042-io.h"
+
+#endif
+
+#endif /* _I8042_PPCIO_H */
diff --git a/drivers/input/serio/i8042-snirm.h b/drivers/input/serio/i8042-snirm.h
new file mode 100644
index 0000000..409a934
--- /dev/null
+++ b/drivers/input/serio/i8042-snirm.h
@@ -0,0 +1,75 @@
+#ifndef _I8042_SNIRM_H
+#define _I8042_SNIRM_H
+
+#include <asm/sni.h>
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "onboard/serio0"
+#define I8042_AUX_PHYS_DESC "onboard/serio1"
+#define I8042_MUX_PHYS_DESC "onboard/serio%d"
+
+/*
+ * IRQs.
+ */
+static int i8042_kbd_irq;
+static int i8042_aux_irq;
+#define I8042_KBD_IRQ i8042_kbd_irq
+#define I8042_AUX_IRQ i8042_aux_irq
+
+static void __iomem *kbd_iobase;
+
+#define I8042_COMMAND_REG	(kbd_iobase + 0x64UL)
+#define I8042_DATA_REG		(kbd_iobase + 0x60UL)
+
+static inline int i8042_read_data(void)
+{
+	return readb(kbd_iobase + 0x60UL);
+}
+
+static inline int i8042_read_status(void)
+{
+	return readb(kbd_iobase + 0x64UL);
+}
+
+static inline void i8042_write_data(int val)
+{
+	writeb(val, kbd_iobase + 0x60UL);
+}
+
+static inline void i8042_write_command(int val)
+{
+	writeb(val, kbd_iobase + 0x64UL);
+}
+static inline int i8042_platform_init(void)
+{
+	/* RM200 is strange ... */
+	if (sni_brd_type == SNI_BRD_RM200) {
+		kbd_iobase = ioremap(0x16000000, 4);
+		i8042_kbd_irq = 33;
+		i8042_aux_irq = 44;
+	} else {
+		kbd_iobase = ioremap(0x14000000, 4);
+		i8042_kbd_irq = 1;
+		i8042_aux_irq = 12;
+	}
+	if (!kbd_iobase)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+
+}
+
+#endif /* _I8042_SNIRM_H */
diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h
new file mode 100644
index 0000000..6231d63
--- /dev/null
+++ b/drivers/input/serio/i8042-sparcio.h
@@ -0,0 +1,157 @@
+#ifndef _I8042_SPARCIO_H
+#define _I8042_SPARCIO_H
+
+#include <linux/of_device.h>
+
+#include <asm/io.h>
+#include <asm/oplib.h>
+#include <asm/prom.h>
+
+static int i8042_kbd_irq = -1;
+static int i8042_aux_irq = -1;
+#define I8042_KBD_IRQ i8042_kbd_irq
+#define I8042_AUX_IRQ i8042_aux_irq
+
+#define I8042_KBD_PHYS_DESC "sparcps2/serio0"
+#define I8042_AUX_PHYS_DESC "sparcps2/serio1"
+#define I8042_MUX_PHYS_DESC "sparcps2/serio%d"
+
+static void __iomem *kbd_iobase;
+
+#define I8042_COMMAND_REG	(kbd_iobase + 0x64UL)
+#define I8042_DATA_REG		(kbd_iobase + 0x60UL)
+
+static inline int i8042_read_data(void)
+{
+	return readb(kbd_iobase + 0x60UL);
+}
+
+static inline int i8042_read_status(void)
+{
+	return readb(kbd_iobase + 0x64UL);
+}
+
+static inline void i8042_write_data(int val)
+{
+	writeb(val, kbd_iobase + 0x60UL);
+}
+
+static inline void i8042_write_command(int val)
+{
+	writeb(val, kbd_iobase + 0x64UL);
+}
+
+#ifdef CONFIG_PCI
+
+static struct resource *kbd_res;
+
+#define OBP_PS2KBD_NAME1	"kb_ps2"
+#define OBP_PS2KBD_NAME2	"keyboard"
+#define OBP_PS2MS_NAME1		"kdmouse"
+#define OBP_PS2MS_NAME2		"mouse"
+
+static int sparc_i8042_probe(struct platform_device *op)
+{
+	struct device_node *dp = op->dev.of_node;
+
+	dp = dp->child;
+	while (dp) {
+		if (!strcmp(dp->name, OBP_PS2KBD_NAME1) ||
+		    !strcmp(dp->name, OBP_PS2KBD_NAME2)) {
+			struct platform_device *kbd = of_find_device_by_node(dp);
+			unsigned int irq = kbd->archdata.irqs[0];
+			if (irq == 0xffffffff)
+				irq = op->archdata.irqs[0];
+			i8042_kbd_irq = irq;
+			kbd_iobase = of_ioremap(&kbd->resource[0],
+						0, 8, "kbd");
+			kbd_res = &kbd->resource[0];
+		} else if (!strcmp(dp->name, OBP_PS2MS_NAME1) ||
+			   !strcmp(dp->name, OBP_PS2MS_NAME2)) {
+			struct platform_device *ms = of_find_device_by_node(dp);
+			unsigned int irq = ms->archdata.irqs[0];
+			if (irq == 0xffffffff)
+				irq = op->archdata.irqs[0];
+			i8042_aux_irq = irq;
+		}
+
+		dp = dp->sibling;
+	}
+
+	return 0;
+}
+
+static int sparc_i8042_remove(struct platform_device *op)
+{
+	of_iounmap(kbd_res, kbd_iobase, 8);
+
+	return 0;
+}
+
+static const struct of_device_id sparc_i8042_match[] = {
+	{
+		.name = "8042",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, sparc_i8042_match);
+
+static struct platform_driver sparc_i8042_driver = {
+	.driver = {
+		.name = "i8042",
+		.of_match_table = sparc_i8042_match,
+	},
+	.probe		= sparc_i8042_probe,
+	.remove		= sparc_i8042_remove,
+};
+
+static int __init i8042_platform_init(void)
+{
+	struct device_node *root = of_find_node_by_path("/");
+
+	if (!strcmp(root->name, "SUNW,JavaStation-1")) {
+		/* Hardcoded values for MrCoffee.  */
+		i8042_kbd_irq = i8042_aux_irq = 13 | 0x20;
+		kbd_iobase = ioremap(0x71300060, 8);
+		if (!kbd_iobase)
+			return -ENODEV;
+	} else {
+		int err = platform_driver_register(&sparc_i8042_driver);
+		if (err)
+			return err;
+
+		if (i8042_kbd_irq == -1 ||
+		    i8042_aux_irq == -1) {
+			if (kbd_iobase) {
+				of_iounmap(kbd_res, kbd_iobase, 8);
+				kbd_iobase = (void __iomem *) NULL;
+			}
+			return -ENODEV;
+		}
+	}
+
+	i8042_reset = I8042_RESET_ALWAYS;
+
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+	struct device_node *root = of_find_node_by_path("/");
+
+	if (strcmp(root->name, "SUNW,JavaStation-1"))
+		platform_driver_unregister(&sparc_i8042_driver);
+}
+
+#else /* !CONFIG_PCI */
+static int __init i8042_platform_init(void)
+{
+	return -ENODEV;
+}
+
+static inline void i8042_platform_exit(void)
+{
+}
+#endif /* !CONFIG_PCI */
+
+#endif /* _I8042_SPARCIO_H */
diff --git a/drivers/input/serio/i8042-unicore32io.h b/drivers/input/serio/i8042-unicore32io.h
new file mode 100644
index 0000000..4557475
--- /dev/null
+++ b/drivers/input/serio/i8042-unicore32io.h
@@ -0,0 +1,73 @@
+/*
+ * Code specific to PKUnity SoC and UniCore ISA
+ *
+ *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
+ *	Copyright (C) 2001-2011 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _I8042_UNICORE32_H
+#define _I8042_UNICORE32_H
+
+#include <mach/hardware.h>
+
+/*
+ * Names.
+ */
+#define I8042_KBD_PHYS_DESC "isa0060/serio0"
+#define I8042_AUX_PHYS_DESC "isa0060/serio1"
+#define I8042_MUX_PHYS_DESC "isa0060/serio%d"
+
+/*
+ * IRQs.
+ */
+#define I8042_KBD_IRQ           IRQ_PS2_KBD
+#define I8042_AUX_IRQ           IRQ_PS2_AUX
+
+/*
+ * Register numbers.
+ */
+#define I8042_COMMAND_REG	PS2_COMMAND
+#define I8042_STATUS_REG	PS2_STATUS
+#define I8042_DATA_REG		PS2_DATA
+
+#define I8042_REGION_START	(resource_size_t)(PS2_DATA)
+#define I8042_REGION_SIZE	(resource_size_t)(16)
+
+static inline int i8042_read_data(void)
+{
+	return readb(I8042_DATA_REG);
+}
+
+static inline int i8042_read_status(void)
+{
+	return readb(I8042_STATUS_REG);
+}
+
+static inline void i8042_write_data(int val)
+{
+	writeb(val, I8042_DATA_REG);
+}
+
+static inline void i8042_write_command(int val)
+{
+	writeb(val, I8042_COMMAND_REG);
+}
+
+static inline int i8042_platform_init(void)
+{
+	if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042"))
+		return -EBUSY;
+
+	i8042_reset = I8042_RESET_ALWAYS;
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+	release_mem_region(I8042_REGION_START, I8042_REGION_SIZE);
+}
+
+#endif /* _I8042_UNICORE32_H */
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
new file mode 100644
index 0000000..dbf0983
--- /dev/null
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -0,0 +1,1245 @@
+#ifndef _I8042_X86IA64IO_H
+#define _I8042_X86IA64IO_H
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifdef CONFIG_X86
+#include <asm/x86_init.h>
+#endif
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "isa0060/serio0"
+#define I8042_AUX_PHYS_DESC "isa0060/serio1"
+#define I8042_MUX_PHYS_DESC "isa0060/serio%d"
+
+/*
+ * IRQs.
+ */
+
+#if defined(__ia64__)
+# define I8042_MAP_IRQ(x)	isa_irq_to_vector((x))
+#else
+# define I8042_MAP_IRQ(x)	(x)
+#endif
+
+#define I8042_KBD_IRQ	i8042_kbd_irq
+#define I8042_AUX_IRQ	i8042_aux_irq
+
+static int i8042_kbd_irq;
+static int i8042_aux_irq;
+
+/*
+ * Register numbers.
+ */
+
+#define I8042_COMMAND_REG	i8042_command_reg
+#define I8042_STATUS_REG	i8042_command_reg
+#define I8042_DATA_REG		i8042_data_reg
+
+static int i8042_command_reg = 0x64;
+static int i8042_data_reg = 0x60;
+
+
+static inline int i8042_read_data(void)
+{
+	return inb(I8042_DATA_REG);
+}
+
+static inline int i8042_read_status(void)
+{
+	return inb(I8042_STATUS_REG);
+}
+
+static inline void i8042_write_data(int val)
+{
+	outb(val, I8042_DATA_REG);
+}
+
+static inline void i8042_write_command(int val)
+{
+	outb(val, I8042_COMMAND_REG);
+}
+
+#ifdef CONFIG_X86
+
+#include <linux/dmi.h>
+
+static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = {
+	{
+		/*
+		 * Arima-Rioworks HDAMB -
+		 * AUX LOOP command does not raise AUX IRQ
+		 */
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"),
+			DMI_MATCH(DMI_BOARD_NAME, "HDAMB"),
+			DMI_MATCH(DMI_BOARD_VERSION, "Rev E"),
+		},
+	},
+	{
+		/* ASUS G1S */
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),
+			DMI_MATCH(DMI_BOARD_NAME, "G1S"),
+			DMI_MATCH(DMI_BOARD_VERSION, "1.0"),
+		},
+	},
+	{
+		/* ASUS P65UP5 - AUX LOOP command does not raise AUX IRQ */
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+			DMI_MATCH(DMI_BOARD_NAME, "P/I-P65UP5"),
+			DMI_MATCH(DMI_BOARD_VERSION, "REV 2.X"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "X750LN"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
+			DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "8500"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
+			DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"),
+		},
+	},
+	{
+		/* Dell Embedded Box PC 3000 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Embedded Box PC 3000"),
+		},
+	},
+	{
+		/* OQO Model 01 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "OQO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "00"),
+		},
+	},
+	{
+		/* ULI EV4873 - AUX LOOP does not work properly */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ULI"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "5a"),
+		},
+	},
+	{
+		/* Microsoft Virtual Machine */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"),
+		},
+	},
+	{
+		/* Medion MAM 2070 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "5a"),
+		},
+	},
+	{
+		/* Medion Akoya E7225 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Medion"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Akoya E7225"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
+		},
+	},
+	{
+		/* Blue FB5601 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "blue"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "M606"),
+		},
+	},
+	{
+		/* Gigabyte M912 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "M912"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "01"),
+		},
+	},
+	{
+		/* Gigabyte M1022M netbook */
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co.,Ltd."),
+			DMI_MATCH(DMI_BOARD_NAME, "M1022E"),
+			DMI_MATCH(DMI_BOARD_VERSION, "1.02"),
+		},
+	},
+	{
+		/* Gigabyte Spring Peak - defines wrong chassis type */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Spring Peak"),
+		},
+	},
+	{
+		/* Gigabyte T1005 - defines wrong chassis type ("Other") */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "T1005"),
+		},
+	},
+	{
+		/* Gigabyte T1005M/P - defines wrong chassis type ("Other") */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "T1005M/P"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "C15B"),
+		},
+	},
+	{ }
+};
+
+/*
+ * Some Fujitsu notebooks are having trouble with touchpads if
+ * active multiplexing mode is activated. Luckily they don't have
+ * external PS/2 ports so we can safely disable it.
+ * ... apparently some Toshibas don't like MUX mode either and
+ * die horrible death on reboot.
+ */
+static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
+	{
+		/* Fujitsu Lifebook P7010/P7010D */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "P7010"),
+		},
+	},
+	{
+		/* Fujitsu Lifebook P7010 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"),
+		},
+	},
+	{
+		/* Fujitsu Lifebook P5020D */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"),
+		},
+	},
+	{
+		/* Fujitsu Lifebook S2000 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"),
+		},
+	},
+	{
+		/* Fujitsu Lifebook S6230 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"),
+		},
+	},
+	{
+		/* Fujitsu Lifebook U745 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"),
+		},
+	},
+	{
+		/* Fujitsu T70H */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"),
+		},
+	},
+	{
+		/* Fujitsu-Siemens Lifebook T3010 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"),
+		},
+	},
+	{
+		/* Fujitsu-Siemens Lifebook E4010 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"),
+		},
+	},
+	{
+		/* Fujitsu-Siemens Amilo Pro 2010 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"),
+		},
+	},
+	{
+		/* Fujitsu-Siemens Amilo Pro 2030 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"),
+		},
+	},
+	{
+		/*
+		 * No data is coming from the touchscreen unless KBC
+		 * is in legacy mode.
+		 */
+		/* Panasonic CF-29 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
+		},
+	},
+	{
+		/*
+		 * HP Pavilion DV4017EA -
+		 * errors on MUX ports are reported without raising AUXDATA
+		 * causing "spurious NAK" messages.
+		 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"),
+		},
+	},
+	{
+		/*
+		 * HP Pavilion ZT1000 -
+		 * like DV4017EA does not raise AUXERR for errors on MUX ports.
+		 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook ZT1000"),
+		},
+	},
+	{
+		/*
+		 * HP Pavilion DV4270ca -
+		 * like DV4017EA does not raise AUXERR for errors on MUX ports.
+		 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"),
+		},
+	},
+	{
+		/* Sharp Actius MM20 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "SHARP"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"),
+		},
+	},
+	{
+		/* Sony Vaio FS-115b */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"),
+		},
+	},
+	{
+		/*
+		 * Sony Vaio FZ-240E -
+		 * reset and GET ID commands issued via KBD port are
+		 * sometimes being delivered to AUX3.
+		 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"),
+		},
+	},
+	{
+		/*
+		 * Most (all?) VAIOs do not have external PS/2 ports nor
+		 * they implement active multiplexing properly, and
+		 * MUX discovery usually messes up keyboard/touchpad.
+		 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+			DMI_MATCH(DMI_BOARD_NAME, "VAIO"),
+		},
+	},
+	{
+		/* Amoi M636/A737 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"),
+		},
+	},
+	{
+		/* Lenovo 3000 n100 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "076804U"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
+		},
+	},
+	{
+		/* Acer Aspire 5710 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710"),
+		},
+	},
+	{
+		/* Acer Aspire 7738 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7738"),
+		},
+	},
+	{
+		/* Gericom Bellagio */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Gericom"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"),
+		},
+	},
+	{
+		/* IBM 2656 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "IBM"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "2656"),
+		},
+	},
+	{
+		/* Dell XPS M1530 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"),
+		},
+	},
+	{
+		/* Compal HEL80I */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"),
+		},
+	},
+	{
+		/* Dell Vostro 1510 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"),
+		},
+	},
+	{
+		/* Acer Aspire 5536 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
+		},
+	},
+	{
+		/* Dell Vostro V13 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
+		},
+	},
+	{
+		/* Newer HP Pavilion dv4 models */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"),
+		},
+	},
+	{
+		/* Asus X450LCP */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"),
+		},
+	},
+	{
+		/* Avatar AVIU-145A6 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Intel"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "IC4I"),
+		},
+	},
+	{ }
+};
+
+/*
+ * On some Asus laptops, just running self tests cause problems.
+ */
+static const struct dmi_system_id i8042_dmi_noselftest_table[] = {
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "A455LD"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "K401LB"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "K501LB"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "K501LX"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "R409L"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "V502LX"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "X302LA"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "X450LD"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "X455LAB"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "X455LDB"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "X455LF"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Z450LA"),
+		},
+	},
+	{ }
+};
+static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
+	{
+		/* MSI Wind U-100 */
+		.matches = {
+			DMI_MATCH(DMI_BOARD_NAME, "U-100"),
+			DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
+		},
+	},
+	{
+		/* LG Electronics X110 */
+		.matches = {
+			DMI_MATCH(DMI_BOARD_NAME, "X110"),
+			DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."),
+		},
+	},
+	{
+		/* Acer Aspire One 150 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"),
+		},
+	},
+	{
+		/* Advent 4211 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"),
+		},
+	},
+	{
+		/* Medion Akoya Mini E1210 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "E1210"),
+		},
+	},
+	{
+		/* Medion Akoya E1222 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "E122X"),
+		},
+	},
+	{
+		/* Mivvy M310 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "N10"),
+		},
+	},
+	{
+		/* Dell Vostro 1320 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"),
+		},
+	},
+	{
+		/* Dell Vostro 1520 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"),
+		},
+	},
+	{
+		/* Dell Vostro 1720 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"),
+		},
+	},
+	{
+		/* Lenovo Ideapad U455 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "20046"),
+		},
+	},
+	{
+		/* Clevo P650RS, 650RP6, Sager NP8152-S, and others */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "P65xRP"),
+		},
+	},
+	{ }
+};
+
+#ifdef CONFIG_PNP
+static const struct dmi_system_id __initconst i8042_dmi_nopnp_table[] = {
+	{
+		/* Intel MBO Desktop D845PESV */
+		.matches = {
+			DMI_MATCH(DMI_BOARD_NAME, "D845PESV"),
+			DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
+		},
+	},
+	{
+		/*
+		 * Intel NUC D54250WYK - does not have i8042 controller but
+		 * declares PS/2 devices in DSDT.
+		 */
+		.matches = {
+			DMI_MATCH(DMI_BOARD_NAME, "D54250WYK"),
+			DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
+		},
+	},
+	{
+		/* MSI Wind U-100 */
+		.matches = {
+			DMI_MATCH(DMI_BOARD_NAME, "U-100"),
+			DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
+		},
+	},
+	{ }
+};
+
+static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = {
+	{
+		.matches = {
+			DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
+		},
+	},
+	{ }
+};
+#endif
+
+static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = {
+	{
+		/* Dell Vostro V13 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
+		},
+	},
+	{
+		/* Newer HP Pavilion dv4 models */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"),
+		},
+	},
+	{
+		/* Fujitsu A544 laptop */
+		/* https://bugzilla.redhat.com/show_bug.cgi?id=1111138 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK A544"),
+		},
+	},
+	{
+		/* Fujitsu AH544 laptop */
+		/* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK AH544"),
+		},
+	},
+	{
+		/* Fujitsu U574 laptop */
+		/* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U574"),
+		},
+	},
+	{
+		/* Fujitsu UH554 laptop */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK UH544"),
+		},
+	},
+	{ }
+};
+
+/*
+ * Some Wistron based laptops need us to explicitly enable the 'Dritek
+ * keyboard extension' to make their extra keys start generating scancodes.
+ * Originally, this was just confined to older laptops, but a few Acer laptops
+ * have turned up in 2007 that also need this again.
+ */
+static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = {
+	{
+		/* Acer Aspire 5100 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
+		},
+	},
+	{
+		/* Acer Aspire 5610 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
+		},
+	},
+	{
+		/* Acer Aspire 5630 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
+		},
+	},
+	{
+		/* Acer Aspire 5650 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
+		},
+	},
+	{
+		/* Acer Aspire 5680 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
+		},
+	},
+	{
+		/* Acer Aspire 5720 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"),
+		},
+	},
+	{
+		/* Acer Aspire 9110 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
+		},
+	},
+	{
+		/* Acer TravelMate 660 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"),
+		},
+	},
+	{
+		/* Acer TravelMate 2490 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
+		},
+	},
+	{
+		/* Acer TravelMate 4280 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"),
+		},
+	},
+	{ }
+};
+
+/*
+ * Some laptops need keyboard reset before probing for the trackpad to get
+ * it detected, initialised & finally work.
+ */
+static const struct dmi_system_id __initconst i8042_dmi_kbdreset_table[] = {
+	{
+		/* Gigabyte P35 v2 - Elantech touchpad */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "P35V2"),
+		},
+	},
+		{
+		/* Aorus branded Gigabyte X3 Plus - Elantech touchpad */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "X3"),
+		},
+	},
+	{
+		/* Gigabyte P34 - Elantech touchpad */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "P34"),
+		},
+	},
+	{
+		/* Gigabyte P57 - Elantech touchpad */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "P57"),
+		},
+	},
+	{
+		/* Schenker XMG C504 - Elantech touchpad */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "XMG"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "C504"),
+		},
+	},
+	{ }
+};
+
+#endif /* CONFIG_X86 */
+
+#ifdef CONFIG_PNP
+#include <linux/pnp.h>
+
+static bool i8042_pnp_kbd_registered;
+static unsigned int i8042_pnp_kbd_devices;
+static bool i8042_pnp_aux_registered;
+static unsigned int i8042_pnp_aux_devices;
+
+static int i8042_pnp_command_reg;
+static int i8042_pnp_data_reg;
+static int i8042_pnp_kbd_irq;
+static int i8042_pnp_aux_irq;
+
+static char i8042_pnp_kbd_name[32];
+static char i8042_pnp_aux_name[32];
+
+static void i8042_pnp_id_to_string(struct pnp_id *id, char *dst, int dst_size)
+{
+	strlcpy(dst, "PNP:", dst_size);
+
+	while (id) {
+		strlcat(dst, " ", dst_size);
+		strlcat(dst, id->id, dst_size);
+		id = id->next;
+	}
+}
+
+static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id *did)
+{
+	if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1)
+		i8042_pnp_data_reg = pnp_port_start(dev,0);
+
+	if (pnp_port_valid(dev, 1) && pnp_port_len(dev, 1) == 1)
+		i8042_pnp_command_reg = pnp_port_start(dev, 1);
+
+	if (pnp_irq_valid(dev,0))
+		i8042_pnp_kbd_irq = pnp_irq(dev, 0);
+
+	strlcpy(i8042_pnp_kbd_name, did->id, sizeof(i8042_pnp_kbd_name));
+	if (strlen(pnp_dev_name(dev))) {
+		strlcat(i8042_pnp_kbd_name, ":", sizeof(i8042_pnp_kbd_name));
+		strlcat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name));
+	}
+	i8042_pnp_id_to_string(dev->id, i8042_kbd_firmware_id,
+			       sizeof(i8042_kbd_firmware_id));
+
+	/* Keyboard ports are always supposed to be wakeup-enabled */
+	device_set_wakeup_enable(&dev->dev, true);
+
+	i8042_pnp_kbd_devices++;
+	return 0;
+}
+
+static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id *did)
+{
+	if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1)
+		i8042_pnp_data_reg = pnp_port_start(dev,0);
+
+	if (pnp_port_valid(dev, 1) && pnp_port_len(dev, 1) == 1)
+		i8042_pnp_command_reg = pnp_port_start(dev, 1);
+
+	if (pnp_irq_valid(dev, 0))
+		i8042_pnp_aux_irq = pnp_irq(dev, 0);
+
+	strlcpy(i8042_pnp_aux_name, did->id, sizeof(i8042_pnp_aux_name));
+	if (strlen(pnp_dev_name(dev))) {
+		strlcat(i8042_pnp_aux_name, ":", sizeof(i8042_pnp_aux_name));
+		strlcat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name));
+	}
+	i8042_pnp_id_to_string(dev->id, i8042_aux_firmware_id,
+			       sizeof(i8042_aux_firmware_id));
+
+	i8042_pnp_aux_devices++;
+	return 0;
+}
+
+static struct pnp_device_id pnp_kbd_devids[] = {
+	{ .id = "PNP0300", .driver_data = 0 },
+	{ .id = "PNP0301", .driver_data = 0 },
+	{ .id = "PNP0302", .driver_data = 0 },
+	{ .id = "PNP0303", .driver_data = 0 },
+	{ .id = "PNP0304", .driver_data = 0 },
+	{ .id = "PNP0305", .driver_data = 0 },
+	{ .id = "PNP0306", .driver_data = 0 },
+	{ .id = "PNP0309", .driver_data = 0 },
+	{ .id = "PNP030a", .driver_data = 0 },
+	{ .id = "PNP030b", .driver_data = 0 },
+	{ .id = "PNP0320", .driver_data = 0 },
+	{ .id = "PNP0343", .driver_data = 0 },
+	{ .id = "PNP0344", .driver_data = 0 },
+	{ .id = "PNP0345", .driver_data = 0 },
+	{ .id = "CPQA0D7", .driver_data = 0 },
+	{ .id = "", },
+};
+MODULE_DEVICE_TABLE(pnp, pnp_kbd_devids);
+
+static struct pnp_driver i8042_pnp_kbd_driver = {
+	.name           = "i8042 kbd",
+	.id_table       = pnp_kbd_devids,
+	.probe          = i8042_pnp_kbd_probe,
+};
+
+static struct pnp_device_id pnp_aux_devids[] = {
+	{ .id = "AUI0200", .driver_data = 0 },
+	{ .id = "FJC6000", .driver_data = 0 },
+	{ .id = "FJC6001", .driver_data = 0 },
+	{ .id = "PNP0f03", .driver_data = 0 },
+	{ .id = "PNP0f0b", .driver_data = 0 },
+	{ .id = "PNP0f0e", .driver_data = 0 },
+	{ .id = "PNP0f12", .driver_data = 0 },
+	{ .id = "PNP0f13", .driver_data = 0 },
+	{ .id = "PNP0f19", .driver_data = 0 },
+	{ .id = "PNP0f1c", .driver_data = 0 },
+	{ .id = "SYN0801", .driver_data = 0 },
+	{ .id = "", },
+};
+MODULE_DEVICE_TABLE(pnp, pnp_aux_devids);
+
+static struct pnp_driver i8042_pnp_aux_driver = {
+	.name           = "i8042 aux",
+	.id_table       = pnp_aux_devids,
+	.probe          = i8042_pnp_aux_probe,
+};
+
+static void i8042_pnp_exit(void)
+{
+	if (i8042_pnp_kbd_registered) {
+		i8042_pnp_kbd_registered = false;
+		pnp_unregister_driver(&i8042_pnp_kbd_driver);
+	}
+
+	if (i8042_pnp_aux_registered) {
+		i8042_pnp_aux_registered = false;
+		pnp_unregister_driver(&i8042_pnp_aux_driver);
+	}
+}
+
+static int __init i8042_pnp_init(void)
+{
+	char kbd_irq_str[4] = { 0 }, aux_irq_str[4] = { 0 };
+	bool pnp_data_busted = false;
+	int err;
+
+#ifdef CONFIG_X86
+	if (dmi_check_system(i8042_dmi_nopnp_table))
+		i8042_nopnp = true;
+#endif
+
+	if (i8042_nopnp) {
+		pr_info("PNP detection disabled\n");
+		return 0;
+	}
+
+	err = pnp_register_driver(&i8042_pnp_kbd_driver);
+	if (!err)
+		i8042_pnp_kbd_registered = true;
+
+	err = pnp_register_driver(&i8042_pnp_aux_driver);
+	if (!err)
+		i8042_pnp_aux_registered = true;
+
+	if (!i8042_pnp_kbd_devices && !i8042_pnp_aux_devices) {
+		i8042_pnp_exit();
+#if defined(__ia64__)
+		return -ENODEV;
+#else
+		pr_info("PNP: No PS/2 controller found. Probing ports directly.\n");
+		return 0;
+#endif
+	}
+
+	if (i8042_pnp_kbd_devices)
+		snprintf(kbd_irq_str, sizeof(kbd_irq_str),
+			"%d", i8042_pnp_kbd_irq);
+	if (i8042_pnp_aux_devices)
+		snprintf(aux_irq_str, sizeof(aux_irq_str),
+			"%d", i8042_pnp_aux_irq);
+
+	pr_info("PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %s%s%s\n",
+		i8042_pnp_kbd_name, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "",
+		i8042_pnp_aux_name,
+		i8042_pnp_data_reg, i8042_pnp_command_reg,
+		kbd_irq_str, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "",
+		aux_irq_str);
+
+#if defined(__ia64__)
+	if (!i8042_pnp_kbd_devices)
+		i8042_nokbd = true;
+	if (!i8042_pnp_aux_devices)
+		i8042_noaux = true;
+#endif
+
+	if (((i8042_pnp_data_reg & ~0xf) == (i8042_data_reg & ~0xf) &&
+	      i8042_pnp_data_reg != i8042_data_reg) ||
+	    !i8042_pnp_data_reg) {
+		pr_warn("PNP: PS/2 controller has invalid data port %#x; using default %#x\n",
+			i8042_pnp_data_reg, i8042_data_reg);
+		i8042_pnp_data_reg = i8042_data_reg;
+		pnp_data_busted = true;
+	}
+
+	if (((i8042_pnp_command_reg & ~0xf) == (i8042_command_reg & ~0xf) &&
+	      i8042_pnp_command_reg != i8042_command_reg) ||
+	    !i8042_pnp_command_reg) {
+		pr_warn("PNP: PS/2 controller has invalid command port %#x; using default %#x\n",
+			i8042_pnp_command_reg, i8042_command_reg);
+		i8042_pnp_command_reg = i8042_command_reg;
+		pnp_data_busted = true;
+	}
+
+	if (!i8042_nokbd && !i8042_pnp_kbd_irq) {
+		pr_warn("PNP: PS/2 controller doesn't have KBD irq; using default %d\n",
+			i8042_kbd_irq);
+		i8042_pnp_kbd_irq = i8042_kbd_irq;
+		pnp_data_busted = true;
+	}
+
+	if (!i8042_noaux && !i8042_pnp_aux_irq) {
+		if (!pnp_data_busted && i8042_pnp_kbd_irq) {
+			pr_warn("PNP: PS/2 appears to have AUX port disabled, "
+				"if this is incorrect please boot with i8042.nopnp\n");
+			i8042_noaux = true;
+		} else {
+			pr_warn("PNP: PS/2 controller doesn't have AUX irq; using default %d\n",
+				i8042_aux_irq);
+			i8042_pnp_aux_irq = i8042_aux_irq;
+		}
+	}
+
+	i8042_data_reg = i8042_pnp_data_reg;
+	i8042_command_reg = i8042_pnp_command_reg;
+	i8042_kbd_irq = i8042_pnp_kbd_irq;
+	i8042_aux_irq = i8042_pnp_aux_irq;
+
+#ifdef CONFIG_X86
+	i8042_bypass_aux_irq_test = !pnp_data_busted &&
+				    dmi_check_system(i8042_dmi_laptop_table);
+#endif
+
+	return 0;
+}
+
+#else
+static inline int i8042_pnp_init(void) { return 0; }
+static inline void i8042_pnp_exit(void) { }
+#endif
+
+static int __init i8042_platform_init(void)
+{
+	int retval;
+
+#ifdef CONFIG_X86
+	u8 a20_on = 0xdf;
+	/* Just return if pre-detection shows no i8042 controller exist */
+	if (!x86_platform.i8042_detect())
+		return -ENODEV;
+#endif
+
+/*
+ * On ix86 platforms touching the i8042 data register region can do really
+ * bad things. Because of this the region is always reserved on ix86 boxes.
+ *
+ *	if (!request_region(I8042_DATA_REG, 16, "i8042"))
+ *		return -EBUSY;
+ */
+
+	i8042_kbd_irq = I8042_MAP_IRQ(1);
+	i8042_aux_irq = I8042_MAP_IRQ(12);
+
+	retval = i8042_pnp_init();
+	if (retval)
+		return retval;
+
+#if defined(__ia64__)
+        i8042_reset = I8042_RESET_ALWAYS;
+#endif
+
+#ifdef CONFIG_X86
+	/* Honor module parameter when value is not default */
+	if (i8042_reset == I8042_RESET_DEFAULT) {
+		if (dmi_check_system(i8042_dmi_reset_table))
+			i8042_reset = I8042_RESET_ALWAYS;
+
+		if (dmi_check_system(i8042_dmi_noselftest_table))
+			i8042_reset = I8042_RESET_NEVER;
+	}
+
+	if (dmi_check_system(i8042_dmi_noloop_table))
+		i8042_noloop = true;
+
+	if (dmi_check_system(i8042_dmi_nomux_table))
+		i8042_nomux = true;
+
+	if (dmi_check_system(i8042_dmi_notimeout_table))
+		i8042_notimeout = true;
+
+	if (dmi_check_system(i8042_dmi_dritek_table))
+		i8042_dritek = true;
+
+	if (dmi_check_system(i8042_dmi_kbdreset_table))
+		i8042_kbdreset = true;
+
+	/*
+	 * A20 was already enabled during early kernel init. But some buggy
+	 * BIOSes (in MSI Laptops) require A20 to be enabled using 8042 to
+	 * resume from S3. So we do it here and hope that nothing breaks.
+	 */
+	i8042_command(&a20_on, 0x10d1);
+	i8042_command(NULL, 0x00ff);	/* Null command for SMM firmware */
+#endif /* CONFIG_X86 */
+
+	return retval;
+}
+
+static inline void i8042_platform_exit(void)
+{
+	i8042_pnp_exit();
+}
+
+#endif /* _I8042_X86IA64IO_H */
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
new file mode 100644
index 0000000..c84c685
--- /dev/null
+++ b/drivers/input/serio/i8042.c
@@ -0,0 +1,1633 @@
+/*
+ *  i8042 keyboard and mouse controller driver for Linux
+ *
+ *  Copyright (c) 1999-2004 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/err.h>
+#include <linux/rcupdate.h>
+#include <linux/platform_device.h>
+#include <linux/i8042.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+
+#include <asm/io.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver");
+MODULE_LICENSE("GPL");
+
+static bool i8042_nokbd;
+module_param_named(nokbd, i8042_nokbd, bool, 0);
+MODULE_PARM_DESC(nokbd, "Do not probe or use KBD port.");
+
+static bool i8042_noaux;
+module_param_named(noaux, i8042_noaux, bool, 0);
+MODULE_PARM_DESC(noaux, "Do not probe or use AUX (mouse) port.");
+
+static bool i8042_nomux;
+module_param_named(nomux, i8042_nomux, bool, 0);
+MODULE_PARM_DESC(nomux, "Do not check whether an active multiplexing controller is present.");
+
+static bool i8042_unlock;
+module_param_named(unlock, i8042_unlock, bool, 0);
+MODULE_PARM_DESC(unlock, "Ignore keyboard lock.");
+
+enum i8042_controller_reset_mode {
+	I8042_RESET_NEVER,
+	I8042_RESET_ALWAYS,
+	I8042_RESET_ON_S2RAM,
+#define I8042_RESET_DEFAULT	I8042_RESET_ON_S2RAM
+};
+static enum i8042_controller_reset_mode i8042_reset = I8042_RESET_DEFAULT;
+static int i8042_set_reset(const char *val, const struct kernel_param *kp)
+{
+	enum i8042_controller_reset_mode *arg = kp->arg;
+	int error;
+	bool reset;
+
+	if (val) {
+		error = kstrtobool(val, &reset);
+		if (error)
+			return error;
+	} else {
+		reset = true;
+	}
+
+	*arg = reset ? I8042_RESET_ALWAYS : I8042_RESET_NEVER;
+	return 0;
+}
+
+static const struct kernel_param_ops param_ops_reset_param = {
+	.flags = KERNEL_PARAM_OPS_FL_NOARG,
+	.set = i8042_set_reset,
+};
+#define param_check_reset_param(name, p)	\
+	__param_check(name, p, enum i8042_controller_reset_mode)
+module_param_named(reset, i8042_reset, reset_param, 0);
+MODULE_PARM_DESC(reset, "Reset controller on resume, cleanup or both");
+
+static bool i8042_direct;
+module_param_named(direct, i8042_direct, bool, 0);
+MODULE_PARM_DESC(direct, "Put keyboard port into non-translated mode.");
+
+static bool i8042_dumbkbd;
+module_param_named(dumbkbd, i8042_dumbkbd, bool, 0);
+MODULE_PARM_DESC(dumbkbd, "Pretend that controller can only read data from keyboard");
+
+static bool i8042_noloop;
+module_param_named(noloop, i8042_noloop, bool, 0);
+MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port");
+
+static bool i8042_notimeout;
+module_param_named(notimeout, i8042_notimeout, bool, 0);
+MODULE_PARM_DESC(notimeout, "Ignore timeouts signalled by i8042");
+
+static bool i8042_kbdreset;
+module_param_named(kbdreset, i8042_kbdreset, bool, 0);
+MODULE_PARM_DESC(kbdreset, "Reset device connected to KBD port");
+
+#ifdef CONFIG_X86
+static bool i8042_dritek;
+module_param_named(dritek, i8042_dritek, bool, 0);
+MODULE_PARM_DESC(dritek, "Force enable the Dritek keyboard extension");
+#endif
+
+#ifdef CONFIG_PNP
+static bool i8042_nopnp;
+module_param_named(nopnp, i8042_nopnp, bool, 0);
+MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings");
+#endif
+
+#define DEBUG
+#ifdef DEBUG
+static bool i8042_debug;
+module_param_named(debug, i8042_debug, bool, 0600);
+MODULE_PARM_DESC(debug, "Turn i8042 debugging mode on and off");
+
+static bool i8042_unmask_kbd_data;
+module_param_named(unmask_kbd_data, i8042_unmask_kbd_data, bool, 0600);
+MODULE_PARM_DESC(unmask_kbd_data, "Unconditional enable (may reveal sensitive data) of normally sanitize-filtered kbd data traffic debug log [pre-condition: i8042.debug=1 enabled]");
+#endif
+
+static bool i8042_bypass_aux_irq_test;
+static char i8042_kbd_firmware_id[128];
+static char i8042_aux_firmware_id[128];
+
+#include "i8042.h"
+
+/*
+ * i8042_lock protects serialization between i8042_command and
+ * the interrupt handler.
+ */
+static DEFINE_SPINLOCK(i8042_lock);
+
+/*
+ * Writers to AUX and KBD ports as well as users issuing i8042_command
+ * directly should acquire i8042_mutex (by means of calling
+ * i8042_lock_chip() and i8042_unlock_ship() helpers) to ensure that
+ * they do not disturb each other (unfortunately in many i8042
+ * implementations write to one of the ports will immediately abort
+ * command that is being processed by another port).
+ */
+static DEFINE_MUTEX(i8042_mutex);
+
+struct i8042_port {
+	struct serio *serio;
+	int irq;
+	bool exists;
+	bool driver_bound;
+	signed char mux;
+};
+
+#define I8042_KBD_PORT_NO	0
+#define I8042_AUX_PORT_NO	1
+#define I8042_MUX_PORT_NO	2
+#define I8042_NUM_PORTS		(I8042_NUM_MUX_PORTS + 2)
+
+static struct i8042_port i8042_ports[I8042_NUM_PORTS];
+
+static unsigned char i8042_initial_ctr;
+static unsigned char i8042_ctr;
+static bool i8042_mux_present;
+static bool i8042_kbd_irq_registered;
+static bool i8042_aux_irq_registered;
+static unsigned char i8042_suppress_kbd_ack;
+static struct platform_device *i8042_platform_device;
+static struct notifier_block i8042_kbd_bind_notifier_block;
+
+static irqreturn_t i8042_interrupt(int irq, void *dev_id);
+static bool (*i8042_platform_filter)(unsigned char data, unsigned char str,
+				     struct serio *serio);
+
+void i8042_lock_chip(void)
+{
+	mutex_lock(&i8042_mutex);
+}
+EXPORT_SYMBOL(i8042_lock_chip);
+
+void i8042_unlock_chip(void)
+{
+	mutex_unlock(&i8042_mutex);
+}
+EXPORT_SYMBOL(i8042_unlock_chip);
+
+int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
+					struct serio *serio))
+{
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+
+	if (i8042_platform_filter) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	i8042_platform_filter = filter;
+
+out:
+	spin_unlock_irqrestore(&i8042_lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL(i8042_install_filter);
+
+int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
+				       struct serio *port))
+{
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+
+	if (i8042_platform_filter != filter) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	i8042_platform_filter = NULL;
+
+out:
+	spin_unlock_irqrestore(&i8042_lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL(i8042_remove_filter);
+
+/*
+ * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
+ * be ready for reading values from it / writing values to it.
+ * Called always with i8042_lock held.
+ */
+
+static int i8042_wait_read(void)
+{
+	int i = 0;
+
+	while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {
+		udelay(50);
+		i++;
+	}
+	return -(i == I8042_CTL_TIMEOUT);
+}
+
+static int i8042_wait_write(void)
+{
+	int i = 0;
+
+	while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {
+		udelay(50);
+		i++;
+	}
+	return -(i == I8042_CTL_TIMEOUT);
+}
+
+/*
+ * i8042_flush() flushes all data that may be in the keyboard and mouse buffers
+ * of the i8042 down the toilet.
+ */
+
+static int i8042_flush(void)
+{
+	unsigned long flags;
+	unsigned char data, str;
+	int count = 0;
+	int retval = 0;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+
+	while ((str = i8042_read_status()) & I8042_STR_OBF) {
+		if (count++ < I8042_BUFFER_SIZE) {
+			udelay(50);
+			data = i8042_read_data();
+			dbg("%02x <- i8042 (flush, %s)\n",
+			    data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
+		} else {
+			retval = -EIO;
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&i8042_lock, flags);
+
+	return retval;
+}
+
+/*
+ * i8042_command() executes a command on the i8042. It also sends the input
+ * parameter(s) of the commands to it, and receives the output value(s). The
+ * parameters are to be stored in the param array, and the output is placed
+ * into the same array. The number of the parameters and output values is
+ * encoded in bits 8-11 of the command number.
+ */
+
+static int __i8042_command(unsigned char *param, int command)
+{
+	int i, error;
+
+	if (i8042_noloop && command == I8042_CMD_AUX_LOOP)
+		return -1;
+
+	error = i8042_wait_write();
+	if (error)
+		return error;
+
+	dbg("%02x -> i8042 (command)\n", command & 0xff);
+	i8042_write_command(command & 0xff);
+
+	for (i = 0; i < ((command >> 12) & 0xf); i++) {
+		error = i8042_wait_write();
+		if (error)
+			return error;
+		dbg("%02x -> i8042 (parameter)\n", param[i]);
+		i8042_write_data(param[i]);
+	}
+
+	for (i = 0; i < ((command >> 8) & 0xf); i++) {
+		error = i8042_wait_read();
+		if (error) {
+			dbg("     -- i8042 (timeout)\n");
+			return error;
+		}
+
+		if (command == I8042_CMD_AUX_LOOP &&
+		    !(i8042_read_status() & I8042_STR_AUXDATA)) {
+			dbg("     -- i8042 (auxerr)\n");
+			return -1;
+		}
+
+		param[i] = i8042_read_data();
+		dbg("%02x <- i8042 (return)\n", param[i]);
+	}
+
+	return 0;
+}
+
+int i8042_command(unsigned char *param, int command)
+{
+	unsigned long flags;
+	int retval;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+	retval = __i8042_command(param, command);
+	spin_unlock_irqrestore(&i8042_lock, flags);
+
+	return retval;
+}
+EXPORT_SYMBOL(i8042_command);
+
+/*
+ * i8042_kbd_write() sends a byte out through the keyboard interface.
+ */
+
+static int i8042_kbd_write(struct serio *port, unsigned char c)
+{
+	unsigned long flags;
+	int retval = 0;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+
+	if (!(retval = i8042_wait_write())) {
+		dbg("%02x -> i8042 (kbd-data)\n", c);
+		i8042_write_data(c);
+	}
+
+	spin_unlock_irqrestore(&i8042_lock, flags);
+
+	return retval;
+}
+
+/*
+ * i8042_aux_write() sends a byte out through the aux interface.
+ */
+
+static int i8042_aux_write(struct serio *serio, unsigned char c)
+{
+	struct i8042_port *port = serio->port_data;
+
+	return i8042_command(&c, port->mux == -1 ?
+					I8042_CMD_AUX_SEND :
+					I8042_CMD_MUX_SEND + port->mux);
+}
+
+
+/*
+ * i8042_aux_close attempts to clear AUX or KBD port state by disabling
+ * and then re-enabling it.
+ */
+
+static void i8042_port_close(struct serio *serio)
+{
+	int irq_bit;
+	int disable_bit;
+	const char *port_name;
+
+	if (serio == i8042_ports[I8042_AUX_PORT_NO].serio) {
+		irq_bit = I8042_CTR_AUXINT;
+		disable_bit = I8042_CTR_AUXDIS;
+		port_name = "AUX";
+	} else {
+		irq_bit = I8042_CTR_KBDINT;
+		disable_bit = I8042_CTR_KBDDIS;
+		port_name = "KBD";
+	}
+
+	i8042_ctr &= ~irq_bit;
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+		pr_warn("Can't write CTR while closing %s port\n", port_name);
+
+	udelay(50);
+
+	i8042_ctr &= ~disable_bit;
+	i8042_ctr |= irq_bit;
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+		pr_err("Can't reactivate %s port\n", port_name);
+
+	/*
+	 * See if there is any data appeared while we were messing with
+	 * port state.
+	 */
+	i8042_interrupt(0, NULL);
+}
+
+/*
+ * i8042_start() is called by serio core when port is about to finish
+ * registering. It will mark port as existing so i8042_interrupt can
+ * start sending data through it.
+ */
+static int i8042_start(struct serio *serio)
+{
+	struct i8042_port *port = serio->port_data;
+
+	spin_lock_irq(&i8042_lock);
+	port->exists = true;
+	spin_unlock_irq(&i8042_lock);
+
+	return 0;
+}
+
+/*
+ * i8042_stop() marks serio port as non-existing so i8042_interrupt
+ * will not try to send data to the port that is about to go away.
+ * The function is called by serio core as part of unregister procedure.
+ */
+static void i8042_stop(struct serio *serio)
+{
+	struct i8042_port *port = serio->port_data;
+
+	spin_lock_irq(&i8042_lock);
+	port->exists = false;
+	port->serio = NULL;
+	spin_unlock_irq(&i8042_lock);
+
+	/*
+	 * We need to make sure that interrupt handler finishes using
+	 * our serio port before we return from this function.
+	 * We synchronize with both AUX and KBD IRQs because there is
+	 * a (very unlikely) chance that AUX IRQ is raised for KBD port
+	 * and vice versa.
+	 */
+	synchronize_irq(I8042_AUX_IRQ);
+	synchronize_irq(I8042_KBD_IRQ);
+}
+
+/*
+ * i8042_filter() filters out unwanted bytes from the input data stream.
+ * It is called from i8042_interrupt and thus is running with interrupts
+ * off and i8042_lock held.
+ */
+static bool i8042_filter(unsigned char data, unsigned char str,
+			 struct serio *serio)
+{
+	if (unlikely(i8042_suppress_kbd_ack)) {
+		if ((~str & I8042_STR_AUXDATA) &&
+		    (data == 0xfa || data == 0xfe)) {
+			i8042_suppress_kbd_ack--;
+			dbg("Extra keyboard ACK - filtered out\n");
+			return true;
+		}
+	}
+
+	if (i8042_platform_filter && i8042_platform_filter(data, str, serio)) {
+		dbg("Filtered out by platform filter\n");
+		return true;
+	}
+
+	return false;
+}
+
+/*
+ * i8042_interrupt() is the most important function in this driver -
+ * it handles the interrupts from the i8042, and sends incoming bytes
+ * to the upper layers.
+ */
+
+static irqreturn_t i8042_interrupt(int irq, void *dev_id)
+{
+	struct i8042_port *port;
+	struct serio *serio;
+	unsigned long flags;
+	unsigned char str, data;
+	unsigned int dfl;
+	unsigned int port_no;
+	bool filtered;
+	int ret = 1;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+
+	str = i8042_read_status();
+	if (unlikely(~str & I8042_STR_OBF)) {
+		spin_unlock_irqrestore(&i8042_lock, flags);
+		if (irq)
+			dbg("Interrupt %d, without any data\n", irq);
+		ret = 0;
+		goto out;
+	}
+
+	data = i8042_read_data();
+
+	if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
+		static unsigned long last_transmit;
+		static unsigned char last_str;
+
+		dfl = 0;
+		if (str & I8042_STR_MUXERR) {
+			dbg("MUX error, status is %02x, data is %02x\n",
+			    str, data);
+/*
+ * When MUXERR condition is signalled the data register can only contain
+ * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately
+ * it is not always the case. Some KBCs also report 0xfc when there is
+ * nothing connected to the port while others sometimes get confused which
+ * port the data came from and signal error leaving the data intact. They
+ * _do not_ revert to legacy mode (actually I've never seen KBC reverting
+ * to legacy mode yet, when we see one we'll add proper handling).
+ * Anyway, we process 0xfc, 0xfd, 0xfe and 0xff as timeouts, and for the
+ * rest assume that the data came from the same serio last byte
+ * was transmitted (if transmission happened not too long ago).
+ */
+
+			switch (data) {
+				default:
+					if (time_before(jiffies, last_transmit + HZ/10)) {
+						str = last_str;
+						break;
+					}
+					/* fall through - report timeout */
+				case 0xfc:
+				case 0xfd:
+				case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break;
+				case 0xff: dfl = SERIO_PARITY;  data = 0xfe; break;
+			}
+		}
+
+		port_no = I8042_MUX_PORT_NO + ((str >> 6) & 3);
+		last_str = str;
+		last_transmit = jiffies;
+	} else {
+
+		dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
+		      ((str & I8042_STR_TIMEOUT && !i8042_notimeout) ? SERIO_TIMEOUT : 0);
+
+		port_no = (str & I8042_STR_AUXDATA) ?
+				I8042_AUX_PORT_NO : I8042_KBD_PORT_NO;
+	}
+
+	port = &i8042_ports[port_no];
+	serio = port->exists ? port->serio : NULL;
+
+	filter_dbg(port->driver_bound, data, "<- i8042 (interrupt, %d, %d%s%s)\n",
+		   port_no, irq,
+		   dfl & SERIO_PARITY ? ", bad parity" : "",
+		   dfl & SERIO_TIMEOUT ? ", timeout" : "");
+
+	filtered = i8042_filter(data, str, serio);
+
+	spin_unlock_irqrestore(&i8042_lock, flags);
+
+	if (likely(serio && !filtered))
+		serio_interrupt(serio, data, dfl);
+
+ out:
+	return IRQ_RETVAL(ret);
+}
+
+/*
+ * i8042_enable_kbd_port enables keyboard port on chip
+ */
+
+static int i8042_enable_kbd_port(void)
+{
+	i8042_ctr &= ~I8042_CTR_KBDDIS;
+	i8042_ctr |= I8042_CTR_KBDINT;
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+		i8042_ctr &= ~I8042_CTR_KBDINT;
+		i8042_ctr |= I8042_CTR_KBDDIS;
+		pr_err("Failed to enable KBD port\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * i8042_enable_aux_port enables AUX (mouse) port on chip
+ */
+
+static int i8042_enable_aux_port(void)
+{
+	i8042_ctr &= ~I8042_CTR_AUXDIS;
+	i8042_ctr |= I8042_CTR_AUXINT;
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+		i8042_ctr &= ~I8042_CTR_AUXINT;
+		i8042_ctr |= I8042_CTR_AUXDIS;
+		pr_err("Failed to enable AUX port\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * i8042_enable_mux_ports enables 4 individual AUX ports after
+ * the controller has been switched into Multiplexed mode
+ */
+
+static int i8042_enable_mux_ports(void)
+{
+	unsigned char param;
+	int i;
+
+	for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
+		i8042_command(&param, I8042_CMD_MUX_PFX + i);
+		i8042_command(&param, I8042_CMD_AUX_ENABLE);
+	}
+
+	return i8042_enable_aux_port();
+}
+
+/*
+ * i8042_set_mux_mode checks whether the controller has an
+ * active multiplexor and puts the chip into Multiplexed (true)
+ * or Legacy (false) mode.
+ */
+
+static int i8042_set_mux_mode(bool multiplex, unsigned char *mux_version)
+{
+
+	unsigned char param, val;
+/*
+ * Get rid of bytes in the queue.
+ */
+
+	i8042_flush();
+
+/*
+ * Internal loopback test - send three bytes, they should come back from the
+ * mouse interface, the last should be version.
+ */
+
+	param = val = 0xf0;
+	if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != val)
+		return -1;
+	param = val = multiplex ? 0x56 : 0xf6;
+	if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != val)
+		return -1;
+	param = val = multiplex ? 0xa4 : 0xa5;
+	if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param == val)
+		return -1;
+
+/*
+ * Workaround for interference with USB Legacy emulation
+ * that causes a v10.12 MUX to be found.
+ */
+	if (param == 0xac)
+		return -1;
+
+	if (mux_version)
+		*mux_version = param;
+
+	return 0;
+}
+
+/*
+ * i8042_check_mux() checks whether the controller supports the PS/2 Active
+ * Multiplexing specification by Synaptics, Phoenix, Insyde and
+ * LCS/Telegraphics.
+ */
+
+static int __init i8042_check_mux(void)
+{
+	unsigned char mux_version;
+
+	if (i8042_set_mux_mode(true, &mux_version))
+		return -1;
+
+	pr_info("Detected active multiplexing controller, rev %d.%d\n",
+		(mux_version >> 4) & 0xf, mux_version & 0xf);
+
+/*
+ * Disable all muxed ports by disabling AUX.
+ */
+	i8042_ctr |= I8042_CTR_AUXDIS;
+	i8042_ctr &= ~I8042_CTR_AUXINT;
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+		pr_err("Failed to disable AUX port, can't use MUX\n");
+		return -EIO;
+	}
+
+	i8042_mux_present = true;
+
+	return 0;
+}
+
+/*
+ * The following is used to test AUX IRQ delivery.
+ */
+static struct completion i8042_aux_irq_delivered __initdata;
+static bool i8042_irq_being_tested __initdata;
+
+static irqreturn_t __init i8042_aux_test_irq(int irq, void *dev_id)
+{
+	unsigned long flags;
+	unsigned char str, data;
+	int ret = 0;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+	str = i8042_read_status();
+	if (str & I8042_STR_OBF) {
+		data = i8042_read_data();
+		dbg("%02x <- i8042 (aux_test_irq, %s)\n",
+		    data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
+		if (i8042_irq_being_tested &&
+		    data == 0xa5 && (str & I8042_STR_AUXDATA))
+			complete(&i8042_aux_irq_delivered);
+		ret = 1;
+	}
+	spin_unlock_irqrestore(&i8042_lock, flags);
+
+	return IRQ_RETVAL(ret);
+}
+
+/*
+ * i8042_toggle_aux - enables or disables AUX port on i8042 via command and
+ * verifies success by readinng CTR. Used when testing for presence of AUX
+ * port.
+ */
+static int __init i8042_toggle_aux(bool on)
+{
+	unsigned char param;
+	int i;
+
+	if (i8042_command(&param,
+			on ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE))
+		return -1;
+
+	/* some chips need some time to set the I8042_CTR_AUXDIS bit */
+	for (i = 0; i < 100; i++) {
+		udelay(50);
+
+		if (i8042_command(&param, I8042_CMD_CTL_RCTR))
+			return -1;
+
+		if (!(param & I8042_CTR_AUXDIS) == on)
+			return 0;
+	}
+
+	return -1;
+}
+
+/*
+ * i8042_check_aux() applies as much paranoia as it can at detecting
+ * the presence of an AUX interface.
+ */
+
+static int __init i8042_check_aux(void)
+{
+	int retval = -1;
+	bool irq_registered = false;
+	bool aux_loop_broken = false;
+	unsigned long flags;
+	unsigned char param;
+
+/*
+ * Get rid of bytes in the queue.
+ */
+
+	i8042_flush();
+
+/*
+ * Internal loopback test - filters out AT-type i8042's. Unfortunately
+ * SiS screwed up and their 5597 doesn't support the LOOP command even
+ * though it has an AUX port.
+ */
+
+	param = 0x5a;
+	retval = i8042_command(&param, I8042_CMD_AUX_LOOP);
+	if (retval || param != 0x5a) {
+
+/*
+ * External connection test - filters out AT-soldered PS/2 i8042's
+ * 0x00 - no error, 0x01-0x03 - clock/data stuck, 0xff - general error
+ * 0xfa - no error on some notebooks which ignore the spec
+ * Because it's common for chipsets to return error on perfectly functioning
+ * AUX ports, we test for this only when the LOOP command failed.
+ */
+
+		if (i8042_command(&param, I8042_CMD_AUX_TEST) ||
+		    (param && param != 0xfa && param != 0xff))
+			return -1;
+
+/*
+ * If AUX_LOOP completed without error but returned unexpected data
+ * mark it as broken
+ */
+		if (!retval)
+			aux_loop_broken = true;
+	}
+
+/*
+ * Bit assignment test - filters out PS/2 i8042's in AT mode
+ */
+
+	if (i8042_toggle_aux(false)) {
+		pr_warn("Failed to disable AUX port, but continuing anyway... Is this a SiS?\n");
+		pr_warn("If AUX port is really absent please use the 'i8042.noaux' option\n");
+	}
+
+	if (i8042_toggle_aux(true))
+		return -1;
+
+/*
+ * Reset keyboard (needed on some laptops to successfully detect
+ * touchpad, e.g., some Gigabyte laptop models with Elantech
+ * touchpads).
+ */
+	if (i8042_kbdreset) {
+		pr_warn("Attempting to reset device connected to KBD port\n");
+		i8042_kbd_write(NULL, (unsigned char) 0xff);
+	}
+
+/*
+ * Test AUX IRQ delivery to make sure BIOS did not grab the IRQ and
+ * used it for a PCI card or somethig else.
+ */
+
+	if (i8042_noloop || i8042_bypass_aux_irq_test || aux_loop_broken) {
+/*
+ * Without LOOP command we can't test AUX IRQ delivery. Assume the port
+ * is working and hope we are right.
+ */
+		retval = 0;
+		goto out;
+	}
+
+	if (request_irq(I8042_AUX_IRQ, i8042_aux_test_irq, IRQF_SHARED,
+			"i8042", i8042_platform_device))
+		goto out;
+
+	irq_registered = true;
+
+	if (i8042_enable_aux_port())
+		goto out;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+
+	init_completion(&i8042_aux_irq_delivered);
+	i8042_irq_being_tested = true;
+
+	param = 0xa5;
+	retval = __i8042_command(&param, I8042_CMD_AUX_LOOP & 0xf0ff);
+
+	spin_unlock_irqrestore(&i8042_lock, flags);
+
+	if (retval)
+		goto out;
+
+	if (wait_for_completion_timeout(&i8042_aux_irq_delivered,
+					msecs_to_jiffies(250)) == 0) {
+/*
+ * AUX IRQ was never delivered so we need to flush the controller to
+ * get rid of the byte we put there; otherwise keyboard may not work.
+ */
+		dbg("     -- i8042 (aux irq test timeout)\n");
+		i8042_flush();
+		retval = -1;
+	}
+
+ out:
+
+/*
+ * Disable the interface.
+ */
+
+	i8042_ctr |= I8042_CTR_AUXDIS;
+	i8042_ctr &= ~I8042_CTR_AUXINT;
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+		retval = -1;
+
+	if (irq_registered)
+		free_irq(I8042_AUX_IRQ, i8042_platform_device);
+
+	return retval;
+}
+
+static int i8042_controller_check(void)
+{
+	if (i8042_flush()) {
+		pr_info("No controller found\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int i8042_controller_selftest(void)
+{
+	unsigned char param;
+	int i = 0;
+
+	/*
+	 * We try this 5 times; on some really fragile systems this does not
+	 * take the first time...
+	 */
+	do {
+
+		if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
+			pr_err("i8042 controller selftest timeout\n");
+			return -ENODEV;
+		}
+
+		if (param == I8042_RET_CTL_TEST)
+			return 0;
+
+		dbg("i8042 controller selftest: %#x != %#x\n",
+		    param, I8042_RET_CTL_TEST);
+		msleep(50);
+	} while (i++ < 5);
+
+#ifdef CONFIG_X86
+	/*
+	 * On x86, we don't fail entire i8042 initialization if controller
+	 * reset fails in hopes that keyboard port will still be functional
+	 * and user will still get a working keyboard. This is especially
+	 * important on netbooks. On other arches we trust hardware more.
+	 */
+	pr_info("giving up on controller selftest, continuing anyway...\n");
+	return 0;
+#else
+	pr_err("i8042 controller selftest failed\n");
+	return -EIO;
+#endif
+}
+
+/*
+ * i8042_controller init initializes the i8042 controller, and,
+ * most importantly, sets it into non-xlated mode if that's
+ * desired.
+ */
+
+static int i8042_controller_init(void)
+{
+	unsigned long flags;
+	int n = 0;
+	unsigned char ctr[2];
+
+/*
+ * Save the CTR for restore on unload / reboot.
+ */
+
+	do {
+		if (n >= 10) {
+			pr_err("Unable to get stable CTR read\n");
+			return -EIO;
+		}
+
+		if (n != 0)
+			udelay(50);
+
+		if (i8042_command(&ctr[n++ % 2], I8042_CMD_CTL_RCTR)) {
+			pr_err("Can't read CTR while initializing i8042\n");
+			return -EIO;
+		}
+
+	} while (n < 2 || ctr[0] != ctr[1]);
+
+	i8042_initial_ctr = i8042_ctr = ctr[0];
+
+/*
+ * Disable the keyboard interface and interrupt.
+ */
+
+	i8042_ctr |= I8042_CTR_KBDDIS;
+	i8042_ctr &= ~I8042_CTR_KBDINT;
+
+/*
+ * Handle keylock.
+ */
+
+	spin_lock_irqsave(&i8042_lock, flags);
+	if (~i8042_read_status() & I8042_STR_KEYLOCK) {
+		if (i8042_unlock)
+			i8042_ctr |= I8042_CTR_IGNKEYLOCK;
+		else
+			pr_warn("Warning: Keylock active\n");
+	}
+	spin_unlock_irqrestore(&i8042_lock, flags);
+
+/*
+ * If the chip is configured into nontranslated mode by the BIOS, don't
+ * bother enabling translating and be happy.
+ */
+
+	if (~i8042_ctr & I8042_CTR_XLATE)
+		i8042_direct = true;
+
+/*
+ * Set nontranslated mode for the kbd interface if requested by an option.
+ * After this the kbd interface becomes a simple serial in/out, like the aux
+ * interface is. We don't do this by default, since it can confuse notebook
+ * BIOSes.
+ */
+
+	if (i8042_direct)
+		i8042_ctr &= ~I8042_CTR_XLATE;
+
+/*
+ * Write CTR back.
+ */
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+		pr_err("Can't write CTR while initializing i8042\n");
+		return -EIO;
+	}
+
+/*
+ * Flush whatever accumulated while we were disabling keyboard port.
+ */
+
+	i8042_flush();
+
+	return 0;
+}
+
+
+/*
+ * Reset the controller and reset CRT to the original value set by BIOS.
+ */
+
+static void i8042_controller_reset(bool s2r_wants_reset)
+{
+	i8042_flush();
+
+/*
+ * Disable both KBD and AUX interfaces so they don't get in the way
+ */
+
+	i8042_ctr |= I8042_CTR_KBDDIS | I8042_CTR_AUXDIS;
+	i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT);
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+		pr_warn("Can't write CTR while resetting\n");
+
+/*
+ * Disable MUX mode if present.
+ */
+
+	if (i8042_mux_present)
+		i8042_set_mux_mode(false, NULL);
+
+/*
+ * Reset the controller if requested.
+ */
+
+	if (i8042_reset == I8042_RESET_ALWAYS ||
+	    (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
+		i8042_controller_selftest();
+	}
+
+/*
+ * Restore the original control register setting.
+ */
+
+	if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR))
+		pr_warn("Can't restore CTR\n");
+}
+
+
+/*
+ * i8042_panic_blink() will turn the keyboard LEDs on or off and is called
+ * when kernel panics. Flashing LEDs is useful for users running X who may
+ * not see the console and will help distinguishing panics from "real"
+ * lockups.
+ *
+ * Note that DELAY has a limit of 10ms so we will not get stuck here
+ * waiting for KBC to free up even if KBD interrupt is off
+ */
+
+#define DELAY do { mdelay(1); if (++delay > 10) return delay; } while(0)
+
+static long i8042_panic_blink(int state)
+{
+	long delay = 0;
+	char led;
+
+	led = (state) ? 0x01 | 0x04 : 0;
+	while (i8042_read_status() & I8042_STR_IBF)
+		DELAY;
+	dbg("%02x -> i8042 (panic blink)\n", 0xed);
+	i8042_suppress_kbd_ack = 2;
+	i8042_write_data(0xed); /* set leds */
+	DELAY;
+	while (i8042_read_status() & I8042_STR_IBF)
+		DELAY;
+	DELAY;
+	dbg("%02x -> i8042 (panic blink)\n", led);
+	i8042_write_data(led);
+	DELAY;
+	return delay;
+}
+
+#undef DELAY
+
+#ifdef CONFIG_X86
+static void i8042_dritek_enable(void)
+{
+	unsigned char param = 0x90;
+	int error;
+
+	error = i8042_command(&param, 0x1059);
+	if (error)
+		pr_warn("Failed to enable DRITEK extension: %d\n", error);
+}
+#endif
+
+#ifdef CONFIG_PM
+
+/*
+ * Here we try to reset everything back to a state we had
+ * before suspending.
+ */
+
+static int i8042_controller_resume(bool s2r_wants_reset)
+{
+	int error;
+
+	error = i8042_controller_check();
+	if (error)
+		return error;
+
+	if (i8042_reset == I8042_RESET_ALWAYS ||
+	    (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
+		error = i8042_controller_selftest();
+		if (error)
+			return error;
+	}
+
+/*
+ * Restore original CTR value and disable all ports
+ */
+
+	i8042_ctr = i8042_initial_ctr;
+	if (i8042_direct)
+		i8042_ctr &= ~I8042_CTR_XLATE;
+	i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS;
+	i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT);
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+		pr_warn("Can't write CTR to resume, retrying...\n");
+		msleep(50);
+		if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+			pr_err("CTR write retry failed\n");
+			return -EIO;
+		}
+	}
+
+
+#ifdef CONFIG_X86
+	if (i8042_dritek)
+		i8042_dritek_enable();
+#endif
+
+	if (i8042_mux_present) {
+		if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports())
+			pr_warn("failed to resume active multiplexor, mouse won't work\n");
+	} else if (i8042_ports[I8042_AUX_PORT_NO].serio)
+		i8042_enable_aux_port();
+
+	if (i8042_ports[I8042_KBD_PORT_NO].serio)
+		i8042_enable_kbd_port();
+
+	i8042_interrupt(0, NULL);
+
+	return 0;
+}
+
+/*
+ * Here we try to restore the original BIOS settings to avoid
+ * upsetting it.
+ */
+
+static int i8042_pm_suspend(struct device *dev)
+{
+	int i;
+
+	if (pm_suspend_via_firmware())
+		i8042_controller_reset(true);
+
+	/* Set up serio interrupts for system wakeup. */
+	for (i = 0; i < I8042_NUM_PORTS; i++) {
+		struct serio *serio = i8042_ports[i].serio;
+
+		if (serio && device_may_wakeup(&serio->dev))
+			enable_irq_wake(i8042_ports[i].irq);
+	}
+
+	return 0;
+}
+
+static int i8042_pm_resume_noirq(struct device *dev)
+{
+	if (!pm_resume_via_firmware())
+		i8042_interrupt(0, NULL);
+
+	return 0;
+}
+
+static int i8042_pm_resume(struct device *dev)
+{
+	bool want_reset;
+	int i;
+
+	for (i = 0; i < I8042_NUM_PORTS; i++) {
+		struct serio *serio = i8042_ports[i].serio;
+
+		if (serio && device_may_wakeup(&serio->dev))
+			disable_irq_wake(i8042_ports[i].irq);
+	}
+
+	/*
+	 * If platform firmware was not going to be involved in suspend, we did
+	 * not restore the controller state to whatever it had been at boot
+	 * time, so we do not need to do anything.
+	 */
+	if (!pm_suspend_via_firmware())
+		return 0;
+
+	/*
+	 * We only need to reset the controller if we are resuming after handing
+	 * off control to the platform firmware, otherwise we can simply restore
+	 * the mode.
+	 */
+	want_reset = pm_resume_via_firmware();
+
+	return i8042_controller_resume(want_reset);
+}
+
+static int i8042_pm_thaw(struct device *dev)
+{
+	i8042_interrupt(0, NULL);
+
+	return 0;
+}
+
+static int i8042_pm_reset(struct device *dev)
+{
+	i8042_controller_reset(false);
+
+	return 0;
+}
+
+static int i8042_pm_restore(struct device *dev)
+{
+	return i8042_controller_resume(false);
+}
+
+static const struct dev_pm_ops i8042_pm_ops = {
+	.suspend	= i8042_pm_suspend,
+	.resume_noirq	= i8042_pm_resume_noirq,
+	.resume		= i8042_pm_resume,
+	.thaw		= i8042_pm_thaw,
+	.poweroff	= i8042_pm_reset,
+	.restore	= i8042_pm_restore,
+};
+
+#endif /* CONFIG_PM */
+
+/*
+ * We need to reset the 8042 back to original mode on system shutdown,
+ * because otherwise BIOSes will be confused.
+ */
+
+static void i8042_shutdown(struct platform_device *dev)
+{
+	i8042_controller_reset(false);
+}
+
+static int __init i8042_create_kbd_port(void)
+{
+	struct serio *serio;
+	struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO];
+
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!serio)
+		return -ENOMEM;
+
+	serio->id.type		= i8042_direct ? SERIO_8042 : SERIO_8042_XL;
+	serio->write		= i8042_dumbkbd ? NULL : i8042_kbd_write;
+	serio->start		= i8042_start;
+	serio->stop		= i8042_stop;
+	serio->close		= i8042_port_close;
+	serio->ps2_cmd_mutex	= &i8042_mutex;
+	serio->port_data	= port;
+	serio->dev.parent	= &i8042_platform_device->dev;
+	strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name));
+	strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys));
+	strlcpy(serio->firmware_id, i8042_kbd_firmware_id,
+		sizeof(serio->firmware_id));
+
+	port->serio = serio;
+	port->irq = I8042_KBD_IRQ;
+
+	return 0;
+}
+
+static int __init i8042_create_aux_port(int idx)
+{
+	struct serio *serio;
+	int port_no = idx < 0 ? I8042_AUX_PORT_NO : I8042_MUX_PORT_NO + idx;
+	struct i8042_port *port = &i8042_ports[port_no];
+
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!serio)
+		return -ENOMEM;
+
+	serio->id.type		= SERIO_8042;
+	serio->write		= i8042_aux_write;
+	serio->start		= i8042_start;
+	serio->stop		= i8042_stop;
+	serio->ps2_cmd_mutex	= &i8042_mutex;
+	serio->port_data	= port;
+	serio->dev.parent	= &i8042_platform_device->dev;
+	if (idx < 0) {
+		strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name));
+		strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
+		strlcpy(serio->firmware_id, i8042_aux_firmware_id,
+			sizeof(serio->firmware_id));
+		serio->close = i8042_port_close;
+	} else {
+		snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx);
+		snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1);
+		strlcpy(serio->firmware_id, i8042_aux_firmware_id,
+			sizeof(serio->firmware_id));
+	}
+
+	port->serio = serio;
+	port->mux = idx;
+	port->irq = I8042_AUX_IRQ;
+
+	return 0;
+}
+
+static void __init i8042_free_kbd_port(void)
+{
+	kfree(i8042_ports[I8042_KBD_PORT_NO].serio);
+	i8042_ports[I8042_KBD_PORT_NO].serio = NULL;
+}
+
+static void __init i8042_free_aux_ports(void)
+{
+	int i;
+
+	for (i = I8042_AUX_PORT_NO; i < I8042_NUM_PORTS; i++) {
+		kfree(i8042_ports[i].serio);
+		i8042_ports[i].serio = NULL;
+	}
+}
+
+static void __init i8042_register_ports(void)
+{
+	int i;
+
+	for (i = 0; i < I8042_NUM_PORTS; i++) {
+		struct serio *serio = i8042_ports[i].serio;
+
+		if (serio) {
+			printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n",
+				serio->name,
+				(unsigned long) I8042_DATA_REG,
+				(unsigned long) I8042_COMMAND_REG,
+				i8042_ports[i].irq);
+			serio_register_port(serio);
+			device_set_wakeup_capable(&serio->dev, true);
+		}
+	}
+}
+
+static void i8042_unregister_ports(void)
+{
+	int i;
+
+	for (i = 0; i < I8042_NUM_PORTS; i++) {
+		if (i8042_ports[i].serio) {
+			serio_unregister_port(i8042_ports[i].serio);
+			i8042_ports[i].serio = NULL;
+		}
+	}
+}
+
+static void i8042_free_irqs(void)
+{
+	if (i8042_aux_irq_registered)
+		free_irq(I8042_AUX_IRQ, i8042_platform_device);
+	if (i8042_kbd_irq_registered)
+		free_irq(I8042_KBD_IRQ, i8042_platform_device);
+
+	i8042_aux_irq_registered = i8042_kbd_irq_registered = false;
+}
+
+static int __init i8042_setup_aux(void)
+{
+	int (*aux_enable)(void);
+	int error;
+	int i;
+
+	if (i8042_check_aux())
+		return -ENODEV;
+
+	if (i8042_nomux || i8042_check_mux()) {
+		error = i8042_create_aux_port(-1);
+		if (error)
+			goto err_free_ports;
+		aux_enable = i8042_enable_aux_port;
+	} else {
+		for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
+			error = i8042_create_aux_port(i);
+			if (error)
+				goto err_free_ports;
+		}
+		aux_enable = i8042_enable_mux_ports;
+	}
+
+	error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED,
+			    "i8042", i8042_platform_device);
+	if (error)
+		goto err_free_ports;
+
+	if (aux_enable())
+		goto err_free_irq;
+
+	i8042_aux_irq_registered = true;
+	return 0;
+
+ err_free_irq:
+	free_irq(I8042_AUX_IRQ, i8042_platform_device);
+ err_free_ports:
+	i8042_free_aux_ports();
+	return error;
+}
+
+static int __init i8042_setup_kbd(void)
+{
+	int error;
+
+	error = i8042_create_kbd_port();
+	if (error)
+		return error;
+
+	error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED,
+			    "i8042", i8042_platform_device);
+	if (error)
+		goto err_free_port;
+
+	error = i8042_enable_kbd_port();
+	if (error)
+		goto err_free_irq;
+
+	i8042_kbd_irq_registered = true;
+	return 0;
+
+ err_free_irq:
+	free_irq(I8042_KBD_IRQ, i8042_platform_device);
+ err_free_port:
+	i8042_free_kbd_port();
+	return error;
+}
+
+static int i8042_kbd_bind_notifier(struct notifier_block *nb,
+				   unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct serio *serio = to_serio_port(dev);
+	struct i8042_port *port = serio->port_data;
+
+	if (serio != i8042_ports[I8042_KBD_PORT_NO].serio)
+		return 0;
+
+	switch (action) {
+	case BUS_NOTIFY_BOUND_DRIVER:
+		port->driver_bound = true;
+		break;
+
+	case BUS_NOTIFY_UNBIND_DRIVER:
+		port->driver_bound = false;
+		break;
+	}
+
+	return 0;
+}
+
+static int __init i8042_probe(struct platform_device *dev)
+{
+	int error;
+
+	i8042_platform_device = dev;
+
+	if (i8042_reset == I8042_RESET_ALWAYS) {
+		error = i8042_controller_selftest();
+		if (error)
+			return error;
+	}
+
+	error = i8042_controller_init();
+	if (error)
+		return error;
+
+#ifdef CONFIG_X86
+	if (i8042_dritek)
+		i8042_dritek_enable();
+#endif
+
+	if (!i8042_noaux) {
+		error = i8042_setup_aux();
+		if (error && error != -ENODEV && error != -EBUSY)
+			goto out_fail;
+	}
+
+	if (!i8042_nokbd) {
+		error = i8042_setup_kbd();
+		if (error)
+			goto out_fail;
+	}
+/*
+ * Ok, everything is ready, let's register all serio ports
+ */
+	i8042_register_ports();
+
+	return 0;
+
+ out_fail:
+	i8042_free_aux_ports();	/* in case KBD failed but AUX not */
+	i8042_free_irqs();
+	i8042_controller_reset(false);
+	i8042_platform_device = NULL;
+
+	return error;
+}
+
+static int i8042_remove(struct platform_device *dev)
+{
+	i8042_unregister_ports();
+	i8042_free_irqs();
+	i8042_controller_reset(false);
+	i8042_platform_device = NULL;
+
+	return 0;
+}
+
+static struct platform_driver i8042_driver = {
+	.driver		= {
+		.name	= "i8042",
+#ifdef CONFIG_PM
+		.pm	= &i8042_pm_ops,
+#endif
+	},
+	.remove		= i8042_remove,
+	.shutdown	= i8042_shutdown,
+};
+
+static struct notifier_block i8042_kbd_bind_notifier_block = {
+	.notifier_call = i8042_kbd_bind_notifier,
+};
+
+static int __init i8042_init(void)
+{
+	struct platform_device *pdev;
+	int err;
+
+	dbg_init();
+
+	err = i8042_platform_init();
+	if (err)
+		return err;
+
+	err = i8042_controller_check();
+	if (err)
+		goto err_platform_exit;
+
+	pdev = platform_create_bundle(&i8042_driver, i8042_probe, NULL, 0, NULL, 0);
+	if (IS_ERR(pdev)) {
+		err = PTR_ERR(pdev);
+		goto err_platform_exit;
+	}
+
+	bus_register_notifier(&serio_bus, &i8042_kbd_bind_notifier_block);
+	panic_blink = i8042_panic_blink;
+
+	return 0;
+
+ err_platform_exit:
+	i8042_platform_exit();
+	return err;
+}
+
+static void __exit i8042_exit(void)
+{
+	platform_device_unregister(i8042_platform_device);
+	platform_driver_unregister(&i8042_driver);
+	i8042_platform_exit();
+
+	bus_unregister_notifier(&serio_bus, &i8042_kbd_bind_notifier_block);
+	panic_blink = NULL;
+}
+
+module_init(i8042_init);
+module_exit(i8042_exit);
diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h
new file mode 100644
index 0000000..1db0a40
--- /dev/null
+++ b/drivers/input/serio/i8042.h
@@ -0,0 +1,98 @@
+#ifndef _I8042_H
+#define _I8042_H
+
+
+/*
+ *  Copyright (c) 1999-2002 Vojtech Pavlik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * Arch-dependent inline functions and defines.
+ */
+
+#if defined(CONFIG_MACH_JAZZ)
+#include "i8042-jazzio.h"
+#elif defined(CONFIG_SGI_HAS_I8042)
+#include "i8042-ip22io.h"
+#elif defined(CONFIG_SNI_RM)
+#include "i8042-snirm.h"
+#elif defined(CONFIG_PPC)
+#include "i8042-ppcio.h"
+#elif defined(CONFIG_SPARC)
+#include "i8042-sparcio.h"
+#elif defined(CONFIG_X86) || defined(CONFIG_IA64)
+#include "i8042-x86ia64io.h"
+#elif defined(CONFIG_UNICORE32)
+#include "i8042-unicore32io.h"
+#else
+#include "i8042-io.h"
+#endif
+
+/*
+ * This is in 50us units, the time we wait for the i8042 to react. This
+ * has to be long enough for the i8042 itself to timeout on sending a byte
+ * to a non-existent mouse.
+ */
+
+#define I8042_CTL_TIMEOUT	10000
+
+/*
+ * Return codes.
+ */
+
+#define I8042_RET_CTL_TEST	0x55
+
+/*
+ * Expected maximum internal i8042 buffer size. This is used for flushing
+ * the i8042 buffers.
+ */
+
+#define I8042_BUFFER_SIZE	16
+
+/*
+ * Number of AUX ports on controllers supporting active multiplexing
+ * specification
+ */
+
+#define I8042_NUM_MUX_PORTS	4
+
+/*
+ * Debug.
+ */
+
+#ifdef DEBUG
+static unsigned long i8042_start_time;
+#define dbg_init() do { i8042_start_time = jiffies; } while (0)
+#define dbg(format, arg...)							\
+	do {									\
+		if (i8042_debug)						\
+			printk(KERN_DEBUG KBUILD_MODNAME ": [%d] " format,	\
+			       (int) (jiffies - i8042_start_time), ##arg);	\
+	} while (0)
+
+#define filter_dbg(filter, data, format, args...)		\
+	do {							\
+		if (!i8042_debug)				\
+			break;					\
+								\
+		if (!filter || i8042_unmask_kbd_data)		\
+			dbg("%02x " format, data, ##args);	\
+		else						\
+			dbg("** " format, ##args);		\
+	} while (0)
+#else
+#define dbg_init() do { } while (0)
+#define dbg(format, arg...)							\
+	do {									\
+		if (0)								\
+			printk(KERN_DEBUG pr_fmt(format), ##arg);		\
+	} while (0)
+
+#define filter_dbg(filter, data, format, args...) do { } while (0)
+#endif
+
+#endif /* _I8042_H */
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
new file mode 100644
index 0000000..83e9c66
--- /dev/null
+++ b/drivers/input/serio/libps2.c
@@ -0,0 +1,378 @@
+/*
+ * PS/2 driver library
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 2004 Dmitry Torokhov
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/i8042.h>
+#include <linux/libps2.h>
+
+#define DRIVER_DESC	"PS/2 driver library"
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION("PS/2 driver library");
+MODULE_LICENSE("GPL");
+
+/*
+ * ps2_sendbyte() sends a byte to the device and waits for acknowledge.
+ * It doesn't handle retransmission, though it could - because if there
+ * is a need for retransmissions device has to be replaced anyway.
+ *
+ * ps2_sendbyte() can only be called from a process context.
+ */
+
+int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout)
+{
+	serio_pause_rx(ps2dev->serio);
+	ps2dev->nak = 1;
+	ps2dev->flags |= PS2_FLAG_ACK;
+	serio_continue_rx(ps2dev->serio);
+
+	if (serio_write(ps2dev->serio, byte) == 0)
+		wait_event_timeout(ps2dev->wait,
+				   !(ps2dev->flags & PS2_FLAG_ACK),
+				   msecs_to_jiffies(timeout));
+
+	serio_pause_rx(ps2dev->serio);
+	ps2dev->flags &= ~PS2_FLAG_ACK;
+	serio_continue_rx(ps2dev->serio);
+
+	return -ps2dev->nak;
+}
+EXPORT_SYMBOL(ps2_sendbyte);
+
+void ps2_begin_command(struct ps2dev *ps2dev)
+{
+	struct mutex *m = ps2dev->serio->ps2_cmd_mutex ?: &ps2dev->cmd_mutex;
+
+	mutex_lock(m);
+}
+EXPORT_SYMBOL(ps2_begin_command);
+
+void ps2_end_command(struct ps2dev *ps2dev)
+{
+	struct mutex *m = ps2dev->serio->ps2_cmd_mutex ?: &ps2dev->cmd_mutex;
+
+	mutex_unlock(m);
+}
+EXPORT_SYMBOL(ps2_end_command);
+
+/*
+ * ps2_drain() waits for device to transmit requested number of bytes
+ * and discards them.
+ */
+
+void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
+{
+	if (maxbytes > sizeof(ps2dev->cmdbuf)) {
+		WARN_ON(1);
+		maxbytes = sizeof(ps2dev->cmdbuf);
+	}
+
+	ps2_begin_command(ps2dev);
+
+	serio_pause_rx(ps2dev->serio);
+	ps2dev->flags = PS2_FLAG_CMD;
+	ps2dev->cmdcnt = maxbytes;
+	serio_continue_rx(ps2dev->serio);
+
+	wait_event_timeout(ps2dev->wait,
+			   !(ps2dev->flags & PS2_FLAG_CMD),
+			   msecs_to_jiffies(timeout));
+
+	ps2_end_command(ps2dev);
+}
+EXPORT_SYMBOL(ps2_drain);
+
+/*
+ * ps2_is_keyboard_id() checks received ID byte against the list of
+ * known keyboard IDs.
+ */
+
+int ps2_is_keyboard_id(char id_byte)
+{
+	static const char keyboard_ids[] = {
+		0xab,	/* Regular keyboards		*/
+		0xac,	/* NCD Sun keyboard		*/
+		0x2b,	/* Trust keyboard, translated	*/
+		0x5d,	/* Trust keyboard		*/
+		0x60,	/* NMB SGI keyboard, translated */
+		0x47,	/* NMB SGI keyboard		*/
+	};
+
+	return memchr(keyboard_ids, id_byte, sizeof(keyboard_ids)) != NULL;
+}
+EXPORT_SYMBOL(ps2_is_keyboard_id);
+
+/*
+ * ps2_adjust_timeout() is called after receiving 1st byte of command
+ * response and tries to reduce remaining timeout to speed up command
+ * completion.
+ */
+
+static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout)
+{
+	switch (command) {
+		case PS2_CMD_RESET_BAT:
+			/*
+			 * Device has sent the first response byte after
+			 * reset command, reset is thus done, so we can
+			 * shorten the timeout.
+			 * The next byte will come soon (keyboard) or not
+			 * at all (mouse).
+			 */
+			if (timeout > msecs_to_jiffies(100))
+				timeout = msecs_to_jiffies(100);
+			break;
+
+		case PS2_CMD_GETID:
+			/*
+			 * Microsoft Natural Elite keyboard responds to
+			 * the GET ID command as it were a mouse, with
+			 * a single byte. Fail the command so atkbd will
+			 * use alternative probe to detect it.
+			 */
+			if (ps2dev->cmdbuf[1] == 0xaa) {
+				serio_pause_rx(ps2dev->serio);
+				ps2dev->flags = 0;
+				serio_continue_rx(ps2dev->serio);
+				timeout = 0;
+			}
+
+			/*
+			 * If device behind the port is not a keyboard there
+			 * won't be 2nd byte of ID response.
+			 */
+			if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) {
+				serio_pause_rx(ps2dev->serio);
+				ps2dev->flags = ps2dev->cmdcnt = 0;
+				serio_continue_rx(ps2dev->serio);
+				timeout = 0;
+			}
+			break;
+
+		default:
+			break;
+	}
+
+	return timeout;
+}
+
+/*
+ * ps2_command() sends a command and its parameters to the mouse,
+ * then waits for the response and puts it in the param array.
+ *
+ * ps2_command() can only be called from a process context
+ */
+
+int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
+{
+	int timeout;
+	int send = (command >> 12) & 0xf;
+	int receive = (command >> 8) & 0xf;
+	int rc = -1;
+	int i;
+
+	if (receive > sizeof(ps2dev->cmdbuf)) {
+		WARN_ON(1);
+		return -1;
+	}
+
+	if (send && !param) {
+		WARN_ON(1);
+		return -1;
+	}
+
+	serio_pause_rx(ps2dev->serio);
+	ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
+	ps2dev->cmdcnt = receive;
+	if (receive && param)
+		for (i = 0; i < receive; i++)
+			ps2dev->cmdbuf[(receive - 1) - i] = param[i];
+	serio_continue_rx(ps2dev->serio);
+
+	/*
+	 * Some devices (Synaptics) peform the reset before
+	 * ACKing the reset command, and so it can take a long
+	 * time before the ACK arrives.
+	 */
+	if (ps2_sendbyte(ps2dev, command & 0xff,
+			 command == PS2_CMD_RESET_BAT ? 1000 : 200)) {
+		serio_pause_rx(ps2dev->serio);
+		goto out_reset_flags;
+	}
+
+	for (i = 0; i < send; i++) {
+		if (ps2_sendbyte(ps2dev, param[i], 200)) {
+			serio_pause_rx(ps2dev->serio);
+			goto out_reset_flags;
+		}
+	}
+
+	/*
+	 * The reset command takes a long time to execute.
+	 */
+	timeout = msecs_to_jiffies(command == PS2_CMD_RESET_BAT ? 4000 : 500);
+
+	timeout = wait_event_timeout(ps2dev->wait,
+				     !(ps2dev->flags & PS2_FLAG_CMD1), timeout);
+
+	if (ps2dev->cmdcnt && !(ps2dev->flags & PS2_FLAG_CMD1)) {
+
+		timeout = ps2_adjust_timeout(ps2dev, command, timeout);
+		wait_event_timeout(ps2dev->wait,
+				   !(ps2dev->flags & PS2_FLAG_CMD), timeout);
+	}
+
+	serio_pause_rx(ps2dev->serio);
+
+	if (param)
+		for (i = 0; i < receive; i++)
+			param[i] = ps2dev->cmdbuf[(receive - 1) - i];
+
+	if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1))
+		goto out_reset_flags;
+
+	rc = 0;
+
+ out_reset_flags:
+	ps2dev->flags = 0;
+	serio_continue_rx(ps2dev->serio);
+
+	return rc;
+}
+EXPORT_SYMBOL(__ps2_command);
+
+int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
+{
+	int rc;
+
+	ps2_begin_command(ps2dev);
+	rc = __ps2_command(ps2dev, param, command);
+	ps2_end_command(ps2dev);
+
+	return rc;
+}
+EXPORT_SYMBOL(ps2_command);
+
+/*
+ * ps2_init() initializes ps2dev structure
+ */
+
+void ps2_init(struct ps2dev *ps2dev, struct serio *serio)
+{
+	mutex_init(&ps2dev->cmd_mutex);
+	lockdep_set_subclass(&ps2dev->cmd_mutex, serio->depth);
+	init_waitqueue_head(&ps2dev->wait);
+	ps2dev->serio = serio;
+}
+EXPORT_SYMBOL(ps2_init);
+
+/*
+ * ps2_handle_ack() is supposed to be used in interrupt handler
+ * to properly process ACK/NAK of a command from a PS/2 device.
+ */
+
+int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
+{
+	switch (data) {
+		case PS2_RET_ACK:
+			ps2dev->nak = 0;
+			break;
+
+		case PS2_RET_NAK:
+			ps2dev->flags |= PS2_FLAG_NAK;
+			ps2dev->nak = PS2_RET_NAK;
+			break;
+
+		case PS2_RET_ERR:
+			if (ps2dev->flags & PS2_FLAG_NAK) {
+				ps2dev->flags &= ~PS2_FLAG_NAK;
+				ps2dev->nak = PS2_RET_ERR;
+				break;
+			}
+
+		/*
+		 * Workaround for mice which don't ACK the Get ID command.
+		 * These are valid mouse IDs that we recognize.
+		 */
+		case 0x00:
+		case 0x03:
+		case 0x04:
+			if (ps2dev->flags & PS2_FLAG_WAITID) {
+				ps2dev->nak = 0;
+				break;
+			}
+			/* Fall through */
+		default:
+			return 0;
+	}
+
+
+	if (!ps2dev->nak) {
+		ps2dev->flags &= ~PS2_FLAG_NAK;
+		if (ps2dev->cmdcnt)
+			ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1;
+	}
+
+	ps2dev->flags &= ~PS2_FLAG_ACK;
+	wake_up(&ps2dev->wait);
+
+	if (data != PS2_RET_ACK)
+		ps2_handle_response(ps2dev, data);
+
+	return 1;
+}
+EXPORT_SYMBOL(ps2_handle_ack);
+
+/*
+ * ps2_handle_response() is supposed to be used in interrupt handler
+ * to properly store device's response to a command and notify process
+ * waiting for completion of the command.
+ */
+
+int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data)
+{
+	if (ps2dev->cmdcnt)
+		ps2dev->cmdbuf[--ps2dev->cmdcnt] = data;
+
+	if (ps2dev->flags & PS2_FLAG_CMD1) {
+		ps2dev->flags &= ~PS2_FLAG_CMD1;
+		if (ps2dev->cmdcnt)
+			wake_up(&ps2dev->wait);
+	}
+
+	if (!ps2dev->cmdcnt) {
+		ps2dev->flags &= ~PS2_FLAG_CMD;
+		wake_up(&ps2dev->wait);
+	}
+
+	return 1;
+}
+EXPORT_SYMBOL(ps2_handle_response);
+
+void ps2_cmd_aborted(struct ps2dev *ps2dev)
+{
+	if (ps2dev->flags & PS2_FLAG_ACK)
+		ps2dev->nak = 1;
+
+	if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD))
+		wake_up(&ps2dev->wait);
+
+	/* reset all flags except last nack */
+	ps2dev->flags &= PS2_FLAG_NAK;
+}
+EXPORT_SYMBOL(ps2_cmd_aborted);
diff --git a/drivers/input/serio/maceps2.c b/drivers/input/serio/maceps2.c
new file mode 100644
index 0000000..e365c5f
--- /dev/null
+++ b/drivers/input/serio/maceps2.c
@@ -0,0 +1,209 @@
+/*
+ * SGI O2 MACE PS2 controller driver for linux
+ *
+ * Copyright (C) 2002 Vivien Chappelier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ip32/mace.h>
+#include <asm/ip32/ip32_ints.h>
+
+MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org");
+MODULE_DESCRIPTION("SGI O2 MACE PS2 controller driver");
+MODULE_LICENSE("GPL");
+
+#define MACE_PS2_TIMEOUT 10000 /* in 50us unit */
+
+#define PS2_STATUS_CLOCK_SIGNAL  BIT(0) /* external clock signal */
+#define PS2_STATUS_CLOCK_INHIBIT BIT(1) /* clken output signal */
+#define PS2_STATUS_TX_INPROGRESS BIT(2) /* transmission in progress */
+#define PS2_STATUS_TX_EMPTY      BIT(3) /* empty transmit buffer */
+#define PS2_STATUS_RX_FULL       BIT(4) /* full receive buffer */
+#define PS2_STATUS_RX_INPROGRESS BIT(5) /* reception in progress */
+#define PS2_STATUS_ERROR_PARITY  BIT(6) /* parity error */
+#define PS2_STATUS_ERROR_FRAMING BIT(7) /* framing error */
+
+#define PS2_CONTROL_TX_CLOCK_DISABLE BIT(0) /* inhibit clock signal after TX */
+#define PS2_CONTROL_TX_ENABLE        BIT(1) /* transmit enable */
+#define PS2_CONTROL_TX_INT_ENABLE    BIT(2) /* enable transmit interrupt */
+#define PS2_CONTROL_RX_INT_ENABLE    BIT(3) /* enable receive interrupt */
+#define PS2_CONTROL_RX_CLOCK_ENABLE  BIT(4) /* pause reception if set to 0 */
+#define PS2_CONTROL_RESET            BIT(5) /* reset */
+
+struct maceps2_data {
+	struct mace_ps2port *port;
+	int irq;
+};
+
+static struct maceps2_data port_data[2];
+static struct serio *maceps2_port[2];
+static struct platform_device *maceps2_device;
+
+static int maceps2_write(struct serio *dev, unsigned char val)
+{
+	struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port;
+	unsigned int timeout = MACE_PS2_TIMEOUT;
+
+	do {
+		if (port->status & PS2_STATUS_TX_EMPTY) {
+			port->tx = val;
+			return 0;
+		}
+		udelay(50);
+	} while (timeout--);
+
+	return -1;
+}
+
+static irqreturn_t maceps2_interrupt(int irq, void *dev_id)
+{
+	struct serio *dev = dev_id;
+	struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port;
+	unsigned long byte;
+
+	if (port->status & PS2_STATUS_RX_FULL) {
+		byte = port->rx;
+		serio_interrupt(dev, byte & 0xff, 0);
+        }
+
+	return IRQ_HANDLED;
+}
+
+static int maceps2_open(struct serio *dev)
+{
+	struct maceps2_data *data = (struct maceps2_data *)dev->port_data;
+
+	if (request_irq(data->irq, maceps2_interrupt, 0, "PS2 port", dev)) {
+		printk(KERN_ERR "Could not allocate PS/2 IRQ\n");
+		return -EBUSY;
+	}
+
+	/* Reset port */
+	data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET;
+	udelay(100);
+
+        /* Enable interrupts */
+	data->port->control = PS2_CONTROL_RX_CLOCK_ENABLE |
+			      PS2_CONTROL_TX_ENABLE |
+			      PS2_CONTROL_RX_INT_ENABLE;
+
+	return 0;
+}
+
+static void maceps2_close(struct serio *dev)
+{
+	struct maceps2_data *data = (struct maceps2_data *)dev->port_data;
+
+	data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET;
+	udelay(100);
+	free_irq(data->irq, dev);
+}
+
+
+static struct serio *maceps2_allocate_port(int idx)
+{
+	struct serio *serio;
+
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (serio) {
+		serio->id.type		= SERIO_8042;
+		serio->write		= maceps2_write;
+		serio->open		= maceps2_open;
+		serio->close		= maceps2_close;
+		snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx);
+		snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx);
+		serio->port_data	= &port_data[idx];
+		serio->dev.parent	= &maceps2_device->dev;
+	}
+
+	return serio;
+}
+
+static int maceps2_probe(struct platform_device *dev)
+{
+	maceps2_port[0] = maceps2_allocate_port(0);
+	maceps2_port[1] = maceps2_allocate_port(1);
+	if (!maceps2_port[0] || !maceps2_port[1]) {
+		kfree(maceps2_port[0]);
+		kfree(maceps2_port[1]);
+		return -ENOMEM;
+	}
+
+	serio_register_port(maceps2_port[0]);
+	serio_register_port(maceps2_port[1]);
+
+	return 0;
+}
+
+static int maceps2_remove(struct platform_device *dev)
+{
+	serio_unregister_port(maceps2_port[0]);
+	serio_unregister_port(maceps2_port[1]);
+
+	return 0;
+}
+
+static struct platform_driver maceps2_driver = {
+	.driver		= {
+		.name	= "maceps2",
+	},
+	.probe		= maceps2_probe,
+	.remove		= maceps2_remove,
+};
+
+static int __init maceps2_init(void)
+{
+	int error;
+
+	error = platform_driver_register(&maceps2_driver);
+	if (error)
+		return error;
+
+	maceps2_device = platform_device_alloc("maceps2", -1);
+	if (!maceps2_device) {
+		error = -ENOMEM;
+		goto err_unregister_driver;
+	}
+
+	port_data[0].port = &mace->perif.ps2.keyb;
+	port_data[0].irq  = MACEISA_KEYB_IRQ;
+	port_data[1].port = &mace->perif.ps2.mouse;
+	port_data[1].irq  = MACEISA_MOUSE_IRQ;
+
+	error = platform_device_add(maceps2_device);
+	if (error)
+		goto err_free_device;
+
+	return 0;
+
+ err_free_device:
+	platform_device_put(maceps2_device);
+ err_unregister_driver:
+	platform_driver_unregister(&maceps2_driver);
+	return error;
+}
+
+static void __exit maceps2_exit(void)
+{
+	platform_device_unregister(maceps2_device);
+	platform_driver_unregister(&maceps2_driver);
+}
+
+module_init(maceps2_init);
+module_exit(maceps2_exit);
diff --git a/drivers/input/serio/olpc_apsp.c b/drivers/input/serio/olpc_apsp.c
new file mode 100644
index 0000000..8e9a420
--- /dev/null
+++ b/drivers/input/serio/olpc_apsp.c
@@ -0,0 +1,282 @@
+/*
+ * OLPC serio driver for multiplexed input from Marvell MMP security processor
+ *
+ * Copyright (C) 2011-2013 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/serio.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+/*
+ * The OLPC XO-1.75 and XO-4 laptops do not have a hardware PS/2 controller.
+ * Instead, the OLPC firmware runs a bit-banging PS/2 implementation on an
+ * otherwise-unused slow processor which is included in the Marvell MMP2/MMP3
+ * SoC, known as the "Security Processor" (SP) or "Wireless Trusted Module"
+ * (WTM). This firmware then reports its results via the WTM registers,
+ * which we read from the Application Processor (AP, i.e. main CPU) in this
+ * driver.
+ *
+ * On the hardware side we have a PS/2 mouse and an AT keyboard, the data
+ * is multiplexed through this system. We create a serio port for each one,
+ * and demultiplex the data accordingly.
+ */
+
+/* WTM register offsets */
+#define SECURE_PROCESSOR_COMMAND	0x40
+#define COMMAND_RETURN_STATUS		0x80
+#define COMMAND_FIFO_STATUS		0xc4
+#define PJ_RST_INTERRUPT		0xc8
+#define PJ_INTERRUPT_MASK		0xcc
+
+/*
+ * The upper byte of SECURE_PROCESSOR_COMMAND and COMMAND_RETURN_STATUS is
+ * used to identify which port (device) is being talked to. The lower byte
+ * is the data being sent/received.
+ */
+#define PORT_MASK	0xff00
+#define DATA_MASK	0x00ff
+#define PORT_SHIFT	8
+#define KEYBOARD_PORT	0
+#define TOUCHPAD_PORT	1
+
+/* COMMAND_FIFO_STATUS */
+#define CMD_CNTR_MASK		0x7 /* Number of pending/unprocessed commands */
+#define MAX_PENDING_CMDS	4   /* from device specs */
+
+/* PJ_RST_INTERRUPT */
+#define SP_COMMAND_COMPLETE_RESET	0x1
+
+/* PJ_INTERRUPT_MASK */
+#define INT_0	(1 << 0)
+
+/* COMMAND_FIFO_STATUS */
+#define CMD_STS_MASK	0x100
+
+struct olpc_apsp {
+	struct device *dev;
+	struct serio *kbio;
+	struct serio *padio;
+	void __iomem *base;
+	int open_count;
+	int irq;
+};
+
+static int olpc_apsp_write(struct serio *port, unsigned char val)
+{
+	struct olpc_apsp *priv = port->port_data;
+	unsigned int i;
+	u32 which = 0;
+
+	if (port == priv->padio)
+		which = TOUCHPAD_PORT << PORT_SHIFT;
+	else
+		which = KEYBOARD_PORT << PORT_SHIFT;
+
+	dev_dbg(priv->dev, "olpc_apsp_write which=%x val=%x\n", which, val);
+	for (i = 0; i < 50; i++) {
+		u32 sts = readl(priv->base + COMMAND_FIFO_STATUS);
+		if ((sts & CMD_CNTR_MASK) < MAX_PENDING_CMDS) {
+			writel(which | val,
+			       priv->base + SECURE_PROCESSOR_COMMAND);
+			return 0;
+		}
+		/* SP busy. This has not been seen in practice. */
+		mdelay(1);
+	}
+
+	dev_dbg(priv->dev, "olpc_apsp_write timeout, status=%x\n",
+		readl(priv->base + COMMAND_FIFO_STATUS));
+
+	return -ETIMEDOUT;
+}
+
+static irqreturn_t olpc_apsp_rx(int irq, void *dev_id)
+{
+	struct olpc_apsp *priv = dev_id;
+	unsigned int w, tmp;
+	struct serio *serio;
+
+	/*
+	 * Write 1 to PJ_RST_INTERRUPT to acknowledge and clear the interrupt
+	 * Write 0xff00 to SECURE_PROCESSOR_COMMAND.
+	 */
+	tmp = readl(priv->base + PJ_RST_INTERRUPT);
+	if (!(tmp & SP_COMMAND_COMPLETE_RESET)) {
+		dev_warn(priv->dev, "spurious interrupt?\n");
+		return IRQ_NONE;
+	}
+
+	w = readl(priv->base + COMMAND_RETURN_STATUS);
+	dev_dbg(priv->dev, "olpc_apsp_rx %x\n", w);
+
+	if (w >> PORT_SHIFT == KEYBOARD_PORT)
+		serio = priv->kbio;
+	else
+		serio = priv->padio;
+
+	serio_interrupt(serio, w & DATA_MASK, 0);
+
+	/* Ack and clear interrupt */
+	writel(tmp | SP_COMMAND_COMPLETE_RESET, priv->base + PJ_RST_INTERRUPT);
+	writel(PORT_MASK, priv->base + SECURE_PROCESSOR_COMMAND);
+
+	pm_wakeup_event(priv->dev, 1000);
+	return IRQ_HANDLED;
+}
+
+static int olpc_apsp_open(struct serio *port)
+{
+	struct olpc_apsp *priv = port->port_data;
+	unsigned int tmp;
+
+	if (priv->open_count++ == 0) {
+		/* Enable interrupt 0 by clearing its bit */
+		tmp = readl(priv->base + PJ_INTERRUPT_MASK);
+		writel(tmp & ~INT_0, priv->base + PJ_INTERRUPT_MASK);
+	}
+
+	return 0;
+}
+
+static void olpc_apsp_close(struct serio *port)
+{
+	struct olpc_apsp *priv = port->port_data;
+	unsigned int tmp;
+
+	if (--priv->open_count == 0) {
+		/* Disable interrupt 0 */
+		tmp = readl(priv->base + PJ_INTERRUPT_MASK);
+		writel(tmp | INT_0, priv->base + PJ_INTERRUPT_MASK);
+	}
+}
+
+static int olpc_apsp_probe(struct platform_device *pdev)
+{
+	struct serio *kb_serio, *pad_serio;
+	struct olpc_apsp *priv;
+	struct resource *res;
+	struct device_node *np;
+	unsigned long l;
+	int error;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(struct olpc_apsp), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	np = pdev->dev.of_node;
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->base)) {
+		dev_err(&pdev->dev, "Failed to map WTM registers\n");
+		return PTR_ERR(priv->base);
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (priv->irq < 0)
+		return priv->irq;
+
+	l = readl(priv->base + COMMAND_FIFO_STATUS);
+	if (!(l & CMD_STS_MASK)) {
+		dev_err(&pdev->dev, "SP cannot accept commands.\n");
+		return -EIO;
+	}
+
+	/* KEYBOARD */
+	kb_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!kb_serio)
+		return -ENOMEM;
+	kb_serio->id.type	= SERIO_8042_XL;
+	kb_serio->write		= olpc_apsp_write;
+	kb_serio->open		= olpc_apsp_open;
+	kb_serio->close		= olpc_apsp_close;
+	kb_serio->port_data	= priv;
+	kb_serio->dev.parent	= &pdev->dev;
+	strlcpy(kb_serio->name, "sp keyboard", sizeof(kb_serio->name));
+	strlcpy(kb_serio->phys, "sp/serio0", sizeof(kb_serio->phys));
+	priv->kbio		= kb_serio;
+	serio_register_port(kb_serio);
+
+	/* TOUCHPAD */
+	pad_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!pad_serio) {
+		error = -ENOMEM;
+		goto err_pad;
+	}
+	pad_serio->id.type	= SERIO_8042;
+	pad_serio->write	= olpc_apsp_write;
+	pad_serio->open		= olpc_apsp_open;
+	pad_serio->close	= olpc_apsp_close;
+	pad_serio->port_data	= priv;
+	pad_serio->dev.parent	= &pdev->dev;
+	strlcpy(pad_serio->name, "sp touchpad", sizeof(pad_serio->name));
+	strlcpy(pad_serio->phys, "sp/serio1", sizeof(pad_serio->phys));
+	priv->padio		= pad_serio;
+	serio_register_port(pad_serio);
+
+	error = request_irq(priv->irq, olpc_apsp_rx, 0, "olpc-apsp", priv);
+	if (error) {
+		dev_err(&pdev->dev, "Failed to request IRQ\n");
+		goto err_irq;
+	}
+
+	priv->dev = &pdev->dev;
+	device_init_wakeup(priv->dev, 1);
+	platform_set_drvdata(pdev, priv);
+
+	dev_dbg(&pdev->dev, "probed successfully.\n");
+	return 0;
+
+err_irq:
+	serio_unregister_port(pad_serio);
+err_pad:
+	serio_unregister_port(kb_serio);
+	return error;
+}
+
+static int olpc_apsp_remove(struct platform_device *pdev)
+{
+	struct olpc_apsp *priv = platform_get_drvdata(pdev);
+
+	free_irq(priv->irq, priv);
+
+	serio_unregister_port(priv->kbio);
+	serio_unregister_port(priv->padio);
+
+	return 0;
+}
+
+static const struct of_device_id olpc_apsp_dt_ids[] = {
+	{ .compatible = "olpc,ap-sp", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, olpc_apsp_dt_ids);
+
+static struct platform_driver olpc_apsp_driver = {
+	.probe		= olpc_apsp_probe,
+	.remove		= olpc_apsp_remove,
+	.driver		= {
+		.name	= "olpc-apsp",
+		.of_match_table = olpc_apsp_dt_ids,
+	},
+};
+
+MODULE_DESCRIPTION("OLPC AP-SP serio driver");
+MODULE_LICENSE("GPL");
+module_platform_driver(olpc_apsp_driver);
diff --git a/drivers/input/serio/parkbd.c b/drivers/input/serio/parkbd.c
new file mode 100644
index 0000000..1edfac7
--- /dev/null
+++ b/drivers/input/serio/parkbd.c
@@ -0,0 +1,239 @@
+/*
+ *  Parallel port to Keyboard port adapter driver for Linux
+ *
+ *  Copyright (c) 1999-2004 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * To connect an AT or XT keyboard to the parallel port, a fairly simple adapter
+ * can be made:
+ * 
+ *  Parallel port            Keyboard port
+ *
+ *     +5V --------------------- +5V (4)
+ *  
+ *                 ______
+ *     +5V -------|______|--.
+ *                          |
+ *     ACK (10) ------------|
+ *                          |--- KBD CLOCK (5)
+ *     STROBE (1) ---|<|----'
+ *     
+ *                 ______
+ *     +5V -------|______|--.
+ *                          |
+ *     BUSY (11) -----------|
+ *                          |--- KBD DATA (1)
+ *     AUTOFD (14) --|<|----'
+ *
+ *     GND (18-25) ------------- GND (3)
+ *     
+ * The diodes can be fairly any type, and the resistors should be somewhere
+ * around 5 kOhm, but the adapter will likely work without the resistors,
+ * too.
+ *
+ * The +5V source can be taken either from USB, from mouse or keyboard ports,
+ * or from a joystick port. Unfortunately, the parallel port of a PC doesn't
+ * have a +5V pin, and feeding the keyboard from signal pins is out of question
+ * with 300 mA power reqirement of a typical AT keyboard.
+ */
+
+#include <linux/module.h>
+#include <linux/parport.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Parallel port to Keyboard port adapter driver");
+MODULE_LICENSE("GPL");
+
+static unsigned int parkbd_pp_no;
+module_param_named(port, parkbd_pp_no, int, 0);
+MODULE_PARM_DESC(port, "Parallel port the adapter is connected to (default is 0)");
+
+static unsigned int parkbd_mode = SERIO_8042;
+module_param_named(mode, parkbd_mode, uint, 0);
+MODULE_PARM_DESC(mode, "Mode of operation: XT = 0/AT = 1 (default)");
+
+#define PARKBD_CLOCK	0x01	/* Strobe & Ack */
+#define PARKBD_DATA	0x02	/* AutoFd & Busy */
+
+static int parkbd_buffer;
+static int parkbd_counter;
+static unsigned long parkbd_last;
+static int parkbd_writing;
+static unsigned long parkbd_start;
+
+static struct pardevice *parkbd_dev;
+static struct serio *parkbd_port;
+
+static int parkbd_readlines(void)
+{
+	return (parport_read_status(parkbd_dev->port) >> 6) ^ 2;
+}
+
+static void parkbd_writelines(int data)
+{
+	parport_write_control(parkbd_dev->port, (~data & 3) | 0x10);
+}
+
+static int parkbd_write(struct serio *port, unsigned char c)
+{
+	unsigned char p;
+
+	if (!parkbd_mode) return -1;
+
+        p = c ^ (c >> 4);
+	p = p ^ (p >> 2);
+	p = p ^ (p >> 1);
+
+	parkbd_counter = 0;
+	parkbd_writing = 1;
+	parkbd_buffer = c | (((int) (~p & 1)) << 8) | 0x600;
+
+	parkbd_writelines(2);
+
+	return 0;
+}
+
+static void parkbd_interrupt(void *dev_id)
+{
+
+	if (parkbd_writing) {
+
+		if (parkbd_counter && ((parkbd_counter == 11) || time_after(jiffies, parkbd_last + HZ/100))) {
+			parkbd_counter = 0;
+			parkbd_buffer = 0;
+			parkbd_writing = 0;
+			parkbd_writelines(3);
+			return;
+		}
+
+		parkbd_writelines(((parkbd_buffer >> parkbd_counter++) & 1) | 2);
+
+		if (parkbd_counter == 11) {
+			parkbd_counter = 0;
+			parkbd_buffer = 0;
+			parkbd_writing = 0;
+			parkbd_writelines(3);
+		}
+
+	} else {
+
+		if ((parkbd_counter == parkbd_mode + 10) || time_after(jiffies, parkbd_last + HZ/100)) {
+			parkbd_counter = 0;
+			parkbd_buffer = 0;
+		}
+
+		parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++;
+
+		if (parkbd_counter == parkbd_mode + 10)
+			serio_interrupt(parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0);
+	}
+
+	parkbd_last = jiffies;
+}
+
+static int parkbd_getport(struct parport *pp)
+{
+	struct pardev_cb parkbd_parport_cb;
+
+	memset(&parkbd_parport_cb, 0, sizeof(parkbd_parport_cb));
+	parkbd_parport_cb.irq_func = parkbd_interrupt;
+	parkbd_parport_cb.flags = PARPORT_FLAG_EXCL;
+
+	parkbd_dev = parport_register_dev_model(pp, "parkbd",
+						&parkbd_parport_cb, 0);
+
+	if (!parkbd_dev)
+		return -ENODEV;
+
+	if (parport_claim(parkbd_dev)) {
+		parport_unregister_device(parkbd_dev);
+		return -EBUSY;
+	}
+
+	parkbd_start = jiffies;
+
+	return 0;
+}
+
+static struct serio *parkbd_allocate_serio(void)
+{
+	struct serio *serio;
+
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (serio) {
+		serio->id.type = parkbd_mode;
+		serio->write = parkbd_write,
+		strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name));
+		snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", parkbd_dev->port->name);
+	}
+
+	return serio;
+}
+
+static void parkbd_attach(struct parport *pp)
+{
+	if (pp->number != parkbd_pp_no) {
+		pr_debug("Not using parport%d.\n", pp->number);
+		return;
+	}
+
+	if (parkbd_getport(pp))
+		return;
+
+	parkbd_port = parkbd_allocate_serio();
+	if (!parkbd_port) {
+		parport_release(parkbd_dev);
+		parport_unregister_device(parkbd_dev);
+		return;
+	}
+
+	parkbd_writelines(3);
+
+	serio_register_port(parkbd_port);
+
+	printk(KERN_INFO "serio: PARKBD %s adapter on %s\n",
+                        parkbd_mode ? "AT" : "XT", parkbd_dev->port->name);
+
+	return;
+}
+
+static void parkbd_detach(struct parport *port)
+{
+	if (!parkbd_port || port->number != parkbd_pp_no)
+		return;
+
+	parport_release(parkbd_dev);
+	serio_unregister_port(parkbd_port);
+	parport_unregister_device(parkbd_dev);
+	parkbd_port = NULL;
+}
+
+static struct parport_driver parkbd_parport_driver = {
+	.name = "parkbd",
+	.match_port = parkbd_attach,
+	.detach = parkbd_detach,
+	.devmodel = true,
+};
+
+static int __init parkbd_init(void)
+{
+	return parport_register_driver(&parkbd_parport_driver);
+}
+
+static void __exit parkbd_exit(void)
+{
+	parport_unregister_driver(&parkbd_parport_driver);
+}
+
+module_init(parkbd_init);
+module_exit(parkbd_exit);
diff --git a/drivers/input/serio/pcips2.c b/drivers/input/serio/pcips2.c
new file mode 100644
index 0000000..e862c6e
--- /dev/null
+++ b/drivers/input/serio/pcips2.c
@@ -0,0 +1,220 @@
+/*
+ * linux/drivers/input/serio/pcips2.c
+ *
+ *  Copyright (C) 2003 Russell King, All Rights Reserved.
+ *
+ * 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.
+ *
+ *  I'm not sure if this is a generic PS/2 PCI interface or specific to
+ *  the Mobility Electronics docking station.
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/input.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/serio.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#define PS2_CTRL		(0)
+#define PS2_STATUS		(1)
+#define PS2_DATA		(2)
+
+#define PS2_CTRL_CLK		(1<<0)
+#define PS2_CTRL_DAT		(1<<1)
+#define PS2_CTRL_TXIRQ		(1<<2)
+#define PS2_CTRL_ENABLE		(1<<3)
+#define PS2_CTRL_RXIRQ		(1<<4)
+
+#define PS2_STAT_CLK		(1<<0)
+#define PS2_STAT_DAT		(1<<1)
+#define PS2_STAT_PARITY		(1<<2)
+#define PS2_STAT_RXFULL		(1<<5)
+#define PS2_STAT_TXBUSY		(1<<6)
+#define PS2_STAT_TXEMPTY	(1<<7)
+
+struct pcips2_data {
+	struct serio	*io;
+	unsigned int	base;
+	struct pci_dev	*dev;
+};
+
+static int pcips2_write(struct serio *io, unsigned char val)
+{
+	struct pcips2_data *ps2if = io->port_data;
+	unsigned int stat;
+
+	do {
+		stat = inb(ps2if->base + PS2_STATUS);
+		cpu_relax();
+	} while (!(stat & PS2_STAT_TXEMPTY));
+
+	outb(val, ps2if->base + PS2_DATA);
+
+	return 0;
+}
+
+static irqreturn_t pcips2_interrupt(int irq, void *devid)
+{
+	struct pcips2_data *ps2if = devid;
+	unsigned char status, scancode;
+	int handled = 0;
+
+	do {
+		unsigned int flag;
+
+		status = inb(ps2if->base + PS2_STATUS);
+		if (!(status & PS2_STAT_RXFULL))
+			break;
+		handled = 1;
+		scancode = inb(ps2if->base + PS2_DATA);
+		if (status == 0xff && scancode == 0xff)
+			break;
+
+		flag = (status & PS2_STAT_PARITY) ? 0 : SERIO_PARITY;
+
+		if (hweight8(scancode) & 1)
+			flag ^= SERIO_PARITY;
+
+		serio_interrupt(ps2if->io, scancode, flag);
+	} while (1);
+	return IRQ_RETVAL(handled);
+}
+
+static void pcips2_flush_input(struct pcips2_data *ps2if)
+{
+	unsigned char status, scancode;
+
+	do {
+		status = inb(ps2if->base + PS2_STATUS);
+		if (!(status & PS2_STAT_RXFULL))
+			break;
+		scancode = inb(ps2if->base + PS2_DATA);
+		if (status == 0xff && scancode == 0xff)
+			break;
+	} while (1);
+}
+
+static int pcips2_open(struct serio *io)
+{
+	struct pcips2_data *ps2if = io->port_data;
+	int ret, val = 0;
+
+	outb(PS2_CTRL_ENABLE, ps2if->base);
+	pcips2_flush_input(ps2if);
+
+	ret = request_irq(ps2if->dev->irq, pcips2_interrupt, IRQF_SHARED,
+			  "pcips2", ps2if);
+	if (ret == 0)
+		val = PS2_CTRL_ENABLE | PS2_CTRL_RXIRQ;
+
+	outb(val, ps2if->base);
+
+	return ret;
+}
+
+static void pcips2_close(struct serio *io)
+{
+	struct pcips2_data *ps2if = io->port_data;
+
+	outb(0, ps2if->base);
+
+	free_irq(ps2if->dev->irq, ps2if);
+}
+
+static int pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct pcips2_data *ps2if;
+	struct serio *serio;
+	int ret;
+
+	ret = pci_enable_device(dev);
+	if (ret)
+		goto out;
+
+	ret = pci_request_regions(dev, "pcips2");
+	if (ret)
+		goto disable;
+
+	ps2if = kzalloc(sizeof(struct pcips2_data), GFP_KERNEL);
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!ps2if || !serio) {
+		ret = -ENOMEM;
+		goto release;
+	}
+
+
+	serio->id.type		= SERIO_8042;
+	serio->write		= pcips2_write;
+	serio->open		= pcips2_open;
+	serio->close		= pcips2_close;
+	strlcpy(serio->name, pci_name(dev), sizeof(serio->name));
+	strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
+	serio->port_data	= ps2if;
+	serio->dev.parent	= &dev->dev;
+	ps2if->io		= serio;
+	ps2if->dev		= dev;
+	ps2if->base		= pci_resource_start(dev, 0);
+
+	pci_set_drvdata(dev, ps2if);
+
+	serio_register_port(ps2if->io);
+	return 0;
+
+ release:
+	kfree(ps2if);
+	kfree(serio);
+	pci_release_regions(dev);
+ disable:
+	pci_disable_device(dev);
+ out:
+	return ret;
+}
+
+static void pcips2_remove(struct pci_dev *dev)
+{
+	struct pcips2_data *ps2if = pci_get_drvdata(dev);
+
+	serio_unregister_port(ps2if->io);
+	kfree(ps2if);
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+}
+
+static const struct pci_device_id pcips2_ids[] = {
+	{
+		.vendor		= 0x14f2,	/* MOBILITY */
+		.device		= 0x0123,	/* Keyboard */
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.class		= PCI_CLASS_INPUT_KEYBOARD << 8,
+		.class_mask	= 0xffff00,
+	},
+	{
+		.vendor		= 0x14f2,	/* MOBILITY */
+		.device		= 0x0124,	/* Mouse */
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.class		= PCI_CLASS_INPUT_MOUSE << 8,
+		.class_mask	= 0xffff00,
+	},
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, pcips2_ids);
+
+static struct pci_driver pcips2_driver = {
+	.name			= "pcips2",
+	.id_table		= pcips2_ids,
+	.probe			= pcips2_probe,
+	.remove			= pcips2_remove,
+};
+
+module_pci_driver(pcips2_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver");
diff --git a/drivers/input/serio/ps2mult.c b/drivers/input/serio/ps2mult.c
new file mode 100644
index 0000000..a76fb64
--- /dev/null
+++ b/drivers/input/serio/ps2mult.c
@@ -0,0 +1,307 @@
+/*
+ * TQC PS/2 Multiplexer driver
+ *
+ * Copyright (C) 2010 Dmitry Eremin-Solenikov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+
+MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
+MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
+MODULE_LICENSE("GPL");
+
+#define PS2MULT_KB_SELECTOR		0xA0
+#define PS2MULT_MS_SELECTOR		0xA1
+#define PS2MULT_ESCAPE			0x7D
+#define PS2MULT_BSYNC			0x7E
+#define PS2MULT_SESSION_START		0x55
+#define PS2MULT_SESSION_END		0x56
+
+struct ps2mult_port {
+	struct serio *serio;
+	unsigned char sel;
+	bool registered;
+};
+
+#define PS2MULT_NUM_PORTS	2
+#define PS2MULT_KBD_PORT	0
+#define PS2MULT_MOUSE_PORT	1
+
+struct ps2mult {
+	struct serio *mx_serio;
+	struct ps2mult_port ports[PS2MULT_NUM_PORTS];
+
+	spinlock_t lock;
+	struct ps2mult_port *in_port;
+	struct ps2mult_port *out_port;
+	bool escape;
+};
+
+/* First MUST come PS2MULT_NUM_PORTS selectors */
+static const unsigned char ps2mult_controls[] = {
+	PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR,
+	PS2MULT_ESCAPE, PS2MULT_BSYNC,
+	PS2MULT_SESSION_START, PS2MULT_SESSION_END,
+};
+
+static const struct serio_device_id ps2mult_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_PS2MULT,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);
+
+static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port)
+{
+	struct serio *mx_serio = psm->mx_serio;
+
+	serio_write(mx_serio, port->sel);
+	psm->out_port = port;
+	dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel);
+}
+
+static int ps2mult_serio_write(struct serio *serio, unsigned char data)
+{
+	struct serio *mx_port = serio->parent;
+	struct ps2mult *psm = serio_get_drvdata(mx_port);
+	struct ps2mult_port *port = serio->port_data;
+	bool need_escape;
+	unsigned long flags;
+
+	spin_lock_irqsave(&psm->lock, flags);
+
+	if (psm->out_port != port)
+		ps2mult_select_port(psm, port);
+
+	need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls));
+
+	dev_dbg(&serio->dev,
+		"write: %s%02x\n", need_escape ? "ESC " : "", data);
+
+	if (need_escape)
+		serio_write(mx_port, PS2MULT_ESCAPE);
+
+	serio_write(mx_port, data);
+
+	spin_unlock_irqrestore(&psm->lock, flags);
+
+	return 0;
+}
+
+static int ps2mult_serio_start(struct serio *serio)
+{
+	struct ps2mult *psm = serio_get_drvdata(serio->parent);
+	struct ps2mult_port *port = serio->port_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&psm->lock, flags);
+	port->registered = true;
+	spin_unlock_irqrestore(&psm->lock, flags);
+
+	return 0;
+}
+
+static void ps2mult_serio_stop(struct serio *serio)
+{
+	struct ps2mult *psm = serio_get_drvdata(serio->parent);
+	struct ps2mult_port *port = serio->port_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&psm->lock, flags);
+	port->registered = false;
+	spin_unlock_irqrestore(&psm->lock, flags);
+}
+
+static int ps2mult_create_port(struct ps2mult *psm, int i)
+{
+	struct serio *mx_serio = psm->mx_serio;
+	struct serio *serio;
+
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!serio)
+		return -ENOMEM;
+
+	strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name));
+	snprintf(serio->phys, sizeof(serio->phys),
+		 "%s/port%d", mx_serio->phys, i);
+	serio->id.type = SERIO_8042;
+	serio->write = ps2mult_serio_write;
+	serio->start = ps2mult_serio_start;
+	serio->stop = ps2mult_serio_stop;
+	serio->parent = psm->mx_serio;
+	serio->port_data = &psm->ports[i];
+
+	psm->ports[i].serio = serio;
+
+	return 0;
+}
+
+static void ps2mult_reset(struct ps2mult *psm)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&psm->lock, flags);
+
+	serio_write(psm->mx_serio, PS2MULT_SESSION_END);
+	serio_write(psm->mx_serio, PS2MULT_SESSION_START);
+
+	ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]);
+
+	spin_unlock_irqrestore(&psm->lock, flags);
+}
+
+static int ps2mult_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct ps2mult *psm;
+	int i;
+	int error;
+
+	if (!serio->write)
+		return -EINVAL;
+
+	psm = kzalloc(sizeof(*psm), GFP_KERNEL);
+	if (!psm)
+		return -ENOMEM;
+
+	spin_lock_init(&psm->lock);
+	psm->mx_serio = serio;
+
+	for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
+		psm->ports[i].sel = ps2mult_controls[i];
+		error = ps2mult_create_port(psm, i);
+		if (error)
+			goto err_out;
+	}
+
+	psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT];
+
+	serio_set_drvdata(serio, psm);
+	error = serio_open(serio, drv);
+	if (error)
+		goto err_out;
+
+	ps2mult_reset(psm);
+
+	for (i = 0; i <  PS2MULT_NUM_PORTS; i++) {
+		struct serio *s = psm->ports[i].serio;
+
+		dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys);
+		serio_register_port(s);
+	}
+
+	return 0;
+
+err_out:
+	while (--i >= 0)
+		kfree(psm->ports[i].serio);
+	kfree(psm);
+	return error;
+}
+
+static void ps2mult_disconnect(struct serio *serio)
+{
+	struct ps2mult *psm = serio_get_drvdata(serio);
+
+	/* Note that serio core already take care of children ports */
+	serio_write(serio, PS2MULT_SESSION_END);
+	serio_close(serio);
+	kfree(psm);
+
+	serio_set_drvdata(serio, NULL);
+}
+
+static int ps2mult_reconnect(struct serio *serio)
+{
+	struct ps2mult *psm = serio_get_drvdata(serio);
+
+	ps2mult_reset(psm);
+
+	return 0;
+}
+
+static irqreturn_t ps2mult_interrupt(struct serio *serio,
+				     unsigned char data, unsigned int dfl)
+{
+	struct ps2mult *psm = serio_get_drvdata(serio);
+	struct ps2mult_port *in_port;
+	unsigned long flags;
+
+	dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl);
+
+	spin_lock_irqsave(&psm->lock, flags);
+
+	if (psm->escape) {
+		psm->escape = false;
+		in_port = psm->in_port;
+		if (in_port->registered)
+			serio_interrupt(in_port->serio, data, dfl);
+		goto out;
+	}
+
+	switch (data) {
+	case PS2MULT_ESCAPE:
+		dev_dbg(&serio->dev, "ESCAPE\n");
+		psm->escape = true;
+		break;
+
+	case PS2MULT_BSYNC:
+		dev_dbg(&serio->dev, "BSYNC\n");
+		psm->in_port = psm->out_port;
+		break;
+
+	case PS2MULT_SESSION_START:
+		dev_dbg(&serio->dev, "SS\n");
+		break;
+
+	case PS2MULT_SESSION_END:
+		dev_dbg(&serio->dev, "SE\n");
+		break;
+
+	case PS2MULT_KB_SELECTOR:
+		dev_dbg(&serio->dev, "KB\n");
+		psm->in_port = &psm->ports[PS2MULT_KBD_PORT];
+		break;
+
+	case PS2MULT_MS_SELECTOR:
+		dev_dbg(&serio->dev, "MS\n");
+		psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT];
+		break;
+
+	default:
+		in_port = psm->in_port;
+		if (in_port->registered)
+			serio_interrupt(in_port->serio, data, dfl);
+		break;
+	}
+
+ out:
+	spin_unlock_irqrestore(&psm->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static struct serio_driver ps2mult_drv = {
+	.driver		= {
+		.name	= "ps2mult",
+	},
+	.description	= "TQC PS/2 Multiplexer driver",
+	.id_table	= ps2mult_serio_ids,
+	.interrupt	= ps2mult_interrupt,
+	.connect	= ps2mult_connect,
+	.disconnect	= ps2mult_disconnect,
+	.reconnect	= ps2mult_reconnect,
+};
+
+module_serio_driver(ps2mult_drv);
diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c
new file mode 100644
index 0000000..5a9d521
--- /dev/null
+++ b/drivers/input/serio/q40kbd.c
@@ -0,0 +1,193 @@
+/*
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Richard Zidlicky <Richard.Zidlicky@stud.informatik.uni-erlangen.de>
+ */
+
+/*
+ * Q40 PS/2 keyboard controller driver for Linux/m68k
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/q40_master.h>
+#include <asm/irq.h>
+#include <asm/q40ints.h>
+
+#define DRV_NAME	"q40kbd"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+
+struct q40kbd {
+	struct serio *port;
+	spinlock_t lock;
+};
+
+static irqreturn_t q40kbd_interrupt(int irq, void *dev_id)
+{
+	struct q40kbd *q40kbd = dev_id;
+	unsigned long flags;
+
+	spin_lock_irqsave(&q40kbd->lock, flags);
+
+	if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))
+		serio_interrupt(q40kbd->port, master_inb(KEYCODE_REG), 0);
+
+	master_outb(-1, KEYBOARD_UNLOCK_REG);
+
+	spin_unlock_irqrestore(&q40kbd->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * q40kbd_flush() flushes all data that may be in the keyboard buffers
+ */
+
+static void q40kbd_flush(struct q40kbd *q40kbd)
+{
+	int maxread = 100;
+	unsigned long flags;
+
+	spin_lock_irqsave(&q40kbd->lock, flags);
+
+	while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)))
+		master_inb(KEYCODE_REG);
+
+	spin_unlock_irqrestore(&q40kbd->lock, flags);
+}
+
+static void q40kbd_stop(void)
+{
+	master_outb(0, KEY_IRQ_ENABLE_REG);
+	master_outb(-1, KEYBOARD_UNLOCK_REG);
+}
+
+/*
+ * q40kbd_open() is called when a port is open by the higher layer.
+ * It allocates the interrupt and enables in in the chip.
+ */
+
+static int q40kbd_open(struct serio *port)
+{
+	struct q40kbd *q40kbd = port->port_data;
+
+	q40kbd_flush(q40kbd);
+
+	/* off we go */
+	master_outb(-1, KEYBOARD_UNLOCK_REG);
+	master_outb(1, KEY_IRQ_ENABLE_REG);
+
+	return 0;
+}
+
+static void q40kbd_close(struct serio *port)
+{
+	struct q40kbd *q40kbd = port->port_data;
+
+	q40kbd_stop();
+	q40kbd_flush(q40kbd);
+}
+
+static int q40kbd_probe(struct platform_device *pdev)
+{
+	struct q40kbd *q40kbd;
+	struct serio *port;
+	int error;
+
+	q40kbd = kzalloc(sizeof(struct q40kbd), GFP_KERNEL);
+	port = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!q40kbd || !port) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	q40kbd->port = port;
+	spin_lock_init(&q40kbd->lock);
+
+	port->id.type = SERIO_8042;
+	port->open = q40kbd_open;
+	port->close = q40kbd_close;
+	port->port_data = q40kbd;
+	port->dev.parent = &pdev->dev;
+	strlcpy(port->name, "Q40 Kbd Port", sizeof(port->name));
+	strlcpy(port->phys, "Q40", sizeof(port->phys));
+
+	q40kbd_stop();
+
+	error = request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0,
+			    DRV_NAME, q40kbd);
+	if (error) {
+		dev_err(&pdev->dev, "Can't get irq %d.\n", Q40_IRQ_KEYBOARD);
+		goto err_free_mem;
+	}
+
+	serio_register_port(q40kbd->port);
+
+	platform_set_drvdata(pdev, q40kbd);
+	printk(KERN_INFO "serio: Q40 kbd registered\n");
+
+	return 0;
+
+err_free_mem:
+	kfree(port);
+	kfree(q40kbd);
+	return error;
+}
+
+static int q40kbd_remove(struct platform_device *pdev)
+{
+	struct q40kbd *q40kbd = platform_get_drvdata(pdev);
+
+	/*
+	 * q40kbd_close() will be called as part of unregistering
+	 * and will ensure that IRQ is turned off, so it is safe
+	 * to unregister port first and free IRQ later.
+	 */
+	serio_unregister_port(q40kbd->port);
+	free_irq(Q40_IRQ_KEYBOARD, q40kbd);
+	kfree(q40kbd);
+
+	return 0;
+}
+
+static struct platform_driver q40kbd_driver = {
+	.driver		= {
+		.name	= "q40kbd",
+	},
+	.remove		= q40kbd_remove,
+};
+
+module_platform_driver_probe(q40kbd_driver, q40kbd_probe);
diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c
new file mode 100644
index 0000000..8cf9647
--- /dev/null
+++ b/drivers/input/serio/rpckbd.c
@@ -0,0 +1,173 @@
+/*
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *  Copyright (c) 2002 Russell King
+ */
+
+/*
+ * Acorn RiscPC PS/2 keyboard controller driver for Linux/ARM
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/serio.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+#include <asm/hardware/iomd.h>
+
+MODULE_AUTHOR("Vojtech Pavlik, Russell King");
+MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:kart");
+
+struct rpckbd_data {
+	int tx_irq;
+	int rx_irq;
+};
+
+static int rpckbd_write(struct serio *port, unsigned char val)
+{
+	while (!(iomd_readb(IOMD_KCTRL) & (1 << 7)))
+		cpu_relax();
+
+	iomd_writeb(val, IOMD_KARTTX);
+
+	return 0;
+}
+
+static irqreturn_t rpckbd_rx(int irq, void *dev_id)
+{
+	struct serio *port = dev_id;
+	unsigned int byte;
+	int handled = IRQ_NONE;
+
+	while (iomd_readb(IOMD_KCTRL) & (1 << 5)) {
+		byte = iomd_readb(IOMD_KARTRX);
+
+		serio_interrupt(port, byte, 0);
+		handled = IRQ_HANDLED;
+	}
+	return handled;
+}
+
+static irqreturn_t rpckbd_tx(int irq, void *dev_id)
+{
+	return IRQ_HANDLED;
+}
+
+static int rpckbd_open(struct serio *port)
+{
+	struct rpckbd_data *rpckbd = port->port_data;
+
+	/* Reset the keyboard state machine. */
+	iomd_writeb(0, IOMD_KCTRL);
+	iomd_writeb(8, IOMD_KCTRL);
+	iomd_readb(IOMD_KARTRX);
+
+	if (request_irq(rpckbd->rx_irq, rpckbd_rx, 0, "rpckbd", port) != 0) {
+		printk(KERN_ERR "rpckbd.c: Could not allocate keyboard receive IRQ\n");
+		return -EBUSY;
+	}
+
+	if (request_irq(rpckbd->tx_irq, rpckbd_tx, 0, "rpckbd", port) != 0) {
+		printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ\n");
+		free_irq(rpckbd->rx_irq, port);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static void rpckbd_close(struct serio *port)
+{
+	struct rpckbd_data *rpckbd = port->port_data;
+
+	free_irq(rpckbd->rx_irq, port);
+	free_irq(rpckbd->tx_irq, port);
+}
+
+/*
+ * Allocate and initialize serio structure for subsequent registration
+ * with serio core.
+ */
+static int rpckbd_probe(struct platform_device *dev)
+{
+	struct rpckbd_data *rpckbd;
+	struct serio *serio;
+	int tx_irq, rx_irq;
+
+	rx_irq = platform_get_irq(dev, 0);
+	if (rx_irq <= 0)
+		return rx_irq < 0 ? rx_irq : -ENXIO;
+
+	tx_irq = platform_get_irq(dev, 1);
+	if (tx_irq <= 0)
+		return tx_irq < 0 ? tx_irq : -ENXIO;
+
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	rpckbd = kzalloc(sizeof(*rpckbd), GFP_KERNEL);
+	if (!serio || !rpckbd) {
+		kfree(rpckbd);
+		kfree(serio);
+		return -ENOMEM;
+	}
+
+	rpckbd->rx_irq = rx_irq;
+	rpckbd->tx_irq = tx_irq;
+
+	serio->id.type		= SERIO_8042;
+	serio->write		= rpckbd_write;
+	serio->open		= rpckbd_open;
+	serio->close		= rpckbd_close;
+	serio->dev.parent	= &dev->dev;
+	serio->port_data	= rpckbd;
+	strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name));
+	strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys));
+
+	platform_set_drvdata(dev, serio);
+	serio_register_port(serio);
+	return 0;
+}
+
+static int rpckbd_remove(struct platform_device *dev)
+{
+	struct serio *serio = platform_get_drvdata(dev);
+	struct rpckbd_data *rpckbd = serio->port_data;
+
+	serio_unregister_port(serio);
+	kfree(rpckbd);
+
+	return 0;
+}
+
+static struct platform_driver rpckbd_driver = {
+	.probe		= rpckbd_probe,
+	.remove		= rpckbd_remove,
+	.driver		= {
+		.name	= "kart",
+	},
+};
+module_platform_driver(rpckbd_driver);
diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c
new file mode 100644
index 0000000..b3e6889
--- /dev/null
+++ b/drivers/input/serio/sa1111ps2.c
@@ -0,0 +1,378 @@
+/*
+ *  linux/drivers/input/serio/sa1111ps2.c
+ *
+ *  Copyright (C) 2002 Russell King
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/io.h>
+
+#include <asm/hardware/sa1111.h>
+
+#define PS2CR		0x0000
+#define PS2STAT		0x0004
+#define PS2DATA		0x0008
+#define PS2CLKDIV	0x000c
+#define PS2PRECNT	0x0010
+
+#define PS2CR_ENA	0x08
+#define PS2CR_FKD	0x02
+#define PS2CR_FKC	0x01
+
+#define PS2STAT_STP	0x0100
+#define PS2STAT_TXE	0x0080
+#define PS2STAT_TXB	0x0040
+#define PS2STAT_RXF	0x0020
+#define PS2STAT_RXB	0x0010
+#define PS2STAT_ENA	0x0008
+#define PS2STAT_RXP	0x0004
+#define PS2STAT_KBD	0x0002
+#define PS2STAT_KBC	0x0001
+
+struct ps2if {
+	struct serio		*io;
+	struct sa1111_dev	*dev;
+	void __iomem		*base;
+	unsigned int		open;
+	spinlock_t		lock;
+	unsigned int		head;
+	unsigned int		tail;
+	unsigned char		buf[4];
+};
+
+/*
+ * Read all bytes waiting in the PS2 port.  There should be
+ * at the most one, but we loop for safety.  If there was a
+ * framing error, we have to manually clear the status.
+ */
+static irqreturn_t ps2_rxint(int irq, void *dev_id)
+{
+	struct ps2if *ps2if = dev_id;
+	unsigned int scancode, flag, status;
+
+	status = sa1111_readl(ps2if->base + PS2STAT);
+	while (status & PS2STAT_RXF) {
+		if (status & PS2STAT_STP)
+			sa1111_writel(PS2STAT_STP, ps2if->base + PS2STAT);
+
+		flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) |
+		       (status & PS2STAT_RXP ? 0 : SERIO_PARITY);
+
+		scancode = sa1111_readl(ps2if->base + PS2DATA) & 0xff;
+
+		if (hweight8(scancode) & 1)
+			flag ^= SERIO_PARITY;
+
+		serio_interrupt(ps2if->io, scancode, flag);
+
+		status = sa1111_readl(ps2if->base + PS2STAT);
+        }
+
+        return IRQ_HANDLED;
+}
+
+/*
+ * Completion of ps2 write
+ */
+static irqreturn_t ps2_txint(int irq, void *dev_id)
+{
+	struct ps2if *ps2if = dev_id;
+	unsigned int status;
+
+	spin_lock(&ps2if->lock);
+	status = sa1111_readl(ps2if->base + PS2STAT);
+	if (ps2if->head == ps2if->tail) {
+		disable_irq_nosync(irq);
+		/* done */
+	} else if (status & PS2STAT_TXE) {
+		sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + PS2DATA);
+		ps2if->tail = (ps2if->tail + 1) & (sizeof(ps2if->buf) - 1);
+	}
+	spin_unlock(&ps2if->lock);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Write a byte to the PS2 port.  We have to wait for the
+ * port to indicate that the transmitter is empty.
+ */
+static int ps2_write(struct serio *io, unsigned char val)
+{
+	struct ps2if *ps2if = io->port_data;
+	unsigned long flags;
+	unsigned int head;
+
+	spin_lock_irqsave(&ps2if->lock, flags);
+
+	/*
+	 * If the TX register is empty, we can go straight out.
+	 */
+	if (sa1111_readl(ps2if->base + PS2STAT) & PS2STAT_TXE) {
+		sa1111_writel(val, ps2if->base + PS2DATA);
+	} else {
+		if (ps2if->head == ps2if->tail)
+			enable_irq(ps2if->dev->irq[1]);
+		head = (ps2if->head + 1) & (sizeof(ps2if->buf) - 1);
+		if (head != ps2if->tail) {
+			ps2if->buf[ps2if->head] = val;
+			ps2if->head = head;
+		}
+	}
+
+	spin_unlock_irqrestore(&ps2if->lock, flags);
+	return 0;
+}
+
+static int ps2_open(struct serio *io)
+{
+	struct ps2if *ps2if = io->port_data;
+	int ret;
+
+	ret = sa1111_enable_device(ps2if->dev);
+	if (ret)
+		return ret;
+
+	ret = request_irq(ps2if->dev->irq[0], ps2_rxint, 0,
+			  SA1111_DRIVER_NAME(ps2if->dev), ps2if);
+	if (ret) {
+		printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
+			ps2if->dev->irq[0], ret);
+		sa1111_disable_device(ps2if->dev);
+		return ret;
+	}
+
+	ret = request_irq(ps2if->dev->irq[1], ps2_txint, 0,
+			  SA1111_DRIVER_NAME(ps2if->dev), ps2if);
+	if (ret) {
+		printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
+			ps2if->dev->irq[1], ret);
+		free_irq(ps2if->dev->irq[0], ps2if);
+		sa1111_disable_device(ps2if->dev);
+		return ret;
+	}
+
+	ps2if->open = 1;
+
+	enable_irq_wake(ps2if->dev->irq[0]);
+
+	sa1111_writel(PS2CR_ENA, ps2if->base + PS2CR);
+	return 0;
+}
+
+static void ps2_close(struct serio *io)
+{
+	struct ps2if *ps2if = io->port_data;
+
+	sa1111_writel(0, ps2if->base + PS2CR);
+
+	disable_irq_wake(ps2if->dev->irq[0]);
+
+	ps2if->open = 0;
+
+	free_irq(ps2if->dev->irq[1], ps2if);
+	free_irq(ps2if->dev->irq[0], ps2if);
+
+	sa1111_disable_device(ps2if->dev);
+}
+
+/*
+ * Clear the input buffer.
+ */
+static void ps2_clear_input(struct ps2if *ps2if)
+{
+	int maxread = 100;
+
+	while (maxread--) {
+		if ((sa1111_readl(ps2if->base + PS2DATA) & 0xff) == 0xff)
+			break;
+	}
+}
+
+static unsigned int ps2_test_one(struct ps2if *ps2if,
+					   unsigned int mask)
+{
+	unsigned int val;
+
+	sa1111_writel(PS2CR_ENA | mask, ps2if->base + PS2CR);
+
+	udelay(2);
+
+	val = sa1111_readl(ps2if->base + PS2STAT);
+	return val & (PS2STAT_KBC | PS2STAT_KBD);
+}
+
+/*
+ * Test the keyboard interface.  We basically check to make sure that
+ * we can drive each line to the keyboard independently of each other.
+ */
+static int ps2_test(struct ps2if *ps2if)
+{
+	unsigned int stat;
+	int ret = 0;
+
+	stat = ps2_test_one(ps2if, PS2CR_FKC);
+	if (stat != PS2STAT_KBD) {
+		printk("PS/2 interface test failed[1]: %02x\n", stat);
+		ret = -ENODEV;
+	}
+
+	stat = ps2_test_one(ps2if, 0);
+	if (stat != (PS2STAT_KBC | PS2STAT_KBD)) {
+		printk("PS/2 interface test failed[2]: %02x\n", stat);
+		ret = -ENODEV;
+	}
+
+	stat = ps2_test_one(ps2if, PS2CR_FKD);
+	if (stat != PS2STAT_KBC) {
+		printk("PS/2 interface test failed[3]: %02x\n", stat);
+		ret = -ENODEV;
+	}
+
+	sa1111_writel(0, ps2if->base + PS2CR);
+
+	return ret;
+}
+
+/*
+ * Add one device to this driver.
+ */
+static int ps2_probe(struct sa1111_dev *dev)
+{
+	struct ps2if *ps2if;
+	struct serio *serio;
+	int ret;
+
+	ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL);
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!ps2if || !serio) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+
+	serio->id.type		= SERIO_8042;
+	serio->write		= ps2_write;
+	serio->open		= ps2_open;
+	serio->close		= ps2_close;
+	strlcpy(serio->name, dev_name(&dev->dev), sizeof(serio->name));
+	strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
+	serio->port_data	= ps2if;
+	serio->dev.parent	= &dev->dev;
+	ps2if->io		= serio;
+	ps2if->dev		= dev;
+	sa1111_set_drvdata(dev, ps2if);
+
+	spin_lock_init(&ps2if->lock);
+
+	/*
+	 * Request the physical region for this PS2 port.
+	 */
+	if (!request_mem_region(dev->res.start,
+				dev->res.end - dev->res.start + 1,
+				SA1111_DRIVER_NAME(dev))) {
+		ret = -EBUSY;
+		goto free;
+	}
+
+	/*
+	 * Our parent device has already mapped the region.
+	 */
+	ps2if->base = dev->mapbase;
+
+	sa1111_enable_device(ps2if->dev);
+
+	/* Incoming clock is 8MHz */
+	sa1111_writel(0, ps2if->base + PS2CLKDIV);
+	sa1111_writel(127, ps2if->base + PS2PRECNT);
+
+	/*
+	 * Flush any pending input.
+	 */
+	ps2_clear_input(ps2if);
+
+	/*
+	 * Test the keyboard interface.
+	 */
+	ret = ps2_test(ps2if);
+	if (ret)
+		goto out;
+
+	/*
+	 * Flush any pending input.
+	 */
+	ps2_clear_input(ps2if);
+
+	sa1111_disable_device(ps2if->dev);
+	serio_register_port(ps2if->io);
+	return 0;
+
+ out:
+	sa1111_disable_device(ps2if->dev);
+	release_mem_region(dev->res.start, resource_size(&dev->res));
+ free:
+	sa1111_set_drvdata(dev, NULL);
+	kfree(ps2if);
+	kfree(serio);
+	return ret;
+}
+
+/*
+ * Remove one device from this driver.
+ */
+static int ps2_remove(struct sa1111_dev *dev)
+{
+	struct ps2if *ps2if = sa1111_get_drvdata(dev);
+
+	serio_unregister_port(ps2if->io);
+	release_mem_region(dev->res.start, resource_size(&dev->res));
+	sa1111_set_drvdata(dev, NULL);
+
+	kfree(ps2if);
+
+	return 0;
+}
+
+/*
+ * Our device driver structure
+ */
+static struct sa1111_driver ps2_driver = {
+	.drv = {
+		.name	= "sa1111-ps2",
+		.owner	= THIS_MODULE,
+	},
+	.devid		= SA1111_DEVID_PS2,
+	.probe		= ps2_probe,
+	.remove		= ps2_remove,
+};
+
+static int __init ps2_init(void)
+{
+	return sa1111_driver_register(&ps2_driver);
+}
+
+static void __exit ps2_exit(void)
+{
+	sa1111_driver_unregister(&ps2_driver);
+}
+
+module_init(ps2_init);
+module_exit(ps2_exit);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("SA1111 PS2 controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
new file mode 100644
index 0000000..8f82897
--- /dev/null
+++ b/drivers/input/serio/serio.c
@@ -0,0 +1,1057 @@
+/*
+ *  The Serio abstraction module
+ *
+ *  Copyright (c) 1999-2004 Vojtech Pavlik
+ *  Copyright (c) 2004 Dmitry Torokhov
+ *  Copyright (c) 2003 Daniele Bellucci
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Serio abstraction core");
+MODULE_LICENSE("GPL");
+
+/*
+ * serio_mutex protects entire serio subsystem and is taken every time
+ * serio port or driver registered or unregistered.
+ */
+static DEFINE_MUTEX(serio_mutex);
+
+static LIST_HEAD(serio_list);
+
+static void serio_add_port(struct serio *serio);
+static int serio_reconnect_port(struct serio *serio);
+static void serio_disconnect_port(struct serio *serio);
+static void serio_reconnect_subtree(struct serio *serio);
+static void serio_attach_driver(struct serio_driver *drv);
+
+static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
+{
+	int retval;
+
+	mutex_lock(&serio->drv_mutex);
+	retval = drv->connect(serio, drv);
+	mutex_unlock(&serio->drv_mutex);
+
+	return retval;
+}
+
+static int serio_reconnect_driver(struct serio *serio)
+{
+	int retval = -1;
+
+	mutex_lock(&serio->drv_mutex);
+	if (serio->drv && serio->drv->reconnect)
+		retval = serio->drv->reconnect(serio);
+	mutex_unlock(&serio->drv_mutex);
+
+	return retval;
+}
+
+static void serio_disconnect_driver(struct serio *serio)
+{
+	mutex_lock(&serio->drv_mutex);
+	if (serio->drv)
+		serio->drv->disconnect(serio);
+	mutex_unlock(&serio->drv_mutex);
+}
+
+static int serio_match_port(const struct serio_device_id *ids, struct serio *serio)
+{
+	while (ids->type || ids->proto) {
+		if ((ids->type == SERIO_ANY || ids->type == serio->id.type) &&
+		    (ids->proto == SERIO_ANY || ids->proto == serio->id.proto) &&
+		    (ids->extra == SERIO_ANY || ids->extra == serio->id.extra) &&
+		    (ids->id == SERIO_ANY || ids->id == serio->id.id))
+			return 1;
+		ids++;
+	}
+	return 0;
+}
+
+/*
+ * Basic serio -> driver core mappings
+ */
+
+static int serio_bind_driver(struct serio *serio, struct serio_driver *drv)
+{
+	int error;
+
+	if (serio_match_port(drv->id_table, serio)) {
+
+		serio->dev.driver = &drv->driver;
+		if (serio_connect_driver(serio, drv)) {
+			serio->dev.driver = NULL;
+			return -ENODEV;
+		}
+
+		error = device_bind_driver(&serio->dev);
+		if (error) {
+			dev_warn(&serio->dev,
+				 "device_bind_driver() failed for %s (%s) and %s, error: %d\n",
+				 serio->phys, serio->name,
+				 drv->description, error);
+			serio_disconnect_driver(serio);
+			serio->dev.driver = NULL;
+			return error;
+		}
+	}
+	return 0;
+}
+
+static void serio_find_driver(struct serio *serio)
+{
+	int error;
+
+	error = device_attach(&serio->dev);
+	if (error < 0)
+		dev_warn(&serio->dev,
+			 "device_attach() failed for %s (%s), error: %d\n",
+			 serio->phys, serio->name, error);
+}
+
+
+/*
+ * Serio event processing.
+ */
+
+enum serio_event_type {
+	SERIO_RESCAN_PORT,
+	SERIO_RECONNECT_PORT,
+	SERIO_RECONNECT_SUBTREE,
+	SERIO_REGISTER_PORT,
+	SERIO_ATTACH_DRIVER,
+};
+
+struct serio_event {
+	enum serio_event_type type;
+	void *object;
+	struct module *owner;
+	struct list_head node;
+};
+
+static DEFINE_SPINLOCK(serio_event_lock);	/* protects serio_event_list */
+static LIST_HEAD(serio_event_list);
+
+static struct serio_event *serio_get_event(void)
+{
+	struct serio_event *event = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&serio_event_lock, flags);
+
+	if (!list_empty(&serio_event_list)) {
+		event = list_first_entry(&serio_event_list,
+					 struct serio_event, node);
+		list_del_init(&event->node);
+	}
+
+	spin_unlock_irqrestore(&serio_event_lock, flags);
+	return event;
+}
+
+static void serio_free_event(struct serio_event *event)
+{
+	module_put(event->owner);
+	kfree(event);
+}
+
+static void serio_remove_duplicate_events(void *object,
+					  enum serio_event_type type)
+{
+	struct serio_event *e, *next;
+	unsigned long flags;
+
+	spin_lock_irqsave(&serio_event_lock, flags);
+
+	list_for_each_entry_safe(e, next, &serio_event_list, node) {
+		if (object == e->object) {
+			/*
+			 * If this event is of different type we should not
+			 * look further - we only suppress duplicate events
+			 * that were sent back-to-back.
+			 */
+			if (type != e->type)
+				break;
+
+			list_del_init(&e->node);
+			serio_free_event(e);
+		}
+	}
+
+	spin_unlock_irqrestore(&serio_event_lock, flags);
+}
+
+static void serio_handle_event(struct work_struct *work)
+{
+	struct serio_event *event;
+
+	mutex_lock(&serio_mutex);
+
+	while ((event = serio_get_event())) {
+
+		switch (event->type) {
+
+		case SERIO_REGISTER_PORT:
+			serio_add_port(event->object);
+			break;
+
+		case SERIO_RECONNECT_PORT:
+			serio_reconnect_port(event->object);
+			break;
+
+		case SERIO_RESCAN_PORT:
+			serio_disconnect_port(event->object);
+			serio_find_driver(event->object);
+			break;
+
+		case SERIO_RECONNECT_SUBTREE:
+			serio_reconnect_subtree(event->object);
+			break;
+
+		case SERIO_ATTACH_DRIVER:
+			serio_attach_driver(event->object);
+			break;
+		}
+
+		serio_remove_duplicate_events(event->object, event->type);
+		serio_free_event(event);
+	}
+
+	mutex_unlock(&serio_mutex);
+}
+
+static DECLARE_WORK(serio_event_work, serio_handle_event);
+
+static int serio_queue_event(void *object, struct module *owner,
+			     enum serio_event_type event_type)
+{
+	unsigned long flags;
+	struct serio_event *event;
+	int retval = 0;
+
+	spin_lock_irqsave(&serio_event_lock, flags);
+
+	/*
+	 * Scan event list for the other events for the same serio port,
+	 * starting with the most recent one. If event is the same we
+	 * do not need add new one. If event is of different type we
+	 * need to add this event and should not look further because
+	 * we need to preseve sequence of distinct events.
+	 */
+	list_for_each_entry_reverse(event, &serio_event_list, node) {
+		if (event->object == object) {
+			if (event->type == event_type)
+				goto out;
+			break;
+		}
+	}
+
+	event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
+	if (!event) {
+		pr_err("Not enough memory to queue event %d\n", event_type);
+		retval = -ENOMEM;
+		goto out;
+	}
+
+	if (!try_module_get(owner)) {
+		pr_warning("Can't get module reference, dropping event %d\n",
+			   event_type);
+		kfree(event);
+		retval = -EINVAL;
+		goto out;
+	}
+
+	event->type = event_type;
+	event->object = object;
+	event->owner = owner;
+
+	list_add_tail(&event->node, &serio_event_list);
+	queue_work(system_long_wq, &serio_event_work);
+
+out:
+	spin_unlock_irqrestore(&serio_event_lock, flags);
+	return retval;
+}
+
+/*
+ * Remove all events that have been submitted for a given
+ * object, be it serio port or driver.
+ */
+static void serio_remove_pending_events(void *object)
+{
+	struct serio_event *event, *next;
+	unsigned long flags;
+
+	spin_lock_irqsave(&serio_event_lock, flags);
+
+	list_for_each_entry_safe(event, next, &serio_event_list, node) {
+		if (event->object == object) {
+			list_del_init(&event->node);
+			serio_free_event(event);
+		}
+	}
+
+	spin_unlock_irqrestore(&serio_event_lock, flags);
+}
+
+/*
+ * Locate child serio port (if any) that has not been fully registered yet.
+ *
+ * Children are registered by driver's connect() handler so there can't be a
+ * grandchild pending registration together with a child.
+ */
+static struct serio *serio_get_pending_child(struct serio *parent)
+{
+	struct serio_event *event;
+	struct serio *serio, *child = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&serio_event_lock, flags);
+
+	list_for_each_entry(event, &serio_event_list, node) {
+		if (event->type == SERIO_REGISTER_PORT) {
+			serio = event->object;
+			if (serio->parent == parent) {
+				child = serio;
+				break;
+			}
+		}
+	}
+
+	spin_unlock_irqrestore(&serio_event_lock, flags);
+	return child;
+}
+
+/*
+ * Serio port operations
+ */
+
+static ssize_t serio_show_description(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	return sprintf(buf, "%s\n", serio->name);
+}
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+
+	return sprintf(buf, "serio:ty%02Xpr%02Xid%02Xex%02X\n",
+			serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);
+}
+
+static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	return sprintf(buf, "%02x\n", serio->id.type);
+}
+
+static ssize_t proto_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	return sprintf(buf, "%02x\n", serio->id.proto);
+}
+
+static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	return sprintf(buf, "%02x\n", serio->id.id);
+}
+
+static ssize_t extra_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	return sprintf(buf, "%02x\n", serio->id.extra);
+}
+
+static ssize_t drvctl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct serio *serio = to_serio_port(dev);
+	struct device_driver *drv;
+	int error;
+
+	error = mutex_lock_interruptible(&serio_mutex);
+	if (error)
+		return error;
+
+	if (!strncmp(buf, "none", count)) {
+		serio_disconnect_port(serio);
+	} else if (!strncmp(buf, "reconnect", count)) {
+		serio_reconnect_subtree(serio);
+	} else if (!strncmp(buf, "rescan", count)) {
+		serio_disconnect_port(serio);
+		serio_find_driver(serio);
+		serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT);
+	} else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
+		serio_disconnect_port(serio);
+		error = serio_bind_driver(serio, to_serio_driver(drv));
+		serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT);
+	} else {
+		error = -EINVAL;
+	}
+
+	mutex_unlock(&serio_mutex);
+
+	return error ? error : count;
+}
+
+static ssize_t serio_show_bind_mode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	return sprintf(buf, "%s\n", serio->manual_bind ? "manual" : "auto");
+}
+
+static ssize_t serio_set_bind_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct serio *serio = to_serio_port(dev);
+	int retval;
+
+	retval = count;
+	if (!strncmp(buf, "manual", count)) {
+		serio->manual_bind = true;
+	} else if (!strncmp(buf, "auto", count)) {
+		serio->manual_bind = false;
+	} else {
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+static ssize_t firmware_id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+
+	return sprintf(buf, "%s\n", serio->firmware_id);
+}
+
+static DEVICE_ATTR_RO(type);
+static DEVICE_ATTR_RO(proto);
+static DEVICE_ATTR_RO(id);
+static DEVICE_ATTR_RO(extra);
+
+static struct attribute *serio_device_id_attrs[] = {
+	&dev_attr_type.attr,
+	&dev_attr_proto.attr,
+	&dev_attr_id.attr,
+	&dev_attr_extra.attr,
+	NULL
+};
+
+static struct attribute_group serio_id_attr_group = {
+	.name	= "id",
+	.attrs	= serio_device_id_attrs,
+};
+
+static DEVICE_ATTR_RO(modalias);
+static DEVICE_ATTR_WO(drvctl);
+static DEVICE_ATTR(description, S_IRUGO, serio_show_description, NULL);
+static DEVICE_ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode);
+static DEVICE_ATTR_RO(firmware_id);
+
+static struct attribute *serio_device_attrs[] = {
+	&dev_attr_modalias.attr,
+	&dev_attr_description.attr,
+	&dev_attr_drvctl.attr,
+	&dev_attr_bind_mode.attr,
+	&dev_attr_firmware_id.attr,
+	NULL
+};
+
+static struct attribute_group serio_device_attr_group = {
+	.attrs	= serio_device_attrs,
+};
+
+static const struct attribute_group *serio_device_attr_groups[] = {
+	&serio_id_attr_group,
+	&serio_device_attr_group,
+	NULL
+};
+
+static void serio_release_port(struct device *dev)
+{
+	struct serio *serio = to_serio_port(dev);
+
+	kfree(serio);
+	module_put(THIS_MODULE);
+}
+
+/*
+ * Prepare serio port for registration.
+ */
+static void serio_init_port(struct serio *serio)
+{
+	static atomic_t serio_no = ATOMIC_INIT(-1);
+
+	__module_get(THIS_MODULE);
+
+	INIT_LIST_HEAD(&serio->node);
+	INIT_LIST_HEAD(&serio->child_node);
+	INIT_LIST_HEAD(&serio->children);
+	spin_lock_init(&serio->lock);
+	mutex_init(&serio->drv_mutex);
+	device_initialize(&serio->dev);
+	dev_set_name(&serio->dev, "serio%lu",
+		     (unsigned long)atomic_inc_return(&serio_no));
+	serio->dev.bus = &serio_bus;
+	serio->dev.release = serio_release_port;
+	serio->dev.groups = serio_device_attr_groups;
+	if (serio->parent) {
+		serio->dev.parent = &serio->parent->dev;
+		serio->depth = serio->parent->depth + 1;
+	} else
+		serio->depth = 0;
+	lockdep_set_subclass(&serio->lock, serio->depth);
+}
+
+/*
+ * Complete serio port registration.
+ * Driver core will attempt to find appropriate driver for the port.
+ */
+static void serio_add_port(struct serio *serio)
+{
+	struct serio *parent = serio->parent;
+	int error;
+
+	if (parent) {
+		serio_pause_rx(parent);
+		list_add_tail(&serio->child_node, &parent->children);
+		serio_continue_rx(parent);
+	}
+
+	list_add_tail(&serio->node, &serio_list);
+
+	if (serio->start)
+		serio->start(serio);
+
+	error = device_add(&serio->dev);
+	if (error)
+		dev_err(&serio->dev,
+			"device_add() failed for %s (%s), error: %d\n",
+			serio->phys, serio->name, error);
+}
+
+/*
+ * serio_destroy_port() completes unregistration process and removes
+ * port from the system
+ */
+static void serio_destroy_port(struct serio *serio)
+{
+	struct serio *child;
+
+	while ((child = serio_get_pending_child(serio)) != NULL) {
+		serio_remove_pending_events(child);
+		put_device(&child->dev);
+	}
+
+	if (serio->stop)
+		serio->stop(serio);
+
+	if (serio->parent) {
+		serio_pause_rx(serio->parent);
+		list_del_init(&serio->child_node);
+		serio_continue_rx(serio->parent);
+		serio->parent = NULL;
+	}
+
+	if (device_is_registered(&serio->dev))
+		device_del(&serio->dev);
+
+	list_del_init(&serio->node);
+	serio_remove_pending_events(serio);
+	put_device(&serio->dev);
+}
+
+/*
+ * Reconnect serio port (re-initialize attached device).
+ * If reconnect fails (old device is no longer attached or
+ * there was no device to begin with) we do full rescan in
+ * hope of finding a driver for the port.
+ */
+static int serio_reconnect_port(struct serio *serio)
+{
+	int error = serio_reconnect_driver(serio);
+
+	if (error) {
+		serio_disconnect_port(serio);
+		serio_find_driver(serio);
+	}
+
+	return error;
+}
+
+/*
+ * Reconnect serio port and all its children (re-initialize attached
+ * devices).
+ */
+static void serio_reconnect_subtree(struct serio *root)
+{
+	struct serio *s = root;
+	int error;
+
+	do {
+		error = serio_reconnect_port(s);
+		if (!error) {
+			/*
+			 * Reconnect was successful, move on to do the
+			 * first child.
+			 */
+			if (!list_empty(&s->children)) {
+				s = list_first_entry(&s->children,
+						     struct serio, child_node);
+				continue;
+			}
+		}
+
+		/*
+		 * Either it was a leaf node or reconnect failed and it
+		 * became a leaf node. Continue reconnecting starting with
+		 * the next sibling of the parent node.
+		 */
+		while (s != root) {
+			struct serio *parent = s->parent;
+
+			if (!list_is_last(&s->child_node, &parent->children)) {
+				s = list_entry(s->child_node.next,
+					       struct serio, child_node);
+				break;
+			}
+
+			s = parent;
+		}
+	} while (s != root);
+}
+
+/*
+ * serio_disconnect_port() unbinds a port from its driver. As a side effect
+ * all children ports are unbound and destroyed.
+ */
+static void serio_disconnect_port(struct serio *serio)
+{
+	struct serio *s = serio;
+
+	/*
+	 * Children ports should be disconnected and destroyed
+	 * first; we travel the tree in depth-first order.
+	 */
+	while (!list_empty(&serio->children)) {
+
+		/* Locate a leaf */
+		while (!list_empty(&s->children))
+			s = list_first_entry(&s->children,
+					     struct serio, child_node);
+
+		/*
+		 * Prune this leaf node unless it is the one we
+		 * started with.
+		 */
+		if (s != serio) {
+			struct serio *parent = s->parent;
+
+			device_release_driver(&s->dev);
+			serio_destroy_port(s);
+
+			s = parent;
+		}
+	}
+
+	/*
+	 * OK, no children left, now disconnect this port.
+	 */
+	device_release_driver(&serio->dev);
+}
+
+void serio_rescan(struct serio *serio)
+{
+	serio_queue_event(serio, NULL, SERIO_RESCAN_PORT);
+}
+EXPORT_SYMBOL(serio_rescan);
+
+void serio_reconnect(struct serio *serio)
+{
+	serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE);
+}
+EXPORT_SYMBOL(serio_reconnect);
+
+/*
+ * Submits register request to kseriod for subsequent execution.
+ * Note that port registration is always asynchronous.
+ */
+void __serio_register_port(struct serio *serio, struct module *owner)
+{
+	serio_init_port(serio);
+	serio_queue_event(serio, owner, SERIO_REGISTER_PORT);
+}
+EXPORT_SYMBOL(__serio_register_port);
+
+/*
+ * Synchronously unregisters serio port.
+ */
+void serio_unregister_port(struct serio *serio)
+{
+	mutex_lock(&serio_mutex);
+	serio_disconnect_port(serio);
+	serio_destroy_port(serio);
+	mutex_unlock(&serio_mutex);
+}
+EXPORT_SYMBOL(serio_unregister_port);
+
+/*
+ * Safely unregisters children ports if they are present.
+ */
+void serio_unregister_child_port(struct serio *serio)
+{
+	struct serio *s, *next;
+
+	mutex_lock(&serio_mutex);
+	list_for_each_entry_safe(s, next, &serio->children, child_node) {
+		serio_disconnect_port(s);
+		serio_destroy_port(s);
+	}
+	mutex_unlock(&serio_mutex);
+}
+EXPORT_SYMBOL(serio_unregister_child_port);
+
+
+/*
+ * Serio driver operations
+ */
+
+static ssize_t description_show(struct device_driver *drv, char *buf)
+{
+	struct serio_driver *driver = to_serio_driver(drv);
+	return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");
+}
+static DRIVER_ATTR_RO(description);
+
+static ssize_t bind_mode_show(struct device_driver *drv, char *buf)
+{
+	struct serio_driver *serio_drv = to_serio_driver(drv);
+	return sprintf(buf, "%s\n", serio_drv->manual_bind ? "manual" : "auto");
+}
+
+static ssize_t bind_mode_store(struct device_driver *drv, const char *buf, size_t count)
+{
+	struct serio_driver *serio_drv = to_serio_driver(drv);
+	int retval;
+
+	retval = count;
+	if (!strncmp(buf, "manual", count)) {
+		serio_drv->manual_bind = true;
+	} else if (!strncmp(buf, "auto", count)) {
+		serio_drv->manual_bind = false;
+	} else {
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+static DRIVER_ATTR_RW(bind_mode);
+
+static struct attribute *serio_driver_attrs[] = {
+	&driver_attr_description.attr,
+	&driver_attr_bind_mode.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(serio_driver);
+
+static int serio_driver_probe(struct device *dev)
+{
+	struct serio *serio = to_serio_port(dev);
+	struct serio_driver *drv = to_serio_driver(dev->driver);
+
+	return serio_connect_driver(serio, drv);
+}
+
+static int serio_driver_remove(struct device *dev)
+{
+	struct serio *serio = to_serio_port(dev);
+
+	serio_disconnect_driver(serio);
+	return 0;
+}
+
+static void serio_cleanup(struct serio *serio)
+{
+	mutex_lock(&serio->drv_mutex);
+	if (serio->drv && serio->drv->cleanup)
+		serio->drv->cleanup(serio);
+	mutex_unlock(&serio->drv_mutex);
+}
+
+static void serio_shutdown(struct device *dev)
+{
+	struct serio *serio = to_serio_port(dev);
+
+	serio_cleanup(serio);
+}
+
+static void serio_attach_driver(struct serio_driver *drv)
+{
+	int error;
+
+	error = driver_attach(&drv->driver);
+	if (error)
+		pr_warning("driver_attach() failed for %s with error %d\n",
+			   drv->driver.name, error);
+}
+
+int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name)
+{
+	bool manual_bind = drv->manual_bind;
+	int error;
+
+	drv->driver.bus = &serio_bus;
+	drv->driver.owner = owner;
+	drv->driver.mod_name = mod_name;
+
+	/*
+	 * Temporarily disable automatic binding because probing
+	 * takes long time and we are better off doing it in kseriod
+	 */
+	drv->manual_bind = true;
+
+	error = driver_register(&drv->driver);
+	if (error) {
+		pr_err("driver_register() failed for %s, error: %d\n",
+			drv->driver.name, error);
+		return error;
+	}
+
+	/*
+	 * Restore original bind mode and let kseriod bind the
+	 * driver to free ports
+	 */
+	if (!manual_bind) {
+		drv->manual_bind = false;
+		error = serio_queue_event(drv, NULL, SERIO_ATTACH_DRIVER);
+		if (error) {
+			driver_unregister(&drv->driver);
+			return error;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(__serio_register_driver);
+
+void serio_unregister_driver(struct serio_driver *drv)
+{
+	struct serio *serio;
+
+	mutex_lock(&serio_mutex);
+
+	drv->manual_bind = true;	/* so serio_find_driver ignores it */
+	serio_remove_pending_events(drv);
+
+start_over:
+	list_for_each_entry(serio, &serio_list, node) {
+		if (serio->drv == drv) {
+			serio_disconnect_port(serio);
+			serio_find_driver(serio);
+			/* we could've deleted some ports, restart */
+			goto start_over;
+		}
+	}
+
+	driver_unregister(&drv->driver);
+	mutex_unlock(&serio_mutex);
+}
+EXPORT_SYMBOL(serio_unregister_driver);
+
+static void serio_set_drv(struct serio *serio, struct serio_driver *drv)
+{
+	serio_pause_rx(serio);
+	serio->drv = drv;
+	serio_continue_rx(serio);
+}
+
+static int serio_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct serio *serio = to_serio_port(dev);
+	struct serio_driver *serio_drv = to_serio_driver(drv);
+
+	if (serio->manual_bind || serio_drv->manual_bind)
+		return 0;
+
+	return serio_match_port(serio_drv->id_table, serio);
+}
+
+#define SERIO_ADD_UEVENT_VAR(fmt, val...)				\
+	do {								\
+		int err = add_uevent_var(env, fmt, val);		\
+		if (err)						\
+			return err;					\
+	} while (0)
+
+static int serio_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct serio *serio;
+
+	if (!dev)
+		return -ENODEV;
+
+	serio = to_serio_port(dev);
+
+	SERIO_ADD_UEVENT_VAR("SERIO_TYPE=%02x", serio->id.type);
+	SERIO_ADD_UEVENT_VAR("SERIO_PROTO=%02x", serio->id.proto);
+	SERIO_ADD_UEVENT_VAR("SERIO_ID=%02x", serio->id.id);
+	SERIO_ADD_UEVENT_VAR("SERIO_EXTRA=%02x", serio->id.extra);
+
+	SERIO_ADD_UEVENT_VAR("MODALIAS=serio:ty%02Xpr%02Xid%02Xex%02X",
+				serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);
+
+	if (serio->firmware_id[0])
+		SERIO_ADD_UEVENT_VAR("SERIO_FIRMWARE_ID=%s",
+				     serio->firmware_id);
+
+	return 0;
+}
+#undef SERIO_ADD_UEVENT_VAR
+
+#ifdef CONFIG_PM
+static int serio_suspend(struct device *dev)
+{
+	struct serio *serio = to_serio_port(dev);
+
+	serio_cleanup(serio);
+
+	return 0;
+}
+
+static int serio_resume(struct device *dev)
+{
+	struct serio *serio = to_serio_port(dev);
+
+	/*
+	 * Driver reconnect can take a while, so better let kseriod
+	 * deal with it.
+	 */
+	serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
+
+	return 0;
+}
+
+static const struct dev_pm_ops serio_pm_ops = {
+	.suspend	= serio_suspend,
+	.resume		= serio_resume,
+	.poweroff	= serio_suspend,
+	.restore	= serio_resume,
+};
+#endif /* CONFIG_PM */
+
+/* called from serio_driver->connect/disconnect methods under serio_mutex */
+int serio_open(struct serio *serio, struct serio_driver *drv)
+{
+	serio_set_drv(serio, drv);
+
+	if (serio->open && serio->open(serio)) {
+		serio_set_drv(serio, NULL);
+		return -1;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(serio_open);
+
+/* called from serio_driver->connect/disconnect methods under serio_mutex */
+void serio_close(struct serio *serio)
+{
+	if (serio->close)
+		serio->close(serio);
+
+	serio_set_drv(serio, NULL);
+}
+EXPORT_SYMBOL(serio_close);
+
+irqreturn_t serio_interrupt(struct serio *serio,
+		unsigned char data, unsigned int dfl)
+{
+	unsigned long flags;
+	irqreturn_t ret = IRQ_NONE;
+
+	spin_lock_irqsave(&serio->lock, flags);
+
+        if (likely(serio->drv)) {
+                ret = serio->drv->interrupt(serio, data, dfl);
+	} else if (!dfl && device_is_registered(&serio->dev)) {
+		serio_rescan(serio);
+		ret = IRQ_HANDLED;
+	}
+
+	spin_unlock_irqrestore(&serio->lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL(serio_interrupt);
+
+struct bus_type serio_bus = {
+	.name		= "serio",
+	.drv_groups	= serio_driver_groups,
+	.match		= serio_bus_match,
+	.uevent		= serio_uevent,
+	.probe		= serio_driver_probe,
+	.remove		= serio_driver_remove,
+	.shutdown	= serio_shutdown,
+#ifdef CONFIG_PM
+	.pm		= &serio_pm_ops,
+#endif
+};
+EXPORT_SYMBOL(serio_bus);
+
+static int __init serio_init(void)
+{
+	int error;
+
+	error = bus_register(&serio_bus);
+	if (error) {
+		pr_err("Failed to register serio bus, error: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static void __exit serio_exit(void)
+{
+	bus_unregister(&serio_bus);
+
+	/*
+	 * There should not be any outstanding events but work may
+	 * still be scheduled so simply cancel it.
+	 */
+	cancel_work_sync(&serio_event_work);
+}
+
+subsys_initcall(serio_init);
+module_exit(serio_exit);
diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
new file mode 100644
index 0000000..71ef5d6
--- /dev/null
+++ b/drivers/input/serio/serio_raw.c
@@ -0,0 +1,444 @@
+/*
+ * Raw serio device providing access to a raw byte stream from underlying
+ * serio port. Closely emulates behavior of pre-2.6 /dev/psaux device
+ *
+ * Copyright (c) 2004 Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kref.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/major.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+
+#define DRIVER_DESC	"Raw serio driver"
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define SERIO_RAW_QUEUE_LEN	64
+struct serio_raw {
+	unsigned char queue[SERIO_RAW_QUEUE_LEN];
+	unsigned int tail, head;
+
+	char name[16];
+	struct kref kref;
+	struct serio *serio;
+	struct miscdevice dev;
+	wait_queue_head_t wait;
+	struct list_head client_list;
+	struct list_head node;
+	bool dead;
+};
+
+struct serio_raw_client {
+	struct fasync_struct *fasync;
+	struct serio_raw *serio_raw;
+	struct list_head node;
+};
+
+static DEFINE_MUTEX(serio_raw_mutex);
+static LIST_HEAD(serio_raw_list);
+
+/*********************************************************************
+ *             Interface with userspace (file operations)            *
+ *********************************************************************/
+
+static int serio_raw_fasync(int fd, struct file *file, int on)
+{
+	struct serio_raw_client *client = file->private_data;
+
+	return fasync_helper(fd, file, on, &client->fasync);
+}
+
+static struct serio_raw *serio_raw_locate(int minor)
+{
+	struct serio_raw *serio_raw;
+
+	list_for_each_entry(serio_raw, &serio_raw_list, node) {
+		if (serio_raw->dev.minor == minor)
+			return serio_raw;
+	}
+
+	return NULL;
+}
+
+static int serio_raw_open(struct inode *inode, struct file *file)
+{
+	struct serio_raw *serio_raw;
+	struct serio_raw_client *client;
+	int retval;
+
+	retval = mutex_lock_interruptible(&serio_raw_mutex);
+	if (retval)
+		return retval;
+
+	serio_raw = serio_raw_locate(iminor(inode));
+	if (!serio_raw) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	if (serio_raw->dead) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	client = kzalloc(sizeof(struct serio_raw_client), GFP_KERNEL);
+	if (!client) {
+		retval = -ENOMEM;
+		goto out;
+	}
+
+	client->serio_raw = serio_raw;
+	file->private_data = client;
+
+	kref_get(&serio_raw->kref);
+
+	serio_pause_rx(serio_raw->serio);
+	list_add_tail(&client->node, &serio_raw->client_list);
+	serio_continue_rx(serio_raw->serio);
+
+out:
+	mutex_unlock(&serio_raw_mutex);
+	return retval;
+}
+
+static void serio_raw_free(struct kref *kref)
+{
+	struct serio_raw *serio_raw =
+			container_of(kref, struct serio_raw, kref);
+
+	put_device(&serio_raw->serio->dev);
+	kfree(serio_raw);
+}
+
+static int serio_raw_release(struct inode *inode, struct file *file)
+{
+	struct serio_raw_client *client = file->private_data;
+	struct serio_raw *serio_raw = client->serio_raw;
+
+	serio_pause_rx(serio_raw->serio);
+	list_del(&client->node);
+	serio_continue_rx(serio_raw->serio);
+
+	kfree(client);
+
+	kref_put(&serio_raw->kref, serio_raw_free);
+
+	return 0;
+}
+
+static bool serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c)
+{
+	bool empty;
+
+	serio_pause_rx(serio_raw->serio);
+
+	empty = serio_raw->head == serio_raw->tail;
+	if (!empty) {
+		*c = serio_raw->queue[serio_raw->tail];
+		serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN;
+	}
+
+	serio_continue_rx(serio_raw->serio);
+
+	return !empty;
+}
+
+static ssize_t serio_raw_read(struct file *file, char __user *buffer,
+			      size_t count, loff_t *ppos)
+{
+	struct serio_raw_client *client = file->private_data;
+	struct serio_raw *serio_raw = client->serio_raw;
+	char uninitialized_var(c);
+	ssize_t read = 0;
+	int error;
+
+	for (;;) {
+		if (serio_raw->dead)
+			return -ENODEV;
+
+		if (serio_raw->head == serio_raw->tail &&
+		    (file->f_flags & O_NONBLOCK))
+			return -EAGAIN;
+
+		if (count == 0)
+			break;
+
+		while (read < count && serio_raw_fetch_byte(serio_raw, &c)) {
+			if (put_user(c, buffer++))
+				return -EFAULT;
+			read++;
+		}
+
+		if (read)
+			break;
+
+		if (!(file->f_flags & O_NONBLOCK)) {
+			error = wait_event_interruptible(serio_raw->wait,
+					serio_raw->head != serio_raw->tail ||
+					serio_raw->dead);
+			if (error)
+				return error;
+		}
+	}
+
+	return read;
+}
+
+static ssize_t serio_raw_write(struct file *file, const char __user *buffer,
+			       size_t count, loff_t *ppos)
+{
+	struct serio_raw_client *client = file->private_data;
+	struct serio_raw *serio_raw = client->serio_raw;
+	int retval = 0;
+	unsigned char c;
+
+	retval = mutex_lock_interruptible(&serio_raw_mutex);
+	if (retval)
+		return retval;
+
+	if (serio_raw->dead) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	if (count > 32)
+		count = 32;
+
+	while (count--) {
+		if (get_user(c, buffer++)) {
+			retval = -EFAULT;
+			goto out;
+		}
+
+		if (serio_write(serio_raw->serio, c)) {
+			/* Either signal error or partial write */
+			if (retval == 0)
+				retval = -EIO;
+			goto out;
+		}
+
+		retval++;
+	}
+
+out:
+	mutex_unlock(&serio_raw_mutex);
+	return retval;
+}
+
+static unsigned int serio_raw_poll(struct file *file, poll_table *wait)
+{
+	struct serio_raw_client *client = file->private_data;
+	struct serio_raw *serio_raw = client->serio_raw;
+	unsigned int mask;
+
+	poll_wait(file, &serio_raw->wait, wait);
+
+	mask = serio_raw->dead ? POLLHUP | POLLERR : POLLOUT | POLLWRNORM;
+	if (serio_raw->head != serio_raw->tail)
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+static const struct file_operations serio_raw_fops = {
+	.owner		= THIS_MODULE,
+	.open		= serio_raw_open,
+	.release	= serio_raw_release,
+	.read		= serio_raw_read,
+	.write		= serio_raw_write,
+	.poll		= serio_raw_poll,
+	.fasync		= serio_raw_fasync,
+	.llseek		= noop_llseek,
+};
+
+
+/*********************************************************************
+ *                   Interface with serio port                       *
+ *********************************************************************/
+
+static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data,
+					unsigned int dfl)
+{
+	struct serio_raw *serio_raw = serio_get_drvdata(serio);
+	struct serio_raw_client *client;
+	unsigned int head = serio_raw->head;
+
+	/* we are holding serio->lock here so we are protected */
+	serio_raw->queue[head] = data;
+	head = (head + 1) % SERIO_RAW_QUEUE_LEN;
+	if (likely(head != serio_raw->tail)) {
+		serio_raw->head = head;
+		list_for_each_entry(client, &serio_raw->client_list, node)
+			kill_fasync(&client->fasync, SIGIO, POLL_IN);
+		wake_up_interruptible(&serio_raw->wait);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
+{
+	static atomic_t serio_raw_no = ATOMIC_INIT(-1);
+	struct serio_raw *serio_raw;
+	int err;
+
+	serio_raw = kzalloc(sizeof(struct serio_raw), GFP_KERNEL);
+	if (!serio_raw) {
+		dev_dbg(&serio->dev, "can't allocate memory for a device\n");
+		return -ENOMEM;
+	}
+
+	snprintf(serio_raw->name, sizeof(serio_raw->name),
+		 "serio_raw%ld", (long)atomic_inc_return(&serio_raw_no));
+	kref_init(&serio_raw->kref);
+	INIT_LIST_HEAD(&serio_raw->client_list);
+	init_waitqueue_head(&serio_raw->wait);
+
+	serio_raw->serio = serio;
+	get_device(&serio->dev);
+
+	serio_set_drvdata(serio, serio_raw);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto err_free;
+
+	err = mutex_lock_killable(&serio_raw_mutex);
+	if (err)
+		goto err_close;
+
+	list_add_tail(&serio_raw->node, &serio_raw_list);
+	mutex_unlock(&serio_raw_mutex);
+
+	serio_raw->dev.minor = PSMOUSE_MINOR;
+	serio_raw->dev.name = serio_raw->name;
+	serio_raw->dev.parent = &serio->dev;
+	serio_raw->dev.fops = &serio_raw_fops;
+
+	err = misc_register(&serio_raw->dev);
+	if (err) {
+		serio_raw->dev.minor = MISC_DYNAMIC_MINOR;
+		err = misc_register(&serio_raw->dev);
+	}
+
+	if (err) {
+		dev_err(&serio->dev,
+			"failed to register raw access device for %s\n",
+			serio->phys);
+		goto err_unlink;
+	}
+
+	dev_info(&serio->dev, "raw access enabled on %s (%s, minor %d)\n",
+		 serio->phys, serio_raw->name, serio_raw->dev.minor);
+	return 0;
+
+err_unlink:
+	list_del_init(&serio_raw->node);
+err_close:
+	serio_close(serio);
+err_free:
+	serio_set_drvdata(serio, NULL);
+	kref_put(&serio_raw->kref, serio_raw_free);
+	return err;
+}
+
+static int serio_raw_reconnect(struct serio *serio)
+{
+	struct serio_raw *serio_raw = serio_get_drvdata(serio);
+	struct serio_driver *drv = serio->drv;
+
+	if (!drv || !serio_raw) {
+		dev_dbg(&serio->dev,
+			"reconnect request, but serio is disconnected, ignoring...\n");
+		return -1;
+	}
+
+	/*
+	 * Nothing needs to be done here, we just need this method to
+	 * keep the same device.
+	 */
+	return 0;
+}
+
+/*
+ * Wake up users waiting for IO so they can disconnect from
+ * dead device.
+ */
+static void serio_raw_hangup(struct serio_raw *serio_raw)
+{
+	struct serio_raw_client *client;
+
+	serio_pause_rx(serio_raw->serio);
+	list_for_each_entry(client, &serio_raw->client_list, node)
+		kill_fasync(&client->fasync, SIGIO, POLL_HUP);
+	serio_continue_rx(serio_raw->serio);
+
+	wake_up_interruptible(&serio_raw->wait);
+}
+
+
+static void serio_raw_disconnect(struct serio *serio)
+{
+	struct serio_raw *serio_raw = serio_get_drvdata(serio);
+
+	misc_deregister(&serio_raw->dev);
+
+	mutex_lock(&serio_raw_mutex);
+	serio_raw->dead = true;
+	list_del_init(&serio_raw->node);
+	mutex_unlock(&serio_raw_mutex);
+
+	serio_raw_hangup(serio_raw);
+
+	serio_close(serio);
+	kref_put(&serio_raw->kref, serio_raw_free);
+
+	serio_set_drvdata(serio, NULL);
+}
+
+static struct serio_device_id serio_raw_serio_ids[] = {
+	{
+		.type	= SERIO_8042,
+		.proto	= SERIO_ANY,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_8042_XL,
+		.proto	= SERIO_ANY,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, serio_raw_serio_ids);
+
+static struct serio_driver serio_raw_drv = {
+	.driver		= {
+		.name	= "serio_raw",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= serio_raw_serio_ids,
+	.interrupt	= serio_raw_interrupt,
+	.connect	= serio_raw_connect,
+	.reconnect	= serio_raw_reconnect,
+	.disconnect	= serio_raw_disconnect,
+	.manual_bind	= true,
+};
+
+module_serio_driver(serio_raw_drv);
diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c
new file mode 100644
index 0000000..9c927d3
--- /dev/null
+++ b/drivers/input/serio/serport.c
@@ -0,0 +1,300 @@
+/*
+ * Input device TTY line discipline
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ *
+ * This is a module that converts a tty line into a much simpler
+ * 'serial io port' abstraction that the input device drivers use.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <asm/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/tty.h>
+#include <linux/compat.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Input device TTY line discipline");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_MOUSE);
+
+#define SERPORT_BUSY	1
+#define SERPORT_ACTIVE	2
+#define SERPORT_DEAD	3
+
+struct serport {
+	struct tty_struct *tty;
+	wait_queue_head_t wait;
+	struct serio *serio;
+	struct serio_device_id id;
+	spinlock_t lock;
+	unsigned long flags;
+};
+
+/*
+ * Callback functions from the serio code.
+ */
+
+static int serport_serio_write(struct serio *serio, unsigned char data)
+{
+	struct serport *serport = serio->port_data;
+	return -(serport->tty->ops->write(serport->tty, &data, 1) != 1);
+}
+
+static int serport_serio_open(struct serio *serio)
+{
+	struct serport *serport = serio->port_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&serport->lock, flags);
+	set_bit(SERPORT_ACTIVE, &serport->flags);
+	spin_unlock_irqrestore(&serport->lock, flags);
+
+	return 0;
+}
+
+
+static void serport_serio_close(struct serio *serio)
+{
+	struct serport *serport = serio->port_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&serport->lock, flags);
+	clear_bit(SERPORT_ACTIVE, &serport->flags);
+	set_bit(SERPORT_DEAD, &serport->flags);
+	spin_unlock_irqrestore(&serport->lock, flags);
+
+	wake_up_interruptible(&serport->wait);
+}
+
+/*
+ * serport_ldisc_open() is the routine that is called upon setting our line
+ * discipline on a tty. It prepares the serio struct.
+ */
+
+static int serport_ldisc_open(struct tty_struct *tty)
+{
+	struct serport *serport;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	serport = kzalloc(sizeof(struct serport), GFP_KERNEL);
+	if (!serport)
+		return -ENOMEM;
+
+	serport->tty = tty;
+	spin_lock_init(&serport->lock);
+	init_waitqueue_head(&serport->wait);
+
+	tty->disc_data = serport;
+	tty->receive_room = 256;
+	set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+
+	return 0;
+}
+
+/*
+ * serport_ldisc_close() is the opposite of serport_ldisc_open()
+ */
+
+static void serport_ldisc_close(struct tty_struct *tty)
+{
+	struct serport *serport = (struct serport *) tty->disc_data;
+
+	kfree(serport);
+}
+
+/*
+ * serport_ldisc_receive() is called by the low level tty driver when characters
+ * are ready for us. We forward the characters and flags, one by one to the
+ * 'interrupt' routine.
+ */
+
+static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
+{
+	struct serport *serport = (struct serport*) tty->disc_data;
+	unsigned long flags;
+	unsigned int ch_flags = 0;
+	int i;
+
+	spin_lock_irqsave(&serport->lock, flags);
+
+	if (!test_bit(SERPORT_ACTIVE, &serport->flags))
+		goto out;
+
+	for (i = 0; i < count; i++) {
+		if (fp) {
+			switch (fp[i]) {
+			case TTY_FRAME:
+				ch_flags = SERIO_FRAME;
+				break;
+
+			case TTY_PARITY:
+				ch_flags = SERIO_PARITY;
+				break;
+
+			default:
+				ch_flags = 0;
+				break;
+			}
+		}
+
+		serio_interrupt(serport->serio, cp[i], ch_flags);
+	}
+
+out:
+	spin_unlock_irqrestore(&serport->lock, flags);
+}
+
+/*
+ * serport_ldisc_read() just waits indefinitely if everything goes well.
+ * However, when the serio driver closes the serio port, it finishes,
+ * returning 0 characters.
+ */
+
+static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, unsigned char __user * buf, size_t nr)
+{
+	struct serport *serport = (struct serport*) tty->disc_data;
+	struct serio *serio;
+
+	if (test_and_set_bit(SERPORT_BUSY, &serport->flags))
+		return -EBUSY;
+
+	serport->serio = serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!serio)
+		return -ENOMEM;
+
+	strlcpy(serio->name, "Serial port", sizeof(serio->name));
+	snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty));
+	serio->id = serport->id;
+	serio->id.type = SERIO_RS232;
+	serio->write = serport_serio_write;
+	serio->open = serport_serio_open;
+	serio->close = serport_serio_close;
+	serio->port_data = serport;
+	serio->dev.parent = tty->dev;
+
+	serio_register_port(serport->serio);
+	printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty));
+
+	wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags));
+	serio_unregister_port(serport->serio);
+	serport->serio = NULL;
+
+	clear_bit(SERPORT_DEAD, &serport->flags);
+	clear_bit(SERPORT_BUSY, &serport->flags);
+
+	return 0;
+}
+
+static void serport_set_type(struct tty_struct *tty, unsigned long type)
+{
+	struct serport *serport = tty->disc_data;
+
+	serport->id.proto = type & 0x000000ff;
+	serport->id.id    = (type & 0x0000ff00) >> 8;
+	serport->id.extra = (type & 0x00ff0000) >> 16;
+}
+
+/*
+ * serport_ldisc_ioctl() allows to set the port protocol, and device ID
+ */
+
+static int serport_ldisc_ioctl(struct tty_struct *tty, struct file *file,
+			       unsigned int cmd, unsigned long arg)
+{
+	if (cmd == SPIOCSTYPE) {
+		unsigned long type;
+
+		if (get_user(type, (unsigned long __user *) arg))
+			return -EFAULT;
+
+		serport_set_type(tty, type);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+#ifdef CONFIG_COMPAT
+#define COMPAT_SPIOCSTYPE	_IOW('q', 0x01, compat_ulong_t)
+static long serport_ldisc_compat_ioctl(struct tty_struct *tty,
+				       struct file *file,
+				       unsigned int cmd, unsigned long arg)
+{
+	if (cmd == COMPAT_SPIOCSTYPE) {
+		void __user *uarg = compat_ptr(arg);
+		compat_ulong_t compat_type;
+
+		if (get_user(compat_type, (compat_ulong_t __user *)uarg))
+			return -EFAULT;
+
+		serport_set_type(tty, compat_type);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+#endif
+
+static void serport_ldisc_write_wakeup(struct tty_struct * tty)
+{
+	struct serport *serport = (struct serport *) tty->disc_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&serport->lock, flags);
+	if (test_bit(SERPORT_ACTIVE, &serport->flags))
+		serio_drv_write_wakeup(serport->serio);
+	spin_unlock_irqrestore(&serport->lock, flags);
+}
+
+/*
+ * The line discipline structure.
+ */
+
+static struct tty_ldisc_ops serport_ldisc = {
+	.owner =	THIS_MODULE,
+	.name =		"input",
+	.open =		serport_ldisc_open,
+	.close =	serport_ldisc_close,
+	.read =		serport_ldisc_read,
+	.ioctl =	serport_ldisc_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl =	serport_ldisc_compat_ioctl,
+#endif
+	.receive_buf =	serport_ldisc_receive,
+	.write_wakeup =	serport_ldisc_write_wakeup
+};
+
+/*
+ * The functions for insering/removing us as a module.
+ */
+
+static int __init serport_init(void)
+{
+	int retval;
+	retval = tty_register_ldisc(N_MOUSE, &serport_ldisc);
+	if (retval)
+		printk(KERN_ERR "serport.c: Error registering line discipline.\n");
+
+	return  retval;
+}
+
+static void __exit serport_exit(void)
+{
+	tty_unregister_ldisc(N_MOUSE);
+}
+
+module_init(serport_init);
+module_exit(serport_exit);
diff --git a/drivers/input/serio/sun4i-ps2.c b/drivers/input/serio/sun4i-ps2.c
new file mode 100644
index 0000000..04b96fe
--- /dev/null
+++ b/drivers/input/serio/sun4i-ps2.c
@@ -0,0 +1,340 @@
+/*
+ *	Driver for Allwinner A10 PS2 host controller
+ *
+ *	Author: Vishnu Patekar <vishnupatekar0510@gmail.com>
+ *		Aaron.maoye <leafy.myeh@newbietech.com>
+ */
+
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#define DRIVER_NAME		"sun4i-ps2"
+
+/* register offset definitions */
+#define PS2_REG_GCTL		0x00	/* PS2 Module Global Control Reg */
+#define PS2_REG_DATA		0x04	/* PS2 Module Data Reg		*/
+#define PS2_REG_LCTL		0x08	/* PS2 Module Line Control Reg */
+#define PS2_REG_LSTS		0x0C	/* PS2 Module Line Status Reg	*/
+#define PS2_REG_FCTL		0x10	/* PS2 Module FIFO Control Reg */
+#define PS2_REG_FSTS		0x14	/* PS2 Module FIFO Status Reg	*/
+#define PS2_REG_CLKDR		0x18	/* PS2 Module Clock Divider Reg*/
+
+/*  PS2 GLOBAL CONTROL REGISTER PS2_GCTL */
+#define PS2_GCTL_INTFLAG	BIT(4)
+#define PS2_GCTL_INTEN		BIT(3)
+#define PS2_GCTL_RESET		BIT(2)
+#define PS2_GCTL_MASTER		BIT(1)
+#define PS2_GCTL_BUSEN		BIT(0)
+
+/* PS2 LINE CONTROL REGISTER */
+#define PS2_LCTL_NOACK		BIT(18)
+#define PS2_LCTL_TXDTOEN	BIT(8)
+#define PS2_LCTL_STOPERREN	BIT(3)
+#define PS2_LCTL_ACKERREN	BIT(2)
+#define PS2_LCTL_PARERREN	BIT(1)
+#define PS2_LCTL_RXDTOEN	BIT(0)
+
+/* PS2 LINE STATUS REGISTER */
+#define PS2_LSTS_TXTDO		BIT(8)
+#define PS2_LSTS_STOPERR	BIT(3)
+#define PS2_LSTS_ACKERR		BIT(2)
+#define PS2_LSTS_PARERR		BIT(1)
+#define PS2_LSTS_RXTDO		BIT(0)
+
+#define PS2_LINE_ERROR_BIT \
+	(PS2_LSTS_TXTDO | PS2_LSTS_STOPERR | PS2_LSTS_ACKERR | \
+	PS2_LSTS_PARERR | PS2_LSTS_RXTDO)
+
+/* PS2 FIFO CONTROL REGISTER */
+#define PS2_FCTL_TXRST		BIT(17)
+#define PS2_FCTL_RXRST		BIT(16)
+#define PS2_FCTL_TXUFIEN	BIT(10)
+#define PS2_FCTL_TXOFIEN	BIT(9)
+#define PS2_FCTL_TXRDYIEN	BIT(8)
+#define PS2_FCTL_RXUFIEN	BIT(2)
+#define PS2_FCTL_RXOFIEN	BIT(1)
+#define PS2_FCTL_RXRDYIEN	BIT(0)
+
+/* PS2 FIFO STATUS REGISTER */
+#define PS2_FSTS_TXUF		BIT(10)
+#define PS2_FSTS_TXOF		BIT(9)
+#define PS2_FSTS_TXRDY		BIT(8)
+#define PS2_FSTS_RXUF		BIT(2)
+#define PS2_FSTS_RXOF		BIT(1)
+#define PS2_FSTS_RXRDY		BIT(0)
+
+#define PS2_FIFO_ERROR_BIT \
+	(PS2_FSTS_TXUF | PS2_FSTS_TXOF | PS2_FSTS_RXUF | PS2_FSTS_RXOF)
+
+#define PS2_SAMPLE_CLK		1000000
+#define PS2_SCLK		125000
+
+struct sun4i_ps2data {
+	struct serio *serio;
+	struct device *dev;
+
+	/* IO mapping base */
+	void __iomem	*reg_base;
+
+	/* clock management */
+	struct clk	*clk;
+
+	/* irq */
+	spinlock_t	lock;
+	int		irq;
+};
+
+static irqreturn_t sun4i_ps2_interrupt(int irq, void *dev_id)
+{
+	struct sun4i_ps2data *drvdata = dev_id;
+	u32 intr_status;
+	u32 fifo_status;
+	unsigned char byte;
+	unsigned int rxflags = 0;
+	u32 rval;
+
+	spin_lock(&drvdata->lock);
+
+	/* Get the PS/2 interrupts and clear them */
+	intr_status  = readl(drvdata->reg_base + PS2_REG_LSTS);
+	fifo_status  = readl(drvdata->reg_base + PS2_REG_FSTS);
+
+	/* Check line status register */
+	if (intr_status & PS2_LINE_ERROR_BIT) {
+		rxflags = (intr_status & PS2_LINE_ERROR_BIT) ? SERIO_FRAME : 0;
+		rxflags |= (intr_status & PS2_LSTS_PARERR) ? SERIO_PARITY : 0;
+		rxflags |= (intr_status & PS2_LSTS_PARERR) ? SERIO_TIMEOUT : 0;
+
+		rval = PS2_LSTS_TXTDO | PS2_LSTS_STOPERR | PS2_LSTS_ACKERR |
+			PS2_LSTS_PARERR | PS2_LSTS_RXTDO;
+		writel(rval, drvdata->reg_base + PS2_REG_LSTS);
+	}
+
+	/* Check FIFO status register */
+	if (fifo_status & PS2_FIFO_ERROR_BIT) {
+		rval = PS2_FSTS_TXUF | PS2_FSTS_TXOF | PS2_FSTS_TXRDY |
+			PS2_FSTS_RXUF | PS2_FSTS_RXOF | PS2_FSTS_RXRDY;
+		writel(rval, drvdata->reg_base + PS2_REG_FSTS);
+	}
+
+	rval = (fifo_status >> 16) & 0x3;
+	while (rval--) {
+		byte = readl(drvdata->reg_base + PS2_REG_DATA) & 0xff;
+		serio_interrupt(drvdata->serio, byte, rxflags);
+	}
+
+	writel(intr_status, drvdata->reg_base + PS2_REG_LSTS);
+	writel(fifo_status, drvdata->reg_base + PS2_REG_FSTS);
+
+	spin_unlock(&drvdata->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int sun4i_ps2_open(struct serio *serio)
+{
+	struct sun4i_ps2data *drvdata = serio->port_data;
+	u32 src_clk = 0;
+	u32 clk_scdf;
+	u32 clk_pcdf;
+	u32 rval;
+	unsigned long flags;
+
+	/* Set line control and enable interrupt */
+	rval = PS2_LCTL_STOPERREN | PS2_LCTL_ACKERREN
+		| PS2_LCTL_PARERREN | PS2_LCTL_RXDTOEN;
+	writel(rval, drvdata->reg_base + PS2_REG_LCTL);
+
+	/* Reset FIFO */
+	rval = PS2_FCTL_TXRST | PS2_FCTL_RXRST | PS2_FCTL_TXUFIEN
+		| PS2_FCTL_TXOFIEN | PS2_FCTL_RXUFIEN
+		| PS2_FCTL_RXOFIEN | PS2_FCTL_RXRDYIEN;
+
+	writel(rval, drvdata->reg_base + PS2_REG_FCTL);
+
+	src_clk = clk_get_rate(drvdata->clk);
+	/* Set clock divider register */
+	clk_scdf = src_clk / PS2_SAMPLE_CLK - 1;
+	clk_pcdf = PS2_SAMPLE_CLK / PS2_SCLK - 1;
+	rval = (clk_scdf << 8) | clk_pcdf;
+	writel(rval, drvdata->reg_base + PS2_REG_CLKDR);
+
+	/* Set global control register */
+	rval = PS2_GCTL_RESET | PS2_GCTL_INTEN | PS2_GCTL_MASTER
+		| PS2_GCTL_BUSEN;
+
+	spin_lock_irqsave(&drvdata->lock, flags);
+	writel(rval, drvdata->reg_base + PS2_REG_GCTL);
+	spin_unlock_irqrestore(&drvdata->lock, flags);
+
+	return 0;
+}
+
+static void sun4i_ps2_close(struct serio *serio)
+{
+	struct sun4i_ps2data *drvdata = serio->port_data;
+	u32 rval;
+
+	/* Shut off the interrupt */
+	rval = readl(drvdata->reg_base + PS2_REG_GCTL);
+	writel(rval & ~(PS2_GCTL_INTEN), drvdata->reg_base + PS2_REG_GCTL);
+
+	synchronize_irq(drvdata->irq);
+}
+
+static int sun4i_ps2_write(struct serio *serio, unsigned char val)
+{
+	unsigned long expire = jiffies + msecs_to_jiffies(10000);
+	struct sun4i_ps2data *drvdata = serio->port_data;
+
+	do {
+		if (readl(drvdata->reg_base + PS2_REG_FSTS) & PS2_FSTS_TXRDY) {
+			writel(val, drvdata->reg_base + PS2_REG_DATA);
+			return 0;
+		}
+	} while (time_before(jiffies, expire));
+
+	return SERIO_TIMEOUT;
+}
+
+static int sun4i_ps2_probe(struct platform_device *pdev)
+{
+	struct resource *res; /* IO mem resources */
+	struct sun4i_ps2data *drvdata;
+	struct serio *serio;
+	struct device *dev = &pdev->dev;
+	unsigned int irq;
+	int error;
+
+	drvdata = kzalloc(sizeof(struct sun4i_ps2data), GFP_KERNEL);
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!drvdata || !serio) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	spin_lock_init(&drvdata->lock);
+
+	/* IO */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "failed to locate registers\n");
+		error = -ENXIO;
+		goto err_free_mem;
+	}
+
+	drvdata->reg_base = ioremap(res->start, resource_size(res));
+	if (!drvdata->reg_base) {
+		dev_err(dev, "failed to map registers\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	drvdata->clk = clk_get(dev, NULL);
+	if (IS_ERR(drvdata->clk)) {
+		error = PTR_ERR(drvdata->clk);
+		dev_err(dev, "couldn't get clock %d\n", error);
+		goto err_ioremap;
+	}
+
+	error = clk_prepare_enable(drvdata->clk);
+	if (error) {
+		dev_err(dev, "failed to enable clock %d\n", error);
+		goto err_clk;
+	}
+
+	serio->id.type = SERIO_8042;
+	serio->write = sun4i_ps2_write;
+	serio->open = sun4i_ps2_open;
+	serio->close = sun4i_ps2_close;
+	serio->port_data = drvdata;
+	serio->dev.parent = dev;
+	strlcpy(serio->name, dev_name(dev), sizeof(serio->name));
+	strlcpy(serio->phys, dev_name(dev), sizeof(serio->phys));
+
+	/* shutoff interrupt */
+	writel(0, drvdata->reg_base + PS2_REG_GCTL);
+
+	/* Get IRQ for the device */
+	irq = platform_get_irq(pdev, 0);
+	if (!irq) {
+		dev_err(dev, "no IRQ found\n");
+		error = -ENXIO;
+		goto err_disable_clk;
+	}
+
+	drvdata->irq = irq;
+	drvdata->serio = serio;
+	drvdata->dev = dev;
+
+	error = request_irq(drvdata->irq, sun4i_ps2_interrupt, 0,
+			    DRIVER_NAME, drvdata);
+	if (error) {
+		dev_err(drvdata->dev, "failed to allocate interrupt %d: %d\n",
+			drvdata->irq, error);
+		goto err_disable_clk;
+	}
+
+	serio_register_port(serio);
+	platform_set_drvdata(pdev, drvdata);
+
+	return 0;	/* success */
+
+err_disable_clk:
+	clk_disable_unprepare(drvdata->clk);
+err_clk:
+	clk_put(drvdata->clk);
+err_ioremap:
+	iounmap(drvdata->reg_base);
+err_free_mem:
+	kfree(serio);
+	kfree(drvdata);
+	return error;
+}
+
+static int sun4i_ps2_remove(struct platform_device *pdev)
+{
+	struct sun4i_ps2data *drvdata = platform_get_drvdata(pdev);
+
+	serio_unregister_port(drvdata->serio);
+
+	free_irq(drvdata->irq, drvdata);
+
+	clk_disable_unprepare(drvdata->clk);
+	clk_put(drvdata->clk);
+
+	iounmap(drvdata->reg_base);
+
+	kfree(drvdata);
+
+	return 0;
+}
+
+static const struct of_device_id sun4i_ps2_match[] = {
+	{ .compatible = "allwinner,sun4i-a10-ps2", },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, sun4i_ps2_match);
+
+static struct platform_driver sun4i_ps2_driver = {
+	.probe		= sun4i_ps2_probe,
+	.remove		= sun4i_ps2_remove,
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = sun4i_ps2_match,
+	},
+};
+module_platform_driver(sun4i_ps2_driver);
+
+MODULE_AUTHOR("Vishnu Patekar <vishnupatekar0510@gmail.com>");
+MODULE_AUTHOR("Aaron.maoye <leafy.myeh@newbietech.com>");
+MODULE_DESCRIPTION("Allwinner A10/Sun4i PS/2 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/serio/userio.c b/drivers/input/serio/userio.c
new file mode 100644
index 0000000..df1fd41
--- /dev/null
+++ b/drivers/input/serio/userio.c
@@ -0,0 +1,285 @@
+/*
+ * userio kernel serio device emulation module
+ * Copyright (C) 2015 Red Hat
+ * Copyright (C) 2015 Stephen Chandler Paul <thatslyude@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ * General Public License for more details.
+ */
+
+#include <linux/circ_buf.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <uapi/linux/userio.h>
+
+#define USERIO_NAME		"userio"
+#define USERIO_BUFSIZE		16
+
+static struct miscdevice userio_misc;
+
+struct userio_device {
+	struct serio *serio;
+	struct mutex mutex;
+
+	bool running;
+
+	u8 head;
+	u8 tail;
+
+	spinlock_t buf_lock;
+	unsigned char buf[USERIO_BUFSIZE];
+
+	wait_queue_head_t waitq;
+};
+
+/**
+ * userio_device_write - Write data from serio to a userio device in userspace
+ * @id: The serio port for the userio device
+ * @val: The data to write to the device
+ */
+static int userio_device_write(struct serio *id, unsigned char val)
+{
+	struct userio_device *userio = id->port_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&userio->buf_lock, flags);
+
+	userio->buf[userio->head] = val;
+	userio->head = (userio->head + 1) % USERIO_BUFSIZE;
+
+	if (userio->head == userio->tail)
+		dev_warn(userio_misc.this_device,
+			 "Buffer overflowed, userio client isn't keeping up");
+
+	spin_unlock_irqrestore(&userio->buf_lock, flags);
+
+	wake_up_interruptible(&userio->waitq);
+
+	return 0;
+}
+
+static int userio_char_open(struct inode *inode, struct file *file)
+{
+	struct userio_device *userio;
+
+	userio = kzalloc(sizeof(struct userio_device), GFP_KERNEL);
+	if (!userio)
+		return -ENOMEM;
+
+	mutex_init(&userio->mutex);
+	spin_lock_init(&userio->buf_lock);
+	init_waitqueue_head(&userio->waitq);
+
+	userio->serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!userio->serio) {
+		kfree(userio);
+		return -ENOMEM;
+	}
+
+	userio->serio->write = userio_device_write;
+	userio->serio->port_data = userio;
+
+	file->private_data = userio;
+
+	return 0;
+}
+
+static int userio_char_release(struct inode *inode, struct file *file)
+{
+	struct userio_device *userio = file->private_data;
+
+	if (userio->running) {
+		/*
+		 * Don't free the serio port here, serio_unregister_port()
+		 * does it for us.
+		 */
+		serio_unregister_port(userio->serio);
+	} else {
+		kfree(userio->serio);
+	}
+
+	kfree(userio);
+
+	return 0;
+}
+
+static ssize_t userio_char_read(struct file *file, char __user *user_buffer,
+				size_t count, loff_t *ppos)
+{
+	struct userio_device *userio = file->private_data;
+	int error;
+	size_t nonwrap_len, copylen;
+	unsigned char buf[USERIO_BUFSIZE];
+	unsigned long flags;
+
+	/*
+	 * By the time we get here, the data that was waiting might have
+	 * been taken by another thread. Grab the buffer lock and check if
+	 * there's still any data waiting, otherwise repeat this process
+	 * until we have data (unless the file descriptor is non-blocking
+	 * of course).
+	 */
+	for (;;) {
+		spin_lock_irqsave(&userio->buf_lock, flags);
+
+		nonwrap_len = CIRC_CNT_TO_END(userio->head,
+					      userio->tail,
+					      USERIO_BUFSIZE);
+		copylen = min(nonwrap_len, count);
+		if (copylen) {
+			memcpy(buf, &userio->buf[userio->tail], copylen);
+			userio->tail = (userio->tail + copylen) %
+							USERIO_BUFSIZE;
+		}
+
+		spin_unlock_irqrestore(&userio->buf_lock, flags);
+
+		if (nonwrap_len)
+			break;
+
+		/* buffer was/is empty */
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		/*
+		 * count == 0 is special - no IO is done but we check
+		 * for error conditions (see above).
+		 */
+		if (count == 0)
+			return 0;
+
+		error = wait_event_interruptible(userio->waitq,
+						 userio->head != userio->tail);
+		if (error)
+			return error;
+	}
+
+	if (copylen)
+		if (copy_to_user(user_buffer, buf, copylen))
+			return -EFAULT;
+
+	return copylen;
+}
+
+static ssize_t userio_char_write(struct file *file, const char __user *buffer,
+				 size_t count, loff_t *ppos)
+{
+	struct userio_device *userio = file->private_data;
+	struct userio_cmd cmd;
+	int error;
+
+	if (count != sizeof(cmd)) {
+		dev_warn(userio_misc.this_device, "Invalid payload size\n");
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&cmd, buffer, sizeof(cmd)))
+		return -EFAULT;
+
+	error = mutex_lock_interruptible(&userio->mutex);
+	if (error)
+		return error;
+
+	switch (cmd.type) {
+	case USERIO_CMD_REGISTER:
+		if (!userio->serio->id.type) {
+			dev_warn(userio_misc.this_device,
+				 "No port type given on /dev/userio\n");
+
+			error = -EINVAL;
+			goto out;
+		}
+
+		if (userio->running) {
+			dev_warn(userio_misc.this_device,
+				 "Begin command sent, but we're already running\n");
+			error = -EBUSY;
+			goto out;
+		}
+
+		userio->running = true;
+		serio_register_port(userio->serio);
+		break;
+
+	case USERIO_CMD_SET_PORT_TYPE:
+		if (userio->running) {
+			dev_warn(userio_misc.this_device,
+				 "Can't change port type on an already running userio instance\n");
+			error = -EBUSY;
+			goto out;
+		}
+
+		userio->serio->id.type = cmd.data;
+		break;
+
+	case USERIO_CMD_SEND_INTERRUPT:
+		if (!userio->running) {
+			dev_warn(userio_misc.this_device,
+				 "The device must be registered before sending interrupts\n");
+			error = -ENODEV;
+			goto out;
+		}
+
+		serio_interrupt(userio->serio, cmd.data, 0);
+		break;
+
+	default:
+		error = -EOPNOTSUPP;
+		goto out;
+	}
+
+out:
+	mutex_unlock(&userio->mutex);
+	return error ?: count;
+}
+
+static unsigned int userio_char_poll(struct file *file, poll_table *wait)
+{
+	struct userio_device *userio = file->private_data;
+
+	poll_wait(file, &userio->waitq, wait);
+
+	if (userio->head != userio->tail)
+		return POLLIN | POLLRDNORM;
+
+	return 0;
+}
+
+static const struct file_operations userio_fops = {
+	.owner		= THIS_MODULE,
+	.open		= userio_char_open,
+	.release	= userio_char_release,
+	.read		= userio_char_read,
+	.write		= userio_char_write,
+	.poll		= userio_char_poll,
+	.llseek		= no_llseek,
+};
+
+static struct miscdevice userio_misc = {
+	.fops	= &userio_fops,
+	.minor	= USERIO_MINOR,
+	.name	= USERIO_NAME,
+};
+module_driver(userio_misc, misc_register, misc_deregister);
+
+MODULE_ALIAS_MISCDEV(USERIO_MINOR);
+MODULE_ALIAS("devname:" USERIO_NAME);
+
+MODULE_AUTHOR("Stephen Chandler Paul <thatslyude@gmail.com>");
+MODULE_DESCRIPTION("Virtual Serio Device Support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c
new file mode 100644
index 0000000..5223cbf
--- /dev/null
+++ b/drivers/input/serio/xilinx_ps2.c
@@ -0,0 +1,376 @@
+/*
+ * Xilinx XPS PS/2 device driver
+ *
+ * (c) 2005 MontaVista Software, Inc.
+ * (c) 2008 Xilinx, Inc.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+#define DRIVER_NAME		"xilinx_ps2"
+
+/* Register offsets for the xps2 device */
+#define XPS2_SRST_OFFSET	0x00000000 /* Software Reset register */
+#define XPS2_STATUS_OFFSET	0x00000004 /* Status register */
+#define XPS2_RX_DATA_OFFSET	0x00000008 /* Receive Data register */
+#define XPS2_TX_DATA_OFFSET	0x0000000C /* Transmit Data register */
+#define XPS2_GIER_OFFSET	0x0000002C /* Global Interrupt Enable reg */
+#define XPS2_IPISR_OFFSET	0x00000030 /* Interrupt Status register */
+#define XPS2_IPIER_OFFSET	0x00000038 /* Interrupt Enable register */
+
+/* Reset Register Bit Definitions */
+#define XPS2_SRST_RESET		0x0000000A /* Software Reset  */
+
+/* Status Register Bit Positions */
+#define XPS2_STATUS_RX_FULL	0x00000001 /* Receive Full  */
+#define XPS2_STATUS_TX_FULL	0x00000002 /* Transmit Full  */
+
+/* Bit definitions for ISR/IER registers. Both the registers have the same bit
+ * definitions and are only defined once. */
+#define XPS2_IPIXR_WDT_TOUT	0x00000001 /* Watchdog Timeout Interrupt */
+#define XPS2_IPIXR_TX_NOACK	0x00000002 /* Transmit No ACK Interrupt */
+#define XPS2_IPIXR_TX_ACK	0x00000004 /* Transmit ACK (Data) Interrupt */
+#define XPS2_IPIXR_RX_OVF	0x00000008 /* Receive Overflow Interrupt */
+#define XPS2_IPIXR_RX_ERR	0x00000010 /* Receive Error Interrupt */
+#define XPS2_IPIXR_RX_FULL	0x00000020 /* Receive Data Interrupt */
+
+/* Mask for all the Transmit Interrupts */
+#define XPS2_IPIXR_TX_ALL	(XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_TX_ACK)
+
+/* Mask for all the Receive Interrupts */
+#define XPS2_IPIXR_RX_ALL	(XPS2_IPIXR_RX_OVF | XPS2_IPIXR_RX_ERR |  \
+				 XPS2_IPIXR_RX_FULL)
+
+/* Mask for all the Interrupts */
+#define XPS2_IPIXR_ALL		(XPS2_IPIXR_TX_ALL | XPS2_IPIXR_RX_ALL |  \
+				 XPS2_IPIXR_WDT_TOUT)
+
+/* Global Interrupt Enable mask */
+#define XPS2_GIER_GIE_MASK	0x80000000
+
+struct xps2data {
+	int irq;
+	spinlock_t lock;
+	void __iomem *base_address;	/* virt. address of control registers */
+	unsigned int flags;
+	struct serio *serio;		/* serio */
+	struct device *dev;
+};
+
+/************************************/
+/* XPS PS/2 data transmission calls */
+/************************************/
+
+/**
+ * xps2_recv() - attempts to receive a byte from the PS/2 port.
+ * @drvdata:	pointer to ps2 device private data structure
+ * @byte:	address where the read data will be copied
+ *
+ * If there is any data available in the PS/2 receiver, this functions reads
+ * the data, otherwise it returns error.
+ */
+static int xps2_recv(struct xps2data *drvdata, u8 *byte)
+{
+	u32 sr;
+	int status = -1;
+
+	/* If there is data available in the PS/2 receiver, read it */
+	sr = in_be32(drvdata->base_address + XPS2_STATUS_OFFSET);
+	if (sr & XPS2_STATUS_RX_FULL) {
+		*byte = in_be32(drvdata->base_address + XPS2_RX_DATA_OFFSET);
+		status = 0;
+	}
+
+	return status;
+}
+
+/*********************/
+/* Interrupt handler */
+/*********************/
+static irqreturn_t xps2_interrupt(int irq, void *dev_id)
+{
+	struct xps2data *drvdata = dev_id;
+	u32 intr_sr;
+	u8 c;
+	int status;
+
+	/* Get the PS/2 interrupts and clear them */
+	intr_sr = in_be32(drvdata->base_address + XPS2_IPISR_OFFSET);
+	out_be32(drvdata->base_address + XPS2_IPISR_OFFSET, intr_sr);
+
+	/* Check which interrupt is active */
+	if (intr_sr & XPS2_IPIXR_RX_OVF)
+		dev_warn(drvdata->dev, "receive overrun error\n");
+
+	if (intr_sr & XPS2_IPIXR_RX_ERR)
+		drvdata->flags |= SERIO_PARITY;
+
+	if (intr_sr & (XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_WDT_TOUT))
+		drvdata->flags |= SERIO_TIMEOUT;
+
+	if (intr_sr & XPS2_IPIXR_RX_FULL) {
+		status = xps2_recv(drvdata, &c);
+
+		/* Error, if a byte is not received */
+		if (status) {
+			dev_err(drvdata->dev,
+				"wrong rcvd byte count (%d)\n", status);
+		} else {
+			serio_interrupt(drvdata->serio, c, drvdata->flags);
+			drvdata->flags = 0;
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*******************/
+/* serio callbacks */
+/*******************/
+
+/**
+ * sxps2_write() - sends a byte out through the PS/2 port.
+ * @pserio:	pointer to the serio structure of the PS/2 port
+ * @c:		data that needs to be written to the PS/2 port
+ *
+ * This function checks if the PS/2 transmitter is empty and sends a byte.
+ * Otherwise it returns error. Transmission fails only when nothing is connected
+ * to the PS/2 port. Thats why, we do not try to resend the data in case of a
+ * failure.
+ */
+static int sxps2_write(struct serio *pserio, unsigned char c)
+{
+	struct xps2data *drvdata = pserio->port_data;
+	unsigned long flags;
+	u32 sr;
+	int status = -1;
+
+	spin_lock_irqsave(&drvdata->lock, flags);
+
+	/* If the PS/2 transmitter is empty send a byte of data */
+	sr = in_be32(drvdata->base_address + XPS2_STATUS_OFFSET);
+	if (!(sr & XPS2_STATUS_TX_FULL)) {
+		out_be32(drvdata->base_address + XPS2_TX_DATA_OFFSET, c);
+		status = 0;
+	}
+
+	spin_unlock_irqrestore(&drvdata->lock, flags);
+
+	return status;
+}
+
+/**
+ * sxps2_open() - called when a port is opened by the higher layer.
+ * @pserio:	pointer to the serio structure of the PS/2 device
+ *
+ * This function requests irq and enables interrupts for the PS/2 device.
+ */
+static int sxps2_open(struct serio *pserio)
+{
+	struct xps2data *drvdata = pserio->port_data;
+	int error;
+	u8 c;
+
+	error = request_irq(drvdata->irq, &xps2_interrupt, 0,
+				DRIVER_NAME, drvdata);
+	if (error) {
+		dev_err(drvdata->dev,
+			"Couldn't allocate interrupt %d\n", drvdata->irq);
+		return error;
+	}
+
+	/* start reception by enabling the interrupts */
+	out_be32(drvdata->base_address + XPS2_GIER_OFFSET, XPS2_GIER_GIE_MASK);
+	out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, XPS2_IPIXR_RX_ALL);
+	(void)xps2_recv(drvdata, &c);
+
+	return 0;		/* success */
+}
+
+/**
+ * sxps2_close() - frees the interrupt.
+ * @pserio:	pointer to the serio structure of the PS/2 device
+ *
+ * This function frees the irq and disables interrupts for the PS/2 device.
+ */
+static void sxps2_close(struct serio *pserio)
+{
+	struct xps2data *drvdata = pserio->port_data;
+
+	/* Disable the PS2 interrupts */
+	out_be32(drvdata->base_address + XPS2_GIER_OFFSET, 0x00);
+	out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, 0x00);
+	free_irq(drvdata->irq, drvdata);
+}
+
+/**
+ * xps2_of_probe - probe method for the PS/2 device.
+ * @of_dev:	pointer to OF device structure
+ * @match:	pointer to the structure used for matching a device
+ *
+ * This function probes the PS/2 device in the device tree.
+ * It initializes the driver data structure and the hardware.
+ * It returns 0, if the driver is bound to the PS/2 device, or a negative
+ * value if there is an error.
+ */
+static int xps2_of_probe(struct platform_device *ofdev)
+{
+	struct resource r_mem; /* IO mem resources */
+	struct xps2data *drvdata;
+	struct serio *serio;
+	struct device *dev = &ofdev->dev;
+	resource_size_t remap_size, phys_addr;
+	unsigned int irq;
+	int error;
+
+	dev_info(dev, "Device Tree Probing \'%s\'\n",
+			ofdev->dev.of_node->name);
+
+	/* Get iospace for the device */
+	error = of_address_to_resource(ofdev->dev.of_node, 0, &r_mem);
+	if (error) {
+		dev_err(dev, "invalid address\n");
+		return error;
+	}
+
+	/* Get IRQ for the device */
+	irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
+	if (!irq) {
+		dev_err(dev, "no IRQ found\n");
+		return -ENODEV;
+	}
+
+	drvdata = kzalloc(sizeof(struct xps2data), GFP_KERNEL);
+	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!drvdata || !serio) {
+		error = -ENOMEM;
+		goto failed1;
+	}
+
+	spin_lock_init(&drvdata->lock);
+	drvdata->irq = irq;
+	drvdata->serio = serio;
+	drvdata->dev = dev;
+
+	phys_addr = r_mem.start;
+	remap_size = resource_size(&r_mem);
+	if (!request_mem_region(phys_addr, remap_size, DRIVER_NAME)) {
+		dev_err(dev, "Couldn't lock memory region at 0x%08llX\n",
+			(unsigned long long)phys_addr);
+		error = -EBUSY;
+		goto failed1;
+	}
+
+	/* Fill in configuration data and add them to the list */
+	drvdata->base_address = ioremap(phys_addr, remap_size);
+	if (drvdata->base_address == NULL) {
+		dev_err(dev, "Couldn't ioremap memory at 0x%08llX\n",
+			(unsigned long long)phys_addr);
+		error = -EFAULT;
+		goto failed2;
+	}
+
+	/* Disable all the interrupts, just in case */
+	out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, 0);
+
+	/* Reset the PS2 device and abort any current transaction, to make sure
+	 * we have the PS2 in a good state */
+	out_be32(drvdata->base_address + XPS2_SRST_OFFSET, XPS2_SRST_RESET);
+
+	dev_info(dev, "Xilinx PS2 at 0x%08llX mapped to 0x%p, irq=%d\n",
+		 (unsigned long long)phys_addr, drvdata->base_address,
+		 drvdata->irq);
+
+	serio->id.type = SERIO_8042;
+	serio->write = sxps2_write;
+	serio->open = sxps2_open;
+	serio->close = sxps2_close;
+	serio->port_data = drvdata;
+	serio->dev.parent = dev;
+	snprintf(serio->name, sizeof(serio->name),
+		 "Xilinx XPS PS/2 at %08llX", (unsigned long long)phys_addr);
+	snprintf(serio->phys, sizeof(serio->phys),
+		 "xilinxps2/serio at %08llX", (unsigned long long)phys_addr);
+
+	serio_register_port(serio);
+
+	platform_set_drvdata(ofdev, drvdata);
+	return 0;		/* success */
+
+failed2:
+	release_mem_region(phys_addr, remap_size);
+failed1:
+	kfree(serio);
+	kfree(drvdata);
+
+	return error;
+}
+
+/**
+ * xps2_of_remove - unbinds the driver from the PS/2 device.
+ * @of_dev:	pointer to OF device structure
+ *
+ * This function is called if a device is physically removed from the system or
+ * if the driver module is being unloaded. It frees any resources allocated to
+ * the device.
+ */
+static int xps2_of_remove(struct platform_device *of_dev)
+{
+	struct xps2data *drvdata = platform_get_drvdata(of_dev);
+	struct resource r_mem; /* IO mem resources */
+
+	serio_unregister_port(drvdata->serio);
+	iounmap(drvdata->base_address);
+
+	/* Get iospace of the device */
+	if (of_address_to_resource(of_dev->dev.of_node, 0, &r_mem))
+		dev_err(drvdata->dev, "invalid address\n");
+	else
+		release_mem_region(r_mem.start, resource_size(&r_mem));
+
+	kfree(drvdata);
+
+	return 0;
+}
+
+/* Match table for of_platform binding */
+static const struct of_device_id xps2_of_match[] = {
+	{ .compatible = "xlnx,xps-ps2-1.00.a", },
+	{ /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, xps2_of_match);
+
+static struct platform_driver xps2_of_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = xps2_of_match,
+	},
+	.probe		= xps2_of_probe,
+	.remove		= xps2_of_remove,
+};
+module_platform_driver(xps2_of_driver);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Xilinx XPS PS/2 driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c
new file mode 100644
index 0000000..e7409c4
--- /dev/null
+++ b/drivers/input/sparse-keymap.c
@@ -0,0 +1,332 @@
+/*
+ * Generic support for sparse keymaps
+ *
+ * Copyright (c) 2009 Dmitry Torokhov
+ *
+ * Derived from wistron button driver:
+ * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
+ * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
+ * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION("Generic support for sparse keymaps");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
+
+static unsigned int sparse_keymap_get_key_index(struct input_dev *dev,
+						const struct key_entry *k)
+{
+	struct key_entry *key;
+	unsigned int idx = 0;
+
+	for (key = dev->keycode; key->type != KE_END; key++) {
+		if (key->type == KE_KEY) {
+			if (key == k)
+				break;
+			idx++;
+		}
+	}
+
+	return idx;
+}
+
+static struct key_entry *sparse_keymap_entry_by_index(struct input_dev *dev,
+						      unsigned int index)
+{
+	struct key_entry *key;
+	unsigned int key_cnt = 0;
+
+	for (key = dev->keycode; key->type != KE_END; key++)
+		if (key->type == KE_KEY)
+			if (key_cnt++ == index)
+				return key;
+
+	return NULL;
+}
+
+/**
+ * sparse_keymap_entry_from_scancode - perform sparse keymap lookup
+ * @dev: Input device using sparse keymap
+ * @code: Scan code
+ *
+ * This function is used to perform &struct key_entry lookup in an
+ * input device using sparse keymap.
+ */
+struct key_entry *sparse_keymap_entry_from_scancode(struct input_dev *dev,
+						    unsigned int code)
+{
+	struct key_entry *key;
+
+	for (key = dev->keycode; key->type != KE_END; key++)
+		if (code == key->code)
+			return key;
+
+	return NULL;
+}
+EXPORT_SYMBOL(sparse_keymap_entry_from_scancode);
+
+/**
+ * sparse_keymap_entry_from_keycode - perform sparse keymap lookup
+ * @dev: Input device using sparse keymap
+ * @keycode: Key code
+ *
+ * This function is used to perform &struct key_entry lookup in an
+ * input device using sparse keymap.
+ */
+struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev,
+						   unsigned int keycode)
+{
+	struct key_entry *key;
+
+	for (key = dev->keycode; key->type != KE_END; key++)
+		if (key->type == KE_KEY && keycode == key->keycode)
+			return key;
+
+	return NULL;
+}
+EXPORT_SYMBOL(sparse_keymap_entry_from_keycode);
+
+static struct key_entry *sparse_keymap_locate(struct input_dev *dev,
+					const struct input_keymap_entry *ke)
+{
+	struct key_entry *key;
+	unsigned int scancode;
+
+	if (ke->flags & INPUT_KEYMAP_BY_INDEX)
+		key = sparse_keymap_entry_by_index(dev, ke->index);
+	else if (input_scancode_to_scalar(ke, &scancode) == 0)
+		key = sparse_keymap_entry_from_scancode(dev, scancode);
+	else
+		key = NULL;
+
+	return key;
+}
+
+static int sparse_keymap_getkeycode(struct input_dev *dev,
+				    struct input_keymap_entry *ke)
+{
+	const struct key_entry *key;
+
+	if (dev->keycode) {
+		key = sparse_keymap_locate(dev, ke);
+		if (key && key->type == KE_KEY) {
+			ke->keycode = key->keycode;
+			if (!(ke->flags & INPUT_KEYMAP_BY_INDEX))
+				ke->index =
+					sparse_keymap_get_key_index(dev, key);
+			ke->len = sizeof(key->code);
+			memcpy(ke->scancode, &key->code, sizeof(key->code));
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int sparse_keymap_setkeycode(struct input_dev *dev,
+				    const struct input_keymap_entry *ke,
+				    unsigned int *old_keycode)
+{
+	struct key_entry *key;
+
+	if (dev->keycode) {
+		key = sparse_keymap_locate(dev, ke);
+		if (key && key->type == KE_KEY) {
+			*old_keycode = key->keycode;
+			key->keycode = ke->keycode;
+			set_bit(ke->keycode, dev->keybit);
+			if (!sparse_keymap_entry_from_keycode(dev, *old_keycode))
+				clear_bit(*old_keycode, dev->keybit);
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * sparse_keymap_setup - set up sparse keymap for an input device
+ * @dev: Input device
+ * @keymap: Keymap in form of array of &key_entry structures ending
+ *	with %KE_END type entry
+ * @setup: Function that can be used to adjust keymap entries
+ *	depending on device's deeds, may be %NULL
+ *
+ * The function calculates size and allocates copy of the original
+ * keymap after which sets up input device event bits appropriately.
+ * Before destroying input device allocated keymap should be freed
+ * with a call to sparse_keymap_free().
+ */
+int sparse_keymap_setup(struct input_dev *dev,
+			const struct key_entry *keymap,
+			int (*setup)(struct input_dev *, struct key_entry *))
+{
+	size_t map_size = 1; /* to account for the last KE_END entry */
+	const struct key_entry *e;
+	struct key_entry *map, *entry;
+	int i;
+	int error;
+
+	for (e = keymap; e->type != KE_END; e++)
+		map_size++;
+
+	map = kcalloc(map_size, sizeof(struct key_entry), GFP_KERNEL);
+	if (!map)
+		return -ENOMEM;
+
+	memcpy(map, keymap, map_size * sizeof(struct key_entry));
+
+	for (i = 0; i < map_size; i++) {
+		entry = &map[i];
+
+		if (setup) {
+			error = setup(dev, entry);
+			if (error)
+				goto err_out;
+		}
+
+		switch (entry->type) {
+		case KE_KEY:
+			__set_bit(EV_KEY, dev->evbit);
+			__set_bit(entry->keycode, dev->keybit);
+			break;
+
+		case KE_SW:
+		case KE_VSW:
+			__set_bit(EV_SW, dev->evbit);
+			__set_bit(entry->sw.code, dev->swbit);
+			break;
+		}
+	}
+
+	if (test_bit(EV_KEY, dev->evbit)) {
+		__set_bit(KEY_UNKNOWN, dev->keybit);
+		__set_bit(EV_MSC, dev->evbit);
+		__set_bit(MSC_SCAN, dev->mscbit);
+	}
+
+	dev->keycode = map;
+	dev->keycodemax = map_size;
+	dev->getkeycode = sparse_keymap_getkeycode;
+	dev->setkeycode = sparse_keymap_setkeycode;
+
+	return 0;
+
+ err_out:
+	kfree(map);
+	return error;
+}
+EXPORT_SYMBOL(sparse_keymap_setup);
+
+/**
+ * sparse_keymap_free - free memory allocated for sparse keymap
+ * @dev: Input device using sparse keymap
+ *
+ * This function is used to free memory allocated by sparse keymap
+ * in an input device that was set up by sparse_keymap_setup().
+ * NOTE: It is safe to cal this function while input device is
+ * still registered (however the drivers should care not to try to
+ * use freed keymap and thus have to shut off interrupts/polling
+ * before freeing the keymap).
+ */
+void sparse_keymap_free(struct input_dev *dev)
+{
+	unsigned long flags;
+
+	/*
+	 * Take event lock to prevent racing with input_get_keycode()
+	 * and input_set_keycode() if we are called while input device
+	 * is still registered.
+	 */
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	kfree(dev->keycode);
+	dev->keycode = NULL;
+	dev->keycodemax = 0;
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+EXPORT_SYMBOL(sparse_keymap_free);
+
+/**
+ * sparse_keymap_report_entry - report event corresponding to given key entry
+ * @dev: Input device for which event should be reported
+ * @ke: key entry describing event
+ * @value: Value that should be reported (ignored by %KE_SW entries)
+ * @autorelease: Signals whether release event should be emitted for %KE_KEY
+ *	entries right after reporting press event, ignored by all other
+ *	entries
+ *
+ * This function is used to report input event described by given
+ * &struct key_entry.
+ */
+void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *ke,
+				unsigned int value, bool autorelease)
+{
+	switch (ke->type) {
+	case KE_KEY:
+		input_event(dev, EV_MSC, MSC_SCAN, ke->code);
+		input_report_key(dev, ke->keycode, value);
+		input_sync(dev);
+		if (value && autorelease) {
+			input_report_key(dev, ke->keycode, 0);
+			input_sync(dev);
+		}
+		break;
+
+	case KE_SW:
+		value = ke->sw.value;
+		/* fall through */
+
+	case KE_VSW:
+		input_report_switch(dev, ke->sw.code, value);
+		break;
+	}
+}
+EXPORT_SYMBOL(sparse_keymap_report_entry);
+
+/**
+ * sparse_keymap_report_event - report event corresponding to given scancode
+ * @dev: Input device using sparse keymap
+ * @code: Scan code
+ * @value: Value that should be reported (ignored by %KE_SW entries)
+ * @autorelease: Signals whether release event should be emitted for %KE_KEY
+ *	entries right after reporting press event, ignored by all other
+ *	entries
+ *
+ * This function is used to perform lookup in an input device using sparse
+ * keymap and report corresponding event. Returns %true if lookup was
+ * successful and %false otherwise.
+ */
+bool sparse_keymap_report_event(struct input_dev *dev, unsigned int code,
+				unsigned int value, bool autorelease)
+{
+	const struct key_entry *ke =
+		sparse_keymap_entry_from_scancode(dev, code);
+	struct key_entry unknown_ke;
+
+	if (ke) {
+		sparse_keymap_report_entry(dev, ke, value, autorelease);
+		return true;
+	}
+
+	/* Report an unknown key event as a debugging aid */
+	unknown_ke.type = KE_KEY;
+	unknown_ke.code = code;
+	unknown_ke.keycode = KEY_UNKNOWN;
+	sparse_keymap_report_entry(dev, &unknown_ke, value, true);
+
+	return false;
+}
+EXPORT_SYMBOL(sparse_keymap_report_event);
+
diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig
new file mode 100644
index 0000000..623bb9e
--- /dev/null
+++ b/drivers/input/tablet/Kconfig
@@ -0,0 +1,86 @@
+#
+# Tablet driver configuration
+#
+menuconfig INPUT_TABLET
+	bool "Tablets"
+	help
+	  Say Y here, and a list of supported tablets will be displayed.
+	  This option doesn't affect the kernel.
+
+	  If unsure, say Y.
+
+if INPUT_TABLET
+
+config TABLET_USB_ACECAD
+	tristate "Acecad Flair tablet support (USB)"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you want to use the USB version of the Acecad Flair
+	  tablet.  Make sure to say Y to "Mouse support"
+	  (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
+	  (CONFIG_INPUT_EVDEV) as well.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called acecad.
+
+config TABLET_USB_AIPTEK
+	tristate "Aiptek 6000U/8000U and Genius G_PEN tablet support (USB)"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you want to use the USB version of the Aiptek 6000U,
+	  Aiptek 8000U or Genius G-PEN 560 tablet.  Make sure to say Y to
+	  "Mouse support" (CONFIG_INPUT_MOUSEDEV) and/or "Event interface
+	  support" (CONFIG_INPUT_EVDEV) as well.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called aiptek.
+
+config TABLET_USB_GTCO
+        tristate "GTCO CalComp/InterWrite USB Support"
+        depends on USB && INPUT
+        help
+          Say Y here if you want to use the USB version of the GTCO
+          CalComp/InterWrite Tablet.  Make sure to say Y to "Mouse support"
+          (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
+          (CONFIG_INPUT_EVDEV) as well.
+
+          To compile this driver as a module, choose M here: the
+          module will be called gtco.
+
+config TABLET_USB_HANWANG
+	tristate "Hanwang Art Master III tablet support (USB)"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you want to use the USB version of the Hanwang Art
+	  Master III tablet.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hanwang.
+
+config TABLET_USB_KBTAB
+	tristate "KB Gear JamStudio tablet support (USB)"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  Say Y here if you want to use the USB version of the KB Gear
+	  JamStudio tablet.  Make sure to say Y to "Mouse support"
+	  (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
+	  (CONFIG_INPUT_EVDEV) as well.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called kbtab.
+
+config TABLET_SERIAL_WACOM4
+	tristate "Wacom protocol 4 serial tablet support"
+	select SERIO
+	help
+	  Say Y here if you want to use Wacom protocol 4 serial tablets.
+	  E.g. serial versions of the Cintiq, Graphire or Penpartner.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wacom_serial4.
+
+endif
diff --git a/drivers/input/tablet/Makefile b/drivers/input/tablet/Makefile
new file mode 100644
index 0000000..2e13010
--- /dev/null
+++ b/drivers/input/tablet/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the tablet drivers
+#
+
+
+obj-$(CONFIG_TABLET_USB_ACECAD)	+= acecad.o
+obj-$(CONFIG_TABLET_USB_AIPTEK)	+= aiptek.o
+obj-$(CONFIG_TABLET_USB_GTCO)	+= gtco.o
+obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o
+obj-$(CONFIG_TABLET_USB_KBTAB)	+= kbtab.o
+obj-$(CONFIG_TABLET_SERIAL_WACOM4) += wacom_serial4.o
diff --git a/drivers/input/tablet/acecad.c b/drivers/input/tablet/acecad.c
new file mode 100644
index 0000000..889f6b7
--- /dev/null
+++ b/drivers/input/tablet/acecad.c
@@ -0,0 +1,278 @@
+/*
+ *  Copyright (c) 2001-2005 Edouard TISSERANT   <edouard.tisserant@wanadoo.fr>
+ *  Copyright (c) 2004-2005 Stephane VOLTZ      <svoltz@numericable.fr>
+ *
+ *  USB Acecad "Acecad Flair" tablet support
+ *
+ *  Changelog:
+ *      v3.2 - Added sysfs support
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v3.2"
+#define DRIVER_DESC    "USB Acecad Flair tablet driver"
+#define DRIVER_LICENSE "GPL"
+#define DRIVER_AUTHOR  "Edouard TISSERANT <edouard.tisserant@wanadoo.fr>"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_ACECAD	0x0460
+#define USB_DEVICE_ID_FLAIR	0x0004
+#define USB_DEVICE_ID_302	0x0008
+
+struct usb_acecad {
+	char name[128];
+	char phys[64];
+	struct usb_device *usbdev;
+	struct usb_interface *intf;
+	struct input_dev *input;
+	struct urb *irq;
+
+	unsigned char *data;
+	dma_addr_t data_dma;
+};
+
+static void usb_acecad_irq(struct urb *urb)
+{
+	struct usb_acecad *acecad = urb->context;
+	unsigned char *data = acecad->data;
+	struct input_dev *dev = acecad->input;
+	struct usb_interface *intf = acecad->intf;
+	int prox, status;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&intf->dev, "%s - urb shutting down with status: %d\n",
+			__func__, urb->status);
+		return;
+	default:
+		dev_dbg(&intf->dev, "%s - nonzero urb status received: %d\n",
+			__func__, urb->status);
+		goto resubmit;
+	}
+
+	prox = (data[0] & 0x04) >> 2;
+	input_report_key(dev, BTN_TOOL_PEN, prox);
+
+	if (prox) {
+		int x = data[1] | (data[2] << 8);
+		int y = data[3] | (data[4] << 8);
+		/* Pressure should compute the same way for flair and 302 */
+		int pressure = data[5] | (data[6] << 8);
+		int touch = data[0] & 0x01;
+		int stylus = (data[0] & 0x10) >> 4;
+		int stylus2 = (data[0] & 0x20) >> 5;
+		input_report_abs(dev, ABS_X, x);
+		input_report_abs(dev, ABS_Y, y);
+		input_report_abs(dev, ABS_PRESSURE, pressure);
+		input_report_key(dev, BTN_TOUCH, touch);
+		input_report_key(dev, BTN_STYLUS, stylus);
+		input_report_key(dev, BTN_STYLUS2, stylus2);
+	}
+
+	/* event termination */
+	input_sync(dev);
+
+resubmit:
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status)
+		dev_err(&intf->dev,
+			"can't resubmit intr, %s-%s/input0, status %d\n",
+			acecad->usbdev->bus->bus_name,
+			acecad->usbdev->devpath, status);
+}
+
+static int usb_acecad_open(struct input_dev *dev)
+{
+	struct usb_acecad *acecad = input_get_drvdata(dev);
+
+	acecad->irq->dev = acecad->usbdev;
+	if (usb_submit_urb(acecad->irq, GFP_KERNEL))
+		return -EIO;
+
+	return 0;
+}
+
+static void usb_acecad_close(struct input_dev *dev)
+{
+	struct usb_acecad *acecad = input_get_drvdata(dev);
+
+	usb_kill_urb(acecad->irq);
+}
+
+static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_host_interface *interface = intf->cur_altsetting;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_acecad *acecad;
+	struct input_dev *input_dev;
+	int pipe, maxp;
+	int err;
+
+	if (interface->desc.bNumEndpoints != 1)
+		return -ENODEV;
+
+	endpoint = &interface->endpoint[0].desc;
+
+	if (!usb_endpoint_is_int_in(endpoint))
+		return -ENODEV;
+
+	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	acecad = kzalloc(sizeof(struct usb_acecad), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!acecad || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	acecad->data = usb_alloc_coherent(dev, 8, GFP_KERNEL, &acecad->data_dma);
+	if (!acecad->data) {
+		err= -ENOMEM;
+		goto fail1;
+	}
+
+	acecad->irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!acecad->irq) {
+		err = -ENOMEM;
+		goto fail2;
+	}
+
+	acecad->usbdev = dev;
+	acecad->intf = intf;
+	acecad->input = input_dev;
+
+	if (dev->manufacturer)
+		strlcpy(acecad->name, dev->manufacturer, sizeof(acecad->name));
+
+	if (dev->product) {
+		if (dev->manufacturer)
+			strlcat(acecad->name, " ", sizeof(acecad->name));
+		strlcat(acecad->name, dev->product, sizeof(acecad->name));
+	}
+
+	usb_make_path(dev, acecad->phys, sizeof(acecad->phys));
+	strlcat(acecad->phys, "/input0", sizeof(acecad->phys));
+
+	input_dev->name = acecad->name;
+	input_dev->phys = acecad->phys;
+	usb_to_input_id(dev, &input_dev->id);
+	input_dev->dev.parent = &intf->dev;
+
+	input_set_drvdata(input_dev, acecad);
+
+	input_dev->open = usb_acecad_open;
+	input_dev->close = usb_acecad_close;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_DIGI)] = BIT_MASK(BTN_TOOL_PEN) |
+		BIT_MASK(BTN_TOUCH) | BIT_MASK(BTN_STYLUS) |
+		BIT_MASK(BTN_STYLUS2);
+
+	switch (id->driver_info) {
+	case 0:
+		input_set_abs_params(input_dev, ABS_X, 0, 5000, 4, 0);
+		input_set_abs_params(input_dev, ABS_Y, 0, 3750, 4, 0);
+		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 512, 0, 0);
+		if (!strlen(acecad->name))
+			snprintf(acecad->name, sizeof(acecad->name),
+				"USB Acecad Flair Tablet %04x:%04x",
+				le16_to_cpu(dev->descriptor.idVendor),
+				le16_to_cpu(dev->descriptor.idProduct));
+		break;
+
+	case 1:
+		input_set_abs_params(input_dev, ABS_X, 0, 53000, 4, 0);
+		input_set_abs_params(input_dev, ABS_Y, 0, 2250, 4, 0);
+		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1024, 0, 0);
+		if (!strlen(acecad->name))
+			snprintf(acecad->name, sizeof(acecad->name),
+				"USB Acecad 302 Tablet %04x:%04x",
+				le16_to_cpu(dev->descriptor.idVendor),
+				le16_to_cpu(dev->descriptor.idProduct));
+		break;
+	}
+
+	usb_fill_int_urb(acecad->irq, dev, pipe,
+			acecad->data, maxp > 8 ? 8 : maxp,
+			usb_acecad_irq, acecad, endpoint->bInterval);
+	acecad->irq->transfer_dma = acecad->data_dma;
+	acecad->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	err = input_register_device(acecad->input);
+	if (err)
+		goto fail3;
+
+	usb_set_intfdata(intf, acecad);
+
+	return 0;
+
+ fail3:	usb_free_urb(acecad->irq);
+ fail2:	usb_free_coherent(dev, 8, acecad->data, acecad->data_dma);
+ fail1: input_free_device(input_dev);
+	kfree(acecad);
+	return err;
+}
+
+static void usb_acecad_disconnect(struct usb_interface *intf)
+{
+	struct usb_acecad *acecad = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+
+	input_unregister_device(acecad->input);
+	usb_free_urb(acecad->irq);
+	usb_free_coherent(acecad->usbdev, 8, acecad->data, acecad->data_dma);
+	kfree(acecad);
+}
+
+static struct usb_device_id usb_acecad_id_table [] = {
+	{ USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_FLAIR), .driver_info = 0 },
+	{ USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_302),	 .driver_info = 1 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, usb_acecad_id_table);
+
+static struct usb_driver usb_acecad_driver = {
+	.name =		"usb_acecad",
+	.probe =	usb_acecad_probe,
+	.disconnect =	usb_acecad_disconnect,
+	.id_table =	usb_acecad_id_table,
+};
+
+module_usb_driver(usb_acecad_driver);
diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c
new file mode 100644
index 0000000..78ca448
--- /dev/null
+++ b/drivers/input/tablet/aiptek.c
@@ -0,0 +1,1949 @@
+/*
+ *  Native support for the Aiptek HyperPen USB Tablets
+ *  (4000U/5000U/6000U/8000U/12000U)
+ *
+ *  Copyright (c) 2001      Chris Atenasio   <chris@crud.net>
+ *  Copyright (c) 2002-2004 Bryan W. Headley <bwheadley@earthlink.net>
+ *
+ *  based on wacom.c by
+ *     Vojtech Pavlik      <vojtech@suse.cz>
+ *     Andreas Bach Aaen   <abach@stofanet.dk>
+ *     Clifford Wolf       <clifford@clifford.at>
+ *     Sam Mosel           <sam.mosel@computer.org>
+ *     James E. Blair      <corvus@gnu.org>
+ *     Daniel Egger        <egger@suse.de>
+ *
+ *  Many thanks to Oliver Kuechemann for his support.
+ *
+ *  ChangeLog:
+ *      v0.1 - Initial release
+ *      v0.2 - Hack to get around fake event 28's. (Bryan W. Headley)
+ *      v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002)
+ *             Released to Linux 2.4.19 and 2.5.x
+ *      v0.4 - Rewrote substantial portions of the code to deal with
+ *             corrected control sequences, timing, dynamic configuration,
+ *             support of 6000U - 12000U, procfs, and macro key support
+ *             (Jan-1-2003 - Feb-5-2003, Bryan W. Headley)
+ *      v1.0 - Added support for diagnostic messages, count of messages
+ *             received from URB - Mar-8-2003, Bryan W. Headley
+ *      v1.1 - added support for tablet resolution, changed DV and proximity
+ *             some corrections - Jun-22-2003, martin schneebacher
+ *           - Added support for the sysfs interface, deprecating the
+ *             procfs interface for 2.5.x kernel. Also added support for
+ *             Wheel command. Bryan W. Headley July-15-2003.
+ *      v1.2 - Reworked jitter timer as a kernel thread.
+ *             Bryan W. Headley November-28-2003/Jan-10-2004.
+ *      v1.3 - Repaired issue of kernel thread going nuts on single-processor
+ *             machines, introduced programmableDelay as a command line
+ *             parameter. Feb 7 2004, Bryan W. Headley.
+ *      v1.4 - Re-wire jitter so it does not require a thread. Courtesy of
+ *             Rene van Paassen. Added reporting of physical pointer device
+ *             (e.g., stylus, mouse in reports 2, 3, 4, 5. We don't know
+ *             for reports 1, 6.)
+ *             what physical device reports for reports 1, 6.) Also enabled
+ *             MOUSE and LENS tool button modes. Renamed "rubber" to "eraser".
+ *             Feb 20, 2004, Bryan W. Headley.
+ *      v1.5 - Added previousJitterable, so we don't do jitter delay when the
+ *             user is holding a button down for periods of time.
+ *
+ * NOTE:
+ *      This kernel driver is augmented by the "Aiptek" XFree86 input
+ *      driver for your X server, as well as the Gaiptek GUI Front-end
+ *      "Tablet Manager".
+ *      These three products are highly interactive with one another,
+ *      so therefore it's easier to document them all as one subsystem.
+ *      Please visit the project's "home page", located at,
+ *      http://aiptektablet.sourceforge.net.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v2.3 (May 2, 2007)"
+#define DRIVER_AUTHOR  "Bryan W. Headley/Chris Atenasio/Cedric Brun/Rene van Paassen"
+#define DRIVER_DESC    "Aiptek HyperPen USB Tablet Driver (Linux 2.6.x)"
+
+/*
+ * Aiptek status packet:
+ *
+ * (returned as Report 1 - relative coordinates from mouse and stylus)
+ *
+ *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
+ * byte0   0     0     0     0     0     0     0     1
+ * byte1   0     0     0     0     0    BS2   BS    Tip
+ * byte2  X7    X6    X5    X4    X3    X2    X1    X0
+ * byte3  Y7    Y6    Y5    Y4    Y3    Y2    Y1    Y0
+ *
+ * (returned as Report 2 - absolute coordinates from the stylus)
+ *
+ *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
+ * byte0   0     0     0     0     0     0     1     0
+ * byte1  X7    X6    X5    X4    X3    X2    X1    X0
+ * byte2  X15   X14   X13   X12   X11   X10   X9    X8
+ * byte3  Y7    Y6    Y5    Y4    Y3    Y2    Y1    Y0
+ * byte4  Y15   Y14   Y13   Y12   Y11   Y10   Y9    Y8
+ * byte5   *     *     *    BS2   BS1   Tip   IR    DV
+ * byte6  P7    P6    P5    P4    P3    P2    P1    P0
+ * byte7  P15   P14   P13   P12   P11   P10   P9    P8
+ *
+ * (returned as Report 3 - absolute coordinates from the mouse)
+ *
+ *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
+ * byte0   0     0     0     0     0     0     1     1
+ * byte1  X7    X6    X5    X4    X3    X2    X1    X0
+ * byte2  X15   X14   X13   X12   X11   X10   X9    X8
+ * byte3  Y7    Y6    Y5    Y4    Y3    Y2    Y1    Y0
+ * byte4  Y15   Y14   Y13   Y12   Y11   Y10   Y9    Y8
+ * byte5   *     *     *    BS2   BS1   Tip   IR    DV
+ * byte6  P7    P6    P5    P4    P3    P2    P1    P0
+ * byte7  P15   P14   P13   P12   P11   P10   P9    P8
+ *
+ * (returned as Report 4 - macrokeys from the stylus)
+ *
+ *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
+ * byte0   0     0     0     0     0     1     0     0
+ * byte1   0     0     0    BS2   BS    Tip   IR    DV
+ * byte2   0     0     0     0     0     0     1     0
+ * byte3   0     0     0    K4    K3    K2    K1    K0
+ * byte4  P7    P6    P5    P4    P3    P2    P1    P0
+ * byte5  P15   P14   P13   P12   P11   P10   P9    P8
+ *
+ * (returned as Report 5 - macrokeys from the mouse)
+ *
+ *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
+ * byte0   0     0     0     0     0     1     0     1
+ * byte1   0     0     0    BS2   BS    Tip   IR    DV
+ * byte2   0     0     0     0     0     0     1     0
+ * byte3   0     0     0    K4    K3    K2    K1    K0
+ * byte4  P7    P6    P5    P4    P3    P2    P1    P0
+ * byte5  P15   P14   P13   P12   P11   P10   P9    P8
+ *
+ * IR: In Range = Proximity on
+ * DV = Data Valid
+ * BS = Barrel Switch (as in, macro keys)
+ * BS2 also referred to as Tablet Pick
+ *
+ * Command Summary:
+ *
+ * Use report_type CONTROL (3)
+ * Use report_id   2
+ *
+ * Command/Data    Description     Return Bytes    Return Value
+ * 0x10/0x00       SwitchToMouse       0
+ * 0x10/0x01       SwitchToTablet      0
+ * 0x18/0x04       SetResolution       0
+ * 0x12/0xFF       AutoGainOn          0
+ * 0x17/0x00       FilterOn            0
+ * 0x01/0x00       GetXExtension       2           MaxX
+ * 0x01/0x01       GetYExtension       2           MaxY
+ * 0x02/0x00       GetModelCode        2           ModelCode = LOBYTE
+ * 0x03/0x00       GetODMCode          2           ODMCode
+ * 0x08/0x00       GetPressureLevels   2           =512
+ * 0x04/0x00       GetFirmwareVersion  2           Firmware Version
+ * 0x11/0x02       EnableMacroKeys     0
+ *
+ * To initialize the tablet:
+ *
+ * (1) Send Resolution500LPI (Command)
+ * (2) Query for Model code (Option Report)
+ * (3) Query for ODM code (Option Report)
+ * (4) Query for firmware (Option Report)
+ * (5) Query for GetXExtension (Option Report)
+ * (6) Query for GetYExtension (Option Report)
+ * (7) Query for GetPressureLevels (Option Report)
+ * (8) SwitchToTablet for Absolute coordinates, or
+ *     SwitchToMouse for Relative coordinates (Command)
+ * (9) EnableMacroKeys (Command)
+ * (10) FilterOn (Command)
+ * (11) AutoGainOn (Command)
+ *
+ * (Step 9 can be omitted, but you'll then have no function keys.)
+ */
+
+#define USB_VENDOR_ID_AIPTEK				0x08ca
+#define USB_VENDOR_ID_KYE				0x0458
+#define USB_REQ_GET_REPORT				0x01
+#define USB_REQ_SET_REPORT				0x09
+
+	/* PointerMode codes
+	 */
+#define AIPTEK_POINTER_ONLY_MOUSE_MODE			0
+#define AIPTEK_POINTER_ONLY_STYLUS_MODE			1
+#define AIPTEK_POINTER_EITHER_MODE			2
+
+#define AIPTEK_POINTER_ALLOW_MOUSE_MODE(a)		\
+	(a == AIPTEK_POINTER_ONLY_MOUSE_MODE ||		\
+	 a == AIPTEK_POINTER_EITHER_MODE)
+#define AIPTEK_POINTER_ALLOW_STYLUS_MODE(a)		\
+	(a == AIPTEK_POINTER_ONLY_STYLUS_MODE ||	\
+	 a == AIPTEK_POINTER_EITHER_MODE)
+
+	/* CoordinateMode code
+	 */
+#define AIPTEK_COORDINATE_RELATIVE_MODE			0
+#define AIPTEK_COORDINATE_ABSOLUTE_MODE			1
+
+       /* XTilt and YTilt values
+        */
+#define AIPTEK_TILT_MIN					(-128)
+#define AIPTEK_TILT_MAX					127
+#define AIPTEK_TILT_DISABLE				(-10101)
+
+	/* Wheel values
+	 */
+#define AIPTEK_WHEEL_MIN				0
+#define AIPTEK_WHEEL_MAX				1024
+#define AIPTEK_WHEEL_DISABLE				(-10101)
+
+	/* ToolCode values, which BTW are 0x140 .. 0x14f
+	 * We have things set up such that if the tool button has changed,
+	 * the tools get reset.
+	 */
+	/* toolMode codes
+	 */
+#define AIPTEK_TOOL_BUTTON_PEN_MODE			BTN_TOOL_PEN
+#define AIPTEK_TOOL_BUTTON_PENCIL_MODE			BTN_TOOL_PENCIL
+#define AIPTEK_TOOL_BUTTON_BRUSH_MODE			BTN_TOOL_BRUSH
+#define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE		BTN_TOOL_AIRBRUSH
+#define AIPTEK_TOOL_BUTTON_ERASER_MODE			BTN_TOOL_RUBBER
+#define AIPTEK_TOOL_BUTTON_MOUSE_MODE			BTN_TOOL_MOUSE
+#define AIPTEK_TOOL_BUTTON_LENS_MODE			BTN_TOOL_LENS
+
+	/* Diagnostic message codes
+	 */
+#define AIPTEK_DIAGNOSTIC_NA				0
+#define AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE	1
+#define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE	2
+#define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED		3
+
+	/* Time to wait (in ms) to help mask hand jittering
+	 * when pressing the stylus buttons.
+	 */
+#define AIPTEK_JITTER_DELAY_DEFAULT			50
+
+	/* Time to wait (in ms) in-between sending the tablet
+	 * a command and beginning the process of reading the return
+	 * sequence from the tablet.
+	 */
+#define AIPTEK_PROGRAMMABLE_DELAY_25		25
+#define AIPTEK_PROGRAMMABLE_DELAY_50		50
+#define AIPTEK_PROGRAMMABLE_DELAY_100		100
+#define AIPTEK_PROGRAMMABLE_DELAY_200		200
+#define AIPTEK_PROGRAMMABLE_DELAY_300		300
+#define AIPTEK_PROGRAMMABLE_DELAY_400		400
+#define AIPTEK_PROGRAMMABLE_DELAY_DEFAULT	AIPTEK_PROGRAMMABLE_DELAY_400
+
+	/* Mouse button programming
+	 */
+#define AIPTEK_MOUSE_LEFT_BUTTON		0x04
+#define AIPTEK_MOUSE_RIGHT_BUTTON		0x08
+#define AIPTEK_MOUSE_MIDDLE_BUTTON		0x10
+
+	/* Stylus button programming
+	 */
+#define AIPTEK_STYLUS_LOWER_BUTTON		0x08
+#define AIPTEK_STYLUS_UPPER_BUTTON		0x10
+
+	/* Length of incoming packet from the tablet
+	 */
+#define AIPTEK_PACKET_LENGTH			8
+
+	/* We report in EV_MISC both the proximity and
+	 * whether the report came from the stylus, tablet mouse
+	 * or "unknown" -- Unknown when the tablet is in relative
+	 * mode, because we only get report 1's.
+	 */
+#define AIPTEK_REPORT_TOOL_UNKNOWN		0x10
+#define AIPTEK_REPORT_TOOL_STYLUS		0x20
+#define AIPTEK_REPORT_TOOL_MOUSE		0x40
+
+static int programmableDelay = AIPTEK_PROGRAMMABLE_DELAY_DEFAULT;
+static int jitterDelay = AIPTEK_JITTER_DELAY_DEFAULT;
+
+struct aiptek_features {
+	int odmCode;		/* Tablet manufacturer code       */
+	int modelCode;		/* Tablet model code (not unique) */
+	int firmwareCode;	/* prom/eeprom version            */
+	char usbPath[64 + 1];	/* device's physical usb path     */
+};
+
+struct aiptek_settings {
+	int pointerMode;	/* stylus-, mouse-only or either */
+	int coordinateMode;	/* absolute/relative coords      */
+	int toolMode;		/* pen, pencil, brush, etc. tool */
+	int xTilt;		/* synthetic xTilt amount        */
+	int yTilt;		/* synthetic yTilt amount        */
+	int wheel;		/* synthetic wheel amount        */
+	int stylusButtonUpper;	/* stylus upper btn delivers...  */
+	int stylusButtonLower;	/* stylus lower btn delivers...  */
+	int mouseButtonLeft;	/* mouse left btn delivers...    */
+	int mouseButtonMiddle;	/* mouse middle btn delivers...  */
+	int mouseButtonRight;	/* mouse right btn delivers...   */
+	int programmableDelay;	/* delay for tablet programming  */
+	int jitterDelay;	/* delay for hand jittering      */
+};
+
+struct aiptek {
+	struct input_dev *inputdev;		/* input device struct           */
+	struct usb_device *usbdev;		/* usb device struct             */
+	struct usb_interface *intf;		/* usb interface struct          */
+	struct urb *urb;			/* urb for incoming reports      */
+	dma_addr_t data_dma;			/* our dma stuffage              */
+	struct aiptek_features features;	/* tablet's array of features    */
+	struct aiptek_settings curSetting;	/* tablet's current programmable */
+	struct aiptek_settings newSetting;	/* ... and new param settings    */
+	unsigned int ifnum;			/* interface number for IO       */
+	int diagnostic;				/* tablet diagnostic codes       */
+	unsigned long eventCount;		/* event count                   */
+	int inDelay;				/* jitter: in jitter delay?      */
+	unsigned long endDelay;			/* jitter: time when delay ends  */
+	int previousJitterable;			/* jitterable prev value     */
+
+	int lastMacro;				/* macro key to reset            */
+	int previousToolMode;			/* pen, pencil, brush, etc. tool */
+	unsigned char *data;			/* incoming packet data          */
+};
+
+static const int eventTypes[] = {
+        EV_KEY, EV_ABS, EV_REL, EV_MSC,
+};
+
+static const int absEvents[] = {
+        ABS_X, ABS_Y, ABS_PRESSURE, ABS_TILT_X, ABS_TILT_Y,
+        ABS_WHEEL, ABS_MISC,
+};
+
+static const int relEvents[] = {
+        REL_X, REL_Y, REL_WHEEL,
+};
+
+static const int buttonEvents[] = {
+	BTN_LEFT, BTN_RIGHT, BTN_MIDDLE,
+	BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH,
+	BTN_TOOL_BRUSH, BTN_TOOL_MOUSE, BTN_TOOL_LENS, BTN_TOUCH,
+	BTN_STYLUS, BTN_STYLUS2,
+};
+
+/*
+ * Permit easy lookup of keyboard events to send, versus
+ * the bitmap which comes from the tablet. This hides the
+ * issue that the F_keys are not sequentially numbered.
+ */
+static const int macroKeyEvents[] = {
+	KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
+	KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11,
+	KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17,
+	KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23,
+	KEY_F24, KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO,
+	KEY_FRONT, KEY_COPY, KEY_OPEN, KEY_PASTE, 0
+};
+
+/***********************************************************************
+ * Map values to strings and back. Every map should have the following
+ * as its last element: { NULL, AIPTEK_INVALID_VALUE }.
+ */
+#define AIPTEK_INVALID_VALUE	-1
+
+struct aiptek_map {
+	const char *string;
+	int value;
+};
+
+static int map_str_to_val(const struct aiptek_map *map, const char *str, size_t count)
+{
+	const struct aiptek_map *p;
+
+	if (str[count - 1] == '\n')
+		count--;
+
+	for (p = map; p->string; p++)
+	        if (!strncmp(str, p->string, count))
+			return p->value;
+
+	return AIPTEK_INVALID_VALUE;
+}
+
+static const char *map_val_to_str(const struct aiptek_map *map, int val)
+{
+	const struct aiptek_map *p;
+
+	for (p = map; p->value != AIPTEK_INVALID_VALUE; p++)
+		if (val == p->value)
+			return p->string;
+
+	return "unknown";
+}
+
+/***********************************************************************
+ * aiptek_irq can receive one of six potential reports.
+ * The documentation for each is in the body of the function.
+ *
+ * The tablet reports on several attributes per invocation of
+ * aiptek_irq. Because the Linux Input Event system allows the
+ * transmission of ONE attribute per input_report_xxx() call,
+ * collation has to be done on the other end to reconstitute
+ * a complete tablet report. Further, the number of Input Event reports
+ * submitted varies, depending on what USB report type, and circumstance.
+ * To deal with this, EV_MSC is used to indicate an 'end-of-report'
+ * message. This has been an undocumented convention understood by the kernel
+ * tablet driver and clients such as gpm and XFree86's tablet drivers.
+ *
+ * Of the information received from the tablet, the one piece I
+ * cannot transmit is the proximity bit (without resorting to an EV_MSC
+ * convention above.) I therefore have taken over REL_MISC and ABS_MISC
+ * (for relative and absolute reports, respectively) for communicating
+ * Proximity. Why two events? I thought it interesting to know if the
+ * Proximity event occurred while the tablet was in absolute or relative
+ * mode.
+ * Update: REL_MISC proved not to be such a good idea. With REL_MISC you
+ * get an event transmitted each time. ABS_MISC works better, since it
+ * can be set and re-set. Thus, only using ABS_MISC from now on.
+ *
+ * Other tablets use the notion of a certain minimum stylus pressure
+ * to infer proximity. While that could have been done, that is yet
+ * another 'by convention' behavior, the documentation for which
+ * would be spread between two (or more) pieces of software.
+ *
+ * EV_MSC usage was terminated for this purpose in Linux 2.5.x, and
+ * replaced with the input_sync() method (which emits EV_SYN.)
+ */
+
+static void aiptek_irq(struct urb *urb)
+{
+	struct aiptek *aiptek = urb->context;
+	unsigned char *data = aiptek->data;
+	struct input_dev *inputdev = aiptek->inputdev;
+	struct usb_interface *intf = aiptek->intf;
+	int jitterable = 0;
+	int retval, macro, x, y, z, left, right, middle, p, dv, tip, bs, pck;
+
+	switch (urb->status) {
+	case 0:
+		/* Success */
+		break;
+
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* This urb is terminated, clean up */
+		dev_dbg(&intf->dev, "%s - urb shutting down with status: %d\n",
+			__func__, urb->status);
+		return;
+
+	default:
+		dev_dbg(&intf->dev, "%s - nonzero urb status received: %d\n",
+			__func__, urb->status);
+		goto exit;
+	}
+
+	/* See if we are in a delay loop -- throw out report if true.
+	 */
+	if (aiptek->inDelay == 1 && time_after(aiptek->endDelay, jiffies)) {
+		goto exit;
+	}
+
+	aiptek->inDelay = 0;
+	aiptek->eventCount++;
+
+	/* Report 1 delivers relative coordinates with either a stylus
+	 * or the mouse. You do not know, however, which input
+	 * tool generated the event.
+	 */
+	if (data[0] == 1) {
+		if (aiptek->curSetting.coordinateMode ==
+		    AIPTEK_COORDINATE_ABSOLUTE_MODE) {
+			aiptek->diagnostic =
+			    AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE;
+		} else {
+			x = (signed char) data[2];
+			y = (signed char) data[3];
+
+			/* jitterable keeps track of whether any button has been pressed.
+			 * We're also using it to remap the physical mouse button mask
+			 * to pseudo-settings. (We don't specifically care about it's
+			 * value after moving/transposing mouse button bitmasks, except
+			 * that a non-zero value indicates that one or more
+			 * mouse button was pressed.)
+			 */
+			jitterable = data[1] & 0x07;
+
+			left = (data[1] & aiptek->curSetting.mouseButtonLeft >> 2) != 0 ? 1 : 0;
+			right = (data[1] & aiptek->curSetting.mouseButtonRight >> 2) != 0 ? 1 : 0;
+			middle = (data[1] & aiptek->curSetting.mouseButtonMiddle >> 2) != 0 ? 1 : 0;
+
+			input_report_key(inputdev, BTN_LEFT, left);
+			input_report_key(inputdev, BTN_MIDDLE, middle);
+			input_report_key(inputdev, BTN_RIGHT, right);
+
+			input_report_abs(inputdev, ABS_MISC,
+					 1 | AIPTEK_REPORT_TOOL_UNKNOWN);
+			input_report_rel(inputdev, REL_X, x);
+			input_report_rel(inputdev, REL_Y, y);
+
+			/* Wheel support is in the form of a single-event
+			 * firing.
+			 */
+			if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) {
+				input_report_rel(inputdev, REL_WHEEL,
+						 aiptek->curSetting.wheel);
+				aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
+			}
+			if (aiptek->lastMacro != -1) {
+			        input_report_key(inputdev,
+						 macroKeyEvents[aiptek->lastMacro], 0);
+				aiptek->lastMacro = -1;
+			}
+			input_sync(inputdev);
+		}
+	}
+	/* Report 2 is delivered only by the stylus, and delivers
+	 * absolute coordinates.
+	 */
+	else if (data[0] == 2) {
+		if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) {
+			aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE;
+		} else if (!AIPTEK_POINTER_ALLOW_STYLUS_MODE
+			    (aiptek->curSetting.pointerMode)) {
+				aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED;
+		} else {
+			x = get_unaligned_le16(data + 1);
+			y = get_unaligned_le16(data + 3);
+			z = get_unaligned_le16(data + 6);
+
+			dv = (data[5] & 0x01) != 0 ? 1 : 0;
+			p = (data[5] & 0x02) != 0 ? 1 : 0;
+			tip = (data[5] & 0x04) != 0 ? 1 : 0;
+
+			/* Use jitterable to re-arrange button masks
+			 */
+			jitterable = data[5] & 0x18;
+
+			bs = (data[5] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0;
+			pck = (data[5] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0;
+
+			/* dv indicates 'data valid' (e.g., the tablet is in sync
+			 * and has delivered a "correct" report) We will ignore
+			 * all 'bad' reports...
+			 */
+			if (dv != 0) {
+				/* If the selected tool changed, reset the old
+				 * tool key, and set the new one.
+				 */
+				if (aiptek->previousToolMode !=
+				    aiptek->curSetting.toolMode) {
+				        input_report_key(inputdev,
+							 aiptek->previousToolMode, 0);
+					input_report_key(inputdev,
+							 aiptek->curSetting.toolMode,
+							 1);
+					aiptek->previousToolMode =
+					          aiptek->curSetting.toolMode;
+				}
+
+				if (p != 0) {
+					input_report_abs(inputdev, ABS_X, x);
+					input_report_abs(inputdev, ABS_Y, y);
+					input_report_abs(inputdev, ABS_PRESSURE, z);
+
+					input_report_key(inputdev, BTN_TOUCH, tip);
+					input_report_key(inputdev, BTN_STYLUS, bs);
+					input_report_key(inputdev, BTN_STYLUS2, pck);
+
+					if (aiptek->curSetting.xTilt !=
+					    AIPTEK_TILT_DISABLE) {
+						input_report_abs(inputdev,
+								 ABS_TILT_X,
+								 aiptek->curSetting.xTilt);
+					}
+					if (aiptek->curSetting.yTilt != AIPTEK_TILT_DISABLE) {
+						input_report_abs(inputdev,
+								 ABS_TILT_Y,
+								 aiptek->curSetting.yTilt);
+					}
+
+					/* Wheel support is in the form of a single-event
+					 * firing.
+					 */
+					if (aiptek->curSetting.wheel !=
+					    AIPTEK_WHEEL_DISABLE) {
+						input_report_abs(inputdev,
+								 ABS_WHEEL,
+								 aiptek->curSetting.wheel);
+						aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
+					}
+				}
+				input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_STYLUS);
+				if (aiptek->lastMacro != -1) {
+			                input_report_key(inputdev,
+							 macroKeyEvents[aiptek->lastMacro], 0);
+					aiptek->lastMacro = -1;
+				}
+				input_sync(inputdev);
+			}
+		}
+	}
+	/* Report 3's come from the mouse in absolute mode.
+	 */
+	else if (data[0] == 3) {
+		if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) {
+			aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE;
+		} else if (!AIPTEK_POINTER_ALLOW_MOUSE_MODE
+			(aiptek->curSetting.pointerMode)) {
+			aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED;
+		} else {
+			x = get_unaligned_le16(data + 1);
+			y = get_unaligned_le16(data + 3);
+
+			jitterable = data[5] & 0x1c;
+
+			dv = (data[5] & 0x01) != 0 ? 1 : 0;
+			p = (data[5] & 0x02) != 0 ? 1 : 0;
+			left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
+			right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
+			middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
+
+			if (dv != 0) {
+				/* If the selected tool changed, reset the old
+				 * tool key, and set the new one.
+				 */
+				if (aiptek->previousToolMode !=
+				    aiptek->curSetting.toolMode) {
+				        input_report_key(inputdev,
+							 aiptek->previousToolMode, 0);
+					input_report_key(inputdev,
+							 aiptek->curSetting.toolMode,
+							 1);
+					aiptek->previousToolMode =
+					          aiptek->curSetting.toolMode;
+				}
+
+				if (p != 0) {
+					input_report_abs(inputdev, ABS_X, x);
+					input_report_abs(inputdev, ABS_Y, y);
+
+					input_report_key(inputdev, BTN_LEFT, left);
+					input_report_key(inputdev, BTN_MIDDLE, middle);
+					input_report_key(inputdev, BTN_RIGHT, right);
+
+					/* Wheel support is in the form of a single-event
+					 * firing.
+					 */
+					if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) {
+						input_report_abs(inputdev,
+								 ABS_WHEEL,
+								 aiptek->curSetting.wheel);
+						aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
+					}
+				}
+				input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_MOUSE);
+				if (aiptek->lastMacro != -1) {
+			                input_report_key(inputdev,
+							 macroKeyEvents[aiptek->lastMacro], 0);
+				        aiptek->lastMacro = -1;
+				}
+				input_sync(inputdev);
+			}
+		}
+	}
+	/* Report 4s come from the macro keys when pressed by stylus
+	 */
+	else if (data[0] == 4) {
+		jitterable = data[1] & 0x18;
+
+		dv = (data[1] & 0x01) != 0 ? 1 : 0;
+		p = (data[1] & 0x02) != 0 ? 1 : 0;
+		tip = (data[1] & 0x04) != 0 ? 1 : 0;
+		bs = (data[1] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0;
+		pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0;
+
+		macro = dv && p && tip && !(data[3] & 1) ? (data[3] >> 1) : -1;
+		z = get_unaligned_le16(data + 4);
+
+		if (dv) {
+		        /* If the selected tool changed, reset the old
+			 * tool key, and set the new one.
+			 */
+		        if (aiptek->previousToolMode !=
+			    aiptek->curSetting.toolMode) {
+			        input_report_key(inputdev,
+						 aiptek->previousToolMode, 0);
+				input_report_key(inputdev,
+						 aiptek->curSetting.toolMode,
+						 1);
+				aiptek->previousToolMode =
+				        aiptek->curSetting.toolMode;
+			}
+		}
+
+		if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) {
+		        input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0);
+			aiptek->lastMacro = -1;
+		}
+
+		if (macro != -1 && macro != aiptek->lastMacro) {
+			input_report_key(inputdev, macroKeyEvents[macro], 1);
+			aiptek->lastMacro = macro;
+		}
+		input_report_abs(inputdev, ABS_MISC,
+				 p | AIPTEK_REPORT_TOOL_STYLUS);
+		input_sync(inputdev);
+	}
+	/* Report 5s come from the macro keys when pressed by mouse
+	 */
+	else if (data[0] == 5) {
+		jitterable = data[1] & 0x1c;
+
+		dv = (data[1] & 0x01) != 0 ? 1 : 0;
+		p = (data[1] & 0x02) != 0 ? 1 : 0;
+		left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
+		right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
+		middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
+		macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 1) : 0;
+
+		if (dv) {
+		        /* If the selected tool changed, reset the old
+			 * tool key, and set the new one.
+			 */
+		        if (aiptek->previousToolMode !=
+			    aiptek->curSetting.toolMode) {
+		                input_report_key(inputdev,
+						 aiptek->previousToolMode, 0);
+			        input_report_key(inputdev,
+						 aiptek->curSetting.toolMode, 1);
+			        aiptek->previousToolMode = aiptek->curSetting.toolMode;
+			}
+		}
+
+		if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) {
+		        input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0);
+			aiptek->lastMacro = -1;
+		}
+
+		if (macro != -1 && macro != aiptek->lastMacro) {
+			input_report_key(inputdev, macroKeyEvents[macro], 1);
+			aiptek->lastMacro = macro;
+		}
+
+		input_report_abs(inputdev, ABS_MISC,
+				 p | AIPTEK_REPORT_TOOL_MOUSE);
+		input_sync(inputdev);
+	}
+	/* We have no idea which tool can generate a report 6. Theoretically,
+	 * neither need to, having been given reports 4 & 5 for such use.
+	 * However, report 6 is the 'official-looking' report for macroKeys;
+	 * reports 4 & 5 supposively are used to support unnamed, unknown
+	 * hat switches (which just so happen to be the macroKeys.)
+	 */
+	else if (data[0] == 6) {
+		macro = get_unaligned_le16(data + 1);
+		if (macro > 0) {
+			input_report_key(inputdev, macroKeyEvents[macro - 1],
+					 0);
+		}
+		if (macro < 25) {
+			input_report_key(inputdev, macroKeyEvents[macro + 1],
+					 0);
+		}
+
+		/* If the selected tool changed, reset the old
+		   tool key, and set the new one.
+		*/
+		if (aiptek->previousToolMode !=
+		    aiptek->curSetting.toolMode) {
+		        input_report_key(inputdev,
+					 aiptek->previousToolMode, 0);
+			input_report_key(inputdev,
+					 aiptek->curSetting.toolMode,
+					 1);
+			aiptek->previousToolMode =
+				aiptek->curSetting.toolMode;
+		}
+
+		input_report_key(inputdev, macroKeyEvents[macro], 1);
+		input_report_abs(inputdev, ABS_MISC,
+				 1 | AIPTEK_REPORT_TOOL_UNKNOWN);
+		input_sync(inputdev);
+	} else {
+		dev_dbg(&intf->dev, "Unknown report %d\n", data[0]);
+	}
+
+	/* Jitter may occur when the user presses a button on the stlyus
+	 * or the mouse. What we do to prevent that is wait 'x' milliseconds
+	 * following a 'jitterable' event, which should give the hand some time
+	 * stabilize itself.
+	 *
+	 * We just introduced aiptek->previousJitterable to carry forth the
+	 * notion that jitter occurs when the button state changes from on to off:
+	 * a person drawing, holding a button down is not subject to jittering.
+	 * With that in mind, changing from upper button depressed to lower button
+	 * WILL transition through a jitter delay.
+	 */
+
+	if (aiptek->previousJitterable != jitterable &&
+	    aiptek->curSetting.jitterDelay != 0 && aiptek->inDelay != 1) {
+		aiptek->endDelay = jiffies +
+		    ((aiptek->curSetting.jitterDelay * HZ) / 1000);
+		aiptek->inDelay = 1;
+	}
+	aiptek->previousJitterable = jitterable;
+
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval != 0) {
+		dev_err(&intf->dev,
+			"%s - usb_submit_urb failed with result %d\n",
+			__func__, retval);
+	}
+}
+
+/***********************************************************************
+ * These are the USB id's known so far. We do not identify them to
+ * specific Aiptek model numbers, because there has been overlaps,
+ * use, and reuse of id's in existing models. Certain models have
+ * been known to use more than one ID, indicative perhaps of
+ * manufacturing revisions. In any event, we consider these
+ * IDs to not be model-specific nor unique.
+ */
+static const struct usb_device_id aiptek_ids[] = {
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x01)},
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x10)},
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20)},
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x21)},
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x22)},
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x23)},
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x24)},
+	{USB_DEVICE(USB_VENDOR_ID_KYE, 0x5003)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, aiptek_ids);
+
+/***********************************************************************
+ * Open an instance of the tablet driver.
+ */
+static int aiptek_open(struct input_dev *inputdev)
+{
+	struct aiptek *aiptek = input_get_drvdata(inputdev);
+
+	aiptek->urb->dev = aiptek->usbdev;
+	if (usb_submit_urb(aiptek->urb, GFP_KERNEL) != 0)
+		return -EIO;
+
+	return 0;
+}
+
+/***********************************************************************
+ * Close an instance of the tablet driver.
+ */
+static void aiptek_close(struct input_dev *inputdev)
+{
+	struct aiptek *aiptek = input_get_drvdata(inputdev);
+
+	usb_kill_urb(aiptek->urb);
+}
+
+/***********************************************************************
+ * aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x,
+ * where they were known as usb_set_report and usb_get_report.
+ */
+static int
+aiptek_set_report(struct aiptek *aiptek,
+		  unsigned char report_type,
+		  unsigned char report_id, void *buffer, int size)
+{
+	return usb_control_msg(aiptek->usbdev,
+			       usb_sndctrlpipe(aiptek->usbdev, 0),
+			       USB_REQ_SET_REPORT,
+			       USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+			       USB_DIR_OUT, (report_type << 8) + report_id,
+			       aiptek->ifnum, buffer, size, 5000);
+}
+
+static int
+aiptek_get_report(struct aiptek *aiptek,
+		  unsigned char report_type,
+		  unsigned char report_id, void *buffer, int size)
+{
+	return usb_control_msg(aiptek->usbdev,
+			       usb_rcvctrlpipe(aiptek->usbdev, 0),
+			       USB_REQ_GET_REPORT,
+			       USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+			       USB_DIR_IN, (report_type << 8) + report_id,
+			       aiptek->ifnum, buffer, size, 5000);
+}
+
+/***********************************************************************
+ * Send a command to the tablet.
+ */
+static int
+aiptek_command(struct aiptek *aiptek, unsigned char command, unsigned char data)
+{
+	const int sizeof_buf = 3 * sizeof(u8);
+	int ret;
+	u8 *buf;
+
+	buf = kmalloc(sizeof_buf, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = 2;
+	buf[1] = command;
+	buf[2] = data;
+
+	if ((ret =
+	     aiptek_set_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) {
+		dev_dbg(&aiptek->intf->dev,
+			"aiptek_program: failed, tried to send: 0x%02x 0x%02x\n",
+			command, data);
+	}
+	kfree(buf);
+	return ret < 0 ? ret : 0;
+}
+
+/***********************************************************************
+ * Retrieve information from the tablet. Querying info is defined as first
+ * sending the {command,data} sequence as a command, followed by a wait
+ * (aka, "programmaticDelay") and then a "read" request.
+ */
+static int
+aiptek_query(struct aiptek *aiptek, unsigned char command, unsigned char data)
+{
+	const int sizeof_buf = 3 * sizeof(u8);
+	int ret;
+	u8 *buf;
+
+	buf = kmalloc(sizeof_buf, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = 2;
+	buf[1] = command;
+	buf[2] = data;
+
+	if (aiptek_command(aiptek, command, data) != 0) {
+		kfree(buf);
+		return -EIO;
+	}
+	msleep(aiptek->curSetting.programmableDelay);
+
+	if ((ret =
+	     aiptek_get_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) {
+		dev_dbg(&aiptek->intf->dev,
+			"aiptek_query failed: returned 0x%02x 0x%02x 0x%02x\n",
+			buf[0], buf[1], buf[2]);
+		ret = -EIO;
+	} else {
+		ret = get_unaligned_le16(buf + 1);
+	}
+	kfree(buf);
+	return ret;
+}
+
+/***********************************************************************
+ * Program the tablet into either absolute or relative mode.
+ * We also get information about the tablet's size.
+ */
+static int aiptek_program_tablet(struct aiptek *aiptek)
+{
+	int ret;
+	/* Execute Resolution500LPI */
+	if ((ret = aiptek_command(aiptek, 0x18, 0x04)) < 0)
+		return ret;
+
+	/* Query getModelCode */
+	if ((ret = aiptek_query(aiptek, 0x02, 0x00)) < 0)
+		return ret;
+	aiptek->features.modelCode = ret & 0xff;
+
+	/* Query getODMCode */
+	if ((ret = aiptek_query(aiptek, 0x03, 0x00)) < 0)
+		return ret;
+	aiptek->features.odmCode = ret;
+
+	/* Query getFirmwareCode */
+	if ((ret = aiptek_query(aiptek, 0x04, 0x00)) < 0)
+		return ret;
+	aiptek->features.firmwareCode = ret;
+
+	/* Query getXextension */
+	if ((ret = aiptek_query(aiptek, 0x01, 0x00)) < 0)
+		return ret;
+	input_set_abs_params(aiptek->inputdev, ABS_X, 0, ret - 1, 0, 0);
+
+	/* Query getYextension */
+	if ((ret = aiptek_query(aiptek, 0x01, 0x01)) < 0)
+		return ret;
+	input_set_abs_params(aiptek->inputdev, ABS_Y, 0, ret - 1, 0, 0);
+
+	/* Query getPressureLevels */
+	if ((ret = aiptek_query(aiptek, 0x08, 0x00)) < 0)
+		return ret;
+	input_set_abs_params(aiptek->inputdev, ABS_PRESSURE, 0, ret - 1, 0, 0);
+
+	/* Depending on whether we are in absolute or relative mode, we will
+	 * do a switchToTablet(absolute) or switchToMouse(relative) command.
+	 */
+	if (aiptek->curSetting.coordinateMode ==
+	    AIPTEK_COORDINATE_ABSOLUTE_MODE) {
+		/* Execute switchToTablet */
+		if ((ret = aiptek_command(aiptek, 0x10, 0x01)) < 0) {
+			return ret;
+		}
+	} else {
+		/* Execute switchToMouse */
+		if ((ret = aiptek_command(aiptek, 0x10, 0x00)) < 0) {
+			return ret;
+		}
+	}
+
+	/* Enable the macro keys */
+	if ((ret = aiptek_command(aiptek, 0x11, 0x02)) < 0)
+		return ret;
+#if 0
+	/* Execute FilterOn */
+	if ((ret = aiptek_command(aiptek, 0x17, 0x00)) < 0)
+		return ret;
+#endif
+
+	/* Execute AutoGainOn */
+	if ((ret = aiptek_command(aiptek, 0x12, 0xff)) < 0)
+		return ret;
+
+	/* Reset the eventCount, so we track events from last (re)programming
+	 */
+	aiptek->diagnostic = AIPTEK_DIAGNOSTIC_NA;
+	aiptek->eventCount = 0;
+
+	return 0;
+}
+
+/***********************************************************************
+ * Sysfs functions. Sysfs prefers that individually-tunable parameters
+ * exist in their separate pseudo-files. Summary data that is immutable
+ * may exist in a singular file so long as you don't define a writeable
+ * interface.
+ */
+
+/***********************************************************************
+ * support the 'size' file -- display support
+ */
+static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%dx%d\n",
+			input_abs_get_max(aiptek->inputdev, ABS_X) + 1,
+			input_abs_get_max(aiptek->inputdev, ABS_Y) + 1);
+}
+
+/* These structs define the sysfs files, param #1 is the name of the
+ * file, param 2 is the file permissions, param 3 & 4 are to the
+ * output generator and input parser routines. Absence of a routine is
+ * permitted -- it only means can't either 'cat' the file, or send data
+ * to it.
+ */
+static DEVICE_ATTR(size, S_IRUGO, show_tabletSize, NULL);
+
+/***********************************************************************
+ * support routines for the 'pointer_mode' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static struct aiptek_map pointer_mode_map[] = {
+	{ "stylus",	AIPTEK_POINTER_ONLY_STYLUS_MODE },
+	{ "mouse",	AIPTEK_POINTER_ONLY_MOUSE_MODE },
+	{ "either",	AIPTEK_POINTER_EITHER_MODE },
+	{ NULL,		AIPTEK_INVALID_VALUE }
+};
+
+static ssize_t show_tabletPointerMode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			map_val_to_str(pointer_mode_map,
+					aiptek->curSetting.pointerMode));
+}
+
+static ssize_t
+store_tabletPointerMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int new_mode = map_str_to_val(pointer_mode_map, buf, count);
+
+	if (new_mode == AIPTEK_INVALID_VALUE)
+		return -EINVAL;
+
+	aiptek->newSetting.pointerMode = new_mode;
+	return count;
+}
+
+static DEVICE_ATTR(pointer_mode,
+		   S_IRUGO | S_IWUSR,
+		   show_tabletPointerMode, store_tabletPointerMode);
+
+/***********************************************************************
+ * support routines for the 'coordinate_mode' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+
+static struct aiptek_map coordinate_mode_map[] = {
+	{ "absolute",	AIPTEK_COORDINATE_ABSOLUTE_MODE },
+	{ "relative",	AIPTEK_COORDINATE_RELATIVE_MODE },
+	{ NULL,		AIPTEK_INVALID_VALUE }
+};
+
+static ssize_t show_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			map_val_to_str(coordinate_mode_map,
+					aiptek->curSetting.coordinateMode));
+}
+
+static ssize_t
+store_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int new_mode = map_str_to_val(coordinate_mode_map, buf, count);
+
+	if (new_mode == AIPTEK_INVALID_VALUE)
+		return -EINVAL;
+
+	aiptek->newSetting.coordinateMode = new_mode;
+	return count;
+}
+
+static DEVICE_ATTR(coordinate_mode,
+		   S_IRUGO | S_IWUSR,
+		   show_tabletCoordinateMode, store_tabletCoordinateMode);
+
+/***********************************************************************
+ * support routines for the 'tool_mode' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+
+static struct aiptek_map tool_mode_map[] = {
+	{ "mouse",	AIPTEK_TOOL_BUTTON_MOUSE_MODE },
+	{ "eraser",	AIPTEK_TOOL_BUTTON_ERASER_MODE },
+	{ "pencil",	AIPTEK_TOOL_BUTTON_PENCIL_MODE },
+	{ "pen",	AIPTEK_TOOL_BUTTON_PEN_MODE },
+	{ "brush",	AIPTEK_TOOL_BUTTON_BRUSH_MODE },
+	{ "airbrush",	AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE },
+	{ "lens",	AIPTEK_TOOL_BUTTON_LENS_MODE },
+	{ NULL,		AIPTEK_INVALID_VALUE }
+};
+
+static ssize_t show_tabletToolMode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			map_val_to_str(tool_mode_map,
+					aiptek->curSetting.toolMode));
+}
+
+static ssize_t
+store_tabletToolMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int new_mode = map_str_to_val(tool_mode_map, buf, count);
+
+	if (new_mode == AIPTEK_INVALID_VALUE)
+		return -EINVAL;
+
+	aiptek->newSetting.toolMode = new_mode;
+	return count;
+}
+
+static DEVICE_ATTR(tool_mode,
+		   S_IRUGO | S_IWUSR,
+		   show_tabletToolMode, store_tabletToolMode);
+
+/***********************************************************************
+ * support routines for the 'xtilt' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletXtilt(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) {
+		return snprintf(buf, PAGE_SIZE, "disable\n");
+	} else {
+		return snprintf(buf, PAGE_SIZE, "%d\n",
+				aiptek->curSetting.xTilt);
+	}
+}
+
+static ssize_t
+store_tabletXtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int x;
+
+	if (kstrtoint(buf, 10, &x)) {
+		size_t len = buf[count - 1] == '\n' ? count - 1 : count;
+
+		if (strncmp(buf, "disable", len))
+			return -EINVAL;
+
+		aiptek->newSetting.xTilt = AIPTEK_TILT_DISABLE;
+	} else {
+		if (x < AIPTEK_TILT_MIN || x > AIPTEK_TILT_MAX)
+			return -EINVAL;
+
+		aiptek->newSetting.xTilt = x;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(xtilt,
+		   S_IRUGO | S_IWUSR, show_tabletXtilt, store_tabletXtilt);
+
+/***********************************************************************
+ * support routines for the 'ytilt' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletYtilt(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) {
+		return snprintf(buf, PAGE_SIZE, "disable\n");
+	} else {
+		return snprintf(buf, PAGE_SIZE, "%d\n",
+				aiptek->curSetting.yTilt);
+	}
+}
+
+static ssize_t
+store_tabletYtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int y;
+
+	if (kstrtoint(buf, 10, &y)) {
+		size_t len = buf[count - 1] == '\n' ? count - 1 : count;
+
+		if (strncmp(buf, "disable", len))
+			return -EINVAL;
+
+		aiptek->newSetting.yTilt = AIPTEK_TILT_DISABLE;
+	} else {
+		if (y < AIPTEK_TILT_MIN || y > AIPTEK_TILT_MAX)
+			return -EINVAL;
+
+		aiptek->newSetting.yTilt = y;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(ytilt,
+		   S_IRUGO | S_IWUSR, show_tabletYtilt, store_tabletYtilt);
+
+/***********************************************************************
+ * support routines for the 'jitter' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletJitterDelay(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay);
+}
+
+static ssize_t
+store_tabletJitterDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int err, j;
+
+	err = kstrtoint(buf, 10, &j);
+	if (err)
+		return err;
+
+	aiptek->newSetting.jitterDelay = j;
+	return count;
+}
+
+static DEVICE_ATTR(jitter,
+		   S_IRUGO | S_IWUSR,
+		   show_tabletJitterDelay, store_tabletJitterDelay);
+
+/***********************************************************************
+ * support routines for the 'delay' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			aiptek->curSetting.programmableDelay);
+}
+
+static ssize_t
+store_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int err, d;
+
+	err = kstrtoint(buf, 10, &d);
+	if (err)
+		return err;
+
+	aiptek->newSetting.programmableDelay = d;
+	return count;
+}
+
+static DEVICE_ATTR(delay,
+		   S_IRUGO | S_IWUSR,
+		   show_tabletProgrammableDelay, store_tabletProgrammableDelay);
+
+/***********************************************************************
+ * support routines for the 'event_count' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletEventsReceived(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount);
+}
+
+static DEVICE_ATTR(event_count, S_IRUGO, show_tabletEventsReceived, NULL);
+
+/***********************************************************************
+ * support routines for the 'diagnostic' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletDiagnosticMessage(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	char *retMsg;
+
+	switch (aiptek->diagnostic) {
+	case AIPTEK_DIAGNOSTIC_NA:
+		retMsg = "no errors\n";
+		break;
+
+	case AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE:
+		retMsg = "Error: receiving relative reports\n";
+		break;
+
+	case AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE:
+		retMsg = "Error: receiving absolute reports\n";
+		break;
+
+	case AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED:
+		if (aiptek->curSetting.pointerMode ==
+		    AIPTEK_POINTER_ONLY_MOUSE_MODE) {
+			retMsg = "Error: receiving stylus reports\n";
+		} else {
+			retMsg = "Error: receiving mouse reports\n";
+		}
+		break;
+
+	default:
+		return 0;
+	}
+	return snprintf(buf, PAGE_SIZE, retMsg);
+}
+
+static DEVICE_ATTR(diagnostic, S_IRUGO, show_tabletDiagnosticMessage, NULL);
+
+/***********************************************************************
+ * support routines for the 'stylus_upper' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+
+static struct aiptek_map stylus_button_map[] = {
+	{ "upper",	AIPTEK_STYLUS_UPPER_BUTTON },
+	{ "lower",	AIPTEK_STYLUS_LOWER_BUTTON },
+	{ NULL,		AIPTEK_INVALID_VALUE }
+};
+
+static ssize_t show_tabletStylusUpper(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			map_val_to_str(stylus_button_map,
+					aiptek->curSetting.stylusButtonUpper));
+}
+
+static ssize_t
+store_tabletStylusUpper(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int new_button = map_str_to_val(stylus_button_map, buf, count);
+
+	if (new_button == AIPTEK_INVALID_VALUE)
+		return -EINVAL;
+
+	aiptek->newSetting.stylusButtonUpper = new_button;
+	return count;
+}
+
+static DEVICE_ATTR(stylus_upper,
+		   S_IRUGO | S_IWUSR,
+		   show_tabletStylusUpper, store_tabletStylusUpper);
+
+/***********************************************************************
+ * support routines for the 'stylus_lower' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+
+static ssize_t show_tabletStylusLower(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			map_val_to_str(stylus_button_map,
+					aiptek->curSetting.stylusButtonLower));
+}
+
+static ssize_t
+store_tabletStylusLower(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int new_button = map_str_to_val(stylus_button_map, buf, count);
+
+	if (new_button == AIPTEK_INVALID_VALUE)
+		return -EINVAL;
+
+	aiptek->newSetting.stylusButtonLower = new_button;
+	return count;
+}
+
+static DEVICE_ATTR(stylus_lower,
+		   S_IRUGO | S_IWUSR,
+		   show_tabletStylusLower, store_tabletStylusLower);
+
+/***********************************************************************
+ * support routines for the 'mouse_left' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+
+static struct aiptek_map mouse_button_map[] = {
+	{ "left",	AIPTEK_MOUSE_LEFT_BUTTON },
+	{ "middle",	AIPTEK_MOUSE_MIDDLE_BUTTON },
+	{ "right",	AIPTEK_MOUSE_RIGHT_BUTTON },
+	{ NULL,		AIPTEK_INVALID_VALUE }
+};
+
+static ssize_t show_tabletMouseLeft(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			map_val_to_str(mouse_button_map,
+					aiptek->curSetting.mouseButtonLeft));
+}
+
+static ssize_t
+store_tabletMouseLeft(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int new_button = map_str_to_val(mouse_button_map, buf, count);
+
+	if (new_button == AIPTEK_INVALID_VALUE)
+		return -EINVAL;
+
+	aiptek->newSetting.mouseButtonLeft = new_button;
+	return count;
+}
+
+static DEVICE_ATTR(mouse_left,
+		   S_IRUGO | S_IWUSR,
+		   show_tabletMouseLeft, store_tabletMouseLeft);
+
+/***********************************************************************
+ * support routines for the 'mouse_middle' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			map_val_to_str(mouse_button_map,
+					aiptek->curSetting.mouseButtonMiddle));
+}
+
+static ssize_t
+store_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int new_button = map_str_to_val(mouse_button_map, buf, count);
+
+	if (new_button == AIPTEK_INVALID_VALUE)
+		return -EINVAL;
+
+	aiptek->newSetting.mouseButtonMiddle = new_button;
+	return count;
+}
+
+static DEVICE_ATTR(mouse_middle,
+		   S_IRUGO | S_IWUSR,
+		   show_tabletMouseMiddle, store_tabletMouseMiddle);
+
+/***********************************************************************
+ * support routines for the 'mouse_right' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletMouseRight(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			map_val_to_str(mouse_button_map,
+					aiptek->curSetting.mouseButtonRight));
+}
+
+static ssize_t
+store_tabletMouseRight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int new_button = map_str_to_val(mouse_button_map, buf, count);
+
+	if (new_button == AIPTEK_INVALID_VALUE)
+		return -EINVAL;
+
+	aiptek->newSetting.mouseButtonRight = new_button;
+	return count;
+}
+
+static DEVICE_ATTR(mouse_right,
+		   S_IRUGO | S_IWUSR,
+		   show_tabletMouseRight, store_tabletMouseRight);
+
+/***********************************************************************
+ * support routines for the 'wheel' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletWheel(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) {
+		return snprintf(buf, PAGE_SIZE, "disable\n");
+	} else {
+		return snprintf(buf, PAGE_SIZE, "%d\n",
+				aiptek->curSetting.wheel);
+	}
+}
+
+static ssize_t
+store_tabletWheel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int err, w;
+
+	err = kstrtoint(buf, 10, &w);
+	if (err)
+		return err;
+
+	aiptek->newSetting.wheel = w;
+	return count;
+}
+
+static DEVICE_ATTR(wheel,
+		   S_IRUGO | S_IWUSR, show_tabletWheel, store_tabletWheel);
+
+/***********************************************************************
+ * support routines for the 'execute' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletExecute(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	/* There is nothing useful to display, so a one-line manual
+	 * is in order...
+	 */
+	return snprintf(buf, PAGE_SIZE,
+			"Write anything to this file to program your tablet.\n");
+}
+
+static ssize_t
+store_tabletExecute(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	/* We do not care what you write to this file. Merely the action
+	 * of writing to this file triggers a tablet reprogramming.
+	 */
+	memcpy(&aiptek->curSetting, &aiptek->newSetting,
+	       sizeof(struct aiptek_settings));
+
+	if (aiptek_program_tablet(aiptek) < 0)
+		return -EIO;
+
+	return count;
+}
+
+static DEVICE_ATTR(execute,
+		   S_IRUGO | S_IWUSR, show_tabletExecute, store_tabletExecute);
+
+/***********************************************************************
+ * support routines for the 'odm_code' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletODMCode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode);
+}
+
+static DEVICE_ATTR(odm_code, S_IRUGO, show_tabletODMCode, NULL);
+
+/***********************************************************************
+ * support routines for the 'model_code' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletModelCode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode);
+}
+
+static DEVICE_ATTR(model_code, S_IRUGO, show_tabletModelCode, NULL);
+
+/***********************************************************************
+ * support routines for the 'firmware_code' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_firmwareCode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%04x\n",
+			aiptek->features.firmwareCode);
+}
+
+static DEVICE_ATTR(firmware_code, S_IRUGO, show_firmwareCode, NULL);
+
+static struct attribute *aiptek_attributes[] = {
+	&dev_attr_size.attr,
+	&dev_attr_pointer_mode.attr,
+	&dev_attr_coordinate_mode.attr,
+	&dev_attr_tool_mode.attr,
+	&dev_attr_xtilt.attr,
+	&dev_attr_ytilt.attr,
+	&dev_attr_jitter.attr,
+	&dev_attr_delay.attr,
+	&dev_attr_event_count.attr,
+	&dev_attr_diagnostic.attr,
+	&dev_attr_odm_code.attr,
+	&dev_attr_model_code.attr,
+	&dev_attr_firmware_code.attr,
+	&dev_attr_stylus_lower.attr,
+	&dev_attr_stylus_upper.attr,
+	&dev_attr_mouse_left.attr,
+	&dev_attr_mouse_middle.attr,
+	&dev_attr_mouse_right.attr,
+	&dev_attr_wheel.attr,
+	&dev_attr_execute.attr,
+	NULL
+};
+
+static struct attribute_group aiptek_attribute_group = {
+	.attrs	= aiptek_attributes,
+};
+
+/***********************************************************************
+ * This routine is called when a tablet has been identified. It basically
+ * sets up the tablet and the driver's internal structures.
+ */
+static int
+aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *usbdev = interface_to_usbdev(intf);
+	struct usb_endpoint_descriptor *endpoint;
+	struct aiptek *aiptek;
+	struct input_dev *inputdev;
+	int i;
+	int speeds[] = { 0,
+		AIPTEK_PROGRAMMABLE_DELAY_50,
+		AIPTEK_PROGRAMMABLE_DELAY_400,
+		AIPTEK_PROGRAMMABLE_DELAY_25,
+		AIPTEK_PROGRAMMABLE_DELAY_100,
+		AIPTEK_PROGRAMMABLE_DELAY_200,
+		AIPTEK_PROGRAMMABLE_DELAY_300
+	};
+	int err = -ENOMEM;
+
+	/* programmableDelay is where the command-line specified
+	 * delay is kept. We make it the first element of speeds[],
+	 * so therefore, your override speed is tried first, then the
+	 * remainder. Note that the default value of 400ms will be tried
+	 * if you do not specify any command line parameter.
+	 */
+	speeds[0] = programmableDelay;
+
+	aiptek = kzalloc(sizeof(struct aiptek), GFP_KERNEL);
+	inputdev = input_allocate_device();
+	if (!aiptek || !inputdev) {
+		dev_warn(&intf->dev,
+			 "cannot allocate memory or input device\n");
+		goto fail1;
+        }
+
+	aiptek->data = usb_alloc_coherent(usbdev, AIPTEK_PACKET_LENGTH,
+					  GFP_ATOMIC, &aiptek->data_dma);
+        if (!aiptek->data) {
+		dev_warn(&intf->dev, "cannot allocate usb buffer\n");
+		goto fail1;
+	}
+
+	aiptek->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!aiptek->urb) {
+	        dev_warn(&intf->dev, "cannot allocate urb\n");
+		goto fail2;
+	}
+
+	aiptek->inputdev = inputdev;
+	aiptek->usbdev = usbdev;
+	aiptek->intf = intf;
+	aiptek->ifnum = intf->altsetting[0].desc.bInterfaceNumber;
+	aiptek->inDelay = 0;
+	aiptek->endDelay = 0;
+	aiptek->previousJitterable = 0;
+	aiptek->lastMacro = -1;
+
+	/* Set up the curSettings struct. Said struct contains the current
+	 * programmable parameters. The newSetting struct contains changes
+	 * the user makes to the settings via the sysfs interface. Those
+	 * changes are not "committed" to curSettings until the user
+	 * writes to the sysfs/.../execute file.
+	 */
+	aiptek->curSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE;
+	aiptek->curSetting.coordinateMode = AIPTEK_COORDINATE_ABSOLUTE_MODE;
+	aiptek->curSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE;
+	aiptek->curSetting.xTilt = AIPTEK_TILT_DISABLE;
+	aiptek->curSetting.yTilt = AIPTEK_TILT_DISABLE;
+	aiptek->curSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON;
+	aiptek->curSetting.mouseButtonMiddle = AIPTEK_MOUSE_MIDDLE_BUTTON;
+	aiptek->curSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON;
+	aiptek->curSetting.stylusButtonUpper = AIPTEK_STYLUS_UPPER_BUTTON;
+	aiptek->curSetting.stylusButtonLower = AIPTEK_STYLUS_LOWER_BUTTON;
+	aiptek->curSetting.jitterDelay = jitterDelay;
+	aiptek->curSetting.programmableDelay = programmableDelay;
+
+	/* Both structs should have equivalent settings
+	 */
+	aiptek->newSetting = aiptek->curSetting;
+
+	/* Determine the usb devices' physical path.
+	 * Asketh not why we always pretend we're using "../input0",
+	 * but I suspect this will have to be refactored one
+	 * day if a single USB device can be a keyboard & a mouse
+	 * & a tablet, and the inputX number actually will tell
+	 * us something...
+	 */
+	usb_make_path(usbdev, aiptek->features.usbPath,
+			sizeof(aiptek->features.usbPath));
+	strlcat(aiptek->features.usbPath, "/input0",
+		sizeof(aiptek->features.usbPath));
+
+	/* Set up client data, pointers to open and close routines
+	 * for the input device.
+	 */
+	inputdev->name = "Aiptek";
+	inputdev->phys = aiptek->features.usbPath;
+	usb_to_input_id(usbdev, &inputdev->id);
+	inputdev->dev.parent = &intf->dev;
+
+	input_set_drvdata(inputdev, aiptek);
+
+	inputdev->open = aiptek_open;
+	inputdev->close = aiptek_close;
+
+	/* Now program the capacities of the tablet, in terms of being
+	 * an input device.
+	 */
+	for (i = 0; i < ARRAY_SIZE(eventTypes); ++i)
+	        __set_bit(eventTypes[i], inputdev->evbit);
+
+	for (i = 0; i < ARRAY_SIZE(absEvents); ++i)
+	        __set_bit(absEvents[i], inputdev->absbit);
+
+	for (i = 0; i < ARRAY_SIZE(relEvents); ++i)
+	        __set_bit(relEvents[i], inputdev->relbit);
+
+	__set_bit(MSC_SERIAL, inputdev->mscbit);
+
+	/* Set up key and button codes */
+	for (i = 0; i < ARRAY_SIZE(buttonEvents); ++i)
+		__set_bit(buttonEvents[i], inputdev->keybit);
+
+	for (i = 0; i < ARRAY_SIZE(macroKeyEvents); ++i)
+		__set_bit(macroKeyEvents[i], inputdev->keybit);
+
+	/*
+	 * Program the input device coordinate capacities. We do not yet
+	 * know what maximum X, Y, and Z values are, so we're putting fake
+	 * values in. Later, we'll ask the tablet to put in the correct
+	 * values.
+	 */
+	input_set_abs_params(inputdev, ABS_X, 0, 2999, 0, 0);
+	input_set_abs_params(inputdev, ABS_Y, 0, 2249, 0, 0);
+	input_set_abs_params(inputdev, ABS_PRESSURE, 0, 511, 0, 0);
+	input_set_abs_params(inputdev, ABS_TILT_X, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0);
+	input_set_abs_params(inputdev, ABS_TILT_Y, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0);
+	input_set_abs_params(inputdev, ABS_WHEEL, AIPTEK_WHEEL_MIN, AIPTEK_WHEEL_MAX - 1, 0, 0);
+
+	/* Verify that a device really has an endpoint */
+	if (intf->altsetting[0].desc.bNumEndpoints < 1) {
+		dev_err(&intf->dev,
+			"interface has %d endpoints, but must have minimum 1\n",
+			intf->altsetting[0].desc.bNumEndpoints);
+		err = -EINVAL;
+		goto fail3;
+	}
+	endpoint = &intf->altsetting[0].endpoint[0].desc;
+
+	/* Go set up our URB, which is called when the tablet receives
+	 * input.
+	 */
+	usb_fill_int_urb(aiptek->urb,
+			 aiptek->usbdev,
+			 usb_rcvintpipe(aiptek->usbdev,
+					endpoint->bEndpointAddress),
+			 aiptek->data, 8, aiptek_irq, aiptek,
+			 endpoint->bInterval);
+
+	aiptek->urb->transfer_dma = aiptek->data_dma;
+	aiptek->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* Program the tablet. This sets the tablet up in the mode
+	 * specified in newSetting, and also queries the tablet's
+	 * physical capacities.
+	 *
+	 * Sanity check: if a tablet doesn't like the slow programmatic
+	 * delay, we often get sizes of 0x0. Let's use that as an indicator
+	 * to try faster delays, up to 25 ms. If that logic fails, well, you'll
+	 * have to explain to us how your tablet thinks it's 0x0, and yet that's
+	 * not an error :-)
+	 */
+
+	for (i = 0; i < ARRAY_SIZE(speeds); ++i) {
+		aiptek->curSetting.programmableDelay = speeds[i];
+		(void)aiptek_program_tablet(aiptek);
+		if (input_abs_get_max(aiptek->inputdev, ABS_X) > 0) {
+			dev_info(&intf->dev,
+				 "Aiptek using %d ms programming speed\n",
+				 aiptek->curSetting.programmableDelay);
+			break;
+		}
+	}
+
+	/* Murphy says that some day someone will have a tablet that fails the
+	   above test. That's you, Frederic Rodrigo */
+	if (i == ARRAY_SIZE(speeds)) {
+		dev_info(&intf->dev,
+			 "Aiptek tried all speeds, no sane response\n");
+		err = -EINVAL;
+		goto fail3;
+	}
+
+	/* Associate this driver's struct with the usb interface.
+	 */
+	usb_set_intfdata(intf, aiptek);
+
+	/* Set up the sysfs files
+	 */
+	err = sysfs_create_group(&intf->dev.kobj, &aiptek_attribute_group);
+	if (err) {
+		dev_warn(&intf->dev, "cannot create sysfs group err: %d\n",
+			 err);
+		goto fail3;
+        }
+
+	/* Register the tablet as an Input Device
+	 */
+	err = input_register_device(aiptek->inputdev);
+	if (err) {
+		dev_warn(&intf->dev,
+			 "input_register_device returned err: %d\n", err);
+		goto fail4;
+        }
+	return 0;
+
+ fail4:	sysfs_remove_group(&intf->dev.kobj, &aiptek_attribute_group);
+ fail3: usb_free_urb(aiptek->urb);
+ fail2:	usb_free_coherent(usbdev, AIPTEK_PACKET_LENGTH, aiptek->data,
+			  aiptek->data_dma);
+ fail1: usb_set_intfdata(intf, NULL);
+	input_free_device(inputdev);
+	kfree(aiptek);
+	return err;
+}
+
+/***********************************************************************
+ * Deal with tablet disconnecting from the system.
+ */
+static void aiptek_disconnect(struct usb_interface *intf)
+{
+	struct aiptek *aiptek = usb_get_intfdata(intf);
+
+	/* Disassociate driver's struct with usb interface
+	 */
+	usb_set_intfdata(intf, NULL);
+	if (aiptek != NULL) {
+		/* Free & unhook everything from the system.
+		 */
+		usb_kill_urb(aiptek->urb);
+		input_unregister_device(aiptek->inputdev);
+		sysfs_remove_group(&intf->dev.kobj, &aiptek_attribute_group);
+		usb_free_urb(aiptek->urb);
+		usb_free_coherent(interface_to_usbdev(intf),
+				  AIPTEK_PACKET_LENGTH,
+				  aiptek->data, aiptek->data_dma);
+		kfree(aiptek);
+	}
+}
+
+static struct usb_driver aiptek_driver = {
+	.name = "aiptek",
+	.probe = aiptek_probe,
+	.disconnect = aiptek_disconnect,
+	.id_table = aiptek_ids,
+};
+
+module_usb_driver(aiptek_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(programmableDelay, int, 0);
+MODULE_PARM_DESC(programmableDelay, "delay used during tablet programming");
+module_param(jitterDelay, int, 0);
+MODULE_PARM_DESC(jitterDelay, "stylus/mouse settlement delay");
diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c
new file mode 100644
index 0000000..8b68a21
--- /dev/null
+++ b/drivers/input/tablet/gtco.c
@@ -0,0 +1,1034 @@
+/*    -*- linux-c -*-
+
+GTCO digitizer USB driver
+
+TO CHECK:  Is pressure done right on report 5?
+
+Copyright (C) 2006  GTCO CalComp
+
+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; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of GTCO-CalComp not be used in advertising
+or publicity pertaining to distribution of the software without specific,
+written prior permission. GTCO-CalComp makes no representations about the
+suitability of this software for any purpose.  It is provided "as is"
+without express or implied warranty.
+
+GTCO-CALCOMP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL GTCO-CALCOMP BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTIONS, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+GTCO CalComp, Inc.
+7125 Riverwood Drive
+Columbia, MD 21046
+
+Jeremy Roberson jroberson@gtcocalcomp.com
+Scott Hill shill@gtcocalcomp.com
+*/
+
+
+
+/*#define DEBUG*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+#include <linux/bitops.h>
+
+#include <linux/usb/input.h>
+
+/* Version with a Major number of 2 is for kernel inclusion only. */
+#define  GTCO_VERSION   "2.00.0006"
+
+
+/*   MACROS  */
+
+#define VENDOR_ID_GTCO	      0x078C
+#define PID_400               0x400
+#define PID_401               0x401
+#define PID_1000              0x1000
+#define PID_1001              0x1001
+#define PID_1002              0x1002
+
+/* Max size of a single report */
+#define REPORT_MAX_SIZE       10
+
+
+/* Bitmask whether pen is in range */
+#define MASK_INRANGE 0x20
+#define MASK_BUTTON  0x01F
+
+#define  PATHLENGTH     64
+
+/* DATA STRUCTURES */
+
+/* Device table */
+static const struct usb_device_id gtco_usbid_table[] = {
+	{ USB_DEVICE(VENDOR_ID_GTCO, PID_400) },
+	{ USB_DEVICE(VENDOR_ID_GTCO, PID_401) },
+	{ USB_DEVICE(VENDOR_ID_GTCO, PID_1000) },
+	{ USB_DEVICE(VENDOR_ID_GTCO, PID_1001) },
+	{ USB_DEVICE(VENDOR_ID_GTCO, PID_1002) },
+	{ }
+};
+MODULE_DEVICE_TABLE (usb, gtco_usbid_table);
+
+
+/* Structure to hold all of our device specific stuff */
+struct gtco {
+
+	struct input_dev  *inputdevice; /* input device struct pointer  */
+	struct usb_device *usbdev; /* the usb device for this device */
+	struct usb_interface *intf;	/* the usb interface for this device */
+	struct urb        *urbinfo;	 /* urb for incoming reports      */
+	dma_addr_t        buf_dma;  /* dma addr of the data buffer*/
+	unsigned char *   buffer;   /* databuffer for reports */
+
+	char  usbpath[PATHLENGTH];
+	int   openCount;
+
+	/* Information pulled from Report Descriptor */
+	u32  usage;
+	u32  min_X;
+	u32  max_X;
+	u32  min_Y;
+	u32  max_Y;
+	s8   mintilt_X;
+	s8   maxtilt_X;
+	s8   mintilt_Y;
+	s8   maxtilt_Y;
+	u32  maxpressure;
+	u32  minpressure;
+};
+
+
+
+/*   Code for parsing the HID REPORT DESCRIPTOR          */
+
+/* From HID1.11 spec */
+struct hid_descriptor
+{
+	struct usb_descriptor_header header;
+	__le16   bcdHID;
+	u8       bCountryCode;
+	u8       bNumDescriptors;
+	u8       bDescriptorType;
+	__le16   wDescriptorLength;
+} __attribute__ ((packed));
+
+
+#define HID_DESCRIPTOR_SIZE   9
+#define HID_DEVICE_TYPE       33
+#define REPORT_DEVICE_TYPE    34
+
+
+#define PREF_TAG(x)     ((x)>>4)
+#define PREF_TYPE(x)    ((x>>2)&0x03)
+#define PREF_SIZE(x)    ((x)&0x03)
+
+#define TYPE_MAIN       0
+#define TYPE_GLOBAL     1
+#define TYPE_LOCAL      2
+#define TYPE_RESERVED   3
+
+#define TAG_MAIN_INPUT        0x8
+#define TAG_MAIN_OUTPUT       0x9
+#define TAG_MAIN_FEATURE      0xB
+#define TAG_MAIN_COL_START    0xA
+#define TAG_MAIN_COL_END      0xC
+
+#define TAG_GLOB_USAGE        0
+#define TAG_GLOB_LOG_MIN      1
+#define TAG_GLOB_LOG_MAX      2
+#define TAG_GLOB_PHYS_MIN     3
+#define TAG_GLOB_PHYS_MAX     4
+#define TAG_GLOB_UNIT_EXP     5
+#define TAG_GLOB_UNIT         6
+#define TAG_GLOB_REPORT_SZ    7
+#define TAG_GLOB_REPORT_ID    8
+#define TAG_GLOB_REPORT_CNT   9
+#define TAG_GLOB_PUSH         10
+#define TAG_GLOB_POP          11
+
+#define TAG_GLOB_MAX          12
+
+#define DIGITIZER_USAGE_TIP_PRESSURE   0x30
+#define DIGITIZER_USAGE_TILT_X         0x3D
+#define DIGITIZER_USAGE_TILT_Y         0x3E
+
+
+/*
+ *   This is an abbreviated parser for the HID Report Descriptor.  We
+ *   know what devices we are talking to, so this is by no means meant
+ *   to be generic.  We can make some safe assumptions:
+ *
+ *   - We know there are no LONG tags, all short
+ *   - We know that we have no MAIN Feature and MAIN Output items
+ *   - We know what the IRQ reports are supposed to look like.
+ *
+ *   The main purpose of this is to use the HID report desc to figure
+ *   out the mins and maxs of the fields in the IRQ reports.  The IRQ
+ *   reports for 400/401 change slightly if the max X is bigger than 64K.
+ *
+ */
+static void parse_hid_report_descriptor(struct gtco *device, char * report,
+					int length)
+{
+	struct device *ddev = &device->intf->dev;
+	int   x, i = 0;
+
+	/* Tag primitive vars */
+	__u8   prefix;
+	__u8   size;
+	__u8   tag;
+	__u8   type;
+	__u8   data   = 0;
+	__u16  data16 = 0;
+	__u32  data32 = 0;
+
+	/* For parsing logic */
+	int   inputnum = 0;
+	__u32 usage = 0;
+
+	/* Global Values, indexed by TAG */
+	__u32 globalval[TAG_GLOB_MAX];
+	__u32 oldval[TAG_GLOB_MAX];
+
+	/* Debug stuff */
+	char  maintype = 'x';
+	char  globtype[12];
+	int   indent = 0;
+	char  indentstr[10] = "";
+
+
+	dev_dbg(ddev, "======>>>>>>PARSE<<<<<<======\n");
+
+	/* Walk  this report and pull out the info we need */
+	while (i < length) {
+		prefix = report[i++];
+
+		/* Determine data size and save the data in the proper variable */
+		size = (1U << PREF_SIZE(prefix)) >> 1;
+		if (i + size > length) {
+			dev_err(ddev,
+				"Not enough data (need %d, have %d)\n",
+				i + size, length);
+			break;
+		}
+
+		switch (size) {
+		case 1:
+			data = report[i];
+			break;
+		case 2:
+			data16 = get_unaligned_le16(&report[i]);
+			break;
+		case 4:
+			data32 = get_unaligned_le32(&report[i]);
+			break;
+		}
+
+		/* Skip size of data */
+		i += size;
+
+		/* What we do depends on the tag type */
+		tag  = PREF_TAG(prefix);
+		type = PREF_TYPE(prefix);
+		switch (type) {
+		case TYPE_MAIN:
+			strcpy(globtype, "");
+			switch (tag) {
+
+			case TAG_MAIN_INPUT:
+				/*
+				 * The INPUT MAIN tag signifies this is
+				 * information from a report.  We need to
+				 * figure out what it is and store the
+				 * min/max values
+				 */
+
+				maintype = 'I';
+				if (data == 2)
+					strcpy(globtype, "Variable");
+				else if (data == 3)
+					strcpy(globtype, "Var|Const");
+
+				dev_dbg(ddev, "::::: Saving Report: %d input #%d Max: 0x%X(%d) Min:0x%X(%d) of %d bits\n",
+					globalval[TAG_GLOB_REPORT_ID], inputnum,
+					globalval[TAG_GLOB_LOG_MAX], globalval[TAG_GLOB_LOG_MAX],
+					globalval[TAG_GLOB_LOG_MIN], globalval[TAG_GLOB_LOG_MIN],
+					globalval[TAG_GLOB_REPORT_SZ] * globalval[TAG_GLOB_REPORT_CNT]);
+
+
+				/*
+				  We can assume that the first two input items
+				  are always the X and Y coordinates.  After
+				  that, we look for everything else by
+				  local usage value
+				 */
+				switch (inputnum) {
+				case 0:  /* X coord */
+					dev_dbg(ddev, "GER: X Usage: 0x%x\n", usage);
+					if (device->max_X == 0) {
+						device->max_X = globalval[TAG_GLOB_LOG_MAX];
+						device->min_X = globalval[TAG_GLOB_LOG_MIN];
+					}
+					break;
+
+				case 1:  /* Y coord */
+					dev_dbg(ddev, "GER: Y Usage: 0x%x\n", usage);
+					if (device->max_Y == 0) {
+						device->max_Y = globalval[TAG_GLOB_LOG_MAX];
+						device->min_Y = globalval[TAG_GLOB_LOG_MIN];
+					}
+					break;
+
+				default:
+					/* Tilt X */
+					if (usage == DIGITIZER_USAGE_TILT_X) {
+						if (device->maxtilt_X == 0) {
+							device->maxtilt_X = globalval[TAG_GLOB_LOG_MAX];
+							device->mintilt_X = globalval[TAG_GLOB_LOG_MIN];
+						}
+					}
+
+					/* Tilt Y */
+					if (usage == DIGITIZER_USAGE_TILT_Y) {
+						if (device->maxtilt_Y == 0) {
+							device->maxtilt_Y = globalval[TAG_GLOB_LOG_MAX];
+							device->mintilt_Y = globalval[TAG_GLOB_LOG_MIN];
+						}
+					}
+
+					/* Pressure */
+					if (usage == DIGITIZER_USAGE_TIP_PRESSURE) {
+						if (device->maxpressure == 0) {
+							device->maxpressure = globalval[TAG_GLOB_LOG_MAX];
+							device->minpressure = globalval[TAG_GLOB_LOG_MIN];
+						}
+					}
+
+					break;
+				}
+
+				inputnum++;
+				break;
+
+			case TAG_MAIN_OUTPUT:
+				maintype = 'O';
+				break;
+
+			case TAG_MAIN_FEATURE:
+				maintype = 'F';
+				break;
+
+			case TAG_MAIN_COL_START:
+				maintype = 'S';
+
+				if (data == 0) {
+					dev_dbg(ddev, "======>>>>>> Physical\n");
+					strcpy(globtype, "Physical");
+				} else
+					dev_dbg(ddev, "======>>>>>>\n");
+
+				/* Indent the debug output */
+				indent++;
+				for (x = 0; x < indent; x++)
+					indentstr[x] = '-';
+				indentstr[x] = 0;
+
+				/* Save global tags */
+				for (x = 0; x < TAG_GLOB_MAX; x++)
+					oldval[x] = globalval[x];
+
+				break;
+
+			case TAG_MAIN_COL_END:
+				dev_dbg(ddev, "<<<<<<======\n");
+				maintype = 'E';
+				indent--;
+				for (x = 0; x < indent; x++)
+					indentstr[x] = '-';
+				indentstr[x] = 0;
+
+				/* Copy global tags back */
+				for (x = 0; x < TAG_GLOB_MAX; x++)
+					globalval[x] = oldval[x];
+
+				break;
+			}
+
+			switch (size) {
+			case 1:
+				dev_dbg(ddev, "%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x\n",
+					indentstr, tag, maintype, size, globtype, data);
+				break;
+
+			case 2:
+				dev_dbg(ddev, "%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x\n",
+					indentstr, tag, maintype, size, globtype, data16);
+				break;
+
+			case 4:
+				dev_dbg(ddev, "%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x\n",
+					indentstr, tag, maintype, size, globtype, data32);
+				break;
+			}
+			break;
+
+		case TYPE_GLOBAL:
+			switch (tag) {
+			case TAG_GLOB_USAGE:
+				/*
+				 * First time we hit the global usage tag,
+				 * it should tell us the type of device
+				 */
+				if (device->usage == 0)
+					device->usage = data;
+
+				strcpy(globtype, "USAGE");
+				break;
+
+			case TAG_GLOB_LOG_MIN:
+				strcpy(globtype, "LOG_MIN");
+				break;
+
+			case TAG_GLOB_LOG_MAX:
+				strcpy(globtype, "LOG_MAX");
+				break;
+
+			case TAG_GLOB_PHYS_MIN:
+				strcpy(globtype, "PHYS_MIN");
+				break;
+
+			case TAG_GLOB_PHYS_MAX:
+				strcpy(globtype, "PHYS_MAX");
+				break;
+
+			case TAG_GLOB_UNIT_EXP:
+				strcpy(globtype, "EXP");
+				break;
+
+			case TAG_GLOB_UNIT:
+				strcpy(globtype, "UNIT");
+				break;
+
+			case TAG_GLOB_REPORT_SZ:
+				strcpy(globtype, "REPORT_SZ");
+				break;
+
+			case TAG_GLOB_REPORT_ID:
+				strcpy(globtype, "REPORT_ID");
+				/* New report, restart numbering */
+				inputnum = 0;
+				break;
+
+			case TAG_GLOB_REPORT_CNT:
+				strcpy(globtype, "REPORT_CNT");
+				break;
+
+			case TAG_GLOB_PUSH:
+				strcpy(globtype, "PUSH");
+				break;
+
+			case TAG_GLOB_POP:
+				strcpy(globtype, "POP");
+				break;
+			}
+
+			/* Check to make sure we have a good tag number
+			   so we don't overflow array */
+			if (tag < TAG_GLOB_MAX) {
+				switch (size) {
+				case 1:
+					dev_dbg(ddev, "%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x\n",
+						indentstr, globtype, tag, size, data);
+					globalval[tag] = data;
+					break;
+
+				case 2:
+					dev_dbg(ddev, "%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x\n",
+						indentstr, globtype, tag, size, data16);
+					globalval[tag] = data16;
+					break;
+
+				case 4:
+					dev_dbg(ddev, "%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x\n",
+						indentstr, globtype, tag, size, data32);
+					globalval[tag] = data32;
+					break;
+				}
+			} else {
+				dev_dbg(ddev, "%sGLOBALTAG: ILLEGAL TAG:%d SIZE: %d\n",
+					indentstr, tag, size);
+			}
+			break;
+
+		case TYPE_LOCAL:
+			switch (tag) {
+			case TAG_GLOB_USAGE:
+				strcpy(globtype, "USAGE");
+				/* Always 1 byte */
+				usage = data;
+				break;
+
+			case TAG_GLOB_LOG_MIN:
+				strcpy(globtype, "MIN");
+				break;
+
+			case TAG_GLOB_LOG_MAX:
+				strcpy(globtype, "MAX");
+				break;
+
+			default:
+				strcpy(globtype, "UNKNOWN");
+				break;
+			}
+
+			switch (size) {
+			case 1:
+				dev_dbg(ddev, "%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x\n",
+					indentstr, tag, globtype, size, data);
+				break;
+
+			case 2:
+				dev_dbg(ddev, "%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x\n",
+					indentstr, tag, globtype, size, data16);
+				break;
+
+			case 4:
+				dev_dbg(ddev, "%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x\n",
+					indentstr, tag, globtype, size, data32);
+				break;
+			}
+
+			break;
+		}
+	}
+}
+
+/*   INPUT DRIVER Routines                               */
+
+/*
+ * Called when opening the input device.  This will submit the URB to
+ * the usb system so we start getting reports
+ */
+static int gtco_input_open(struct input_dev *inputdev)
+{
+	struct gtco *device = input_get_drvdata(inputdev);
+
+	device->urbinfo->dev = device->usbdev;
+	if (usb_submit_urb(device->urbinfo, GFP_KERNEL))
+		return -EIO;
+
+	return 0;
+}
+
+/*
+ * Called when closing the input device.  This will unlink the URB
+ */
+static void gtco_input_close(struct input_dev *inputdev)
+{
+	struct gtco *device = input_get_drvdata(inputdev);
+
+	usb_kill_urb(device->urbinfo);
+}
+
+
+/*
+ *  Setup input device capabilities.  Tell the input system what this
+ *  device is capable of generating.
+ *
+ *  This information is based on what is read from the HID report and
+ *  placed in the struct gtco structure
+ *
+ */
+static void gtco_setup_caps(struct input_dev *inputdev)
+{
+	struct gtco *device = input_get_drvdata(inputdev);
+
+	/* Which events */
+	inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) |
+		BIT_MASK(EV_MSC);
+
+	/* Misc event menu block */
+	inputdev->mscbit[0] = BIT_MASK(MSC_SCAN) | BIT_MASK(MSC_SERIAL) |
+		BIT_MASK(MSC_RAW);
+
+	/* Absolute values based on HID report info */
+	input_set_abs_params(inputdev, ABS_X, device->min_X, device->max_X,
+			     0, 0);
+	input_set_abs_params(inputdev, ABS_Y, device->min_Y, device->max_Y,
+			     0, 0);
+
+	/* Proximity */
+	input_set_abs_params(inputdev, ABS_DISTANCE, 0, 1, 0, 0);
+
+	/* Tilt & pressure */
+	input_set_abs_params(inputdev, ABS_TILT_X, device->mintilt_X,
+			     device->maxtilt_X, 0, 0);
+	input_set_abs_params(inputdev, ABS_TILT_Y, device->mintilt_Y,
+			     device->maxtilt_Y, 0, 0);
+	input_set_abs_params(inputdev, ABS_PRESSURE, device->minpressure,
+			     device->maxpressure, 0, 0);
+
+	/* Transducer */
+	input_set_abs_params(inputdev, ABS_MISC, 0, 0xFF, 0, 0);
+}
+
+/*   USB Routines  */
+
+/*
+ * URB callback routine.  Called when we get IRQ reports from the
+ *  digitizer.
+ *
+ *  This bridges the USB and input device worlds.  It generates events
+ *  on the input device based on the USB reports.
+ */
+static void gtco_urb_callback(struct urb *urbinfo)
+{
+	struct gtco *device = urbinfo->context;
+	struct input_dev  *inputdev;
+	int               rc;
+	u32               val = 0;
+	char              le_buffer[2];
+
+	inputdev = device->inputdevice;
+
+	/* Was callback OK? */
+	if (urbinfo->status == -ECONNRESET ||
+	    urbinfo->status == -ENOENT ||
+	    urbinfo->status == -ESHUTDOWN) {
+
+		/* Shutdown is occurring. Return and don't queue up any more */
+		return;
+	}
+
+	if (urbinfo->status != 0) {
+		/*
+		 * Some unknown error.  Hopefully temporary. Just go and
+		 * requeue an URB
+		 */
+		goto resubmit;
+	}
+
+	/*
+	 * Good URB, now process
+	 */
+
+	/* PID dependent when we interpret the report */
+	if (inputdev->id.product == PID_1000 ||
+	    inputdev->id.product == PID_1001 ||
+	    inputdev->id.product == PID_1002) {
+
+		/*
+		 * Switch on the report ID
+		 * Conveniently, the reports have more information, the higher
+		 * the report number.  We can just fall through the case
+		 * statements if we start with the highest number report
+		 */
+		switch (device->buffer[0]) {
+		case 5:
+			/* Pressure is 9 bits */
+			val = ((u16)(device->buffer[8]) << 1);
+			val |= (u16)(device->buffer[7] >> 7);
+			input_report_abs(inputdev, ABS_PRESSURE,
+					 device->buffer[8]);
+
+			/* Mask out the Y tilt value used for pressure */
+			device->buffer[7] = (u8)((device->buffer[7]) & 0x7F);
+
+			/* Fall thru */
+		case 4:
+			/* Tilt */
+			input_report_abs(inputdev, ABS_TILT_X,
+					 sign_extend32(device->buffer[6], 6));
+
+			input_report_abs(inputdev, ABS_TILT_Y,
+					 sign_extend32(device->buffer[7], 6));
+
+			/* Fall thru */
+		case 2:
+		case 3:
+			/* Convert buttons, only 5 bits possible */
+			val = (device->buffer[5]) & MASK_BUTTON;
+
+			/* We don't apply any meaning to the bitmask,
+			   just report */
+			input_event(inputdev, EV_MSC, MSC_SERIAL, val);
+
+			/*  Fall thru */
+		case 1:
+			/* All reports have X and Y coords in the same place */
+			val = get_unaligned_le16(&device->buffer[1]);
+			input_report_abs(inputdev, ABS_X, val);
+
+			val = get_unaligned_le16(&device->buffer[3]);
+			input_report_abs(inputdev, ABS_Y, val);
+
+			/* Ditto for proximity bit */
+			val = device->buffer[5] & MASK_INRANGE ? 1 : 0;
+			input_report_abs(inputdev, ABS_DISTANCE, val);
+
+			/* Report 1 is an exception to how we handle buttons */
+			/* Buttons are an index, not a bitmask */
+			if (device->buffer[0] == 1) {
+
+				/*
+				 * Convert buttons, 5 bit index
+				 * Report value of index set as one,
+				 * the rest as 0
+				 */
+				val = device->buffer[5] & MASK_BUTTON;
+				dev_dbg(&device->intf->dev,
+					"======>>>>>>REPORT 1: val 0x%X(%d)\n",
+					val, val);
+
+				/*
+				 * We don't apply any meaning to the button
+				 * index, just report it
+				 */
+				input_event(inputdev, EV_MSC, MSC_SERIAL, val);
+			}
+			break;
+
+		case 7:
+			/* Menu blocks */
+			input_event(inputdev, EV_MSC, MSC_SCAN,
+				    device->buffer[1]);
+			break;
+		}
+	}
+
+	/* Other pid class */
+	if (inputdev->id.product == PID_400 ||
+	    inputdev->id.product == PID_401) {
+
+		/* Report 2 */
+		if (device->buffer[0] == 2) {
+			/* Menu blocks */
+			input_event(inputdev, EV_MSC, MSC_SCAN, device->buffer[1]);
+		}
+
+		/*  Report 1 */
+		if (device->buffer[0] == 1) {
+			char buttonbyte;
+
+			/*  IF X max > 64K, we still a bit from the y report */
+			if (device->max_X > 0x10000) {
+
+				val = (u16)(((u16)(device->buffer[2] << 8)) | (u8)device->buffer[1]);
+				val |= (u32)(((u8)device->buffer[3] & 0x1) << 16);
+
+				input_report_abs(inputdev, ABS_X, val);
+
+				le_buffer[0]  = (u8)((u8)(device->buffer[3]) >> 1);
+				le_buffer[0] |= (u8)((device->buffer[3] & 0x1) << 7);
+
+				le_buffer[1]  = (u8)(device->buffer[4] >> 1);
+				le_buffer[1] |= (u8)((device->buffer[5] & 0x1) << 7);
+
+				val = get_unaligned_le16(le_buffer);
+				input_report_abs(inputdev, ABS_Y, val);
+
+				/*
+				 * Shift the button byte right by one to
+				 * make it look like the standard report
+				 */
+				buttonbyte = device->buffer[5] >> 1;
+			} else {
+
+				val = get_unaligned_le16(&device->buffer[1]);
+				input_report_abs(inputdev, ABS_X, val);
+
+				val = get_unaligned_le16(&device->buffer[3]);
+				input_report_abs(inputdev, ABS_Y, val);
+
+				buttonbyte = device->buffer[5];
+			}
+
+			/* BUTTONS and PROXIMITY */
+			val = buttonbyte & MASK_INRANGE ? 1 : 0;
+			input_report_abs(inputdev, ABS_DISTANCE, val);
+
+			/* Convert buttons, only 4 bits possible */
+			val = buttonbyte & 0x0F;
+#ifdef USE_BUTTONS
+			for (i = 0; i < 5; i++)
+				input_report_key(inputdev, BTN_DIGI + i, val & (1 << i));
+#else
+			/* We don't apply any meaning to the bitmask, just report */
+			input_event(inputdev, EV_MSC, MSC_SERIAL, val);
+#endif
+
+			/* TRANSDUCER */
+			input_report_abs(inputdev, ABS_MISC, device->buffer[6]);
+		}
+	}
+
+	/* Everybody gets report ID's */
+	input_event(inputdev, EV_MSC, MSC_RAW,  device->buffer[0]);
+
+	/* Sync it up */
+	input_sync(inputdev);
+
+ resubmit:
+	rc = usb_submit_urb(urbinfo, GFP_ATOMIC);
+	if (rc != 0)
+		dev_err(&device->intf->dev,
+			"usb_submit_urb failed rc=0x%x\n", rc);
+}
+
+/*
+ *  The probe routine.  This is called when the kernel find the matching USB
+ *   vendor/product.  We do the following:
+ *
+ *    - Allocate mem for a local structure to manage the device
+ *    - Request a HID Report Descriptor from the device and parse it to
+ *      find out the device parameters
+ *    - Create an input device and assign it attributes
+ *   - Allocate an URB so the device can talk to us when the input
+ *      queue is open
+ */
+static int gtco_probe(struct usb_interface *usbinterface,
+		      const struct usb_device_id *id)
+{
+
+	struct gtco             *gtco;
+	struct input_dev        *input_dev;
+	struct hid_descriptor   *hid_desc;
+	char                    *report;
+	int                     result = 0, retry;
+	int			error;
+	struct usb_endpoint_descriptor *endpoint;
+
+	/* Allocate memory for device structure */
+	gtco = kzalloc(sizeof(struct gtco), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!gtco || !input_dev) {
+		dev_err(&usbinterface->dev, "No more memory\n");
+		error = -ENOMEM;
+		goto err_free_devs;
+	}
+
+	/* Set pointer to the input device */
+	gtco->inputdevice = input_dev;
+
+	/* Save interface information */
+	gtco->usbdev = interface_to_usbdev(usbinterface);
+	gtco->intf = usbinterface;
+
+	/* Allocate some data for incoming reports */
+	gtco->buffer = usb_alloc_coherent(gtco->usbdev, REPORT_MAX_SIZE,
+					  GFP_KERNEL, &gtco->buf_dma);
+	if (!gtco->buffer) {
+		dev_err(&usbinterface->dev, "No more memory for us buffers\n");
+		error = -ENOMEM;
+		goto err_free_devs;
+	}
+
+	/* Allocate URB for reports */
+	gtco->urbinfo = usb_alloc_urb(0, GFP_KERNEL);
+	if (!gtco->urbinfo) {
+		dev_err(&usbinterface->dev, "Failed to allocate URB\n");
+		error = -ENOMEM;
+		goto err_free_buf;
+	}
+
+	/* Sanity check that a device has an endpoint */
+	if (usbinterface->altsetting[0].desc.bNumEndpoints < 1) {
+		dev_err(&usbinterface->dev,
+			"Invalid number of endpoints\n");
+		error = -EINVAL;
+		goto err_free_urb;
+	}
+
+	/*
+	 * The endpoint is always altsetting 0, we know this since we know
+	 * this device only has one interrupt endpoint
+	 */
+	endpoint = &usbinterface->altsetting[0].endpoint[0].desc;
+
+	/* Some debug */
+	dev_dbg(&usbinterface->dev, "gtco # interfaces: %d\n", usbinterface->num_altsetting);
+	dev_dbg(&usbinterface->dev, "num endpoints:     %d\n", usbinterface->cur_altsetting->desc.bNumEndpoints);
+	dev_dbg(&usbinterface->dev, "interface class:   %d\n", usbinterface->cur_altsetting->desc.bInterfaceClass);
+	dev_dbg(&usbinterface->dev, "endpoint: attribute:0x%x type:0x%x\n", endpoint->bmAttributes, endpoint->bDescriptorType);
+	if (usb_endpoint_xfer_int(endpoint))
+		dev_dbg(&usbinterface->dev, "endpoint: we have interrupt endpoint\n");
+
+	dev_dbg(&usbinterface->dev, "endpoint extra len:%d\n", usbinterface->altsetting[0].extralen);
+
+	/*
+	 * Find the HID descriptor so we can find out the size of the
+	 * HID report descriptor
+	 */
+	if (usb_get_extra_descriptor(usbinterface->cur_altsetting,
+				     HID_DEVICE_TYPE, &hid_desc) != 0) {
+		dev_err(&usbinterface->dev,
+			"Can't retrieve exta USB descriptor to get hid report descriptor length\n");
+		error = -EIO;
+		goto err_free_urb;
+	}
+
+	dev_dbg(&usbinterface->dev,
+		"Extra descriptor success: type:%d  len:%d\n",
+		hid_desc->bDescriptorType,  hid_desc->wDescriptorLength);
+
+	report = kzalloc(le16_to_cpu(hid_desc->wDescriptorLength), GFP_KERNEL);
+	if (!report) {
+		dev_err(&usbinterface->dev, "No more memory for report\n");
+		error = -ENOMEM;
+		goto err_free_urb;
+	}
+
+	/* Couple of tries to get reply */
+	for (retry = 0; retry < 3; retry++) {
+		result = usb_control_msg(gtco->usbdev,
+					 usb_rcvctrlpipe(gtco->usbdev, 0),
+					 USB_REQ_GET_DESCRIPTOR,
+					 USB_RECIP_INTERFACE | USB_DIR_IN,
+					 REPORT_DEVICE_TYPE << 8,
+					 0, /* interface */
+					 report,
+					 le16_to_cpu(hid_desc->wDescriptorLength),
+					 5000); /* 5 secs */
+
+		dev_dbg(&usbinterface->dev, "usb_control_msg result: %d\n", result);
+		if (result == le16_to_cpu(hid_desc->wDescriptorLength)) {
+			parse_hid_report_descriptor(gtco, report, result);
+			break;
+		}
+	}
+
+	kfree(report);
+
+	/* If we didn't get the report, fail */
+	if (result != le16_to_cpu(hid_desc->wDescriptorLength)) {
+		dev_err(&usbinterface->dev,
+			"Failed to get HID Report Descriptor of size: %d\n",
+			hid_desc->wDescriptorLength);
+		error = -EIO;
+		goto err_free_urb;
+	}
+
+	/* Create a device file node */
+	usb_make_path(gtco->usbdev, gtco->usbpath, sizeof(gtco->usbpath));
+	strlcat(gtco->usbpath, "/input0", sizeof(gtco->usbpath));
+
+	/* Set Input device functions */
+	input_dev->open = gtco_input_open;
+	input_dev->close = gtco_input_close;
+
+	/* Set input device information */
+	input_dev->name = "GTCO_CalComp";
+	input_dev->phys = gtco->usbpath;
+
+	input_set_drvdata(input_dev, gtco);
+
+	/* Now set up all the input device capabilities */
+	gtco_setup_caps(input_dev);
+
+	/* Set input device required ID information */
+	usb_to_input_id(gtco->usbdev, &input_dev->id);
+	input_dev->dev.parent = &usbinterface->dev;
+
+	/* Setup the URB, it will be posted later on open of input device */
+	endpoint = &usbinterface->altsetting[0].endpoint[0].desc;
+
+	usb_fill_int_urb(gtco->urbinfo,
+			 gtco->usbdev,
+			 usb_rcvintpipe(gtco->usbdev,
+					endpoint->bEndpointAddress),
+			 gtco->buffer,
+			 REPORT_MAX_SIZE,
+			 gtco_urb_callback,
+			 gtco,
+			 endpoint->bInterval);
+
+	gtco->urbinfo->transfer_dma = gtco->buf_dma;
+	gtco->urbinfo->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* Save gtco pointer in USB interface gtco */
+	usb_set_intfdata(usbinterface, gtco);
+
+	/* All done, now register the input device */
+	error = input_register_device(input_dev);
+	if (error)
+		goto err_free_urb;
+
+	return 0;
+
+ err_free_urb:
+	usb_free_urb(gtco->urbinfo);
+ err_free_buf:
+	usb_free_coherent(gtco->usbdev, REPORT_MAX_SIZE,
+			  gtco->buffer, gtco->buf_dma);
+ err_free_devs:
+	input_free_device(input_dev);
+	kfree(gtco);
+	return error;
+}
+
+/*
+ *  This function is a standard USB function called when the USB device
+ *  is disconnected.  We will get rid of the URV, de-register the input
+ *  device, and free up allocated memory
+ */
+static void gtco_disconnect(struct usb_interface *interface)
+{
+	/* Grab private device ptr */
+	struct gtco *gtco = usb_get_intfdata(interface);
+
+	/* Now reverse all the registration stuff */
+	if (gtco) {
+		input_unregister_device(gtco->inputdevice);
+		usb_kill_urb(gtco->urbinfo);
+		usb_free_urb(gtco->urbinfo);
+		usb_free_coherent(gtco->usbdev, REPORT_MAX_SIZE,
+				  gtco->buffer, gtco->buf_dma);
+		kfree(gtco);
+	}
+
+	dev_info(&interface->dev, "gtco driver disconnected\n");
+}
+
+/*   STANDARD MODULE LOAD ROUTINES  */
+
+static struct usb_driver gtco_driverinfo_table = {
+	.name		= "gtco",
+	.id_table	= gtco_usbid_table,
+	.probe		= gtco_probe,
+	.disconnect	= gtco_disconnect,
+};
+
+module_usb_driver(gtco_driverinfo_table);
+
+MODULE_DESCRIPTION("GTCO digitizer USB driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/tablet/hanwang.c b/drivers/input/tablet/hanwang.c
new file mode 100644
index 0000000..df4bea9
--- /dev/null
+++ b/drivers/input/tablet/hanwang.c
@@ -0,0 +1,464 @@
+/*
+ *  USB Hanwang tablet support
+ *
+ *  Copyright (c) 2010 Xing Wei <weixing@hanwang.com.cn>
+ *
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+
+#define DRIVER_AUTHOR   "Xing Wei <weixing@hanwang.com.cn>"
+#define DRIVER_DESC     "USB Hanwang tablet driver"
+#define DRIVER_LICENSE  "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_HANWANG		0x0b57
+#define HANWANG_TABLET_INT_CLASS	0x0003
+#define HANWANG_TABLET_INT_SUB_CLASS	0x0001
+#define HANWANG_TABLET_INT_PROTOCOL	0x0002
+
+#define ART_MASTER_PKGLEN_MAX	10
+
+/* device IDs */
+#define STYLUS_DEVICE_ID	0x02
+#define TOUCH_DEVICE_ID		0x03
+#define CURSOR_DEVICE_ID	0x06
+#define ERASER_DEVICE_ID	0x0A
+#define PAD_DEVICE_ID		0x0F
+
+/* match vendor and interface info  */
+#define HANWANG_TABLET_DEVICE(vend, cl, sc, pr) \
+	.match_flags = USB_DEVICE_ID_MATCH_VENDOR \
+		| USB_DEVICE_ID_MATCH_INT_INFO, \
+	.idVendor = (vend), \
+	.bInterfaceClass = (cl), \
+	.bInterfaceSubClass = (sc), \
+	.bInterfaceProtocol = (pr)
+
+enum hanwang_tablet_type {
+	HANWANG_ART_MASTER_III,
+	HANWANG_ART_MASTER_HD,
+	HANWANG_ART_MASTER_II,
+};
+
+struct hanwang {
+	unsigned char *data;
+	dma_addr_t data_dma;
+	struct input_dev *dev;
+	struct usb_device *usbdev;
+	struct urb *irq;
+	const struct hanwang_features *features;
+	unsigned int current_tool;
+	unsigned int current_id;
+	char name[64];
+	char phys[32];
+};
+
+struct hanwang_features {
+	unsigned short pid;
+	char *name;
+	enum hanwang_tablet_type type;
+	int pkg_len;
+	int max_x;
+	int max_y;
+	int max_tilt_x;
+	int max_tilt_y;
+	int max_pressure;
+};
+
+static const struct hanwang_features features_array[] = {
+	{ 0x8528, "Hanwang Art Master III 0906", HANWANG_ART_MASTER_III,
+	  ART_MASTER_PKGLEN_MAX, 0x5757, 0x3692, 0x3f, 0x7f, 2048 },
+	{ 0x8529, "Hanwang Art Master III 0604", HANWANG_ART_MASTER_III,
+	  ART_MASTER_PKGLEN_MAX, 0x3d84, 0x2672, 0x3f, 0x7f, 2048 },
+	{ 0x852a, "Hanwang Art Master III 1308", HANWANG_ART_MASTER_III,
+	  ART_MASTER_PKGLEN_MAX, 0x7f00, 0x4f60, 0x3f, 0x7f, 2048 },
+	{ 0x8401, "Hanwang Art Master HD 5012", HANWANG_ART_MASTER_HD,
+	  ART_MASTER_PKGLEN_MAX, 0x678e, 0x4150, 0x3f, 0x7f, 1024 },
+	{ 0x8503, "Hanwang Art Master II", HANWANG_ART_MASTER_II,
+	  ART_MASTER_PKGLEN_MAX, 0x27de, 0x1cfe, 0x3f, 0x7f, 1024 },
+};
+
+static const int hw_eventtypes[] = {
+	EV_KEY, EV_ABS, EV_MSC,
+};
+
+static const int hw_absevents[] = {
+	ABS_X, ABS_Y, ABS_TILT_X, ABS_TILT_Y, ABS_WHEEL,
+	ABS_RX, ABS_RY, ABS_PRESSURE, ABS_MISC,
+};
+
+static const int hw_btnevents[] = {
+	BTN_STYLUS, BTN_STYLUS2, BTN_TOOL_PEN, BTN_TOOL_RUBBER,
+	BTN_TOOL_MOUSE, BTN_TOOL_FINGER,
+	BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8,
+};
+
+static const int hw_mscevents[] = {
+	MSC_SERIAL,
+};
+
+static void hanwang_parse_packet(struct hanwang *hanwang)
+{
+	unsigned char *data = hanwang->data;
+	struct input_dev *input_dev = hanwang->dev;
+	struct usb_device *dev = hanwang->usbdev;
+	enum hanwang_tablet_type type = hanwang->features->type;
+	int i;
+	u16 p;
+
+	if (type == HANWANG_ART_MASTER_II) {
+		hanwang->current_tool = BTN_TOOL_PEN;
+		hanwang->current_id = STYLUS_DEVICE_ID;
+	}
+
+	switch (data[0]) {
+	case 0x02:	/* data packet */
+		switch (data[1]) {
+		case 0x80:	/* tool prox out */
+			if (type != HANWANG_ART_MASTER_II) {
+				hanwang->current_id = 0;
+				input_report_key(input_dev,
+						 hanwang->current_tool, 0);
+			}
+			break;
+
+		case 0x00:	/* artmaster ii pen leave */
+			if (type == HANWANG_ART_MASTER_II) {
+				hanwang->current_id = 0;
+				input_report_key(input_dev,
+						 hanwang->current_tool, 0);
+			}
+			break;
+
+		case 0xc2:	/* first time tool prox in */
+			switch (data[3] & 0xf0) {
+			case 0x20:	/* art_master III */
+			case 0x30:	/* art_master_HD */
+				hanwang->current_id = STYLUS_DEVICE_ID;
+				hanwang->current_tool = BTN_TOOL_PEN;
+				input_report_key(input_dev, BTN_TOOL_PEN, 1);
+				break;
+			case 0xa0:	/* art_master III */
+			case 0xb0:	/* art_master_HD */
+				hanwang->current_id = ERASER_DEVICE_ID;
+				hanwang->current_tool = BTN_TOOL_RUBBER;
+				input_report_key(input_dev, BTN_TOOL_RUBBER, 1);
+				break;
+			default:
+				hanwang->current_id = 0;
+				dev_dbg(&dev->dev,
+					"unknown tablet tool %02x\n", data[0]);
+				break;
+			}
+			break;
+
+		default:	/* tool data packet */
+			switch (type) {
+			case HANWANG_ART_MASTER_III:
+				p = (data[6] << 3) |
+				    ((data[7] & 0xc0) >> 5) |
+				    (data[1] & 0x01);
+				break;
+
+			case HANWANG_ART_MASTER_HD:
+			case HANWANG_ART_MASTER_II:
+				p = (data[7] >> 6) | (data[6] << 2);
+				break;
+
+			default:
+				p = 0;
+				break;
+			}
+
+			input_report_abs(input_dev, ABS_X,
+					 be16_to_cpup((__be16 *)&data[2]));
+			input_report_abs(input_dev, ABS_Y,
+					 be16_to_cpup((__be16 *)&data[4]));
+			input_report_abs(input_dev, ABS_PRESSURE, p);
+			input_report_abs(input_dev, ABS_TILT_X, data[7] & 0x3f);
+			input_report_abs(input_dev, ABS_TILT_Y, data[8] & 0x7f);
+			input_report_key(input_dev, BTN_STYLUS, data[1] & 0x02);
+
+			if (type != HANWANG_ART_MASTER_II)
+				input_report_key(input_dev, BTN_STYLUS2,
+						 data[1] & 0x04);
+			else
+				input_report_key(input_dev, BTN_TOOL_PEN, 1);
+
+			break;
+		}
+
+		input_report_abs(input_dev, ABS_MISC, hanwang->current_id);
+		input_event(input_dev, EV_MSC, MSC_SERIAL,
+				hanwang->features->pid);
+		break;
+
+	case 0x0c:
+		/* roll wheel */
+		hanwang->current_id = PAD_DEVICE_ID;
+
+		switch (type) {
+		case HANWANG_ART_MASTER_III:
+			input_report_key(input_dev, BTN_TOOL_FINGER,
+					 data[1] || data[2] || data[3]);
+			input_report_abs(input_dev, ABS_WHEEL, data[1]);
+			input_report_key(input_dev, BTN_0, data[2]);
+			for (i = 0; i < 8; i++)
+				input_report_key(input_dev,
+					 BTN_1 + i, data[3] & (1 << i));
+			break;
+
+		case HANWANG_ART_MASTER_HD:
+			input_report_key(input_dev, BTN_TOOL_FINGER, data[1] ||
+					data[2] || data[3] || data[4] ||
+					data[5] || data[6]);
+			input_report_abs(input_dev, ABS_RX,
+					((data[1] & 0x1f) << 8) | data[2]);
+			input_report_abs(input_dev, ABS_RY,
+					((data[3] & 0x1f) << 8) | data[4]);
+			input_report_key(input_dev, BTN_0, data[5] & 0x01);
+			for (i = 0; i < 4; i++) {
+				input_report_key(input_dev,
+					 BTN_1 + i, data[5] & (1 << i));
+				input_report_key(input_dev,
+					 BTN_5 + i, data[6] & (1 << i));
+			}
+			break;
+
+		case HANWANG_ART_MASTER_II:
+			dev_dbg(&dev->dev, "error packet  %02x\n", data[0]);
+			return;
+		}
+
+		input_report_abs(input_dev, ABS_MISC, hanwang->current_id);
+		input_event(input_dev, EV_MSC, MSC_SERIAL, 0xffffffff);
+		break;
+
+	default:
+		dev_dbg(&dev->dev, "error packet  %02x\n", data[0]);
+		break;
+	}
+
+	input_sync(input_dev);
+}
+
+static void hanwang_irq(struct urb *urb)
+{
+	struct hanwang *hanwang = urb->context;
+	struct usb_device *dev = hanwang->usbdev;
+	int retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */;
+		hanwang_parse_packet(hanwang);
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_err(&dev->dev, "%s - urb shutting down with status: %d",
+			__func__, urb->status);
+		return;
+	default:
+		dev_err(&dev->dev, "%s - nonzero urb status received: %d",
+			__func__, urb->status);
+		break;
+	}
+
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d",
+			__func__, retval);
+}
+
+static int hanwang_open(struct input_dev *dev)
+{
+	struct hanwang *hanwang = input_get_drvdata(dev);
+
+	hanwang->irq->dev = hanwang->usbdev;
+	if (usb_submit_urb(hanwang->irq, GFP_KERNEL))
+		return -EIO;
+
+	return 0;
+}
+
+static void hanwang_close(struct input_dev *dev)
+{
+	struct hanwang *hanwang = input_get_drvdata(dev);
+
+	usb_kill_urb(hanwang->irq);
+}
+
+static bool get_features(struct usb_device *dev, struct hanwang *hanwang)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(features_array); i++) {
+		if (le16_to_cpu(dev->descriptor.idProduct) ==
+				features_array[i].pid) {
+			hanwang->features = &features_array[i];
+			return true;
+		}
+	}
+
+	return false;
+}
+
+
+static int hanwang_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_endpoint_descriptor *endpoint;
+	struct hanwang *hanwang;
+	struct input_dev *input_dev;
+	int error;
+	int i;
+
+	if (intf->cur_altsetting->desc.bNumEndpoints < 1)
+		return -ENODEV;
+
+	hanwang = kzalloc(sizeof(struct hanwang), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!hanwang || !input_dev) {
+		error = -ENOMEM;
+		goto fail1;
+	}
+
+	if (!get_features(dev, hanwang)) {
+		error = -ENXIO;
+		goto fail1;
+	}
+
+	hanwang->data = usb_alloc_coherent(dev, hanwang->features->pkg_len,
+					GFP_KERNEL, &hanwang->data_dma);
+	if (!hanwang->data) {
+		error = -ENOMEM;
+		goto fail1;
+	}
+
+	hanwang->irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!hanwang->irq) {
+		error = -ENOMEM;
+		goto fail2;
+	}
+
+	hanwang->usbdev = dev;
+	hanwang->dev = input_dev;
+
+	usb_make_path(dev, hanwang->phys, sizeof(hanwang->phys));
+	strlcat(hanwang->phys, "/input0", sizeof(hanwang->phys));
+
+	strlcpy(hanwang->name, hanwang->features->name, sizeof(hanwang->name));
+	input_dev->name = hanwang->name;
+	input_dev->phys = hanwang->phys;
+	usb_to_input_id(dev, &input_dev->id);
+	input_dev->dev.parent = &intf->dev;
+
+	input_set_drvdata(input_dev, hanwang);
+
+	input_dev->open = hanwang_open;
+	input_dev->close = hanwang_close;
+
+	for (i = 0; i < ARRAY_SIZE(hw_eventtypes); ++i)
+		__set_bit(hw_eventtypes[i], input_dev->evbit);
+
+	for (i = 0; i < ARRAY_SIZE(hw_absevents); ++i)
+		__set_bit(hw_absevents[i], input_dev->absbit);
+
+	for (i = 0; i < ARRAY_SIZE(hw_btnevents); ++i)
+		__set_bit(hw_btnevents[i], input_dev->keybit);
+
+	for (i = 0; i < ARRAY_SIZE(hw_mscevents); ++i)
+		__set_bit(hw_mscevents[i], input_dev->mscbit);
+
+	input_set_abs_params(input_dev, ABS_X,
+			     0, hanwang->features->max_x, 4, 0);
+	input_set_abs_params(input_dev, ABS_Y,
+			     0, hanwang->features->max_y, 4, 0);
+	input_set_abs_params(input_dev, ABS_TILT_X,
+			     0, hanwang->features->max_tilt_x, 0, 0);
+	input_set_abs_params(input_dev, ABS_TILT_Y,
+			     0, hanwang->features->max_tilt_y, 0, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE,
+			     0, hanwang->features->max_pressure, 0, 0);
+
+	endpoint = &intf->cur_altsetting->endpoint[0].desc;
+	usb_fill_int_urb(hanwang->irq, dev,
+			usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+			hanwang->data, hanwang->features->pkg_len,
+			hanwang_irq, hanwang, endpoint->bInterval);
+	hanwang->irq->transfer_dma = hanwang->data_dma;
+	hanwang->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	error = input_register_device(hanwang->dev);
+	if (error)
+		goto fail3;
+
+	usb_set_intfdata(intf, hanwang);
+
+	return 0;
+
+ fail3:	usb_free_urb(hanwang->irq);
+ fail2:	usb_free_coherent(dev, hanwang->features->pkg_len,
+			hanwang->data, hanwang->data_dma);
+ fail1:	input_free_device(input_dev);
+	kfree(hanwang);
+	return error;
+
+}
+
+static void hanwang_disconnect(struct usb_interface *intf)
+{
+	struct hanwang *hanwang = usb_get_intfdata(intf);
+
+	input_unregister_device(hanwang->dev);
+	usb_free_urb(hanwang->irq);
+	usb_free_coherent(interface_to_usbdev(intf),
+			hanwang->features->pkg_len, hanwang->data,
+			hanwang->data_dma);
+	kfree(hanwang);
+	usb_set_intfdata(intf, NULL);
+}
+
+static const struct usb_device_id hanwang_ids[] = {
+	{ HANWANG_TABLET_DEVICE(USB_VENDOR_ID_HANWANG, HANWANG_TABLET_INT_CLASS,
+		HANWANG_TABLET_INT_SUB_CLASS, HANWANG_TABLET_INT_PROTOCOL) },
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, hanwang_ids);
+
+static struct usb_driver hanwang_driver = {
+	.name		= "hanwang",
+	.probe		= hanwang_probe,
+	.disconnect	= hanwang_disconnect,
+	.id_table	= hanwang_ids,
+};
+
+module_usb_driver(hanwang_driver);
diff --git a/drivers/input/tablet/kbtab.c b/drivers/input/tablet/kbtab.c
new file mode 100644
index 0000000..2812f92
--- /dev/null
+++ b/drivers/input/tablet/kbtab.c
@@ -0,0 +1,210 @@
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+
+/*
+ * Version Information
+ * v0.0.1 - Original, extremely basic version, 2.4.xx only
+ * v0.0.2 - Updated, works with 2.5.62 and 2.4.20;
+ *           - added pressure-threshold modules param code from
+ *              Alex Perry <alex.perry@ieee.org>
+ */
+
+#define DRIVER_VERSION "v0.0.2"
+#define DRIVER_AUTHOR "Josh Myer <josh@joshisanerd.com>"
+#define DRIVER_DESC "USB KB Gear JamStudio Tablet driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_KBGEAR	0x084e
+
+static int kb_pressure_click = 0x10;
+module_param(kb_pressure_click, int, 0);
+MODULE_PARM_DESC(kb_pressure_click, "pressure threshold for clicks");
+
+struct kbtab {
+	unsigned char *data;
+	dma_addr_t data_dma;
+	struct input_dev *dev;
+	struct usb_device *usbdev;
+	struct usb_interface *intf;
+	struct urb *irq;
+	char phys[32];
+};
+
+static void kbtab_irq(struct urb *urb)
+{
+	struct kbtab *kbtab = urb->context;
+	unsigned char *data = kbtab->data;
+	struct input_dev *dev = kbtab->dev;
+	int pressure;
+	int retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&kbtab->intf->dev,
+			"%s - urb shutting down with status: %d\n",
+			__func__, urb->status);
+		return;
+	default:
+		dev_dbg(&kbtab->intf->dev,
+			"%s - nonzero urb status received: %d\n",
+			__func__, urb->status);
+		goto exit;
+	}
+
+
+	input_report_key(dev, BTN_TOOL_PEN, 1);
+
+	input_report_abs(dev, ABS_X, get_unaligned_le16(&data[1]));
+	input_report_abs(dev, ABS_Y, get_unaligned_le16(&data[3]));
+
+	/*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/
+	input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
+
+	pressure = data[5];
+	if (kb_pressure_click == -1)
+		input_report_abs(dev, ABS_PRESSURE, pressure);
+	else
+		input_report_key(dev, BTN_LEFT, pressure > kb_pressure_click ? 1 : 0);
+
+	input_sync(dev);
+
+ exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&kbtab->intf->dev,
+			"%s - usb_submit_urb failed with result %d\n",
+			__func__, retval);
+}
+
+static struct usb_device_id kbtab_ids[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_KBGEAR, 0x1001), .driver_info = 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, kbtab_ids);
+
+static int kbtab_open(struct input_dev *dev)
+{
+	struct kbtab *kbtab = input_get_drvdata(dev);
+
+	kbtab->irq->dev = kbtab->usbdev;
+	if (usb_submit_urb(kbtab->irq, GFP_KERNEL))
+		return -EIO;
+
+	return 0;
+}
+
+static void kbtab_close(struct input_dev *dev)
+{
+	struct kbtab *kbtab = input_get_drvdata(dev);
+
+	usb_kill_urb(kbtab->irq);
+}
+
+static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_endpoint_descriptor *endpoint;
+	struct kbtab *kbtab;
+	struct input_dev *input_dev;
+	int error = -ENOMEM;
+
+	if (intf->cur_altsetting->desc.bNumEndpoints < 1)
+		return -ENODEV;
+
+	kbtab = kzalloc(sizeof(struct kbtab), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!kbtab || !input_dev)
+		goto fail1;
+
+	kbtab->data = usb_alloc_coherent(dev, 8, GFP_KERNEL, &kbtab->data_dma);
+	if (!kbtab->data)
+		goto fail1;
+
+	kbtab->irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!kbtab->irq)
+		goto fail2;
+
+	kbtab->usbdev = dev;
+	kbtab->intf = intf;
+	kbtab->dev = input_dev;
+
+	usb_make_path(dev, kbtab->phys, sizeof(kbtab->phys));
+	strlcat(kbtab->phys, "/input0", sizeof(kbtab->phys));
+
+	input_dev->name = "KB Gear Tablet";
+	input_dev->phys = kbtab->phys;
+	usb_to_input_id(dev, &input_dev->id);
+	input_dev->dev.parent = &intf->dev;
+
+	input_set_drvdata(input_dev, kbtab);
+
+	input_dev->open = kbtab_open;
+	input_dev->close = kbtab_close;
+
+	input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_LEFT)] |=
+		BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
+	input_dev->keybit[BIT_WORD(BTN_DIGI)] |=
+		BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0);
+
+	endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+	usb_fill_int_urb(kbtab->irq, dev,
+			 usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+			 kbtab->data, 8,
+			 kbtab_irq, kbtab, endpoint->bInterval);
+	kbtab->irq->transfer_dma = kbtab->data_dma;
+	kbtab->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	error = input_register_device(kbtab->dev);
+	if (error)
+		goto fail3;
+
+	usb_set_intfdata(intf, kbtab);
+
+	return 0;
+
+ fail3:	usb_free_urb(kbtab->irq);
+ fail2:	usb_free_coherent(dev, 8, kbtab->data, kbtab->data_dma);
+ fail1:	input_free_device(input_dev);
+	kfree(kbtab);
+	return error;
+}
+
+static void kbtab_disconnect(struct usb_interface *intf)
+{
+	struct kbtab *kbtab = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+
+	input_unregister_device(kbtab->dev);
+	usb_free_urb(kbtab->irq);
+	usb_free_coherent(kbtab->usbdev, 8, kbtab->data, kbtab->data_dma);
+	kfree(kbtab);
+}
+
+static struct usb_driver kbtab_driver = {
+	.name =		"kbtab",
+	.probe =	kbtab_probe,
+	.disconnect =	kbtab_disconnect,
+	.id_table =	kbtab_ids,
+};
+
+module_usb_driver(kbtab_driver);
diff --git a/drivers/input/tablet/wacom_serial4.c b/drivers/input/tablet/wacom_serial4.c
new file mode 100644
index 0000000..20ab802
--- /dev/null
+++ b/drivers/input/tablet/wacom_serial4.c
@@ -0,0 +1,620 @@
+/*
+ * Wacom protocol 4 serial tablet driver
+ *
+ * Copyright 2014      Hans de Goede <hdegoede@redhat.com>
+ * Copyright 2011-2012 Julian Squires <julian@cipht.net>
+ *
+ * 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 of 2 of the License, or (at your
+ * option) any later version. See the file COPYING in the main directory of
+ * this archive for more details.
+ *
+ * Many thanks to Bill Seremetis, without whom PenPartner support
+ * would not have been possible. Thanks to Patrick Mahoney.
+ *
+ * This driver was developed with reference to much code written by others,
+ * particularly:
+ *  - elo, gunze drivers by Vojtech Pavlik <vojtech@ucw.cz>;
+ *  - wacom_w8001 driver by Jaya Kumar <jayakumar.lkml@gmail.com>;
+ *  - the USB wacom input driver, credited to many people
+ *    (see drivers/input/tablet/wacom.h);
+ *  - new and old versions of linuxwacom / xf86-input-wacom credited to
+ *    Frederic Lepied, France. <Lepied@XFree86.org> and
+ *    Ping Cheng, Wacom. <pingc@wacom.com>;
+ *  - and xf86wacom.c (a presumably ancient version of the linuxwacom code),
+ *    by Frederic Lepied and Raph Levien <raph@gtk.org>.
+ *
+ * To do:
+ *  - support pad buttons; (requires access to a model with pad buttons)
+ *  - support (protocol 4-style) tilt (requires access to a > 1.4 rom model)
+ */
+
+/*
+ * Wacom serial protocol 4 documentation taken from linuxwacom-0.9.9 code,
+ * protocol 4 uses 7 or 9 byte of data in the following format:
+ *
+ *	Byte 1
+ *	bit 7  Sync bit always 1
+ *	bit 6  Pointing device detected
+ *	bit 5  Cursor = 0 / Stylus = 1
+ *	bit 4  Reserved
+ *	bit 3  1 if a button on the pointing device has been pressed
+ *	bit 2  P0 (optional)
+ *	bit 1  X15
+ *	bit 0  X14
+ *
+ *	Byte 2
+ *	bit 7  Always 0
+ *	bits 6-0 = X13 - X7
+ *
+ *	Byte 3
+ *	bit 7  Always 0
+ *	bits 6-0 = X6 - X0
+ *
+ *	Byte 4
+ *	bit 7  Always 0
+ *	bit 6  B3
+ *	bit 5  B2
+ *	bit 4  B1
+ *	bit 3  B0
+ *	bit 2  P1 (optional)
+ *	bit 1  Y15
+ *	bit 0  Y14
+ *
+ *	Byte 5
+ *	bit 7  Always 0
+ *	bits 6-0 = Y13 - Y7
+ *
+ *	Byte 6
+ *	bit 7  Always 0
+ *	bits 6-0 = Y6 - Y0
+ *
+ *	Byte 7
+ *	bit 7 Always 0
+ *	bit 6  Sign of pressure data; or wheel-rel for cursor tool
+ *	bit 5  P7; or REL1 for cursor tool
+ *	bit 4  P6; or REL0 for cursor tool
+ *	bit 3  P5
+ *	bit 2  P4
+ *	bit 1  P3
+ *	bit 0  P2
+ *
+ *	byte 8 and 9 are optional and present only
+ *	in tilt mode.
+ *
+ *	Byte 8
+ *	bit 7 Always 0
+ *	bit 6 Sign of tilt X
+ *	bit 5  Xt6
+ *	bit 4  Xt5
+ *	bit 3  Xt4
+ *	bit 2  Xt3
+ *	bit 1  Xt2
+ *	bit 0  Xt1
+ *
+ *	Byte 9
+ *	bit 7 Always 0
+ *	bit 6 Sign of tilt Y
+ *	bit 5  Yt6
+ *	bit 4  Yt5
+ *	bit 3  Yt4
+ *	bit 2  Yt3
+ *	bit 1  Yt2
+ *	bit 0  Yt1
+ */
+
+#include <linux/completion.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+MODULE_AUTHOR("Julian Squires <julian@cipht.net>, Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Wacom protocol 4 serial tablet driver");
+MODULE_LICENSE("GPL");
+
+#define REQUEST_MODEL_AND_ROM_VERSION	"~#"
+#define REQUEST_MAX_COORDINATES		"~C\r"
+#define REQUEST_CONFIGURATION_STRING	"~R\r"
+#define REQUEST_RESET_TO_PROTOCOL_IV	"\r#"
+/*
+ * Note: sending "\r$\r" causes at least the Digitizer II to send
+ * packets in ASCII instead of binary.  "\r#" seems to undo that.
+ */
+
+#define COMMAND_START_SENDING_PACKETS		"ST\r"
+#define COMMAND_STOP_SENDING_PACKETS		"SP\r"
+#define COMMAND_MULTI_MODE_INPUT		"MU1\r"
+#define COMMAND_ORIGIN_IN_UPPER_LEFT		"OC1\r"
+#define COMMAND_ENABLE_ALL_MACRO_BUTTONS	"~M0\r"
+#define COMMAND_DISABLE_GROUP_1_MACRO_BUTTONS	"~M1\r"
+#define COMMAND_TRANSMIT_AT_MAX_RATE		"IT0\r"
+#define COMMAND_DISABLE_INCREMENTAL_MODE	"IN0\r"
+#define COMMAND_ENABLE_CONTINUOUS_MODE		"SR\r"
+#define COMMAND_ENABLE_PRESSURE_MODE		"PH1\r"
+#define COMMAND_Z_FILTER			"ZF1\r"
+
+/* Note that this is a protocol 4 packet without tilt information. */
+#define PACKET_LENGTH		7
+#define DATA_SIZE		32
+
+/* flags */
+#define F_COVERS_SCREEN		0x01
+#define F_HAS_STYLUS2		0x02
+#define F_HAS_SCROLLWHEEL	0x04
+
+/* device IDs */
+#define STYLUS_DEVICE_ID	0x02
+#define CURSOR_DEVICE_ID	0x06
+#define ERASER_DEVICE_ID	0x0A
+
+enum { STYLUS = 1, ERASER, CURSOR };
+
+static const struct {
+	int device_id;
+	int input_id;
+} tools[] = {
+	{ 0, 0 },
+	{ STYLUS_DEVICE_ID, BTN_TOOL_PEN },
+	{ ERASER_DEVICE_ID, BTN_TOOL_RUBBER },
+	{ CURSOR_DEVICE_ID, BTN_TOOL_MOUSE },
+};
+
+struct wacom {
+	struct input_dev *dev;
+	struct completion cmd_done;
+	int result;
+	u8 expect;
+	u8 eraser_mask;
+	unsigned int extra_z_bits;
+	unsigned int flags;
+	unsigned int res_x, res_y;
+	unsigned int max_x, max_y;
+	unsigned int tool;
+	unsigned int idx;
+	u8 data[DATA_SIZE];
+	char phys[32];
+};
+
+enum {
+	MODEL_CINTIQ		= 0x504C, /* PL */
+	MODEL_CINTIQ2		= 0x4454, /* DT */
+	MODEL_DIGITIZER_II	= 0x5544, /* UD */
+	MODEL_GRAPHIRE		= 0x4554, /* ET */
+	MODEL_PENPARTNER	= 0x4354, /* CT */
+};
+
+static void wacom_handle_model_response(struct wacom *wacom)
+{
+	int major_v, minor_v, r = 0;
+	char *p;
+
+	p = strrchr(wacom->data, 'V');
+	if (p)
+		r = sscanf(p + 1, "%u.%u", &major_v, &minor_v);
+	if (r != 2)
+		major_v = minor_v = 0;
+
+	switch (wacom->data[2] << 8 | wacom->data[3]) {
+	case MODEL_CINTIQ:	/* UNTESTED */
+	case MODEL_CINTIQ2:
+		if ((wacom->data[2] << 8 | wacom->data[3]) == MODEL_CINTIQ) {
+			wacom->dev->name = "Wacom Cintiq";
+			wacom->dev->id.version = MODEL_CINTIQ;
+		} else {
+			wacom->dev->name = "Wacom Cintiq II";
+			wacom->dev->id.version = MODEL_CINTIQ2;
+		}
+		wacom->res_x = 508;
+		wacom->res_y = 508;
+
+		switch (wacom->data[5] << 8 | wacom->data[6]) {
+		case 0x3731: /* PL-710 */
+			wacom->res_x = 2540;
+			wacom->res_y = 2540;
+			/* fall through */
+		case 0x3535: /* PL-550 */
+		case 0x3830: /* PL-800 */
+			wacom->extra_z_bits = 2;
+		}
+
+		wacom->flags = F_COVERS_SCREEN;
+		break;
+
+	case MODEL_PENPARTNER:
+		wacom->dev->name = "Wacom Penpartner";
+		wacom->dev->id.version = MODEL_PENPARTNER;
+		wacom->res_x = 1000;
+		wacom->res_y = 1000;
+		break;
+
+	case MODEL_GRAPHIRE:
+		wacom->dev->name = "Wacom Graphire";
+		wacom->dev->id.version = MODEL_GRAPHIRE;
+		wacom->res_x = 1016;
+		wacom->res_y = 1016;
+		wacom->max_x = 5103;
+		wacom->max_y = 3711;
+		wacom->extra_z_bits = 2;
+		wacom->eraser_mask = 0x08;
+		wacom->flags = F_HAS_STYLUS2 | F_HAS_SCROLLWHEEL;
+		break;
+
+	case MODEL_DIGITIZER_II:
+		wacom->dev->name = "Wacom Digitizer II";
+		wacom->dev->id.version = MODEL_DIGITIZER_II;
+		if (major_v == 1 && minor_v <= 2)
+			wacom->extra_z_bits = 0; /* UNTESTED */
+		break;
+
+	default:
+		dev_err(&wacom->dev->dev, "Unsupported Wacom model %s\n",
+			wacom->data);
+		wacom->result = -ENODEV;
+		return;
+	}
+
+	dev_info(&wacom->dev->dev, "%s tablet, version %u.%u\n",
+		 wacom->dev->name, major_v, minor_v);
+}
+
+static void wacom_handle_configuration_response(struct wacom *wacom)
+{
+	int r, skip;
+
+	dev_dbg(&wacom->dev->dev, "Configuration string: %s\n", wacom->data);
+	r = sscanf(wacom->data, "~R%x,%u,%u,%u,%u", &skip, &skip, &skip,
+		   &wacom->res_x, &wacom->res_y);
+	if (r != 5)
+		dev_warn(&wacom->dev->dev, "could not get resolution\n");
+}
+
+static void wacom_handle_coordinates_response(struct wacom *wacom)
+{
+	int r;
+
+	dev_dbg(&wacom->dev->dev, "Coordinates string: %s\n", wacom->data);
+	r = sscanf(wacom->data, "~C%u,%u", &wacom->max_x, &wacom->max_y);
+	if (r != 2)
+		dev_warn(&wacom->dev->dev, "could not get max coordinates\n");
+}
+
+static void wacom_handle_response(struct wacom *wacom)
+{
+	if (wacom->data[0] != '~' || wacom->data[1] != wacom->expect) {
+		dev_err(&wacom->dev->dev,
+			"Wacom got an unexpected response: %s\n", wacom->data);
+		wacom->result = -EIO;
+	} else {
+		wacom->result = 0;
+
+		switch (wacom->data[1]) {
+		case '#':
+			wacom_handle_model_response(wacom);
+			break;
+		case 'R':
+			wacom_handle_configuration_response(wacom);
+			break;
+		case 'C':
+			wacom_handle_coordinates_response(wacom);
+			break;
+		}
+	}
+
+	complete(&wacom->cmd_done);
+}
+
+static void wacom_handle_packet(struct wacom *wacom)
+{
+	u8 in_proximity_p, stylus_p, button;
+	unsigned int tool;
+	int x, y, z;
+
+	in_proximity_p = wacom->data[0] & 0x40;
+	stylus_p = wacom->data[0] & 0x20;
+	button = (wacom->data[3] & 0x78) >> 3;
+	x = (wacom->data[0] & 3) << 14 | wacom->data[1]<<7 | wacom->data[2];
+	y = (wacom->data[3] & 3) << 14 | wacom->data[4]<<7 | wacom->data[5];
+
+	if (in_proximity_p && stylus_p) {
+		z = wacom->data[6] & 0x7f;
+		if (wacom->extra_z_bits >= 1)
+			z = z << 1 | (wacom->data[3] & 0x4) >> 2;
+		if (wacom->extra_z_bits > 1)
+			z = z << 1 | (wacom->data[0] & 0x4) >> 2;
+		z = z ^ (0x40 << wacom->extra_z_bits);
+	} else {
+		z = -1;
+	}
+
+	if (stylus_p)
+		tool = (button & wacom->eraser_mask) ? ERASER : STYLUS;
+	else
+		tool = CURSOR;
+
+	if (tool != wacom->tool && wacom->tool != 0) {
+		input_report_key(wacom->dev, tools[wacom->tool].input_id, 0);
+		input_sync(wacom->dev);
+	}
+	wacom->tool = tool;
+
+	input_report_key(wacom->dev, tools[tool].input_id, in_proximity_p);
+	input_report_abs(wacom->dev, ABS_MISC,
+			 in_proximity_p ? tools[tool].device_id : 0);
+	input_report_abs(wacom->dev, ABS_X, x);
+	input_report_abs(wacom->dev, ABS_Y, y);
+	input_report_abs(wacom->dev, ABS_PRESSURE, z);
+	if (stylus_p) {
+		input_report_key(wacom->dev, BTN_TOUCH, button & 1);
+		input_report_key(wacom->dev, BTN_STYLUS, button & 2);
+		input_report_key(wacom->dev, BTN_STYLUS2, button & 4);
+	} else {
+		input_report_key(wacom->dev, BTN_LEFT, button & 1);
+		input_report_key(wacom->dev, BTN_RIGHT, button & 2);
+		input_report_key(wacom->dev, BTN_MIDDLE, button & 4);
+		/* handle relative wheel for non-stylus device */
+		z = (wacom->data[6] & 0x30) >> 4;
+		if (wacom->data[6] & 0x40)
+			z = -z;
+		input_report_rel(wacom->dev, REL_WHEEL, z);
+	}
+	input_sync(wacom->dev);
+}
+
+static void wacom_clear_data_buf(struct wacom *wacom)
+{
+	memset(wacom->data, 0, DATA_SIZE);
+	wacom->idx = 0;
+}
+
+static irqreturn_t wacom_interrupt(struct serio *serio, unsigned char data,
+				   unsigned int flags)
+{
+	struct wacom *wacom = serio_get_drvdata(serio);
+
+	if (data & 0x80)
+		wacom->idx = 0;
+
+	/*
+	 * We're either expecting a carriage return-terminated ASCII
+	 * response string, or a seven-byte packet with the MSB set on
+	 * the first byte.
+	 *
+	 * Note however that some tablets (the PenPartner, for
+	 * example) don't send a carriage return at the end of a
+	 * command.  We handle these by waiting for timeout.
+	 */
+	if (data == '\r' && !(wacom->data[0] & 0x80)) {
+		wacom_handle_response(wacom);
+		wacom_clear_data_buf(wacom);
+		return IRQ_HANDLED;
+	}
+
+	/* Leave place for 0 termination */
+	if (wacom->idx > (DATA_SIZE - 2)) {
+		dev_dbg(&wacom->dev->dev,
+			"throwing away %d bytes of garbage\n", wacom->idx);
+		wacom_clear_data_buf(wacom);
+	}
+	wacom->data[wacom->idx++] = data;
+
+	if (wacom->idx == PACKET_LENGTH && (wacom->data[0] & 0x80)) {
+		wacom_handle_packet(wacom);
+		wacom_clear_data_buf(wacom);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void wacom_disconnect(struct serio *serio)
+{
+	struct wacom *wacom = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_unregister_device(wacom->dev);
+	kfree(wacom);
+}
+
+static int wacom_send(struct serio *serio, const u8 *command)
+{
+	int err = 0;
+
+	for (; !err && *command; command++)
+		err = serio_write(serio, *command);
+
+	return err;
+}
+
+static int wacom_send_setup_string(struct wacom *wacom, struct serio *serio)
+{
+	const u8 *cmd;
+
+	switch (wacom->dev->id.version) {
+	case MODEL_CINTIQ:	/* UNTESTED */
+		cmd = COMMAND_ORIGIN_IN_UPPER_LEFT
+			COMMAND_TRANSMIT_AT_MAX_RATE
+			COMMAND_ENABLE_CONTINUOUS_MODE
+			COMMAND_START_SENDING_PACKETS;
+		break;
+
+	case MODEL_PENPARTNER:
+		cmd = COMMAND_ENABLE_PRESSURE_MODE
+			COMMAND_START_SENDING_PACKETS;
+		break;
+
+	default:
+		cmd = COMMAND_MULTI_MODE_INPUT
+			COMMAND_ORIGIN_IN_UPPER_LEFT
+			COMMAND_ENABLE_ALL_MACRO_BUTTONS
+			COMMAND_DISABLE_GROUP_1_MACRO_BUTTONS
+			COMMAND_TRANSMIT_AT_MAX_RATE
+			COMMAND_DISABLE_INCREMENTAL_MODE
+			COMMAND_ENABLE_CONTINUOUS_MODE
+			COMMAND_Z_FILTER
+			COMMAND_START_SENDING_PACKETS;
+		break;
+	}
+
+	return wacom_send(serio, cmd);
+}
+
+static int wacom_send_and_wait(struct wacom *wacom, struct serio *serio,
+			       const u8 *cmd, const char *desc)
+{
+	int err;
+	unsigned long u;
+
+	wacom->expect = cmd[1];
+	init_completion(&wacom->cmd_done);
+
+	err = wacom_send(serio, cmd);
+	if (err)
+		return err;
+
+	u = wait_for_completion_timeout(&wacom->cmd_done, HZ);
+	if (u == 0) {
+		/* Timeout, process what we've received. */
+		wacom_handle_response(wacom);
+	}
+
+	wacom->expect = 0;
+	return wacom->result;
+}
+
+static int wacom_setup(struct wacom *wacom, struct serio *serio)
+{
+	int err;
+
+	/* Note that setting the link speed is the job of inputattach.
+	 * We assume that reset negotiation has already happened,
+	 * here. */
+	err = wacom_send_and_wait(wacom, serio, REQUEST_MODEL_AND_ROM_VERSION,
+				  "model and version");
+	if (err)
+		return err;
+
+	if (!(wacom->res_x && wacom->res_y)) {
+		err = wacom_send_and_wait(wacom, serio,
+					  REQUEST_CONFIGURATION_STRING,
+					  "configuration string");
+		if (err)
+			return err;
+	}
+
+	if (!(wacom->max_x && wacom->max_y)) {
+		err = wacom_send_and_wait(wacom, serio,
+					  REQUEST_MAX_COORDINATES,
+					  "coordinates string");
+		if (err)
+			return err;
+	}
+
+	return wacom_send_setup_string(wacom, serio);
+}
+
+static int wacom_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct wacom *wacom;
+	struct input_dev *input_dev;
+	int err = -ENOMEM;
+
+	wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!wacom || !input_dev)
+		goto free_device;
+
+	wacom->dev = input_dev;
+	wacom->extra_z_bits = 1;
+	wacom->eraser_mask = 0x04;
+	wacom->tool = wacom->idx = 0;
+	snprintf(wacom->phys, sizeof(wacom->phys), "%s/input0", serio->phys);
+	input_dev->phys = wacom->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor  = SERIO_WACOM_IV;
+	input_dev->id.product = serio->id.extra;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] =
+		BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | BIT_MASK(EV_REL);
+	set_bit(ABS_MISC, input_dev->absbit);
+	set_bit(BTN_TOOL_PEN, input_dev->keybit);
+	set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+	set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
+	set_bit(BTN_TOUCH, input_dev->keybit);
+	set_bit(BTN_STYLUS, input_dev->keybit);
+	set_bit(BTN_LEFT, input_dev->keybit);
+	set_bit(BTN_RIGHT, input_dev->keybit);
+	set_bit(BTN_MIDDLE, input_dev->keybit);
+
+	serio_set_drvdata(serio, wacom);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto free_device;
+
+	err = wacom_setup(wacom, serio);
+	if (err)
+		goto close_serio;
+
+	set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+	if (!(wacom->flags & F_COVERS_SCREEN))
+		__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+	if (wacom->flags & F_HAS_STYLUS2)
+		__set_bit(BTN_STYLUS2, input_dev->keybit);
+
+	if (wacom->flags & F_HAS_SCROLLWHEEL)
+		__set_bit(REL_WHEEL, input_dev->relbit);
+
+	input_abs_set_res(wacom->dev, ABS_X, wacom->res_x);
+	input_abs_set_res(wacom->dev, ABS_Y, wacom->res_y);
+	input_set_abs_params(wacom->dev, ABS_X, 0, wacom->max_x, 0, 0);
+	input_set_abs_params(wacom->dev, ABS_Y, 0, wacom->max_y, 0, 0);
+	input_set_abs_params(wacom->dev, ABS_PRESSURE, -1,
+			     (1 << (7 + wacom->extra_z_bits)) - 1, 0, 0);
+
+	err = input_register_device(wacom->dev);
+	if (err)
+		goto close_serio;
+
+	return 0;
+
+close_serio:
+	serio_close(serio);
+free_device:
+	serio_set_drvdata(serio, NULL);
+	input_free_device(input_dev);
+	kfree(wacom);
+	return err;
+}
+
+static struct serio_device_id wacom_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_WACOM_IV,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, wacom_serio_ids);
+
+static struct serio_driver wacom_drv = {
+	.driver		= {
+		.name	= "wacom_serial4",
+	},
+	.description	= "Wacom protocol 4 serial tablet driver",
+	.id_table	= wacom_serio_ids,
+	.interrupt	= wacom_interrupt,
+	.connect	= wacom_connect,
+	.disconnect	= wacom_disconnect,
+};
+
+module_serio_driver(wacom_drv);
diff --git a/drivers/input/touchscreen/88pm860x-ts.c b/drivers/input/touchscreen/88pm860x-ts.c
new file mode 100644
index 0000000..251ff2a
--- /dev/null
+++ b/drivers/input/touchscreen/88pm860x-ts.c
@@ -0,0 +1,304 @@
+/*
+ * Touchscreen driver for Marvell 88PM860x
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ * 	Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/mfd/88pm860x.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#define MEAS_LEN		(8)
+#define ACCURATE_BIT		(12)
+
+/* touch register */
+#define MEAS_EN3		(0x52)
+
+#define MEAS_TSIX_1		(0x8D)
+#define MEAS_TSIX_2		(0x8E)
+#define MEAS_TSIY_1		(0x8F)
+#define MEAS_TSIY_2		(0x90)
+#define MEAS_TSIZ1_1		(0x91)
+#define MEAS_TSIZ1_2		(0x92)
+#define MEAS_TSIZ2_1		(0x93)
+#define MEAS_TSIZ2_2		(0x94)
+
+/* bit definitions of touch */
+#define MEAS_PD_EN		(1 << 3)
+#define MEAS_TSIX_EN		(1 << 4)
+#define MEAS_TSIY_EN		(1 << 5)
+#define MEAS_TSIZ1_EN		(1 << 6)
+#define MEAS_TSIZ2_EN		(1 << 7)
+
+struct pm860x_touch {
+	struct input_dev *idev;
+	struct i2c_client *i2c;
+	struct pm860x_chip *chip;
+	int irq;
+	int res_x;		/* resistor of Xplate */
+};
+
+static irqreturn_t pm860x_touch_handler(int irq, void *data)
+{
+	struct pm860x_touch *touch = data;
+	struct pm860x_chip *chip = touch->chip;
+	unsigned char buf[MEAS_LEN];
+	int x, y, pen_down;
+	int z1, z2, rt = 0;
+	int ret;
+
+	ret = pm860x_bulk_read(touch->i2c, MEAS_TSIX_1, MEAS_LEN, buf);
+	if (ret < 0)
+		goto out;
+
+	pen_down = buf[1] & (1 << 6);
+	x = ((buf[0] & 0xFF) << 4) | (buf[1] & 0x0F);
+	y = ((buf[2] & 0xFF) << 4) | (buf[3] & 0x0F);
+	z1 = ((buf[4] & 0xFF) << 4) | (buf[5] & 0x0F);
+	z2 = ((buf[6] & 0xFF) << 4) | (buf[7] & 0x0F);
+
+	if (pen_down) {
+		if ((x != 0) && (z1 != 0) && (touch->res_x != 0)) {
+			rt = z2 / z1 - 1;
+			rt = (rt * touch->res_x * x) >> ACCURATE_BIT;
+			dev_dbg(chip->dev, "z1:%d, z2:%d, rt:%d\n",
+				z1, z2, rt);
+		}
+		input_report_abs(touch->idev, ABS_X, x);
+		input_report_abs(touch->idev, ABS_Y, y);
+		input_report_abs(touch->idev, ABS_PRESSURE, rt);
+		input_report_key(touch->idev, BTN_TOUCH, 1);
+		dev_dbg(chip->dev, "pen down at [%d, %d].\n", x, y);
+	} else {
+		input_report_abs(touch->idev, ABS_PRESSURE, 0);
+		input_report_key(touch->idev, BTN_TOUCH, 0);
+		dev_dbg(chip->dev, "pen release\n");
+	}
+	input_sync(touch->idev);
+
+out:
+	return IRQ_HANDLED;
+}
+
+static int pm860x_touch_open(struct input_dev *dev)
+{
+	struct pm860x_touch *touch = input_get_drvdata(dev);
+	int data, ret;
+
+	data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN
+		| MEAS_TSIZ1_EN | MEAS_TSIZ2_EN;
+	ret = pm860x_set_bits(touch->i2c, MEAS_EN3, data, data);
+	if (ret < 0)
+		goto out;
+	return 0;
+out:
+	return ret;
+}
+
+static void pm860x_touch_close(struct input_dev *dev)
+{
+	struct pm860x_touch *touch = input_get_drvdata(dev);
+	int data;
+
+	data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN
+		| MEAS_TSIZ1_EN | MEAS_TSIZ2_EN;
+	pm860x_set_bits(touch->i2c, MEAS_EN3, data, 0);
+}
+
+#ifdef CONFIG_OF
+static int pm860x_touch_dt_init(struct platform_device *pdev,
+					  struct pm860x_chip *chip,
+					  int *res_x)
+{
+	struct device_node *np = pdev->dev.parent->of_node;
+	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
+				 : chip->companion;
+	int data, n, ret;
+	if (!np)
+		return -ENODEV;
+	np = of_find_node_by_name(np, "touch");
+	if (!np) {
+		dev_err(&pdev->dev, "Can't find touch node\n");
+		return -EINVAL;
+	}
+	/* set GPADC MISC1 register */
+	data = 0;
+	if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-prebias", &n))
+		data |= (n << 1) & PM8607_GPADC_PREBIAS_MASK;
+	if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-slot-cycle", &n))
+		data |= (n << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
+	if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-off-scale", &n))
+		data |= (n << 5) & PM8607_GPADC_OFF_SCALE_MASK;
+	if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-sw-cal", &n))
+		data |= (n << 7) & PM8607_GPADC_SW_CAL_MASK;
+	if (data) {
+		ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
+		if (ret < 0)
+			return -EINVAL;
+	}
+	/* set tsi prebias time */
+	if (!of_property_read_u32(np, "marvell,88pm860x-tsi-prebias", &data)) {
+		ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
+		if (ret < 0)
+			return -EINVAL;
+	}
+	/* set prebias & prechg time of pen detect */
+	data = 0;
+	if (!of_property_read_u32(np, "marvell,88pm860x-pen-prebias", &n))
+		data |= n & PM8607_PD_PREBIAS_MASK;
+	if (!of_property_read_u32(np, "marvell,88pm860x-pen-prechg", &n))
+		data |= n & PM8607_PD_PRECHG_MASK;
+	if (data) {
+		ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
+		if (ret < 0)
+			return -EINVAL;
+	}
+	of_property_read_u32(np, "marvell,88pm860x-resistor-X", res_x);
+	return 0;
+}
+#else
+#define pm860x_touch_dt_init(x, y, z)	(-1)
+#endif
+
+static int pm860x_touch_probe(struct platform_device *pdev)
+{
+	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+	struct pm860x_touch_pdata *pdata = dev_get_platdata(&pdev->dev);
+	struct pm860x_touch *touch;
+	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
+				 : chip->companion;
+	int irq, ret, res_x = 0, data = 0;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "No IRQ resource!\n");
+		return -EINVAL;
+	}
+
+	if (pm860x_touch_dt_init(pdev, chip, &res_x)) {
+		if (pdata) {
+			/* set GPADC MISC1 register */
+			data = 0;
+			data |= (pdata->gpadc_prebias << 1)
+				& PM8607_GPADC_PREBIAS_MASK;
+			data |= (pdata->slot_cycle << 3)
+				& PM8607_GPADC_SLOT_CYCLE_MASK;
+			data |= (pdata->off_scale << 5)
+				& PM8607_GPADC_OFF_SCALE_MASK;
+			data |= (pdata->sw_cal << 7)
+				& PM8607_GPADC_SW_CAL_MASK;
+			if (data) {
+				ret = pm860x_reg_write(i2c,
+					PM8607_GPADC_MISC1, data);
+				if (ret < 0)
+					return -EINVAL;
+			}
+			/* set tsi prebias time */
+			if (pdata->tsi_prebias) {
+				data = pdata->tsi_prebias;
+				ret = pm860x_reg_write(i2c,
+					PM8607_TSI_PREBIAS, data);
+				if (ret < 0)
+					return -EINVAL;
+			}
+			/* set prebias & prechg time of pen detect */
+			data = 0;
+			data |= pdata->pen_prebias
+				& PM8607_PD_PREBIAS_MASK;
+			data |= (pdata->pen_prechg << 5)
+				& PM8607_PD_PRECHG_MASK;
+			if (data) {
+				ret = pm860x_reg_write(i2c,
+					PM8607_PD_PREBIAS, data);
+				if (ret < 0)
+					return -EINVAL;
+			}
+			res_x = pdata->res_x;
+		} else {
+			dev_err(&pdev->dev, "failed to get platform data\n");
+			return -EINVAL;
+		}
+	}
+	/* enable GPADC */
+	ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1, PM8607_GPADC_EN,
+			      PM8607_GPADC_EN);
+	if (ret)
+		return ret;
+
+	touch = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_touch),
+			     GFP_KERNEL);
+	if (!touch)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, touch);
+
+	touch->idev = devm_input_allocate_device(&pdev->dev);
+	if (!touch->idev) {
+		dev_err(&pdev->dev, "Failed to allocate input device!\n");
+		return -ENOMEM;
+	}
+
+	touch->idev->name = "88pm860x-touch";
+	touch->idev->phys = "88pm860x/input0";
+	touch->idev->id.bustype = BUS_I2C;
+	touch->idev->dev.parent = &pdev->dev;
+	touch->idev->open = pm860x_touch_open;
+	touch->idev->close = pm860x_touch_close;
+	touch->chip = chip;
+	touch->i2c = i2c;
+	touch->irq = irq;
+	touch->res_x = res_x;
+	input_set_drvdata(touch->idev, touch);
+
+	ret = devm_request_threaded_irq(&pdev->dev, touch->irq, NULL,
+					pm860x_touch_handler, IRQF_ONESHOT,
+					"touch", touch);
+	if (ret < 0)
+		return ret;
+
+	__set_bit(EV_ABS, touch->idev->evbit);
+	__set_bit(ABS_X, touch->idev->absbit);
+	__set_bit(ABS_Y, touch->idev->absbit);
+	__set_bit(ABS_PRESSURE, touch->idev->absbit);
+	__set_bit(EV_SYN, touch->idev->evbit);
+	__set_bit(EV_KEY, touch->idev->evbit);
+	__set_bit(BTN_TOUCH, touch->idev->keybit);
+
+	input_set_abs_params(touch->idev, ABS_X, 0, 1 << ACCURATE_BIT, 0, 0);
+	input_set_abs_params(touch->idev, ABS_Y, 0, 1 << ACCURATE_BIT, 0, 0);
+	input_set_abs_params(touch->idev, ABS_PRESSURE, 0, 1 << ACCURATE_BIT,
+				0, 0);
+
+	ret = input_register_device(touch->idev);
+	if (ret < 0) {
+		dev_err(chip->dev, "Failed to register touch!\n");
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, touch);
+	return 0;
+}
+
+static struct platform_driver pm860x_touch_driver = {
+	.driver	= {
+		.name	= "88pm860x-touch",
+	},
+	.probe	= pm860x_touch_probe,
+};
+module_platform_driver(pm860x_touch_driver);
+
+MODULE_DESCRIPTION("Touchscreen driver for Marvell Semiconductor 88PM860x");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:88pm860x-touch");
+
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
new file mode 100644
index 0000000..ae33da7
--- /dev/null
+++ b/drivers/input/touchscreen/Kconfig
@@ -0,0 +1,1109 @@
+#
+# Touchscreen driver configuration
+#
+menuconfig INPUT_TOUCHSCREEN
+	bool "Touchscreens"
+	help
+	  Say Y here, and a list of supported touchscreens will be displayed.
+	  This option doesn't affect the kernel.
+
+	  If unsure, say Y.
+
+if INPUT_TOUCHSCREEN
+
+config TOUCHSCREEN_PROPERTIES
+	def_tristate INPUT
+	depends on INPUT
+
+config TOUCHSCREEN_88PM860X
+	tristate "Marvell 88PM860x touchscreen"
+	depends on MFD_88PM860X
+	help
+	  Say Y here if you have a 88PM860x PMIC and want to enable
+	  support for the built-in touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called 88pm860x-ts.
+
+config TOUCHSCREEN_ADS7846
+	tristate "ADS7846/TSC2046/AD7873 and AD(S)7843 based touchscreens"
+	depends on SPI_MASTER
+	depends on HWMON = n || HWMON
+	help
+	  Say Y here if you have a touchscreen interface using the
+	  ADS7846/TSC2046/AD7873 or ADS7843/AD7843 controller,
+	  and your board-specific setup code includes that in its
+	  table of SPI devices.
+
+	  If HWMON is selected, and the driver is told the reference voltage
+	  on your board, you will also get hwmon interfaces for the voltage
+	  (and on ads7846/tsc2046/ad7873, temperature) sensors of this chip.
+
+	  If unsure, say N (but it's safe to say "Y").
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ads7846.
+
+config TOUCHSCREEN_AD7877
+	tristate "AD7877 based touchscreens"
+	depends on SPI_MASTER
+	help
+	  Say Y here if you have a touchscreen interface using the
+	  AD7877 controller, and your board-specific initialization
+	  code includes that in its table of SPI devices.
+
+	  If unsure, say N (but it's safe to say "Y").
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad7877.
+
+config TOUCHSCREEN_AD7879
+	tristate "Analog Devices AD7879-1/AD7889-1 touchscreen interface"
+	help
+	  Say Y here if you want to support a touchscreen interface using
+	  the AD7879-1/AD7889-1 controller.
+
+	  You should select a bus connection too.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad7879.
+
+config TOUCHSCREEN_AD7879_I2C
+	tristate "support I2C bus connection"
+	depends on TOUCHSCREEN_AD7879 && I2C
+	help
+	  Say Y here if you have AD7879-1/AD7889-1 hooked to an I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad7879-i2c.
+
+config TOUCHSCREEN_AD7879_SPI
+	tristate "support SPI bus connection"
+	depends on TOUCHSCREEN_AD7879 && SPI_MASTER
+	help
+	  Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus.
+
+	  If unsure, say N (but it's safe to say "Y").
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad7879-spi.
+
+config TOUCHSCREEN_AR1021_I2C
+	tristate "Microchip AR1021 i2c touchscreen"
+	depends on I2C && OF
+	help
+	  Say Y here if you have the Microchip AR1021 touchscreen controller
+	  chip in your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ar1021_i2c.
+
+config TOUCHSCREEN_ATMEL_MXT
+	tristate "Atmel mXT I2C Touchscreen"
+	depends on I2C
+	select FW_LOADER
+	help
+	  Say Y here if you have Atmel mXT series I2C touchscreen,
+	  such as AT42QT602240/ATMXT224, connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called atmel_mxt_ts.
+
+config TOUCHSCREEN_AUO_PIXCIR
+	tristate "AUO in-cell touchscreen using Pixcir ICs"
+	depends on I2C
+	depends on GPIOLIB || COMPILE_TEST
+	help
+	  Say Y here if you have a AUO display with in-cell touchscreen
+	  using Pixcir ICs.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called auo-pixcir-ts.
+
+config TOUCHSCREEN_BU21013
+	tristate "BU21013 based touch panel controllers"
+	depends on I2C
+	help
+	  Say Y here if you have a bu21013 touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bu21013_ts.
+
+config TOUCHSCREEN_CHIPONE_ICN8318
+	tristate "chipone icn8318 touchscreen controller"
+	depends on GPIOLIB || COMPILE_TEST
+	depends on I2C
+	depends on OF
+	help
+	  Say Y here if you have a ChipOne icn8318 based I2C touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called chipone_icn8318.
+
+config TOUCHSCREEN_CY8CTMG110
+	tristate "cy8ctmg110 touchscreen"
+	depends on I2C
+	depends on GPIOLIB || COMPILE_TEST
+	help
+	  Say Y here if you have a cy8ctmg110 capacitive touchscreen on
+	  an AAVA device.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cy8ctmg110_ts.
+
+config TOUCHSCREEN_CYTTSP_CORE
+	tristate "Cypress TTSP touchscreen"
+	help
+	  Say Y here if you have a touchscreen using controller from
+	  the Cypress TrueTouch(tm) Standard Product family connected
+	  to your system. You will also need to select appropriate
+	  bus connection below.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cyttsp_core.
+
+config TOUCHSCREEN_CYTTSP_I2C
+	tristate "support I2C bus connection"
+	depends on TOUCHSCREEN_CYTTSP_CORE && I2C
+	help
+	  Say Y here if the touchscreen is connected via I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cyttsp_i2c.
+
+config TOUCHSCREEN_CYTTSP_SPI
+	tristate "support SPI bus connection"
+	depends on TOUCHSCREEN_CYTTSP_CORE && SPI_MASTER
+	help
+	  Say Y here if the touchscreen is connected via SPI bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cyttsp_spi.
+
+config TOUCHSCREEN_CYTTSP4_CORE
+	tristate "Cypress TrueTouch Gen4 Touchscreen Driver"
+	help
+	  Core driver for Cypress TrueTouch(tm) Standard Product
+	  Generation4 touchscreen controllers.
+
+	  Say Y here if you have a Cypress Gen4 touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here.
+
+config TOUCHSCREEN_CYTTSP4_I2C
+	tristate "support I2C bus connection"
+	depends on TOUCHSCREEN_CYTTSP4_CORE && I2C
+	help
+	  Say Y here if the touchscreen is connected via I2C bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cyttsp4_i2c.
+
+config TOUCHSCREEN_CYTTSP4_SPI
+	tristate "support SPI bus connection"
+	depends on TOUCHSCREEN_CYTTSP4_CORE && SPI_MASTER
+	help
+	  Say Y here if the touchscreen is connected via SPI bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cyttsp4_spi.
+
+config TOUCHSCREEN_DA9034
+	tristate "Touchscreen support for Dialog Semiconductor DA9034"
+	depends on PMIC_DA903X
+	default y
+	help
+	  Say Y here to enable the support for the touchscreen found
+	  on Dialog Semiconductor DA9034 PMIC.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called da9034-ts.
+
+config TOUCHSCREEN_DA9052
+	tristate "Dialog DA9052/DA9053 TSI"
+	depends on PMIC_DA9052
+	help
+	  Say Y here to support the touchscreen found on Dialog Semiconductor
+	  DA9052-BC and DA9053-AA/Bx PMICs.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called da9052_tsi.
+
+config TOUCHSCREEN_DYNAPRO
+	tristate "Dynapro serial touchscreen"
+	select SERIO
+	help
+	  Say Y here if you have a Dynapro serial touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called dynapro.
+
+config TOUCHSCREEN_HAMPSHIRE
+	tristate "Hampshire serial touchscreen"
+	select SERIO
+	help
+	  Say Y here if you have a Hampshire serial touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hampshire.
+
+config TOUCHSCREEN_EETI
+	tristate "EETI touchscreen panel support"
+	depends on I2C
+	help
+	  Say Y here to enable support for I2C connected EETI touch panels.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called eeti_ts.
+
+config TOUCHSCREEN_EGALAX
+	tristate "EETI eGalax multi-touch panel support"
+	depends on I2C && OF
+	help
+	  Say Y here to enable support for I2C connected EETI
+	  eGalax multi-touch panels.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called egalax_ts.
+
+config TOUCHSCREEN_FT6236
+	tristate "FT6236 I2C touchscreen"
+	depends on I2C
+	depends on GPIOLIB || COMPILE_TEST
+	help
+	  Say Y here to enable support for the I2C connected FT6x06 and
+	  FT6x36 family of capacitive touchscreen drivers.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ft6236.
+
+config TOUCHSCREEN_FUJITSU
+	tristate "Fujitsu serial touchscreen"
+	select SERIO
+	help
+	  Say Y here if you have the Fujitsu touchscreen (such as one
+	  installed in Lifebook P series laptop) connected to your
+	  system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called fujitsu-ts.
+
+config TOUCHSCREEN_GOODIX
+	tristate "Goodix I2C touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have the Goodix touchscreen (such as one
+	  installed in Onda v975w tablets) connected to your
+	  system. It also supports 5-finger chip models, which can be
+	  found on ARM tablets, like Wexler TAB7200 and MSI Primo73.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called goodix.
+
+config TOUCHSCREEN_ILI210X
+	tristate "Ilitek ILI210X based touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have a ILI210X based touchscreen
+	  controller. This driver supports models ILI2102,
+	  ILI2102s, ILI2103, ILI2103s and ILI2105.
+	  Such kind of chipsets can be found in Amazon Kindle Fire
+	  touchscreens.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ili210x.
+
+config TOUCHSCREEN_IPROC
+	tristate "IPROC touch panel driver support"
+	depends on ARCH_BCM_IPROC || COMPILE_TEST
+	help
+	  Say Y here if you want to add support for the IPROC touch
+	  controller to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bcm_iproc_tsc.
+
+config TOUCHSCREEN_S3C2410
+	tristate "Samsung S3C2410/generic touchscreen input driver"
+	depends on ARCH_S3C24XX || SAMSUNG_DEV_TS
+	select S3C_ADC
+	help
+	  Say Y here if you have the s3c2410 touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called s3c2410_ts.
+
+config TOUCHSCREEN_GUNZE
+	tristate "Gunze AHL-51S touchscreen"
+	select SERIO
+	help
+	  Say Y here if you have the Gunze AHL-51 touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gunze.
+
+config TOUCHSCREEN_ELAN
+	tristate "Elan eKTH I2C touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have an Elan eKTH I2C touchscreen
+	  connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called elants_i2c.
+
+config TOUCHSCREEN_ELO
+	tristate "Elo serial touchscreens"
+	select SERIO
+	help
+	  Say Y here if you have an Elo serial touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called elo.
+
+config TOUCHSCREEN_WACOM_W8001
+	tristate "Wacom W8001 penabled serial touchscreen"
+	select SERIO
+	help
+	  Say Y here if you have an Wacom W8001 penabled serial touchscreen
+	  connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wacom_w8001.
+
+config TOUCHSCREEN_WACOM_I2C
+	tristate "Wacom Tablet support (I2C)"
+	depends on I2C
+	help
+	  Say Y here if you want to use the I2C version of the Wacom
+	  Pen Tablet.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called wacom_i2c.
+
+config TOUCHSCREEN_LPC32XX
+	tristate "LPC32XX touchscreen controller"
+	depends on ARCH_LPC32XX
+	help
+	  Say Y here if you have a LPC32XX device and want
+	  to support the built-in touchscreen.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called lpc32xx_ts.
+
+config TOUCHSCREEN_MAX11801
+	tristate "MAX11801 based touchscreens"
+	depends on I2C
+	help
+	  Say Y here if you have a MAX11801 based touchscreen
+	  controller.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called max11801_ts.
+
+config TOUCHSCREEN_MCS5000
+	tristate "MELFAS MCS-5000 touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have the MELFAS MCS-5000 touchscreen controller
+	  chip in your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mcs5000_ts.
+
+config TOUCHSCREEN_MMS114
+	tristate "MELFAS MMS114 touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have the MELFAS MMS114 touchscreen controller
+	  chip in your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mms114.
+
+config TOUCHSCREEN_MTOUCH
+	tristate "MicroTouch serial touchscreens"
+	select SERIO
+	help
+	  Say Y here if you have a MicroTouch (3M) serial touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mtouch.
+
+config TOUCHSCREEN_IMX6UL_TSC
+	tristate "Freescale i.MX6UL touchscreen controller"
+	depends on (OF && GPIOLIB) || COMPILE_TEST
+	help
+	  Say Y here if you have a Freescale i.MX6UL, and want to
+	  use the internal touchscreen controller.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called imx6ul_tsc.
+
+config TOUCHSCREEN_INEXIO
+	tristate "iNexio serial touchscreens"
+	select SERIO
+	help
+	  Say Y here if you have an iNexio serial touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called inexio.
+
+config TOUCHSCREEN_INTEL_MID
+	tristate "Intel MID platform resistive touchscreen"
+	depends on INTEL_SCU_IPC
+	help
+	  Say Y here if you have a Intel MID based touchscreen in
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called intel_mid_touch.
+
+config TOUCHSCREEN_MK712
+	tristate "ICS MicroClock MK712 touchscreen"
+	help
+	  Say Y here if you have the ICS MicroClock MK712 touchscreen
+	  controller chip in your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mk712.
+
+config TOUCHSCREEN_HP600
+	tristate "HP Jornada 6xx touchscreen"
+	depends on SH_HP6XX && SH_ADC
+	help
+	  Say Y here if you have a HP Jornada 620/660/680/690 and want to
+          support the built-in touchscreen.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hp680_ts_input.
+
+config TOUCHSCREEN_HP7XX
+	tristate "HP Jornada 7xx touchscreen"
+	depends on SA1100_JORNADA720_SSP
+	help
+	  Say Y here if you have a HP Jornada 710/720/728 and want
+	  to support the built-in touchscreen.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called jornada720_ts.
+
+config TOUCHSCREEN_IPAQ_MICRO
+	tristate "HP iPAQ Atmel Micro ASIC touchscreen"
+	depends on MFD_IPAQ_MICRO
+	help
+	  Say Y here to enable support for the touchscreen attached to
+	  the Atmel Micro peripheral controller on iPAQ h3100/h3600/h3700
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ipaq-micro-ts.
+
+config TOUCHSCREEN_HTCPEN
+	tristate "HTC Shift X9500 touchscreen"
+	depends on ISA
+	help
+	  Say Y here if you have an HTC Shift UMPC also known as HTC X9500
+	  Clio / Shangrila and want to support the built-in touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called htcpen.
+
+config TOUCHSCREEN_PENMOUNT
+	tristate "Penmount serial touchscreen"
+	select SERIO
+	help
+	  Say Y here if you have a Penmount serial touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called penmount.
+
+config TOUCHSCREEN_EDT_FT5X06
+	tristate "EDT FocalTech FT5x06 I2C Touchscreen support"
+	depends on I2C
+	help
+	  Say Y here if you have an EDT "Polytouch" touchscreen based
+	  on the FocalTech FT5x06 family of controllers connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called edt-ft5x06.
+
+config TOUCHSCREEN_MIGOR
+	tristate "Renesas MIGO-R touchscreen"
+	depends on SH_MIGOR && I2C
+	help
+	  Say Y here to enable MIGO-R touchscreen support.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called migor_ts.
+
+config TOUCHSCREEN_TOUCHRIGHT
+	tristate "Touchright serial touchscreen"
+	select SERIO
+	help
+	  Say Y here if you have a Touchright serial touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called touchright.
+
+config TOUCHSCREEN_TOUCHWIN
+	tristate "Touchwin serial touchscreen"
+	select SERIO
+	help
+	  Say Y here if you have a Touchwin serial touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called touchwin.
+
+config TOUCHSCREEN_TI_AM335X_TSC
+	tristate "TI Touchscreen Interface"
+	depends on MFD_TI_AM335X_TSCADC
+	help
+	  Say Y here if you have 4/5/8 wire touchscreen controller
+	  to be connected to the ADC controller on your TI AM335x SoC.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ti_am335x_tsc.
+
+config TOUCHSCREEN_UCB1400
+	tristate "Philips UCB1400 touchscreen"
+	depends on AC97_BUS
+	depends on UCB1400_CORE
+	help
+	  This enables support for the Philips UCB1400 touchscreen interface.
+	  The UCB1400 is an AC97 audio codec.  The touchscreen interface
+	  will be initialized only after the ALSA subsystem has been
+	  brought up and the UCB1400 detected.  You therefore have to
+	  configure ALSA support as well (either built-in or modular,
+	  independently of whether this driver is itself built-in or
+	  modular) for this driver to work.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ucb1400_ts.
+
+config TOUCHSCREEN_PIXCIR
+	tristate "PIXCIR I2C touchscreens"
+	depends on I2C
+	help
+	  Say Y here if you have a pixcir i2c touchscreen
+	  controller.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pixcir_i2c_ts.
+
+config TOUCHSCREEN_WDT87XX_I2C
+	tristate "Weida HiTech I2C touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have a Weida WDT87XX I2C touchscreen
+	  connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wdt87xx_i2c.
+
+config TOUCHSCREEN_WM831X
+	tristate "Support for WM831x touchscreen controllers"
+	depends on MFD_WM831X
+	help
+	  This enables support for the touchscreen controller on the WM831x
+	  series of PMICs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wm831x-ts.
+
+config TOUCHSCREEN_WM97XX
+	tristate "Support for WM97xx AC97 touchscreen controllers"
+	depends on AC97_BUS
+	help
+	  Say Y here if you have a Wolfson Microelectronics WM97xx
+	  touchscreen connected to your system. Note that this option
+	  only enables core driver, you will also need to select
+	  support for appropriate chip below.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wm97xx-ts.
+
+config TOUCHSCREEN_WM9705
+	bool "WM9705 Touchscreen interface support"
+	depends on TOUCHSCREEN_WM97XX
+	default y
+	help
+	  Say Y here to enable support for the Wolfson Microelectronics
+	  WM9705 touchscreen controller.
+
+config TOUCHSCREEN_WM9712
+	bool "WM9712 Touchscreen interface support"
+	depends on TOUCHSCREEN_WM97XX
+	default y
+	help
+	  Say Y here to enable support for the Wolfson Microelectronics
+	  WM9712 touchscreen controller.
+
+config TOUCHSCREEN_WM9713
+	bool "WM9713 Touchscreen interface support"
+	depends on TOUCHSCREEN_WM97XX
+	default y
+	help
+	  Say Y here to enable support for the Wolfson Microelectronics
+	  WM9713 touchscreen controller.
+
+config TOUCHSCREEN_WM97XX_ATMEL
+	tristate "WM97xx Atmel accelerated touch"
+	depends on TOUCHSCREEN_WM97XX && AVR32
+	help
+	  Say Y here for support for streaming mode with WM97xx touchscreens
+	  on Atmel AT91 or AVR32 systems with an AC97C module.
+
+	  Be aware that this will use channel B in the controller for
+	  streaming data, this must not conflict with other AC97C drivers.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called atmel-wm97xx.
+
+config TOUCHSCREEN_WM97XX_MAINSTONE
+	tristate "WM97xx Mainstone/Palm accelerated touch"
+	depends on TOUCHSCREEN_WM97XX && ARCH_PXA
+	help
+	  Say Y here for support for streaming mode with WM97xx touchscreens
+	  on Mainstone, Palm Tungsten T5, TX and LifeDrive systems.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mainstone-wm97xx.
+
+config TOUCHSCREEN_WM97XX_ZYLONITE
+	tristate "Zylonite accelerated touch"
+	depends on TOUCHSCREEN_WM97XX && MACH_ZYLONITE
+	select TOUCHSCREEN_WM9713
+	help
+	  Say Y here for support for streaming mode with the touchscreen
+	  on Zylonite systems.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called zylonite-wm97xx.
+
+config TOUCHSCREEN_USB_COMPOSITE
+	tristate "USB Touchscreen Driver"
+	depends on USB_ARCH_HAS_HCD
+	select USB
+	help
+	  USB Touchscreen driver for:
+	  - eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700)
+	  - PanJit TouchSet USB
+	  - 3M MicroTouch USB (EX II series)
+	  - ITM
+	  - some other eTurboTouch
+	  - Gunze AHL61
+	  - DMC TSC-10/25
+	  - IRTOUCHSYSTEMS/UNITOP
+	  - IdealTEK URTC1000
+	  - GoTop Super_Q2/GogoPen/PenPower tablets
+	  - JASTEC USB Touch Controller/DigiTech DTR-02U
+	  - Zytronic controllers
+	  - Elo TouchSystems 2700 IntelliTouch
+	  - EasyTouch USB Touch Controller from Data Modul
+	  - e2i (Mimo monitors)
+
+	  Have a look at <http://linux.chapter7.ch/touchkit/> for
+	  a usage description and the required user-space stuff.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbtouchscreen.
+
+config TOUCHSCREEN_MC13783
+	tristate "Freescale MC13783 touchscreen input driver"
+	depends on MFD_MC13XXX
+	help
+	  Say Y here if you have an Freescale MC13783 PMIC on your
+	  board and want to use its touchscreen
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mc13783_ts.
+
+config TOUCHSCREEN_USB_EGALAX
+	default y
+	bool "eGalax, eTurboTouch CT-410/510/700 device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_PANJIT
+	default y
+	bool "PanJit device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_3M
+	default y
+	bool "3M/Microtouch EX II series device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ITM
+	default y
+	bool "ITM device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ETURBO
+	default y
+	bool "eTurboTouch (non-eGalax compatible) device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_GUNZE
+	default y
+	bool "Gunze AHL61 device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_DMC_TSC10
+	default y
+	bool "DMC TSC-10/25 device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_IRTOUCH
+	default y
+	bool "IRTOUCHSYSTEMS/UNITOP device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_IDEALTEK
+	default y
+	bool "IdealTEK URTC1000 device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_GENERAL_TOUCH
+	default y
+	bool "GeneralTouch Touchscreen device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_GOTOP
+	default y
+	bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_JASTEC
+	default y
+	bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ELO
+	default y
+	bool "Elo TouchSystems 2700 IntelliTouch controller device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_E2I
+	default y
+	bool "e2i Touchscreen controller (e.g. from Mimo 740)" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ZYTRONIC
+	default y
+	bool "Zytronic controller" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ETT_TC45USB
+	default y
+	bool "ET&T USB series TC4UM/TC5UH touchscreen controller support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_NEXIO
+	default y
+	bool "NEXIO/iNexio device support" if EXPERT
+	depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_EASYTOUCH
+	default y
+	bool "EasyTouch USB Touch controller device support" if EMBEDDED
+	depends on TOUCHSCREEN_USB_COMPOSITE
+	help
+	  Say Y here if you have an EasyTouch USB Touch controller.
+	  If unsure, say N.
+
+config TOUCHSCREEN_TOUCHIT213
+	tristate "Sahara TouchIT-213 touchscreen"
+	select SERIO
+	help
+	  Say Y here if you have a Sahara TouchIT-213 Tablet PC.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called touchit213.
+
+config TOUCHSCREEN_TSC_SERIO
+	tristate "TSC-10/25/40 serial touchscreen support"
+	select SERIO
+	help
+	  Say Y here if you have a TSC-10, 25 or 40 serial touchscreen connected
+	  to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tsc40.
+
+config TOUCHSCREEN_TSC200X_CORE
+	tristate
+
+config TOUCHSCREEN_TSC2004
+	tristate "TSC2004 based touchscreens"
+	depends on I2C
+	select REGMAP_I2C
+	select TOUCHSCREEN_TSC200X_CORE
+	help
+	  Say Y here if you have a TSC2004 based touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tsc2004.
+
+config TOUCHSCREEN_TSC2005
+	tristate "TSC2005 based touchscreens"
+	depends on SPI_MASTER
+	select REGMAP_SPI
+	select TOUCHSCREEN_TSC200X_CORE
+	help
+	  Say Y here if you have a TSC2005 based touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tsc2005.
+
+config TOUCHSCREEN_TSC2007
+	tristate "TSC2007 based touchscreens"
+	depends on I2C
+	help
+	  Say Y here if you have a TSC2007 based touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tsc2007.
+
+config TOUCHSCREEN_W90X900
+	tristate "W90P910 touchscreen driver"
+	depends on ARCH_W90X900
+	help
+	  Say Y here if you have a W90P910 based touchscreen.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called w90p910_ts.
+
+config TOUCHSCREEN_PCAP
+	tristate "Motorola PCAP touchscreen"
+	depends on EZX_PCAP
+	help
+	  Say Y here if you have a Motorola EZX telephone and
+	  want to enable support for the built-in touchscreen.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pcap_ts.
+
+config TOUCHSCREEN_ST1232
+	tristate "Sitronix ST1232 touchscreen controllers"
+	depends on I2C
+	help
+	  Say Y here if you want to support Sitronix ST1232
+	  touchscreen controller.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called st1232_ts.
+
+config TOUCHSCREEN_STMPE
+	tristate "STMicroelectronics STMPE touchscreens"
+	depends on MFD_STMPE
+	depends on (OF || COMPILE_TEST)
+	help
+	  Say Y here if you want support for STMicroelectronics
+	  STMPE touchscreen controllers.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called stmpe-ts.
+
+config TOUCHSCREEN_SUN4I
+	tristate "Allwinner sun4i resistive touchscreen controller support"
+	depends on ARCH_SUNXI || COMPILE_TEST
+	depends on HWMON
+	depends on THERMAL || !THERMAL_OF
+	help
+	  This selects support for the resistive touchscreen controller
+	  found on Allwinner sunxi SoCs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sun4i-ts.
+
+config TOUCHSCREEN_SUR40
+	tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen"
+	depends on USB && MEDIA_USB_SUPPORT && HAS_DMA
+	depends on VIDEO_V4L2
+	select INPUT_POLLDEV
+	select VIDEOBUF2_DMA_SG
+	help
+	  Say Y here if you want support for the Samsung SUR40 touchscreen
+	  (also known as Microsoft Surface 2.0 or Microsoft PixelSense).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sur40.
+
+config TOUCHSCREEN_SX8654
+	tristate "Semtech SX8654 touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have a Semtech SX8654 touchscreen controller.
+
+	  If unsure, say N
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sx8654.
+
+config TOUCHSCREEN_TPS6507X
+	tristate "TPS6507x based touchscreens"
+	depends on I2C
+	select INPUT_POLLDEV
+	help
+	  Say Y here if you have a TPS6507x based touchscreen
+	  controller.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tps6507x_ts.
+
+config TOUCHSCREEN_ZFORCE
+	tristate "Neonode zForce infrared touchscreens"
+	depends on I2C
+	depends on GPIOLIB || COMPILE_TEST
+	help
+	  Say Y here if you have a touchscreen using the zforce
+	  infraread technology from Neonode.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called zforce_ts.
+
+config TOUCHSCREEN_COLIBRI_VF50
+	tristate "Toradex Colibri on board touchscreen driver"
+	depends on GPIOLIB && IIO && VF610_ADC
+	help
+	  Say Y here if you have a Colibri VF50 and plan to use
+	  the on-board provided 4-wire touchscreen driver.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called colibri_vf50_ts.
+
+config TOUCHSCREEN_ROHM_BU21023
+	tristate "ROHM BU21023/24 Dual touch support resistive touchscreens"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen using ROHM BU21023/24.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bu21023_ts.
+
+endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
new file mode 100644
index 0000000..cbaa6ab
--- /dev/null
+++ b/drivers/input/touchscreen/Makefile
@@ -0,0 +1,93 @@
+#
+# Makefile for the touchscreen drivers.
+#
+
+# Each configuration option enables a list of files.
+
+wm97xx-ts-y := wm97xx-core.o
+
+obj-$(CONFIG_TOUCHSCREEN_PROPERTIES)	+= of_touchscreen.o
+obj-$(CONFIG_TOUCHSCREEN_88PM860X)	+= 88pm860x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_AD7877)	+= ad7877.o
+obj-$(CONFIG_TOUCHSCREEN_AD7879)	+= ad7879.o
+obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C)	+= ad7879-i2c.o
+obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI)	+= ad7879-spi.o
+obj-$(CONFIG_TOUCHSCREEN_ADS7846)	+= ads7846.o
+obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C)	+= ar1021_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT)	+= atmel_mxt_ts.o
+obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR)	+= auo-pixcir-ts.o
+obj-$(CONFIG_TOUCHSCREEN_BU21013)	+= bu21013_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318)	+= chipone_icn8318.o
+obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110)	+= cy8ctmg110_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE)	+= cyttsp_core.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C)	+= cyttsp_i2c.o cyttsp_i2c_common.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI)	+= cyttsp_spi.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_CORE)	+= cyttsp4_core.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_I2C)	+= cyttsp4_i2c.o cyttsp_i2c_common.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_SPI)	+= cyttsp4_spi.o
+obj-$(CONFIG_TOUCHSCREEN_DA9034)	+= da9034-ts.o
+obj-$(CONFIG_TOUCHSCREEN_DA9052)	+= da9052_tsi.o
+obj-$(CONFIG_TOUCHSCREEN_DYNAPRO)	+= dynapro.o
+obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06)	+= edt-ft5x06.o
+obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE)	+= hampshire.o
+obj-$(CONFIG_TOUCHSCREEN_GUNZE)		+= gunze.o
+obj-$(CONFIG_TOUCHSCREEN_EETI)		+= eeti_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ELAN)		+= elants_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_ELO)		+= elo.o
+obj-$(CONFIG_TOUCHSCREEN_EGALAX)	+= egalax_ts.o
+obj-$(CONFIG_TOUCHSCREEN_FT6236)	+= ft6236.o
+obj-$(CONFIG_TOUCHSCREEN_FUJITSU)	+= fujitsu_ts.o
+obj-$(CONFIG_TOUCHSCREEN_GOODIX)	+= goodix.o
+obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o
+obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)	+= imx6ul_tsc.o
+obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
+obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
+obj-$(CONFIG_TOUCHSCREEN_IPROC)		+= bcm_iproc_tsc.o
+obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MMS114)	+= mms114.o
+obj-$(CONFIG_TOUCHSCREEN_MTOUCH)	+= mtouch.o
+obj-$(CONFIG_TOUCHSCREEN_MK712)		+= mk712.o
+obj-$(CONFIG_TOUCHSCREEN_HP600)		+= hp680_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_HP7XX)		+= jornada720_ts.o
+obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO)	+= ipaq-micro-ts.o
+obj-$(CONFIG_TOUCHSCREEN_HTCPEN)	+= htcpen.o
+obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
+obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
+obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
+obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
+obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
+obj-$(CONFIG_TOUCHSCREEN_SUN4I)		+= sun4i-ts.o
+obj-$(CONFIG_TOUCHSCREEN_SUR40)		+= sur40.o
+obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC)	+= ti_am335x_tsc.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)	+= touchwin.o
+obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO)	+= tsc40.o
+obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE)	+= tsc200x-core.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2004)	+= tsc2004.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2005)	+= tsc2005.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2007)	+= tsc2007.o
+obj-$(CONFIG_TOUCHSCREEN_UCB1400)	+= ucb1400_ts.o
+obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001)	+= wacom_w8001.o
+obj-$(CONFIG_TOUCHSCREEN_WACOM_I2C)	+= wacom_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_WDT87XX_I2C)	+= wdt87xx_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_WM831X)	+= wm831x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX)	+= wm97xx-ts.o
+wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705)	+= wm9705.o
+wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712)	+= wm9712.o
+wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713)	+= wm9713.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL)	+= atmel-wm97xx.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)	+= mainstone-wm97xx.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
+obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
+obj-$(CONFIG_TOUCHSCREEN_SX8654)	+= sx8654.o
+obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
+obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50)	+= colibri-vf50-ts.o
+obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)	+= rohm_bu21023.o
diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c
new file mode 100644
index 0000000..9c250ae
--- /dev/null
+++ b/drivers/input/touchscreen/ad7877.c
@@ -0,0 +1,857 @@
+/*
+ * Copyright (C) 2006-2008 Michael Hennerich, Analog Devices Inc.
+ *
+ * Description:	AD7877 based touchscreen, sensor (ADCs), DAC and GPIO driver
+ * Based on:	ads7846.c
+ *
+ * Bugs:        Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * History:
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * Various changes: Imre Deak <imre.deak@nokia.com>
+ *
+ * Using code from:
+ *  - corgi_ts.c
+ *	Copyright (C) 2004-2005 Richard Purdie
+ *  - omap_ts.[hc], ads7846.h, ts_osk.c
+ *	Copyright (C) 2002 MontaVista Software
+ *	Copyright (C) 2004 Texas Instruments
+ *	Copyright (C) 2005 Dirk Behme
+ */
+
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ad7877.h>
+#include <linux/module.h>
+#include <asm/irq.h>
+
+#define	TS_PEN_UP_TIMEOUT	msecs_to_jiffies(100)
+
+#define MAX_SPI_FREQ_HZ			20000000
+#define	MAX_12BIT			((1<<12)-1)
+
+#define AD7877_REG_ZEROS			0
+#define AD7877_REG_CTRL1			1
+#define AD7877_REG_CTRL2			2
+#define AD7877_REG_ALERT			3
+#define AD7877_REG_AUX1HIGH			4
+#define AD7877_REG_AUX1LOW			5
+#define AD7877_REG_BAT1HIGH			6
+#define AD7877_REG_BAT1LOW			7
+#define AD7877_REG_BAT2HIGH			8
+#define AD7877_REG_BAT2LOW			9
+#define AD7877_REG_TEMP1HIGH			10
+#define AD7877_REG_TEMP1LOW			11
+#define AD7877_REG_SEQ0				12
+#define AD7877_REG_SEQ1				13
+#define AD7877_REG_DAC				14
+#define AD7877_REG_NONE1			15
+#define AD7877_REG_EXTWRITE			15
+#define AD7877_REG_XPLUS			16
+#define AD7877_REG_YPLUS			17
+#define AD7877_REG_Z2				18
+#define AD7877_REG_aux1				19
+#define AD7877_REG_aux2				20
+#define AD7877_REG_aux3				21
+#define AD7877_REG_bat1				22
+#define AD7877_REG_bat2				23
+#define AD7877_REG_temp1			24
+#define AD7877_REG_temp2			25
+#define AD7877_REG_Z1				26
+#define AD7877_REG_GPIOCTRL1			27
+#define AD7877_REG_GPIOCTRL2			28
+#define AD7877_REG_GPIODATA			29
+#define AD7877_REG_NONE2			30
+#define AD7877_REG_NONE3			31
+
+#define AD7877_SEQ_YPLUS_BIT			(1<<11)
+#define AD7877_SEQ_XPLUS_BIT			(1<<10)
+#define AD7877_SEQ_Z2_BIT			(1<<9)
+#define AD7877_SEQ_AUX1_BIT			(1<<8)
+#define AD7877_SEQ_AUX2_BIT			(1<<7)
+#define AD7877_SEQ_AUX3_BIT			(1<<6)
+#define AD7877_SEQ_BAT1_BIT			(1<<5)
+#define AD7877_SEQ_BAT2_BIT			(1<<4)
+#define AD7877_SEQ_TEMP1_BIT			(1<<3)
+#define AD7877_SEQ_TEMP2_BIT			(1<<2)
+#define AD7877_SEQ_Z1_BIT			(1<<1)
+
+enum {
+	AD7877_SEQ_YPOS  = 0,
+	AD7877_SEQ_XPOS  = 1,
+	AD7877_SEQ_Z2    = 2,
+	AD7877_SEQ_AUX1  = 3,
+	AD7877_SEQ_AUX2  = 4,
+	AD7877_SEQ_AUX3  = 5,
+	AD7877_SEQ_BAT1  = 6,
+	AD7877_SEQ_BAT2  = 7,
+	AD7877_SEQ_TEMP1 = 8,
+	AD7877_SEQ_TEMP2 = 9,
+	AD7877_SEQ_Z1    = 10,
+	AD7877_NR_SENSE  = 11,
+};
+
+/* DAC Register Default RANGE 0 to Vcc, Volatge Mode, DAC On */
+#define AD7877_DAC_CONF			0x1
+
+/* If gpio3 is set AUX3/GPIO3 acts as GPIO Output */
+#define AD7877_EXTW_GPIO_3_CONF		0x1C4
+#define AD7877_EXTW_GPIO_DATA		0x200
+
+/* Control REG 2 */
+#define AD7877_TMR(x)			((x & 0x3) << 0)
+#define AD7877_REF(x)			((x & 0x1) << 2)
+#define AD7877_POL(x)			((x & 0x1) << 3)
+#define AD7877_FCD(x)			((x & 0x3) << 4)
+#define AD7877_PM(x)			((x & 0x3) << 6)
+#define AD7877_ACQ(x)			((x & 0x3) << 8)
+#define AD7877_AVG(x)			((x & 0x3) << 10)
+
+/* Control REG 1 */
+#define	AD7877_SER			(1 << 11)	/* non-differential */
+#define	AD7877_DFR			(0 << 11)	/* differential */
+
+#define AD7877_MODE_NOC  (0)	/* Do not convert */
+#define AD7877_MODE_SCC  (1)	/* Single channel conversion */
+#define AD7877_MODE_SEQ0 (2)	/* Sequence 0 in Slave Mode */
+#define AD7877_MODE_SEQ1 (3)	/* Sequence 1 in Master Mode */
+
+#define AD7877_CHANADD(x)		((x&0xF)<<7)
+#define AD7877_READADD(x)		((x)<<2)
+#define AD7877_WRITEADD(x)		((x)<<12)
+
+#define AD7877_READ_CHAN(x) (AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_SER | \
+		AD7877_MODE_SCC | AD7877_CHANADD(AD7877_REG_ ## x) | \
+		AD7877_READADD(AD7877_REG_ ## x))
+
+#define AD7877_MM_SEQUENCE (AD7877_SEQ_YPLUS_BIT | AD7877_SEQ_XPLUS_BIT | \
+		AD7877_SEQ_Z2_BIT | AD7877_SEQ_Z1_BIT)
+
+/*
+ * Non-touchscreen sensors only use single-ended conversions.
+ */
+
+struct ser_req {
+	u16			reset;
+	u16			ref_on;
+	u16			command;
+	struct spi_message	msg;
+	struct spi_transfer	xfer[6];
+
+	/*
+	 * DMA (thus cache coherency maintenance) requires the
+	 * transfer buffers to live in their own cache lines.
+	 */
+	u16 sample ____cacheline_aligned;
+};
+
+struct ad7877 {
+	struct input_dev	*input;
+	char			phys[32];
+
+	struct spi_device	*spi;
+	u16			model;
+	u16			vref_delay_usecs;
+	u16			x_plate_ohms;
+	u16			pressure_max;
+
+	u16			cmd_crtl1;
+	u16			cmd_crtl2;
+	u16			cmd_dummy;
+	u16			dac;
+
+	u8			stopacq_polarity;
+	u8			first_conversion_delay;
+	u8			acquisition_time;
+	u8			averaging;
+	u8			pen_down_acc_interval;
+
+	struct spi_transfer	xfer[AD7877_NR_SENSE + 2];
+	struct spi_message	msg;
+
+	struct mutex		mutex;
+	bool			disabled;	/* P: mutex */
+	bool			gpio3;		/* P: mutex */
+	bool			gpio4;		/* P: mutex */
+
+	spinlock_t		lock;
+	struct timer_list	timer;		/* P: lock */
+
+	/*
+	 * DMA (thus cache coherency maintenance) requires the
+	 * transfer buffers to live in their own cache lines.
+	 */
+	u16 conversion_data[AD7877_NR_SENSE] ____cacheline_aligned;
+};
+
+static bool gpio3;
+module_param(gpio3, bool, 0);
+MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3");
+
+static int ad7877_read(struct spi_device *spi, u16 reg)
+{
+	struct ser_req *req;
+	int status, ret;
+
+	req = kzalloc(sizeof *req, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	spi_message_init(&req->msg);
+
+	req->command = (u16) (AD7877_WRITEADD(AD7877_REG_CTRL1) |
+			AD7877_READADD(reg));
+	req->xfer[0].tx_buf = &req->command;
+	req->xfer[0].len = 2;
+	req->xfer[0].cs_change = 1;
+
+	req->xfer[1].rx_buf = &req->sample;
+	req->xfer[1].len = 2;
+
+	spi_message_add_tail(&req->xfer[0], &req->msg);
+	spi_message_add_tail(&req->xfer[1], &req->msg);
+
+	status = spi_sync(spi, &req->msg);
+	ret = status ? : req->sample;
+
+	kfree(req);
+
+	return ret;
+}
+
+static int ad7877_write(struct spi_device *spi, u16 reg, u16 val)
+{
+	struct ser_req *req;
+	int status;
+
+	req = kzalloc(sizeof *req, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	spi_message_init(&req->msg);
+
+	req->command = (u16) (AD7877_WRITEADD(reg) | (val & MAX_12BIT));
+	req->xfer[0].tx_buf = &req->command;
+	req->xfer[0].len = 2;
+
+	spi_message_add_tail(&req->xfer[0], &req->msg);
+
+	status = spi_sync(spi, &req->msg);
+
+	kfree(req);
+
+	return status;
+}
+
+static int ad7877_read_adc(struct spi_device *spi, unsigned command)
+{
+	struct ad7877 *ts = spi_get_drvdata(spi);
+	struct ser_req *req;
+	int status;
+	int sample;
+	int i;
+
+	req = kzalloc(sizeof *req, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	spi_message_init(&req->msg);
+
+	/* activate reference, so it has time to settle; */
+	req->ref_on = AD7877_WRITEADD(AD7877_REG_CTRL2) |
+			 AD7877_POL(ts->stopacq_polarity) |
+			 AD7877_AVG(0) | AD7877_PM(2) | AD7877_TMR(0) |
+			 AD7877_ACQ(ts->acquisition_time) | AD7877_FCD(0);
+
+	req->reset = AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_MODE_NOC;
+
+	req->command = (u16) command;
+
+	req->xfer[0].tx_buf = &req->reset;
+	req->xfer[0].len = 2;
+	req->xfer[0].cs_change = 1;
+
+	req->xfer[1].tx_buf = &req->ref_on;
+	req->xfer[1].len = 2;
+	req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+	req->xfer[1].cs_change = 1;
+
+	req->xfer[2].tx_buf = &req->command;
+	req->xfer[2].len = 2;
+	req->xfer[2].delay_usecs = ts->vref_delay_usecs;
+	req->xfer[2].cs_change = 1;
+
+	req->xfer[3].rx_buf = &req->sample;
+	req->xfer[3].len = 2;
+	req->xfer[3].cs_change = 1;
+
+	req->xfer[4].tx_buf = &ts->cmd_crtl2;	/*REF OFF*/
+	req->xfer[4].len = 2;
+	req->xfer[4].cs_change = 1;
+
+	req->xfer[5].tx_buf = &ts->cmd_crtl1;	/*DEFAULT*/
+	req->xfer[5].len = 2;
+
+	/* group all the transfers together, so we can't interfere with
+	 * reading touchscreen state; disable penirq while sampling
+	 */
+	for (i = 0; i < 6; i++)
+		spi_message_add_tail(&req->xfer[i], &req->msg);
+
+	status = spi_sync(spi, &req->msg);
+	sample = req->sample;
+
+	kfree(req);
+
+	return status ? : sample;
+}
+
+static int ad7877_process_data(struct ad7877 *ts)
+{
+	struct input_dev *input_dev = ts->input;
+	unsigned Rt;
+	u16 x, y, z1, z2;
+
+	x = ts->conversion_data[AD7877_SEQ_XPOS] & MAX_12BIT;
+	y = ts->conversion_data[AD7877_SEQ_YPOS] & MAX_12BIT;
+	z1 = ts->conversion_data[AD7877_SEQ_Z1] & MAX_12BIT;
+	z2 = ts->conversion_data[AD7877_SEQ_Z2] & MAX_12BIT;
+
+	/*
+	 * The samples processed here are already preprocessed by the AD7877.
+	 * The preprocessing function consists of an averaging filter.
+	 * The combination of 'first conversion delay' and averaging provides a robust solution,
+	 * discarding the spurious noise in the signal and keeping only the data of interest.
+	 * The size of the averaging filter is programmable. (dev.platform_data, see linux/spi/ad7877.h)
+	 * Other user-programmable conversion controls include variable acquisition time,
+	 * and first conversion delay. Up to 16 averages can be taken per conversion.
+	 */
+
+	if (likely(x && z1)) {
+		/* compute touch pressure resistance using equation #1 */
+		Rt = (z2 - z1) * x * ts->x_plate_ohms;
+		Rt /= z1;
+		Rt = (Rt + 2047) >> 12;
+
+		/*
+		 * Sample found inconsistent, pressure is beyond
+		 * the maximum. Don't report it to user space.
+		 */
+		if (Rt > ts->pressure_max)
+			return -EINVAL;
+
+		if (!timer_pending(&ts->timer))
+			input_report_key(input_dev, BTN_TOUCH, 1);
+
+		input_report_abs(input_dev, ABS_X, x);
+		input_report_abs(input_dev, ABS_Y, y);
+		input_report_abs(input_dev, ABS_PRESSURE, Rt);
+		input_sync(input_dev);
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static inline void ad7877_ts_event_release(struct ad7877 *ts)
+{
+	struct input_dev *input_dev = ts->input;
+
+	input_report_abs(input_dev, ABS_PRESSURE, 0);
+	input_report_key(input_dev, BTN_TOUCH, 0);
+	input_sync(input_dev);
+}
+
+static void ad7877_timer(unsigned long handle)
+{
+	struct ad7877 *ts = (void *)handle;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ts->lock, flags);
+	ad7877_ts_event_release(ts);
+	spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+static irqreturn_t ad7877_irq(int irq, void *handle)
+{
+	struct ad7877 *ts = handle;
+	unsigned long flags;
+	int error;
+
+	error = spi_sync(ts->spi, &ts->msg);
+	if (error) {
+		dev_err(&ts->spi->dev, "spi_sync --> %d\n", error);
+		goto out;
+	}
+
+	spin_lock_irqsave(&ts->lock, flags);
+	error = ad7877_process_data(ts);
+	if (!error)
+		mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
+	spin_unlock_irqrestore(&ts->lock, flags);
+
+out:
+	return IRQ_HANDLED;
+}
+
+static void ad7877_disable(struct ad7877 *ts)
+{
+	mutex_lock(&ts->mutex);
+
+	if (!ts->disabled) {
+		ts->disabled = true;
+		disable_irq(ts->spi->irq);
+
+		if (del_timer_sync(&ts->timer))
+			ad7877_ts_event_release(ts);
+	}
+
+	/*
+	 * We know the chip's in lowpower mode since we always
+	 * leave it that way after every request
+	 */
+
+	mutex_unlock(&ts->mutex);
+}
+
+static void ad7877_enable(struct ad7877 *ts)
+{
+	mutex_lock(&ts->mutex);
+
+	if (ts->disabled) {
+		ts->disabled = false;
+		enable_irq(ts->spi->irq);
+	}
+
+	mutex_unlock(&ts->mutex);
+}
+
+#define SHOW(name) static ssize_t \
+name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+	struct ad7877 *ts = dev_get_drvdata(dev); \
+	ssize_t v = ad7877_read_adc(ts->spi, \
+			AD7877_READ_CHAN(name)); \
+	if (v < 0) \
+		return v; \
+	return sprintf(buf, "%u\n", (unsigned) v); \
+} \
+static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);
+
+SHOW(aux1)
+SHOW(aux2)
+SHOW(aux3)
+SHOW(bat1)
+SHOW(bat2)
+SHOW(temp1)
+SHOW(temp2)
+
+static ssize_t ad7877_disable_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct ad7877 *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t ad7877_disable_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct ad7877 *ts = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	if (val)
+		ad7877_disable(ts);
+	else
+		ad7877_enable(ts);
+
+	return count;
+}
+
+static DEVICE_ATTR(disable, 0664, ad7877_disable_show, ad7877_disable_store);
+
+static ssize_t ad7877_dac_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct ad7877 *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", ts->dac);
+}
+
+static ssize_t ad7877_dac_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct ad7877 *ts = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	mutex_lock(&ts->mutex);
+	ts->dac = val & 0xFF;
+	ad7877_write(ts->spi, AD7877_REG_DAC, (ts->dac << 4) | AD7877_DAC_CONF);
+	mutex_unlock(&ts->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(dac, 0664, ad7877_dac_show, ad7877_dac_store);
+
+static ssize_t ad7877_gpio3_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct ad7877 *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", ts->gpio3);
+}
+
+static ssize_t ad7877_gpio3_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct ad7877 *ts = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	mutex_lock(&ts->mutex);
+	ts->gpio3 = !!val;
+	ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA |
+		 (ts->gpio4 << 4) | (ts->gpio3 << 5));
+	mutex_unlock(&ts->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(gpio3, 0664, ad7877_gpio3_show, ad7877_gpio3_store);
+
+static ssize_t ad7877_gpio4_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct ad7877 *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", ts->gpio4);
+}
+
+static ssize_t ad7877_gpio4_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct ad7877 *ts = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	mutex_lock(&ts->mutex);
+	ts->gpio4 = !!val;
+	ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA |
+		     (ts->gpio4 << 4) | (ts->gpio3 << 5));
+	mutex_unlock(&ts->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(gpio4, 0664, ad7877_gpio4_show, ad7877_gpio4_store);
+
+static struct attribute *ad7877_attributes[] = {
+	&dev_attr_temp1.attr,
+	&dev_attr_temp2.attr,
+	&dev_attr_aux1.attr,
+	&dev_attr_aux2.attr,
+	&dev_attr_aux3.attr,
+	&dev_attr_bat1.attr,
+	&dev_attr_bat2.attr,
+	&dev_attr_disable.attr,
+	&dev_attr_dac.attr,
+	&dev_attr_gpio3.attr,
+	&dev_attr_gpio4.attr,
+	NULL
+};
+
+static umode_t ad7877_attr_is_visible(struct kobject *kobj,
+				     struct attribute *attr, int n)
+{
+	umode_t mode = attr->mode;
+
+	if (attr == &dev_attr_aux3.attr) {
+		if (gpio3)
+			mode = 0;
+	} else if (attr == &dev_attr_gpio3.attr) {
+		if (!gpio3)
+			mode = 0;
+	}
+
+	return mode;
+}
+
+static const struct attribute_group ad7877_attr_group = {
+	.is_visible	= ad7877_attr_is_visible,
+	.attrs		= ad7877_attributes,
+};
+
+static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)
+{
+	struct spi_message *m;
+	int i;
+
+	ts->cmd_crtl2 = AD7877_WRITEADD(AD7877_REG_CTRL2) |
+			AD7877_POL(ts->stopacq_polarity) |
+			AD7877_AVG(ts->averaging) | AD7877_PM(1) |
+			AD7877_TMR(ts->pen_down_acc_interval) |
+			AD7877_ACQ(ts->acquisition_time) |
+			AD7877_FCD(ts->first_conversion_delay);
+
+	ad7877_write(spi, AD7877_REG_CTRL2, ts->cmd_crtl2);
+
+	ts->cmd_crtl1 = AD7877_WRITEADD(AD7877_REG_CTRL1) |
+			AD7877_READADD(AD7877_REG_XPLUS-1) |
+			AD7877_MODE_SEQ1 | AD7877_DFR;
+
+	ad7877_write(spi, AD7877_REG_CTRL1, ts->cmd_crtl1);
+
+	ts->cmd_dummy = 0;
+
+	m = &ts->msg;
+
+	spi_message_init(m);
+
+	m->context = ts;
+
+	ts->xfer[0].tx_buf = &ts->cmd_crtl1;
+	ts->xfer[0].len = 2;
+	ts->xfer[0].cs_change = 1;
+
+	spi_message_add_tail(&ts->xfer[0], m);
+
+	ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */
+	ts->xfer[1].len = 2;
+	ts->xfer[1].cs_change = 1;
+
+	spi_message_add_tail(&ts->xfer[1], m);
+
+	for (i = 0; i < AD7877_NR_SENSE; i++) {
+		ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i];
+		ts->xfer[i + 2].len = 2;
+		if (i < (AD7877_NR_SENSE - 1))
+			ts->xfer[i + 2].cs_change = 1;
+		spi_message_add_tail(&ts->xfer[i + 2], m);
+	}
+}
+
+static int ad7877_probe(struct spi_device *spi)
+{
+	struct ad7877			*ts;
+	struct input_dev		*input_dev;
+	struct ad7877_platform_data	*pdata = dev_get_platdata(&spi->dev);
+	int				err;
+	u16				verify;
+
+	if (!spi->irq) {
+		dev_dbg(&spi->dev, "no IRQ?\n");
+		return -ENODEV;
+	}
+
+	if (!pdata) {
+		dev_dbg(&spi->dev, "no platform data?\n");
+		return -ENODEV;
+	}
+
+	/* don't exceed max specified SPI CLK frequency */
+	if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
+		dev_dbg(&spi->dev, "SPI CLK %d Hz?\n",spi->max_speed_hz);
+		return -EINVAL;
+	}
+
+	spi->bits_per_word = 16;
+	err = spi_setup(spi);
+	if (err) {
+		dev_dbg(&spi->dev, "spi master doesn't support 16 bits/word\n");
+		return err;
+	}
+
+	ts = kzalloc(sizeof(struct ad7877), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!ts || !input_dev) {
+		err = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	spi_set_drvdata(spi, ts);
+	ts->spi = spi;
+	ts->input = input_dev;
+
+	setup_timer(&ts->timer, ad7877_timer, (unsigned long) ts);
+	mutex_init(&ts->mutex);
+	spin_lock_init(&ts->lock);
+
+	ts->model = pdata->model ? : 7877;
+	ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
+	ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+	ts->pressure_max = pdata->pressure_max ? : ~0;
+
+	ts->stopacq_polarity = pdata->stopacq_polarity;
+	ts->first_conversion_delay = pdata->first_conversion_delay;
+	ts->acquisition_time = pdata->acquisition_time;
+	ts->averaging = pdata->averaging;
+	ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
+
+	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
+
+	input_dev->name = "AD7877 Touchscreen";
+	input_dev->phys = ts->phys;
+	input_dev->dev.parent = &spi->dev;
+
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(ABS_X, input_dev->absbit);
+	__set_bit(ABS_Y, input_dev->absbit);
+	__set_bit(ABS_PRESSURE, input_dev->absbit);
+
+	input_set_abs_params(input_dev, ABS_X,
+			pdata->x_min ? : 0,
+			pdata->x_max ? : MAX_12BIT,
+			0, 0);
+	input_set_abs_params(input_dev, ABS_Y,
+			pdata->y_min ? : 0,
+			pdata->y_max ? : MAX_12BIT,
+			0, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE,
+			pdata->pressure_min, pdata->pressure_max, 0, 0);
+
+	ad7877_write(spi, AD7877_REG_SEQ1, AD7877_MM_SEQUENCE);
+
+	verify = ad7877_read(spi, AD7877_REG_SEQ1);
+
+	if (verify != AD7877_MM_SEQUENCE){
+		dev_err(&spi->dev, "%s: Failed to probe %s\n",
+			dev_name(&spi->dev), input_dev->name);
+		err = -ENODEV;
+		goto err_free_mem;
+	}
+
+	if (gpio3)
+		ad7877_write(spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_3_CONF);
+
+	ad7877_setup_ts_def_msg(spi, ts);
+
+	/* Request AD7877 /DAV GPIO interrupt */
+
+	err = request_threaded_irq(spi->irq, NULL, ad7877_irq,
+				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				   spi->dev.driver->name, ts);
+	if (err) {
+		dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
+		goto err_free_mem;
+	}
+
+	err = sysfs_create_group(&spi->dev.kobj, &ad7877_attr_group);
+	if (err)
+		goto err_free_irq;
+
+	err = input_register_device(input_dev);
+	if (err)
+		goto err_remove_attr_group;
+
+	return 0;
+
+err_remove_attr_group:
+	sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
+err_free_irq:
+	free_irq(spi->irq, ts);
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(ts);
+	return err;
+}
+
+static int ad7877_remove(struct spi_device *spi)
+{
+	struct ad7877 *ts = spi_get_drvdata(spi);
+
+	sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
+
+	ad7877_disable(ts);
+	free_irq(ts->spi->irq, ts);
+
+	input_unregister_device(ts->input);
+	kfree(ts);
+
+	dev_dbg(&spi->dev, "unregistered touchscreen\n");
+
+	return 0;
+}
+
+static int __maybe_unused ad7877_suspend(struct device *dev)
+{
+	struct ad7877 *ts = dev_get_drvdata(dev);
+
+	ad7877_disable(ts);
+
+	return 0;
+}
+
+static int __maybe_unused ad7877_resume(struct device *dev)
+{
+	struct ad7877 *ts = dev_get_drvdata(dev);
+
+	ad7877_enable(ts);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ad7877_pm, ad7877_suspend, ad7877_resume);
+
+static struct spi_driver ad7877_driver = {
+	.driver = {
+		.name	= "ad7877",
+		.pm	= &ad7877_pm,
+	},
+	.probe		= ad7877_probe,
+	.remove		= ad7877_remove,
+};
+
+module_spi_driver(ad7877_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("AD7877 touchscreen Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:ad7877");
diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c
new file mode 100644
index 0000000..d66962c
--- /dev/null
+++ b/drivers/input/touchscreen/ad7879-i2c.c
@@ -0,0 +1,108 @@
+/*
+ * AD7879-1/AD7889-1 touchscreen (I2C bus)
+ *
+ * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h>	/* BUS_I2C */
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/pm.h>
+
+#include "ad7879.h"
+
+#define AD7879_DEVID		0x79	/* AD7879-1/AD7889-1 */
+
+/* All registers are word-sized.
+ * AD7879 uses a high-byte first convention.
+ */
+static int ad7879_i2c_read(struct device *dev, u8 reg)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int ad7879_i2c_multi_read(struct device *dev,
+				 u8 first_reg, u8 count, u16 *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 idx;
+
+	i2c_smbus_read_i2c_block_data(client, first_reg, count * 2, (u8 *)buf);
+
+	for (idx = 0; idx < count; ++idx)
+		buf[idx] = swab16(buf[idx]);
+
+	return 0;
+}
+
+static int ad7879_i2c_write(struct device *dev, u8 reg, u16 val)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	return i2c_smbus_write_word_swapped(client, reg, val);
+}
+
+static const struct ad7879_bus_ops ad7879_i2c_bus_ops = {
+	.bustype	= BUS_I2C,
+	.read		= ad7879_i2c_read,
+	.multi_read	= ad7879_i2c_multi_read,
+	.write		= ad7879_i2c_write,
+};
+
+static int ad7879_i2c_probe(struct i2c_client *client,
+				      const struct i2c_device_id *id)
+{
+	struct ad7879 *ts;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_WORD_DATA)) {
+		dev_err(&client->dev, "SMBUS Word Data not Supported\n");
+		return -EIO;
+	}
+
+	ts = ad7879_probe(&client->dev, AD7879_DEVID, client->irq,
+			  &ad7879_i2c_bus_ops);
+	if (IS_ERR(ts))
+		return PTR_ERR(ts);
+
+	i2c_set_clientdata(client, ts);
+
+	return 0;
+}
+
+static int ad7879_i2c_remove(struct i2c_client *client)
+{
+	struct ad7879 *ts = i2c_get_clientdata(client);
+
+	ad7879_remove(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id ad7879_id[] = {
+	{ "ad7879", 0 },
+	{ "ad7889", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ad7879_id);
+
+static struct i2c_driver ad7879_i2c_driver = {
+	.driver = {
+		.name	= "ad7879",
+		.pm	= &ad7879_pm_ops,
+	},
+	.probe		= ad7879_i2c_probe,
+	.remove		= ad7879_i2c_remove,
+	.id_table	= ad7879_id,
+};
+
+module_i2c_driver(ad7879_i2c_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c
new file mode 100644
index 0000000..48033c2
--- /dev/null
+++ b/drivers/input/touchscreen/ad7879-spi.c
@@ -0,0 +1,163 @@
+/*
+ * AD7879/AD7889 touchscreen (SPI bus)
+ *
+ * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h>	/* BUS_SPI */
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+
+#include "ad7879.h"
+
+#define AD7879_DEVID		0x7A	/* AD7879/AD7889 */
+
+#define MAX_SPI_FREQ_HZ      5000000
+#define AD7879_CMD_MAGIC     0xE000
+#define AD7879_CMD_READ      (1 << 10)
+#define AD7879_CMD(reg)      (AD7879_CMD_MAGIC | ((reg) & 0xF))
+#define AD7879_WRITECMD(reg) (AD7879_CMD(reg))
+#define AD7879_READCMD(reg)  (AD7879_CMD(reg) | AD7879_CMD_READ)
+
+/*
+ * ad7879_read/write are only used for initial setup and for sysfs controls.
+ * The main traffic is done in ad7879_collect().
+ */
+
+static int ad7879_spi_xfer(struct spi_device *spi,
+			   u16 cmd, u8 count, u16 *tx_buf, u16 *rx_buf)
+{
+	struct spi_message msg;
+	struct spi_transfer *xfers;
+	void *spi_data;
+	u16 *command;
+	u16 *_rx_buf = _rx_buf; /* shut gcc up */
+	u8 idx;
+	int ret;
+
+	xfers = spi_data = kzalloc(sizeof(*xfers) * (count + 2), GFP_KERNEL);
+	if (!spi_data)
+		return -ENOMEM;
+
+	spi_message_init(&msg);
+
+	command = spi_data;
+	command[0] = cmd;
+	if (count == 1) {
+		/* ad7879_spi_{read,write} gave us buf on stack */
+		command[1] = *tx_buf;
+		tx_buf = &command[1];
+		_rx_buf = rx_buf;
+		rx_buf = &command[2];
+	}
+
+	++xfers;
+	xfers[0].tx_buf = command;
+	xfers[0].len = 2;
+	spi_message_add_tail(&xfers[0], &msg);
+	++xfers;
+
+	for (idx = 0; idx < count; ++idx) {
+		if (rx_buf)
+			xfers[idx].rx_buf = &rx_buf[idx];
+		if (tx_buf)
+			xfers[idx].tx_buf = &tx_buf[idx];
+		xfers[idx].len = 2;
+		spi_message_add_tail(&xfers[idx], &msg);
+	}
+
+	ret = spi_sync(spi, &msg);
+
+	if (count == 1)
+		_rx_buf[0] = command[2];
+
+	kfree(spi_data);
+
+	return ret;
+}
+
+static int ad7879_spi_multi_read(struct device *dev,
+				 u8 first_reg, u8 count, u16 *buf)
+{
+	struct spi_device *spi = to_spi_device(dev);
+
+	return ad7879_spi_xfer(spi, AD7879_READCMD(first_reg), count, NULL, buf);
+}
+
+static int ad7879_spi_read(struct device *dev, u8 reg)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	u16 ret, dummy;
+
+	return ad7879_spi_xfer(spi, AD7879_READCMD(reg), 1, &dummy, &ret) ? : ret;
+}
+
+static int ad7879_spi_write(struct device *dev, u8 reg, u16 val)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	u16 dummy;
+
+	return ad7879_spi_xfer(spi, AD7879_WRITECMD(reg), 1, &val, &dummy);
+}
+
+static const struct ad7879_bus_ops ad7879_spi_bus_ops = {
+	.bustype	= BUS_SPI,
+	.read		= ad7879_spi_read,
+	.multi_read	= ad7879_spi_multi_read,
+	.write		= ad7879_spi_write,
+};
+
+static int ad7879_spi_probe(struct spi_device *spi)
+{
+	struct ad7879 *ts;
+	int err;
+
+	/* don't exceed max specified SPI CLK frequency */
+	if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
+		dev_err(&spi->dev, "SPI CLK %d Hz?\n", spi->max_speed_hz);
+		return -EINVAL;
+	}
+
+	spi->bits_per_word = 16;
+	err = spi_setup(spi);
+	if (err) {
+	        dev_dbg(&spi->dev, "spi master doesn't support 16 bits/word\n");
+	        return err;
+	}
+
+	ts = ad7879_probe(&spi->dev, AD7879_DEVID, spi->irq, &ad7879_spi_bus_ops);
+	if (IS_ERR(ts))
+		return PTR_ERR(ts);
+
+	spi_set_drvdata(spi, ts);
+
+	return 0;
+}
+
+static int ad7879_spi_remove(struct spi_device *spi)
+{
+	struct ad7879 *ts = spi_get_drvdata(spi);
+
+	ad7879_remove(ts);
+
+	return 0;
+}
+
+static struct spi_driver ad7879_spi_driver = {
+	.driver = {
+		.name	= "ad7879",
+		.pm	= &ad7879_pm_ops,
+	},
+	.probe		= ad7879_spi_probe,
+	.remove		= ad7879_spi_remove,
+};
+
+module_spi_driver(ad7879_spi_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:ad7879");
diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c
new file mode 100644
index 0000000..fec66ad
--- /dev/null
+++ b/drivers/input/touchscreen/ad7879.c
@@ -0,0 +1,647 @@
+/*
+ * AD7879/AD7889 based touchscreen and GPIO driver
+ *
+ * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ *
+ * History:
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * Various changes: Imre Deak <imre.deak@nokia.com>
+ *
+ * Using code from:
+ *  - corgi_ts.c
+ *	Copyright (C) 2004-2005 Richard Purdie
+ *  - omap_ts.[hc], ads7846.h, ts_osk.c
+ *	Copyright (C) 2002 MontaVista Software
+ *	Copyright (C) 2004 Texas Instruments
+ *	Copyright (C) 2005 Dirk Behme
+ *  - ad7877.c
+ *	Copyright (C) 2006-2008 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+
+#include <linux/spi/ad7879.h>
+#include <linux/module.h>
+#include "ad7879.h"
+
+#define AD7879_REG_ZEROS		0
+#define AD7879_REG_CTRL1		1
+#define AD7879_REG_CTRL2		2
+#define AD7879_REG_CTRL3		3
+#define AD7879_REG_AUX1HIGH		4
+#define AD7879_REG_AUX1LOW		5
+#define AD7879_REG_TEMP1HIGH		6
+#define AD7879_REG_TEMP1LOW		7
+#define AD7879_REG_XPLUS		8
+#define AD7879_REG_YPLUS		9
+#define AD7879_REG_Z1			10
+#define AD7879_REG_Z2			11
+#define AD7879_REG_AUXVBAT		12
+#define AD7879_REG_TEMP			13
+#define AD7879_REG_REVID		14
+
+/* Control REG 1 */
+#define AD7879_TMR(x)			((x & 0xFF) << 0)
+#define AD7879_ACQ(x)			((x & 0x3) << 8)
+#define AD7879_MODE_NOC			(0 << 10)	/* Do not convert */
+#define AD7879_MODE_SCC			(1 << 10)	/* Single channel conversion */
+#define AD7879_MODE_SEQ0		(2 << 10)	/* Sequence 0 in Slave Mode */
+#define AD7879_MODE_SEQ1		(3 << 10)	/* Sequence 1 in Master Mode */
+#define AD7879_MODE_INT			(1 << 15)	/* PENIRQ disabled INT enabled */
+
+/* Control REG 2 */
+#define AD7879_FCD(x)			((x & 0x3) << 0)
+#define AD7879_RESET			(1 << 4)
+#define AD7879_MFS(x)			((x & 0x3) << 5)
+#define AD7879_AVG(x)			((x & 0x3) << 7)
+#define	AD7879_SER			(1 << 9)	/* non-differential */
+#define	AD7879_DFR			(0 << 9)	/* differential */
+#define AD7879_GPIOPOL			(1 << 10)
+#define AD7879_GPIODIR			(1 << 11)
+#define AD7879_GPIO_DATA		(1 << 12)
+#define AD7879_GPIO_EN			(1 << 13)
+#define AD7879_PM(x)			((x & 0x3) << 14)
+#define AD7879_PM_SHUTDOWN		(0)
+#define AD7879_PM_DYN			(1)
+#define AD7879_PM_FULLON		(2)
+
+/* Control REG 3 */
+#define AD7879_TEMPMASK_BIT		(1<<15)
+#define AD7879_AUXVBATMASK_BIT		(1<<14)
+#define AD7879_INTMODE_BIT		(1<<13)
+#define AD7879_GPIOALERTMASK_BIT	(1<<12)
+#define AD7879_AUXLOW_BIT		(1<<11)
+#define AD7879_AUXHIGH_BIT		(1<<10)
+#define AD7879_TEMPLOW_BIT		(1<<9)
+#define AD7879_TEMPHIGH_BIT		(1<<8)
+#define AD7879_YPLUS_BIT		(1<<7)
+#define AD7879_XPLUS_BIT		(1<<6)
+#define AD7879_Z1_BIT			(1<<5)
+#define AD7879_Z2_BIT			(1<<4)
+#define AD7879_AUX_BIT			(1<<3)
+#define AD7879_VBAT_BIT			(1<<2)
+#define AD7879_TEMP_BIT			(1<<1)
+
+enum {
+	AD7879_SEQ_XPOS  = 0,
+	AD7879_SEQ_YPOS  = 1,
+	AD7879_SEQ_Z1    = 2,
+	AD7879_SEQ_Z2    = 3,
+	AD7879_NR_SENSE  = 4,
+};
+
+#define	MAX_12BIT			((1<<12)-1)
+#define	TS_PEN_UP_TIMEOUT		msecs_to_jiffies(50)
+
+struct ad7879 {
+	const struct ad7879_bus_ops *bops;
+
+	struct device		*dev;
+	struct input_dev	*input;
+	struct timer_list	timer;
+#ifdef CONFIG_GPIOLIB
+	struct gpio_chip	gc;
+	struct mutex		mutex;
+#endif
+	unsigned int		irq;
+	bool			disabled;	/* P: input->mutex */
+	bool			suspended;	/* P: input->mutex */
+	bool			swap_xy;
+	u16			conversion_data[AD7879_NR_SENSE];
+	char			phys[32];
+	u8			first_conversion_delay;
+	u8			acquisition_time;
+	u8			averaging;
+	u8			pen_down_acc_interval;
+	u8			median;
+	u16			x_plate_ohms;
+	u16			pressure_max;
+	u16			cmd_crtl1;
+	u16			cmd_crtl2;
+	u16			cmd_crtl3;
+	int			x;
+	int			y;
+	int			Rt;
+};
+
+static int ad7879_read(struct ad7879 *ts, u8 reg)
+{
+	return ts->bops->read(ts->dev, reg);
+}
+
+static int ad7879_multi_read(struct ad7879 *ts, u8 first_reg, u8 count, u16 *buf)
+{
+	return ts->bops->multi_read(ts->dev, first_reg, count, buf);
+}
+
+static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val)
+{
+	return ts->bops->write(ts->dev, reg, val);
+}
+
+static int ad7879_report(struct ad7879 *ts)
+{
+	struct input_dev *input_dev = ts->input;
+	unsigned Rt;
+	u16 x, y, z1, z2;
+
+	x = ts->conversion_data[AD7879_SEQ_XPOS] & MAX_12BIT;
+	y = ts->conversion_data[AD7879_SEQ_YPOS] & MAX_12BIT;
+	z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT;
+	z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT;
+
+	if (ts->swap_xy)
+		swap(x, y);
+
+	/*
+	 * The samples processed here are already preprocessed by the AD7879.
+	 * The preprocessing function consists of a median and an averaging
+	 * filter.  The combination of these two techniques provides a robust
+	 * solution, discarding the spurious noise in the signal and keeping
+	 * only the data of interest.  The size of both filters is
+	 * programmable. (dev.platform_data, see linux/spi/ad7879.h) Other
+	 * user-programmable conversion controls include variable acquisition
+	 * time, and first conversion delay. Up to 16 averages can be taken
+	 * per conversion.
+	 */
+
+	if (likely(x && z1)) {
+		/* compute touch pressure resistance using equation #1 */
+		Rt = (z2 - z1) * x * ts->x_plate_ohms;
+		Rt /= z1;
+		Rt = (Rt + 2047) >> 12;
+
+		/*
+		 * Sample found inconsistent, pressure is beyond
+		 * the maximum. Don't report it to user space.
+		 */
+		if (Rt > ts->pressure_max)
+			return -EINVAL;
+
+		/*
+		 * Note that we delay reporting events by one sample.
+		 * This is done to avoid reporting last sample of the
+		 * touch sequence, which may be incomplete if finger
+		 * leaves the surface before last reading is taken.
+		 */
+		if (timer_pending(&ts->timer)) {
+			/* Touch continues */
+			input_report_key(input_dev, BTN_TOUCH, 1);
+			input_report_abs(input_dev, ABS_X, ts->x);
+			input_report_abs(input_dev, ABS_Y, ts->y);
+			input_report_abs(input_dev, ABS_PRESSURE, ts->Rt);
+			input_sync(input_dev);
+		}
+
+		ts->x = x;
+		ts->y = y;
+		ts->Rt = Rt;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static void ad7879_ts_event_release(struct ad7879 *ts)
+{
+	struct input_dev *input_dev = ts->input;
+
+	input_report_abs(input_dev, ABS_PRESSURE, 0);
+	input_report_key(input_dev, BTN_TOUCH, 0);
+	input_sync(input_dev);
+}
+
+static void ad7879_timer(unsigned long handle)
+{
+	struct ad7879 *ts = (void *)handle;
+
+	ad7879_ts_event_release(ts);
+}
+
+static irqreturn_t ad7879_irq(int irq, void *handle)
+{
+	struct ad7879 *ts = handle;
+
+	ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data);
+
+	if (!ad7879_report(ts))
+		mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
+
+	return IRQ_HANDLED;
+}
+
+static void __ad7879_enable(struct ad7879 *ts)
+{
+	ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);
+	ad7879_write(ts, AD7879_REG_CTRL3, ts->cmd_crtl3);
+	ad7879_write(ts, AD7879_REG_CTRL1, ts->cmd_crtl1);
+
+	enable_irq(ts->irq);
+}
+
+static void __ad7879_disable(struct ad7879 *ts)
+{
+	u16 reg = (ts->cmd_crtl2 & ~AD7879_PM(-1)) |
+		AD7879_PM(AD7879_PM_SHUTDOWN);
+	disable_irq(ts->irq);
+
+	if (del_timer_sync(&ts->timer))
+		ad7879_ts_event_release(ts);
+
+	ad7879_write(ts, AD7879_REG_CTRL2, reg);
+}
+
+
+static int ad7879_open(struct input_dev *input)
+{
+	struct ad7879 *ts = input_get_drvdata(input);
+
+	/* protected by input->mutex */
+	if (!ts->disabled && !ts->suspended)
+		__ad7879_enable(ts);
+
+	return 0;
+}
+
+static void ad7879_close(struct input_dev* input)
+{
+	struct ad7879 *ts = input_get_drvdata(input);
+
+	/* protected by input->mutex */
+	if (!ts->disabled && !ts->suspended)
+		__ad7879_disable(ts);
+}
+
+static int __maybe_unused ad7879_suspend(struct device *dev)
+{
+	struct ad7879 *ts = dev_get_drvdata(dev);
+
+	mutex_lock(&ts->input->mutex);
+
+	if (!ts->suspended && !ts->disabled && ts->input->users)
+		__ad7879_disable(ts);
+
+	ts->suspended = true;
+
+	mutex_unlock(&ts->input->mutex);
+
+	return 0;
+}
+
+static int __maybe_unused ad7879_resume(struct device *dev)
+{
+	struct ad7879 *ts = dev_get_drvdata(dev);
+
+	mutex_lock(&ts->input->mutex);
+
+	if (ts->suspended && !ts->disabled && ts->input->users)
+		__ad7879_enable(ts);
+
+	ts->suspended = false;
+
+	mutex_unlock(&ts->input->mutex);
+
+	return 0;
+}
+
+SIMPLE_DEV_PM_OPS(ad7879_pm_ops, ad7879_suspend, ad7879_resume);
+EXPORT_SYMBOL(ad7879_pm_ops);
+
+static void ad7879_toggle(struct ad7879 *ts, bool disable)
+{
+	mutex_lock(&ts->input->mutex);
+
+	if (!ts->suspended && ts->input->users != 0) {
+
+		if (disable) {
+			if (ts->disabled)
+				__ad7879_enable(ts);
+		} else {
+			if (!ts->disabled)
+				__ad7879_disable(ts);
+		}
+	}
+
+	ts->disabled = disable;
+
+	mutex_unlock(&ts->input->mutex);
+}
+
+static ssize_t ad7879_disable_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct ad7879 *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t ad7879_disable_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct ad7879 *ts = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	ad7879_toggle(ts, val);
+
+	return count;
+}
+
+static DEVICE_ATTR(disable, 0664, ad7879_disable_show, ad7879_disable_store);
+
+static struct attribute *ad7879_attributes[] = {
+	&dev_attr_disable.attr,
+	NULL
+};
+
+static const struct attribute_group ad7879_attr_group = {
+	.attrs = ad7879_attributes,
+};
+
+#ifdef CONFIG_GPIOLIB
+static int ad7879_gpio_direction_input(struct gpio_chip *chip,
+					unsigned gpio)
+{
+	struct ad7879 *ts = container_of(chip, struct ad7879, gc);
+	int err;
+
+	mutex_lock(&ts->mutex);
+	ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIODIR | AD7879_GPIOPOL;
+	err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);
+	mutex_unlock(&ts->mutex);
+
+	return err;
+}
+
+static int ad7879_gpio_direction_output(struct gpio_chip *chip,
+					unsigned gpio, int level)
+{
+	struct ad7879 *ts = container_of(chip, struct ad7879, gc);
+	int err;
+
+	mutex_lock(&ts->mutex);
+	ts->cmd_crtl2 &= ~AD7879_GPIODIR;
+	ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIOPOL;
+	if (level)
+		ts->cmd_crtl2 |= AD7879_GPIO_DATA;
+	else
+		ts->cmd_crtl2 &= ~AD7879_GPIO_DATA;
+
+	err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);
+	mutex_unlock(&ts->mutex);
+
+	return err;
+}
+
+static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+	struct ad7879 *ts = container_of(chip, struct ad7879, gc);
+	u16 val;
+
+	mutex_lock(&ts->mutex);
+	val = ad7879_read(ts, AD7879_REG_CTRL2);
+	mutex_unlock(&ts->mutex);
+
+	return !!(val & AD7879_GPIO_DATA);
+}
+
+static void ad7879_gpio_set_value(struct gpio_chip *chip,
+				  unsigned gpio, int value)
+{
+	struct ad7879 *ts = container_of(chip, struct ad7879, gc);
+
+	mutex_lock(&ts->mutex);
+	if (value)
+		ts->cmd_crtl2 |= AD7879_GPIO_DATA;
+	else
+		ts->cmd_crtl2 &= ~AD7879_GPIO_DATA;
+
+	ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);
+	mutex_unlock(&ts->mutex);
+}
+
+static int ad7879_gpio_add(struct ad7879 *ts,
+			   const struct ad7879_platform_data *pdata)
+{
+	int ret = 0;
+
+	mutex_init(&ts->mutex);
+
+	if (pdata->gpio_export) {
+		ts->gc.direction_input = ad7879_gpio_direction_input;
+		ts->gc.direction_output = ad7879_gpio_direction_output;
+		ts->gc.get = ad7879_gpio_get_value;
+		ts->gc.set = ad7879_gpio_set_value;
+		ts->gc.can_sleep = 1;
+		ts->gc.base = pdata->gpio_base;
+		ts->gc.ngpio = 1;
+		ts->gc.label = "AD7879-GPIO";
+		ts->gc.owner = THIS_MODULE;
+		ts->gc.dev = ts->dev;
+
+		ret = gpiochip_add(&ts->gc);
+		if (ret)
+			dev_err(ts->dev, "failed to register gpio %d\n",
+				ts->gc.base);
+	}
+
+	return ret;
+}
+
+static void ad7879_gpio_remove(struct ad7879 *ts)
+{
+	const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev);
+
+	if (pdata->gpio_export)
+		gpiochip_remove(&ts->gc);
+
+}
+#else
+static inline int ad7879_gpio_add(struct ad7879 *ts,
+				  const struct ad7879_platform_data *pdata)
+{
+	return 0;
+}
+
+static inline void ad7879_gpio_remove(struct ad7879 *ts)
+{
+}
+#endif
+
+struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
+			    const struct ad7879_bus_ops *bops)
+{
+	struct ad7879_platform_data *pdata = dev_get_platdata(dev);
+	struct ad7879 *ts;
+	struct input_dev *input_dev;
+	int err;
+	u16 revid;
+
+	if (!irq) {
+		dev_err(dev, "no IRQ?\n");
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	if (!pdata) {
+		dev_err(dev, "no platform data?\n");
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!ts || !input_dev) {
+		err = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	ts->bops = bops;
+	ts->dev = dev;
+	ts->input = input_dev;
+	ts->irq = irq;
+	ts->swap_xy = pdata->swap_xy;
+
+	setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);
+
+	ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+	ts->pressure_max = pdata->pressure_max ? : ~0;
+
+	ts->first_conversion_delay = pdata->first_conversion_delay;
+	ts->acquisition_time = pdata->acquisition_time;
+	ts->averaging = pdata->averaging;
+	ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
+	ts->median = pdata->median;
+
+	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
+
+	input_dev->name = "AD7879 Touchscreen";
+	input_dev->phys = ts->phys;
+	input_dev->dev.parent = dev;
+	input_dev->id.bustype = bops->bustype;
+
+	input_dev->open = ad7879_open;
+	input_dev->close = ad7879_close;
+
+	input_set_drvdata(input_dev, ts);
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(ABS_X, input_dev->absbit);
+	__set_bit(ABS_Y, input_dev->absbit);
+	__set_bit(ABS_PRESSURE, input_dev->absbit);
+
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+
+	input_set_abs_params(input_dev, ABS_X,
+			pdata->x_min ? : 0,
+			pdata->x_max ? : MAX_12BIT,
+			0, 0);
+	input_set_abs_params(input_dev, ABS_Y,
+			pdata->y_min ? : 0,
+			pdata->y_max ? : MAX_12BIT,
+			0, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE,
+			pdata->pressure_min, pdata->pressure_max, 0, 0);
+
+	err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET);
+	if (err < 0) {
+		dev_err(dev, "Failed to write %s\n", input_dev->name);
+		goto err_free_mem;
+	}
+
+	revid = ad7879_read(ts, AD7879_REG_REVID);
+	input_dev->id.product = (revid & 0xff);
+	input_dev->id.version = revid >> 8;
+	if (input_dev->id.product != devid) {
+		dev_err(dev, "Failed to probe %s (%x vs %x)\n",
+			input_dev->name, devid, revid);
+		err = -ENODEV;
+		goto err_free_mem;
+	}
+
+	ts->cmd_crtl3 = AD7879_YPLUS_BIT |
+			AD7879_XPLUS_BIT |
+			AD7879_Z2_BIT |
+			AD7879_Z1_BIT |
+			AD7879_TEMPMASK_BIT |
+			AD7879_AUXVBATMASK_BIT |
+			AD7879_GPIOALERTMASK_BIT;
+
+	ts->cmd_crtl2 = AD7879_PM(AD7879_PM_DYN) | AD7879_DFR |
+			AD7879_AVG(ts->averaging) |
+			AD7879_MFS(ts->median) |
+			AD7879_FCD(ts->first_conversion_delay);
+
+	ts->cmd_crtl1 = AD7879_MODE_INT | AD7879_MODE_SEQ1 |
+			AD7879_ACQ(ts->acquisition_time) |
+			AD7879_TMR(ts->pen_down_acc_interval);
+
+	err = request_threaded_irq(ts->irq, NULL, ad7879_irq,
+				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				   dev_name(dev), ts);
+	if (err) {
+		dev_err(dev, "irq %d busy?\n", ts->irq);
+		goto err_free_mem;
+	}
+
+	__ad7879_disable(ts);
+
+	err = sysfs_create_group(&dev->kobj, &ad7879_attr_group);
+	if (err)
+		goto err_free_irq;
+
+	err = ad7879_gpio_add(ts, pdata);
+	if (err)
+		goto err_remove_attr;
+
+	err = input_register_device(input_dev);
+	if (err)
+		goto err_remove_gpio;
+
+	return ts;
+
+err_remove_gpio:
+	ad7879_gpio_remove(ts);
+err_remove_attr:
+	sysfs_remove_group(&dev->kobj, &ad7879_attr_group);
+err_free_irq:
+	free_irq(ts->irq, ts);
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(ts);
+err_out:
+	return ERR_PTR(err);
+}
+EXPORT_SYMBOL(ad7879_probe);
+
+void ad7879_remove(struct ad7879 *ts)
+{
+	ad7879_gpio_remove(ts);
+	sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
+	free_irq(ts->irq, ts);
+	input_unregister_device(ts->input);
+	kfree(ts);
+}
+EXPORT_SYMBOL(ad7879_remove);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ad7879.h b/drivers/input/touchscreen/ad7879.h
new file mode 100644
index 0000000..6fd13c4
--- /dev/null
+++ b/drivers/input/touchscreen/ad7879.h
@@ -0,0 +1,30 @@
+/*
+ * AD7879/AD7889 touchscreen (bus interfaces)
+ *
+ * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _AD7879_H_
+#define _AD7879_H_
+
+#include <linux/types.h>
+
+struct ad7879;
+struct device;
+
+struct ad7879_bus_ops {
+	u16 bustype;
+	int (*read)(struct device *dev, u8 reg);
+	int (*multi_read)(struct device *dev, u8 first_reg, u8 count, u16 *buf);
+	int (*write)(struct device *dev, u8 reg, u16 val);
+};
+
+extern const struct dev_pm_ops ad7879_pm_ops;
+
+struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq,
+			    const struct ad7879_bus_ops *bops);
+void ad7879_remove(struct ad7879 *);
+
+#endif
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
new file mode 100644
index 0000000..a61b215
--- /dev/null
+++ b/drivers/input/touchscreen/ads7846.c
@@ -0,0 +1,1512 @@
+/*
+ * ADS7846 based touchscreen and sensor driver
+ *
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * Various changes: Imre Deak <imre.deak@nokia.com>
+ *
+ * Using code from:
+ *  - corgi_ts.c
+ *	Copyright (C) 2004-2005 Richard Purdie
+ *  - omap_ts.[hc], ads7846.h, ts_osk.c
+ *	Copyright (C) 2002 MontaVista Software
+ *	Copyright (C) 2004 Texas Instruments
+ *	Copyright (C) 2005 Dirk Behme
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/hwmon.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+#include <asm/irq.h>
+
+/*
+ * This code has been heavily tested on a Nokia 770, and lightly
+ * tested on other ads7846 devices (OSK/Mistral, Lubbock, Spitz).
+ * TSC2046 is just newer ads7846 silicon.
+ * Support for ads7843 tested on Atmel at91sam926x-EK.
+ * Support for ads7845 has only been stubbed in.
+ * Support for Analog Devices AD7873 and AD7843 tested.
+ *
+ * IRQ handling needs a workaround because of a shortcoming in handling
+ * edge triggered IRQs on some platforms like the OMAP1/2. These
+ * platforms don't handle the ARM lazy IRQ disabling properly, thus we
+ * have to maintain our own SW IRQ disabled status. This should be
+ * removed as soon as the affected platform's IRQ handling is fixed.
+ *
+ * App note sbaa036 talks in more detail about accurate sampling...
+ * that ought to help in situations like LCDs inducing noise (which
+ * can also be helped by using synch signals) and more generally.
+ * This driver tries to utilize the measures described in the app
+ * note. The strength of filtering can be set in the board-* specific
+ * files.
+ */
+
+#define TS_POLL_DELAY	1	/* ms delay before the first sample */
+#define TS_POLL_PERIOD	5	/* ms delay between samples */
+
+/* this driver doesn't aim at the peak continuous sample rate */
+#define	SAMPLE_BITS	(8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */)
+
+struct ts_event {
+	/*
+	 * For portability, we can't read 12 bit values using SPI (which
+	 * would make the controller deliver them as native byte order u16
+	 * with msbs zeroed).  Instead, we read them as two 8-bit values,
+	 * *** WHICH NEED BYTESWAPPING *** and range adjustment.
+	 */
+	u16	x;
+	u16	y;
+	u16	z1, z2;
+	bool	ignore;
+	u8	x_buf[3];
+	u8	y_buf[3];
+};
+
+/*
+ * We allocate this separately to avoid cache line sharing issues when
+ * driver is used with DMA-based SPI controllers (like atmel_spi) on
+ * systems where main memory is not DMA-coherent (most non-x86 boards).
+ */
+struct ads7846_packet {
+	u8			read_x, read_y, read_z1, read_z2, pwrdown;
+	u16			dummy;		/* for the pwrdown read */
+	struct ts_event		tc;
+	/* for ads7845 with mpc5121 psc spi we use 3-byte buffers */
+	u8			read_x_cmd[3], read_y_cmd[3], pwrdown_cmd[3];
+};
+
+struct ads7846 {
+	struct input_dev	*input;
+	char			phys[32];
+	char			name[32];
+
+	struct spi_device	*spi;
+	struct regulator	*reg;
+
+#if IS_ENABLED(CONFIG_HWMON)
+	struct device		*hwmon;
+#endif
+
+	u16			model;
+	u16			vref_mv;
+	u16			vref_delay_usecs;
+	u16			x_plate_ohms;
+	u16			pressure_max;
+
+	bool			swap_xy;
+	bool			use_internal;
+
+	struct ads7846_packet	*packet;
+
+	struct spi_transfer	xfer[18];
+	struct spi_message	msg[5];
+	int			msg_count;
+	wait_queue_head_t	wait;
+
+	bool			pendown;
+
+	int			read_cnt;
+	int			read_rep;
+	int			last_read;
+
+	u16			debounce_max;
+	u16			debounce_tol;
+	u16			debounce_rep;
+
+	u16			penirq_recheck_delay_usecs;
+
+	struct mutex		lock;
+	bool			stopped;	/* P: lock */
+	bool			disabled;	/* P: lock */
+	bool			suspended;	/* P: lock */
+
+	int			(*filter)(void *data, int data_idx, int *val);
+	void			*filter_data;
+	void			(*filter_cleanup)(void *data);
+	int			(*get_pendown_state)(void);
+	int			gpio_pendown;
+
+	void			(*wait_for_sync)(void);
+};
+
+/* leave chip selected when we're done, for quicker re-select? */
+#if	0
+#define	CS_CHANGE(xfer)	((xfer).cs_change = 1)
+#else
+#define	CS_CHANGE(xfer)	((xfer).cs_change = 0)
+#endif
+
+/*--------------------------------------------------------------------------*/
+
+/* The ADS7846 has touchscreen and other sensors.
+ * Earlier ads784x chips are somewhat compatible.
+ */
+#define	ADS_START		(1 << 7)
+#define	ADS_A2A1A0_d_y		(1 << 4)	/* differential */
+#define	ADS_A2A1A0_d_z1		(3 << 4)	/* differential */
+#define	ADS_A2A1A0_d_z2		(4 << 4)	/* differential */
+#define	ADS_A2A1A0_d_x		(5 << 4)	/* differential */
+#define	ADS_A2A1A0_temp0	(0 << 4)	/* non-differential */
+#define	ADS_A2A1A0_vbatt	(2 << 4)	/* non-differential */
+#define	ADS_A2A1A0_vaux		(6 << 4)	/* non-differential */
+#define	ADS_A2A1A0_temp1	(7 << 4)	/* non-differential */
+#define	ADS_8_BIT		(1 << 3)
+#define	ADS_12_BIT		(0 << 3)
+#define	ADS_SER			(1 << 2)	/* non-differential */
+#define	ADS_DFR			(0 << 2)	/* differential */
+#define	ADS_PD10_PDOWN		(0 << 0)	/* low power mode + penirq */
+#define	ADS_PD10_ADC_ON		(1 << 0)	/* ADC on */
+#define	ADS_PD10_REF_ON		(2 << 0)	/* vREF on + penirq */
+#define	ADS_PD10_ALL_ON		(3 << 0)	/* ADC + vREF on */
+
+#define	MAX_12BIT	((1<<12)-1)
+
+/* leave ADC powered up (disables penirq) between differential samples */
+#define	READ_12BIT_DFR(x, adc, vref) (ADS_START | ADS_A2A1A0_d_ ## x \
+	| ADS_12_BIT | ADS_DFR | \
+	(adc ? ADS_PD10_ADC_ON : 0) | (vref ? ADS_PD10_REF_ON : 0))
+
+#define	READ_Y(vref)	(READ_12BIT_DFR(y,  1, vref))
+#define	READ_Z1(vref)	(READ_12BIT_DFR(z1, 1, vref))
+#define	READ_Z2(vref)	(READ_12BIT_DFR(z2, 1, vref))
+
+#define	READ_X(vref)	(READ_12BIT_DFR(x,  1, vref))
+#define	PWRDOWN		(READ_12BIT_DFR(y,  0, 0))	/* LAST */
+
+/* single-ended samples need to first power up reference voltage;
+ * we leave both ADC and VREF powered
+ */
+#define	READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \
+	| ADS_12_BIT | ADS_SER)
+
+#define	REF_ON	(READ_12BIT_DFR(x, 1, 1))
+#define	REF_OFF	(READ_12BIT_DFR(y, 0, 0))
+
+/* Must be called with ts->lock held */
+static void ads7846_stop(struct ads7846 *ts)
+{
+	if (!ts->disabled && !ts->suspended) {
+		/* Signal IRQ thread to stop polling and disable the handler. */
+		ts->stopped = true;
+		mb();
+		wake_up(&ts->wait);
+		disable_irq(ts->spi->irq);
+	}
+}
+
+/* Must be called with ts->lock held */
+static void ads7846_restart(struct ads7846 *ts)
+{
+	if (!ts->disabled && !ts->suspended) {
+		/* Tell IRQ thread that it may poll the device. */
+		ts->stopped = false;
+		mb();
+		enable_irq(ts->spi->irq);
+	}
+}
+
+/* Must be called with ts->lock held */
+static void __ads7846_disable(struct ads7846 *ts)
+{
+	ads7846_stop(ts);
+	regulator_disable(ts->reg);
+
+	/*
+	 * We know the chip's in low power mode since we always
+	 * leave it that way after every request
+	 */
+}
+
+/* Must be called with ts->lock held */
+static void __ads7846_enable(struct ads7846 *ts)
+{
+	int error;
+
+	error = regulator_enable(ts->reg);
+	if (error != 0)
+		dev_err(&ts->spi->dev, "Failed to enable supply: %d\n", error);
+
+	ads7846_restart(ts);
+}
+
+static void ads7846_disable(struct ads7846 *ts)
+{
+	mutex_lock(&ts->lock);
+
+	if (!ts->disabled) {
+
+		if  (!ts->suspended)
+			__ads7846_disable(ts);
+
+		ts->disabled = true;
+	}
+
+	mutex_unlock(&ts->lock);
+}
+
+static void ads7846_enable(struct ads7846 *ts)
+{
+	mutex_lock(&ts->lock);
+
+	if (ts->disabled) {
+
+		ts->disabled = false;
+
+		if (!ts->suspended)
+			__ads7846_enable(ts);
+	}
+
+	mutex_unlock(&ts->lock);
+}
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Non-touchscreen sensors only use single-ended conversions.
+ * The range is GND..vREF. The ads7843 and ads7835 must use external vREF;
+ * ads7846 lets that pin be unconnected, to use internal vREF.
+ */
+
+struct ser_req {
+	u8			ref_on;
+	u8			command;
+	u8			ref_off;
+	u16			scratch;
+	struct spi_message	msg;
+	struct spi_transfer	xfer[6];
+	/*
+	 * DMA (thus cache coherency maintenance) requires the
+	 * transfer buffers to live in their own cache lines.
+	 */
+	__be16 sample ____cacheline_aligned;
+};
+
+struct ads7845_ser_req {
+	u8			command[3];
+	struct spi_message	msg;
+	struct spi_transfer	xfer[2];
+	/*
+	 * DMA (thus cache coherency maintenance) requires the
+	 * transfer buffers to live in their own cache lines.
+	 */
+	u8 sample[3] ____cacheline_aligned;
+};
+
+static int ads7846_read12_ser(struct device *dev, unsigned command)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct ads7846 *ts = dev_get_drvdata(dev);
+	struct ser_req *req;
+	int status;
+
+	req = kzalloc(sizeof *req, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	spi_message_init(&req->msg);
+
+	/* maybe turn on internal vREF, and let it settle */
+	if (ts->use_internal) {
+		req->ref_on = REF_ON;
+		req->xfer[0].tx_buf = &req->ref_on;
+		req->xfer[0].len = 1;
+		spi_message_add_tail(&req->xfer[0], &req->msg);
+
+		req->xfer[1].rx_buf = &req->scratch;
+		req->xfer[1].len = 2;
+
+		/* for 1uF, settle for 800 usec; no cap, 100 usec.  */
+		req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+		spi_message_add_tail(&req->xfer[1], &req->msg);
+
+		/* Enable reference voltage */
+		command |= ADS_PD10_REF_ON;
+	}
+
+	/* Enable ADC in every case */
+	command |= ADS_PD10_ADC_ON;
+
+	/* take sample */
+	req->command = (u8) command;
+	req->xfer[2].tx_buf = &req->command;
+	req->xfer[2].len = 1;
+	spi_message_add_tail(&req->xfer[2], &req->msg);
+
+	req->xfer[3].rx_buf = &req->sample;
+	req->xfer[3].len = 2;
+	spi_message_add_tail(&req->xfer[3], &req->msg);
+
+	/* REVISIT:  take a few more samples, and compare ... */
+
+	/* converter in low power mode & enable PENIRQ */
+	req->ref_off = PWRDOWN;
+	req->xfer[4].tx_buf = &req->ref_off;
+	req->xfer[4].len = 1;
+	spi_message_add_tail(&req->xfer[4], &req->msg);
+
+	req->xfer[5].rx_buf = &req->scratch;
+	req->xfer[5].len = 2;
+	CS_CHANGE(req->xfer[5]);
+	spi_message_add_tail(&req->xfer[5], &req->msg);
+
+	mutex_lock(&ts->lock);
+	ads7846_stop(ts);
+	status = spi_sync(spi, &req->msg);
+	ads7846_restart(ts);
+	mutex_unlock(&ts->lock);
+
+	if (status == 0) {
+		/* on-wire is a must-ignore bit, a BE12 value, then padding */
+		status = be16_to_cpu(req->sample);
+		status = status >> 3;
+		status &= 0x0fff;
+	}
+
+	kfree(req);
+	return status;
+}
+
+static int ads7845_read12_ser(struct device *dev, unsigned command)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct ads7846 *ts = dev_get_drvdata(dev);
+	struct ads7845_ser_req *req;
+	int status;
+
+	req = kzalloc(sizeof *req, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	spi_message_init(&req->msg);
+
+	req->command[0] = (u8) command;
+	req->xfer[0].tx_buf = req->command;
+	req->xfer[0].rx_buf = req->sample;
+	req->xfer[0].len = 3;
+	spi_message_add_tail(&req->xfer[0], &req->msg);
+
+	mutex_lock(&ts->lock);
+	ads7846_stop(ts);
+	status = spi_sync(spi, &req->msg);
+	ads7846_restart(ts);
+	mutex_unlock(&ts->lock);
+
+	if (status == 0) {
+		/* BE12 value, then padding */
+		status = be16_to_cpu(*((u16 *)&req->sample[1]));
+		status = status >> 3;
+		status &= 0x0fff;
+	}
+
+	kfree(req);
+	return status;
+}
+
+#if IS_ENABLED(CONFIG_HWMON)
+
+#define SHOW(name, var, adjust) static ssize_t \
+name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+	struct ads7846 *ts = dev_get_drvdata(dev); \
+	ssize_t v = ads7846_read12_ser(&ts->spi->dev, \
+			READ_12BIT_SER(var)); \
+	if (v < 0) \
+		return v; \
+	return sprintf(buf, "%u\n", adjust(ts, v)); \
+} \
+static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);
+
+
+/* Sysfs conventions report temperatures in millidegrees Celsius.
+ * ADS7846 could use the low-accuracy two-sample scheme, but can't do the high
+ * accuracy scheme without calibration data.  For now we won't try either;
+ * userspace sees raw sensor values, and must scale/calibrate appropriately.
+ */
+static inline unsigned null_adjust(struct ads7846 *ts, ssize_t v)
+{
+	return v;
+}
+
+SHOW(temp0, temp0, null_adjust)		/* temp1_input */
+SHOW(temp1, temp1, null_adjust)		/* temp2_input */
+
+
+/* sysfs conventions report voltages in millivolts.  We can convert voltages
+ * if we know vREF.  userspace may need to scale vAUX to match the board's
+ * external resistors; we assume that vBATT only uses the internal ones.
+ */
+static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v)
+{
+	unsigned retval = v;
+
+	/* external resistors may scale vAUX into 0..vREF */
+	retval *= ts->vref_mv;
+	retval = retval >> 12;
+
+	return retval;
+}
+
+static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v)
+{
+	unsigned retval = vaux_adjust(ts, v);
+
+	/* ads7846 has a resistor ladder to scale this signal down */
+	if (ts->model == 7846)
+		retval *= 4;
+
+	return retval;
+}
+
+SHOW(in0_input, vaux, vaux_adjust)
+SHOW(in1_input, vbatt, vbatt_adjust)
+
+static umode_t ads7846_is_visible(struct kobject *kobj, struct attribute *attr,
+				  int index)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct ads7846 *ts = dev_get_drvdata(dev);
+
+	if (ts->model == 7843 && index < 2)	/* in0, in1 */
+		return 0;
+	if (ts->model == 7845 && index != 2)	/* in0 */
+		return 0;
+
+	return attr->mode;
+}
+
+static struct attribute *ads7846_attributes[] = {
+	&dev_attr_temp0.attr,		/* 0 */
+	&dev_attr_temp1.attr,		/* 1 */
+	&dev_attr_in0_input.attr,	/* 2 */
+	&dev_attr_in1_input.attr,	/* 3 */
+	NULL,
+};
+
+static struct attribute_group ads7846_attr_group = {
+	.attrs = ads7846_attributes,
+	.is_visible = ads7846_is_visible,
+};
+__ATTRIBUTE_GROUPS(ads7846_attr);
+
+static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
+{
+	/* hwmon sensors need a reference voltage */
+	switch (ts->model) {
+	case 7846:
+		if (!ts->vref_mv) {
+			dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n");
+			ts->vref_mv = 2500;
+			ts->use_internal = true;
+		}
+		break;
+	case 7845:
+	case 7843:
+		if (!ts->vref_mv) {
+			dev_warn(&spi->dev,
+				"external vREF for ADS%d not specified\n",
+				ts->model);
+			return 0;
+		}
+		break;
+	}
+
+	ts->hwmon = hwmon_device_register_with_groups(&spi->dev, spi->modalias,
+						      ts, ads7846_attr_groups);
+
+	return PTR_ERR_OR_ZERO(ts->hwmon);
+}
+
+static void ads784x_hwmon_unregister(struct spi_device *spi,
+				     struct ads7846 *ts)
+{
+	if (ts->hwmon)
+		hwmon_device_unregister(ts->hwmon);
+}
+
+#else
+static inline int ads784x_hwmon_register(struct spi_device *spi,
+					 struct ads7846 *ts)
+{
+	return 0;
+}
+
+static inline void ads784x_hwmon_unregister(struct spi_device *spi,
+					    struct ads7846 *ts)
+{
+}
+#endif
+
+static ssize_t ads7846_pen_down_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct ads7846 *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", ts->pendown);
+}
+
+static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL);
+
+static ssize_t ads7846_disable_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct ads7846 *ts = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t ads7846_disable_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct ads7846 *ts = dev_get_drvdata(dev);
+	unsigned int i;
+	int err;
+
+	err = kstrtouint(buf, 10, &i);
+	if (err)
+		return err;
+
+	if (i)
+		ads7846_disable(ts);
+	else
+		ads7846_enable(ts);
+
+	return count;
+}
+
+static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store);
+
+static struct attribute *ads784x_attributes[] = {
+	&dev_attr_pen_down.attr,
+	&dev_attr_disable.attr,
+	NULL,
+};
+
+static struct attribute_group ads784x_attr_group = {
+	.attrs = ads784x_attributes,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int get_pendown_state(struct ads7846 *ts)
+{
+	if (ts->get_pendown_state)
+		return ts->get_pendown_state();
+
+	return !gpio_get_value(ts->gpio_pendown);
+}
+
+static void null_wait_for_sync(void)
+{
+}
+
+static int ads7846_debounce_filter(void *ads, int data_idx, int *val)
+{
+	struct ads7846 *ts = ads;
+
+	if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) {
+		/* Start over collecting consistent readings. */
+		ts->read_rep = 0;
+		/*
+		 * Repeat it, if this was the first read or the read
+		 * wasn't consistent enough.
+		 */
+		if (ts->read_cnt < ts->debounce_max) {
+			ts->last_read = *val;
+			ts->read_cnt++;
+			return ADS7846_FILTER_REPEAT;
+		} else {
+			/*
+			 * Maximum number of debouncing reached and still
+			 * not enough number of consistent readings. Abort
+			 * the whole sample, repeat it in the next sampling
+			 * period.
+			 */
+			ts->read_cnt = 0;
+			return ADS7846_FILTER_IGNORE;
+		}
+	} else {
+		if (++ts->read_rep > ts->debounce_rep) {
+			/*
+			 * Got a good reading for this coordinate,
+			 * go for the next one.
+			 */
+			ts->read_cnt = 0;
+			ts->read_rep = 0;
+			return ADS7846_FILTER_OK;
+		} else {
+			/* Read more values that are consistent. */
+			ts->read_cnt++;
+			return ADS7846_FILTER_REPEAT;
+		}
+	}
+}
+
+static int ads7846_no_filter(void *ads, int data_idx, int *val)
+{
+	return ADS7846_FILTER_OK;
+}
+
+static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m)
+{
+	int value;
+	struct spi_transfer *t =
+		list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
+
+	if (ts->model == 7845) {
+		value = be16_to_cpup((__be16 *)&(((char *)t->rx_buf)[1]));
+	} else {
+		/*
+		 * adjust:  on-wire is a must-ignore bit, a BE12 value, then
+		 * padding; built from two 8 bit values written msb-first.
+		 */
+		value = be16_to_cpup((__be16 *)t->rx_buf);
+	}
+
+	/* enforce ADC output is 12 bits width */
+	return (value >> 3) & 0xfff;
+}
+
+static void ads7846_update_value(struct spi_message *m, int val)
+{
+	struct spi_transfer *t =
+		list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
+
+	*(u16 *)t->rx_buf = val;
+}
+
+static void ads7846_read_state(struct ads7846 *ts)
+{
+	struct ads7846_packet *packet = ts->packet;
+	struct spi_message *m;
+	int msg_idx = 0;
+	int val;
+	int action;
+	int error;
+
+	while (msg_idx < ts->msg_count) {
+
+		ts->wait_for_sync();
+
+		m = &ts->msg[msg_idx];
+		error = spi_sync(ts->spi, m);
+		if (error) {
+			dev_err(&ts->spi->dev, "spi_sync --> %d\n", error);
+			packet->tc.ignore = true;
+			return;
+		}
+
+		/*
+		 * Last message is power down request, no need to convert
+		 * or filter the value.
+		 */
+		if (msg_idx < ts->msg_count - 1) {
+
+			val = ads7846_get_value(ts, m);
+
+			action = ts->filter(ts->filter_data, msg_idx, &val);
+			switch (action) {
+			case ADS7846_FILTER_REPEAT:
+				continue;
+
+			case ADS7846_FILTER_IGNORE:
+				packet->tc.ignore = true;
+				msg_idx = ts->msg_count - 1;
+				continue;
+
+			case ADS7846_FILTER_OK:
+				ads7846_update_value(m, val);
+				packet->tc.ignore = false;
+				msg_idx++;
+				break;
+
+			default:
+				BUG();
+			}
+		} else {
+			msg_idx++;
+		}
+	}
+}
+
+static void ads7846_report_state(struct ads7846 *ts)
+{
+	struct ads7846_packet *packet = ts->packet;
+	unsigned int Rt;
+	u16 x, y, z1, z2;
+
+	/*
+	 * ads7846_get_value() does in-place conversion (including byte swap)
+	 * from on-the-wire format as part of debouncing to get stable
+	 * readings.
+	 */
+	if (ts->model == 7845) {
+		x = *(u16 *)packet->tc.x_buf;
+		y = *(u16 *)packet->tc.y_buf;
+		z1 = 0;
+		z2 = 0;
+	} else {
+		x = packet->tc.x;
+		y = packet->tc.y;
+		z1 = packet->tc.z1;
+		z2 = packet->tc.z2;
+	}
+
+	/* range filtering */
+	if (x == MAX_12BIT)
+		x = 0;
+
+	if (ts->model == 7843) {
+		Rt = ts->pressure_max / 2;
+	} else if (ts->model == 7845) {
+		if (get_pendown_state(ts))
+			Rt = ts->pressure_max / 2;
+		else
+			Rt = 0;
+		dev_vdbg(&ts->spi->dev, "x/y: %d/%d, PD %d\n", x, y, Rt);
+	} else if (likely(x && z1)) {
+		/* compute touch pressure resistance using equation #2 */
+		Rt = z2;
+		Rt -= z1;
+		Rt *= x;
+		Rt *= ts->x_plate_ohms;
+		Rt /= z1;
+		Rt = (Rt + 2047) >> 12;
+	} else {
+		Rt = 0;
+	}
+
+	/*
+	 * Sample found inconsistent by debouncing or pressure is beyond
+	 * the maximum. Don't report it to user space, repeat at least
+	 * once more the measurement
+	 */
+	if (packet->tc.ignore || Rt > ts->pressure_max) {
+		dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n",
+			 packet->tc.ignore, Rt);
+		return;
+	}
+
+	/*
+	 * Maybe check the pendown state before reporting. This discards
+	 * false readings when the pen is lifted.
+	 */
+	if (ts->penirq_recheck_delay_usecs) {
+		udelay(ts->penirq_recheck_delay_usecs);
+		if (!get_pendown_state(ts))
+			Rt = 0;
+	}
+
+	/*
+	 * NOTE: We can't rely on the pressure to determine the pen down
+	 * state, even this controller has a pressure sensor. The pressure
+	 * value can fluctuate for quite a while after lifting the pen and
+	 * in some cases may not even settle at the expected value.
+	 *
+	 * The only safe way to check for the pen up condition is in the
+	 * timer by reading the pen signal state (it's a GPIO _and_ IRQ).
+	 */
+	if (Rt) {
+		struct input_dev *input = ts->input;
+
+		if (ts->swap_xy)
+			swap(x, y);
+
+		if (!ts->pendown) {
+			input_report_key(input, BTN_TOUCH, 1);
+			ts->pendown = true;
+			dev_vdbg(&ts->spi->dev, "DOWN\n");
+		}
+
+		input_report_abs(input, ABS_X, x);
+		input_report_abs(input, ABS_Y, y);
+		input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt);
+
+		input_sync(input);
+		dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
+	}
+}
+
+static irqreturn_t ads7846_hard_irq(int irq, void *handle)
+{
+	struct ads7846 *ts = handle;
+
+	return get_pendown_state(ts) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+}
+
+
+static irqreturn_t ads7846_irq(int irq, void *handle)
+{
+	struct ads7846 *ts = handle;
+
+	/* Start with a small delay before checking pendown state */
+	msleep(TS_POLL_DELAY);
+
+	while (!ts->stopped && get_pendown_state(ts)) {
+
+		/* pen is down, continue with the measurement */
+		ads7846_read_state(ts);
+
+		if (!ts->stopped)
+			ads7846_report_state(ts);
+
+		wait_event_timeout(ts->wait, ts->stopped,
+				   msecs_to_jiffies(TS_POLL_PERIOD));
+	}
+
+	if (ts->pendown) {
+		struct input_dev *input = ts->input;
+
+		input_report_key(input, BTN_TOUCH, 0);
+		input_report_abs(input, ABS_PRESSURE, 0);
+		input_sync(input);
+
+		ts->pendown = false;
+		dev_vdbg(&ts->spi->dev, "UP\n");
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int __maybe_unused ads7846_suspend(struct device *dev)
+{
+	struct ads7846 *ts = dev_get_drvdata(dev);
+
+	mutex_lock(&ts->lock);
+
+	if (!ts->suspended) {
+
+		if (!ts->disabled)
+			__ads7846_disable(ts);
+
+		if (device_may_wakeup(&ts->spi->dev))
+			enable_irq_wake(ts->spi->irq);
+
+		ts->suspended = true;
+	}
+
+	mutex_unlock(&ts->lock);
+
+	return 0;
+}
+
+static int __maybe_unused ads7846_resume(struct device *dev)
+{
+	struct ads7846 *ts = dev_get_drvdata(dev);
+
+	mutex_lock(&ts->lock);
+
+	if (ts->suspended) {
+
+		ts->suspended = false;
+
+		if (device_may_wakeup(&ts->spi->dev))
+			disable_irq_wake(ts->spi->irq);
+
+		if (!ts->disabled)
+			__ads7846_enable(ts);
+	}
+
+	mutex_unlock(&ts->lock);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ads7846_pm, ads7846_suspend, ads7846_resume);
+
+static int ads7846_setup_pendown(struct spi_device *spi,
+				 struct ads7846 *ts,
+				 const struct ads7846_platform_data *pdata)
+{
+	int err;
+
+	/*
+	 * REVISIT when the irq can be triggered active-low, or if for some
+	 * reason the touchscreen isn't hooked up, we don't need to access
+	 * the pendown state.
+	 */
+
+	if (pdata->get_pendown_state) {
+		ts->get_pendown_state = pdata->get_pendown_state;
+	} else if (gpio_is_valid(pdata->gpio_pendown)) {
+
+		err = gpio_request_one(pdata->gpio_pendown, GPIOF_IN,
+				       "ads7846_pendown");
+		if (err) {
+			dev_err(&spi->dev,
+				"failed to request/setup pendown GPIO%d: %d\n",
+				pdata->gpio_pendown, err);
+			return err;
+		}
+
+		ts->gpio_pendown = pdata->gpio_pendown;
+
+		if (pdata->gpio_pendown_debounce)
+			gpio_set_debounce(pdata->gpio_pendown,
+					  pdata->gpio_pendown_debounce);
+	} else {
+		dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Set up the transfers to read touchscreen state; this assumes we
+ * use formula #2 for pressure, not #3.
+ */
+static void ads7846_setup_spi_msg(struct ads7846 *ts,
+				  const struct ads7846_platform_data *pdata)
+{
+	struct spi_message *m = &ts->msg[0];
+	struct spi_transfer *x = ts->xfer;
+	struct ads7846_packet *packet = ts->packet;
+	int vref = pdata->keep_vref_on;
+
+	if (ts->model == 7873) {
+		/*
+		 * The AD7873 is almost identical to the ADS7846
+		 * keep VREF off during differential/ratiometric
+		 * conversion modes.
+		 */
+		ts->model = 7846;
+		vref = 0;
+	}
+
+	ts->msg_count = 1;
+	spi_message_init(m);
+	m->context = ts;
+
+	if (ts->model == 7845) {
+		packet->read_y_cmd[0] = READ_Y(vref);
+		packet->read_y_cmd[1] = 0;
+		packet->read_y_cmd[2] = 0;
+		x->tx_buf = &packet->read_y_cmd[0];
+		x->rx_buf = &packet->tc.y_buf[0];
+		x->len = 3;
+		spi_message_add_tail(x, m);
+	} else {
+		/* y- still on; turn on only y+ (and ADC) */
+		packet->read_y = READ_Y(vref);
+		x->tx_buf = &packet->read_y;
+		x->len = 1;
+		spi_message_add_tail(x, m);
+
+		x++;
+		x->rx_buf = &packet->tc.y;
+		x->len = 2;
+		spi_message_add_tail(x, m);
+	}
+
+	/*
+	 * The first sample after switching drivers can be low quality;
+	 * optionally discard it, using a second one after the signals
+	 * have had enough time to stabilize.
+	 */
+	if (pdata->settle_delay_usecs) {
+		x->delay_usecs = pdata->settle_delay_usecs;
+
+		x++;
+		x->tx_buf = &packet->read_y;
+		x->len = 1;
+		spi_message_add_tail(x, m);
+
+		x++;
+		x->rx_buf = &packet->tc.y;
+		x->len = 2;
+		spi_message_add_tail(x, m);
+	}
+
+	ts->msg_count++;
+	m++;
+	spi_message_init(m);
+	m->context = ts;
+
+	if (ts->model == 7845) {
+		x++;
+		packet->read_x_cmd[0] = READ_X(vref);
+		packet->read_x_cmd[1] = 0;
+		packet->read_x_cmd[2] = 0;
+		x->tx_buf = &packet->read_x_cmd[0];
+		x->rx_buf = &packet->tc.x_buf[0];
+		x->len = 3;
+		spi_message_add_tail(x, m);
+	} else {
+		/* turn y- off, x+ on, then leave in lowpower */
+		x++;
+		packet->read_x = READ_X(vref);
+		x->tx_buf = &packet->read_x;
+		x->len = 1;
+		spi_message_add_tail(x, m);
+
+		x++;
+		x->rx_buf = &packet->tc.x;
+		x->len = 2;
+		spi_message_add_tail(x, m);
+	}
+
+	/* ... maybe discard first sample ... */
+	if (pdata->settle_delay_usecs) {
+		x->delay_usecs = pdata->settle_delay_usecs;
+
+		x++;
+		x->tx_buf = &packet->read_x;
+		x->len = 1;
+		spi_message_add_tail(x, m);
+
+		x++;
+		x->rx_buf = &packet->tc.x;
+		x->len = 2;
+		spi_message_add_tail(x, m);
+	}
+
+	/* turn y+ off, x- on; we'll use formula #2 */
+	if (ts->model == 7846) {
+		ts->msg_count++;
+		m++;
+		spi_message_init(m);
+		m->context = ts;
+
+		x++;
+		packet->read_z1 = READ_Z1(vref);
+		x->tx_buf = &packet->read_z1;
+		x->len = 1;
+		spi_message_add_tail(x, m);
+
+		x++;
+		x->rx_buf = &packet->tc.z1;
+		x->len = 2;
+		spi_message_add_tail(x, m);
+
+		/* ... maybe discard first sample ... */
+		if (pdata->settle_delay_usecs) {
+			x->delay_usecs = pdata->settle_delay_usecs;
+
+			x++;
+			x->tx_buf = &packet->read_z1;
+			x->len = 1;
+			spi_message_add_tail(x, m);
+
+			x++;
+			x->rx_buf = &packet->tc.z1;
+			x->len = 2;
+			spi_message_add_tail(x, m);
+		}
+
+		ts->msg_count++;
+		m++;
+		spi_message_init(m);
+		m->context = ts;
+
+		x++;
+		packet->read_z2 = READ_Z2(vref);
+		x->tx_buf = &packet->read_z2;
+		x->len = 1;
+		spi_message_add_tail(x, m);
+
+		x++;
+		x->rx_buf = &packet->tc.z2;
+		x->len = 2;
+		spi_message_add_tail(x, m);
+
+		/* ... maybe discard first sample ... */
+		if (pdata->settle_delay_usecs) {
+			x->delay_usecs = pdata->settle_delay_usecs;
+
+			x++;
+			x->tx_buf = &packet->read_z2;
+			x->len = 1;
+			spi_message_add_tail(x, m);
+
+			x++;
+			x->rx_buf = &packet->tc.z2;
+			x->len = 2;
+			spi_message_add_tail(x, m);
+		}
+	}
+
+	/* power down */
+	ts->msg_count++;
+	m++;
+	spi_message_init(m);
+	m->context = ts;
+
+	if (ts->model == 7845) {
+		x++;
+		packet->pwrdown_cmd[0] = PWRDOWN;
+		packet->pwrdown_cmd[1] = 0;
+		packet->pwrdown_cmd[2] = 0;
+		x->tx_buf = &packet->pwrdown_cmd[0];
+		x->len = 3;
+	} else {
+		x++;
+		packet->pwrdown = PWRDOWN;
+		x->tx_buf = &packet->pwrdown;
+		x->len = 1;
+		spi_message_add_tail(x, m);
+
+		x++;
+		x->rx_buf = &packet->dummy;
+		x->len = 2;
+	}
+
+	CS_CHANGE(*x);
+	spi_message_add_tail(x, m);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ads7846_dt_ids[] = {
+	{ .compatible = "ti,tsc2046",	.data = (void *) 7846 },
+	{ .compatible = "ti,ads7843",	.data = (void *) 7843 },
+	{ .compatible = "ti,ads7845",	.data = (void *) 7845 },
+	{ .compatible = "ti,ads7846",	.data = (void *) 7846 },
+	{ .compatible = "ti,ads7873",	.data = (void *) 7873 },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ads7846_dt_ids);
+
+static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev)
+{
+	struct ads7846_platform_data *pdata;
+	struct device_node *node = dev->of_node;
+	const struct of_device_id *match;
+
+	if (!node) {
+		dev_err(dev, "Device does not have associated DT data\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	match = of_match_device(ads7846_dt_ids, dev);
+	if (!match) {
+		dev_err(dev, "Unknown device model\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	pdata->model = (unsigned long)match->data;
+
+	of_property_read_u16(node, "ti,vref-delay-usecs",
+			     &pdata->vref_delay_usecs);
+	of_property_read_u16(node, "ti,vref-mv", &pdata->vref_mv);
+	pdata->keep_vref_on = of_property_read_bool(node, "ti,keep-vref-on");
+
+	pdata->swap_xy = of_property_read_bool(node, "ti,swap-xy");
+
+	of_property_read_u16(node, "ti,settle-delay-usec",
+			     &pdata->settle_delay_usecs);
+	of_property_read_u16(node, "ti,penirq-recheck-delay-usecs",
+			     &pdata->penirq_recheck_delay_usecs);
+
+	of_property_read_u16(node, "ti,x-plate-ohms", &pdata->x_plate_ohms);
+	of_property_read_u16(node, "ti,y-plate-ohms", &pdata->y_plate_ohms);
+
+	of_property_read_u16(node, "ti,x-min", &pdata->x_min);
+	of_property_read_u16(node, "ti,y-min", &pdata->y_min);
+	of_property_read_u16(node, "ti,x-max", &pdata->x_max);
+	of_property_read_u16(node, "ti,y-max", &pdata->y_max);
+
+	of_property_read_u16(node, "ti,pressure-min", &pdata->pressure_min);
+	of_property_read_u16(node, "ti,pressure-max", &pdata->pressure_max);
+
+	of_property_read_u16(node, "ti,debounce-max", &pdata->debounce_max);
+	of_property_read_u16(node, "ti,debounce-tol", &pdata->debounce_tol);
+	of_property_read_u16(node, "ti,debounce-rep", &pdata->debounce_rep);
+
+	of_property_read_u32(node, "ti,pendown-gpio-debounce",
+			     &pdata->gpio_pendown_debounce);
+
+	pdata->wakeup = of_property_read_bool(node, "wakeup-source") ||
+			of_property_read_bool(node, "linux,wakeup");
+
+	pdata->gpio_pendown = of_get_named_gpio(dev->of_node, "pendown-gpio", 0);
+
+	return pdata;
+}
+#else
+static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev)
+{
+	dev_err(dev, "no platform data defined\n");
+	return ERR_PTR(-EINVAL);
+}
+#endif
+
+static int ads7846_probe(struct spi_device *spi)
+{
+	const struct ads7846_platform_data *pdata;
+	struct ads7846 *ts;
+	struct ads7846_packet *packet;
+	struct input_dev *input_dev;
+	unsigned long irq_flags;
+	int err;
+
+	if (!spi->irq) {
+		dev_dbg(&spi->dev, "no IRQ?\n");
+		return -EINVAL;
+	}
+
+	/* don't exceed max specified sample rate */
+	if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) {
+		dev_err(&spi->dev, "f(sample) %d KHz?\n",
+				(spi->max_speed_hz/SAMPLE_BITS)/1000);
+		return -EINVAL;
+	}
+
+	/*
+	 * We'd set TX word size 8 bits and RX word size to 13 bits ... except
+	 * that even if the hardware can do that, the SPI controller driver
+	 * may not.  So we stick to very-portable 8 bit words, both RX and TX.
+	 */
+	spi->bits_per_word = 8;
+	spi->mode = SPI_MODE_0;
+	err = spi_setup(spi);
+	if (err < 0)
+		return err;
+
+	ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL);
+	packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!ts || !packet || !input_dev) {
+		err = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	spi_set_drvdata(spi, ts);
+
+	ts->packet = packet;
+	ts->spi = spi;
+	ts->input = input_dev;
+
+	mutex_init(&ts->lock);
+	init_waitqueue_head(&ts->wait);
+
+	pdata = dev_get_platdata(&spi->dev);
+	if (!pdata) {
+		pdata = ads7846_probe_dt(&spi->dev);
+		if (IS_ERR(pdata)) {
+			err = PTR_ERR(pdata);
+			goto err_free_mem;
+		}
+	}
+
+	ts->model = pdata->model ? : 7846;
+	ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
+	ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+	ts->pressure_max = pdata->pressure_max ? : ~0;
+
+	ts->vref_mv = pdata->vref_mv;
+	ts->swap_xy = pdata->swap_xy;
+
+	if (pdata->filter != NULL) {
+		if (pdata->filter_init != NULL) {
+			err = pdata->filter_init(pdata, &ts->filter_data);
+			if (err < 0)
+				goto err_free_mem;
+		}
+		ts->filter = pdata->filter;
+		ts->filter_cleanup = pdata->filter_cleanup;
+	} else if (pdata->debounce_max) {
+		ts->debounce_max = pdata->debounce_max;
+		if (ts->debounce_max < 2)
+			ts->debounce_max = 2;
+		ts->debounce_tol = pdata->debounce_tol;
+		ts->debounce_rep = pdata->debounce_rep;
+		ts->filter = ads7846_debounce_filter;
+		ts->filter_data = ts;
+	} else {
+		ts->filter = ads7846_no_filter;
+	}
+
+	err = ads7846_setup_pendown(spi, ts, pdata);
+	if (err)
+		goto err_cleanup_filter;
+
+	if (pdata->penirq_recheck_delay_usecs)
+		ts->penirq_recheck_delay_usecs =
+				pdata->penirq_recheck_delay_usecs;
+
+	ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync;
+
+	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
+	snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model);
+
+	input_dev->name = ts->name;
+	input_dev->phys = ts->phys;
+	input_dev->dev.parent = &spi->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(input_dev, ABS_X,
+			pdata->x_min ? : 0,
+			pdata->x_max ? : MAX_12BIT,
+			0, 0);
+	input_set_abs_params(input_dev, ABS_Y,
+			pdata->y_min ? : 0,
+			pdata->y_max ? : MAX_12BIT,
+			0, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE,
+			pdata->pressure_min, pdata->pressure_max, 0, 0);
+
+	ads7846_setup_spi_msg(ts, pdata);
+
+	ts->reg = regulator_get(&spi->dev, "vcc");
+	if (IS_ERR(ts->reg)) {
+		err = PTR_ERR(ts->reg);
+		dev_err(&spi->dev, "unable to get regulator: %d\n", err);
+		goto err_free_gpio;
+	}
+
+	err = regulator_enable(ts->reg);
+	if (err) {
+		dev_err(&spi->dev, "unable to enable regulator: %d\n", err);
+		goto err_put_regulator;
+	}
+
+	irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING;
+	irq_flags |= IRQF_ONESHOT;
+
+	err = request_threaded_irq(spi->irq, ads7846_hard_irq, ads7846_irq,
+				   irq_flags, spi->dev.driver->name, ts);
+	if (err && !pdata->irq_flags) {
+		dev_info(&spi->dev,
+			"trying pin change workaround on irq %d\n", spi->irq);
+		irq_flags |= IRQF_TRIGGER_RISING;
+		err = request_threaded_irq(spi->irq,
+				  ads7846_hard_irq, ads7846_irq,
+				  irq_flags, spi->dev.driver->name, ts);
+	}
+
+	if (err) {
+		dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
+		goto err_disable_regulator;
+	}
+
+	err = ads784x_hwmon_register(spi, ts);
+	if (err)
+		goto err_free_irq;
+
+	dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq);
+
+	/*
+	 * Take a first sample, leaving nPENIRQ active and vREF off; avoid
+	 * the touchscreen, in case it's not connected.
+	 */
+	if (ts->model == 7845)
+		ads7845_read12_ser(&spi->dev, PWRDOWN);
+	else
+		(void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux));
+
+	err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);
+	if (err)
+		goto err_remove_hwmon;
+
+	err = input_register_device(input_dev);
+	if (err)
+		goto err_remove_attr_group;
+
+	device_init_wakeup(&spi->dev, pdata->wakeup);
+
+	/*
+	 * If device does not carry platform data we must have allocated it
+	 * when parsing DT data.
+	 */
+	if (!dev_get_platdata(&spi->dev))
+		devm_kfree(&spi->dev, (void *)pdata);
+
+	return 0;
+
+ err_remove_attr_group:
+	sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
+ err_remove_hwmon:
+	ads784x_hwmon_unregister(spi, ts);
+ err_free_irq:
+	free_irq(spi->irq, ts);
+ err_disable_regulator:
+	regulator_disable(ts->reg);
+ err_put_regulator:
+	regulator_put(ts->reg);
+ err_free_gpio:
+	if (!ts->get_pendown_state)
+		gpio_free(ts->gpio_pendown);
+ err_cleanup_filter:
+	if (ts->filter_cleanup)
+		ts->filter_cleanup(ts->filter_data);
+ err_free_mem:
+	input_free_device(input_dev);
+	kfree(packet);
+	kfree(ts);
+	return err;
+}
+
+static int ads7846_remove(struct spi_device *spi)
+{
+	struct ads7846 *ts = spi_get_drvdata(spi);
+
+	device_init_wakeup(&spi->dev, false);
+
+	sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
+
+	ads7846_disable(ts);
+	free_irq(ts->spi->irq, ts);
+
+	input_unregister_device(ts->input);
+
+	ads784x_hwmon_unregister(spi, ts);
+
+	regulator_disable(ts->reg);
+	regulator_put(ts->reg);
+
+	if (!ts->get_pendown_state) {
+		/*
+		 * If we are not using specialized pendown method we must
+		 * have been relying on gpio we set up ourselves.
+		 */
+		gpio_free(ts->gpio_pendown);
+	}
+
+	if (ts->filter_cleanup)
+		ts->filter_cleanup(ts->filter_data);
+
+	kfree(ts->packet);
+	kfree(ts);
+
+	dev_dbg(&spi->dev, "unregistered touchscreen\n");
+
+	return 0;
+}
+
+static struct spi_driver ads7846_driver = {
+	.driver = {
+		.name	= "ads7846",
+		.pm	= &ads7846_pm,
+		.of_match_table = of_match_ptr(ads7846_dt_ids),
+	},
+	.probe		= ads7846_probe,
+	.remove		= ads7846_remove,
+};
+
+module_spi_driver(ads7846_driver);
+
+MODULE_DESCRIPTION("ADS7846 TouchScreen Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:ads7846");
diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c
new file mode 100644
index 0000000..71b5a63
--- /dev/null
+++ b/drivers/input/touchscreen/ar1021_i2c.c
@@ -0,0 +1,180 @@
+/*
+ * Microchip AR1021 driver for I2C
+ *
+ * Author: Christian Gmeiner <christian.gmeiner@gmail.com>
+ *
+ * License: GPLv2 as published by the FSF.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#define AR1021_TOCUH_PKG_SIZE	5
+
+#define AR1021_MAX_X	4095
+#define AR1021_MAX_Y	4095
+
+struct ar1021_i2c {
+	struct i2c_client *client;
+	struct input_dev *input;
+	u8 data[AR1021_TOCUH_PKG_SIZE];
+};
+
+static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id)
+{
+	struct ar1021_i2c *ar1021 = dev_id;
+	struct input_dev *input = ar1021->input;
+	u8 *data = ar1021->data;
+	unsigned int x, y, button;
+	int retval;
+
+	retval = i2c_master_recv(ar1021->client,
+				ar1021->data, sizeof(ar1021->data));
+	if (retval != sizeof(ar1021->data))
+		goto out;
+
+	/* sync bit set ? */
+	if ((data[0] & 0x80) == 0)
+		goto out;
+
+	button = data[0] & BIT(0);
+	x = ((data[2] & 0x1f) << 7) | (data[1] & 0x7f);
+	y = ((data[4] & 0x1f) << 7) | (data[3] & 0x7f);
+
+	input_report_abs(input, ABS_X, x);
+	input_report_abs(input, ABS_Y, y);
+	input_report_key(input, BTN_TOUCH, button);
+	input_sync(input);
+
+out:
+	return IRQ_HANDLED;
+}
+
+static int ar1021_i2c_open(struct input_dev *dev)
+{
+	struct ar1021_i2c *ar1021 = input_get_drvdata(dev);
+	struct i2c_client *client = ar1021->client;
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static void ar1021_i2c_close(struct input_dev *dev)
+{
+	struct ar1021_i2c *ar1021 = input_get_drvdata(dev);
+	struct i2c_client *client = ar1021->client;
+
+	disable_irq(client->irq);
+}
+
+static int ar1021_i2c_probe(struct i2c_client *client,
+				     const struct i2c_device_id *id)
+{
+	struct ar1021_i2c *ar1021;
+	struct input_dev *input;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "i2c_check_functionality error\n");
+		return -ENXIO;
+	}
+
+	ar1021 = devm_kzalloc(&client->dev, sizeof(*ar1021), GFP_KERNEL);
+	if (!ar1021)
+		return -ENOMEM;
+
+	input = devm_input_allocate_device(&client->dev);
+	if (!input)
+		return -ENOMEM;
+
+	ar1021->client = client;
+	ar1021->input = input;
+
+	input->name = "ar1021 I2C Touchscreen";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &client->dev;
+	input->open = ar1021_i2c_open;
+	input->close = ar1021_i2c_close;
+
+	input_set_capability(input, EV_KEY, BTN_TOUCH);
+	input_set_abs_params(input, ABS_X, 0, AR1021_MAX_X, 0, 0);
+	input_set_abs_params(input, ABS_Y, 0, AR1021_MAX_Y, 0, 0);
+
+	input_set_drvdata(input, ar1021);
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, ar1021_i2c_irq,
+					  IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					  "ar1021_i2c", ar1021);
+	if (error) {
+		dev_err(&client->dev,
+			"Failed to enable IRQ, error: %d\n", error);
+		return error;
+	}
+
+	/* Disable the IRQ, we'll enable it in ar1021_i2c_open() */
+	disable_irq(client->irq);
+
+	error = input_register_device(ar1021->input);
+	if (error) {
+		dev_err(&client->dev,
+			"Failed to register input device, error: %d\n", error);
+		return error;
+	}
+
+	i2c_set_clientdata(client, ar1021);
+	return 0;
+}
+
+static int __maybe_unused ar1021_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	disable_irq(client->irq);
+
+	return 0;
+}
+
+static int __maybe_unused ar1021_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ar1021_i2c_pm, ar1021_i2c_suspend, ar1021_i2c_resume);
+
+static const struct i2c_device_id ar1021_i2c_id[] = {
+	{ "MICROCHIP_AR1021_I2C", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id);
+
+static const struct of_device_id ar1021_i2c_of_match[] = {
+	{ .compatible = "microchip,ar1021-i2c", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ar1021_i2c_of_match);
+
+static struct i2c_driver ar1021_i2c_driver = {
+	.driver	= {
+		.name	= "ar1021_i2c",
+		.pm	= &ar1021_i2c_pm,
+		.of_match_table = ar1021_i2c_of_match,
+	},
+
+	.probe		= ar1021_i2c_probe,
+	.id_table	= ar1021_i2c_id,
+};
+module_i2c_driver(ar1021_i2c_driver);
+
+MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>");
+MODULE_DESCRIPTION("Microchip AR1021 I2C Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c
new file mode 100644
index 0000000..7ec0421
--- /dev/null
+++ b/drivers/input/touchscreen/atmel-wm97xx.c
@@ -0,0 +1,436 @@
+/*
+ * Atmel AT91 and AVR32 continuous touch screen driver for Wolfson WM97xx AC97
+ * codecs.
+ *
+ * Copyright (C) 2008 - 2009 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/wm97xx.h>
+#include <linux/timer.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#define AC97C_ICA		0x10
+#define AC97C_CBRHR		0x30
+#define AC97C_CBSR		0x38
+#define AC97C_CBMR		0x3c
+#define AC97C_IER		0x54
+#define AC97C_IDR		0x58
+
+#define AC97C_RXRDY		(1 << 4)
+#define AC97C_OVRUN		(1 << 5)
+
+#define AC97C_CMR_SIZE_20	(0 << 16)
+#define AC97C_CMR_SIZE_18	(1 << 16)
+#define AC97C_CMR_SIZE_16	(2 << 16)
+#define AC97C_CMR_SIZE_10	(3 << 16)
+#define AC97C_CMR_CEM_LITTLE	(1 << 18)
+#define AC97C_CMR_CEM_BIG	(0 << 18)
+#define AC97C_CMR_CENA		(1 << 21)
+
+#define AC97C_INT_CBEVT		(1 << 4)
+
+#define AC97C_SR_CAEVT		(1 << 3)
+
+#define AC97C_CH_MASK(slot)						\
+	(0x7 << (3 * (slot - 3)))
+#define AC97C_CH_ASSIGN(slot, channel)					\
+	(AC97C_CHANNEL_##channel << (3 * (slot - 3)))
+#define AC97C_CHANNEL_NONE	0x0
+#define AC97C_CHANNEL_B		0x2
+
+#define ac97c_writel(chip, reg, val)			\
+	__raw_writel((val), (chip)->regs + AC97C_##reg)
+#define ac97c_readl(chip, reg)				\
+	__raw_readl((chip)->regs + AC97C_##reg)
+
+#ifdef CONFIG_CPU_AT32AP700X
+#define ATMEL_WM97XX_AC97C_IOMEM	(0xfff02800)
+#define ATMEL_WM97XX_AC97C_IRQ		(29)
+#define ATMEL_WM97XX_GPIO_DEFAULT	(32+16) /* Pin 16 on port B. */
+#else
+#error Unknown CPU, this driver only supports AT32AP700X CPUs.
+#endif
+
+struct continuous {
+	u16 id;    /* codec id */
+	u8 code;   /* continuous code */
+	u8 reads;  /* number of coord reads per read cycle */
+	u32 speed; /* number of coords per second */
+};
+
+#define WM_READS(sp) ((sp / HZ) + 1)
+
+static const struct continuous cinfo[] = {
+	{WM9705_ID2, 0, WM_READS(94), 94},
+	{WM9705_ID2, 1, WM_READS(188), 188},
+	{WM9705_ID2, 2, WM_READS(375), 375},
+	{WM9705_ID2, 3, WM_READS(750), 750},
+	{WM9712_ID2, 0, WM_READS(94), 94},
+	{WM9712_ID2, 1, WM_READS(188), 188},
+	{WM9712_ID2, 2, WM_READS(375), 375},
+	{WM9712_ID2, 3, WM_READS(750), 750},
+	{WM9713_ID2, 0, WM_READS(94), 94},
+	{WM9713_ID2, 1, WM_READS(120), 120},
+	{WM9713_ID2, 2, WM_READS(154), 154},
+	{WM9713_ID2, 3, WM_READS(188), 188},
+};
+
+/* Continuous speed index. */
+static int sp_idx;
+
+/*
+ * Pen sampling frequency (Hz) in continuous mode.
+ */
+static int cont_rate = 188;
+module_param(cont_rate, int, 0);
+MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
+
+/*
+ * Pen down detection.
+ *
+ * This driver can either poll or use an interrupt to indicate a pen down
+ * event. If the irq request fails then it will fall back to polling mode.
+ */
+static int pen_int = 1;
+module_param(pen_int, int, 0);
+MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)");
+
+/*
+ * Pressure readback.
+ *
+ * Set to 1 to read back pen down pressure.
+ */
+static int pressure;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
+
+/*
+ * AC97 touch data slot.
+ *
+ * Touch screen readback data ac97 slot.
+ */
+static int ac97_touch_slot = 5;
+module_param(ac97_touch_slot, int, 0);
+MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
+
+/*
+ * GPIO line number.
+ *
+ * Set to GPIO number where the signal from the WM97xx device is hooked up.
+ */
+static int atmel_gpio_line = ATMEL_WM97XX_GPIO_DEFAULT;
+module_param(atmel_gpio_line, int, 0);
+MODULE_PARM_DESC(atmel_gpio_line, "GPIO line number connected to WM97xx");
+
+struct atmel_wm97xx {
+	struct wm97xx		*wm;
+	struct timer_list	pen_timer;
+	void __iomem		*regs;
+	unsigned long		ac97c_irq;
+	unsigned long		gpio_pen;
+	unsigned long		gpio_irq;
+	unsigned short		x;
+	unsigned short		y;
+};
+
+static irqreturn_t atmel_wm97xx_channel_b_interrupt(int irq, void *dev_id)
+{
+	struct atmel_wm97xx *atmel_wm97xx = dev_id;
+	struct wm97xx *wm = atmel_wm97xx->wm;
+	int status = ac97c_readl(atmel_wm97xx, CBSR);
+	irqreturn_t retval = IRQ_NONE;
+
+	if (status & AC97C_OVRUN) {
+		dev_dbg(&wm->touch_dev->dev, "AC97C overrun\n");
+		ac97c_readl(atmel_wm97xx, CBRHR);
+		retval = IRQ_HANDLED;
+	} else if (status & AC97C_RXRDY) {
+		u16 data;
+		u16 value;
+		u16 source;
+		u16 pen_down;
+
+		data = ac97c_readl(atmel_wm97xx, CBRHR);
+		value = data & 0x0fff;
+		source = data & WM97XX_ADCSEL_MASK;
+		pen_down = (data & WM97XX_PEN_DOWN) >> 8;
+
+		if (source == WM97XX_ADCSEL_X)
+			atmel_wm97xx->x = value;
+		if (source == WM97XX_ADCSEL_Y)
+			atmel_wm97xx->y = value;
+
+		if (!pressure && source == WM97XX_ADCSEL_Y) {
+			input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x);
+			input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y);
+			input_report_key(wm->input_dev, BTN_TOUCH, pen_down);
+			input_sync(wm->input_dev);
+		} else if (pressure && source == WM97XX_ADCSEL_PRES) {
+			input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x);
+			input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y);
+			input_report_abs(wm->input_dev, ABS_PRESSURE, value);
+			input_report_key(wm->input_dev, BTN_TOUCH, value);
+			input_sync(wm->input_dev);
+		}
+
+		retval = IRQ_HANDLED;
+	}
+
+	return retval;
+}
+
+static void atmel_wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+	struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev);
+	struct input_dev *input_dev = wm->input_dev;
+	int pen_down = gpio_get_value(atmel_wm97xx->gpio_pen);
+
+	if (pen_down != 0) {
+		mod_timer(&atmel_wm97xx->pen_timer,
+			  jiffies + msecs_to_jiffies(1));
+	} else {
+		if (pressure)
+			input_report_abs(input_dev, ABS_PRESSURE, 0);
+		input_report_key(input_dev, BTN_TOUCH, 0);
+		input_sync(input_dev);
+	}
+}
+
+static void atmel_wm97xx_pen_timer(unsigned long data)
+{
+	atmel_wm97xx_acc_pen_up((struct wm97xx *)data);
+}
+
+static int atmel_wm97xx_acc_startup(struct wm97xx *wm)
+{
+	struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev);
+	int idx = 0;
+
+	if (wm->ac97 == NULL)
+		return -ENODEV;
+
+	for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
+		if (wm->id != cinfo[idx].id)
+			continue;
+
+		sp_idx = idx;
+
+		if (cont_rate <= cinfo[idx].speed)
+			break;
+	}
+
+	wm->acc_rate = cinfo[sp_idx].code;
+	wm->acc_slot = ac97_touch_slot;
+	dev_info(&wm->touch_dev->dev, "atmel accelerated touchscreen driver, "
+			"%d samples/sec\n", cinfo[sp_idx].speed);
+
+	if (pen_int) {
+		unsigned long reg;
+
+		wm->pen_irq = atmel_wm97xx->gpio_irq;
+
+		switch (wm->id) {
+		case WM9712_ID2: /* Fall through. */
+		case WM9713_ID2:
+			/*
+			 * Use GPIO 13 (PEN_DOWN) to assert GPIO line 3
+			 * (PENDOWN).
+			 */
+			wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
+					WM97XX_GPIO_POL_HIGH,
+					WM97XX_GPIO_STICKY,
+					WM97XX_GPIO_WAKE);
+			wm97xx_config_gpio(wm, WM97XX_GPIO_3, WM97XX_GPIO_OUT,
+					WM97XX_GPIO_POL_HIGH,
+					WM97XX_GPIO_NOTSTICKY,
+					WM97XX_GPIO_NOWAKE);
+		case WM9705_ID2: /* Fall through. */
+			/*
+			 * Enable touch data slot in AC97 controller channel B.
+			 */
+			reg = ac97c_readl(atmel_wm97xx, ICA);
+			reg &= ~AC97C_CH_MASK(wm->acc_slot);
+			reg |= AC97C_CH_ASSIGN(wm->acc_slot, B);
+			ac97c_writel(atmel_wm97xx, ICA, reg);
+
+			/*
+			 * Enable channel and interrupt for RXRDY and OVERRUN.
+			 */
+			ac97c_writel(atmel_wm97xx, CBMR, AC97C_CMR_CENA
+					| AC97C_CMR_CEM_BIG
+					| AC97C_CMR_SIZE_16
+					| AC97C_OVRUN
+					| AC97C_RXRDY);
+			/* Dummy read to empty RXRHR. */
+			ac97c_readl(atmel_wm97xx, CBRHR);
+			/*
+			 * Enable interrupt for channel B in the AC97
+			 * controller.
+			 */
+			ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT);
+			break;
+		default:
+			dev_err(&wm->touch_dev->dev, "pen down irq not "
+					"supported on this device\n");
+			pen_int = 0;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static void atmel_wm97xx_acc_shutdown(struct wm97xx *wm)
+{
+	if (pen_int) {
+		struct atmel_wm97xx *atmel_wm97xx =
+			platform_get_drvdata(wm->touch_dev);
+		unsigned long ica;
+
+		switch (wm->id & 0xffff) {
+		case WM9705_ID2: /* Fall through. */
+		case WM9712_ID2: /* Fall through. */
+		case WM9713_ID2:
+			/* Disable slot and turn off channel B interrupts. */
+			ica = ac97c_readl(atmel_wm97xx, ICA);
+			ica &= ~AC97C_CH_MASK(wm->acc_slot);
+			ac97c_writel(atmel_wm97xx, ICA, ica);
+			ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT);
+			ac97c_writel(atmel_wm97xx, CBMR, 0);
+			wm->pen_irq = 0;
+			break;
+		default:
+			dev_err(&wm->touch_dev->dev, "unknown codec\n");
+			break;
+		}
+	}
+}
+
+static void atmel_wm97xx_irq_enable(struct wm97xx *wm, int enable)
+{
+	/* Intentionally left empty. */
+}
+
+static struct wm97xx_mach_ops atmel_mach_ops = {
+	.acc_enabled	= 1,
+	.acc_pen_up	= atmel_wm97xx_acc_pen_up,
+	.acc_startup	= atmel_wm97xx_acc_startup,
+	.acc_shutdown	= atmel_wm97xx_acc_shutdown,
+	.irq_enable	= atmel_wm97xx_irq_enable,
+	.irq_gpio	= WM97XX_GPIO_3,
+};
+
+static int __init atmel_wm97xx_probe(struct platform_device *pdev)
+{
+	struct wm97xx *wm = platform_get_drvdata(pdev);
+	struct atmel_wm97xx *atmel_wm97xx;
+	int ret;
+
+	atmel_wm97xx = kzalloc(sizeof(struct atmel_wm97xx), GFP_KERNEL);
+	if (!atmel_wm97xx) {
+		dev_dbg(&pdev->dev, "out of memory\n");
+		return -ENOMEM;
+	}
+
+	atmel_wm97xx->wm	= wm;
+	atmel_wm97xx->regs	= (void *)ATMEL_WM97XX_AC97C_IOMEM;
+	atmel_wm97xx->ac97c_irq	= ATMEL_WM97XX_AC97C_IRQ;
+	atmel_wm97xx->gpio_pen	= atmel_gpio_line;
+	atmel_wm97xx->gpio_irq	= gpio_to_irq(atmel_wm97xx->gpio_pen);
+
+	setup_timer(&atmel_wm97xx->pen_timer, atmel_wm97xx_pen_timer,
+			(unsigned long)wm);
+
+	ret = request_irq(atmel_wm97xx->ac97c_irq,
+			  atmel_wm97xx_channel_b_interrupt,
+			  IRQF_SHARED, "atmel-wm97xx-ch-b", atmel_wm97xx);
+	if (ret) {
+		dev_dbg(&pdev->dev, "could not request ac97c irq\n");
+		goto err;
+	}
+
+	platform_set_drvdata(pdev, atmel_wm97xx);
+
+	ret = wm97xx_register_mach_ops(wm, &atmel_mach_ops);
+	if (ret)
+		goto err_irq;
+
+	return ret;
+
+err_irq:
+	free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx);
+err:
+	kfree(atmel_wm97xx);
+	return ret;
+}
+
+static int __exit atmel_wm97xx_remove(struct platform_device *pdev)
+{
+	struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
+	struct wm97xx *wm = atmel_wm97xx->wm;
+
+	ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT);
+	free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx);
+	del_timer_sync(&atmel_wm97xx->pen_timer);
+	wm97xx_unregister_mach_ops(wm);
+	kfree(atmel_wm97xx);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int atmel_wm97xx_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
+
+	ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT);
+	disable_irq(atmel_wm97xx->gpio_irq);
+	del_timer_sync(&atmel_wm97xx->pen_timer);
+
+	return 0;
+}
+
+static int atmel_wm97xx_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
+	struct wm97xx *wm = atmel_wm97xx->wm;
+
+	if (wm->input_dev->users) {
+		enable_irq(atmel_wm97xx->gpio_irq);
+		ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT);
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(atmel_wm97xx_pm_ops,
+			 atmel_wm97xx_suspend, atmel_wm97xx_resume);
+
+static struct platform_driver atmel_wm97xx_driver = {
+	.remove		= __exit_p(atmel_wm97xx_remove),
+	.driver		= {
+		.name	= "wm97xx-touch",
+		.pm	= &atmel_wm97xx_pm_ops,
+	},
+};
+
+module_platform_driver_probe(atmel_wm97xx_driver, atmel_wm97xx_probe);
+
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
+MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
new file mode 100644
index 0000000..2d5794e
--- /dev/null
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -0,0 +1,2760 @@
+/*
+ * Atmel maXTouch Touchscreen driver
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Copyright (C) 2011-2014 Atmel Corporation
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/platform_data/atmel_mxt_ts.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+/* Firmware files */
+#define MXT_FW_NAME		"maxtouch.fw"
+#define MXT_CFG_NAME		"maxtouch.cfg"
+#define MXT_CFG_MAGIC		"OBP_RAW V1"
+
+/* Registers */
+#define MXT_OBJECT_START	0x07
+#define MXT_OBJECT_SIZE		6
+#define MXT_INFO_CHECKSUM_SIZE	3
+#define MXT_MAX_BLOCK_WRITE	256
+
+/* Object types */
+#define MXT_DEBUG_DIAGNOSTIC_T37	37
+#define MXT_GEN_MESSAGE_T5		5
+#define MXT_GEN_COMMAND_T6		6
+#define MXT_GEN_POWER_T7		7
+#define MXT_GEN_ACQUIRE_T8		8
+#define MXT_GEN_DATASOURCE_T53		53
+#define MXT_TOUCH_MULTI_T9		9
+#define MXT_TOUCH_KEYARRAY_T15		15
+#define MXT_TOUCH_PROXIMITY_T23		23
+#define MXT_TOUCH_PROXKEY_T52		52
+#define MXT_PROCI_GRIPFACE_T20		20
+#define MXT_PROCG_NOISE_T22		22
+#define MXT_PROCI_ONETOUCH_T24		24
+#define MXT_PROCI_TWOTOUCH_T27		27
+#define MXT_PROCI_GRIP_T40		40
+#define MXT_PROCI_PALM_T41		41
+#define MXT_PROCI_TOUCHSUPPRESSION_T42	42
+#define MXT_PROCI_STYLUS_T47		47
+#define MXT_PROCG_NOISESUPPRESSION_T48	48
+#define MXT_SPT_COMMSCONFIG_T18		18
+#define MXT_SPT_GPIOPWM_T19		19
+#define MXT_SPT_SELFTEST_T25		25
+#define MXT_SPT_CTECONFIG_T28		28
+#define MXT_SPT_USERDATA_T38		38
+#define MXT_SPT_DIGITIZER_T43		43
+#define MXT_SPT_MESSAGECOUNT_T44	44
+#define MXT_SPT_CTECONFIG_T46		46
+#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100
+
+/* MXT_GEN_MESSAGE_T5 object */
+#define MXT_RPTID_NOMSG		0xff
+
+/* MXT_GEN_COMMAND_T6 field */
+#define MXT_COMMAND_RESET	0
+#define MXT_COMMAND_BACKUPNV	1
+#define MXT_COMMAND_CALIBRATE	2
+#define MXT_COMMAND_REPORTALL	3
+#define MXT_COMMAND_DIAGNOSTIC	5
+
+/* Define for T6 status byte */
+#define MXT_T6_STATUS_RESET	(1 << 7)
+#define MXT_T6_STATUS_OFL	(1 << 6)
+#define MXT_T6_STATUS_SIGERR	(1 << 5)
+#define MXT_T6_STATUS_CAL	(1 << 4)
+#define MXT_T6_STATUS_CFGERR	(1 << 3)
+#define MXT_T6_STATUS_COMSERR	(1 << 2)
+
+/* MXT_GEN_POWER_T7 field */
+struct t7_config {
+	u8 idle;
+	u8 active;
+} __packed;
+
+#define MXT_POWER_CFG_RUN		0
+#define MXT_POWER_CFG_DEEPSLEEP		1
+
+/* MXT_TOUCH_MULTI_T9 field */
+#define MXT_T9_CTRL		0
+#define MXT_T9_ORIENT		9
+#define MXT_T9_RANGE		18
+
+/* MXT_TOUCH_MULTI_T9 status */
+#define MXT_T9_UNGRIP		(1 << 0)
+#define MXT_T9_SUPPRESS		(1 << 1)
+#define MXT_T9_AMP		(1 << 2)
+#define MXT_T9_VECTOR		(1 << 3)
+#define MXT_T9_MOVE		(1 << 4)
+#define MXT_T9_RELEASE		(1 << 5)
+#define MXT_T9_PRESS		(1 << 6)
+#define MXT_T9_DETECT		(1 << 7)
+
+struct t9_range {
+	u16 x;
+	u16 y;
+} __packed;
+
+/* MXT_TOUCH_MULTI_T9 orient */
+#define MXT_T9_ORIENT_SWITCH	(1 << 0)
+
+/* MXT_SPT_COMMSCONFIG_T18 */
+#define MXT_COMMS_CTRL		0
+#define MXT_COMMS_CMD		1
+
+/* Define for MXT_GEN_COMMAND_T6 */
+#define MXT_BOOT_VALUE		0xa5
+#define MXT_RESET_VALUE		0x01
+#define MXT_BACKUP_VALUE	0x55
+
+/* T100 Multiple Touch Touchscreen */
+#define MXT_T100_CTRL		0
+#define MXT_T100_CFG1		1
+#define MXT_T100_TCHAUX		3
+#define MXT_T100_XRANGE		13
+#define MXT_T100_YRANGE		24
+
+#define MXT_T100_CFG_SWITCHXY	BIT(5)
+
+#define MXT_T100_TCHAUX_VECT	BIT(0)
+#define MXT_T100_TCHAUX_AMPL	BIT(1)
+#define MXT_T100_TCHAUX_AREA	BIT(2)
+
+#define MXT_T100_DETECT		BIT(7)
+#define MXT_T100_TYPE_MASK	0x70
+
+enum t100_type {
+	MXT_T100_TYPE_FINGER		= 1,
+	MXT_T100_TYPE_PASSIVE_STYLUS	= 2,
+	MXT_T100_TYPE_HOVERING_FINGER	= 4,
+	MXT_T100_TYPE_GLOVE		= 5,
+	MXT_T100_TYPE_LARGE_TOUCH	= 6,
+};
+
+#define MXT_DISTANCE_ACTIVE_TOUCH	0
+#define MXT_DISTANCE_HOVERING		1
+
+#define MXT_TOUCH_MAJOR_DEFAULT		1
+#define MXT_PRESSURE_DEFAULT		1
+
+/* Delay times */
+#define MXT_BACKUP_TIME		50	/* msec */
+#define MXT_RESET_TIME		200	/* msec */
+#define MXT_RESET_TIMEOUT	3000	/* msec */
+#define MXT_CRC_TIMEOUT		1000	/* msec */
+#define MXT_FW_RESET_TIME	3000	/* msec */
+#define MXT_FW_CHG_TIMEOUT	300	/* msec */
+
+/* Command to unlock bootloader */
+#define MXT_UNLOCK_CMD_MSB	0xaa
+#define MXT_UNLOCK_CMD_LSB	0xdc
+
+/* Bootloader mode status */
+#define MXT_WAITING_BOOTLOAD_CMD	0xc0	/* valid 7 6 bit only */
+#define MXT_WAITING_FRAME_DATA	0x80	/* valid 7 6 bit only */
+#define MXT_FRAME_CRC_CHECK	0x02
+#define MXT_FRAME_CRC_FAIL	0x03
+#define MXT_FRAME_CRC_PASS	0x04
+#define MXT_APP_CRC_FAIL	0x40	/* valid 7 8 bit only */
+#define MXT_BOOT_STATUS_MASK	0x3f
+#define MXT_BOOT_EXTENDED_ID	(1 << 5)
+#define MXT_BOOT_ID_MASK	0x1f
+
+/* Touchscreen absolute values */
+#define MXT_MAX_AREA		0xff
+
+#define MXT_PIXELS_PER_MM	20
+
+struct mxt_info {
+	u8 family_id;
+	u8 variant_id;
+	u8 version;
+	u8 build;
+	u8 matrix_xsize;
+	u8 matrix_ysize;
+	u8 object_num;
+};
+
+struct mxt_object {
+	u8 type;
+	u16 start_address;
+	u8 size_minus_one;
+	u8 instances_minus_one;
+	u8 num_report_ids;
+} __packed;
+
+/* Each client has this additional data */
+struct mxt_data {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	char phys[64];		/* device physical location */
+	const struct mxt_platform_data *pdata;
+	struct mxt_object *object_table;
+	struct mxt_info info;
+	unsigned int irq;
+	unsigned int max_x;
+	unsigned int max_y;
+	bool in_bootloader;
+	u16 mem_size;
+	u8 t100_aux_ampl;
+	u8 t100_aux_area;
+	u8 t100_aux_vect;
+	u8 max_reportid;
+	u32 config_crc;
+	u32 info_crc;
+	u8 bootloader_addr;
+	u8 *msg_buf;
+	u8 t6_status;
+	bool update_input;
+	u8 last_message_count;
+	u8 num_touchids;
+	u8 multitouch;
+	struct t7_config t7_cfg;
+
+	/* Cached parameters from object table */
+	u16 T5_address;
+	u8 T5_msg_size;
+	u8 T6_reportid;
+	u16 T6_address;
+	u16 T7_address;
+	u8 T9_reportid_min;
+	u8 T9_reportid_max;
+	u8 T19_reportid;
+	u16 T44_address;
+	u8 T100_reportid_min;
+	u8 T100_reportid_max;
+
+	/* for fw update in bootloader */
+	struct completion bl_completion;
+
+	/* for reset handling */
+	struct completion reset_completion;
+
+	/* for config update handling */
+	struct completion crc_completion;
+};
+
+static size_t mxt_obj_size(const struct mxt_object *obj)
+{
+	return obj->size_minus_one + 1;
+}
+
+static size_t mxt_obj_instances(const struct mxt_object *obj)
+{
+	return obj->instances_minus_one + 1;
+}
+
+static bool mxt_object_readable(unsigned int type)
+{
+	switch (type) {
+	case MXT_GEN_COMMAND_T6:
+	case MXT_GEN_POWER_T7:
+	case MXT_GEN_ACQUIRE_T8:
+	case MXT_GEN_DATASOURCE_T53:
+	case MXT_TOUCH_MULTI_T9:
+	case MXT_TOUCH_KEYARRAY_T15:
+	case MXT_TOUCH_PROXIMITY_T23:
+	case MXT_TOUCH_PROXKEY_T52:
+	case MXT_PROCI_GRIPFACE_T20:
+	case MXT_PROCG_NOISE_T22:
+	case MXT_PROCI_ONETOUCH_T24:
+	case MXT_PROCI_TWOTOUCH_T27:
+	case MXT_PROCI_GRIP_T40:
+	case MXT_PROCI_PALM_T41:
+	case MXT_PROCI_TOUCHSUPPRESSION_T42:
+	case MXT_PROCI_STYLUS_T47:
+	case MXT_PROCG_NOISESUPPRESSION_T48:
+	case MXT_SPT_COMMSCONFIG_T18:
+	case MXT_SPT_GPIOPWM_T19:
+	case MXT_SPT_SELFTEST_T25:
+	case MXT_SPT_CTECONFIG_T28:
+	case MXT_SPT_USERDATA_T38:
+	case MXT_SPT_DIGITIZER_T43:
+	case MXT_SPT_CTECONFIG_T46:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static void mxt_dump_message(struct mxt_data *data, u8 *message)
+{
+	dev_dbg(&data->client->dev, "message: %*ph\n",
+		data->T5_msg_size, message);
+}
+
+static int mxt_wait_for_completion(struct mxt_data *data,
+				   struct completion *comp,
+				   unsigned int timeout_ms)
+{
+	struct device *dev = &data->client->dev;
+	unsigned long timeout = msecs_to_jiffies(timeout_ms);
+	long ret;
+
+	ret = wait_for_completion_interruptible_timeout(comp, timeout);
+	if (ret < 0) {
+		return ret;
+	} else if (ret == 0) {
+		dev_err(dev, "Wait for completion timed out.\n");
+		return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+static int mxt_bootloader_read(struct mxt_data *data,
+			       u8 *val, unsigned int count)
+{
+	int ret;
+	struct i2c_msg msg;
+
+	msg.addr = data->bootloader_addr;
+	msg.flags = data->client->flags & I2C_M_TEN;
+	msg.flags |= I2C_M_RD;
+	msg.len = count;
+	msg.buf = val;
+
+	ret = i2c_transfer(data->client->adapter, &msg, 1);
+	if (ret == 1) {
+		ret = 0;
+	} else {
+		ret = ret < 0 ? ret : -EIO;
+		dev_err(&data->client->dev, "%s: i2c recv failed (%d)\n",
+			__func__, ret);
+	}
+
+	return ret;
+}
+
+static int mxt_bootloader_write(struct mxt_data *data,
+				const u8 * const val, unsigned int count)
+{
+	int ret;
+	struct i2c_msg msg;
+
+	msg.addr = data->bootloader_addr;
+	msg.flags = data->client->flags & I2C_M_TEN;
+	msg.len = count;
+	msg.buf = (u8 *)val;
+
+	ret = i2c_transfer(data->client->adapter, &msg, 1);
+	if (ret == 1) {
+		ret = 0;
+	} else {
+		ret = ret < 0 ? ret : -EIO;
+		dev_err(&data->client->dev, "%s: i2c send failed (%d)\n",
+			__func__, ret);
+	}
+
+	return ret;
+}
+
+static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
+{
+	u8 appmode = data->client->addr;
+	u8 bootloader;
+
+	switch (appmode) {
+	case 0x4a:
+	case 0x4b:
+		/* Chips after 1664S use different scheme */
+		if (retry || data->info.family_id >= 0xa2) {
+			bootloader = appmode - 0x24;
+			break;
+		}
+		/* Fall through for normal case */
+	case 0x4c:
+	case 0x4d:
+	case 0x5a:
+	case 0x5b:
+		bootloader = appmode - 0x26;
+		break;
+
+	default:
+		dev_err(&data->client->dev,
+			"Appmode i2c address 0x%02x not found\n",
+			appmode);
+		return -EINVAL;
+	}
+
+	data->bootloader_addr = bootloader;
+	return 0;
+}
+
+static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address)
+{
+	struct device *dev = &data->client->dev;
+	int error;
+	u8 val;
+	bool crc_failure;
+
+	error = mxt_lookup_bootloader_address(data, alt_address);
+	if (error)
+		return error;
+
+	error = mxt_bootloader_read(data, &val, 1);
+	if (error)
+		return error;
+
+	/* Check app crc fail mode */
+	crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL;
+
+	dev_err(dev, "Detected bootloader, status:%02X%s\n",
+			val, crc_failure ? ", APP_CRC_FAIL" : "");
+
+	return 0;
+}
+
+static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val)
+{
+	struct device *dev = &data->client->dev;
+	u8 buf[3];
+
+	if (val & MXT_BOOT_EXTENDED_ID) {
+		if (mxt_bootloader_read(data, &buf[0], 3) != 0) {
+			dev_err(dev, "%s: i2c failure\n", __func__);
+			return val;
+		}
+
+		dev_dbg(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]);
+
+		return buf[0];
+	} else {
+		dev_dbg(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK);
+
+		return val;
+	}
+}
+
+static int mxt_check_bootloader(struct mxt_data *data, unsigned int state,
+				bool wait)
+{
+	struct device *dev = &data->client->dev;
+	u8 val;
+	int ret;
+
+recheck:
+	if (wait) {
+		/*
+		 * In application update mode, the interrupt
+		 * line signals state transitions. We must wait for the
+		 * CHG assertion before reading the status byte.
+		 * Once the status byte has been read, the line is deasserted.
+		 */
+		ret = mxt_wait_for_completion(data, &data->bl_completion,
+					      MXT_FW_CHG_TIMEOUT);
+		if (ret) {
+			/*
+			 * TODO: handle -ERESTARTSYS better by terminating
+			 * fw update process before returning to userspace
+			 * by writing length 0x000 to device (iff we are in
+			 * WAITING_FRAME_DATA state).
+			 */
+			dev_err(dev, "Update wait error %d\n", ret);
+			return ret;
+		}
+	}
+
+	ret = mxt_bootloader_read(data, &val, 1);
+	if (ret)
+		return ret;
+
+	if (state == MXT_WAITING_BOOTLOAD_CMD)
+		val = mxt_get_bootloader_version(data, val);
+
+	switch (state) {
+	case MXT_WAITING_BOOTLOAD_CMD:
+	case MXT_WAITING_FRAME_DATA:
+	case MXT_APP_CRC_FAIL:
+		val &= ~MXT_BOOT_STATUS_MASK;
+		break;
+	case MXT_FRAME_CRC_PASS:
+		if (val == MXT_FRAME_CRC_CHECK) {
+			goto recheck;
+		} else if (val == MXT_FRAME_CRC_FAIL) {
+			dev_err(dev, "Bootloader CRC fail\n");
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (val != state) {
+		dev_err(dev, "Invalid bootloader state %02X != %02X\n",
+			val, state);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock)
+{
+	int ret;
+	u8 buf[2];
+
+	if (unlock) {
+		buf[0] = MXT_UNLOCK_CMD_LSB;
+		buf[1] = MXT_UNLOCK_CMD_MSB;
+	} else {
+		buf[0] = 0x01;
+		buf[1] = 0x01;
+	}
+
+	ret = mxt_bootloader_write(data, buf, 2);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int __mxt_read_reg(struct i2c_client *client,
+			       u16 reg, u16 len, void *val)
+{
+	struct i2c_msg xfer[2];
+	u8 buf[2];
+	int ret;
+
+	buf[0] = reg & 0xff;
+	buf[1] = (reg >> 8) & 0xff;
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 2;
+	xfer[0].buf = buf;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = len;
+	xfer[1].buf = val;
+
+	ret = i2c_transfer(client->adapter, xfer, 2);
+	if (ret == 2) {
+		ret = 0;
+	} else {
+		if (ret >= 0)
+			ret = -EIO;
+		dev_err(&client->dev, "%s: i2c transfer failed (%d)\n",
+			__func__, ret);
+	}
+
+	return ret;
+}
+
+static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
+			   const void *val)
+{
+	u8 *buf;
+	size_t count;
+	int ret;
+
+	count = len + 2;
+	buf = kmalloc(count, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = reg & 0xff;
+	buf[1] = (reg >> 8) & 0xff;
+	memcpy(&buf[2], val, len);
+
+	ret = i2c_master_send(client, buf, count);
+	if (ret == count) {
+		ret = 0;
+	} else {
+		if (ret >= 0)
+			ret = -EIO;
+		dev_err(&client->dev, "%s: i2c send failed (%d)\n",
+			__func__, ret);
+	}
+
+	kfree(buf);
+	return ret;
+}
+
+static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
+{
+	return __mxt_write_reg(client, reg, 1, &val);
+}
+
+static struct mxt_object *
+mxt_get_object(struct mxt_data *data, u8 type)
+{
+	struct mxt_object *object;
+	int i;
+
+	for (i = 0; i < data->info.object_num; i++) {
+		object = data->object_table + i;
+		if (object->type == type)
+			return object;
+	}
+
+	dev_warn(&data->client->dev, "Invalid object type T%u\n", type);
+	return NULL;
+}
+
+static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg)
+{
+	struct device *dev = &data->client->dev;
+	u8 status = msg[1];
+	u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16);
+
+	complete(&data->crc_completion);
+
+	if (crc != data->config_crc) {
+		data->config_crc = crc;
+		dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc);
+	}
+
+	/* Detect reset */
+	if (status & MXT_T6_STATUS_RESET)
+		complete(&data->reset_completion);
+
+	/* Output debug if status has changed */
+	if (status != data->t6_status)
+		dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n",
+			status,
+			status == 0 ? " OK" : "",
+			status & MXT_T6_STATUS_RESET ? " RESET" : "",
+			status & MXT_T6_STATUS_OFL ? " OFL" : "",
+			status & MXT_T6_STATUS_SIGERR ? " SIGERR" : "",
+			status & MXT_T6_STATUS_CAL ? " CAL" : "",
+			status & MXT_T6_STATUS_CFGERR ? " CFGERR" : "",
+			status & MXT_T6_STATUS_COMSERR ? " COMSERR" : "");
+
+	/* Save current status */
+	data->t6_status = status;
+}
+
+static int mxt_write_object(struct mxt_data *data,
+				 u8 type, u8 offset, u8 val)
+{
+	struct mxt_object *object;
+	u16 reg;
+
+	object = mxt_get_object(data, type);
+	if (!object || offset >= mxt_obj_size(object))
+		return -EINVAL;
+
+	reg = object->start_address;
+	return mxt_write_reg(data->client, reg + offset, val);
+}
+
+static void mxt_input_button(struct mxt_data *data, u8 *message)
+{
+	struct input_dev *input = data->input_dev;
+	const struct mxt_platform_data *pdata = data->pdata;
+	int i;
+
+	for (i = 0; i < pdata->t19_num_keys; i++) {
+		if (pdata->t19_keymap[i] == KEY_RESERVED)
+			continue;
+
+		/* Active-low switch */
+		input_report_key(input, pdata->t19_keymap[i],
+				 !(message[1] & BIT(i)));
+	}
+}
+
+static void mxt_input_sync(struct mxt_data *data)
+{
+	input_mt_report_pointer_emulation(data->input_dev,
+					  data->pdata->t19_num_keys);
+	input_sync(data->input_dev);
+}
+
+static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
+{
+	struct device *dev = &data->client->dev;
+	struct input_dev *input_dev = data->input_dev;
+	int id;
+	u8 status;
+	int x;
+	int y;
+	int area;
+	int amplitude;
+
+	id = message[0] - data->T9_reportid_min;
+	status = message[1];
+	x = (message[2] << 4) | ((message[4] >> 4) & 0xf);
+	y = (message[3] << 4) | ((message[4] & 0xf));
+
+	/* Handle 10/12 bit switching */
+	if (data->max_x < 1024)
+		x >>= 2;
+	if (data->max_y < 1024)
+		y >>= 2;
+
+	area = message[5];
+	amplitude = message[6];
+
+	dev_dbg(dev,
+		"[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n",
+		id,
+		(status & MXT_T9_DETECT) ? 'D' : '.',
+		(status & MXT_T9_PRESS) ? 'P' : '.',
+		(status & MXT_T9_RELEASE) ? 'R' : '.',
+		(status & MXT_T9_MOVE) ? 'M' : '.',
+		(status & MXT_T9_VECTOR) ? 'V' : '.',
+		(status & MXT_T9_AMP) ? 'A' : '.',
+		(status & MXT_T9_SUPPRESS) ? 'S' : '.',
+		(status & MXT_T9_UNGRIP) ? 'U' : '.',
+		x, y, area, amplitude);
+
+	input_mt_slot(input_dev, id);
+
+	if (status & MXT_T9_DETECT) {
+		/*
+		 * Multiple bits may be set if the host is slow to read
+		 * the status messages, indicating all the events that
+		 * have happened.
+		 */
+		if (status & MXT_T9_RELEASE) {
+			input_mt_report_slot_state(input_dev,
+						   MT_TOOL_FINGER, 0);
+			mxt_input_sync(data);
+		}
+
+		/* Touch active */
+		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1);
+		input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+		input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+		input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude);
+		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area);
+	} else {
+		/* Touch no longer active, close out slot */
+		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0);
+	}
+
+	data->update_input = true;
+}
+
+static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
+{
+	struct device *dev = &data->client->dev;
+	struct input_dev *input_dev = data->input_dev;
+	int id;
+	u8 status;
+	u8 type = 0;
+	u16 x;
+	u16 y;
+	int distance = 0;
+	int tool = 0;
+	u8 major = 0;
+	u8 pressure = 0;
+	u8 orientation = 0;
+
+	id = message[0] - data->T100_reportid_min - 2;
+
+	/* ignore SCRSTATUS events */
+	if (id < 0)
+		return;
+
+	status = message[1];
+	x = get_unaligned_le16(&message[2]);
+	y = get_unaligned_le16(&message[4]);
+
+	if (status & MXT_T100_DETECT) {
+		type = (status & MXT_T100_TYPE_MASK) >> 4;
+
+		switch (type) {
+		case MXT_T100_TYPE_HOVERING_FINGER:
+			tool = MT_TOOL_FINGER;
+			distance = MXT_DISTANCE_HOVERING;
+
+			if (data->t100_aux_vect)
+				orientation = message[data->t100_aux_vect];
+
+			break;
+
+		case MXT_T100_TYPE_FINGER:
+		case MXT_T100_TYPE_GLOVE:
+			tool = MT_TOOL_FINGER;
+			distance = MXT_DISTANCE_ACTIVE_TOUCH;
+
+			if (data->t100_aux_area)
+				major = message[data->t100_aux_area];
+
+			if (data->t100_aux_ampl)
+				pressure = message[data->t100_aux_ampl];
+
+			if (data->t100_aux_vect)
+				orientation = message[data->t100_aux_vect];
+
+			break;
+
+		case MXT_T100_TYPE_PASSIVE_STYLUS:
+			tool = MT_TOOL_PEN;
+
+			/*
+			 * Passive stylus is reported with size zero so
+			 * hardcode.
+			 */
+			major = MXT_TOUCH_MAJOR_DEFAULT;
+
+			if (data->t100_aux_ampl)
+				pressure = message[data->t100_aux_ampl];
+
+			break;
+
+		case MXT_T100_TYPE_LARGE_TOUCH:
+			/* Ignore suppressed touch */
+			break;
+
+		default:
+			dev_dbg(dev, "Unexpected T100 type\n");
+			return;
+		}
+	}
+
+	/*
+	 * Values reported should be non-zero if tool is touching the
+	 * device
+	 */
+	if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER)
+		pressure = MXT_PRESSURE_DEFAULT;
+
+	input_mt_slot(input_dev, id);
+
+	if (status & MXT_T100_DETECT) {
+		dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n",
+			id, type, x, y, major, pressure, orientation);
+
+		input_mt_report_slot_state(input_dev, tool, 1);
+		input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+		input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major);
+		input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+		input_report_abs(input_dev, ABS_MT_DISTANCE, distance);
+		input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation);
+	} else {
+		dev_dbg(dev, "[%u] release\n", id);
+
+		/* close out slot */
+		input_mt_report_slot_state(input_dev, 0, 0);
+	}
+
+	data->update_input = true;
+}
+
+static int mxt_proc_message(struct mxt_data *data, u8 *message)
+{
+	u8 report_id = message[0];
+
+	if (report_id == MXT_RPTID_NOMSG)
+		return 0;
+
+	if (report_id == data->T6_reportid) {
+		mxt_proc_t6_messages(data, message);
+	} else if (!data->input_dev) {
+		/*
+		 * Do not report events if input device
+		 * is not yet registered.
+		 */
+		mxt_dump_message(data, message);
+	} else if (report_id >= data->T9_reportid_min &&
+		   report_id <= data->T9_reportid_max) {
+		mxt_proc_t9_message(data, message);
+	} else if (report_id >= data->T100_reportid_min &&
+		   report_id <= data->T100_reportid_max) {
+		mxt_proc_t100_message(data, message);
+	} else if (report_id == data->T19_reportid) {
+		mxt_input_button(data, message);
+		data->update_input = true;
+	} else {
+		mxt_dump_message(data, message);
+	}
+
+	return 1;
+}
+
+static int mxt_read_and_process_messages(struct mxt_data *data, u8 count)
+{
+	struct device *dev = &data->client->dev;
+	int ret;
+	int i;
+	u8 num_valid = 0;
+
+	/* Safety check for msg_buf */
+	if (count > data->max_reportid)
+		return -EINVAL;
+
+	/* Process remaining messages if necessary */
+	ret = __mxt_read_reg(data->client, data->T5_address,
+				data->T5_msg_size * count, data->msg_buf);
+	if (ret) {
+		dev_err(dev, "Failed to read %u messages (%d)\n", count, ret);
+		return ret;
+	}
+
+	for (i = 0;  i < count; i++) {
+		ret = mxt_proc_message(data,
+			data->msg_buf + data->T5_msg_size * i);
+
+		if (ret == 1)
+			num_valid++;
+	}
+
+	/* return number of messages read */
+	return num_valid;
+}
+
+static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
+{
+	struct device *dev = &data->client->dev;
+	int ret;
+	u8 count, num_left;
+
+	/* Read T44 and T5 together */
+	ret = __mxt_read_reg(data->client, data->T44_address,
+		data->T5_msg_size + 1, data->msg_buf);
+	if (ret) {
+		dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret);
+		return IRQ_NONE;
+	}
+
+	count = data->msg_buf[0];
+
+	/*
+	 * This condition may be caused by the CHG line being configured in
+	 * Mode 0. It results in unnecessary I2C operations but it is benign.
+	 */
+	if (count == 0)
+		return IRQ_NONE;
+
+	if (count > data->max_reportid) {
+		dev_warn(dev, "T44 count %d exceeded max report id\n", count);
+		count = data->max_reportid;
+	}
+
+	/* Process first message */
+	ret = mxt_proc_message(data, data->msg_buf + 1);
+	if (ret < 0) {
+		dev_warn(dev, "Unexpected invalid message\n");
+		return IRQ_NONE;
+	}
+
+	num_left = count - 1;
+
+	/* Process remaining messages if necessary */
+	if (num_left) {
+		ret = mxt_read_and_process_messages(data, num_left);
+		if (ret < 0)
+			goto end;
+		else if (ret != num_left)
+			dev_warn(dev, "Unexpected invalid message\n");
+	}
+
+end:
+	if (data->update_input) {
+		mxt_input_sync(data);
+		data->update_input = false;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int mxt_process_messages_until_invalid(struct mxt_data *data)
+{
+	struct device *dev = &data->client->dev;
+	int count, read;
+	u8 tries = 2;
+
+	count = data->max_reportid;
+
+	/* Read messages until we force an invalid */
+	do {
+		read = mxt_read_and_process_messages(data, count);
+		if (read < count)
+			return 0;
+	} while (--tries);
+
+	if (data->update_input) {
+		mxt_input_sync(data);
+		data->update_input = false;
+	}
+
+	dev_err(dev, "CHG pin isn't cleared\n");
+	return -EBUSY;
+}
+
+static irqreturn_t mxt_process_messages(struct mxt_data *data)
+{
+	int total_handled, num_handled;
+	u8 count = data->last_message_count;
+
+	if (count < 1 || count > data->max_reportid)
+		count = 1;
+
+	/* include final invalid message */
+	total_handled = mxt_read_and_process_messages(data, count + 1);
+	if (total_handled < 0)
+		return IRQ_NONE;
+	/* if there were invalid messages, then we are done */
+	else if (total_handled <= count)
+		goto update_count;
+
+	/* keep reading two msgs until one is invalid or reportid limit */
+	do {
+		num_handled = mxt_read_and_process_messages(data, 2);
+		if (num_handled < 0)
+			return IRQ_NONE;
+
+		total_handled += num_handled;
+
+		if (num_handled < 2)
+			break;
+	} while (total_handled < data->num_touchids);
+
+update_count:
+	data->last_message_count = total_handled;
+
+	if (data->update_input) {
+		mxt_input_sync(data);
+		data->update_input = false;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mxt_interrupt(int irq, void *dev_id)
+{
+	struct mxt_data *data = dev_id;
+
+	if (data->in_bootloader) {
+		/* bootloader state transition completion */
+		complete(&data->bl_completion);
+		return IRQ_HANDLED;
+	}
+
+	if (!data->object_table)
+		return IRQ_HANDLED;
+
+	if (data->T44_address) {
+		return mxt_process_messages_t44(data);
+	} else {
+		return mxt_process_messages(data);
+	}
+}
+
+static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset,
+			  u8 value, bool wait)
+{
+	u16 reg;
+	u8 command_register;
+	int timeout_counter = 0;
+	int ret;
+
+	reg = data->T6_address + cmd_offset;
+
+	ret = mxt_write_reg(data->client, reg, value);
+	if (ret)
+		return ret;
+
+	if (!wait)
+		return 0;
+
+	do {
+		msleep(20);
+		ret = __mxt_read_reg(data->client, reg, 1, &command_register);
+		if (ret)
+			return ret;
+	} while (command_register != 0 && timeout_counter++ <= 100);
+
+	if (timeout_counter > 100) {
+		dev_err(&data->client->dev, "Command failed!\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int mxt_soft_reset(struct mxt_data *data)
+{
+	struct device *dev = &data->client->dev;
+	int ret = 0;
+
+	dev_info(dev, "Resetting device\n");
+
+	disable_irq(data->irq);
+
+	reinit_completion(&data->reset_completion);
+
+	ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false);
+	if (ret)
+		return ret;
+
+	/* Ignore CHG line for 100ms after reset */
+	msleep(100);
+
+	enable_irq(data->irq);
+
+	ret = mxt_wait_for_completion(data, &data->reset_completion,
+				      MXT_RESET_TIMEOUT);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value)
+{
+	/*
+	 * On failure, CRC is set to 0 and config will always be
+	 * downloaded.
+	 */
+	data->config_crc = 0;
+	reinit_completion(&data->crc_completion);
+
+	mxt_t6_command(data, cmd, value, true);
+
+	/*
+	 * Wait for crc message. On failure, CRC is set to 0 and config will
+	 * always be downloaded.
+	 */
+	mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT);
+}
+
+static void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte)
+{
+	static const unsigned int crcpoly = 0x80001B;
+	u32 result;
+	u32 data_word;
+
+	data_word = (secondbyte << 8) | firstbyte;
+	result = ((*crc << 1) ^ data_word);
+
+	if (result & 0x1000000)
+		result ^= crcpoly;
+
+	*crc = result;
+}
+
+static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off)
+{
+	u32 crc = 0;
+	u8 *ptr = base + start_off;
+	u8 *last_val = base + end_off - 1;
+
+	if (end_off < start_off)
+		return -EINVAL;
+
+	while (ptr < last_val) {
+		mxt_calc_crc24(&crc, *ptr, *(ptr + 1));
+		ptr += 2;
+	}
+
+	/* if len is odd, fill the last byte with 0 */
+	if (ptr == last_val)
+		mxt_calc_crc24(&crc, *ptr, 0);
+
+	/* Mask to 24-bit */
+	crc &= 0x00FFFFFF;
+
+	return crc;
+}
+
+static int mxt_prepare_cfg_mem(struct mxt_data *data,
+			       const struct firmware *cfg,
+			       unsigned int data_pos,
+			       unsigned int cfg_start_ofs,
+			       u8 *config_mem,
+			       size_t config_mem_size)
+{
+	struct device *dev = &data->client->dev;
+	struct mxt_object *object;
+	unsigned int type, instance, size, byte_offset;
+	int offset;
+	int ret;
+	int i;
+	u16 reg;
+	u8 val;
+
+	while (data_pos < cfg->size) {
+		/* Read type, instance, length */
+		ret = sscanf(cfg->data + data_pos, "%x %x %x%n",
+			     &type, &instance, &size, &offset);
+		if (ret == 0) {
+			/* EOF */
+			break;
+		} else if (ret != 3) {
+			dev_err(dev, "Bad format: failed to parse object\n");
+			return -EINVAL;
+		}
+		data_pos += offset;
+
+		object = mxt_get_object(data, type);
+		if (!object) {
+			/* Skip object */
+			for (i = 0; i < size; i++) {
+				ret = sscanf(cfg->data + data_pos, "%hhx%n",
+					     &val, &offset);
+				if (ret != 1) {
+					dev_err(dev, "Bad format in T%d at %d\n",
+						type, i);
+					return -EINVAL;
+				}
+				data_pos += offset;
+			}
+			continue;
+		}
+
+		if (size > mxt_obj_size(object)) {
+			/*
+			 * Either we are in fallback mode due to wrong
+			 * config or config from a later fw version,
+			 * or the file is corrupt or hand-edited.
+			 */
+			dev_warn(dev, "Discarding %zu byte(s) in T%u\n",
+				 size - mxt_obj_size(object), type);
+		} else if (mxt_obj_size(object) > size) {
+			/*
+			 * If firmware is upgraded, new bytes may be added to
+			 * end of objects. It is generally forward compatible
+			 * to zero these bytes - previous behaviour will be
+			 * retained. However this does invalidate the CRC and
+			 * will force fallback mode until the configuration is
+			 * updated. We warn here but do nothing else - the
+			 * malloc has zeroed the entire configuration.
+			 */
+			dev_warn(dev, "Zeroing %zu byte(s) in T%d\n",
+				 mxt_obj_size(object) - size, type);
+		}
+
+		if (instance >= mxt_obj_instances(object)) {
+			dev_err(dev, "Object instances exceeded!\n");
+			return -EINVAL;
+		}
+
+		reg = object->start_address + mxt_obj_size(object) * instance;
+
+		for (i = 0; i < size; i++) {
+			ret = sscanf(cfg->data + data_pos, "%hhx%n",
+				     &val,
+				     &offset);
+			if (ret != 1) {
+				dev_err(dev, "Bad format in T%d at %d\n",
+					type, i);
+				return -EINVAL;
+			}
+			data_pos += offset;
+
+			if (i > mxt_obj_size(object))
+				continue;
+
+			byte_offset = reg + i - cfg_start_ofs;
+
+			if (byte_offset >= 0 && byte_offset < config_mem_size) {
+				*(config_mem + byte_offset) = val;
+			} else {
+				dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n",
+					reg, object->type, byte_offset);
+				return -EINVAL;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int mxt_upload_cfg_mem(struct mxt_data *data, unsigned int cfg_start,
+			      u8 *config_mem, size_t config_mem_size)
+{
+	unsigned int byte_offset = 0;
+	int error;
+
+	/* Write configuration as blocks */
+	while (byte_offset < config_mem_size) {
+		unsigned int size = config_mem_size - byte_offset;
+
+		if (size > MXT_MAX_BLOCK_WRITE)
+			size = MXT_MAX_BLOCK_WRITE;
+
+		error = __mxt_write_reg(data->client,
+					cfg_start + byte_offset,
+					size, config_mem + byte_offset);
+		if (error) {
+			dev_err(&data->client->dev,
+				"Config write error, ret=%d\n", error);
+			return error;
+		}
+
+		byte_offset += size;
+	}
+
+	return 0;
+}
+
+static int mxt_init_t7_power_cfg(struct mxt_data *data);
+
+/*
+ * mxt_update_cfg - download configuration to chip
+ *
+ * Atmel Raw Config File Format
+ *
+ * The first four lines of the raw config file contain:
+ *  1) Version
+ *  2) Chip ID Information (first 7 bytes of device memory)
+ *  3) Chip Information Block 24-bit CRC Checksum
+ *  4) Chip Configuration 24-bit CRC Checksum
+ *
+ * The rest of the file consists of one line per object instance:
+ *   <TYPE> <INSTANCE> <SIZE> <CONTENTS>
+ *
+ *   <TYPE> - 2-byte object type as hex
+ *   <INSTANCE> - 2-byte object instance number as hex
+ *   <SIZE> - 2-byte object size as hex
+ *   <CONTENTS> - array of <SIZE> 1-byte hex values
+ */
+static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
+{
+	struct device *dev = &data->client->dev;
+	struct mxt_info cfg_info;
+	int ret;
+	int offset;
+	int data_pos;
+	int i;
+	int cfg_start_ofs;
+	u32 info_crc, config_crc, calculated_crc;
+	u8 *config_mem;
+	size_t config_mem_size;
+
+	mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1);
+
+	if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) {
+		dev_err(dev, "Unrecognised config file\n");
+		return -EINVAL;
+	}
+
+	data_pos = strlen(MXT_CFG_MAGIC);
+
+	/* Load information block and check */
+	for (i = 0; i < sizeof(struct mxt_info); i++) {
+		ret = sscanf(cfg->data + data_pos, "%hhx%n",
+			     (unsigned char *)&cfg_info + i,
+			     &offset);
+		if (ret != 1) {
+			dev_err(dev, "Bad format\n");
+			return -EINVAL;
+		}
+
+		data_pos += offset;
+	}
+
+	if (cfg_info.family_id != data->info.family_id) {
+		dev_err(dev, "Family ID mismatch!\n");
+		return -EINVAL;
+	}
+
+	if (cfg_info.variant_id != data->info.variant_id) {
+		dev_err(dev, "Variant ID mismatch!\n");
+		return -EINVAL;
+	}
+
+	/* Read CRCs */
+	ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset);
+	if (ret != 1) {
+		dev_err(dev, "Bad format: failed to parse Info CRC\n");
+		return -EINVAL;
+	}
+	data_pos += offset;
+
+	ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset);
+	if (ret != 1) {
+		dev_err(dev, "Bad format: failed to parse Config CRC\n");
+		return -EINVAL;
+	}
+	data_pos += offset;
+
+	/*
+	 * The Info Block CRC is calculated over mxt_info and the object
+	 * table. If it does not match then we are trying to load the
+	 * configuration from a different chip or firmware version, so
+	 * the configuration CRC is invalid anyway.
+	 */
+	if (info_crc == data->info_crc) {
+		if (config_crc == 0 || data->config_crc == 0) {
+			dev_info(dev, "CRC zero, attempting to apply config\n");
+		} else if (config_crc == data->config_crc) {
+			dev_dbg(dev, "Config CRC 0x%06X: OK\n",
+				 data->config_crc);
+			return 0;
+		} else {
+			dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
+				 data->config_crc, config_crc);
+		}
+	} else {
+		dev_warn(dev,
+			 "Warning: Info CRC error - device=0x%06X file=0x%06X\n",
+			 data->info_crc, info_crc);
+	}
+
+	/* Malloc memory to store configuration */
+	cfg_start_ofs = MXT_OBJECT_START +
+			data->info.object_num * sizeof(struct mxt_object) +
+			MXT_INFO_CHECKSUM_SIZE;
+	config_mem_size = data->mem_size - cfg_start_ofs;
+	config_mem = kzalloc(config_mem_size, GFP_KERNEL);
+	if (!config_mem) {
+		dev_err(dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	ret = mxt_prepare_cfg_mem(data, cfg, data_pos, cfg_start_ofs,
+				  config_mem, config_mem_size);
+	if (ret)
+		goto release_mem;
+
+	/* Calculate crc of the received configs (not the raw config file) */
+	if (data->T7_address < cfg_start_ofs) {
+		dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n",
+			data->T7_address, cfg_start_ofs);
+		ret = 0;
+		goto release_mem;
+	}
+
+	calculated_crc = mxt_calculate_crc(config_mem,
+					   data->T7_address - cfg_start_ofs,
+					   config_mem_size);
+
+	if (config_crc > 0 && config_crc != calculated_crc)
+		dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n",
+			 calculated_crc, config_crc);
+
+	ret = mxt_upload_cfg_mem(data, cfg_start_ofs,
+				 config_mem, config_mem_size);
+	if (ret)
+		goto release_mem;
+
+	mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);
+
+	ret = mxt_soft_reset(data);
+	if (ret)
+		goto release_mem;
+
+	dev_info(dev, "Config successfully updated\n");
+
+	/* T7 config may have changed */
+	mxt_init_t7_power_cfg(data);
+
+release_mem:
+	kfree(config_mem);
+	return ret;
+}
+
+static int mxt_acquire_irq(struct mxt_data *data)
+{
+	int error;
+
+	enable_irq(data->irq);
+
+	error = mxt_process_messages_until_invalid(data);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static int mxt_get_info(struct mxt_data *data)
+{
+	struct i2c_client *client = data->client;
+	struct mxt_info *info = &data->info;
+	int error;
+
+	/* Read 7-byte info block starting at address 0 */
+	error = __mxt_read_reg(client, 0, sizeof(*info), info);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static void mxt_free_input_device(struct mxt_data *data)
+{
+	if (data->input_dev) {
+		input_unregister_device(data->input_dev);
+		data->input_dev = NULL;
+	}
+}
+
+static void mxt_free_object_table(struct mxt_data *data)
+{
+	kfree(data->object_table);
+	data->object_table = NULL;
+	kfree(data->msg_buf);
+	data->msg_buf = NULL;
+	data->T5_address = 0;
+	data->T5_msg_size = 0;
+	data->T6_reportid = 0;
+	data->T7_address = 0;
+	data->T9_reportid_min = 0;
+	data->T9_reportid_max = 0;
+	data->T19_reportid = 0;
+	data->T44_address = 0;
+	data->T100_reportid_min = 0;
+	data->T100_reportid_max = 0;
+	data->max_reportid = 0;
+}
+
+static int mxt_get_object_table(struct mxt_data *data)
+{
+	struct i2c_client *client = data->client;
+	size_t table_size;
+	struct mxt_object *object_table;
+	int error;
+	int i;
+	u8 reportid;
+	u16 end_address;
+
+	table_size = data->info.object_num * sizeof(struct mxt_object);
+	object_table = kzalloc(table_size, GFP_KERNEL);
+	if (!object_table) {
+		dev_err(&data->client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	error = __mxt_read_reg(client, MXT_OBJECT_START, table_size,
+			object_table);
+	if (error) {
+		kfree(object_table);
+		return error;
+	}
+
+	/* Valid Report IDs start counting from 1 */
+	reportid = 1;
+	data->mem_size = 0;
+	for (i = 0; i < data->info.object_num; i++) {
+		struct mxt_object *object = object_table + i;
+		u8 min_id, max_id;
+
+		le16_to_cpus(&object->start_address);
+
+		if (object->num_report_ids) {
+			min_id = reportid;
+			reportid += object->num_report_ids *
+					mxt_obj_instances(object);
+			max_id = reportid - 1;
+		} else {
+			min_id = 0;
+			max_id = 0;
+		}
+
+		dev_dbg(&data->client->dev,
+			"T%u Start:%u Size:%zu Instances:%zu Report IDs:%u-%u\n",
+			object->type, object->start_address,
+			mxt_obj_size(object), mxt_obj_instances(object),
+			min_id, max_id);
+
+		switch (object->type) {
+		case MXT_GEN_MESSAGE_T5:
+			if (data->info.family_id == 0x80 &&
+			    data->info.version < 0x20) {
+				/*
+				 * On mXT224 firmware versions prior to V2.0
+				 * read and discard unused CRC byte otherwise
+				 * DMA reads are misaligned.
+				 */
+				data->T5_msg_size = mxt_obj_size(object);
+			} else {
+				/* CRC not enabled, so skip last byte */
+				data->T5_msg_size = mxt_obj_size(object) - 1;
+			}
+			data->T5_address = object->start_address;
+			break;
+		case MXT_GEN_COMMAND_T6:
+			data->T6_reportid = min_id;
+			data->T6_address = object->start_address;
+			break;
+		case MXT_GEN_POWER_T7:
+			data->T7_address = object->start_address;
+			break;
+		case MXT_TOUCH_MULTI_T9:
+			data->multitouch = MXT_TOUCH_MULTI_T9;
+			data->T9_reportid_min = min_id;
+			data->T9_reportid_max = max_id;
+			data->num_touchids = object->num_report_ids
+						* mxt_obj_instances(object);
+			break;
+		case MXT_SPT_MESSAGECOUNT_T44:
+			data->T44_address = object->start_address;
+			break;
+		case MXT_SPT_GPIOPWM_T19:
+			data->T19_reportid = min_id;
+			break;
+		case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+			data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100;
+			data->T100_reportid_min = min_id;
+			data->T100_reportid_max = max_id;
+			/* first two report IDs reserved */
+			data->num_touchids = object->num_report_ids - 2;
+			break;
+		}
+
+		end_address = object->start_address
+			+ mxt_obj_size(object) * mxt_obj_instances(object) - 1;
+
+		if (end_address >= data->mem_size)
+			data->mem_size = end_address + 1;
+	}
+
+	/* Store maximum reportid */
+	data->max_reportid = reportid;
+
+	/* If T44 exists, T5 position has to be directly after */
+	if (data->T44_address && (data->T5_address != data->T44_address + 1)) {
+		dev_err(&client->dev, "Invalid T44 position\n");
+		error = -EINVAL;
+		goto free_object_table;
+	}
+
+	data->msg_buf = kcalloc(data->max_reportid,
+				data->T5_msg_size, GFP_KERNEL);
+	if (!data->msg_buf) {
+		dev_err(&client->dev, "Failed to allocate message buffer\n");
+		error = -ENOMEM;
+		goto free_object_table;
+	}
+
+	data->object_table = object_table;
+
+	return 0;
+
+free_object_table:
+	mxt_free_object_table(data);
+	return error;
+}
+
+static int mxt_read_t9_resolution(struct mxt_data *data)
+{
+	struct i2c_client *client = data->client;
+	int error;
+	struct t9_range range;
+	unsigned char orient;
+	struct mxt_object *object;
+
+	object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
+	if (!object)
+		return -EINVAL;
+
+	error = __mxt_read_reg(client,
+			       object->start_address + MXT_T9_RANGE,
+			       sizeof(range), &range);
+	if (error)
+		return error;
+
+	le16_to_cpus(&range.x);
+	le16_to_cpus(&range.y);
+
+	error =  __mxt_read_reg(client,
+				object->start_address + MXT_T9_ORIENT,
+				1, &orient);
+	if (error)
+		return error;
+
+	/* Handle default values */
+	if (range.x == 0)
+		range.x = 1023;
+
+	if (range.y == 0)
+		range.y = 1023;
+
+	if (orient & MXT_T9_ORIENT_SWITCH) {
+		data->max_x = range.y;
+		data->max_y = range.x;
+	} else {
+		data->max_x = range.x;
+		data->max_y = range.y;
+	}
+
+	dev_dbg(&client->dev,
+		"Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+	return 0;
+}
+
+static int mxt_read_t100_config(struct mxt_data *data)
+{
+	struct i2c_client *client = data->client;
+	int error;
+	struct mxt_object *object;
+	u16 range_x, range_y;
+	u8 cfg, tchaux;
+	u8 aux;
+
+	object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100);
+	if (!object)
+		return -EINVAL;
+
+	error = __mxt_read_reg(client,
+			       object->start_address + MXT_T100_XRANGE,
+			       sizeof(range_x), &range_x);
+	if (error)
+		return error;
+
+	le16_to_cpus(&range_x);
+
+	error = __mxt_read_reg(client,
+			       object->start_address + MXT_T100_YRANGE,
+			       sizeof(range_y), &range_y);
+	if (error)
+		return error;
+
+	le16_to_cpus(&range_y);
+
+	error =  __mxt_read_reg(client,
+				object->start_address + MXT_T100_CFG1,
+				1, &cfg);
+	if (error)
+		return error;
+
+	error =  __mxt_read_reg(client,
+				object->start_address + MXT_T100_TCHAUX,
+				1, &tchaux);
+	if (error)
+		return error;
+
+	/* Handle default values */
+	if (range_x == 0)
+		range_x = 1023;
+
+	if (range_y == 0)
+		range_y = 1023;
+
+	if (cfg & MXT_T100_CFG_SWITCHXY) {
+		data->max_x = range_y;
+		data->max_y = range_x;
+	} else {
+		data->max_x = range_x;
+		data->max_y = range_y;
+	}
+
+	/* allocate aux bytes */
+	aux = 6;
+
+	if (tchaux & MXT_T100_TCHAUX_VECT)
+		data->t100_aux_vect = aux++;
+
+	if (tchaux & MXT_T100_TCHAUX_AMPL)
+		data->t100_aux_ampl = aux++;
+
+	if (tchaux & MXT_T100_TCHAUX_AREA)
+		data->t100_aux_area = aux++;
+
+	dev_dbg(&client->dev,
+		"T100 aux mappings vect:%u ampl:%u area:%u\n",
+		data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area);
+
+	dev_info(&client->dev,
+		 "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+	return 0;
+}
+
+static int mxt_input_open(struct input_dev *dev);
+static void mxt_input_close(struct input_dev *dev);
+
+static void mxt_set_up_as_touchpad(struct input_dev *input_dev,
+				   struct mxt_data *data)
+{
+	const struct mxt_platform_data *pdata = data->pdata;
+	int i;
+
+	input_dev->name = "Atmel maXTouch Touchpad";
+
+	__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+
+	input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
+	input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
+	input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+			  MXT_PIXELS_PER_MM);
+	input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+			  MXT_PIXELS_PER_MM);
+
+	for (i = 0; i < pdata->t19_num_keys; i++)
+		if (pdata->t19_keymap[i] != KEY_RESERVED)
+			input_set_capability(input_dev, EV_KEY,
+					     pdata->t19_keymap[i]);
+}
+
+static int mxt_initialize_input_device(struct mxt_data *data)
+{
+	const struct mxt_platform_data *pdata = data->pdata;
+	struct device *dev = &data->client->dev;
+	struct input_dev *input_dev;
+	int error;
+	unsigned int num_mt_slots;
+	unsigned int mt_flags = 0;
+
+	switch (data->multitouch) {
+	case MXT_TOUCH_MULTI_T9:
+		num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
+		error = mxt_read_t9_resolution(data);
+		if (error)
+			dev_warn(dev, "Failed to initialize T9 resolution\n");
+		break;
+
+	case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+		num_mt_slots = data->num_touchids;
+		error = mxt_read_t100_config(data);
+		if (error)
+			dev_warn(dev, "Failed to read T100 config\n");
+		break;
+
+	default:
+		dev_err(dev, "Invalid multitouch object\n");
+		return -EINVAL;
+	}
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	input_dev->name = "Atmel maXTouch Touchscreen";
+	input_dev->phys = data->phys;
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = dev;
+	input_dev->open = mxt_input_open;
+	input_dev->close = mxt_input_close;
+
+	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+
+	/* For single touch */
+	input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, data->max_y, 0, 0);
+
+	if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+	    (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+	     data->t100_aux_ampl)) {
+		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
+	}
+
+	/* If device has buttons we assume it is a touchpad */
+	if (pdata->t19_num_keys) {
+		mxt_set_up_as_touchpad(input_dev, data);
+		mt_flags |= INPUT_MT_POINTER;
+	} else {
+		mt_flags |= INPUT_MT_DIRECT;
+	}
+
+	/* For multi touch */
+	error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags);
+	if (error) {
+		dev_err(dev, "Error %d initialising slots\n", error);
+		goto err_free_mem;
+	}
+
+	if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100) {
+		input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
+				     0, MT_TOOL_MAX, 0, 0);
+		input_set_abs_params(input_dev, ABS_MT_DISTANCE,
+				     MXT_DISTANCE_ACTIVE_TOUCH,
+				     MXT_DISTANCE_HOVERING,
+				     0, 0);
+	}
+
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+			     0, data->max_x, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+			     0, data->max_y, 0, 0);
+
+	if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+	    (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+	     data->t100_aux_area)) {
+		input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+				     0, MXT_MAX_AREA, 0, 0);
+	}
+
+	if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+	    (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+	     data->t100_aux_ampl)) {
+		input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+				     0, 255, 0, 0);
+	}
+
+	if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+	    data->t100_aux_vect) {
+		input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+				     0, 255, 0, 0);
+	}
+
+	if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+	    data->t100_aux_ampl) {
+		input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+				     0, 255, 0, 0);
+	}
+
+	if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+	    data->t100_aux_vect) {
+		input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+				     0, 255, 0, 0);
+	}
+
+	input_set_drvdata(input_dev, data);
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(dev, "Error %d registering input device\n", error);
+		goto err_free_mem;
+	}
+
+	data->input_dev = input_dev;
+
+	return 0;
+
+err_free_mem:
+	input_free_device(input_dev);
+	return error;
+}
+
+static int mxt_configure_objects(struct mxt_data *data,
+				 const struct firmware *cfg);
+
+static void mxt_config_cb(const struct firmware *cfg, void *ctx)
+{
+	mxt_configure_objects(ctx, cfg);
+	release_firmware(cfg);
+}
+
+static int mxt_initialize(struct mxt_data *data)
+{
+	struct i2c_client *client = data->client;
+	int recovery_attempts = 0;
+	int error;
+
+	while (1) {
+		error = mxt_get_info(data);
+		if (!error)
+			break;
+
+		/* Check bootloader state */
+		error = mxt_probe_bootloader(data, false);
+		if (error) {
+			dev_info(&client->dev, "Trying alternate bootloader address\n");
+			error = mxt_probe_bootloader(data, true);
+			if (error) {
+				/* Chip is not in appmode or bootloader mode */
+				return error;
+			}
+		}
+
+		/* OK, we are in bootloader, see if we can recover */
+		if (++recovery_attempts > 1) {
+			dev_err(&client->dev, "Could not recover from bootloader mode\n");
+			/*
+			 * We can reflash from this state, so do not
+			 * abort initialization.
+			 */
+			data->in_bootloader = true;
+			return 0;
+		}
+
+		/* Attempt to exit bootloader into app mode */
+		mxt_send_bootloader_cmd(data, false);
+		msleep(MXT_FW_RESET_TIME);
+	}
+
+	/* Get object table information */
+	error = mxt_get_object_table(data);
+	if (error) {
+		dev_err(&client->dev, "Error %d reading object table\n", error);
+		return error;
+	}
+
+	error = mxt_acquire_irq(data);
+	if (error)
+		goto err_free_object_table;
+
+	error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME,
+					&client->dev, GFP_KERNEL, data,
+					mxt_config_cb);
+	if (error) {
+		dev_err(&client->dev, "Failed to invoke firmware loader: %d\n",
+			error);
+		goto err_free_object_table;
+	}
+
+	return 0;
+
+err_free_object_table:
+	mxt_free_object_table(data);
+	return error;
+}
+
+static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep)
+{
+	struct device *dev = &data->client->dev;
+	int error;
+	struct t7_config *new_config;
+	struct t7_config deepsleep = { .active = 0, .idle = 0 };
+
+	if (sleep == MXT_POWER_CFG_DEEPSLEEP)
+		new_config = &deepsleep;
+	else
+		new_config = &data->t7_cfg;
+
+	error = __mxt_write_reg(data->client, data->T7_address,
+				sizeof(data->t7_cfg), new_config);
+	if (error)
+		return error;
+
+	dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n",
+		new_config->active, new_config->idle);
+
+	return 0;
+}
+
+static int mxt_init_t7_power_cfg(struct mxt_data *data)
+{
+	struct device *dev = &data->client->dev;
+	int error;
+	bool retry = false;
+
+recheck:
+	error = __mxt_read_reg(data->client, data->T7_address,
+				sizeof(data->t7_cfg), &data->t7_cfg);
+	if (error)
+		return error;
+
+	if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) {
+		if (!retry) {
+			dev_dbg(dev, "T7 cfg zero, resetting\n");
+			mxt_soft_reset(data);
+			retry = true;
+			goto recheck;
+		} else {
+			dev_dbg(dev, "T7 cfg zero after reset, overriding\n");
+			data->t7_cfg.active = 20;
+			data->t7_cfg.idle = 100;
+			return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
+		}
+	}
+
+	dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n",
+		data->t7_cfg.active, data->t7_cfg.idle);
+	return 0;
+}
+
+static int mxt_configure_objects(struct mxt_data *data,
+				 const struct firmware *cfg)
+{
+	struct device *dev = &data->client->dev;
+	struct mxt_info *info = &data->info;
+	int error;
+
+	error = mxt_init_t7_power_cfg(data);
+	if (error) {
+		dev_err(dev, "Failed to initialize power cfg\n");
+		return error;
+	}
+
+	if (cfg) {
+		error = mxt_update_cfg(data, cfg);
+		if (error)
+			dev_warn(dev, "Error %d updating config\n", error);
+	}
+
+	if (data->multitouch) {
+		error = mxt_initialize_input_device(data);
+		if (error)
+			return error;
+	} else {
+		dev_warn(dev, "No touch object detected\n");
+	}
+
+	dev_info(dev,
+		 "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
+		 info->family_id, info->variant_id, info->version >> 4,
+		 info->version & 0xf, info->build, info->object_num);
+
+	return 0;
+}
+
+/* Firmware Version is returned as Major.Minor.Build */
+static ssize_t mxt_fw_version_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	struct mxt_info *info = &data->info;
+	return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
+			 info->version >> 4, info->version & 0xf, info->build);
+}
+
+/* Hardware Version is returned as FamilyID.VariantID */
+static ssize_t mxt_hw_version_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	struct mxt_info *info = &data->info;
+	return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
+			 info->family_id, info->variant_id);
+}
+
+static ssize_t mxt_show_instance(char *buf, int count,
+				 struct mxt_object *object, int instance,
+				 const u8 *val)
+{
+	int i;
+
+	if (mxt_obj_instances(object) > 1)
+		count += scnprintf(buf + count, PAGE_SIZE - count,
+				   "Instance %u\n", instance);
+
+	for (i = 0; i < mxt_obj_size(object); i++)
+		count += scnprintf(buf + count, PAGE_SIZE - count,
+				"\t[%2u]: %02x (%d)\n", i, val[i], val[i]);
+	count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+
+	return count;
+}
+
+static ssize_t mxt_object_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	struct mxt_object *object;
+	int count = 0;
+	int i, j;
+	int error;
+	u8 *obuf;
+
+	/* Pre-allocate buffer large enough to hold max sized object. */
+	obuf = kmalloc(256, GFP_KERNEL);
+	if (!obuf)
+		return -ENOMEM;
+
+	error = 0;
+	for (i = 0; i < data->info.object_num; i++) {
+		object = data->object_table + i;
+
+		if (!mxt_object_readable(object->type))
+			continue;
+
+		count += scnprintf(buf + count, PAGE_SIZE - count,
+				"T%u:\n", object->type);
+
+		for (j = 0; j < mxt_obj_instances(object); j++) {
+			u16 size = mxt_obj_size(object);
+			u16 addr = object->start_address + j * size;
+
+			error = __mxt_read_reg(data->client, addr, size, obuf);
+			if (error)
+				goto done;
+
+			count = mxt_show_instance(buf, count, object, j, obuf);
+		}
+	}
+
+done:
+	kfree(obuf);
+	return error ?: count;
+}
+
+static int mxt_check_firmware_format(struct device *dev,
+				     const struct firmware *fw)
+{
+	unsigned int pos = 0;
+	char c;
+
+	while (pos < fw->size) {
+		c = *(fw->data + pos);
+
+		if (c < '0' || (c > '9' && c < 'A') || c > 'F')
+			return 0;
+
+		pos++;
+	}
+
+	/*
+	 * To convert file try:
+	 * xxd -r -p mXTXXX__APP_VX-X-XX.enc > maxtouch.fw
+	 */
+	dev_err(dev, "Aborting: firmware file must be in binary format\n");
+
+	return -EINVAL;
+}
+
+static int mxt_load_fw(struct device *dev, const char *fn)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	const struct firmware *fw = NULL;
+	unsigned int frame_size;
+	unsigned int pos = 0;
+	unsigned int retry = 0;
+	unsigned int frame = 0;
+	int ret;
+
+	ret = request_firmware(&fw, fn, dev);
+	if (ret) {
+		dev_err(dev, "Unable to open firmware %s\n", fn);
+		return ret;
+	}
+
+	/* Check for incorrect enc file */
+	ret = mxt_check_firmware_format(dev, fw);
+	if (ret)
+		goto release_firmware;
+
+	if (!data->in_bootloader) {
+		/* Change to the bootloader mode */
+		data->in_bootloader = true;
+
+		ret = mxt_t6_command(data, MXT_COMMAND_RESET,
+				     MXT_BOOT_VALUE, false);
+		if (ret)
+			goto release_firmware;
+
+		msleep(MXT_RESET_TIME);
+
+		/* Do not need to scan since we know family ID */
+		ret = mxt_lookup_bootloader_address(data, 0);
+		if (ret)
+			goto release_firmware;
+
+		mxt_free_input_device(data);
+		mxt_free_object_table(data);
+	} else {
+		enable_irq(data->irq);
+	}
+
+	reinit_completion(&data->bl_completion);
+
+	ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false);
+	if (ret) {
+		/* Bootloader may still be unlocked from previous attempt */
+		ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false);
+		if (ret)
+			goto disable_irq;
+	} else {
+		dev_info(dev, "Unlocking bootloader\n");
+
+		/* Unlock bootloader */
+		ret = mxt_send_bootloader_cmd(data, true);
+		if (ret)
+			goto disable_irq;
+	}
+
+	while (pos < fw->size) {
+		ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true);
+		if (ret)
+			goto disable_irq;
+
+		frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
+
+		/* Take account of CRC bytes */
+		frame_size += 2;
+
+		/* Write one frame to device */
+		ret = mxt_bootloader_write(data, fw->data + pos, frame_size);
+		if (ret)
+			goto disable_irq;
+
+		ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true);
+		if (ret) {
+			retry++;
+
+			/* Back off by 20ms per retry */
+			msleep(retry * 20);
+
+			if (retry > 20) {
+				dev_err(dev, "Retry count exceeded\n");
+				goto disable_irq;
+			}
+		} else {
+			retry = 0;
+			pos += frame_size;
+			frame++;
+		}
+
+		if (frame % 50 == 0)
+			dev_dbg(dev, "Sent %d frames, %d/%zd bytes\n",
+				frame, pos, fw->size);
+	}
+
+	/* Wait for flash. */
+	ret = mxt_wait_for_completion(data, &data->bl_completion,
+				      MXT_FW_RESET_TIME);
+	if (ret)
+		goto disable_irq;
+
+	dev_dbg(dev, "Sent %d frames, %d bytes\n", frame, pos);
+
+	/*
+	 * Wait for device to reset. Some bootloader versions do not assert
+	 * the CHG line after bootloading has finished, so ignore potential
+	 * errors.
+	 */
+	mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME);
+
+	data->in_bootloader = false;
+
+disable_irq:
+	disable_irq(data->irq);
+release_firmware:
+	release_firmware(fw);
+	return ret;
+}
+
+static ssize_t mxt_update_fw_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	int error;
+
+	error = mxt_load_fw(dev, MXT_FW_NAME);
+	if (error) {
+		dev_err(dev, "The firmware update failed(%d)\n", error);
+		count = error;
+	} else {
+		dev_info(dev, "The firmware update succeeded\n");
+
+		error = mxt_initialize(data);
+		if (error)
+			return error;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL);
+static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store);
+
+static struct attribute *mxt_attrs[] = {
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	&dev_attr_object.attr,
+	&dev_attr_update_fw.attr,
+	NULL
+};
+
+static const struct attribute_group mxt_attr_group = {
+	.attrs = mxt_attrs,
+};
+
+static void mxt_start(struct mxt_data *data)
+{
+	switch (data->pdata->suspend_mode) {
+	case MXT_SUSPEND_T9_CTRL:
+		mxt_soft_reset(data);
+
+		/* Touch enable */
+		/* 0x83 = SCANEN | RPTEN | ENABLE */
+		mxt_write_object(data,
+				MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0x83);
+		break;
+
+	case MXT_SUSPEND_DEEP_SLEEP:
+	default:
+		mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
+
+		/* Recalibrate since chip has been in deep sleep */
+		mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false);
+		break;
+	}
+
+}
+
+static void mxt_stop(struct mxt_data *data)
+{
+	switch (data->pdata->suspend_mode) {
+	case MXT_SUSPEND_T9_CTRL:
+		/* Touch disable */
+		mxt_write_object(data,
+				MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0);
+		break;
+
+	case MXT_SUSPEND_DEEP_SLEEP:
+	default:
+		mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP);
+		break;
+	}
+}
+
+static int mxt_input_open(struct input_dev *dev)
+{
+	struct mxt_data *data = input_get_drvdata(dev);
+
+	mxt_start(data);
+
+	return 0;
+}
+
+static void mxt_input_close(struct input_dev *dev)
+{
+	struct mxt_data *data = input_get_drvdata(dev);
+
+	mxt_stop(data);
+}
+
+#ifdef CONFIG_OF
+static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
+{
+	struct mxt_platform_data *pdata;
+	struct device_node *np = client->dev.of_node;
+	u32 *keymap;
+	int proplen, ret;
+
+	if (!np)
+		return ERR_PTR(-ENOENT);
+
+	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	if (of_find_property(np, "linux,gpio-keymap", &proplen)) {
+		pdata->t19_num_keys = proplen / sizeof(u32);
+
+		keymap = devm_kzalloc(&client->dev,
+				pdata->t19_num_keys * sizeof(keymap[0]),
+				GFP_KERNEL);
+		if (!keymap)
+			return ERR_PTR(-ENOMEM);
+
+		ret = of_property_read_u32_array(np, "linux,gpio-keymap",
+						 keymap, pdata->t19_num_keys);
+		if (ret)
+			dev_warn(&client->dev,
+				 "Couldn't read linux,gpio-keymap: %d\n", ret);
+
+		pdata->t19_keymap = keymap;
+	}
+
+	pdata->suspend_mode = MXT_SUSPEND_DEEP_SLEEP;
+
+	return pdata;
+}
+#else
+static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
+{
+	return ERR_PTR(-ENOENT);
+}
+#endif
+
+#ifdef CONFIG_ACPI
+
+struct mxt_acpi_platform_data {
+	const char *hid;
+	struct mxt_platform_data pdata;
+};
+
+static unsigned int samus_touchpad_buttons[] = {
+	KEY_RESERVED,
+	KEY_RESERVED,
+	KEY_RESERVED,
+	BTN_LEFT
+};
+
+static struct mxt_acpi_platform_data samus_platform_data[] = {
+	{
+		/* Touchpad */
+		.hid	= "ATML0000",
+		.pdata	= {
+			.t19_num_keys	= ARRAY_SIZE(samus_touchpad_buttons),
+			.t19_keymap	= samus_touchpad_buttons,
+		},
+	},
+	{
+		/* Touchscreen */
+		.hid	= "ATML0001",
+	},
+	{ }
+};
+
+static unsigned int chromebook_tp_buttons[] = {
+	KEY_RESERVED,
+	KEY_RESERVED,
+	KEY_RESERVED,
+	KEY_RESERVED,
+	KEY_RESERVED,
+	BTN_LEFT
+};
+
+static struct mxt_acpi_platform_data chromebook_platform_data[] = {
+	{
+		/* Touchpad */
+		.hid	= "ATML0000",
+		.pdata	= {
+			.t19_num_keys	= ARRAY_SIZE(chromebook_tp_buttons),
+			.t19_keymap	= chromebook_tp_buttons,
+		},
+	},
+	{
+		/* Touchscreen */
+		.hid	= "ATML0001",
+	},
+	{ }
+};
+
+static const struct dmi_system_id mxt_dmi_table[] = {
+	{
+		/* 2015 Google Pixel */
+		.ident = "Chromebook Pixel 2",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Samus"),
+		},
+		.driver_data = samus_platform_data,
+	},
+	{
+		/* Other Google Chromebooks */
+		.ident = "Chromebook",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
+		},
+		.driver_data = chromebook_platform_data,
+	},
+	{ }
+};
+
+static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
+{
+	struct acpi_device *adev;
+	const struct dmi_system_id *system_id;
+	const struct mxt_acpi_platform_data *acpi_pdata;
+
+	/*
+	 * Ignore ACPI devices representing bootloader mode.
+	 *
+	 * This is a bit of a hack: Google Chromebook BIOS creates ACPI
+	 * devices for both application and bootloader modes, but we are
+	 * interested in application mode only (if device is in bootloader
+	 * mode we'll end up switching into application anyway). So far
+	 * application mode addresses were all above 0x40, so we'll use it
+	 * as a threshold.
+	 */
+	if (client->addr < 0x40)
+		return ERR_PTR(-ENXIO);
+
+	adev = ACPI_COMPANION(&client->dev);
+	if (!adev)
+		return ERR_PTR(-ENOENT);
+
+	system_id = dmi_first_match(mxt_dmi_table);
+	if (!system_id)
+		return ERR_PTR(-ENOENT);
+
+	acpi_pdata = system_id->driver_data;
+	if (!acpi_pdata)
+		return ERR_PTR(-ENOENT);
+
+	while (acpi_pdata->hid) {
+		if (!strcmp(acpi_device_hid(adev), acpi_pdata->hid))
+			return &acpi_pdata->pdata;
+
+		acpi_pdata++;
+	}
+
+	return ERR_PTR(-ENOENT);
+}
+#else
+static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
+{
+	return ERR_PTR(-ENOENT);
+}
+#endif
+
+static const struct mxt_platform_data *
+mxt_get_platform_data(struct i2c_client *client)
+{
+	const struct mxt_platform_data *pdata;
+
+	pdata = dev_get_platdata(&client->dev);
+	if (pdata)
+		return pdata;
+
+	pdata = mxt_parse_dt(client);
+	if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
+		return pdata;
+
+	pdata = mxt_parse_acpi(client);
+	if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
+		return pdata;
+
+	dev_err(&client->dev, "No platform data specified\n");
+	return ERR_PTR(-EINVAL);
+}
+
+static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct mxt_data *data;
+	const struct mxt_platform_data *pdata;
+	int error;
+
+	pdata = mxt_get_platform_data(client);
+	if (IS_ERR(pdata))
+		return PTR_ERR(pdata);
+
+	data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
+	if (!data) {
+		dev_err(&client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
+		 client->adapter->nr, client->addr);
+
+	data->client = client;
+	data->pdata = pdata;
+	data->irq = client->irq;
+	i2c_set_clientdata(client, data);
+
+	init_completion(&data->bl_completion);
+	init_completion(&data->reset_completion);
+	init_completion(&data->crc_completion);
+
+	error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
+				     pdata->irqflags | IRQF_ONESHOT,
+				     client->name, data);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		goto err_free_mem;
+	}
+
+	disable_irq(client->irq);
+
+	error = mxt_initialize(data);
+	if (error)
+		goto err_free_irq;
+
+	error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
+	if (error) {
+		dev_err(&client->dev, "Failure %d creating sysfs group\n",
+			error);
+		goto err_free_object;
+	}
+
+	return 0;
+
+err_free_object:
+	mxt_free_input_device(data);
+	mxt_free_object_table(data);
+err_free_irq:
+	free_irq(client->irq, data);
+err_free_mem:
+	kfree(data);
+	return error;
+}
+
+static int mxt_remove(struct i2c_client *client)
+{
+	struct mxt_data *data = i2c_get_clientdata(client);
+
+	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
+	free_irq(data->irq, data);
+	mxt_free_input_device(data);
+	mxt_free_object_table(data);
+	kfree(data);
+
+	return 0;
+}
+
+static int __maybe_unused mxt_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mxt_data *data = i2c_get_clientdata(client);
+	struct input_dev *input_dev = data->input_dev;
+
+	if (!input_dev)
+		return 0;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users)
+		mxt_stop(data);
+
+	mutex_unlock(&input_dev->mutex);
+
+	return 0;
+}
+
+static int __maybe_unused mxt_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mxt_data *data = i2c_get_clientdata(client);
+	struct input_dev *input_dev = data->input_dev;
+
+	if (!input_dev)
+		return 0;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users)
+		mxt_start(data);
+
+	mutex_unlock(&input_dev->mutex);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume);
+
+static const struct of_device_id mxt_of_match[] = {
+	{ .compatible = "atmel,maxtouch", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mxt_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id mxt_acpi_id[] = {
+	{ "ATML0000", 0 },	/* Touchpad */
+	{ "ATML0001", 0 },	/* Touchscreen */
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, mxt_acpi_id);
+#endif
+
+static const struct i2c_device_id mxt_id[] = {
+	{ "qt602240_ts", 0 },
+	{ "atmel_mxt_ts", 0 },
+	{ "atmel_mxt_tp", 0 },
+	{ "maxtouch", 0 },
+	{ "mXT224", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, mxt_id);
+
+static struct i2c_driver mxt_driver = {
+	.driver = {
+		.name	= "atmel_mxt_ts",
+		.of_match_table = of_match_ptr(mxt_of_match),
+		.acpi_match_table = ACPI_PTR(mxt_acpi_id),
+		.pm	= &mxt_pm_ops,
+	},
+	.probe		= mxt_probe,
+	.remove		= mxt_remove,
+	.id_table	= mxt_id,
+};
+
+module_i2c_driver(mxt_driver);
+
+/* Module information */
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c
new file mode 100644
index 0000000..6592fc5
--- /dev/null
+++ b/drivers/input/touchscreen/auo-pixcir-ts.c
@@ -0,0 +1,696 @@
+/*
+ * Driver for AUO in-cell touchscreens
+ *
+ * Copyright (c) 2011 Heiko Stuebner <heiko@sntech.de>
+ *
+ * loosely based on auo_touch.c from Dell Streak vendor-kernel
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/input/auo-pixcir-ts.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+/*
+ * Coordinate calculation:
+ * X1 = X1_LSB + X1_MSB*256
+ * Y1 = Y1_LSB + Y1_MSB*256
+ * X2 = X2_LSB + X2_MSB*256
+ * Y2 = Y2_LSB + Y2_MSB*256
+ */
+#define AUO_PIXCIR_REG_X1_LSB		0x00
+#define AUO_PIXCIR_REG_X1_MSB		0x01
+#define AUO_PIXCIR_REG_Y1_LSB		0x02
+#define AUO_PIXCIR_REG_Y1_MSB		0x03
+#define AUO_PIXCIR_REG_X2_LSB		0x04
+#define AUO_PIXCIR_REG_X2_MSB		0x05
+#define AUO_PIXCIR_REG_Y2_LSB		0x06
+#define AUO_PIXCIR_REG_Y2_MSB		0x07
+
+#define AUO_PIXCIR_REG_STRENGTH		0x0d
+#define AUO_PIXCIR_REG_STRENGTH_X1_LSB	0x0e
+#define AUO_PIXCIR_REG_STRENGTH_X1_MSB	0x0f
+
+#define AUO_PIXCIR_REG_RAW_DATA_X	0x2b
+#define AUO_PIXCIR_REG_RAW_DATA_Y	0x4f
+
+#define AUO_PIXCIR_REG_X_SENSITIVITY	0x6f
+#define AUO_PIXCIR_REG_Y_SENSITIVITY	0x70
+#define AUO_PIXCIR_REG_INT_SETTING	0x71
+#define AUO_PIXCIR_REG_INT_WIDTH	0x72
+#define AUO_PIXCIR_REG_POWER_MODE	0x73
+
+#define AUO_PIXCIR_REG_VERSION		0x77
+#define AUO_PIXCIR_REG_CALIBRATE	0x78
+
+#define AUO_PIXCIR_REG_TOUCHAREA_X1	0x1e
+#define AUO_PIXCIR_REG_TOUCHAREA_Y1	0x1f
+#define AUO_PIXCIR_REG_TOUCHAREA_X2	0x20
+#define AUO_PIXCIR_REG_TOUCHAREA_Y2	0x21
+
+#define AUO_PIXCIR_REG_EEPROM_CALIB_X	0x42
+#define AUO_PIXCIR_REG_EEPROM_CALIB_Y	0xad
+
+#define AUO_PIXCIR_INT_TPNUM_MASK	0xe0
+#define AUO_PIXCIR_INT_TPNUM_SHIFT	5
+#define AUO_PIXCIR_INT_RELEASE		(1 << 4)
+#define AUO_PIXCIR_INT_ENABLE		(1 << 3)
+#define AUO_PIXCIR_INT_POL_HIGH		(1 << 2)
+#define AUO_PIXCIR_INT_MODE_MASK	0x03
+
+/*
+ * Power modes:
+ * active:	scan speed 60Hz
+ * sleep:	scan speed 10Hz can be auto-activated, wakeup on 1st touch
+ * deep sleep:	scan speed 1Hz can only be entered or left manually.
+ */
+#define AUO_PIXCIR_POWER_ACTIVE		0x00
+#define AUO_PIXCIR_POWER_SLEEP		0x01
+#define AUO_PIXCIR_POWER_DEEP_SLEEP	0x02
+#define AUO_PIXCIR_POWER_MASK		0x03
+
+#define AUO_PIXCIR_POWER_ALLOW_SLEEP	(1 << 2)
+#define AUO_PIXCIR_POWER_IDLE_TIME(ms)	((ms & 0xf) << 4)
+
+#define AUO_PIXCIR_CALIBRATE		0x03
+
+#define AUO_PIXCIR_EEPROM_CALIB_X_LEN	62
+#define AUO_PIXCIR_EEPROM_CALIB_Y_LEN	36
+
+#define AUO_PIXCIR_RAW_DATA_X_LEN	18
+#define AUO_PIXCIR_RAW_DATA_Y_LEN	11
+
+#define AUO_PIXCIR_STRENGTH_ENABLE	(1 << 0)
+
+/* Touchscreen absolute values */
+#define AUO_PIXCIR_REPORT_POINTS	2
+#define AUO_PIXCIR_MAX_AREA		0xff
+#define AUO_PIXCIR_PENUP_TIMEOUT_MS	10
+
+struct auo_pixcir_ts {
+	struct i2c_client	*client;
+	struct input_dev	*input;
+	const struct auo_pixcir_ts_platdata *pdata;
+	char			phys[32];
+
+	/* special handling for touch_indicate interupt mode */
+	bool			touch_ind_mode;
+
+	wait_queue_head_t	wait;
+	bool			stopped;
+};
+
+struct auo_point_t {
+	int	coord_x;
+	int	coord_y;
+	int	area_major;
+	int	area_minor;
+	int	orientation;
+};
+
+static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts,
+				   struct auo_point_t *point)
+{
+	struct i2c_client *client = ts->client;
+	const struct auo_pixcir_ts_platdata *pdata = ts->pdata;
+	uint8_t raw_coord[8];
+	uint8_t raw_area[4];
+	int i, ret;
+
+	/* touch coordinates */
+	ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_X1_LSB,
+					    8, raw_coord);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to read coordinate, %d\n", ret);
+		return ret;
+	}
+
+	/* touch area */
+	ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_TOUCHAREA_X1,
+					    4, raw_area);
+	if (ret < 0) {
+		dev_err(&client->dev, "could not read touch area, %d\n", ret);
+		return ret;
+	}
+
+	for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
+		point[i].coord_x =
+			raw_coord[4 * i + 1] << 8 | raw_coord[4 * i];
+		point[i].coord_y =
+			raw_coord[4 * i + 3] << 8 | raw_coord[4 * i + 2];
+
+		if (point[i].coord_x > pdata->x_max ||
+		    point[i].coord_y > pdata->y_max) {
+			dev_warn(&client->dev, "coordinates (%d,%d) invalid\n",
+				point[i].coord_x, point[i].coord_y);
+			point[i].coord_x = point[i].coord_y = 0;
+		}
+
+		/* determine touch major, minor and orientation */
+		point[i].area_major = max(raw_area[2 * i], raw_area[2 * i + 1]);
+		point[i].area_minor = min(raw_area[2 * i], raw_area[2 * i + 1]);
+		point[i].orientation = raw_area[2 * i] > raw_area[2 * i + 1];
+	}
+
+	return 0;
+}
+
+static irqreturn_t auo_pixcir_interrupt(int irq, void *dev_id)
+{
+	struct auo_pixcir_ts *ts = dev_id;
+	const struct auo_pixcir_ts_platdata *pdata = ts->pdata;
+	struct auo_point_t point[AUO_PIXCIR_REPORT_POINTS];
+	int i;
+	int ret;
+	int fingers = 0;
+	int abs = -1;
+
+	while (!ts->stopped) {
+
+		/* check for up event in touch touch_ind_mode */
+		if (ts->touch_ind_mode) {
+			if (gpio_get_value(pdata->gpio_int) == 0) {
+				input_mt_sync(ts->input);
+				input_report_key(ts->input, BTN_TOUCH, 0);
+				input_sync(ts->input);
+				break;
+			}
+		}
+
+		ret = auo_pixcir_collect_data(ts, point);
+		if (ret < 0) {
+			/* we want to loop only in touch_ind_mode */
+			if (!ts->touch_ind_mode)
+				break;
+
+			wait_event_timeout(ts->wait, ts->stopped,
+				msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS));
+			continue;
+		}
+
+		for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
+			if (point[i].coord_x > 0 || point[i].coord_y > 0) {
+				input_report_abs(ts->input, ABS_MT_POSITION_X,
+						 point[i].coord_x);
+				input_report_abs(ts->input, ABS_MT_POSITION_Y,
+						 point[i].coord_y);
+				input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+						 point[i].area_major);
+				input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+						 point[i].area_minor);
+				input_report_abs(ts->input, ABS_MT_ORIENTATION,
+						 point[i].orientation);
+				input_mt_sync(ts->input);
+
+				/* use first finger as source for singletouch */
+				if (fingers == 0)
+					abs = i;
+
+				/* number of touch points could also be queried
+				 * via i2c but would require an additional call
+				 */
+				fingers++;
+			}
+		}
+
+		input_report_key(ts->input, BTN_TOUCH, fingers > 0);
+
+		if (abs > -1) {
+			input_report_abs(ts->input, ABS_X, point[abs].coord_x);
+			input_report_abs(ts->input, ABS_Y, point[abs].coord_y);
+		}
+
+		input_sync(ts->input);
+
+		/* we want to loop only in touch_ind_mode */
+		if (!ts->touch_ind_mode)
+			break;
+
+		wait_event_timeout(ts->wait, ts->stopped,
+				 msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS));
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Set the power mode of the device.
+ * Valid modes are
+ * - AUO_PIXCIR_POWER_ACTIVE
+ * - AUO_PIXCIR_POWER_SLEEP - automatically left on first touch
+ * - AUO_PIXCIR_POWER_DEEP_SLEEP
+ */
+static int auo_pixcir_power_mode(struct auo_pixcir_ts *ts, int mode)
+{
+	struct i2c_client *client = ts->client;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_POWER_MODE);
+	if (ret < 0) {
+		dev_err(&client->dev, "unable to read reg %Xh, %d\n",
+			AUO_PIXCIR_REG_POWER_MODE, ret);
+		return ret;
+	}
+
+	ret &= ~AUO_PIXCIR_POWER_MASK;
+	ret |= mode;
+
+	ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_POWER_MODE, ret);
+	if (ret) {
+		dev_err(&client->dev, "unable to write reg %Xh, %d\n",
+			AUO_PIXCIR_REG_POWER_MODE, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int auo_pixcir_int_config(struct auo_pixcir_ts *ts,
+					   int int_setting)
+{
+	struct i2c_client *client = ts->client;
+	const struct auo_pixcir_ts_platdata *pdata = ts->pdata;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
+	if (ret < 0) {
+		dev_err(&client->dev, "unable to read reg %Xh, %d\n",
+			AUO_PIXCIR_REG_INT_SETTING, ret);
+		return ret;
+	}
+
+	ret &= ~AUO_PIXCIR_INT_MODE_MASK;
+	ret |= int_setting;
+	ret |= AUO_PIXCIR_INT_POL_HIGH; /* always use high for interrupts */
+
+	ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING,
+					ret);
+	if (ret < 0) {
+		dev_err(&client->dev, "unable to write reg %Xh, %d\n",
+			AUO_PIXCIR_REG_INT_SETTING, ret);
+		return ret;
+	}
+
+	ts->touch_ind_mode = pdata->int_setting == AUO_PIXCIR_INT_TOUCH_IND;
+
+	return 0;
+}
+
+/* control the generation of interrupts on the device side */
+static int auo_pixcir_int_toggle(struct auo_pixcir_ts *ts, bool enable)
+{
+	struct i2c_client *client = ts->client;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
+	if (ret < 0) {
+		dev_err(&client->dev, "unable to read reg %Xh, %d\n",
+			AUO_PIXCIR_REG_INT_SETTING, ret);
+		return ret;
+	}
+
+	if (enable)
+		ret |= AUO_PIXCIR_INT_ENABLE;
+	else
+		ret &= ~AUO_PIXCIR_INT_ENABLE;
+
+	ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING,
+					ret);
+	if (ret < 0) {
+		dev_err(&client->dev, "unable to write reg %Xh, %d\n",
+			AUO_PIXCIR_REG_INT_SETTING, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int auo_pixcir_start(struct auo_pixcir_ts *ts)
+{
+	struct i2c_client *client = ts->client;
+	int ret;
+
+	ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_ACTIVE);
+	if (ret < 0) {
+		dev_err(&client->dev, "could not set power mode, %d\n",
+			ret);
+		return ret;
+	}
+
+	ts->stopped = false;
+	mb();
+	enable_irq(client->irq);
+
+	ret = auo_pixcir_int_toggle(ts, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "could not enable interrupt, %d\n",
+			ret);
+		disable_irq(client->irq);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int auo_pixcir_stop(struct auo_pixcir_ts *ts)
+{
+	struct i2c_client *client = ts->client;
+	int ret;
+
+	ret = auo_pixcir_int_toggle(ts, 0);
+	if (ret < 0) {
+		dev_err(&client->dev, "could not disable interrupt, %d\n",
+			ret);
+		return ret;
+	}
+
+	/* disable receiving of interrupts */
+	disable_irq(client->irq);
+	ts->stopped = true;
+	mb();
+	wake_up(&ts->wait);
+
+	return auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_DEEP_SLEEP);
+}
+
+static int auo_pixcir_input_open(struct input_dev *dev)
+{
+	struct auo_pixcir_ts *ts = input_get_drvdata(dev);
+
+	return auo_pixcir_start(ts);
+}
+
+static void auo_pixcir_input_close(struct input_dev *dev)
+{
+	struct auo_pixcir_ts *ts = input_get_drvdata(dev);
+
+	auo_pixcir_stop(ts);
+
+	return;
+}
+
+static int __maybe_unused auo_pixcir_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct auo_pixcir_ts *ts = i2c_get_clientdata(client);
+	struct input_dev *input = ts->input;
+	int ret = 0;
+
+	mutex_lock(&input->mutex);
+
+	/* when configured as wakeup source, device should always wake system
+	 * therefore start device if necessary
+	 */
+	if (device_may_wakeup(&client->dev)) {
+		/* need to start device if not open, to be wakeup source */
+		if (!input->users) {
+			ret = auo_pixcir_start(ts);
+			if (ret)
+				goto unlock;
+		}
+
+		enable_irq_wake(client->irq);
+		ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_SLEEP);
+	} else if (input->users) {
+		ret = auo_pixcir_stop(ts);
+	}
+
+unlock:
+	mutex_unlock(&input->mutex);
+
+	return ret;
+}
+
+static int __maybe_unused auo_pixcir_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct auo_pixcir_ts *ts = i2c_get_clientdata(client);
+	struct input_dev *input = ts->input;
+	int ret = 0;
+
+	mutex_lock(&input->mutex);
+
+	if (device_may_wakeup(&client->dev)) {
+		disable_irq_wake(client->irq);
+
+		/* need to stop device if it was not open on suspend */
+		if (!input->users) {
+			ret = auo_pixcir_stop(ts);
+			if (ret)
+				goto unlock;
+		}
+
+		/* device wakes automatically from SLEEP */
+	} else if (input->users) {
+		ret = auo_pixcir_start(ts);
+	}
+
+unlock:
+	mutex_unlock(&input->mutex);
+
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops,
+			 auo_pixcir_suspend, auo_pixcir_resume);
+
+#ifdef CONFIG_OF
+static struct auo_pixcir_ts_platdata *auo_pixcir_parse_dt(struct device *dev)
+{
+	struct auo_pixcir_ts_platdata *pdata;
+	struct device_node *np = dev->of_node;
+
+	if (!np)
+		return ERR_PTR(-ENOENT);
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		dev_err(dev, "failed to allocate platform data\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	pdata->gpio_int = of_get_gpio(np, 0);
+	if (!gpio_is_valid(pdata->gpio_int)) {
+		dev_err(dev, "failed to get interrupt gpio\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	pdata->gpio_rst = of_get_gpio(np, 1);
+	if (!gpio_is_valid(pdata->gpio_rst)) {
+		dev_err(dev, "failed to get reset gpio\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (of_property_read_u32(np, "x-size", &pdata->x_max)) {
+		dev_err(dev, "failed to get x-size property\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (of_property_read_u32(np, "y-size", &pdata->y_max)) {
+		dev_err(dev, "failed to get y-size property\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* default to asserting the interrupt when the screen is touched */
+	pdata->int_setting = AUO_PIXCIR_INT_TOUCH_IND;
+
+	return pdata;
+}
+#else
+static struct auo_pixcir_ts_platdata *auo_pixcir_parse_dt(struct device *dev)
+{
+	return ERR_PTR(-EINVAL);
+}
+#endif
+
+static void auo_pixcir_reset(void *data)
+{
+	struct auo_pixcir_ts *ts = data;
+
+	gpio_set_value(ts->pdata->gpio_rst, 0);
+}
+
+static int auo_pixcir_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	const struct auo_pixcir_ts_platdata *pdata;
+	struct auo_pixcir_ts *ts;
+	struct input_dev *input_dev;
+	int version;
+	int error;
+
+	pdata = dev_get_platdata(&client->dev);
+	if (!pdata) {
+		pdata = auo_pixcir_parse_dt(&client->dev);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+	}
+
+	ts = devm_kzalloc(&client->dev,
+			  sizeof(struct auo_pixcir_ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	input_dev = devm_input_allocate_device(&client->dev);
+	if (!input_dev) {
+		dev_err(&client->dev, "could not allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->pdata = pdata;
+	ts->client = client;
+	ts->input = input_dev;
+	ts->touch_ind_mode = 0;
+	ts->stopped = true;
+	init_waitqueue_head(&ts->wait);
+
+	snprintf(ts->phys, sizeof(ts->phys),
+		 "%s/input0", dev_name(&client->dev));
+
+	input_dev->name = "AUO-Pixcir touchscreen";
+	input_dev->phys = ts->phys;
+	input_dev->id.bustype = BUS_I2C;
+
+	input_dev->open = auo_pixcir_input_open;
+	input_dev->close = auo_pixcir_input_close;
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+
+	/* For single touch */
+	input_set_abs_params(input_dev, ABS_X, 0, pdata->x_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, pdata->y_max, 0, 0);
+
+	/* For multi touch */
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+			     pdata->x_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+			     pdata->y_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
+			     AUO_PIXCIR_MAX_AREA, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0,
+			     AUO_PIXCIR_MAX_AREA, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+
+	input_set_drvdata(ts->input, ts);
+
+	error = devm_gpio_request_one(&client->dev, pdata->gpio_int,
+				      GPIOF_DIR_IN, "auo_pixcir_ts_int");
+	if (error) {
+		dev_err(&client->dev, "request of gpio %d failed, %d\n",
+			pdata->gpio_int, error);
+		return error;
+	}
+
+	error = devm_gpio_request_one(&client->dev, pdata->gpio_rst,
+				      GPIOF_DIR_OUT | GPIOF_INIT_HIGH,
+				      "auo_pixcir_ts_rst");
+	if (error) {
+		dev_err(&client->dev, "request of gpio %d failed, %d\n",
+			pdata->gpio_rst, error);
+		return error;
+	}
+
+	error = devm_add_action(&client->dev, auo_pixcir_reset, ts);
+	if (error) {
+		auo_pixcir_reset(ts);
+		dev_err(&client->dev, "failed to register reset action, %d\n",
+			error);
+		return error;
+	}
+
+	msleep(200);
+
+	version = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_VERSION);
+	if (version < 0) {
+		error = version;
+		return error;
+	}
+
+	dev_info(&client->dev, "firmware version 0x%X\n", version);
+
+	error = auo_pixcir_int_config(ts, pdata->int_setting);
+	if (error)
+		return error;
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, auo_pixcir_interrupt,
+					  IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					  input_dev->name, ts);
+	if (error) {
+		dev_err(&client->dev, "irq %d requested failed, %d\n",
+			client->irq, error);
+		return error;
+	}
+
+	/* stop device and put it into deep sleep until it is opened */
+	error = auo_pixcir_stop(ts);
+	if (error)
+		return error;
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(&client->dev, "could not register input device, %d\n",
+			error);
+		return error;
+	}
+
+	i2c_set_clientdata(client, ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id auo_pixcir_idtable[] = {
+	{ "auo_pixcir_ts", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, auo_pixcir_idtable);
+
+#ifdef CONFIG_OF
+static const struct of_device_id auo_pixcir_ts_dt_idtable[] = {
+	{ .compatible = "auo,auo_pixcir_ts" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, auo_pixcir_ts_dt_idtable);
+#endif
+
+static struct i2c_driver auo_pixcir_driver = {
+	.driver = {
+		.name	= "auo_pixcir_ts",
+		.pm	= &auo_pixcir_pm_ops,
+		.of_match_table	= of_match_ptr(auo_pixcir_ts_dt_idtable),
+	},
+	.probe		= auo_pixcir_probe,
+	.id_table	= auo_pixcir_idtable,
+};
+
+module_i2c_driver(auo_pixcir_driver);
+
+MODULE_DESCRIPTION("AUO-PIXCIR touchscreen driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
diff --git a/drivers/input/touchscreen/bcm_iproc_tsc.c b/drivers/input/touchscreen/bcm_iproc_tsc.c
new file mode 100644
index 0000000..ae460a5
--- /dev/null
+++ b/drivers/input/touchscreen/bcm_iproc_tsc.c
@@ -0,0 +1,522 @@
+/*
+* Copyright (C) 2015 Broadcom Corporation
+*
+* 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 version 2.
+*
+* This program is distributed "as is" WITHOUT ANY WARRANTY of any
+* kind, whether express or implied; without even the implied warranty
+* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/keyboard.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <asm/irq.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/serio.h>
+
+#define IPROC_TS_NAME "iproc-ts"
+
+#define PEN_DOWN_STATUS     1
+#define PEN_UP_STATUS       0
+
+#define X_MIN               0
+#define Y_MIN               0
+#define X_MAX               0xFFF
+#define Y_MAX               0xFFF
+
+/* Value given by controller for invalid coordinate. */
+#define INVALID_COORD       0xFFFFFFFF
+
+/* Register offsets */
+#define REGCTL1             0x00
+#define REGCTL2             0x04
+#define INTERRUPT_THRES     0x08
+#define INTERRUPT_MASK      0x0c
+
+#define INTERRUPT_STATUS    0x10
+#define CONTROLLER_STATUS   0x14
+#define FIFO_DATA           0x18
+#define FIFO_DATA_X_Y_MASK  0xFFFF
+#define ANALOG_CONTROL      0x1c
+
+#define AUX_DATA            0x20
+#define DEBOUNCE_CNTR_STAT  0x24
+#define SCAN_CNTR_STAT      0x28
+#define REM_CNTR_STAT       0x2c
+
+#define SETTLING_TIMER_STAT 0x30
+#define SPARE_REG           0x34
+#define SOFT_BYPASS_CONTROL 0x38
+#define SOFT_BYPASS_DATA    0x3c
+
+
+/* Bit values for INTERRUPT_MASK and INTERRUPT_STATUS regs */
+#define TS_PEN_INTR_MASK        BIT(0)
+#define TS_FIFO_INTR_MASK       BIT(2)
+
+/* Bit values for CONTROLLER_STATUS reg1 */
+#define TS_PEN_DOWN             BIT(0)
+
+/* Shift values for control reg1 */
+#define SCANNING_PERIOD_SHIFT   24
+#define DEBOUNCE_TIMEOUT_SHIFT  16
+#define SETTLING_TIMEOUT_SHIFT  8
+#define TOUCH_TIMEOUT_SHIFT     0
+
+/* Shift values for coordinates from fifo */
+#define X_COORD_SHIFT  0
+#define Y_COORD_SHIFT  16
+
+/* Bit values for REGCTL2 */
+#define TS_CONTROLLER_EN_BIT    BIT(16)
+#define TS_CONTROLLER_AVGDATA_SHIFT 8
+#define TS_CONTROLLER_AVGDATA_MASK (0x7 << TS_CONTROLLER_AVGDATA_SHIFT)
+#define TS_CONTROLLER_PWR_LDO   BIT(5)
+#define TS_CONTROLLER_PWR_ADC   BIT(4)
+#define TS_CONTROLLER_PWR_BGP   BIT(3)
+#define TS_CONTROLLER_PWR_TS    BIT(2)
+#define TS_WIRE_MODE_BIT        BIT(1)
+
+#define dbg_reg(dev, priv, reg) \
+	dev_dbg(dev, "%20s= 0x%08x\n", #reg, readl((priv)->regs + reg))
+
+struct tsc_param {
+	/* Each step is 1024 us.  Valid 1-256 */
+	u32 scanning_period;
+
+	/*  Each step is 512 us.  Valid 0-255 */
+	u32 debounce_timeout;
+
+	/*
+	 * The settling duration (in ms) is the amount of time the tsc
+	 * waits to allow the voltage to settle after turning on the
+	 * drivers in detection mode. Valid values: 0-11
+	 *   0 =  0.008 ms
+	 *   1 =  0.01 ms
+	 *   2 =  0.02 ms
+	 *   3 =  0.04 ms
+	 *   4 =  0.08 ms
+	 *   5 =  0.16 ms
+	 *   6 =  0.32 ms
+	 *   7 =  0.64 ms
+	 *   8 =  1.28 ms
+	 *   9 =  2.56 ms
+	 *   10 = 5.12 ms
+	 *   11 = 10.24 ms
+	 */
+	u32 settling_timeout;
+
+	/* touch timeout in sample counts */
+	u32 touch_timeout;
+
+	/*
+	 * Number of data samples which are averaged before a final data point
+	 * is placed into the FIFO
+	 */
+	u32 average_data;
+
+	/* FIFO threshold */
+	u32 fifo_threshold;
+
+	/* Optional standard touchscreen properties. */
+	u32 max_x;
+	u32 max_y;
+	u32 fuzz_x;
+	u32 fuzz_y;
+	bool invert_x;
+	bool invert_y;
+};
+
+struct iproc_ts_priv {
+	struct platform_device *pdev;
+	struct input_dev *idev;
+
+	void __iomem *regs;
+	struct clk *tsc_clk;
+
+	int  pen_status;
+	struct tsc_param cfg_params;
+};
+
+/*
+ * Set default values the same as hardware reset values
+ * except for fifo_threshold with is set to 1.
+ */
+static const struct tsc_param iproc_default_config = {
+	.scanning_period  = 0x5,  /* 1 to 256 */
+	.debounce_timeout = 0x28, /* 0 to 255 */
+	.settling_timeout = 0x7,  /* 0 to 11 */
+	.touch_timeout    = 0xa,  /* 0 to 255 */
+	.average_data     = 5,    /* entry 5 = 32 pts */
+	.fifo_threshold   = 1,    /* 0 to 31 */
+	.max_x            = X_MAX,
+	.max_y            = Y_MAX,
+};
+
+static void ts_reg_dump(struct iproc_ts_priv *priv)
+{
+	struct device *dev = &priv->pdev->dev;
+
+	dbg_reg(dev, priv, REGCTL1);
+	dbg_reg(dev, priv, REGCTL2);
+	dbg_reg(dev, priv, INTERRUPT_THRES);
+	dbg_reg(dev, priv, INTERRUPT_MASK);
+	dbg_reg(dev, priv, INTERRUPT_STATUS);
+	dbg_reg(dev, priv, CONTROLLER_STATUS);
+	dbg_reg(dev, priv, FIFO_DATA);
+	dbg_reg(dev, priv, ANALOG_CONTROL);
+	dbg_reg(dev, priv, AUX_DATA);
+	dbg_reg(dev, priv, DEBOUNCE_CNTR_STAT);
+	dbg_reg(dev, priv, SCAN_CNTR_STAT);
+	dbg_reg(dev, priv, REM_CNTR_STAT);
+	dbg_reg(dev, priv, SETTLING_TIMER_STAT);
+	dbg_reg(dev, priv, SPARE_REG);
+	dbg_reg(dev, priv, SOFT_BYPASS_CONTROL);
+	dbg_reg(dev, priv, SOFT_BYPASS_DATA);
+}
+
+static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)
+{
+	struct platform_device *pdev = data;
+	struct iproc_ts_priv *priv = platform_get_drvdata(pdev);
+	u32 intr_status;
+	u32 raw_coordinate;
+	u16 x;
+	u16 y;
+	int i;
+	bool needs_sync = false;
+
+	intr_status = readl(priv->regs + INTERRUPT_STATUS);
+	intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
+	if (intr_status == 0)
+		return IRQ_NONE;
+
+	/* Clear all interrupt status bits, write-1-clear */
+	writel(intr_status, priv->regs + INTERRUPT_STATUS);
+
+	/* Pen up/down */
+	if (intr_status & TS_PEN_INTR_MASK) {
+		if (readl(priv->regs + CONTROLLER_STATUS) & TS_PEN_DOWN)
+			priv->pen_status = PEN_DOWN_STATUS;
+		else
+			priv->pen_status = PEN_UP_STATUS;
+
+		input_report_key(priv->idev, BTN_TOUCH, priv->pen_status);
+		needs_sync = true;
+
+		dev_dbg(&priv->pdev->dev,
+			"pen up-down (%d)\n", priv->pen_status);
+	}
+
+	/* coordinates in FIFO exceed the theshold */
+	if (intr_status & TS_FIFO_INTR_MASK) {
+		for (i = 0; i < priv->cfg_params.fifo_threshold; i++) {
+			raw_coordinate = readl(priv->regs + FIFO_DATA);
+			if (raw_coordinate == INVALID_COORD)
+				continue;
+
+			/*
+			 * The x and y coordinate are 16 bits each
+			 * with the x in the lower 16 bits and y in the
+			 * upper 16 bits.
+			 */
+			x = (raw_coordinate >> X_COORD_SHIFT) &
+				FIFO_DATA_X_Y_MASK;
+			y = (raw_coordinate >> Y_COORD_SHIFT) &
+				FIFO_DATA_X_Y_MASK;
+
+			/* We only want to retain the 12 msb of the 16 */
+			x = (x >> 4) & 0x0FFF;
+			y = (y >> 4) & 0x0FFF;
+
+			/* adjust x y according to lcd tsc mount angle */
+			if (priv->cfg_params.invert_x)
+				x = priv->cfg_params.max_x - x;
+
+			if (priv->cfg_params.invert_y)
+				y = priv->cfg_params.max_y - y;
+
+			input_report_abs(priv->idev, ABS_X, x);
+			input_report_abs(priv->idev, ABS_Y, y);
+			needs_sync = true;
+
+			dev_dbg(&priv->pdev->dev, "xy (0x%x 0x%x)\n", x, y);
+		}
+	}
+
+	if (needs_sync)
+		input_sync(priv->idev);
+
+	return IRQ_HANDLED;
+}
+
+static int iproc_ts_start(struct input_dev *idev)
+{
+	struct iproc_ts_priv *priv = input_get_drvdata(idev);
+	u32 val;
+	int error;
+
+	/* Enable clock */
+	error = clk_prepare_enable(priv->tsc_clk);
+	if (error) {
+		dev_err(&priv->pdev->dev, "%s clk_prepare_enable failed %d\n",
+			__func__, error);
+		return error;
+	}
+
+	/*
+	 * Interrupt is generated when:
+	 *  FIFO reaches the int_th value, and pen event(up/down)
+	 */
+	val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
+	writel(val, priv->regs + INTERRUPT_MASK);
+
+	writel(priv->cfg_params.fifo_threshold, priv->regs + INTERRUPT_THRES);
+
+	/* Initialize control reg1 */
+	val = 0;
+	val |= priv->cfg_params.scanning_period << SCANNING_PERIOD_SHIFT;
+	val |= priv->cfg_params.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT;
+	val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT;
+	val |= priv->cfg_params.touch_timeout << TOUCH_TIMEOUT_SHIFT;
+	writel(val, priv->regs + REGCTL1);
+
+	/* Try to clear all interrupt status */
+	val = readl(priv->regs + INTERRUPT_STATUS);
+	val |= TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK;
+	writel(val, priv->regs + INTERRUPT_STATUS);
+
+	/* Initialize control reg2 */
+	val = readl(priv->regs + REGCTL2);
+	val |= TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT;
+
+	val &= ~TS_CONTROLLER_AVGDATA_MASK;
+	val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT;
+
+	val &= ~(TS_CONTROLLER_PWR_LDO |	/* PWR up LDO */
+		   TS_CONTROLLER_PWR_ADC |	/* PWR up ADC */
+		   TS_CONTROLLER_PWR_BGP |	/* PWR up BGP */
+		   TS_CONTROLLER_PWR_TS);	/* PWR up TS */
+
+	writel(val, priv->regs + REGCTL2);
+
+	ts_reg_dump(priv);
+
+	return 0;
+}
+
+static void iproc_ts_stop(struct input_dev *dev)
+{
+	u32 val;
+	struct iproc_ts_priv *priv = input_get_drvdata(dev);
+
+	writel(0, priv->regs + INTERRUPT_MASK); /* Disable all interrupts */
+
+	/* Only power down touch screen controller */
+	val = readl(priv->regs + REGCTL2);
+	val |= TS_CONTROLLER_PWR_TS;
+	writel(val, priv->regs + REGCTL2);
+
+	clk_disable(priv->tsc_clk);
+}
+
+static int iproc_get_tsc_config(struct device *dev, struct iproc_ts_priv *priv)
+{
+	struct device_node *np = dev->of_node;
+	u32 val;
+
+	priv->cfg_params = iproc_default_config;
+
+	if (!np)
+		return 0;
+
+	if (of_property_read_u32(np, "scanning_period", &val) >= 0) {
+		if (val < 1 || val > 256) {
+			dev_err(dev, "scanning_period (%u) must be [1-256]\n",
+				val);
+			return -EINVAL;
+		}
+		priv->cfg_params.scanning_period = val;
+	}
+
+	if (of_property_read_u32(np, "debounce_timeout", &val) >= 0) {
+		if (val > 255) {
+			dev_err(dev, "debounce_timeout (%u) must be [0-255]\n",
+				val);
+			return -EINVAL;
+		}
+		priv->cfg_params.debounce_timeout = val;
+	}
+
+	if (of_property_read_u32(np, "settling_timeout", &val) >= 0) {
+		if (val > 11) {
+			dev_err(dev, "settling_timeout (%u) must be [0-11]\n",
+				val);
+			return -EINVAL;
+		}
+		priv->cfg_params.settling_timeout = val;
+	}
+
+	if (of_property_read_u32(np, "touch_timeout", &val) >= 0) {
+		if (val > 255) {
+			dev_err(dev, "touch_timeout (%u) must be [0-255]\n",
+				val);
+			return -EINVAL;
+		}
+		priv->cfg_params.touch_timeout = val;
+	}
+
+	if (of_property_read_u32(np, "average_data", &val) >= 0) {
+		if (val > 8) {
+			dev_err(dev, "average_data (%u) must be [0-8]\n", val);
+			return -EINVAL;
+		}
+		priv->cfg_params.average_data = val;
+	}
+
+	if (of_property_read_u32(np, "fifo_threshold", &val) >= 0) {
+		if (val > 31) {
+			dev_err(dev, "fifo_threshold (%u)) must be [0-31]\n",
+				val);
+			return -EINVAL;
+		}
+		priv->cfg_params.fifo_threshold = val;
+	}
+
+	/* Parse optional properties. */
+	of_property_read_u32(np, "touchscreen-size-x", &priv->cfg_params.max_x);
+	of_property_read_u32(np, "touchscreen-size-y", &priv->cfg_params.max_y);
+
+	of_property_read_u32(np, "touchscreen-fuzz-x",
+			     &priv->cfg_params.fuzz_x);
+	of_property_read_u32(np, "touchscreen-fuzz-y",
+			     &priv->cfg_params.fuzz_y);
+
+	priv->cfg_params.invert_x =
+		of_property_read_bool(np, "touchscreen-inverted-x");
+	priv->cfg_params.invert_y =
+		of_property_read_bool(np, "touchscreen-inverted-y");
+
+	return 0;
+}
+
+static int iproc_ts_probe(struct platform_device *pdev)
+{
+	struct iproc_ts_priv *priv;
+	struct input_dev *idev;
+	struct resource *res;
+	int irq;
+	int error;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	/* touchscreen controller memory mapped regs */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->regs)) {
+		error = PTR_ERR(priv->regs);
+		dev_err(&pdev->dev, "unable to map I/O memory: %d\n", error);
+		return error;
+	}
+
+	priv->tsc_clk = devm_clk_get(&pdev->dev, "tsc_clk");
+	if (IS_ERR(priv->tsc_clk)) {
+		error = PTR_ERR(priv->tsc_clk);
+		dev_err(&pdev->dev,
+			"failed getting clock tsc_clk: %d\n", error);
+		return error;
+	}
+
+	priv->pdev = pdev;
+	error = iproc_get_tsc_config(&pdev->dev, priv);
+	if (error) {
+		dev_err(&pdev->dev, "get_tsc_config failed: %d\n", error);
+		return error;
+	}
+
+	idev = devm_input_allocate_device(&pdev->dev);
+	if (!idev) {
+		dev_err(&pdev->dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	priv->idev = idev;
+	priv->pen_status = PEN_UP_STATUS;
+
+	/* Set input device info  */
+	idev->name = IPROC_TS_NAME;
+	idev->dev.parent = &pdev->dev;
+
+	idev->id.bustype = BUS_HOST;
+	idev->id.vendor = SERIO_UNKNOWN;
+	idev->id.product = 0;
+	idev->id.version = 0;
+
+	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	__set_bit(BTN_TOUCH, idev->keybit);
+
+	input_set_abs_params(idev, ABS_X, X_MIN, priv->cfg_params.max_x,
+			     priv->cfg_params.fuzz_x, 0);
+	input_set_abs_params(idev, ABS_Y, Y_MIN, priv->cfg_params.max_y,
+			     priv->cfg_params.fuzz_y, 0);
+
+	idev->open = iproc_ts_start;
+	idev->close = iproc_ts_stop;
+
+	input_set_drvdata(idev, priv);
+	platform_set_drvdata(pdev, priv);
+
+	/* get interrupt */
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "platform_get_irq failed: %d\n", irq);
+		return irq;
+	}
+
+	error = devm_request_irq(&pdev->dev, irq,
+				 iproc_touchscreen_interrupt,
+				 IRQF_SHARED, IPROC_TS_NAME, pdev);
+	if (error)
+		return error;
+
+	error = input_register_device(priv->idev);
+	if (error) {
+		dev_err(&pdev->dev,
+			"failed to register input device: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id iproc_ts_of_match[] = {
+	{.compatible = "brcm,iproc-touchscreen", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, iproc_ts_of_match);
+
+static struct platform_driver iproc_ts_driver = {
+	.probe = iproc_ts_probe,
+	.driver = {
+		.name	= IPROC_TS_NAME,
+		.of_match_table = of_match_ptr(iproc_ts_of_match),
+	},
+};
+
+module_platform_driver(iproc_ts_driver);
+
+MODULE_DESCRIPTION("IPROC Touchscreen driver");
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c
new file mode 100644
index 0000000..931417e
--- /dev/null
+++ b/drivers/input/touchscreen/bu21013_ts.c
@@ -0,0 +1,732 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * License terms:GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/input.h>
+#include <linux/input/bu21013.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#define PEN_DOWN_INTR	0
+#define MAX_FINGERS	2
+#define RESET_DELAY	30
+#define PENUP_TIMEOUT	(10)
+#define DELTA_MIN	16
+#define MASK_BITS	0x03
+#define SHIFT_8		8
+#define SHIFT_2		2
+#define LENGTH_OF_BUFFER	11
+#define I2C_RETRY_COUNT	5
+
+#define BU21013_SENSORS_BTN_0_7_REG	0x70
+#define BU21013_SENSORS_BTN_8_15_REG	0x71
+#define BU21013_SENSORS_BTN_16_23_REG	0x72
+#define BU21013_X1_POS_MSB_REG		0x73
+#define BU21013_X1_POS_LSB_REG		0x74
+#define BU21013_Y1_POS_MSB_REG		0x75
+#define BU21013_Y1_POS_LSB_REG		0x76
+#define BU21013_X2_POS_MSB_REG		0x77
+#define BU21013_X2_POS_LSB_REG		0x78
+#define BU21013_Y2_POS_MSB_REG		0x79
+#define BU21013_Y2_POS_LSB_REG		0x7A
+#define BU21013_INT_CLR_REG		0xE8
+#define BU21013_INT_MODE_REG		0xE9
+#define BU21013_GAIN_REG		0xEA
+#define BU21013_OFFSET_MODE_REG		0xEB
+#define BU21013_XY_EDGE_REG		0xEC
+#define BU21013_RESET_REG		0xED
+#define BU21013_CALIB_REG		0xEE
+#define BU21013_DONE_REG		0xEF
+#define BU21013_SENSOR_0_7_REG		0xF0
+#define BU21013_SENSOR_8_15_REG		0xF1
+#define BU21013_SENSOR_16_23_REG	0xF2
+#define BU21013_POS_MODE1_REG		0xF3
+#define BU21013_POS_MODE2_REG		0xF4
+#define BU21013_CLK_MODE_REG		0xF5
+#define BU21013_IDLE_REG		0xFA
+#define BU21013_FILTER_REG		0xFB
+#define BU21013_TH_ON_REG		0xFC
+#define BU21013_TH_OFF_REG		0xFD
+
+
+#define BU21013_RESET_ENABLE		0x01
+
+#define BU21013_SENSORS_EN_0_7		0x3F
+#define BU21013_SENSORS_EN_8_15		0xFC
+#define BU21013_SENSORS_EN_16_23	0x1F
+
+#define BU21013_POS_MODE1_0		0x02
+#define BU21013_POS_MODE1_1		0x04
+#define BU21013_POS_MODE1_2		0x08
+
+#define BU21013_POS_MODE2_ZERO		0x01
+#define BU21013_POS_MODE2_AVG1		0x02
+#define BU21013_POS_MODE2_AVG2		0x04
+#define BU21013_POS_MODE2_EN_XY		0x08
+#define BU21013_POS_MODE2_EN_RAW	0x10
+#define BU21013_POS_MODE2_MULTI		0x80
+
+#define BU21013_CLK_MODE_DIV		0x01
+#define BU21013_CLK_MODE_EXT		0x02
+#define BU21013_CLK_MODE_CALIB		0x80
+
+#define BU21013_IDLET_0			0x01
+#define BU21013_IDLET_1			0x02
+#define BU21013_IDLET_2			0x04
+#define BU21013_IDLET_3			0x08
+#define BU21013_IDLE_INTERMIT_EN	0x10
+
+#define BU21013_DELTA_0_6	0x7F
+#define BU21013_FILTER_EN	0x80
+
+#define BU21013_INT_MODE_LEVEL	0x00
+#define BU21013_INT_MODE_EDGE	0x01
+
+#define BU21013_GAIN_0		0x01
+#define BU21013_GAIN_1		0x02
+#define BU21013_GAIN_2		0x04
+
+#define BU21013_OFFSET_MODE_DEFAULT	0x00
+#define BU21013_OFFSET_MODE_MOVE	0x01
+#define BU21013_OFFSET_MODE_DISABLE	0x02
+
+#define BU21013_TH_ON_0		0x01
+#define BU21013_TH_ON_1		0x02
+#define BU21013_TH_ON_2		0x04
+#define BU21013_TH_ON_3		0x08
+#define BU21013_TH_ON_4		0x10
+#define BU21013_TH_ON_5		0x20
+#define BU21013_TH_ON_6		0x40
+#define BU21013_TH_ON_7		0x80
+#define BU21013_TH_ON_MAX	0xFF
+
+#define BU21013_TH_OFF_0	0x01
+#define BU21013_TH_OFF_1	0x02
+#define BU21013_TH_OFF_2	0x04
+#define BU21013_TH_OFF_3	0x08
+#define BU21013_TH_OFF_4	0x10
+#define BU21013_TH_OFF_5	0x20
+#define BU21013_TH_OFF_6	0x40
+#define BU21013_TH_OFF_7	0x80
+#define BU21013_TH_OFF_MAX	0xFF
+
+#define BU21013_X_EDGE_0	0x01
+#define BU21013_X_EDGE_1	0x02
+#define BU21013_X_EDGE_2	0x04
+#define BU21013_X_EDGE_3	0x08
+#define BU21013_Y_EDGE_0	0x10
+#define BU21013_Y_EDGE_1	0x20
+#define BU21013_Y_EDGE_2	0x40
+#define BU21013_Y_EDGE_3	0x80
+
+#define BU21013_DONE	0x01
+#define BU21013_NUMBER_OF_X_SENSORS	(6)
+#define BU21013_NUMBER_OF_Y_SENSORS	(11)
+
+#define DRIVER_TP	"bu21013_tp"
+
+/**
+ * struct bu21013_ts_data - touch panel data structure
+ * @client: pointer to the i2c client
+ * @wait: variable to wait_queue_head_t structure
+ * @touch_stopped: touch stop flag
+ * @chip: pointer to the touch panel controller
+ * @in_dev: pointer to the input device structure
+ * @intr_pin: interrupt pin value
+ * @regulator: pointer to the Regulator used for touch screen
+ *
+ * Touch panel device data structure
+ */
+struct bu21013_ts_data {
+	struct i2c_client *client;
+	wait_queue_head_t wait;
+	const struct bu21013_platform_device *chip;
+	struct input_dev *in_dev;
+	struct regulator *regulator;
+	unsigned int irq;
+	unsigned int intr_pin;
+	bool touch_stopped;
+};
+
+/**
+ * bu21013_read_block_data(): read the touch co-ordinates
+ * @data: bu21013_ts_data structure pointer
+ * @buf: byte pointer
+ *
+ * Read the touch co-ordinates using i2c read block into buffer
+ * and returns integer.
+ */
+static int bu21013_read_block_data(struct bu21013_ts_data *data, u8 *buf)
+{
+	int ret, i;
+
+	for (i = 0; i < I2C_RETRY_COUNT; i++) {
+		ret = i2c_smbus_read_i2c_block_data
+			(data->client, BU21013_SENSORS_BTN_0_7_REG,
+				LENGTH_OF_BUFFER, buf);
+		if (ret == LENGTH_OF_BUFFER)
+			return 0;
+	}
+	return -EINVAL;
+}
+
+/**
+ * bu21013_do_touch_report(): Get the touch co-ordinates
+ * @data: bu21013_ts_data structure pointer
+ *
+ * Get the touch co-ordinates from touch sensor registers and writes
+ * into device structure and returns integer.
+ */
+static int bu21013_do_touch_report(struct bu21013_ts_data *data)
+{
+	u8	buf[LENGTH_OF_BUFFER];
+	unsigned int pos_x[2], pos_y[2];
+	bool	has_x_sensors, has_y_sensors;
+	int	finger_down_count = 0;
+	int	i;
+
+	if (data == NULL)
+		return -EINVAL;
+
+	if (bu21013_read_block_data(data, buf) < 0)
+		return -EINVAL;
+
+	has_x_sensors = hweight32(buf[0] & BU21013_SENSORS_EN_0_7);
+	has_y_sensors = hweight32(((buf[1] & BU21013_SENSORS_EN_8_15) |
+		((buf[2] & BU21013_SENSORS_EN_16_23) << SHIFT_8)) >> SHIFT_2);
+	if (!has_x_sensors || !has_y_sensors)
+		return 0;
+
+	for (i = 0; i < MAX_FINGERS; i++) {
+		const u8 *p = &buf[4 * i + 3];
+		unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS);
+		unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS);
+		if (x == 0 || y == 0)
+			continue;
+		pos_x[finger_down_count] = x;
+		pos_y[finger_down_count] = y;
+		finger_down_count++;
+	}
+
+	if (finger_down_count) {
+		if (finger_down_count == 2 &&
+		    (abs(pos_x[0] - pos_x[1]) < DELTA_MIN ||
+		     abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) {
+			return 0;
+		}
+
+		for (i = 0; i < finger_down_count; i++) {
+			if (data->chip->x_flip)
+				pos_x[i] = data->chip->touch_x_max - pos_x[i];
+			if (data->chip->y_flip)
+				pos_y[i] = data->chip->touch_y_max - pos_y[i];
+
+			input_report_abs(data->in_dev,
+					 ABS_MT_POSITION_X, pos_x[i]);
+			input_report_abs(data->in_dev,
+					 ABS_MT_POSITION_Y, pos_y[i]);
+			input_mt_sync(data->in_dev);
+		}
+	} else
+		input_mt_sync(data->in_dev);
+
+	input_sync(data->in_dev);
+
+	return 0;
+}
+/**
+ * bu21013_gpio_irq() - gpio thread function for touch interrupt
+ * @irq: irq value
+ * @device_data: void pointer
+ *
+ * This gpio thread function for touch interrupt
+ * and returns irqreturn_t.
+ */
+static irqreturn_t bu21013_gpio_irq(int irq, void *device_data)
+{
+	struct bu21013_ts_data *data = device_data;
+	struct i2c_client *i2c = data->client;
+	int retval;
+
+	do {
+		retval = bu21013_do_touch_report(data);
+		if (retval < 0) {
+			dev_err(&i2c->dev, "bu21013_do_touch_report failed\n");
+			return IRQ_NONE;
+		}
+
+		data->intr_pin = gpio_get_value(data->chip->touch_pin);
+		if (data->intr_pin == PEN_DOWN_INTR)
+			wait_event_timeout(data->wait, data->touch_stopped,
+					   msecs_to_jiffies(2));
+	} while (!data->intr_pin && !data->touch_stopped);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * bu21013_init_chip() - power on sequence for the bu21013 controller
+ * @data: device structure pointer
+ *
+ * This function is used to power on
+ * the bu21013 controller and returns integer.
+ */
+static int bu21013_init_chip(struct bu21013_ts_data *data)
+{
+	int retval;
+	struct i2c_client *i2c = data->client;
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_RESET_REG,
+					BU21013_RESET_ENABLE);
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_RESET reg write failed\n");
+		return retval;
+	}
+	msleep(RESET_DELAY);
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_0_7_REG,
+					BU21013_SENSORS_EN_0_7);
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n");
+		return retval;
+	}
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG,
+						BU21013_SENSORS_EN_8_15);
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n");
+		return retval;
+	}
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG,
+						BU21013_SENSORS_EN_16_23);
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n");
+		return retval;
+	}
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG,
+				(BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1));
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n");
+		return retval;
+	}
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG,
+			(BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 |
+			BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW |
+			BU21013_POS_MODE2_MULTI));
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n");
+		return retval;
+	}
+
+	if (data->chip->ext_clk)
+		retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
+			(BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB));
+	else
+		retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
+			(BU21013_CLK_MODE_DIV | BU21013_CLK_MODE_CALIB));
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n");
+		return retval;
+	}
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG,
+				(BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN));
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n");
+		return retval;
+	}
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG,
+						BU21013_INT_MODE_LEVEL);
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n");
+		return retval;
+	}
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG,
+						(BU21013_DELTA_0_6 |
+							BU21013_FILTER_EN));
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_FILTER reg write failed\n");
+		return retval;
+	}
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_ON_REG,
+					BU21013_TH_ON_5);
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n");
+		return retval;
+	}
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG,
+				BU21013_TH_OFF_4 | BU21013_TH_OFF_3);
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n");
+		return retval;
+	}
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG,
+					(BU21013_GAIN_0 | BU21013_GAIN_1));
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_GAIN reg write failed\n");
+		return retval;
+	}
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_OFFSET_MODE_REG,
+					BU21013_OFFSET_MODE_DEFAULT);
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n");
+		return retval;
+	}
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG,
+				(BU21013_X_EDGE_0 | BU21013_X_EDGE_2 |
+				BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3));
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n");
+		return retval;
+	}
+
+	retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG,
+							BU21013_DONE);
+	if (retval < 0) {
+		dev_err(&i2c->dev, "BU21013_REG_DONE reg write failed\n");
+		return retval;
+	}
+
+	return 0;
+}
+
+/**
+ * bu21013_free_irq() - frees IRQ registered for touchscreen
+ * @bu21013_data: device structure pointer
+ *
+ * This function signals interrupt thread to stop processing and
+ * frees interrupt.
+ */
+static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data)
+{
+	bu21013_data->touch_stopped = true;
+	wake_up(&bu21013_data->wait);
+	free_irq(bu21013_data->irq, bu21013_data);
+}
+
+/**
+ * bu21013_cs_disable() - deconfigures the touch panel controller
+ * @bu21013_data: device structure pointer
+ *
+ * This function is used to deconfigure the chip selection
+ * for touch panel controller.
+ */
+static void bu21013_cs_disable(struct bu21013_ts_data *bu21013_data)
+{
+	int error;
+
+	error = gpio_direction_output(bu21013_data->chip->cs_pin, 0);
+	if (error < 0)
+		dev_warn(&bu21013_data->client->dev,
+			 "%s: gpio direction failed, error: %d\n",
+			 __func__, error);
+	else
+		gpio_set_value(bu21013_data->chip->cs_pin, 0);
+
+	gpio_free(bu21013_data->chip->cs_pin);
+}
+
+#ifdef CONFIG_OF
+static const struct bu21013_platform_device *
+bu21013_parse_dt(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	struct bu21013_platform_device *pdata;
+
+	if (!np) {
+		dev_err(dev, "no device tree or platform data\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	pdata->y_flip = pdata->x_flip = false;
+
+	pdata->x_flip = of_property_read_bool(np, "rohm,flip-x");
+	pdata->y_flip = of_property_read_bool(np, "rohm,flip-y");
+
+	of_property_read_u32(np, "rohm,touch-max-x", &pdata->touch_x_max);
+	of_property_read_u32(np, "rohm,touch-max-y", &pdata->touch_y_max);
+
+	pdata->touch_pin = of_get_named_gpio(np, "touch-gpio", 0);
+	pdata->cs_pin = of_get_named_gpio(np, "reset-gpio", 0);
+
+	pdata->ext_clk = false;
+
+	return pdata;
+}
+#else
+static inline const struct bu21013_platform_device *
+bu21013_parse_dt(struct device *dev)
+{
+	dev_err(dev, "no platform data available\n");
+	return ERR_PTR(-EINVAL);
+}
+#endif
+
+/**
+ * bu21013_probe() - initializes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ * @id: i2c device id pointer
+ *
+ * This function used to initializes the i2c-client touchscreen
+ * driver and returns integer.
+ */
+static int bu21013_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	const struct bu21013_platform_device *pdata =
+					dev_get_platdata(&client->dev);
+	struct bu21013_ts_data *bu21013_data;
+	struct input_dev *in_dev;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&client->dev, "i2c smbus byte data not supported\n");
+		return -EIO;
+	}
+
+	if (!pdata) {
+		pdata = bu21013_parse_dt(&client->dev);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+	}
+
+	if (!gpio_is_valid(pdata->touch_pin)) {
+		dev_err(&client->dev, "invalid touch_pin supplied\n");
+		return -EINVAL;
+	}
+
+	bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL);
+	in_dev = input_allocate_device();
+	if (!bu21013_data || !in_dev) {
+		dev_err(&client->dev, "device memory alloc failed\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	bu21013_data->in_dev = in_dev;
+	bu21013_data->chip = pdata;
+	bu21013_data->client = client;
+	bu21013_data->irq = gpio_to_irq(pdata->touch_pin);
+
+	bu21013_data->regulator = regulator_get(&client->dev, "avdd");
+	if (IS_ERR(bu21013_data->regulator)) {
+		dev_err(&client->dev, "regulator_get failed\n");
+		error = PTR_ERR(bu21013_data->regulator);
+		goto err_free_mem;
+	}
+
+	error = regulator_enable(bu21013_data->regulator);
+	if (error < 0) {
+		dev_err(&client->dev, "regulator enable failed\n");
+		goto err_put_regulator;
+	}
+
+	bu21013_data->touch_stopped = false;
+	init_waitqueue_head(&bu21013_data->wait);
+
+	/* configure the gpio pins */
+	error = gpio_request_one(pdata->cs_pin, GPIOF_OUT_INIT_HIGH,
+				 "touchp_reset");
+	if (error < 0) {
+		dev_err(&client->dev, "Unable to request gpio reset_pin\n");
+		goto err_disable_regulator;
+	}
+
+	/* configure the touch panel controller */
+	error = bu21013_init_chip(bu21013_data);
+	if (error) {
+		dev_err(&client->dev, "error in bu21013 config\n");
+		goto err_cs_disable;
+	}
+
+	/* register the device to input subsystem */
+	in_dev->name = DRIVER_TP;
+	in_dev->id.bustype = BUS_I2C;
+	in_dev->dev.parent = &client->dev;
+
+	__set_bit(EV_SYN, in_dev->evbit);
+	__set_bit(EV_KEY, in_dev->evbit);
+	__set_bit(EV_ABS, in_dev->evbit);
+
+	input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0,
+						pdata->touch_x_max, 0, 0);
+	input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0,
+						pdata->touch_y_max, 0, 0);
+	input_set_drvdata(in_dev, bu21013_data);
+
+	error = request_threaded_irq(bu21013_data->irq, NULL, bu21013_gpio_irq,
+				     IRQF_TRIGGER_FALLING | IRQF_SHARED |
+					IRQF_ONESHOT,
+				     DRIVER_TP, bu21013_data);
+	if (error) {
+		dev_err(&client->dev, "request irq %d failed\n",
+			bu21013_data->irq);
+		goto err_cs_disable;
+	}
+
+	error = input_register_device(in_dev);
+	if (error) {
+		dev_err(&client->dev, "failed to register input device\n");
+		goto err_free_irq;
+	}
+
+	device_init_wakeup(&client->dev, pdata->wakeup);
+	i2c_set_clientdata(client, bu21013_data);
+
+	return 0;
+
+err_free_irq:
+	bu21013_free_irq(bu21013_data);
+err_cs_disable:
+	bu21013_cs_disable(bu21013_data);
+err_disable_regulator:
+	regulator_disable(bu21013_data->regulator);
+err_put_regulator:
+	regulator_put(bu21013_data->regulator);
+err_free_mem:
+	input_free_device(in_dev);
+	kfree(bu21013_data);
+
+	return error;
+}
+/**
+ * bu21013_remove() - removes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ *
+ * This function uses to remove the i2c-client
+ * touchscreen driver and returns integer.
+ */
+static int bu21013_remove(struct i2c_client *client)
+{
+	struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client);
+
+	bu21013_free_irq(bu21013_data);
+
+	bu21013_cs_disable(bu21013_data);
+
+	input_unregister_device(bu21013_data->in_dev);
+
+	regulator_disable(bu21013_data->regulator);
+	regulator_put(bu21013_data->regulator);
+
+	kfree(bu21013_data);
+
+	device_init_wakeup(&client->dev, false);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ * bu21013_suspend() - suspend the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to suspend the
+ * touch panel controller and returns integer
+ */
+static int bu21013_suspend(struct device *dev)
+{
+	struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
+	struct i2c_client *client = bu21013_data->client;
+
+	bu21013_data->touch_stopped = true;
+	if (device_may_wakeup(&client->dev))
+		enable_irq_wake(bu21013_data->irq);
+	else
+		disable_irq(bu21013_data->irq);
+
+	regulator_disable(bu21013_data->regulator);
+
+	return 0;
+}
+
+/**
+ * bu21013_resume() - resume the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to resume the touch panel
+ * controller and returns integer.
+ */
+static int bu21013_resume(struct device *dev)
+{
+	struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
+	struct i2c_client *client = bu21013_data->client;
+	int retval;
+
+	retval = regulator_enable(bu21013_data->regulator);
+	if (retval < 0) {
+		dev_err(&client->dev, "bu21013 regulator enable failed\n");
+		return retval;
+	}
+
+	retval = bu21013_init_chip(bu21013_data);
+	if (retval < 0) {
+		dev_err(&client->dev, "bu21013 controller config failed\n");
+		return retval;
+	}
+
+	bu21013_data->touch_stopped = false;
+
+	if (device_may_wakeup(&client->dev))
+		disable_irq_wake(bu21013_data->irq);
+	else
+		enable_irq(bu21013_data->irq);
+
+	return 0;
+}
+
+static const struct dev_pm_ops bu21013_dev_pm_ops = {
+	.suspend = bu21013_suspend,
+	.resume  = bu21013_resume,
+};
+#endif
+
+static const struct i2c_device_id bu21013_id[] = {
+	{ DRIVER_TP, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, bu21013_id);
+
+static struct i2c_driver bu21013_driver = {
+	.driver	= {
+		.name	=	DRIVER_TP,
+#ifdef CONFIG_PM
+		.pm	=	&bu21013_dev_pm_ops,
+#endif
+	},
+	.probe		=	bu21013_probe,
+	.remove		=	bu21013_remove,
+	.id_table	=	bu21013_id,
+};
+
+module_i2c_driver(bu21013_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Naveen Kumar G <naveen.gaddipati@stericsson.com>");
+MODULE_DESCRIPTION("bu21013 touch screen controller driver");
diff --git a/drivers/input/touchscreen/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c
new file mode 100644
index 0000000..22a6fea
--- /dev/null
+++ b/drivers/input/touchscreen/chipone_icn8318.c
@@ -0,0 +1,315 @@
+/*
+ * Driver for ChipOne icn8318 i2c touchscreen controller
+ *
+ * Copyright (c) 2015 Red Hat Inc.
+ *
+ * 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.
+ *
+ * Red Hat authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#define ICN8318_REG_POWER		4
+#define ICN8318_REG_TOUCHDATA		16
+
+#define ICN8318_POWER_ACTIVE		0
+#define ICN8318_POWER_MONITOR		1
+#define ICN8318_POWER_HIBERNATE		2
+
+#define ICN8318_MAX_TOUCHES		5
+
+struct icn8318_touch {
+	__u8 slot;
+	__be16 x;
+	__be16 y;
+	__u8 pressure;	/* Seems more like finger width then pressure really */
+	__u8 event;
+/* The difference between 2 and 3 is unclear */
+#define ICN8318_EVENT_NO_DATA	1 /* No finger seen yet since wakeup */
+#define ICN8318_EVENT_UPDATE1	2 /* New or updated coordinates */
+#define ICN8318_EVENT_UPDATE2	3 /* New or updated coordinates */
+#define ICN8318_EVENT_END	4 /* Finger lifted */
+} __packed;
+
+struct icn8318_touch_data {
+	__u8 softbutton;
+	__u8 touch_count;
+	struct icn8318_touch touches[ICN8318_MAX_TOUCHES];
+} __packed;
+
+struct icn8318_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct gpio_desc *wake_gpio;
+	u32 max_x;
+	u32 max_y;
+	bool invert_x;
+	bool invert_y;
+	bool swap_x_y;
+};
+
+static int icn8318_read_touch_data(struct i2c_client *client,
+				   struct icn8318_touch_data *touch_data)
+{
+	u8 reg = ICN8318_REG_TOUCHDATA;
+	struct i2c_msg msg[2] = {
+		{
+			.addr = client->addr,
+			.len = 1,
+			.buf = &reg
+		},
+		{
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = sizeof(struct icn8318_touch_data),
+			.buf = (u8 *)touch_data
+		}
+	};
+
+	return i2c_transfer(client->adapter, msg, 2);
+}
+
+static inline bool icn8318_touch_active(u8 event)
+{
+	return (event == ICN8318_EVENT_UPDATE1) ||
+	       (event == ICN8318_EVENT_UPDATE2);
+}
+
+static irqreturn_t icn8318_irq(int irq, void *dev_id)
+{
+	struct icn8318_data *data = dev_id;
+	struct device *dev = &data->client->dev;
+	struct icn8318_touch_data touch_data;
+	int i, ret, x, y;
+
+	ret = icn8318_read_touch_data(data->client, &touch_data);
+	if (ret < 0) {
+		dev_err(dev, "Error reading touch data: %d\n", ret);
+		return IRQ_HANDLED;
+	}
+
+	if (touch_data.softbutton) {
+		/*
+		 * Other data is invalid when a softbutton is pressed.
+		 * This needs some extra devicetree bindings to map the icn8318
+		 * softbutton codes to evdev codes. Currently no known devices
+		 * use this.
+		 */
+		return IRQ_HANDLED;
+	}
+
+	if (touch_data.touch_count > ICN8318_MAX_TOUCHES) {
+		dev_warn(dev, "Too much touches %d > %d\n",
+			 touch_data.touch_count, ICN8318_MAX_TOUCHES);
+		touch_data.touch_count = ICN8318_MAX_TOUCHES;
+	}
+
+	for (i = 0; i < touch_data.touch_count; i++) {
+		struct icn8318_touch *touch = &touch_data.touches[i];
+		bool act = icn8318_touch_active(touch->event);
+
+		input_mt_slot(data->input, touch->slot);
+		input_mt_report_slot_state(data->input, MT_TOOL_FINGER, act);
+		if (!act)
+			continue;
+
+		x = be16_to_cpu(touch->x);
+		y = be16_to_cpu(touch->y);
+
+		if (data->invert_x)
+			x = data->max_x - x;
+
+		if (data->invert_y)
+			y = data->max_y - y;
+
+		if (!data->swap_x_y) {
+			input_event(data->input, EV_ABS, ABS_MT_POSITION_X, x);
+			input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, y);
+		} else {
+			input_event(data->input, EV_ABS, ABS_MT_POSITION_X, y);
+			input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, x);
+		}
+	}
+
+	input_mt_sync_frame(data->input);
+	input_sync(data->input);
+
+	return IRQ_HANDLED;
+}
+
+static int icn8318_start(struct input_dev *dev)
+{
+	struct icn8318_data *data = input_get_drvdata(dev);
+
+	enable_irq(data->client->irq);
+	gpiod_set_value_cansleep(data->wake_gpio, 1);
+
+	return 0;
+}
+
+static void icn8318_stop(struct input_dev *dev)
+{
+	struct icn8318_data *data = input_get_drvdata(dev);
+
+	disable_irq(data->client->irq);
+	i2c_smbus_write_byte_data(data->client, ICN8318_REG_POWER,
+				  ICN8318_POWER_HIBERNATE);
+	gpiod_set_value_cansleep(data->wake_gpio, 0);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int icn8318_suspend(struct device *dev)
+{
+	struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+	mutex_lock(&data->input->mutex);
+	if (data->input->users)
+		icn8318_stop(data->input);
+	mutex_unlock(&data->input->mutex);
+
+	return 0;
+}
+
+static int icn8318_resume(struct device *dev)
+{
+	struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+	mutex_lock(&data->input->mutex);
+	if (data->input->users)
+		icn8318_start(data->input);
+	mutex_unlock(&data->input->mutex);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(icn8318_pm_ops, icn8318_suspend, icn8318_resume);
+
+static int icn8318_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct device_node *np = dev->of_node;
+	struct icn8318_data *data;
+	struct input_dev *input;
+	u32 fuzz_x = 0, fuzz_y = 0;
+	int error;
+
+	if (!client->irq) {
+		dev_err(dev, "Error no irq specified\n");
+		return -EINVAL;
+	}
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->wake_gpio = devm_gpiod_get(dev, "wake", GPIOD_OUT_LOW);
+	if (IS_ERR(data->wake_gpio)) {
+		error = PTR_ERR(data->wake_gpio);
+		if (error != -EPROBE_DEFER)
+			dev_err(dev, "Error getting wake gpio: %d\n", error);
+		return error;
+	}
+
+	if (of_property_read_u32(np, "touchscreen-size-x", &data->max_x) ||
+	    of_property_read_u32(np, "touchscreen-size-y", &data->max_y)) {
+		dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
+		return -EINVAL;
+	}
+
+	/* Optional */
+	of_property_read_u32(np, "touchscreen-fuzz-x", &fuzz_x);
+	of_property_read_u32(np, "touchscreen-fuzz-y", &fuzz_y);
+	data->invert_x = of_property_read_bool(np, "touchscreen-inverted-x");
+	data->invert_y = of_property_read_bool(np, "touchscreen-inverted-y");
+	data->swap_x_y = of_property_read_bool(np, "touchscreen-swapped-x-y");
+
+	input = devm_input_allocate_device(dev);
+	if (!input)
+		return -ENOMEM;
+
+	input->name = client->name;
+	input->id.bustype = BUS_I2C;
+	input->open = icn8318_start;
+	input->close = icn8318_stop;
+	input->dev.parent = dev;
+
+	if (!data->swap_x_y) {
+		input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+				     data->max_x, fuzz_x, 0);
+		input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+				     data->max_y, fuzz_y, 0);
+	} else {
+		input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+				     data->max_y, fuzz_y, 0);
+		input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+				     data->max_x, fuzz_x, 0);
+	}
+
+	error = input_mt_init_slots(input, ICN8318_MAX_TOUCHES,
+				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error)
+		return error;
+
+	data->client = client;
+	data->input = input;
+	input_set_drvdata(input, data);
+
+	error = devm_request_threaded_irq(dev, client->irq, NULL, icn8318_irq,
+					  IRQF_ONESHOT, client->name, data);
+	if (error) {
+		dev_err(dev, "Error requesting irq: %d\n", error);
+		return error;
+	}
+
+	/* Stop device till opened */
+	icn8318_stop(data->input);
+
+	error = input_register_device(input);
+	if (error)
+		return error;
+
+	i2c_set_clientdata(client, data);
+
+	return 0;
+}
+
+static const struct of_device_id icn8318_of_match[] = {
+	{ .compatible = "chipone,icn8318" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, icn8318_of_match);
+
+/* This is useless for OF-enabled devices, but it is needed by I2C subsystem */
+static const struct i2c_device_id icn8318_i2c_id[] = {
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, icn8318_i2c_id);
+
+static struct i2c_driver icn8318_driver = {
+	.driver = {
+		.name	= "chipone_icn8318",
+		.pm	= &icn8318_pm_ops,
+		.of_match_table = icn8318_of_match,
+	},
+	.probe = icn8318_probe,
+	.id_table = icn8318_i2c_id,
+};
+
+module_i2c_driver(icn8318_driver);
+
+MODULE_DESCRIPTION("ChipOne icn8318 I2C Touchscreen Driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c
new file mode 100644
index 0000000..5d4903a
--- /dev/null
+++ b/drivers/input/touchscreen/colibri-vf50-ts.c
@@ -0,0 +1,386 @@
+/*
+ * Toradex Colibri VF50 Touchscreen driver
+ *
+ * Copyright 2015 Toradex AG
+ *
+ * Originally authored by Stefan Agner for 3.0 kernel
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#define DRIVER_NAME			"colibri-vf50-ts"
+#define DRV_VERSION			"1.0"
+
+#define VF_ADC_MAX			((1 << 12) - 1)
+
+#define COLI_TOUCH_MIN_DELAY_US		1000
+#define COLI_TOUCH_MAX_DELAY_US		2000
+#define COLI_PULLUP_MIN_DELAY_US	10000
+#define COLI_PULLUP_MAX_DELAY_US	11000
+#define COLI_TOUCH_NO_OF_AVGS		5
+#define COLI_TOUCH_REQ_ADC_CHAN		4
+
+struct vf50_touch_device {
+	struct platform_device *pdev;
+	struct input_dev *ts_input;
+	struct iio_channel *channels;
+	struct gpio_desc *gpio_xp;
+	struct gpio_desc *gpio_xm;
+	struct gpio_desc *gpio_yp;
+	struct gpio_desc *gpio_ym;
+	int pen_irq;
+	int min_pressure;
+	bool stop_touchscreen;
+};
+
+/*
+ * Enables given plates and measures touch parameters using ADC
+ */
+static int adc_ts_measure(struct iio_channel *channel,
+			  struct gpio_desc *plate_p, struct gpio_desc *plate_m)
+{
+	int i, value = 0, val = 0;
+	int error;
+
+	gpiod_set_value(plate_p, 1);
+	gpiod_set_value(plate_m, 1);
+
+	usleep_range(COLI_TOUCH_MIN_DELAY_US, COLI_TOUCH_MAX_DELAY_US);
+
+	for (i = 0; i < COLI_TOUCH_NO_OF_AVGS; i++) {
+		error = iio_read_channel_raw(channel, &val);
+		if (error < 0) {
+			value = error;
+			goto error_iio_read;
+		}
+
+		value += val;
+	}
+
+	value /= COLI_TOUCH_NO_OF_AVGS;
+
+error_iio_read:
+	gpiod_set_value(plate_p, 0);
+	gpiod_set_value(plate_m, 0);
+
+	return value;
+}
+
+/*
+ * Enable touch detection using falling edge detection on XM
+ */
+static void vf50_ts_enable_touch_detection(struct vf50_touch_device *vf50_ts)
+{
+	/* Enable plate YM (needs to be strong GND, high active) */
+	gpiod_set_value(vf50_ts->gpio_ym, 1);
+
+	/*
+	 * Let the platform mux to idle state in order to enable
+	 * Pull-Up on GPIO
+	 */
+	pinctrl_pm_select_idle_state(&vf50_ts->pdev->dev);
+
+	/* Wait for the pull-up to be stable on high */
+	usleep_range(COLI_PULLUP_MIN_DELAY_US, COLI_PULLUP_MAX_DELAY_US);
+}
+
+/*
+ * ADC touch screen sampling bottom half irq handler
+ */
+static irqreturn_t vf50_ts_irq_bh(int irq, void *private)
+{
+	struct vf50_touch_device *vf50_ts = private;
+	struct device *dev = &vf50_ts->pdev->dev;
+	int val_x, val_y, val_z1, val_z2, val_p = 0;
+	bool discard_val_on_start = true;
+
+	/* Disable the touch detection plates */
+	gpiod_set_value(vf50_ts->gpio_ym, 0);
+
+	/* Let the platform mux to default state in order to mux as ADC */
+	pinctrl_pm_select_default_state(dev);
+
+	while (!vf50_ts->stop_touchscreen) {
+		/* X-Direction */
+		val_x = adc_ts_measure(&vf50_ts->channels[0],
+				vf50_ts->gpio_xp, vf50_ts->gpio_xm);
+		if (val_x < 0)
+			break;
+
+		/* Y-Direction */
+		val_y = adc_ts_measure(&vf50_ts->channels[1],
+				vf50_ts->gpio_yp, vf50_ts->gpio_ym);
+		if (val_y < 0)
+			break;
+
+		/*
+		 * Touch pressure
+		 * Measure on XP/YM
+		 */
+		val_z1 = adc_ts_measure(&vf50_ts->channels[2],
+				vf50_ts->gpio_yp, vf50_ts->gpio_xm);
+		if (val_z1 < 0)
+			break;
+		val_z2 = adc_ts_measure(&vf50_ts->channels[3],
+				vf50_ts->gpio_yp, vf50_ts->gpio_xm);
+		if (val_z2 < 0)
+			break;
+
+		/* Validate signal (avoid calculation using noise) */
+		if (val_z1 > 64 && val_x > 64) {
+			/*
+			 * Calculate resistance between the plates
+			 * lower resistance means higher pressure
+			 */
+			int r_x = (1000 * val_x) / VF_ADC_MAX;
+
+			val_p = (r_x * val_z2) / val_z1 - r_x;
+
+		} else {
+			val_p = 2000;
+		}
+
+		val_p = 2000 - val_p;
+		dev_dbg(dev,
+			"Measured values: x: %d, y: %d, z1: %d, z2: %d, p: %d\n",
+			val_x, val_y, val_z1, val_z2, val_p);
+
+		/*
+		 * If touch pressure is too low, stop measuring and reenable
+		 * touch detection
+		 */
+		if (val_p < vf50_ts->min_pressure || val_p > 2000)
+			break;
+
+		/*
+		 * The pressure may not be enough for the first x and the
+		 * second y measurement, but, the pressure is ok when the
+		 * driver is doing the third and fourth measurement. To
+		 * take care of this, we drop the first measurement always.
+		 */
+		if (discard_val_on_start) {
+			discard_val_on_start = false;
+		} else {
+			/*
+			 * Report touch position and sleep for
+			 * the next measurement.
+			 */
+			input_report_abs(vf50_ts->ts_input,
+					ABS_X, VF_ADC_MAX - val_x);
+			input_report_abs(vf50_ts->ts_input,
+					ABS_Y, VF_ADC_MAX - val_y);
+			input_report_abs(vf50_ts->ts_input,
+					ABS_PRESSURE, val_p);
+			input_report_key(vf50_ts->ts_input, BTN_TOUCH, 1);
+			input_sync(vf50_ts->ts_input);
+		}
+
+		usleep_range(COLI_PULLUP_MIN_DELAY_US,
+			     COLI_PULLUP_MAX_DELAY_US);
+	}
+
+	/* Report no more touch, re-enable touch detection */
+	input_report_abs(vf50_ts->ts_input, ABS_PRESSURE, 0);
+	input_report_key(vf50_ts->ts_input, BTN_TOUCH, 0);
+	input_sync(vf50_ts->ts_input);
+
+	vf50_ts_enable_touch_detection(vf50_ts);
+
+	return IRQ_HANDLED;
+}
+
+static int vf50_ts_open(struct input_dev *dev_input)
+{
+	struct vf50_touch_device *touchdev = input_get_drvdata(dev_input);
+	struct device *dev = &touchdev->pdev->dev;
+
+	dev_dbg(dev, "Input device %s opened, starting touch detection\n",
+		dev_input->name);
+
+	touchdev->stop_touchscreen = false;
+
+	/* Mux detection before request IRQ, wait for pull-up to settle */
+	vf50_ts_enable_touch_detection(touchdev);
+
+	return 0;
+}
+
+static void vf50_ts_close(struct input_dev *dev_input)
+{
+	struct vf50_touch_device *touchdev = input_get_drvdata(dev_input);
+	struct device *dev = &touchdev->pdev->dev;
+
+	touchdev->stop_touchscreen = true;
+
+	/* Make sure IRQ is not running past close */
+	mb();
+	synchronize_irq(touchdev->pen_irq);
+
+	gpiod_set_value(touchdev->gpio_ym, 0);
+	pinctrl_pm_select_default_state(dev);
+
+	dev_dbg(dev, "Input device %s closed, disable touch detection\n",
+		dev_input->name);
+}
+
+static int vf50_ts_get_gpiod(struct device *dev, struct gpio_desc **gpio_d,
+			     const char *con_id, enum gpiod_flags flags)
+{
+	int error;
+
+	*gpio_d = devm_gpiod_get(dev, con_id, flags);
+	if (IS_ERR(*gpio_d)) {
+		error = PTR_ERR(*gpio_d);
+		dev_err(dev, "Could not get gpio_%s %d\n", con_id, error);
+		return error;
+	}
+
+	return 0;
+}
+
+static void vf50_ts_channel_release(void *data)
+{
+	struct iio_channel *channels = data;
+
+	iio_channel_release_all(channels);
+}
+
+static int vf50_ts_probe(struct platform_device *pdev)
+{
+	struct input_dev *input;
+	struct iio_channel *channels;
+	struct device *dev = &pdev->dev;
+	struct vf50_touch_device *touchdev;
+	int num_adc_channels;
+	int error;
+
+	channels = iio_channel_get_all(dev);
+	if (IS_ERR(channels))
+		return PTR_ERR(channels);
+
+	error = devm_add_action(dev, vf50_ts_channel_release, channels);
+	if (error) {
+		iio_channel_release_all(channels);
+		dev_err(dev, "Failed to register iio channel release action");
+		return error;
+	}
+
+	num_adc_channels = 0;
+	while (channels[num_adc_channels].indio_dev)
+		num_adc_channels++;
+
+	if (num_adc_channels != COLI_TOUCH_REQ_ADC_CHAN) {
+		dev_err(dev, "Inadequate ADC channels specified\n");
+		return -EINVAL;
+	}
+
+	touchdev = devm_kzalloc(dev, sizeof(*touchdev), GFP_KERNEL);
+	if (!touchdev)
+		return -ENOMEM;
+
+	touchdev->pdev = pdev;
+	touchdev->channels = channels;
+
+	error = of_property_read_u32(dev->of_node, "vf50-ts-min-pressure",
+				 &touchdev->min_pressure);
+	if (error)
+		return error;
+
+	input = devm_input_allocate_device(dev);
+	if (!input) {
+		dev_err(dev, "Failed to allocate TS input device\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, touchdev);
+
+	input->name = DRIVER_NAME;
+	input->id.bustype = BUS_HOST;
+	input->dev.parent = dev;
+	input->open = vf50_ts_open;
+	input->close = vf50_ts_close;
+
+	input_set_capability(input, EV_KEY, BTN_TOUCH);
+	input_set_abs_params(input, ABS_X, 0, VF_ADC_MAX, 0, 0);
+	input_set_abs_params(input, ABS_Y, 0, VF_ADC_MAX, 0, 0);
+	input_set_abs_params(input, ABS_PRESSURE, 0, VF_ADC_MAX, 0, 0);
+
+	touchdev->ts_input = input;
+	input_set_drvdata(input, touchdev);
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(dev, "Failed to register input device\n");
+		return error;
+	}
+
+	error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xp, "xp", GPIOD_OUT_LOW);
+	if (error)
+		return error;
+
+	error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xm,
+				"xm", GPIOD_OUT_LOW);
+	if (error)
+		return error;
+
+	error = vf50_ts_get_gpiod(dev, &touchdev->gpio_yp, "yp", GPIOD_OUT_LOW);
+	if (error)
+		return error;
+
+	error = vf50_ts_get_gpiod(dev, &touchdev->gpio_ym, "ym", GPIOD_OUT_LOW);
+	if (error)
+		return error;
+
+	touchdev->pen_irq = platform_get_irq(pdev, 0);
+	if (touchdev->pen_irq < 0)
+		return touchdev->pen_irq;
+
+	error = devm_request_threaded_irq(dev, touchdev->pen_irq,
+					  NULL, vf50_ts_irq_bh, IRQF_ONESHOT,
+					  "vf50 touch", touchdev);
+	if (error) {
+		dev_err(dev, "Failed to request IRQ %d: %d\n",
+			touchdev->pen_irq, error);
+		return error;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id vf50_touch_of_match[] = {
+	{ .compatible = "toradex,vf50-touchscreen", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, vf50_touch_of_match);
+
+static struct platform_driver vf50_touch_driver = {
+	.driver = {
+		.name = "toradex,vf50_touchctrl",
+		.of_match_table = vf50_touch_of_match,
+	},
+	.probe = vf50_ts_probe,
+};
+module_platform_driver(vf50_touch_driver);
+
+MODULE_AUTHOR("Sanchayan Maity");
+MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c
new file mode 100644
index 0000000..cc1d135
--- /dev/null
+++ b/drivers/input/touchscreen/cy8ctmg110_ts.c
@@ -0,0 +1,362 @@
+/*
+ * Driver for cypress touch screen controller
+ *
+ * Copyright (c) 2009 Aava Mobile
+ *
+ * Some cleanups by Alan Cox <alan@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/input/cy8ctmg110_pdata.h>
+
+#define CY8CTMG110_DRIVER_NAME      "cy8ctmg110"
+
+/* Touch coordinates */
+#define CY8CTMG110_X_MIN		0
+#define CY8CTMG110_Y_MIN		0
+#define CY8CTMG110_X_MAX		759
+#define CY8CTMG110_Y_MAX		465
+
+
+/* cy8ctmg110 register definitions */
+#define CY8CTMG110_TOUCH_WAKEUP_TIME	0
+#define CY8CTMG110_TOUCH_SLEEP_TIME	2
+#define CY8CTMG110_TOUCH_X1		3
+#define CY8CTMG110_TOUCH_Y1		5
+#define CY8CTMG110_TOUCH_X2		7
+#define CY8CTMG110_TOUCH_Y2		9
+#define CY8CTMG110_FINGERS		11
+#define CY8CTMG110_GESTURE		12
+#define CY8CTMG110_REG_MAX		13
+
+
+/*
+ * The touch driver structure.
+ */
+struct cy8ctmg110 {
+	struct input_dev *input;
+	char phys[32];
+	struct i2c_client *client;
+	int reset_pin;
+	int irq_pin;
+};
+
+/*
+ * cy8ctmg110_power is the routine that is called when touch hardware
+ * will powered off or on.
+ */
+static void cy8ctmg110_power(struct cy8ctmg110 *ts, bool poweron)
+{
+	if (ts->reset_pin)
+		gpio_direction_output(ts->reset_pin, 1 - poweron);
+}
+
+static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg,
+		unsigned char len, unsigned char *value)
+{
+	struct i2c_client *client = tsc->client;
+	int ret;
+	unsigned char i2c_data[6];
+
+	BUG_ON(len > 5);
+
+	i2c_data[0] = reg;
+	memcpy(i2c_data + 1, value, len);
+
+	ret = i2c_master_send(client, i2c_data, len + 1);
+	if (ret != len + 1) {
+		dev_err(&client->dev, "i2c write data cmd failed\n");
+		return ret < 0 ? ret : -EIO;
+	}
+
+	return 0;
+}
+
+static int cy8ctmg110_read_regs(struct cy8ctmg110 *tsc,
+		unsigned char *data, unsigned char len, unsigned char cmd)
+{
+	struct i2c_client *client = tsc->client;
+	int ret;
+	struct i2c_msg msg[2] = {
+		/* first write slave position to i2c devices */
+		{
+			.addr = client->addr,
+			.len = 1,
+			.buf = &cmd
+		},
+		/* Second read data from position */
+		{
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = data
+		}
+	};
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int cy8ctmg110_touch_pos(struct cy8ctmg110 *tsc)
+{
+	struct input_dev *input = tsc->input;
+	unsigned char reg_p[CY8CTMG110_REG_MAX];
+	int x, y;
+
+	memset(reg_p, 0, CY8CTMG110_REG_MAX);
+
+	/* Reading coordinates */
+	if (cy8ctmg110_read_regs(tsc, reg_p, 9, CY8CTMG110_TOUCH_X1) != 0)
+		return -EIO;
+
+	y = reg_p[2] << 8 | reg_p[3];
+	x = reg_p[0] << 8 | reg_p[1];
+
+	/* Number of touch */
+	if (reg_p[8] == 0) {
+		input_report_key(input, BTN_TOUCH, 0);
+	} else  {
+		input_report_key(input, BTN_TOUCH, 1);
+		input_report_abs(input, ABS_X, x);
+		input_report_abs(input, ABS_Y, y);
+	}
+
+	input_sync(input);
+
+	return 0;
+}
+
+static int cy8ctmg110_set_sleepmode(struct cy8ctmg110 *ts, bool sleep)
+{
+	unsigned char reg_p[3];
+
+	if (sleep) {
+		reg_p[0] = 0x00;
+		reg_p[1] = 0xff;
+		reg_p[2] = 5;
+	} else {
+		reg_p[0] = 0x10;
+		reg_p[1] = 0xff;
+		reg_p[2] = 0;
+	}
+
+	return cy8ctmg110_write_regs(ts, CY8CTMG110_TOUCH_WAKEUP_TIME, 3, reg_p);
+}
+
+static irqreturn_t cy8ctmg110_irq_thread(int irq, void *dev_id)
+{
+	struct cy8ctmg110 *tsc = dev_id;
+
+	cy8ctmg110_touch_pos(tsc);
+
+	return IRQ_HANDLED;
+}
+
+static int cy8ctmg110_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	const struct cy8ctmg110_pdata *pdata = dev_get_platdata(&client->dev);
+	struct cy8ctmg110 *ts;
+	struct input_dev *input_dev;
+	int err;
+
+	/* No pdata no way forward */
+	if (pdata == NULL) {
+		dev_err(&client->dev, "no pdata\n");
+		return -ENODEV;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+					I2C_FUNC_SMBUS_READ_WORD_DATA))
+		return -EIO;
+
+	ts = kzalloc(sizeof(struct cy8ctmg110), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!ts || !input_dev) {
+		err = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	ts->client = client;
+	ts->input = input_dev;
+	ts->reset_pin = pdata->reset_pin;
+	ts->irq_pin = pdata->irq_pin;
+
+	snprintf(ts->phys, sizeof(ts->phys),
+		 "%s/input0", dev_name(&client->dev));
+
+	input_dev->name = CY8CTMG110_DRIVER_NAME " Touchscreen";
+	input_dev->phys = ts->phys;
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &client->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_set_abs_params(input_dev, ABS_X,
+			CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0);
+	input_set_abs_params(input_dev, ABS_Y,
+			CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0);
+
+	if (ts->reset_pin) {
+		err = gpio_request(ts->reset_pin, NULL);
+		if (err) {
+			dev_err(&client->dev,
+				"Unable to request GPIO pin %d.\n",
+				ts->reset_pin);
+			goto err_free_mem;
+		}
+	}
+
+	cy8ctmg110_power(ts, true);
+	cy8ctmg110_set_sleepmode(ts, false);
+
+	err = gpio_request(ts->irq_pin, "touch_irq_key");
+	if (err < 0) {
+		dev_err(&client->dev,
+			"Failed to request GPIO %d, error %d\n",
+			ts->irq_pin, err);
+		goto err_shutoff_device;
+	}
+
+	err = gpio_direction_input(ts->irq_pin);
+	if (err < 0) {
+		dev_err(&client->dev,
+			"Failed to configure input direction for GPIO %d, error %d\n",
+			ts->irq_pin, err);
+		goto err_free_irq_gpio;
+	}
+
+	client->irq = gpio_to_irq(ts->irq_pin);
+	if (client->irq < 0) {
+		err = client->irq;
+		dev_err(&client->dev,
+			"Unable to get irq number for GPIO %d, error %d\n",
+			ts->irq_pin, err);
+		goto err_free_irq_gpio;
+	}
+
+	err = request_threaded_irq(client->irq, NULL, cy8ctmg110_irq_thread,
+				   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+				   "touch_reset_key", ts);
+	if (err < 0) {
+		dev_err(&client->dev,
+			"irq %d busy? error %d\n", client->irq, err);
+		goto err_free_irq_gpio;
+	}
+
+	err = input_register_device(input_dev);
+	if (err)
+		goto err_free_irq;
+
+	i2c_set_clientdata(client, ts);
+	device_init_wakeup(&client->dev, 1);
+	return 0;
+
+err_free_irq:
+	free_irq(client->irq, ts);
+err_free_irq_gpio:
+	gpio_free(ts->irq_pin);
+err_shutoff_device:
+	cy8ctmg110_set_sleepmode(ts, true);
+	cy8ctmg110_power(ts, false);
+	if (ts->reset_pin)
+		gpio_free(ts->reset_pin);
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(ts);
+	return err;
+}
+
+static int __maybe_unused cy8ctmg110_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct cy8ctmg110 *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(&client->dev))
+		enable_irq_wake(client->irq);
+	else {
+		cy8ctmg110_set_sleepmode(ts, true);
+		cy8ctmg110_power(ts, false);
+	}
+	return 0;
+}
+
+static int __maybe_unused cy8ctmg110_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct cy8ctmg110 *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(&client->dev))
+		disable_irq_wake(client->irq);
+	else {
+		cy8ctmg110_power(ts, true);
+		cy8ctmg110_set_sleepmode(ts, false);
+	}
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume);
+
+static int cy8ctmg110_remove(struct i2c_client *client)
+{
+	struct cy8ctmg110 *ts = i2c_get_clientdata(client);
+
+	cy8ctmg110_set_sleepmode(ts, true);
+	cy8ctmg110_power(ts, false);
+
+	free_irq(client->irq, ts);
+	input_unregister_device(ts->input);
+	gpio_free(ts->irq_pin);
+	if (ts->reset_pin)
+		gpio_free(ts->reset_pin);
+	kfree(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id cy8ctmg110_idtable[] = {
+	{ CY8CTMG110_DRIVER_NAME, 1 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable);
+
+static struct i2c_driver cy8ctmg110_driver = {
+	.driver		= {
+		.name	= CY8CTMG110_DRIVER_NAME,
+		.pm	= &cy8ctmg110_pm,
+	},
+	.id_table	= cy8ctmg110_idtable,
+	.probe		= cy8ctmg110_probe,
+	.remove		= cy8ctmg110_remove,
+};
+
+module_i2c_driver(cy8ctmg110_driver);
+
+MODULE_AUTHOR("Samuli Konttila <samuli.konttila@aavamobile.com>");
+MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c
new file mode 100644
index 0000000..5ed3105
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_core.c
@@ -0,0 +1,2157 @@
+/*
+ * cyttsp4_core.c
+ * Cypress TrueTouch(TM) Standard Product V4 Core driver module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include "cyttsp4_core.h"
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+/* Timeout in ms. */
+#define CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT	500
+#define CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT	5000
+#define CY_CORE_MODE_CHANGE_TIMEOUT		1000
+#define CY_CORE_RESET_AND_WAIT_TIMEOUT		500
+#define CY_CORE_WAKEUP_TIMEOUT			500
+
+#define CY_CORE_STARTUP_RETRY_COUNT		3
+
+static const u8 ldr_exit[] = {
+	0xFF, 0x01, 0x3B, 0x00, 0x00, 0x4F, 0x6D, 0x17
+};
+
+static const u8 ldr_err_app[] = {
+	0x01, 0x02, 0x00, 0x00, 0x55, 0xDD, 0x17
+};
+
+static inline size_t merge_bytes(u8 high, u8 low)
+{
+	return (high << 8) + low;
+}
+
+#ifdef VERBOSE_DEBUG
+static void cyttsp4_pr_buf(struct device *dev, u8 *pr_buf, u8 *dptr, int size,
+		const char *data_name)
+{
+	int i, k;
+	const char fmt[] = "%02X ";
+	int max;
+
+	if (!size)
+		return;
+
+	max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED);
+
+	pr_buf[0] = 0;
+	for (i = k = 0; i < size && k < max; i++, k += 3)
+		scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, dptr[i]);
+
+	dev_vdbg(dev, "%s:  %s[0..%d]=%s%s\n", __func__, data_name, size - 1,
+			pr_buf, size <= max ? "" : CY_PR_TRUNCATED);
+}
+#else
+#define cyttsp4_pr_buf(dev, pr_buf, dptr, size, data_name) do { } while (0)
+#endif
+
+static int cyttsp4_load_status_regs(struct cyttsp4 *cd)
+{
+	struct cyttsp4_sysinfo *si = &cd->sysinfo;
+	struct device *dev = cd->dev;
+	int rc;
+
+	rc = cyttsp4_adap_read(cd, CY_REG_BASE, si->si_ofs.mode_size,
+			si->xy_mode);
+	if (rc < 0)
+		dev_err(dev, "%s: fail read mode regs r=%d\n",
+			__func__, rc);
+	else
+		cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_mode,
+			si->si_ofs.mode_size, "xy_mode");
+
+	return rc;
+}
+
+static int cyttsp4_handshake(struct cyttsp4 *cd, u8 mode)
+{
+	u8 cmd = mode ^ CY_HST_TOGGLE;
+	int rc;
+
+	/*
+	 * Mode change issued, handshaking now will cause endless mode change
+	 * requests, for sync mode modechange will do same with handshake
+	 * */
+	if (mode & CY_HST_MODE_CHANGE)
+		return 0;
+
+	rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd);
+	if (rc < 0)
+		dev_err(cd->dev, "%s: bus write fail on handshake (ret=%d)\n",
+				__func__, rc);
+
+	return rc;
+}
+
+static int cyttsp4_hw_soft_reset(struct cyttsp4 *cd)
+{
+	u8 cmd = CY_HST_RESET;
+	int rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: FAILED to execute SOFT reset\n",
+				__func__);
+		return rc;
+	}
+	return 0;
+}
+
+static int cyttsp4_hw_hard_reset(struct cyttsp4 *cd)
+{
+	if (cd->cpdata->xres) {
+		cd->cpdata->xres(cd->cpdata, cd->dev);
+		dev_dbg(cd->dev, "%s: execute HARD reset\n", __func__);
+		return 0;
+	}
+	dev_err(cd->dev, "%s: FAILED to execute HARD reset\n", __func__);
+	return -ENOSYS;
+}
+
+static int cyttsp4_hw_reset(struct cyttsp4 *cd)
+{
+	int rc = cyttsp4_hw_hard_reset(cd);
+	if (rc == -ENOSYS)
+		rc = cyttsp4_hw_soft_reset(cd);
+	return rc;
+}
+
+/*
+ * Gets number of bits for a touch filed as parameter,
+ * sets maximum value for field which is used as bit mask
+ * and returns number of bytes required for that field
+ */
+static int cyttsp4_bits_2_bytes(unsigned int nbits, size_t *max)
+{
+	*max = 1UL << nbits;
+	return (nbits + 7) / 8;
+}
+
+static int cyttsp4_si_data_offsets(struct cyttsp4 *cd)
+{
+	struct cyttsp4_sysinfo *si = &cd->sysinfo;
+	int rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(si->si_data),
+			&si->si_data);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: fail read sysinfo data offsets r=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	/* Print sysinfo data offsets */
+	cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)&si->si_data,
+		       sizeof(si->si_data), "sysinfo_data_offsets");
+
+	/* convert sysinfo data offset bytes into integers */
+
+	si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh,
+			si->si_data.map_szl);
+	si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh,
+			si->si_data.map_szl);
+	si->si_ofs.cydata_ofs = merge_bytes(si->si_data.cydata_ofsh,
+			si->si_data.cydata_ofsl);
+	si->si_ofs.test_ofs = merge_bytes(si->si_data.test_ofsh,
+			si->si_data.test_ofsl);
+	si->si_ofs.pcfg_ofs = merge_bytes(si->si_data.pcfg_ofsh,
+			si->si_data.pcfg_ofsl);
+	si->si_ofs.opcfg_ofs = merge_bytes(si->si_data.opcfg_ofsh,
+			si->si_data.opcfg_ofsl);
+	si->si_ofs.ddata_ofs = merge_bytes(si->si_data.ddata_ofsh,
+			si->si_data.ddata_ofsl);
+	si->si_ofs.mdata_ofs = merge_bytes(si->si_data.mdata_ofsh,
+			si->si_data.mdata_ofsl);
+	return rc;
+}
+
+static int cyttsp4_si_get_cydata(struct cyttsp4 *cd)
+{
+	struct cyttsp4_sysinfo *si = &cd->sysinfo;
+	int read_offset;
+	int mfgid_sz, calc_mfgid_sz;
+	void *p;
+	int rc;
+
+	si->si_ofs.cydata_size = si->si_ofs.test_ofs - si->si_ofs.cydata_ofs;
+	dev_dbg(cd->dev, "%s: cydata size: %Zd\n", __func__,
+			si->si_ofs.cydata_size);
+
+	p = krealloc(si->si_ptrs.cydata, si->si_ofs.cydata_size, GFP_KERNEL);
+	if (p == NULL) {
+		dev_err(cd->dev, "%s: fail alloc cydata memory\n", __func__);
+		return -ENOMEM;
+	}
+	si->si_ptrs.cydata = p;
+
+	read_offset = si->si_ofs.cydata_ofs;
+
+	/* Read the CYDA registers up to MFGID field */
+	rc = cyttsp4_adap_read(cd, read_offset,
+			offsetof(struct cyttsp4_cydata, mfgid_sz)
+				+ sizeof(si->si_ptrs.cydata->mfgid_sz),
+			si->si_ptrs.cydata);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: fail read cydata r=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	/* Check MFGID size */
+	mfgid_sz = si->si_ptrs.cydata->mfgid_sz;
+	calc_mfgid_sz = si->si_ofs.cydata_size - sizeof(struct cyttsp4_cydata);
+	if (mfgid_sz != calc_mfgid_sz) {
+		dev_err(cd->dev, "%s: mismatch in MFGID size, reported:%d calculated:%d\n",
+			__func__, mfgid_sz, calc_mfgid_sz);
+		return -EINVAL;
+	}
+
+	read_offset += offsetof(struct cyttsp4_cydata, mfgid_sz)
+			+ sizeof(si->si_ptrs.cydata->mfgid_sz);
+
+	/* Read the CYDA registers for MFGID field */
+	rc = cyttsp4_adap_read(cd, read_offset, si->si_ptrs.cydata->mfgid_sz,
+			si->si_ptrs.cydata->mfg_id);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: fail read cydata r=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	read_offset += si->si_ptrs.cydata->mfgid_sz;
+
+	/* Read the rest of the CYDA registers */
+	rc = cyttsp4_adap_read(cd, read_offset,
+			sizeof(struct cyttsp4_cydata)
+				- offsetof(struct cyttsp4_cydata, cyito_idh),
+			&si->si_ptrs.cydata->cyito_idh);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: fail read cydata r=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.cydata,
+		si->si_ofs.cydata_size, "sysinfo_cydata");
+	return rc;
+}
+
+static int cyttsp4_si_get_test_data(struct cyttsp4 *cd)
+{
+	struct cyttsp4_sysinfo *si = &cd->sysinfo;
+	void *p;
+	int rc;
+
+	si->si_ofs.test_size = si->si_ofs.pcfg_ofs - si->si_ofs.test_ofs;
+
+	p = krealloc(si->si_ptrs.test, si->si_ofs.test_size, GFP_KERNEL);
+	if (p == NULL) {
+		dev_err(cd->dev, "%s: fail alloc test memory\n", __func__);
+		return -ENOMEM;
+	}
+	si->si_ptrs.test = p;
+
+	rc = cyttsp4_adap_read(cd, si->si_ofs.test_ofs, si->si_ofs.test_size,
+			si->si_ptrs.test);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: fail read test data r=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	cyttsp4_pr_buf(cd->dev, cd->pr_buf,
+		       (u8 *)si->si_ptrs.test, si->si_ofs.test_size,
+		       "sysinfo_test_data");
+	if (si->si_ptrs.test->post_codel &
+	    CY_POST_CODEL_WDG_RST)
+		dev_info(cd->dev, "%s: %s codel=%02X\n",
+			 __func__, "Reset was a WATCHDOG RESET",
+			 si->si_ptrs.test->post_codel);
+
+	if (!(si->si_ptrs.test->post_codel &
+	      CY_POST_CODEL_CFG_DATA_CRC_FAIL))
+		dev_info(cd->dev, "%s: %s codel=%02X\n", __func__,
+			 "Config Data CRC FAIL",
+			 si->si_ptrs.test->post_codel);
+
+	if (!(si->si_ptrs.test->post_codel &
+	      CY_POST_CODEL_PANEL_TEST_FAIL))
+		dev_info(cd->dev, "%s: %s codel=%02X\n",
+			 __func__, "PANEL TEST FAIL",
+			 si->si_ptrs.test->post_codel);
+
+	dev_info(cd->dev, "%s: SCANNING is %s codel=%02X\n",
+		 __func__, si->si_ptrs.test->post_codel & 0x08 ?
+		 "ENABLED" : "DISABLED",
+		 si->si_ptrs.test->post_codel);
+	return rc;
+}
+
+static int cyttsp4_si_get_pcfg_data(struct cyttsp4 *cd)
+{
+	struct cyttsp4_sysinfo *si = &cd->sysinfo;
+	void *p;
+	int rc;
+
+	si->si_ofs.pcfg_size = si->si_ofs.opcfg_ofs - si->si_ofs.pcfg_ofs;
+
+	p = krealloc(si->si_ptrs.pcfg, si->si_ofs.pcfg_size, GFP_KERNEL);
+	if (p == NULL) {
+		rc = -ENOMEM;
+		dev_err(cd->dev, "%s: fail alloc pcfg memory r=%d\n",
+			__func__, rc);
+		return rc;
+	}
+	si->si_ptrs.pcfg = p;
+
+	rc = cyttsp4_adap_read(cd, si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size,
+			si->si_ptrs.pcfg);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: fail read pcfg data r=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	si->si_ofs.max_x = merge_bytes((si->si_ptrs.pcfg->res_xh
+			& CY_PCFG_RESOLUTION_X_MASK), si->si_ptrs.pcfg->res_xl);
+	si->si_ofs.x_origin = !!(si->si_ptrs.pcfg->res_xh
+			& CY_PCFG_ORIGIN_X_MASK);
+	si->si_ofs.max_y = merge_bytes((si->si_ptrs.pcfg->res_yh
+			& CY_PCFG_RESOLUTION_Y_MASK), si->si_ptrs.pcfg->res_yl);
+	si->si_ofs.y_origin = !!(si->si_ptrs.pcfg->res_yh
+			& CY_PCFG_ORIGIN_Y_MASK);
+	si->si_ofs.max_p = merge_bytes(si->si_ptrs.pcfg->max_zh,
+			si->si_ptrs.pcfg->max_zl);
+
+	cyttsp4_pr_buf(cd->dev, cd->pr_buf,
+		       (u8 *)si->si_ptrs.pcfg,
+		       si->si_ofs.pcfg_size, "sysinfo_pcfg_data");
+	return rc;
+}
+
+static int cyttsp4_si_get_opcfg_data(struct cyttsp4 *cd)
+{
+	struct cyttsp4_sysinfo *si = &cd->sysinfo;
+	struct cyttsp4_tch_abs_params *tch;
+	struct cyttsp4_tch_rec_params *tch_old, *tch_new;
+	enum cyttsp4_tch_abs abs;
+	int i;
+	void *p;
+	int rc;
+
+	si->si_ofs.opcfg_size = si->si_ofs.ddata_ofs - si->si_ofs.opcfg_ofs;
+
+	p = krealloc(si->si_ptrs.opcfg, si->si_ofs.opcfg_size, GFP_KERNEL);
+	if (p == NULL) {
+		dev_err(cd->dev, "%s: fail alloc opcfg memory\n", __func__);
+		rc = -ENOMEM;
+		goto cyttsp4_si_get_opcfg_data_exit;
+	}
+	si->si_ptrs.opcfg = p;
+
+	rc = cyttsp4_adap_read(cd, si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size,
+			si->si_ptrs.opcfg);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: fail read opcfg data r=%d\n",
+			__func__, rc);
+		goto cyttsp4_si_get_opcfg_data_exit;
+	}
+	si->si_ofs.cmd_ofs = si->si_ptrs.opcfg->cmd_ofs;
+	si->si_ofs.rep_ofs = si->si_ptrs.opcfg->rep_ofs;
+	si->si_ofs.rep_sz = (si->si_ptrs.opcfg->rep_szh * 256) +
+		si->si_ptrs.opcfg->rep_szl;
+	si->si_ofs.num_btns = si->si_ptrs.opcfg->num_btns;
+	si->si_ofs.num_btn_regs = (si->si_ofs.num_btns +
+		CY_NUM_BTN_PER_REG - 1) / CY_NUM_BTN_PER_REG;
+	si->si_ofs.tt_stat_ofs = si->si_ptrs.opcfg->tt_stat_ofs;
+	si->si_ofs.obj_cfg0 = si->si_ptrs.opcfg->obj_cfg0;
+	si->si_ofs.max_tchs = si->si_ptrs.opcfg->max_tchs &
+		CY_BYTE_OFS_MASK;
+	si->si_ofs.tch_rec_size = si->si_ptrs.opcfg->tch_rec_size &
+		CY_BYTE_OFS_MASK;
+
+	/* Get the old touch fields */
+	for (abs = CY_TCH_X; abs < CY_NUM_TCH_FIELDS; abs++) {
+		tch = &si->si_ofs.tch_abs[abs];
+		tch_old = &si->si_ptrs.opcfg->tch_rec_old[abs];
+
+		tch->ofs = tch_old->loc & CY_BYTE_OFS_MASK;
+		tch->size = cyttsp4_bits_2_bytes(tch_old->size,
+						 &tch->max);
+		tch->bofs = (tch_old->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT;
+	}
+
+	/* button fields */
+	si->si_ofs.btn_rec_size = si->si_ptrs.opcfg->btn_rec_size;
+	si->si_ofs.btn_diff_ofs = si->si_ptrs.opcfg->btn_diff_ofs;
+	si->si_ofs.btn_diff_size = si->si_ptrs.opcfg->btn_diff_size;
+
+	if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) {
+		/* Get the extended touch fields */
+		for (i = 0; i < CY_NUM_EXT_TCH_FIELDS; abs++, i++) {
+			tch = &si->si_ofs.tch_abs[abs];
+			tch_new = &si->si_ptrs.opcfg->tch_rec_new[i];
+
+			tch->ofs = tch_new->loc & CY_BYTE_OFS_MASK;
+			tch->size = cyttsp4_bits_2_bytes(tch_new->size,
+							 &tch->max);
+			tch->bofs = (tch_new->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT;
+		}
+	}
+
+	for (abs = 0; abs < CY_TCH_NUM_ABS; abs++) {
+		dev_dbg(cd->dev, "%s: tch_rec_%s\n", __func__,
+			cyttsp4_tch_abs_string[abs]);
+		dev_dbg(cd->dev, "%s:     ofs =%2Zd\n", __func__,
+			si->si_ofs.tch_abs[abs].ofs);
+		dev_dbg(cd->dev, "%s:     siz =%2Zd\n", __func__,
+			si->si_ofs.tch_abs[abs].size);
+		dev_dbg(cd->dev, "%s:     max =%2Zd\n", __func__,
+			si->si_ofs.tch_abs[abs].max);
+		dev_dbg(cd->dev, "%s:     bofs=%2Zd\n", __func__,
+			si->si_ofs.tch_abs[abs].bofs);
+	}
+
+	si->si_ofs.mode_size = si->si_ofs.tt_stat_ofs + 1;
+	si->si_ofs.data_size = si->si_ofs.max_tchs *
+		si->si_ptrs.opcfg->tch_rec_size;
+
+	cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.opcfg,
+		si->si_ofs.opcfg_size, "sysinfo_opcfg_data");
+
+cyttsp4_si_get_opcfg_data_exit:
+	return rc;
+}
+
+static int cyttsp4_si_get_ddata(struct cyttsp4 *cd)
+{
+	struct cyttsp4_sysinfo *si = &cd->sysinfo;
+	void *p;
+	int rc;
+
+	si->si_ofs.ddata_size = si->si_ofs.mdata_ofs - si->si_ofs.ddata_ofs;
+
+	p = krealloc(si->si_ptrs.ddata, si->si_ofs.ddata_size, GFP_KERNEL);
+	if (p == NULL) {
+		dev_err(cd->dev, "%s: fail alloc ddata memory\n", __func__);
+		return -ENOMEM;
+	}
+	si->si_ptrs.ddata = p;
+
+	rc = cyttsp4_adap_read(cd, si->si_ofs.ddata_ofs, si->si_ofs.ddata_size,
+			si->si_ptrs.ddata);
+	if (rc < 0)
+		dev_err(cd->dev, "%s: fail read ddata data r=%d\n",
+			__func__, rc);
+	else
+		cyttsp4_pr_buf(cd->dev, cd->pr_buf,
+			       (u8 *)si->si_ptrs.ddata,
+			       si->si_ofs.ddata_size, "sysinfo_ddata");
+	return rc;
+}
+
+static int cyttsp4_si_get_mdata(struct cyttsp4 *cd)
+{
+	struct cyttsp4_sysinfo *si = &cd->sysinfo;
+	void *p;
+	int rc;
+
+	si->si_ofs.mdata_size = si->si_ofs.map_sz - si->si_ofs.mdata_ofs;
+
+	p = krealloc(si->si_ptrs.mdata, si->si_ofs.mdata_size, GFP_KERNEL);
+	if (p == NULL) {
+		dev_err(cd->dev, "%s: fail alloc mdata memory\n", __func__);
+		return -ENOMEM;
+	}
+	si->si_ptrs.mdata = p;
+
+	rc = cyttsp4_adap_read(cd, si->si_ofs.mdata_ofs, si->si_ofs.mdata_size,
+			si->si_ptrs.mdata);
+	if (rc < 0)
+		dev_err(cd->dev, "%s: fail read mdata data r=%d\n",
+			__func__, rc);
+	else
+		cyttsp4_pr_buf(cd->dev, cd->pr_buf,
+			       (u8 *)si->si_ptrs.mdata,
+			       si->si_ofs.mdata_size, "sysinfo_mdata");
+	return rc;
+}
+
+static int cyttsp4_si_get_btn_data(struct cyttsp4 *cd)
+{
+	struct cyttsp4_sysinfo *si = &cd->sysinfo;
+	int btn;
+	int num_defined_keys;
+	u16 *key_table;
+	void *p;
+	int rc = 0;
+
+	if (si->si_ofs.num_btns) {
+		si->si_ofs.btn_keys_size = si->si_ofs.num_btns *
+			sizeof(struct cyttsp4_btn);
+
+		p = krealloc(si->btn, si->si_ofs.btn_keys_size,
+				GFP_KERNEL|__GFP_ZERO);
+		if (p == NULL) {
+			dev_err(cd->dev, "%s: %s\n", __func__,
+				"fail alloc btn_keys memory");
+			return -ENOMEM;
+		}
+		si->btn = p;
+
+		if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS] == NULL)
+			num_defined_keys = 0;
+		else if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS]->data == NULL)
+			num_defined_keys = 0;
+		else
+			num_defined_keys = cd->cpdata->sett
+				[CY_IC_GRPNUM_BTN_KEYS]->size;
+
+		for (btn = 0; btn < si->si_ofs.num_btns &&
+			btn < num_defined_keys; btn++) {
+			key_table = (u16 *)cd->cpdata->sett
+				[CY_IC_GRPNUM_BTN_KEYS]->data;
+			si->btn[btn].key_code = key_table[btn];
+			si->btn[btn].state = CY_BTN_RELEASED;
+			si->btn[btn].enabled = true;
+		}
+		for (; btn < si->si_ofs.num_btns; btn++) {
+			si->btn[btn].key_code = KEY_RESERVED;
+			si->btn[btn].state = CY_BTN_RELEASED;
+			si->btn[btn].enabled = true;
+		}
+
+		return rc;
+	}
+
+	si->si_ofs.btn_keys_size = 0;
+	kfree(si->btn);
+	si->btn = NULL;
+	return rc;
+}
+
+static int cyttsp4_si_get_op_data_ptrs(struct cyttsp4 *cd)
+{
+	struct cyttsp4_sysinfo *si = &cd->sysinfo;
+	void *p;
+
+	p = krealloc(si->xy_mode, si->si_ofs.mode_size, GFP_KERNEL|__GFP_ZERO);
+	if (p == NULL)
+		return -ENOMEM;
+	si->xy_mode = p;
+
+	p = krealloc(si->xy_data, si->si_ofs.data_size, GFP_KERNEL|__GFP_ZERO);
+	if (p == NULL)
+		return -ENOMEM;
+	si->xy_data = p;
+
+	p = krealloc(si->btn_rec_data,
+			si->si_ofs.btn_rec_size * si->si_ofs.num_btns,
+			GFP_KERNEL|__GFP_ZERO);
+	if (p == NULL)
+		return -ENOMEM;
+	si->btn_rec_data = p;
+
+	return 0;
+}
+
+static void cyttsp4_si_put_log_data(struct cyttsp4 *cd)
+{
+	struct cyttsp4_sysinfo *si = &cd->sysinfo;
+	dev_dbg(cd->dev, "%s: cydata_ofs =%4Zd siz=%4Zd\n", __func__,
+		si->si_ofs.cydata_ofs, si->si_ofs.cydata_size);
+	dev_dbg(cd->dev, "%s: test_ofs   =%4Zd siz=%4Zd\n", __func__,
+		si->si_ofs.test_ofs, si->si_ofs.test_size);
+	dev_dbg(cd->dev, "%s: pcfg_ofs   =%4Zd siz=%4Zd\n", __func__,
+		si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size);
+	dev_dbg(cd->dev, "%s: opcfg_ofs  =%4Zd siz=%4Zd\n", __func__,
+		si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size);
+	dev_dbg(cd->dev, "%s: ddata_ofs  =%4Zd siz=%4Zd\n", __func__,
+		si->si_ofs.ddata_ofs, si->si_ofs.ddata_size);
+	dev_dbg(cd->dev, "%s: mdata_ofs  =%4Zd siz=%4Zd\n", __func__,
+		si->si_ofs.mdata_ofs, si->si_ofs.mdata_size);
+
+	dev_dbg(cd->dev, "%s: cmd_ofs       =%4Zd\n", __func__,
+		si->si_ofs.cmd_ofs);
+	dev_dbg(cd->dev, "%s: rep_ofs       =%4Zd\n", __func__,
+		si->si_ofs.rep_ofs);
+	dev_dbg(cd->dev, "%s: rep_sz        =%4Zd\n", __func__,
+		si->si_ofs.rep_sz);
+	dev_dbg(cd->dev, "%s: num_btns      =%4Zd\n", __func__,
+		si->si_ofs.num_btns);
+	dev_dbg(cd->dev, "%s: num_btn_regs  =%4Zd\n", __func__,
+		si->si_ofs.num_btn_regs);
+	dev_dbg(cd->dev, "%s: tt_stat_ofs   =%4Zd\n", __func__,
+		si->si_ofs.tt_stat_ofs);
+	dev_dbg(cd->dev, "%s: tch_rec_size  =%4Zd\n", __func__,
+		si->si_ofs.tch_rec_size);
+	dev_dbg(cd->dev, "%s: max_tchs      =%4Zd\n", __func__,
+		si->si_ofs.max_tchs);
+	dev_dbg(cd->dev, "%s: mode_size     =%4Zd\n", __func__,
+		si->si_ofs.mode_size);
+	dev_dbg(cd->dev, "%s: data_size     =%4Zd\n", __func__,
+		si->si_ofs.data_size);
+	dev_dbg(cd->dev, "%s: map_sz        =%4Zd\n", __func__,
+		si->si_ofs.map_sz);
+
+	dev_dbg(cd->dev, "%s: btn_rec_size   =%2Zd\n", __func__,
+		si->si_ofs.btn_rec_size);
+	dev_dbg(cd->dev, "%s: btn_diff_ofs   =%2Zd\n", __func__,
+		si->si_ofs.btn_diff_ofs);
+	dev_dbg(cd->dev, "%s: btn_diff_size  =%2Zd\n", __func__,
+		si->si_ofs.btn_diff_size);
+
+	dev_dbg(cd->dev, "%s: max_x    = 0x%04ZX (%Zd)\n", __func__,
+		si->si_ofs.max_x, si->si_ofs.max_x);
+	dev_dbg(cd->dev, "%s: x_origin = %Zd (%s)\n", __func__,
+		si->si_ofs.x_origin,
+		si->si_ofs.x_origin == CY_NORMAL_ORIGIN ?
+		"left corner" : "right corner");
+	dev_dbg(cd->dev, "%s: max_y    = 0x%04ZX (%Zd)\n", __func__,
+		si->si_ofs.max_y, si->si_ofs.max_y);
+	dev_dbg(cd->dev, "%s: y_origin = %Zd (%s)\n", __func__,
+		si->si_ofs.y_origin,
+		si->si_ofs.y_origin == CY_NORMAL_ORIGIN ?
+		"upper corner" : "lower corner");
+	dev_dbg(cd->dev, "%s: max_p    = 0x%04ZX (%Zd)\n", __func__,
+		si->si_ofs.max_p, si->si_ofs.max_p);
+
+	dev_dbg(cd->dev, "%s: xy_mode=%p xy_data=%p\n", __func__,
+		si->xy_mode, si->xy_data);
+}
+
+static int cyttsp4_get_sysinfo_regs(struct cyttsp4 *cd)
+{
+	struct cyttsp4_sysinfo *si = &cd->sysinfo;
+	int rc;
+
+	rc = cyttsp4_si_data_offsets(cd);
+	if (rc < 0)
+		return rc;
+
+	rc = cyttsp4_si_get_cydata(cd);
+	if (rc < 0)
+		return rc;
+
+	rc = cyttsp4_si_get_test_data(cd);
+	if (rc < 0)
+		return rc;
+
+	rc = cyttsp4_si_get_pcfg_data(cd);
+	if (rc < 0)
+		return rc;
+
+	rc = cyttsp4_si_get_opcfg_data(cd);
+	if (rc < 0)
+		return rc;
+
+	rc = cyttsp4_si_get_ddata(cd);
+	if (rc < 0)
+		return rc;
+
+	rc = cyttsp4_si_get_mdata(cd);
+	if (rc < 0)
+		return rc;
+
+	rc = cyttsp4_si_get_btn_data(cd);
+	if (rc < 0)
+		return rc;
+
+	rc = cyttsp4_si_get_op_data_ptrs(cd);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: failed to get_op_data\n",
+			__func__);
+		return rc;
+	}
+
+	cyttsp4_si_put_log_data(cd);
+
+	/* provide flow control handshake */
+	rc = cyttsp4_handshake(cd, si->si_data.hst_mode);
+	if (rc < 0)
+		dev_err(cd->dev, "%s: handshake fail on sysinfo reg\n",
+			__func__);
+
+	si->ready = true;
+	return rc;
+}
+
+static void cyttsp4_queue_startup_(struct cyttsp4 *cd)
+{
+	if (cd->startup_state == STARTUP_NONE) {
+		cd->startup_state = STARTUP_QUEUED;
+		schedule_work(&cd->startup_work);
+		dev_dbg(cd->dev, "%s: cyttsp4_startup queued\n", __func__);
+	} else {
+		dev_dbg(cd->dev, "%s: startup_state = %d\n", __func__,
+			cd->startup_state);
+	}
+}
+
+static void cyttsp4_report_slot_liftoff(struct cyttsp4_mt_data *md,
+		int max_slots)
+{
+	int t;
+
+	if (md->num_prv_tch == 0)
+		return;
+
+	for (t = 0; t < max_slots; t++) {
+		input_mt_slot(md->input, t);
+		input_mt_report_slot_state(md->input,
+			MT_TOOL_FINGER, false);
+	}
+}
+
+static void cyttsp4_lift_all(struct cyttsp4_mt_data *md)
+{
+	if (!md->si)
+		return;
+
+	if (md->num_prv_tch != 0) {
+		cyttsp4_report_slot_liftoff(md,
+				md->si->si_ofs.tch_abs[CY_TCH_T].max);
+		input_sync(md->input);
+		md->num_prv_tch = 0;
+	}
+}
+
+static void cyttsp4_get_touch_axis(struct cyttsp4_mt_data *md,
+	int *axis, int size, int max, u8 *xy_data, int bofs)
+{
+	int nbyte;
+	int next;
+
+	for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
+		dev_vdbg(&md->input->dev,
+			"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p"
+			" xy_data[%d]=%02X(%d) bofs=%d\n",
+			__func__, *axis, *axis, size, max, xy_data, next,
+			xy_data[next], xy_data[next], bofs);
+		*axis = (*axis * 256) + (xy_data[next] >> bofs);
+		next++;
+	}
+
+	*axis &= max - 1;
+
+	dev_vdbg(&md->input->dev,
+		"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p"
+		" xy_data[%d]=%02X(%d)\n",
+		__func__, *axis, *axis, size, max, xy_data, next,
+		xy_data[next], xy_data[next]);
+}
+
+static void cyttsp4_get_touch(struct cyttsp4_mt_data *md,
+	struct cyttsp4_touch *touch, u8 *xy_data)
+{
+	struct device *dev = &md->input->dev;
+	struct cyttsp4_sysinfo *si = md->si;
+	enum cyttsp4_tch_abs abs;
+	bool flipped;
+
+	for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) {
+		cyttsp4_get_touch_axis(md, &touch->abs[abs],
+			si->si_ofs.tch_abs[abs].size,
+			si->si_ofs.tch_abs[abs].max,
+			xy_data + si->si_ofs.tch_abs[abs].ofs,
+			si->si_ofs.tch_abs[abs].bofs);
+		dev_vdbg(dev, "%s: get %s=%04X(%d)\n", __func__,
+			cyttsp4_tch_abs_string[abs],
+			touch->abs[abs], touch->abs[abs]);
+	}
+
+	if (md->pdata->flags & CY_FLAG_FLIP) {
+		swap(touch->abs[CY_TCH_X], touch->abs[CY_TCH_Y]);
+		flipped = true;
+	} else
+		flipped = false;
+
+	if (md->pdata->flags & CY_FLAG_INV_X) {
+		if (flipped)
+			touch->abs[CY_TCH_X] = md->si->si_ofs.max_y -
+				touch->abs[CY_TCH_X];
+		else
+			touch->abs[CY_TCH_X] = md->si->si_ofs.max_x -
+				touch->abs[CY_TCH_X];
+	}
+	if (md->pdata->flags & CY_FLAG_INV_Y) {
+		if (flipped)
+			touch->abs[CY_TCH_Y] = md->si->si_ofs.max_x -
+				touch->abs[CY_TCH_Y];
+		else
+			touch->abs[CY_TCH_Y] = md->si->si_ofs.max_y -
+				touch->abs[CY_TCH_Y];
+	}
+
+	dev_vdbg(dev, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n",
+		__func__, flipped ? "true" : "false",
+		md->pdata->flags & CY_FLAG_INV_X ? "true" : "false",
+		md->pdata->flags & CY_FLAG_INV_Y ? "true" : "false",
+		touch->abs[CY_TCH_X], touch->abs[CY_TCH_X],
+		touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]);
+}
+
+static void cyttsp4_final_sync(struct input_dev *input, int max_slots, int *ids)
+{
+	int t;
+
+	for (t = 0; t < max_slots; t++) {
+		if (ids[t])
+			continue;
+		input_mt_slot(input, t);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+	}
+
+	input_sync(input);
+}
+
+static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch)
+{
+	struct device *dev = &md->input->dev;
+	struct cyttsp4_sysinfo *si = md->si;
+	struct cyttsp4_touch tch;
+	int sig;
+	int i, j, t = 0;
+	int ids[max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)];
+
+	memset(ids, 0, si->si_ofs.tch_abs[CY_TCH_T].max * sizeof(int));
+	for (i = 0; i < num_cur_tch; i++) {
+		cyttsp4_get_touch(md, &tch, si->xy_data +
+			(i * si->si_ofs.tch_rec_size));
+		if ((tch.abs[CY_TCH_T] < md->pdata->frmwrk->abs
+			[(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]) ||
+			(tch.abs[CY_TCH_T] > md->pdata->frmwrk->abs
+			[(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MAX_OST])) {
+			dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n",
+				__func__, i, tch.abs[CY_TCH_T],
+				md->pdata->frmwrk->abs[(CY_ABS_ID_OST *
+				CY_NUM_ABS_SET) + CY_MAX_OST]);
+			continue;
+		}
+
+		/* use 0 based track id's */
+		sig = md->pdata->frmwrk->abs
+			[(CY_ABS_ID_OST * CY_NUM_ABS_SET) + 0];
+		if (sig != CY_IGNORE_VALUE) {
+			t = tch.abs[CY_TCH_T] - md->pdata->frmwrk->abs
+				[(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST];
+			if (tch.abs[CY_TCH_E] == CY_EV_LIFTOFF) {
+				dev_dbg(dev, "%s: t=%d e=%d lift-off\n",
+					__func__, t, tch.abs[CY_TCH_E]);
+				goto cyttsp4_get_mt_touches_pr_tch;
+			}
+			input_mt_slot(md->input, t);
+			input_mt_report_slot_state(md->input, MT_TOOL_FINGER,
+					true);
+			ids[t] = true;
+		}
+
+		/* all devices: position and pressure fields */
+		for (j = 0; j <= CY_ABS_W_OST; j++) {
+			sig = md->pdata->frmwrk->abs[((CY_ABS_X_OST + j) *
+				CY_NUM_ABS_SET) + 0];
+			if (sig != CY_IGNORE_VALUE)
+				input_report_abs(md->input, sig,
+					tch.abs[CY_TCH_X + j]);
+		}
+		if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) {
+			/*
+			 * TMA400 size and orientation fields:
+			 * if pressure is non-zero and major touch
+			 * signal is zero, then set major and minor touch
+			 * signals to minimum non-zero value
+			 */
+			if (tch.abs[CY_TCH_P] > 0 && tch.abs[CY_TCH_MAJ] == 0)
+				tch.abs[CY_TCH_MAJ] = tch.abs[CY_TCH_MIN] = 1;
+
+			/* Get the extended touch fields */
+			for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) {
+				sig = md->pdata->frmwrk->abs
+					[((CY_ABS_MAJ_OST + j) *
+					CY_NUM_ABS_SET) + 0];
+				if (sig != CY_IGNORE_VALUE)
+					input_report_abs(md->input, sig,
+						tch.abs[CY_TCH_MAJ + j]);
+			}
+		}
+
+cyttsp4_get_mt_touches_pr_tch:
+		if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE)
+			dev_dbg(dev,
+				"%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d\n",
+				__func__, t,
+				tch.abs[CY_TCH_X],
+				tch.abs[CY_TCH_Y],
+				tch.abs[CY_TCH_P],
+				tch.abs[CY_TCH_MAJ],
+				tch.abs[CY_TCH_MIN],
+				tch.abs[CY_TCH_OR],
+				tch.abs[CY_TCH_E]);
+		else
+			dev_dbg(dev,
+				"%s: t=%d x=%d y=%d z=%d e=%d\n", __func__,
+				t,
+				tch.abs[CY_TCH_X],
+				tch.abs[CY_TCH_Y],
+				tch.abs[CY_TCH_P],
+				tch.abs[CY_TCH_E]);
+	}
+
+	cyttsp4_final_sync(md->input, si->si_ofs.tch_abs[CY_TCH_T].max, ids);
+
+	md->num_prv_tch = num_cur_tch;
+
+	return;
+}
+
+/* read xy_data for all current touches */
+static int cyttsp4_xy_worker(struct cyttsp4 *cd)
+{
+	struct cyttsp4_mt_data *md = &cd->md;
+	struct device *dev = &md->input->dev;
+	struct cyttsp4_sysinfo *si = md->si;
+	u8 num_cur_tch;
+	u8 hst_mode;
+	u8 rep_len;
+	u8 rep_stat;
+	u8 tt_stat;
+	int rc = 0;
+
+	/*
+	 * Get event data from cyttsp4 device.
+	 * The event data includes all data
+	 * for all active touches.
+	 * Event data also includes button data
+	 */
+	/*
+	 * Use 2 reads:
+	 * 1st read to get mode + button bytes + touch count (core)
+	 * 2nd read (optional) to get touch 1 - touch n data
+	 */
+	hst_mode = si->xy_mode[CY_REG_BASE];
+	rep_len = si->xy_mode[si->si_ofs.rep_ofs];
+	rep_stat = si->xy_mode[si->si_ofs.rep_ofs + 1];
+	tt_stat = si->xy_mode[si->si_ofs.tt_stat_ofs];
+	dev_vdbg(dev, "%s: %s%02X %s%d %s%02X %s%02X\n", __func__,
+		"hst_mode=", hst_mode, "rep_len=", rep_len,
+		"rep_stat=", rep_stat, "tt_stat=", tt_stat);
+
+	num_cur_tch = GET_NUM_TOUCHES(tt_stat);
+	dev_vdbg(dev, "%s: num_cur_tch=%d\n", __func__, num_cur_tch);
+
+	if (rep_len == 0 && num_cur_tch > 0) {
+		dev_err(dev, "%s: report length error rep_len=%d num_tch=%d\n",
+			__func__, rep_len, num_cur_tch);
+		goto cyttsp4_xy_worker_exit;
+	}
+
+	/* read touches */
+	if (num_cur_tch > 0) {
+		rc = cyttsp4_adap_read(cd, si->si_ofs.tt_stat_ofs + 1,
+				num_cur_tch * si->si_ofs.tch_rec_size,
+				si->xy_data);
+		if (rc < 0) {
+			dev_err(dev, "%s: read fail on touch regs r=%d\n",
+				__func__, rc);
+			goto cyttsp4_xy_worker_exit;
+		}
+	}
+
+	/* print xy data */
+	cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_data, num_cur_tch *
+		si->si_ofs.tch_rec_size, "xy_data");
+
+	/* check any error conditions */
+	if (IS_BAD_PKT(rep_stat)) {
+		dev_dbg(dev, "%s: Invalid buffer detected\n", __func__);
+		rc = 0;
+		goto cyttsp4_xy_worker_exit;
+	}
+
+	if (IS_LARGE_AREA(tt_stat))
+		dev_dbg(dev, "%s: Large area detected\n", __func__);
+
+	if (num_cur_tch > si->si_ofs.max_tchs) {
+		dev_err(dev, "%s: too many tch; set to max tch (n=%d c=%Zd)\n",
+				__func__, num_cur_tch, si->si_ofs.max_tchs);
+		num_cur_tch = si->si_ofs.max_tchs;
+	}
+
+	/* extract xy_data for all currently reported touches */
+	dev_vdbg(dev, "%s: extract data num_cur_tch=%d\n", __func__,
+		num_cur_tch);
+	if (num_cur_tch)
+		cyttsp4_get_mt_touches(md, num_cur_tch);
+	else
+		cyttsp4_lift_all(md);
+
+	rc = 0;
+
+cyttsp4_xy_worker_exit:
+	return rc;
+}
+
+static int cyttsp4_mt_attention(struct cyttsp4 *cd)
+{
+	struct device *dev = cd->dev;
+	struct cyttsp4_mt_data *md = &cd->md;
+	int rc = 0;
+
+	if (!md->si)
+		return 0;
+
+	mutex_lock(&md->report_lock);
+	if (!md->is_suspended) {
+		/* core handles handshake */
+		rc = cyttsp4_xy_worker(cd);
+	} else {
+		dev_vdbg(dev, "%s: Ignoring report while suspended\n",
+			__func__);
+	}
+	mutex_unlock(&md->report_lock);
+	if (rc < 0)
+		dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);
+
+	return rc;
+}
+
+static irqreturn_t cyttsp4_irq(int irq, void *handle)
+{
+	struct cyttsp4 *cd = handle;
+	struct device *dev = cd->dev;
+	enum cyttsp4_mode cur_mode;
+	u8 cmd_ofs = cd->sysinfo.si_ofs.cmd_ofs;
+	u8 mode[3];
+	int rc;
+
+	/*
+	 * Check whether this IRQ should be ignored (external)
+	 * This should be the very first thing to check since
+	 * ignore_irq may be set for a very short period of time
+	 */
+	if (atomic_read(&cd->ignore_irq)) {
+		dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__);
+		return IRQ_HANDLED;
+	}
+
+	dev_dbg(dev, "%s int:0x%x\n", __func__, cd->int_status);
+
+	mutex_lock(&cd->system_lock);
+
+	/* Just to debug */
+	if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING)
+		dev_vdbg(dev, "%s: Received IRQ while in sleep\n", __func__);
+
+	rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), mode);
+	if (rc) {
+		dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc);
+		goto cyttsp4_irq_exit;
+	}
+	dev_vdbg(dev, "%s mode[0-2]:0x%X 0x%X 0x%X\n", __func__,
+			mode[0], mode[1], mode[2]);
+
+	if (IS_BOOTLOADER(mode[0], mode[1])) {
+		cur_mode = CY_MODE_BOOTLOADER;
+		dev_vdbg(dev, "%s: bl running\n", __func__);
+		if (cd->mode == CY_MODE_BOOTLOADER) {
+			/* Signal bootloader heartbeat heard */
+			wake_up(&cd->wait_q);
+			goto cyttsp4_irq_exit;
+		}
+
+		/* switch to bootloader */
+		dev_dbg(dev, "%s: restart switch to bl m=%d -> m=%d\n",
+			__func__, cd->mode, cur_mode);
+
+		/* catch operation->bl glitch */
+		if (cd->mode != CY_MODE_UNKNOWN) {
+			/* Incase startup_state do not let startup_() */
+			cd->mode = CY_MODE_UNKNOWN;
+			cyttsp4_queue_startup_(cd);
+			goto cyttsp4_irq_exit;
+		}
+
+		/*
+		 * do not wake thread on this switch since
+		 * it is possible to get an early heartbeat
+		 * prior to performing the reset
+		 */
+		cd->mode = cur_mode;
+
+		goto cyttsp4_irq_exit;
+	}
+
+	switch (mode[0] & CY_HST_MODE) {
+	case CY_HST_OPERATE:
+		cur_mode = CY_MODE_OPERATIONAL;
+		dev_vdbg(dev, "%s: operational\n", __func__);
+		break;
+	case CY_HST_CAT:
+		cur_mode = CY_MODE_CAT;
+		dev_vdbg(dev, "%s: CaT\n", __func__);
+		break;
+	case CY_HST_SYSINFO:
+		cur_mode = CY_MODE_SYSINFO;
+		dev_vdbg(dev, "%s: sysinfo\n", __func__);
+		break;
+	default:
+		cur_mode = CY_MODE_UNKNOWN;
+		dev_err(dev, "%s: unknown HST mode 0x%02X\n", __func__,
+			mode[0]);
+		break;
+	}
+
+	/* Check whether this IRQ should be ignored (internal) */
+	if (cd->int_status & CY_INT_IGNORE) {
+		dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__);
+		goto cyttsp4_irq_exit;
+	}
+
+	/* Check for wake up interrupt */
+	if (cd->int_status & CY_INT_AWAKE) {
+		cd->int_status &= ~CY_INT_AWAKE;
+		wake_up(&cd->wait_q);
+		dev_vdbg(dev, "%s: Received wake up interrupt\n", __func__);
+		goto cyttsp4_irq_handshake;
+	}
+
+	/* Expecting mode change interrupt */
+	if ((cd->int_status & CY_INT_MODE_CHANGE)
+			&& (mode[0] & CY_HST_MODE_CHANGE) == 0) {
+		cd->int_status &= ~CY_INT_MODE_CHANGE;
+		dev_dbg(dev, "%s: finish mode switch m=%d -> m=%d\n",
+				__func__, cd->mode, cur_mode);
+		cd->mode = cur_mode;
+		wake_up(&cd->wait_q);
+		goto cyttsp4_irq_handshake;
+	}
+
+	/* compare current core mode to current device mode */
+	dev_vdbg(dev, "%s: cd->mode=%d cur_mode=%d\n",
+			__func__, cd->mode, cur_mode);
+	if ((mode[0] & CY_HST_MODE_CHANGE) == 0 && cd->mode != cur_mode) {
+		/* Unexpected mode change occurred */
+		dev_err(dev, "%s %d->%d 0x%x\n", __func__, cd->mode,
+				cur_mode, cd->int_status);
+		dev_dbg(dev, "%s: Unexpected mode change, startup\n",
+				__func__);
+		cyttsp4_queue_startup_(cd);
+		goto cyttsp4_irq_exit;
+	}
+
+	/* Expecting command complete interrupt */
+	dev_vdbg(dev, "%s: command byte:0x%x\n", __func__, mode[cmd_ofs]);
+	if ((cd->int_status & CY_INT_EXEC_CMD)
+			&& mode[cmd_ofs] & CY_CMD_COMPLETE) {
+		cd->int_status &= ~CY_INT_EXEC_CMD;
+		dev_vdbg(dev, "%s: Received command complete interrupt\n",
+				__func__);
+		wake_up(&cd->wait_q);
+		/*
+		 * It is possible to receive a single interrupt for
+		 * command complete and touch/button status report.
+		 * Continue processing for a possible status report.
+		 */
+	}
+
+	/* This should be status report, read status regs */
+	if (cd->mode == CY_MODE_OPERATIONAL) {
+		dev_vdbg(dev, "%s: Read status registers\n", __func__);
+		rc = cyttsp4_load_status_regs(cd);
+		if (rc < 0)
+			dev_err(dev, "%s: fail read mode regs r=%d\n",
+				__func__, rc);
+	}
+
+	cyttsp4_mt_attention(cd);
+
+cyttsp4_irq_handshake:
+	/* handshake the event */
+	dev_vdbg(dev, "%s: Handshake mode=0x%02X r=%d\n",
+			__func__, mode[0], rc);
+	rc = cyttsp4_handshake(cd, mode[0]);
+	if (rc < 0)
+		dev_err(dev, "%s: Fail handshake mode=0x%02X r=%d\n",
+				__func__, mode[0], rc);
+
+	/*
+	 * a non-zero udelay period is required for using
+	 * IRQF_TRIGGER_LOW in order to delay until the
+	 * device completes isr deassert
+	 */
+	udelay(cd->cpdata->level_irq_udelay);
+
+cyttsp4_irq_exit:
+	mutex_unlock(&cd->system_lock);
+	return IRQ_HANDLED;
+}
+
+static void cyttsp4_start_wd_timer(struct cyttsp4 *cd)
+{
+	if (!CY_WATCHDOG_TIMEOUT)
+		return;
+
+	mod_timer(&cd->watchdog_timer, jiffies +
+			msecs_to_jiffies(CY_WATCHDOG_TIMEOUT));
+}
+
+static void cyttsp4_stop_wd_timer(struct cyttsp4 *cd)
+{
+	if (!CY_WATCHDOG_TIMEOUT)
+		return;
+
+	/*
+	 * Ensure we wait until the watchdog timer
+	 * running on a different CPU finishes
+	 */
+	del_timer_sync(&cd->watchdog_timer);
+	cancel_work_sync(&cd->watchdog_work);
+	del_timer_sync(&cd->watchdog_timer);
+}
+
+static void cyttsp4_watchdog_timer(unsigned long handle)
+{
+	struct cyttsp4 *cd = (struct cyttsp4 *)handle;
+
+	dev_vdbg(cd->dev, "%s: Watchdog timer triggered\n", __func__);
+
+	schedule_work(&cd->watchdog_work);
+
+	return;
+}
+
+static int cyttsp4_request_exclusive(struct cyttsp4 *cd, void *ownptr,
+		int timeout_ms)
+{
+	int t = msecs_to_jiffies(timeout_ms);
+	bool with_timeout = (timeout_ms != 0);
+
+	mutex_lock(&cd->system_lock);
+	if (!cd->exclusive_dev && cd->exclusive_waits == 0) {
+		cd->exclusive_dev = ownptr;
+		goto exit;
+	}
+
+	cd->exclusive_waits++;
+wait:
+	mutex_unlock(&cd->system_lock);
+	if (with_timeout) {
+		t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t);
+		if (IS_TMO(t)) {
+			dev_err(cd->dev, "%s: tmo waiting exclusive access\n",
+				__func__);
+			mutex_lock(&cd->system_lock);
+			cd->exclusive_waits--;
+			mutex_unlock(&cd->system_lock);
+			return -ETIME;
+		}
+	} else {
+		wait_event(cd->wait_q, !cd->exclusive_dev);
+	}
+	mutex_lock(&cd->system_lock);
+	if (cd->exclusive_dev)
+		goto wait;
+	cd->exclusive_dev = ownptr;
+	cd->exclusive_waits--;
+exit:
+	mutex_unlock(&cd->system_lock);
+
+	return 0;
+}
+
+/*
+ * returns error if was not owned
+ */
+static int cyttsp4_release_exclusive(struct cyttsp4 *cd, void *ownptr)
+{
+	mutex_lock(&cd->system_lock);
+	if (cd->exclusive_dev != ownptr) {
+		mutex_unlock(&cd->system_lock);
+		return -EINVAL;
+	}
+
+	dev_vdbg(cd->dev, "%s: exclusive_dev %p freed\n",
+		__func__, cd->exclusive_dev);
+	cd->exclusive_dev = NULL;
+	wake_up(&cd->wait_q);
+	mutex_unlock(&cd->system_lock);
+	return 0;
+}
+
+static int cyttsp4_wait_bl_heartbeat(struct cyttsp4 *cd)
+{
+	long t;
+	int rc = 0;
+
+	/* wait heartbeat */
+	dev_vdbg(cd->dev, "%s: wait heartbeat...\n", __func__);
+	t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_BOOTLOADER,
+			msecs_to_jiffies(CY_CORE_RESET_AND_WAIT_TIMEOUT));
+	if (IS_TMO(t)) {
+		dev_err(cd->dev, "%s: tmo waiting bl heartbeat cd->mode=%d\n",
+			__func__, cd->mode);
+		rc = -ETIME;
+	}
+
+	return rc;
+}
+
+static int cyttsp4_wait_sysinfo_mode(struct cyttsp4 *cd)
+{
+	long t;
+
+	dev_vdbg(cd->dev, "%s: wait sysinfo...\n", __func__);
+
+	t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_SYSINFO,
+			msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT));
+	if (IS_TMO(t)) {
+		dev_err(cd->dev, "%s: tmo waiting exit bl cd->mode=%d\n",
+			__func__, cd->mode);
+		mutex_lock(&cd->system_lock);
+		cd->int_status &= ~CY_INT_MODE_CHANGE;
+		mutex_unlock(&cd->system_lock);
+		return -ETIME;
+	}
+
+	return 0;
+}
+
+static int cyttsp4_reset_and_wait(struct cyttsp4 *cd)
+{
+	int rc;
+
+	/* reset hardware */
+	mutex_lock(&cd->system_lock);
+	dev_dbg(cd->dev, "%s: reset hw...\n", __func__);
+	rc = cyttsp4_hw_reset(cd);
+	cd->mode = CY_MODE_UNKNOWN;
+	mutex_unlock(&cd->system_lock);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s:Fail hw reset r=%d\n", __func__, rc);
+		return rc;
+	}
+
+	return cyttsp4_wait_bl_heartbeat(cd);
+}
+
+/*
+ * returns err if refused or timeout; block until mode change complete
+ * bit is set (mode change interrupt)
+ */
+static int cyttsp4_set_mode(struct cyttsp4 *cd, int new_mode)
+{
+	u8 new_dev_mode;
+	u8 mode;
+	long t;
+	int rc;
+
+	switch (new_mode) {
+	case CY_MODE_OPERATIONAL:
+		new_dev_mode = CY_HST_OPERATE;
+		break;
+	case CY_MODE_SYSINFO:
+		new_dev_mode = CY_HST_SYSINFO;
+		break;
+	case CY_MODE_CAT:
+		new_dev_mode = CY_HST_CAT;
+		break;
+	default:
+		dev_err(cd->dev, "%s: invalid mode: %02X(%d)\n",
+			__func__, new_mode, new_mode);
+		return -EINVAL;
+	}
+
+	/* change mode */
+	dev_dbg(cd->dev, "%s: %s=%p new_dev_mode=%02X new_mode=%d\n",
+			__func__, "have exclusive", cd->exclusive_dev,
+			new_dev_mode, new_mode);
+
+	mutex_lock(&cd->system_lock);
+	rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode);
+	if (rc < 0) {
+		mutex_unlock(&cd->system_lock);
+		dev_err(cd->dev, "%s: Fail read mode r=%d\n",
+			__func__, rc);
+		goto exit;
+	}
+
+	/* Clear device mode bits and set to new mode */
+	mode &= ~CY_HST_MODE;
+	mode |= new_dev_mode | CY_HST_MODE_CHANGE;
+
+	cd->int_status |= CY_INT_MODE_CHANGE;
+	rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode), &mode);
+	mutex_unlock(&cd->system_lock);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: Fail write mode change r=%d\n",
+				__func__, rc);
+		goto exit;
+	}
+
+	/* wait for mode change done interrupt */
+	t = wait_event_timeout(cd->wait_q,
+			(cd->int_status & CY_INT_MODE_CHANGE) == 0,
+			msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT));
+	dev_dbg(cd->dev, "%s: back from wait t=%ld cd->mode=%d\n",
+			__func__, t, cd->mode);
+
+	if (IS_TMO(t)) {
+		dev_err(cd->dev, "%s: %s\n", __func__,
+				"tmo waiting mode change");
+		mutex_lock(&cd->system_lock);
+		cd->int_status &= ~CY_INT_MODE_CHANGE;
+		mutex_unlock(&cd->system_lock);
+		rc = -EINVAL;
+	}
+
+exit:
+	return rc;
+}
+
+static void cyttsp4_watchdog_work(struct work_struct *work)
+{
+	struct cyttsp4 *cd =
+		container_of(work, struct cyttsp4, watchdog_work);
+	u8 *mode;
+	int retval;
+
+	mutex_lock(&cd->system_lock);
+	retval = cyttsp4_load_status_regs(cd);
+	if (retval < 0) {
+		dev_err(cd->dev,
+			"%s: failed to access device in watchdog timer r=%d\n",
+			__func__, retval);
+		cyttsp4_queue_startup_(cd);
+		goto cyttsp4_timer_watchdog_exit_error;
+	}
+	mode = &cd->sysinfo.xy_mode[CY_REG_BASE];
+	if (IS_BOOTLOADER(mode[0], mode[1])) {
+		dev_err(cd->dev,
+			"%s: device found in bootloader mode when operational mode\n",
+			__func__);
+		cyttsp4_queue_startup_(cd);
+		goto cyttsp4_timer_watchdog_exit_error;
+	}
+
+	cyttsp4_start_wd_timer(cd);
+cyttsp4_timer_watchdog_exit_error:
+	mutex_unlock(&cd->system_lock);
+	return;
+}
+
+static int cyttsp4_core_sleep_(struct cyttsp4 *cd)
+{
+	enum cyttsp4_sleep_state ss = SS_SLEEP_ON;
+	enum cyttsp4_int_state int_status = CY_INT_IGNORE;
+	int rc = 0;
+	u8 mode[2];
+
+	/* Already in sleep mode? */
+	mutex_lock(&cd->system_lock);
+	if (cd->sleep_state == SS_SLEEP_ON) {
+		mutex_unlock(&cd->system_lock);
+		return 0;
+	}
+	cd->sleep_state = SS_SLEEPING;
+	mutex_unlock(&cd->system_lock);
+
+	cyttsp4_stop_wd_timer(cd);
+
+	/* Wait until currently running IRQ handler exits and disable IRQ */
+	disable_irq(cd->irq);
+
+	dev_vdbg(cd->dev, "%s: write DEEP SLEEP...\n", __func__);
+	mutex_lock(&cd->system_lock);
+	rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode);
+	if (rc) {
+		mutex_unlock(&cd->system_lock);
+		dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc);
+		goto error;
+	}
+
+	if (IS_BOOTLOADER(mode[0], mode[1])) {
+		mutex_unlock(&cd->system_lock);
+		dev_err(cd->dev, "%s: Device in BOOTLADER mode.\n", __func__);
+		rc = -EINVAL;
+		goto error;
+	}
+
+	mode[0] |= CY_HST_SLEEP;
+	rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode[0]), &mode[0]);
+	mutex_unlock(&cd->system_lock);
+	if (rc) {
+		dev_err(cd->dev, "%s: Fail write adapter r=%d\n", __func__, rc);
+		goto error;
+	}
+	dev_vdbg(cd->dev, "%s: write DEEP SLEEP succeeded\n", __func__);
+
+	if (cd->cpdata->power) {
+		dev_dbg(cd->dev, "%s: Power down HW\n", __func__);
+		rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, &cd->ignore_irq);
+	} else {
+		dev_dbg(cd->dev, "%s: No power function\n", __func__);
+		rc = 0;
+	}
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: HW Power down fails r=%d\n",
+				__func__, rc);
+		goto error;
+	}
+
+	/* Give time to FW to sleep */
+	msleep(50);
+
+	goto exit;
+
+error:
+	ss = SS_SLEEP_OFF;
+	int_status = CY_INT_NONE;
+	cyttsp4_start_wd_timer(cd);
+
+exit:
+	mutex_lock(&cd->system_lock);
+	cd->sleep_state = ss;
+	cd->int_status |= int_status;
+	mutex_unlock(&cd->system_lock);
+	enable_irq(cd->irq);
+	return rc;
+}
+
+static int cyttsp4_startup_(struct cyttsp4 *cd)
+{
+	int retry = CY_CORE_STARTUP_RETRY_COUNT;
+	int rc;
+
+	cyttsp4_stop_wd_timer(cd);
+
+reset:
+	if (retry != CY_CORE_STARTUP_RETRY_COUNT)
+		dev_dbg(cd->dev, "%s: Retry %d\n", __func__,
+			CY_CORE_STARTUP_RETRY_COUNT - retry);
+
+	/* reset hardware and wait for heartbeat */
+	rc = cyttsp4_reset_and_wait(cd);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: Error on h/w reset r=%d\n", __func__, rc);
+		if (retry--)
+			goto reset;
+		goto exit;
+	}
+
+	/* exit bl into sysinfo mode */
+	dev_vdbg(cd->dev, "%s: write exit ldr...\n", __func__);
+	mutex_lock(&cd->system_lock);
+	cd->int_status &= ~CY_INT_IGNORE;
+	cd->int_status |= CY_INT_MODE_CHANGE;
+
+	rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(ldr_exit),
+			(u8 *)ldr_exit);
+	mutex_unlock(&cd->system_lock);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: Fail write r=%d\n", __func__, rc);
+		if (retry--)
+			goto reset;
+		goto exit;
+	}
+
+	rc = cyttsp4_wait_sysinfo_mode(cd);
+	if (rc < 0) {
+		u8 buf[sizeof(ldr_err_app)];
+		int rc1;
+
+		/* Check for invalid/corrupted touch application */
+		rc1 = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(ldr_err_app),
+				buf);
+		if (rc1) {
+			dev_err(cd->dev, "%s: Fail read r=%d\n", __func__, rc1);
+		} else if (!memcmp(buf, ldr_err_app, sizeof(ldr_err_app))) {
+			dev_err(cd->dev, "%s: Error launching touch application\n",
+				__func__);
+			mutex_lock(&cd->system_lock);
+			cd->invalid_touch_app = true;
+			mutex_unlock(&cd->system_lock);
+			goto exit_no_wd;
+		}
+
+		if (retry--)
+			goto reset;
+		goto exit;
+	}
+
+	mutex_lock(&cd->system_lock);
+	cd->invalid_touch_app = false;
+	mutex_unlock(&cd->system_lock);
+
+	/* read sysinfo data */
+	dev_vdbg(cd->dev, "%s: get sysinfo regs..\n", __func__);
+	rc = cyttsp4_get_sysinfo_regs(cd);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: failed to get sysinfo regs rc=%d\n",
+			__func__, rc);
+		if (retry--)
+			goto reset;
+		goto exit;
+	}
+
+	rc = cyttsp4_set_mode(cd, CY_MODE_OPERATIONAL);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: failed to set mode to operational rc=%d\n",
+			__func__, rc);
+		if (retry--)
+			goto reset;
+		goto exit;
+	}
+
+	cyttsp4_lift_all(&cd->md);
+
+	/* restore to sleep if was suspended */
+	mutex_lock(&cd->system_lock);
+	if (cd->sleep_state == SS_SLEEP_ON) {
+		cd->sleep_state = SS_SLEEP_OFF;
+		mutex_unlock(&cd->system_lock);
+		cyttsp4_core_sleep_(cd);
+		goto exit_no_wd;
+	}
+	mutex_unlock(&cd->system_lock);
+
+exit:
+	cyttsp4_start_wd_timer(cd);
+exit_no_wd:
+	return rc;
+}
+
+static int cyttsp4_startup(struct cyttsp4 *cd)
+{
+	int rc;
+
+	mutex_lock(&cd->system_lock);
+	cd->startup_state = STARTUP_RUNNING;
+	mutex_unlock(&cd->system_lock);
+
+	rc = cyttsp4_request_exclusive(cd, cd->dev,
+			CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		goto exit;
+	}
+
+	rc = cyttsp4_startup_(cd);
+
+	if (cyttsp4_release_exclusive(cd, cd->dev) < 0)
+		/* Don't return fail code, mode is already changed. */
+		dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+	else
+		dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__);
+
+exit:
+	mutex_lock(&cd->system_lock);
+	cd->startup_state = STARTUP_NONE;
+	mutex_unlock(&cd->system_lock);
+
+	/* Wake the waiters for end of startup */
+	wake_up(&cd->wait_q);
+
+	return rc;
+}
+
+static void cyttsp4_startup_work_function(struct work_struct *work)
+{
+	struct cyttsp4 *cd =  container_of(work, struct cyttsp4, startup_work);
+	int rc;
+
+	rc = cyttsp4_startup(cd);
+	if (rc < 0)
+		dev_err(cd->dev, "%s: Fail queued startup r=%d\n",
+			__func__, rc);
+}
+
+static void cyttsp4_free_si_ptrs(struct cyttsp4 *cd)
+{
+	struct cyttsp4_sysinfo *si = &cd->sysinfo;
+
+	if (!si)
+		return;
+
+	kfree(si->si_ptrs.cydata);
+	kfree(si->si_ptrs.test);
+	kfree(si->si_ptrs.pcfg);
+	kfree(si->si_ptrs.opcfg);
+	kfree(si->si_ptrs.ddata);
+	kfree(si->si_ptrs.mdata);
+	kfree(si->btn);
+	kfree(si->xy_mode);
+	kfree(si->xy_data);
+	kfree(si->btn_rec_data);
+}
+
+#ifdef CONFIG_PM
+static int cyttsp4_core_sleep(struct cyttsp4 *cd)
+{
+	int rc;
+
+	rc = cyttsp4_request_exclusive(cd, cd->dev,
+			CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return 0;
+	}
+
+	rc = cyttsp4_core_sleep_(cd);
+
+	if (cyttsp4_release_exclusive(cd, cd->dev) < 0)
+		dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+	else
+		dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__);
+
+	return rc;
+}
+
+static int cyttsp4_core_wake_(struct cyttsp4 *cd)
+{
+	struct device *dev = cd->dev;
+	int rc;
+	u8 mode;
+	int t;
+
+	/* Already woken? */
+	mutex_lock(&cd->system_lock);
+	if (cd->sleep_state == SS_SLEEP_OFF) {
+		mutex_unlock(&cd->system_lock);
+		return 0;
+	}
+	cd->int_status &= ~CY_INT_IGNORE;
+	cd->int_status |= CY_INT_AWAKE;
+	cd->sleep_state = SS_WAKING;
+
+	if (cd->cpdata->power) {
+		dev_dbg(dev, "%s: Power up HW\n", __func__);
+		rc = cd->cpdata->power(cd->cpdata, 1, dev, &cd->ignore_irq);
+	} else {
+		dev_dbg(dev, "%s: No power function\n", __func__);
+		rc = -ENOSYS;
+	}
+	if (rc < 0) {
+		dev_err(dev, "%s: HW Power up fails r=%d\n",
+				__func__, rc);
+
+		/* Initiate a read transaction to wake up */
+		cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode);
+	} else
+		dev_vdbg(cd->dev, "%s: HW power up succeeds\n",
+			__func__);
+	mutex_unlock(&cd->system_lock);
+
+	t = wait_event_timeout(cd->wait_q,
+			(cd->int_status & CY_INT_AWAKE) == 0,
+			msecs_to_jiffies(CY_CORE_WAKEUP_TIMEOUT));
+	if (IS_TMO(t)) {
+		dev_err(dev, "%s: TMO waiting for wakeup\n", __func__);
+		mutex_lock(&cd->system_lock);
+		cd->int_status &= ~CY_INT_AWAKE;
+		/* Try starting up */
+		cyttsp4_queue_startup_(cd);
+		mutex_unlock(&cd->system_lock);
+	}
+
+	mutex_lock(&cd->system_lock);
+	cd->sleep_state = SS_SLEEP_OFF;
+	mutex_unlock(&cd->system_lock);
+
+	cyttsp4_start_wd_timer(cd);
+
+	return 0;
+}
+
+static int cyttsp4_core_wake(struct cyttsp4 *cd)
+{
+	int rc;
+
+	rc = cyttsp4_request_exclusive(cd, cd->dev,
+			CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return 0;
+	}
+
+	rc = cyttsp4_core_wake_(cd);
+
+	if (cyttsp4_release_exclusive(cd, cd->dev) < 0)
+		dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+	else
+		dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__);
+
+	return rc;
+}
+
+static int cyttsp4_core_suspend(struct device *dev)
+{
+	struct cyttsp4 *cd = dev_get_drvdata(dev);
+	struct cyttsp4_mt_data *md = &cd->md;
+	int rc;
+
+	md->is_suspended = true;
+
+	rc = cyttsp4_core_sleep(cd);
+	if (rc < 0) {
+		dev_err(dev, "%s: Error on sleep\n", __func__);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+static int cyttsp4_core_resume(struct device *dev)
+{
+	struct cyttsp4 *cd = dev_get_drvdata(dev);
+	struct cyttsp4_mt_data *md = &cd->md;
+	int rc;
+
+	md->is_suspended = false;
+
+	rc = cyttsp4_core_wake(cd);
+	if (rc < 0) {
+		dev_err(dev, "%s: Error on wake\n", __func__);
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+#endif
+
+const struct dev_pm_ops cyttsp4_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume)
+	SET_RUNTIME_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(cyttsp4_pm_ops);
+
+static int cyttsp4_mt_open(struct input_dev *input)
+{
+	pm_runtime_get(input->dev.parent);
+	return 0;
+}
+
+static void cyttsp4_mt_close(struct input_dev *input)
+{
+	struct cyttsp4_mt_data *md = input_get_drvdata(input);
+	mutex_lock(&md->report_lock);
+	if (!md->is_suspended)
+		pm_runtime_put(input->dev.parent);
+	mutex_unlock(&md->report_lock);
+}
+
+
+static int cyttsp4_setup_input_device(struct cyttsp4 *cd)
+{
+	struct device *dev = cd->dev;
+	struct cyttsp4_mt_data *md = &cd->md;
+	int signal = CY_IGNORE_VALUE;
+	int max_x, max_y, max_p, min, max;
+	int max_x_tmp, max_y_tmp;
+	int i;
+	int rc;
+
+	dev_vdbg(dev, "%s: Initialize event signals\n", __func__);
+	__set_bit(EV_ABS, md->input->evbit);
+	__set_bit(EV_REL, md->input->evbit);
+	__set_bit(EV_KEY, md->input->evbit);
+
+	max_x_tmp = md->si->si_ofs.max_x;
+	max_y_tmp = md->si->si_ofs.max_y;
+
+	/* get maximum values from the sysinfo data */
+	if (md->pdata->flags & CY_FLAG_FLIP) {
+		max_x = max_y_tmp - 1;
+		max_y = max_x_tmp - 1;
+	} else {
+		max_x = max_x_tmp - 1;
+		max_y = max_y_tmp - 1;
+	}
+	max_p = md->si->si_ofs.max_p;
+
+	/* set event signal capabilities */
+	for (i = 0; i < (md->pdata->frmwrk->size / CY_NUM_ABS_SET); i++) {
+		signal = md->pdata->frmwrk->abs
+			[(i * CY_NUM_ABS_SET) + CY_SIGNAL_OST];
+		if (signal != CY_IGNORE_VALUE) {
+			__set_bit(signal, md->input->absbit);
+			min = md->pdata->frmwrk->abs
+				[(i * CY_NUM_ABS_SET) + CY_MIN_OST];
+			max = md->pdata->frmwrk->abs
+				[(i * CY_NUM_ABS_SET) + CY_MAX_OST];
+			if (i == CY_ABS_ID_OST) {
+				/* shift track ids down to start at 0 */
+				max = max - min;
+				min = min - min;
+			} else if (i == CY_ABS_X_OST)
+				max = max_x;
+			else if (i == CY_ABS_Y_OST)
+				max = max_y;
+			else if (i == CY_ABS_P_OST)
+				max = max_p;
+			input_set_abs_params(md->input, signal, min, max,
+				md->pdata->frmwrk->abs
+				[(i * CY_NUM_ABS_SET) + CY_FUZZ_OST],
+				md->pdata->frmwrk->abs
+				[(i * CY_NUM_ABS_SET) + CY_FLAT_OST]);
+			dev_dbg(dev, "%s: register signal=%02X min=%d max=%d\n",
+				__func__, signal, min, max);
+			if ((i == CY_ABS_ID_OST) &&
+				(md->si->si_ofs.tch_rec_size <
+				CY_TMA4XX_TCH_REC_SIZE))
+				break;
+		}
+	}
+
+	input_mt_init_slots(md->input, md->si->si_ofs.tch_abs[CY_TCH_T].max,
+			INPUT_MT_DIRECT);
+	rc = input_register_device(md->input);
+	if (rc < 0)
+		dev_err(dev, "%s: Error, failed register input device r=%d\n",
+			__func__, rc);
+	return rc;
+}
+
+static int cyttsp4_mt_probe(struct cyttsp4 *cd)
+{
+	struct device *dev = cd->dev;
+	struct cyttsp4_mt_data *md = &cd->md;
+	struct cyttsp4_mt_platform_data *pdata = cd->pdata->mt_pdata;
+	int rc = 0;
+
+	mutex_init(&md->report_lock);
+	md->pdata = pdata;
+	/* Create the input device and register it. */
+	dev_vdbg(dev, "%s: Create the input device and register it\n",
+		__func__);
+	md->input = input_allocate_device();
+	if (md->input == NULL) {
+		dev_err(dev, "%s: Error, failed to allocate input device\n",
+			__func__);
+		rc = -ENOSYS;
+		goto error_alloc_failed;
+	}
+
+	md->input->name = pdata->inp_dev_name;
+	scnprintf(md->phys, sizeof(md->phys)-1, "%s", dev_name(dev));
+	md->input->phys = md->phys;
+	md->input->id.bustype = cd->bus_ops->bustype;
+	md->input->dev.parent = dev;
+	md->input->open = cyttsp4_mt_open;
+	md->input->close = cyttsp4_mt_close;
+	input_set_drvdata(md->input, md);
+
+	/* get sysinfo */
+	md->si = &cd->sysinfo;
+	if (!md->si) {
+		dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
+			__func__, md->si);
+		goto error_get_sysinfo;
+	}
+
+	rc = cyttsp4_setup_input_device(cd);
+	if (rc)
+		goto error_init_input;
+
+	return 0;
+
+error_init_input:
+	input_free_device(md->input);
+error_get_sysinfo:
+	input_set_drvdata(md->input, NULL);
+error_alloc_failed:
+	dev_err(dev, "%s failed.\n", __func__);
+	return rc;
+}
+
+struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops,
+		struct device *dev, u16 irq, size_t xfer_buf_size)
+{
+	struct cyttsp4 *cd;
+	struct cyttsp4_platform_data *pdata = dev_get_platdata(dev);
+	unsigned long irq_flags;
+	int rc = 0;
+
+	if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) {
+		dev_err(dev, "%s: Missing platform data\n", __func__);
+		rc = -ENODEV;
+		goto error_no_pdata;
+	}
+
+	cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+	if (!cd) {
+		dev_err(dev, "%s: Error, kzalloc\n", __func__);
+		rc = -ENOMEM;
+		goto error_alloc_data;
+	}
+
+	cd->xfer_buf = kzalloc(xfer_buf_size, GFP_KERNEL);
+	if (!cd->xfer_buf) {
+		dev_err(dev, "%s: Error, kzalloc\n", __func__);
+		rc = -ENOMEM;
+		goto error_free_cd;
+	}
+
+	/* Initialize device info */
+	cd->dev = dev;
+	cd->pdata = pdata;
+	cd->cpdata = pdata->core_pdata;
+	cd->bus_ops = ops;
+
+	/* Initialize mutexes and spinlocks */
+	mutex_init(&cd->system_lock);
+	mutex_init(&cd->adap_lock);
+
+	/* Initialize wait queue */
+	init_waitqueue_head(&cd->wait_q);
+
+	/* Initialize works */
+	INIT_WORK(&cd->startup_work, cyttsp4_startup_work_function);
+	INIT_WORK(&cd->watchdog_work, cyttsp4_watchdog_work);
+
+	/* Initialize IRQ */
+	cd->irq = gpio_to_irq(cd->cpdata->irq_gpio);
+	if (cd->irq < 0) {
+		rc = -EINVAL;
+		goto error_free_xfer;
+	}
+
+	dev_set_drvdata(dev, cd);
+
+	/* Call platform init function */
+	if (cd->cpdata->init) {
+		dev_dbg(cd->dev, "%s: Init HW\n", __func__);
+		rc = cd->cpdata->init(cd->cpdata, 1, cd->dev);
+	} else {
+		dev_dbg(cd->dev, "%s: No HW INIT function\n", __func__);
+		rc = 0;
+	}
+	if (rc < 0)
+		dev_err(cd->dev, "%s: HW Init fail r=%d\n", __func__, rc);
+
+	dev_dbg(dev, "%s: initialize threaded irq=%d\n", __func__, cd->irq);
+	if (cd->cpdata->level_irq_udelay > 0)
+		/* use level triggered interrupts */
+		irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT;
+	else
+		/* use edge triggered interrupts */
+		irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+
+	rc = request_threaded_irq(cd->irq, NULL, cyttsp4_irq, irq_flags,
+		dev_name(dev), cd);
+	if (rc < 0) {
+		dev_err(dev, "%s: Error, could not request irq\n", __func__);
+		goto error_request_irq;
+	}
+
+	/* Setup watchdog timer */
+	setup_timer(&cd->watchdog_timer, cyttsp4_watchdog_timer,
+		(unsigned long)cd);
+
+	/*
+	 * call startup directly to ensure that the device
+	 * is tested before leaving the probe
+	 */
+	rc = cyttsp4_startup(cd);
+
+	/* Do not fail probe if startup fails but the device is detected */
+	if (rc < 0 && cd->mode == CY_MODE_UNKNOWN) {
+		dev_err(cd->dev, "%s: Fail initial startup r=%d\n",
+			__func__, rc);
+		goto error_startup;
+	}
+
+	rc = cyttsp4_mt_probe(cd);
+	if (rc < 0) {
+		dev_err(dev, "%s: Error, fail mt probe\n", __func__);
+		goto error_startup;
+	}
+
+	pm_runtime_enable(dev);
+
+	return cd;
+
+error_startup:
+	cancel_work_sync(&cd->startup_work);
+	cyttsp4_stop_wd_timer(cd);
+	pm_runtime_disable(dev);
+	cyttsp4_free_si_ptrs(cd);
+	free_irq(cd->irq, cd);
+error_request_irq:
+	if (cd->cpdata->init)
+		cd->cpdata->init(cd->cpdata, 0, dev);
+error_free_xfer:
+	kfree(cd->xfer_buf);
+error_free_cd:
+	kfree(cd);
+error_alloc_data:
+error_no_pdata:
+	dev_err(dev, "%s failed.\n", __func__);
+	return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(cyttsp4_probe);
+
+static void cyttsp4_mt_release(struct cyttsp4_mt_data *md)
+{
+	input_unregister_device(md->input);
+	input_set_drvdata(md->input, NULL);
+}
+
+int cyttsp4_remove(struct cyttsp4 *cd)
+{
+	struct device *dev = cd->dev;
+
+	cyttsp4_mt_release(&cd->md);
+
+	/*
+	 * Suspend the device before freeing the startup_work and stopping
+	 * the watchdog since sleep function restarts watchdog on failure
+	 */
+	pm_runtime_suspend(dev);
+	pm_runtime_disable(dev);
+
+	cancel_work_sync(&cd->startup_work);
+
+	cyttsp4_stop_wd_timer(cd);
+
+	free_irq(cd->irq, cd);
+	if (cd->cpdata->init)
+		cd->cpdata->init(cd->cpdata, 0, dev);
+	cyttsp4_free_si_ptrs(cd);
+	kfree(cd);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen core driver");
+MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/cyttsp4_core.h b/drivers/input/touchscreen/cyttsp4_core.h
new file mode 100644
index 0000000..8e0d4d4
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_core.h
@@ -0,0 +1,472 @@
+/*
+ * cyttsp4_core.h
+ * Cypress TrueTouch(TM) Standard Product V4 Core driver module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#ifndef _LINUX_CYTTSP4_CORE_H
+#define _LINUX_CYTTSP4_CORE_H
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/platform_data/cyttsp4.h>
+
+#define CY_REG_BASE			0x00
+
+#define CY_POST_CODEL_WDG_RST		0x01
+#define CY_POST_CODEL_CFG_DATA_CRC_FAIL	0x02
+#define CY_POST_CODEL_PANEL_TEST_FAIL	0x04
+
+#define CY_NUM_BTN_PER_REG		4
+
+/* touch record system information offset masks and shifts */
+#define CY_BYTE_OFS_MASK		0x1F
+#define CY_BOFS_MASK			0xE0
+#define CY_BOFS_SHIFT			5
+
+#define CY_TMA1036_TCH_REC_SIZE		6
+#define CY_TMA4XX_TCH_REC_SIZE		9
+#define CY_TMA1036_MAX_TCH		0x0E
+#define CY_TMA4XX_MAX_TCH		0x1E
+
+#define CY_NORMAL_ORIGIN		0	/* upper, left corner */
+#define CY_INVERT_ORIGIN		1	/* lower, right corner */
+
+/* helpers */
+#define GET_NUM_TOUCHES(x)		((x) & 0x1F)
+#define IS_LARGE_AREA(x)		((x) & 0x20)
+#define IS_BAD_PKT(x)			((x) & 0x20)
+#define IS_BOOTLOADER(hst_mode, reset_detect)	\
+		((hst_mode) & 0x01 || (reset_detect) != 0)
+#define IS_TMO(t)			((t) == 0)
+
+
+enum cyttsp_cmd_bits {
+	CY_CMD_COMPLETE = (1 << 6),
+};
+
+/* Timeout in ms. */
+#define CY_WATCHDOG_TIMEOUT		1000
+
+#define CY_MAX_PRINT_SIZE		512
+#ifdef VERBOSE_DEBUG
+#define CY_MAX_PRBUF_SIZE		PIPE_BUF
+#define CY_PR_TRUNCATED			" truncated..."
+#endif
+
+enum cyttsp4_ic_grpnum {
+	CY_IC_GRPNUM_RESERVED,
+	CY_IC_GRPNUM_CMD_REGS,
+	CY_IC_GRPNUM_TCH_REP,
+	CY_IC_GRPNUM_DATA_REC,
+	CY_IC_GRPNUM_TEST_REC,
+	CY_IC_GRPNUM_PCFG_REC,
+	CY_IC_GRPNUM_TCH_PARM_VAL,
+	CY_IC_GRPNUM_TCH_PARM_SIZE,
+	CY_IC_GRPNUM_RESERVED1,
+	CY_IC_GRPNUM_RESERVED2,
+	CY_IC_GRPNUM_OPCFG_REC,
+	CY_IC_GRPNUM_DDATA_REC,
+	CY_IC_GRPNUM_MDATA_REC,
+	CY_IC_GRPNUM_TEST_REGS,
+	CY_IC_GRPNUM_BTN_KEYS,
+	CY_IC_GRPNUM_TTHE_REGS,
+	CY_IC_GRPNUM_NUM
+};
+
+enum cyttsp4_int_state {
+	CY_INT_NONE,
+	CY_INT_IGNORE      = (1 << 0),
+	CY_INT_MODE_CHANGE = (1 << 1),
+	CY_INT_EXEC_CMD    = (1 << 2),
+	CY_INT_AWAKE       = (1 << 3),
+};
+
+enum cyttsp4_mode {
+	CY_MODE_UNKNOWN,
+	CY_MODE_BOOTLOADER   = (1 << 1),
+	CY_MODE_OPERATIONAL  = (1 << 2),
+	CY_MODE_SYSINFO      = (1 << 3),
+	CY_MODE_CAT          = (1 << 4),
+	CY_MODE_STARTUP      = (1 << 5),
+	CY_MODE_LOADER       = (1 << 6),
+	CY_MODE_CHANGE_MODE  = (1 << 7),
+	CY_MODE_CHANGED      = (1 << 8),
+	CY_MODE_CMD_COMPLETE = (1 << 9),
+};
+
+enum cyttsp4_sleep_state {
+	SS_SLEEP_OFF,
+	SS_SLEEP_ON,
+	SS_SLEEPING,
+	SS_WAKING,
+};
+
+enum cyttsp4_startup_state {
+	STARTUP_NONE,
+	STARTUP_QUEUED,
+	STARTUP_RUNNING,
+};
+
+#define CY_NUM_REVCTRL			8
+struct cyttsp4_cydata {
+	u8 ttpidh;
+	u8 ttpidl;
+	u8 fw_ver_major;
+	u8 fw_ver_minor;
+	u8 revctrl[CY_NUM_REVCTRL];
+	u8 blver_major;
+	u8 blver_minor;
+	u8 jtag_si_id3;
+	u8 jtag_si_id2;
+	u8 jtag_si_id1;
+	u8 jtag_si_id0;
+	u8 mfgid_sz;
+	u8 cyito_idh;
+	u8 cyito_idl;
+	u8 cyito_verh;
+	u8 cyito_verl;
+	u8 ttsp_ver_major;
+	u8 ttsp_ver_minor;
+	u8 device_info;
+	u8 mfg_id[];
+} __packed;
+
+struct cyttsp4_test {
+	u8 post_codeh;
+	u8 post_codel;
+} __packed;
+
+struct cyttsp4_pcfg {
+	u8 electrodes_x;
+	u8 electrodes_y;
+	u8 len_xh;
+	u8 len_xl;
+	u8 len_yh;
+	u8 len_yl;
+	u8 res_xh;
+	u8 res_xl;
+	u8 res_yh;
+	u8 res_yl;
+	u8 max_zh;
+	u8 max_zl;
+	u8 panel_info0;
+} __packed;
+
+struct cyttsp4_tch_rec_params {
+	u8 loc;
+	u8 size;
+} __packed;
+
+#define CY_NUM_TCH_FIELDS		7
+#define CY_NUM_EXT_TCH_FIELDS		3
+struct cyttsp4_opcfg {
+	u8 cmd_ofs;
+	u8 rep_ofs;
+	u8 rep_szh;
+	u8 rep_szl;
+	u8 num_btns;
+	u8 tt_stat_ofs;
+	u8 obj_cfg0;
+	u8 max_tchs;
+	u8 tch_rec_size;
+	struct cyttsp4_tch_rec_params tch_rec_old[CY_NUM_TCH_FIELDS];
+	u8 btn_rec_size;	/* btn record size (in bytes) */
+	u8 btn_diff_ofs;	/* btn data loc, diff counts  */
+	u8 btn_diff_size;	/* btn size of diff counts (in bits) */
+	struct cyttsp4_tch_rec_params tch_rec_new[CY_NUM_EXT_TCH_FIELDS];
+} __packed;
+
+struct cyttsp4_sysinfo_ptr {
+	struct cyttsp4_cydata *cydata;
+	struct cyttsp4_test *test;
+	struct cyttsp4_pcfg *pcfg;
+	struct cyttsp4_opcfg *opcfg;
+	struct cyttsp4_ddata *ddata;
+	struct cyttsp4_mdata *mdata;
+} __packed;
+
+struct cyttsp4_sysinfo_data {
+	u8 hst_mode;
+	u8 reserved;
+	u8 map_szh;
+	u8 map_szl;
+	u8 cydata_ofsh;
+	u8 cydata_ofsl;
+	u8 test_ofsh;
+	u8 test_ofsl;
+	u8 pcfg_ofsh;
+	u8 pcfg_ofsl;
+	u8 opcfg_ofsh;
+	u8 opcfg_ofsl;
+	u8 ddata_ofsh;
+	u8 ddata_ofsl;
+	u8 mdata_ofsh;
+	u8 mdata_ofsl;
+} __packed;
+
+enum cyttsp4_tch_abs {	/* for ordering within the extracted touch data array */
+	CY_TCH_X,	/* X */
+	CY_TCH_Y,	/* Y */
+	CY_TCH_P,	/* P (Z) */
+	CY_TCH_T,	/* TOUCH ID */
+	CY_TCH_E,	/* EVENT ID */
+	CY_TCH_O,	/* OBJECT ID */
+	CY_TCH_W,	/* SIZE */
+	CY_TCH_MAJ,	/* TOUCH_MAJOR */
+	CY_TCH_MIN,	/* TOUCH_MINOR */
+	CY_TCH_OR,	/* ORIENTATION */
+	CY_TCH_NUM_ABS
+};
+
+static const char * const cyttsp4_tch_abs_string[] = {
+	[CY_TCH_X]	= "X",
+	[CY_TCH_Y]	= "Y",
+	[CY_TCH_P]	= "P",
+	[CY_TCH_T]	= "T",
+	[CY_TCH_E]	= "E",
+	[CY_TCH_O]	= "O",
+	[CY_TCH_W]	= "W",
+	[CY_TCH_MAJ]	= "MAJ",
+	[CY_TCH_MIN]	= "MIN",
+	[CY_TCH_OR]	= "OR",
+	[CY_TCH_NUM_ABS] = "INVALID"
+};
+
+struct cyttsp4_touch {
+	int abs[CY_TCH_NUM_ABS];
+};
+
+struct cyttsp4_tch_abs_params {
+	size_t ofs;	/* abs byte offset */
+	size_t size;	/* size in bits */
+	size_t max;	/* max value */
+	size_t bofs;	/* bit offset */
+};
+
+struct cyttsp4_sysinfo_ofs {
+	size_t chip_type;
+	size_t cmd_ofs;
+	size_t rep_ofs;
+	size_t rep_sz;
+	size_t num_btns;
+	size_t num_btn_regs;	/* ceil(num_btns/4) */
+	size_t tt_stat_ofs;
+	size_t tch_rec_size;
+	size_t obj_cfg0;
+	size_t max_tchs;
+	size_t mode_size;
+	size_t data_size;
+	size_t map_sz;
+	size_t max_x;
+	size_t x_origin;	/* left or right corner */
+	size_t max_y;
+	size_t y_origin;	/* upper or lower corner */
+	size_t max_p;
+	size_t cydata_ofs;
+	size_t test_ofs;
+	size_t pcfg_ofs;
+	size_t opcfg_ofs;
+	size_t ddata_ofs;
+	size_t mdata_ofs;
+	size_t cydata_size;
+	size_t test_size;
+	size_t pcfg_size;
+	size_t opcfg_size;
+	size_t ddata_size;
+	size_t mdata_size;
+	size_t btn_keys_size;
+	struct cyttsp4_tch_abs_params tch_abs[CY_TCH_NUM_ABS];
+	size_t btn_rec_size; /* btn record size (in bytes) */
+	size_t btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */
+	size_t btn_diff_size;/* btn size of diff counts (in bits) */
+};
+
+enum cyttsp4_btn_state {
+	CY_BTN_RELEASED,
+	CY_BTN_PRESSED,
+	CY_BTN_NUM_STATE
+};
+
+struct cyttsp4_btn {
+	bool enabled;
+	int state;	/* CY_BTN_PRESSED, CY_BTN_RELEASED */
+	int key_code;
+};
+
+struct cyttsp4_sysinfo {
+	bool ready;
+	struct cyttsp4_sysinfo_data si_data;
+	struct cyttsp4_sysinfo_ptr si_ptrs;
+	struct cyttsp4_sysinfo_ofs si_ofs;
+	struct cyttsp4_btn *btn;	/* button states */
+	u8 *btn_rec_data;		/* button diff count data */
+	u8 *xy_mode;			/* operational mode and status regs */
+	u8 *xy_data;			/* operational touch regs */
+};
+
+struct cyttsp4_mt_data {
+	struct cyttsp4_mt_platform_data *pdata;
+	struct cyttsp4_sysinfo *si;
+	struct input_dev *input;
+	struct mutex report_lock;
+	bool is_suspended;
+	char phys[NAME_MAX];
+	int num_prv_tch;
+};
+
+struct cyttsp4 {
+	struct device *dev;
+	struct mutex system_lock;
+	struct mutex adap_lock;
+	enum cyttsp4_mode mode;
+	enum cyttsp4_sleep_state sleep_state;
+	enum cyttsp4_startup_state startup_state;
+	int int_status;
+	wait_queue_head_t wait_q;
+	int irq;
+	struct work_struct startup_work;
+	struct work_struct watchdog_work;
+	struct timer_list watchdog_timer;
+	struct cyttsp4_sysinfo sysinfo;
+	void *exclusive_dev;
+	int exclusive_waits;
+	atomic_t ignore_irq;
+	bool invalid_touch_app;
+	struct cyttsp4_mt_data md;
+	struct cyttsp4_platform_data *pdata;
+	struct cyttsp4_core_platform_data *cpdata;
+	const struct cyttsp4_bus_ops *bus_ops;
+	u8 *xfer_buf;
+#ifdef VERBOSE_DEBUG
+	u8 pr_buf[CY_MAX_PRBUF_SIZE];
+#endif
+};
+
+struct cyttsp4_bus_ops {
+	u16 bustype;
+	int (*write)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length,
+			const void *values);
+	int (*read)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length,
+			void *values);
+};
+
+enum cyttsp4_hst_mode_bits {
+	CY_HST_TOGGLE      = (1 << 7),
+	CY_HST_MODE_CHANGE = (1 << 3),
+	CY_HST_MODE        = (7 << 4),
+	CY_HST_OPERATE     = (0 << 4),
+	CY_HST_SYSINFO     = (1 << 4),
+	CY_HST_CAT         = (2 << 4),
+	CY_HST_LOWPOW      = (1 << 2),
+	CY_HST_SLEEP       = (1 << 1),
+	CY_HST_RESET       = (1 << 0),
+};
+
+/* abs settings */
+#define CY_IGNORE_VALUE			0xFFFF
+
+/* abs signal capabilities offsets in the frameworks array */
+enum cyttsp4_sig_caps {
+	CY_SIGNAL_OST,
+	CY_MIN_OST,
+	CY_MAX_OST,
+	CY_FUZZ_OST,
+	CY_FLAT_OST,
+	CY_NUM_ABS_SET	/* number of signal capability fields */
+};
+
+/* abs axis signal offsets in the framworks array  */
+enum cyttsp4_sig_ost {
+	CY_ABS_X_OST,
+	CY_ABS_Y_OST,
+	CY_ABS_P_OST,
+	CY_ABS_W_OST,
+	CY_ABS_ID_OST,
+	CY_ABS_MAJ_OST,
+	CY_ABS_MIN_OST,
+	CY_ABS_OR_OST,
+	CY_NUM_ABS_OST	/* number of abs signals */
+};
+
+enum cyttsp4_flags {
+	CY_FLAG_NONE = 0x00,
+	CY_FLAG_HOVER = 0x04,
+	CY_FLAG_FLIP = 0x08,
+	CY_FLAG_INV_X = 0x10,
+	CY_FLAG_INV_Y = 0x20,
+	CY_FLAG_VKEYS = 0x40,
+};
+
+enum cyttsp4_object_id {
+	CY_OBJ_STANDARD_FINGER,
+	CY_OBJ_LARGE_OBJECT,
+	CY_OBJ_STYLUS,
+	CY_OBJ_HOVER,
+};
+
+enum cyttsp4_event_id {
+	CY_EV_NO_EVENT,
+	CY_EV_TOUCHDOWN,
+	CY_EV_MOVE,		/* significant displacement (> act dist) */
+	CY_EV_LIFTOFF,		/* record reports last position */
+};
+
+/* x-axis resolution of panel in pixels */
+#define CY_PCFG_RESOLUTION_X_MASK	0x7F
+
+/* y-axis resolution of panel in pixels */
+#define CY_PCFG_RESOLUTION_Y_MASK	0x7F
+
+/* x-axis, 0:origin is on left side of panel, 1: right */
+#define CY_PCFG_ORIGIN_X_MASK		0x80
+
+/* y-axis, 0:origin is on top side of panel, 1: bottom */
+#define CY_PCFG_ORIGIN_Y_MASK		0x80
+
+static inline int cyttsp4_adap_read(struct cyttsp4 *ts, u16 addr, int size,
+		void *buf)
+{
+	return ts->bus_ops->read(ts->dev, ts->xfer_buf, addr, size, buf);
+}
+
+static inline int cyttsp4_adap_write(struct cyttsp4 *ts, u16 addr, int size,
+		const void *buf)
+{
+	return ts->bus_ops->write(ts->dev, ts->xfer_buf, addr, size, buf);
+}
+
+extern struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops,
+		struct device *dev, u16 irq, size_t xfer_buf_size);
+extern int cyttsp4_remove(struct cyttsp4 *ts);
+int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
+		u8 length, const void *values);
+int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
+		u8 length, void *values);
+extern const struct dev_pm_ops cyttsp4_pm_ops;
+
+#endif /* _LINUX_CYTTSP4_CORE_H */
diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c
new file mode 100644
index 0000000..564e490
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_i2c.c
@@ -0,0 +1,85 @@
+/*
+ * cyttsp_i2c.c
+ * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver.
+ * For use with Cypress  Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ * Copyright (C) 2013 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include "cyttsp4_core.h"
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+
+#define CYTTSP4_I2C_DATA_SIZE	(3 * 256)
+
+static const struct cyttsp4_bus_ops cyttsp4_i2c_bus_ops = {
+	.bustype	= BUS_I2C,
+	.write		= cyttsp_i2c_write_block_data,
+	.read           = cyttsp_i2c_read_block_data,
+};
+
+static int cyttsp4_i2c_probe(struct i2c_client *client,
+				      const struct i2c_device_id *id)
+{
+	struct cyttsp4 *ts;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "I2C functionality not Supported\n");
+		return -EIO;
+	}
+
+	ts = cyttsp4_probe(&cyttsp4_i2c_bus_ops, &client->dev, client->irq,
+			  CYTTSP4_I2C_DATA_SIZE);
+
+	return PTR_ERR_OR_ZERO(ts);
+}
+
+static int cyttsp4_i2c_remove(struct i2c_client *client)
+{
+	struct cyttsp4 *ts = i2c_get_clientdata(client);
+
+	cyttsp4_remove(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id cyttsp4_i2c_id[] = {
+	{ CYTTSP4_I2C_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, cyttsp4_i2c_id);
+
+static struct i2c_driver cyttsp4_i2c_driver = {
+	.driver = {
+		.name	= CYTTSP4_I2C_NAME,
+		.pm	= &cyttsp4_pm_ops,
+	},
+	.probe		= cyttsp4_i2c_probe,
+	.remove		= cyttsp4_i2c_remove,
+	.id_table	= cyttsp4_i2c_id,
+};
+
+module_i2c_driver(cyttsp4_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver");
+MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/cyttsp4_spi.c b/drivers/input/touchscreen/cyttsp4_spi.c
new file mode 100644
index 0000000..ec5f7c7
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_spi.c
@@ -0,0 +1,199 @@
+/*
+ * Source for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ * Copyright (C) 2013 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include "cyttsp4_core.h"
+
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+
+#define CY_SPI_WR_OP		0x00 /* r/~w */
+#define CY_SPI_RD_OP		0x01
+#define CY_SPI_BITS_PER_WORD	8
+#define CY_SPI_A8_BIT		0x02
+#define CY_SPI_WR_HEADER_BYTES	2
+#define CY_SPI_RD_HEADER_BYTES	1
+#define CY_SPI_CMD_BYTES	2
+#define CY_SPI_SYNC_BYTE	0
+#define CY_SPI_SYNC_ACK		0x62 /* from TRM *A protocol */
+#define CY_SPI_DATA_SIZE	(2 * 256)
+
+#define CY_SPI_DATA_BUF_SIZE	(CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE)
+
+static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf,
+			   u8 op, u16 reg, u8 *buf, int length)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct spi_message msg;
+	struct spi_transfer xfer[2];
+	u8 *wr_buf = &xfer_buf[0];
+	u8 rd_buf[CY_SPI_CMD_BYTES];
+	int retval;
+	int i;
+
+	if (length > CY_SPI_DATA_SIZE) {
+		dev_err(dev, "%s: length %d is too big.\n",
+			__func__, length);
+		return -EINVAL;
+	}
+
+	memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE);
+	memset(rd_buf, 0, CY_SPI_CMD_BYTES);
+
+	wr_buf[0] = op + (((reg >> 8) & 0x1) ? CY_SPI_A8_BIT : 0);
+	if (op == CY_SPI_WR_OP) {
+		wr_buf[1] = reg & 0xFF;
+		if (length > 0)
+			memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length);
+	}
+
+	memset(xfer, 0, sizeof(xfer));
+	spi_message_init(&msg);
+
+	/*
+	  We set both TX and RX buffers because Cypress TTSP
+	  requires full duplex operation.
+	*/
+	xfer[0].tx_buf = wr_buf;
+	xfer[0].rx_buf = rd_buf;
+	switch (op) {
+	case CY_SPI_WR_OP:
+		xfer[0].len = length + CY_SPI_CMD_BYTES;
+		spi_message_add_tail(&xfer[0], &msg);
+		break;
+
+	case CY_SPI_RD_OP:
+		xfer[0].len = CY_SPI_RD_HEADER_BYTES;
+		spi_message_add_tail(&xfer[0], &msg);
+
+		xfer[1].rx_buf = buf;
+		xfer[1].len = length;
+		spi_message_add_tail(&xfer[1], &msg);
+		break;
+
+	default:
+		dev_err(dev, "%s: bad operation code=%d\n", __func__, op);
+		return -EINVAL;
+	}
+
+	retval = spi_sync(spi, &msg);
+	if (retval < 0) {
+		dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n",
+			__func__, retval, xfer[1].len, op);
+
+		/*
+		 * do not return here since was a bad ACK sequence
+		 * let the following ACK check handle any errors and
+		 * allow silent retries
+		 */
+	}
+
+	if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK) {
+		dev_dbg(dev, "%s: operation %d failed\n", __func__, op);
+
+		for (i = 0; i < CY_SPI_CMD_BYTES; i++)
+			dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n",
+				__func__, i, rd_buf[i]);
+		for (i = 0; i < length; i++)
+			dev_dbg(dev, "%s: test buf[%d]:0x%02x\n",
+				__func__, i, buf[i]);
+
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf,
+				      u16 addr, u8 length, void *data)
+{
+	int rc;
+
+	rc = cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, NULL, 0);
+	if (rc)
+		return rc;
+	else
+		return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, addr, data,
+				length);
+}
+
+static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf,
+				       u16 addr, u8 length, const void *data)
+{
+	return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, (void *)data,
+			length);
+}
+
+static const struct cyttsp4_bus_ops cyttsp_spi_bus_ops = {
+	.bustype	= BUS_SPI,
+	.write		= cyttsp_spi_write_block_data,
+	.read		= cyttsp_spi_read_block_data,
+};
+
+static int cyttsp4_spi_probe(struct spi_device *spi)
+{
+	struct cyttsp4 *ts;
+	int error;
+
+	/* Set up SPI*/
+	spi->bits_per_word = CY_SPI_BITS_PER_WORD;
+	spi->mode = SPI_MODE_0;
+	error = spi_setup(spi);
+	if (error < 0) {
+		dev_err(&spi->dev, "%s: SPI setup error %d\n",
+			__func__, error);
+		return error;
+	}
+
+	ts = cyttsp4_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq,
+			  CY_SPI_DATA_BUF_SIZE);
+
+	return PTR_ERR_OR_ZERO(ts);
+}
+
+static int cyttsp4_spi_remove(struct spi_device *spi)
+{
+	struct cyttsp4 *ts = spi_get_drvdata(spi);
+	cyttsp4_remove(ts);
+
+	return 0;
+}
+
+static struct spi_driver cyttsp4_spi_driver = {
+	.driver = {
+		.name	= CYTTSP4_SPI_NAME,
+		.pm	= &cyttsp4_pm_ops,
+	},
+	.probe  = cyttsp4_spi_probe,
+	.remove = cyttsp4_spi_remove,
+};
+
+module_spi_driver(cyttsp4_spi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
+MODULE_AUTHOR("Cypress");
+MODULE_ALIAS("spi:cyttsp4");
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c
new file mode 100644
index 0000000..5b74e8b
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_core.c
@@ -0,0 +1,638 @@
+/*
+ * Core Source for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include "cyttsp_core.h"
+
+/* Bootloader number of command keys */
+#define CY_NUM_BL_KEYS		8
+
+/* helpers */
+#define GET_NUM_TOUCHES(x)		((x) & 0x0F)
+#define IS_LARGE_AREA(x)		(((x) & 0x10) >> 4)
+#define IS_BAD_PKT(x)			((x) & 0x20)
+#define IS_VALID_APP(x)			((x) & 0x01)
+#define IS_OPERATIONAL_ERR(x)		((x) & 0x3F)
+#define GET_HSTMODE(reg)		(((reg) & 0x70) >> 4)
+#define GET_BOOTLOADERMODE(reg)		(((reg) & 0x10) >> 4)
+
+#define CY_REG_BASE			0x00
+#define CY_REG_ACT_DIST			0x1E
+#define CY_REG_ACT_INTRVL		0x1D
+#define CY_REG_TCH_TMOUT		(CY_REG_ACT_INTRVL + 1)
+#define CY_REG_LP_INTRVL		(CY_REG_TCH_TMOUT + 1)
+#define CY_MAXZ				255
+#define CY_DELAY_DFLT			20 /* ms */
+#define CY_DELAY_MAX			500
+#define CY_ACT_DIST_DFLT		0xF8
+#define CY_HNDSHK_BIT			0x80
+/* device mode bits */
+#define CY_OPERATE_MODE			0x00
+#define CY_SYSINFO_MODE			0x10
+/* power mode select bits */
+#define CY_SOFT_RESET_MODE		0x01 /* return to Bootloader mode */
+#define CY_DEEP_SLEEP_MODE		0x02
+#define CY_LOW_POWER_MODE		0x04
+
+/* Slots management */
+#define CY_MAX_FINGER			4
+#define CY_MAX_ID			16
+
+static const u8 bl_command[] = {
+	0x00,			/* file offset */
+	0xFF,			/* command */
+	0xA5,			/* exit bootloader command */
+	0, 1, 2, 3, 4, 5, 6, 7	/* default keys */
+};
+
+static int ttsp_read_block_data(struct cyttsp *ts, u8 command,
+				u8 length, void *buf)
+{
+	int error;
+	int tries;
+
+	for (tries = 0; tries < CY_NUM_RETRY; tries++) {
+		error = ts->bus_ops->read(ts->dev, ts->xfer_buf, command,
+				length, buf);
+		if (!error)
+			return 0;
+
+		msleep(CY_DELAY_DFLT);
+	}
+
+	return -EIO;
+}
+
+static int ttsp_write_block_data(struct cyttsp *ts, u8 command,
+				 u8 length, void *buf)
+{
+	int error;
+	int tries;
+
+	for (tries = 0; tries < CY_NUM_RETRY; tries++) {
+		error = ts->bus_ops->write(ts->dev, ts->xfer_buf, command,
+				length, buf);
+		if (!error)
+			return 0;
+
+		msleep(CY_DELAY_DFLT);
+	}
+
+	return -EIO;
+}
+
+static int ttsp_send_command(struct cyttsp *ts, u8 cmd)
+{
+	return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd);
+}
+
+static int cyttsp_handshake(struct cyttsp *ts)
+{
+	if (ts->pdata->use_hndshk)
+		return ttsp_send_command(ts,
+				ts->xy_data.hst_mode ^ CY_HNDSHK_BIT);
+
+	return 0;
+}
+
+static int cyttsp_load_bl_regs(struct cyttsp *ts)
+{
+	memset(&ts->bl_data, 0, sizeof(ts->bl_data));
+	ts->bl_data.bl_status = 0x10;
+
+	return ttsp_read_block_data(ts, CY_REG_BASE,
+				    sizeof(ts->bl_data), &ts->bl_data);
+}
+
+static int cyttsp_exit_bl_mode(struct cyttsp *ts)
+{
+	int error;
+	u8 bl_cmd[sizeof(bl_command)];
+
+	memcpy(bl_cmd, bl_command, sizeof(bl_command));
+	if (ts->pdata->bl_keys)
+		memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS],
+			ts->pdata->bl_keys, CY_NUM_BL_KEYS);
+
+	error = ttsp_write_block_data(ts, CY_REG_BASE,
+				      sizeof(bl_cmd), bl_cmd);
+	if (error)
+		return error;
+
+	/* wait for TTSP Device to complete the operation */
+	msleep(CY_DELAY_DFLT);
+
+	error = cyttsp_load_bl_regs(ts);
+	if (error)
+		return error;
+
+	if (GET_BOOTLOADERMODE(ts->bl_data.bl_status))
+		return -EIO;
+
+	return 0;
+}
+
+static int cyttsp_set_operational_mode(struct cyttsp *ts)
+{
+	int error;
+
+	error = ttsp_send_command(ts, CY_OPERATE_MODE);
+	if (error)
+		return error;
+
+	/* wait for TTSP Device to complete switch to Operational mode */
+	error = ttsp_read_block_data(ts, CY_REG_BASE,
+				     sizeof(ts->xy_data), &ts->xy_data);
+	if (error)
+		return error;
+
+	error = cyttsp_handshake(ts);
+	if (error)
+		return error;
+
+	return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0;
+}
+
+static int cyttsp_set_sysinfo_mode(struct cyttsp *ts)
+{
+	int error;
+
+	memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data));
+
+	/* switch to sysinfo mode */
+	error = ttsp_send_command(ts, CY_SYSINFO_MODE);
+	if (error)
+		return error;
+
+	/* read sysinfo registers */
+	msleep(CY_DELAY_DFLT);
+	error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data),
+				      &ts->sysinfo_data);
+	if (error)
+		return error;
+
+	error = cyttsp_handshake(ts);
+	if (error)
+		return error;
+
+	if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl)
+		return -EIO;
+
+	return 0;
+}
+
+static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
+{
+	int retval = 0;
+
+	if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT ||
+	    ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT ||
+	    ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) {
+
+		u8 intrvl_ray[] = {
+			ts->pdata->act_intrvl,
+			ts->pdata->tch_tmout,
+			ts->pdata->lp_intrvl
+		};
+
+		/* set intrvl registers */
+		retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL,
+					sizeof(intrvl_ray), intrvl_ray);
+		msleep(CY_DELAY_DFLT);
+	}
+
+	return retval;
+}
+
+static int cyttsp_soft_reset(struct cyttsp *ts)
+{
+	unsigned long timeout;
+	int retval;
+
+	/* wait for interrupt to set ready completion */
+	reinit_completion(&ts->bl_ready);
+	ts->state = CY_BL_STATE;
+
+	enable_irq(ts->irq);
+
+	retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE);
+	if (retval)
+		goto out;
+
+	timeout = wait_for_completion_timeout(&ts->bl_ready,
+			msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX));
+	retval = timeout ? 0 : -EIO;
+
+out:
+	ts->state = CY_IDLE_STATE;
+	disable_irq(ts->irq);
+	return retval;
+}
+
+static int cyttsp_act_dist_setup(struct cyttsp *ts)
+{
+	u8 act_dist_setup = ts->pdata->act_dist;
+
+	/* Init gesture; active distance setup */
+	return ttsp_write_block_data(ts, CY_REG_ACT_DIST,
+				sizeof(act_dist_setup), &act_dist_setup);
+}
+
+static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids)
+{
+	ids[0] = xy_data->touch12_id >> 4;
+	ids[1] = xy_data->touch12_id & 0xF;
+	ids[2] = xy_data->touch34_id >> 4;
+	ids[3] = xy_data->touch34_id & 0xF;
+}
+
+static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data,
+					       int idx)
+{
+	switch (idx) {
+	case 0:
+		return &xy_data->tch1;
+	case 1:
+		return &xy_data->tch2;
+	case 2:
+		return &xy_data->tch3;
+	case 3:
+		return &xy_data->tch4;
+	default:
+		return NULL;
+	}
+}
+
+static void cyttsp_report_tchdata(struct cyttsp *ts)
+{
+	struct cyttsp_xydata *xy_data = &ts->xy_data;
+	struct input_dev *input = ts->input;
+	int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat);
+	const struct cyttsp_tch *tch;
+	int ids[CY_MAX_ID];
+	int i;
+	DECLARE_BITMAP(used, CY_MAX_ID);
+
+	if (IS_LARGE_AREA(xy_data->tt_stat) == 1) {
+		/* terminate all active tracks */
+		num_tch = 0;
+		dev_dbg(ts->dev, "%s: Large area detected\n", __func__);
+	} else if (num_tch > CY_MAX_FINGER) {
+		/* terminate all active tracks */
+		num_tch = 0;
+		dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__);
+	} else if (IS_BAD_PKT(xy_data->tt_mode)) {
+		/* terminate all active tracks */
+		num_tch = 0;
+		dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__);
+	}
+
+	cyttsp_extract_track_ids(xy_data, ids);
+
+	bitmap_zero(used, CY_MAX_ID);
+
+	for (i = 0; i < num_tch; i++) {
+		tch = cyttsp_get_tch(xy_data, i);
+
+		input_mt_slot(input, ids[i]);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+		input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x));
+		input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y));
+		input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z);
+
+		__set_bit(ids[i], used);
+	}
+
+	for (i = 0; i < CY_MAX_ID; i++) {
+		if (test_bit(i, used))
+			continue;
+
+		input_mt_slot(input, i);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+	}
+
+	input_sync(input);
+}
+
+static irqreturn_t cyttsp_irq(int irq, void *handle)
+{
+	struct cyttsp *ts = handle;
+	int error;
+
+	if (unlikely(ts->state == CY_BL_STATE)) {
+		complete(&ts->bl_ready);
+		goto out;
+	}
+
+	/* Get touch data from CYTTSP device */
+	error = ttsp_read_block_data(ts, CY_REG_BASE,
+				 sizeof(struct cyttsp_xydata), &ts->xy_data);
+	if (error)
+		goto out;
+
+	/* provide flow control handshake */
+	error = cyttsp_handshake(ts);
+	if (error)
+		goto out;
+
+	if (unlikely(ts->state == CY_IDLE_STATE))
+		goto out;
+
+	if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) {
+		/*
+		 * TTSP device has reset back to bootloader mode.
+		 * Restore to operational mode.
+		 */
+		error = cyttsp_exit_bl_mode(ts);
+		if (error) {
+			dev_err(ts->dev,
+				"Could not return to operational mode, err: %d\n",
+				error);
+			ts->state = CY_IDLE_STATE;
+		}
+	} else {
+		cyttsp_report_tchdata(ts);
+	}
+
+out:
+	return IRQ_HANDLED;
+}
+
+static int cyttsp_power_on(struct cyttsp *ts)
+{
+	int error;
+
+	error = cyttsp_soft_reset(ts);
+	if (error)
+		return error;
+
+	error = cyttsp_load_bl_regs(ts);
+	if (error)
+		return error;
+
+	if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) &&
+	    IS_VALID_APP(ts->bl_data.bl_status)) {
+		error = cyttsp_exit_bl_mode(ts);
+		if (error)
+			return error;
+	}
+
+	if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE ||
+	    IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) {
+		return -ENODEV;
+	}
+
+	error = cyttsp_set_sysinfo_mode(ts);
+	if (error)
+		return error;
+
+	error = cyttsp_set_sysinfo_regs(ts);
+	if (error)
+		return error;
+
+	error = cyttsp_set_operational_mode(ts);
+	if (error)
+		return error;
+
+	/* init active distance */
+	error = cyttsp_act_dist_setup(ts);
+	if (error)
+		return error;
+
+	ts->state = CY_ACTIVE_STATE;
+
+	return 0;
+}
+
+static int cyttsp_enable(struct cyttsp *ts)
+{
+	int error;
+
+	/*
+	 * The device firmware can wake on an I2C or SPI memory slave
+	 * address match. So just reading a register is sufficient to
+	 * wake up the device. The first read attempt will fail but it
+	 * will wake it up making the second read attempt successful.
+	 */
+	error = ttsp_read_block_data(ts, CY_REG_BASE,
+				     sizeof(ts->xy_data), &ts->xy_data);
+	if (error)
+		return error;
+
+	if (GET_HSTMODE(ts->xy_data.hst_mode))
+		return -EIO;
+
+	enable_irq(ts->irq);
+
+	return 0;
+}
+
+static int cyttsp_disable(struct cyttsp *ts)
+{
+	int error;
+
+	error = ttsp_send_command(ts, CY_LOW_POWER_MODE);
+	if (error)
+		return error;
+
+	disable_irq(ts->irq);
+
+	return 0;
+}
+
+static int __maybe_unused cyttsp_suspend(struct device *dev)
+{
+	struct cyttsp *ts = dev_get_drvdata(dev);
+	int retval = 0;
+
+	mutex_lock(&ts->input->mutex);
+
+	if (ts->input->users) {
+		retval = cyttsp_disable(ts);
+		if (retval == 0)
+			ts->suspended = true;
+	}
+
+	mutex_unlock(&ts->input->mutex);
+
+	return retval;
+}
+
+static int __maybe_unused cyttsp_resume(struct device *dev)
+{
+	struct cyttsp *ts = dev_get_drvdata(dev);
+
+	mutex_lock(&ts->input->mutex);
+
+	if (ts->input->users)
+		cyttsp_enable(ts);
+
+	ts->suspended = false;
+
+	mutex_unlock(&ts->input->mutex);
+
+	return 0;
+}
+
+SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume);
+EXPORT_SYMBOL_GPL(cyttsp_pm_ops);
+
+static int cyttsp_open(struct input_dev *dev)
+{
+	struct cyttsp *ts = input_get_drvdata(dev);
+	int retval = 0;
+
+	if (!ts->suspended)
+		retval = cyttsp_enable(ts);
+
+	return retval;
+}
+
+static void cyttsp_close(struct input_dev *dev)
+{
+	struct cyttsp *ts = input_get_drvdata(dev);
+
+	if (!ts->suspended)
+		cyttsp_disable(ts);
+}
+
+struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
+			    struct device *dev, int irq, size_t xfer_buf_size)
+{
+	const struct cyttsp_platform_data *pdata = dev_get_platdata(dev);
+	struct cyttsp *ts;
+	struct input_dev *input_dev;
+	int error;
+
+	if (!pdata || !pdata->name || irq <= 0) {
+		error = -EINVAL;
+		goto err_out;
+	}
+
+	ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!ts || !input_dev) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	ts->dev = dev;
+	ts->input = input_dev;
+	ts->pdata = dev_get_platdata(dev);
+	ts->bus_ops = bus_ops;
+	ts->irq = irq;
+
+	init_completion(&ts->bl_ready);
+	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
+
+	if (pdata->init) {
+		error = pdata->init();
+		if (error) {
+			dev_err(ts->dev, "platform init failed, err: %d\n",
+				error);
+			goto err_free_mem;
+		}
+	}
+
+	input_dev->name = pdata->name;
+	input_dev->phys = ts->phys;
+	input_dev->id.bustype = bus_ops->bustype;
+	input_dev->dev.parent = ts->dev;
+
+	input_dev->open = cyttsp_open;
+	input_dev->close = cyttsp_close;
+
+	input_set_drvdata(input_dev, ts);
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+			     0, pdata->maxx, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+			     0, pdata->maxy, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+			     0, CY_MAXZ, 0, 0);
+
+	input_mt_init_slots(input_dev, CY_MAX_ID, 0);
+
+	error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
+				     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				     pdata->name, ts);
+	if (error) {
+		dev_err(ts->dev, "failed to request IRQ %d, err: %d\n",
+			ts->irq, error);
+		goto err_platform_exit;
+	}
+
+	disable_irq(ts->irq);
+
+	error = cyttsp_power_on(ts);
+	if (error)
+		goto err_free_irq;
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(ts->dev, "failed to register input device: %d\n",
+			error);
+		goto err_free_irq;
+	}
+
+	return ts;
+
+err_free_irq:
+	free_irq(ts->irq, ts);
+err_platform_exit:
+	if (pdata->exit)
+		pdata->exit();
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(ts);
+err_out:
+	return ERR_PTR(error);
+}
+EXPORT_SYMBOL_GPL(cyttsp_probe);
+
+void cyttsp_remove(struct cyttsp *ts)
+{
+	free_irq(ts->irq, ts);
+	input_unregister_device(ts->input);
+	if (ts->pdata->exit)
+		ts->pdata->exit();
+	kfree(ts);
+}
+EXPORT_SYMBOL_GPL(cyttsp_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core");
+MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h
new file mode 100644
index 0000000..0707411
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_core.h
@@ -0,0 +1,154 @@
+/*
+ * Header file for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+
+#ifndef __CYTTSP_CORE_H__
+#define __CYTTSP_CORE_H__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/input/cyttsp.h>
+
+#define CY_NUM_RETRY		16 /* max number of retries for read ops */
+
+struct cyttsp_tch {
+	__be16 x, y;
+	u8 z;
+} __packed;
+
+/* TrueTouch Standard Product Gen3 interface definition */
+struct cyttsp_xydata {
+	u8 hst_mode;
+	u8 tt_mode;
+	u8 tt_stat;
+	struct cyttsp_tch tch1;
+	u8 touch12_id;
+	struct cyttsp_tch tch2;
+	u8 gest_cnt;
+	u8 gest_id;
+	struct cyttsp_tch tch3;
+	u8 touch34_id;
+	struct cyttsp_tch tch4;
+	u8 tt_undef[3];
+	u8 act_dist;
+	u8 tt_reserved;
+} __packed;
+
+
+/* TTSP System Information interface definition */
+struct cyttsp_sysinfo_data {
+	u8 hst_mode;
+	u8 mfg_stat;
+	u8 mfg_cmd;
+	u8 cid[3];
+	u8 tt_undef1;
+	u8 uid[8];
+	u8 bl_verh;
+	u8 bl_verl;
+	u8 tts_verh;
+	u8 tts_verl;
+	u8 app_idh;
+	u8 app_idl;
+	u8 app_verh;
+	u8 app_verl;
+	u8 tt_undef[5];
+	u8 scn_typ;
+	u8 act_intrvl;
+	u8 tch_tmout;
+	u8 lp_intrvl;
+};
+
+/* TTSP Bootloader Register Map interface definition */
+#define CY_BL_CHKSUM_OK 0x01
+struct cyttsp_bootloader_data {
+	u8 bl_file;
+	u8 bl_status;
+	u8 bl_error;
+	u8 blver_hi;
+	u8 blver_lo;
+	u8 bld_blver_hi;
+	u8 bld_blver_lo;
+	u8 ttspver_hi;
+	u8 ttspver_lo;
+	u8 appid_hi;
+	u8 appid_lo;
+	u8 appver_hi;
+	u8 appver_lo;
+	u8 cid_0;
+	u8 cid_1;
+	u8 cid_2;
+};
+
+struct cyttsp;
+
+struct cyttsp_bus_ops {
+	u16 bustype;
+	int (*write)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length,
+			const void *values);
+	int (*read)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length,
+			void *values);
+};
+
+enum cyttsp_state {
+	CY_IDLE_STATE,
+	CY_ACTIVE_STATE,
+	CY_BL_STATE,
+};
+
+struct cyttsp {
+	struct device *dev;
+	int irq;
+	struct input_dev *input;
+	char phys[32];
+	const struct cyttsp_platform_data *pdata;
+	const struct cyttsp_bus_ops *bus_ops;
+	struct cyttsp_bootloader_data bl_data;
+	struct cyttsp_sysinfo_data sysinfo_data;
+	struct cyttsp_xydata xy_data;
+	struct completion bl_ready;
+	enum cyttsp_state state;
+	bool suspended;
+
+	u8 xfer_buf[] ____cacheline_aligned;
+};
+
+struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
+			    struct device *dev, int irq, size_t xfer_buf_size);
+void cyttsp_remove(struct cyttsp *ts);
+
+int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
+		u8 length, const void *values);
+int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
+		u8 length, void *values);
+extern const struct dev_pm_ops cyttsp_pm_ops;
+
+#endif /* __CYTTSP_CORE_H__ */
diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c
new file mode 100644
index 0000000..eee51b3
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_i2c.c
@@ -0,0 +1,88 @@
+/*
+ * cyttsp_i2c.c
+ * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include "cyttsp_core.h"
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+
+#define CY_I2C_DATA_SIZE	128
+
+static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = {
+	.bustype	= BUS_I2C,
+	.write		= cyttsp_i2c_write_block_data,
+	.read           = cyttsp_i2c_read_block_data,
+};
+
+static int cyttsp_i2c_probe(struct i2c_client *client,
+				      const struct i2c_device_id *id)
+{
+	struct cyttsp *ts;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "I2C functionality not Supported\n");
+		return -EIO;
+	}
+
+	ts = cyttsp_probe(&cyttsp_i2c_bus_ops, &client->dev, client->irq,
+			  CY_I2C_DATA_SIZE);
+
+	if (IS_ERR(ts))
+		return PTR_ERR(ts);
+
+	i2c_set_clientdata(client, ts);
+	return 0;
+}
+
+static int cyttsp_i2c_remove(struct i2c_client *client)
+{
+	struct cyttsp *ts = i2c_get_clientdata(client);
+
+	cyttsp_remove(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id cyttsp_i2c_id[] = {
+	{ CY_I2C_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id);
+
+static struct i2c_driver cyttsp_i2c_driver = {
+	.driver = {
+		.name	= CY_I2C_NAME,
+		.pm	= &cyttsp_pm_ops,
+	},
+	.probe		= cyttsp_i2c_probe,
+	.remove		= cyttsp_i2c_remove,
+	.id_table	= cyttsp_i2c_id,
+};
+
+module_i2c_driver(cyttsp_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver");
+MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/cyttsp_i2c_common.c b/drivers/input/touchscreen/cyttsp_i2c_common.c
new file mode 100644
index 0000000..ccefa56
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_i2c_common.c
@@ -0,0 +1,95 @@
+/*
+ * cyttsp_i2c_common.c
+ * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver.
+ * For use with Cypress Txx3xx and Txx4xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include "cyttsp4_core.h"
+
+int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf,
+				      u16 addr, u8 length, void *values)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 client_addr = client->addr | ((addr >> 8) & 0x1);
+	u8 addr_lo = addr & 0xFF;
+	struct i2c_msg msgs[] = {
+		{
+			.addr = client_addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &addr_lo,
+		},
+		{
+			.addr = client_addr,
+			.flags = I2C_M_RD,
+			.len = length,
+			.buf = values,
+		},
+	};
+	int retval;
+
+	retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (retval < 0)
+		return retval;
+
+	return retval != ARRAY_SIZE(msgs) ? -EIO : 0;
+}
+EXPORT_SYMBOL_GPL(cyttsp_i2c_read_block_data);
+
+int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf,
+				       u16 addr, u8 length, const void *values)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 client_addr = client->addr | ((addr >> 8) & 0x1);
+	u8 addr_lo = addr & 0xFF;
+	struct i2c_msg msgs[] = {
+		{
+			.addr = client_addr,
+			.flags = 0,
+			.len = length + 1,
+			.buf = xfer_buf,
+		},
+	};
+	int retval;
+
+	xfer_buf[0] = addr_lo;
+	memcpy(&xfer_buf[1], values, length);
+
+	retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (retval < 0)
+		return retval;
+
+	return retval != ARRAY_SIZE(msgs) ? -EIO : 0;
+}
+EXPORT_SYMBOL_GPL(cyttsp_i2c_write_block_data);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c
new file mode 100644
index 0000000..bbeeb24
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_spi.c
@@ -0,0 +1,196 @@
+/*
+ * Source for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
+ * Copyright (C) 2013 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include "cyttsp_core.h"
+
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+
+#define CY_SPI_WR_OP		0x00 /* r/~w */
+#define CY_SPI_RD_OP		0x01
+#define CY_SPI_CMD_BYTES	4
+#define CY_SPI_SYNC_BYTE	2
+#define CY_SPI_SYNC_ACK1	0x62 /* from protocol v.2 */
+#define CY_SPI_SYNC_ACK2	0x9D /* from protocol v.2 */
+#define CY_SPI_DATA_SIZE	128
+#define CY_SPI_DATA_BUF_SIZE	(CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE)
+#define CY_SPI_BITS_PER_WORD	8
+
+static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf,
+			   u8 op, u16 reg, u8 *buf, int length)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct spi_message msg;
+	struct spi_transfer xfer[2];
+	u8 *wr_buf = &xfer_buf[0];
+	u8 *rd_buf = &xfer_buf[CY_SPI_DATA_BUF_SIZE];
+	int retval;
+	int i;
+
+	if (length > CY_SPI_DATA_SIZE) {
+		dev_err(dev, "%s: length %d is too big.\n",
+			__func__, length);
+		return -EINVAL;
+	}
+
+	memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE);
+	memset(rd_buf, 0, CY_SPI_DATA_BUF_SIZE);
+
+	wr_buf[0] = 0x00; /* header byte 0 */
+	wr_buf[1] = 0xFF; /* header byte 1 */
+	wr_buf[2] = reg;  /* reg index */
+	wr_buf[3] = op;   /* r/~w */
+	if (op == CY_SPI_WR_OP)
+		memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length);
+
+	memset(xfer, 0, sizeof(xfer));
+	spi_message_init(&msg);
+
+	/*
+	  We set both TX and RX buffers because Cypress TTSP
+	  requires full duplex operation.
+	*/
+	xfer[0].tx_buf = wr_buf;
+	xfer[0].rx_buf = rd_buf;
+	switch (op) {
+	case CY_SPI_WR_OP:
+		xfer[0].len = length + CY_SPI_CMD_BYTES;
+		spi_message_add_tail(&xfer[0], &msg);
+		break;
+
+	case CY_SPI_RD_OP:
+		xfer[0].len = CY_SPI_CMD_BYTES;
+		spi_message_add_tail(&xfer[0], &msg);
+
+		xfer[1].rx_buf = buf;
+		xfer[1].len = length;
+		spi_message_add_tail(&xfer[1], &msg);
+		break;
+
+	default:
+		dev_err(dev, "%s: bad operation code=%d\n", __func__, op);
+		return -EINVAL;
+	}
+
+	retval = spi_sync(spi, &msg);
+	if (retval < 0) {
+		dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n",
+			__func__, retval, xfer[1].len, op);
+
+		/*
+		 * do not return here since was a bad ACK sequence
+		 * let the following ACK check handle any errors and
+		 * allow silent retries
+		 */
+	}
+
+	if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK1 ||
+	    rd_buf[CY_SPI_SYNC_BYTE + 1] != CY_SPI_SYNC_ACK2) {
+		dev_dbg(dev, "%s: operation %d failed\n", __func__, op);
+
+		for (i = 0; i < CY_SPI_CMD_BYTES; i++)
+			dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n",
+				__func__, i, rd_buf[i]);
+		for (i = 0; i < length; i++)
+			dev_dbg(dev, "%s: test buf[%d]:0x%02x\n",
+				__func__, i, buf[i]);
+
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf,
+				      u16 addr, u8 length, void *data)
+{
+	return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, addr, data,
+			length);
+}
+
+static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf,
+				       u16 addr, u8 length, const void *data)
+{
+	return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, (void *)data,
+			length);
+}
+
+static const struct cyttsp_bus_ops cyttsp_spi_bus_ops = {
+	.bustype	= BUS_SPI,
+	.write		= cyttsp_spi_write_block_data,
+	.read		= cyttsp_spi_read_block_data,
+};
+
+static int cyttsp_spi_probe(struct spi_device *spi)
+{
+	struct cyttsp *ts;
+	int error;
+
+	/* Set up SPI*/
+	spi->bits_per_word = CY_SPI_BITS_PER_WORD;
+	spi->mode = SPI_MODE_0;
+	error = spi_setup(spi);
+	if (error < 0) {
+		dev_err(&spi->dev, "%s: SPI setup error %d\n",
+			__func__, error);
+		return error;
+	}
+
+	ts = cyttsp_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq,
+			  CY_SPI_DATA_BUF_SIZE * 2);
+	if (IS_ERR(ts))
+		return PTR_ERR(ts);
+
+	spi_set_drvdata(spi, ts);
+
+	return 0;
+}
+
+static int cyttsp_spi_remove(struct spi_device *spi)
+{
+	struct cyttsp *ts = spi_get_drvdata(spi);
+
+	cyttsp_remove(ts);
+
+	return 0;
+}
+
+static struct spi_driver cyttsp_spi_driver = {
+	.driver = {
+		.name	= CY_SPI_NAME,
+		.pm	= &cyttsp_pm_ops,
+	},
+	.probe  = cyttsp_spi_probe,
+	.remove = cyttsp_spi_remove,
+};
+
+module_spi_driver(cyttsp_spi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
+MODULE_AUTHOR("Cypress");
+MODULE_ALIAS("spi:cyttsp");
diff --git a/drivers/input/touchscreen/da9034-ts.c b/drivers/input/touchscreen/da9034-ts.c
new file mode 100644
index 0000000..8264822
--- /dev/null
+++ b/drivers/input/touchscreen/da9034-ts.c
@@ -0,0 +1,368 @@
+/*
+ * Touchscreen driver for Dialog Semiconductor DA9034
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ *	Fengwei Yin <fengwei.yin@marvell.com>
+ *	Bin Yang  <bin.yang@marvell.com>
+ *	Eric Miao <eric.miao@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/da903x.h>
+#include <linux/slab.h>
+
+#define DA9034_MANUAL_CTRL	0x50
+#define DA9034_LDO_ADC_EN	(1 << 4)
+
+#define DA9034_AUTO_CTRL1	0x51
+
+#define DA9034_AUTO_CTRL2	0x52
+#define DA9034_AUTO_TSI_EN	(1 << 3)
+#define DA9034_PEN_DETECT	(1 << 4)
+
+#define DA9034_TSI_CTRL1	0x53
+#define DA9034_TSI_CTRL2	0x54
+#define DA9034_TSI_X_MSB	0x6c
+#define DA9034_TSI_Y_MSB	0x6d
+#define DA9034_TSI_XY_LSB	0x6e
+
+enum {
+	STATE_IDLE,	/* wait for pendown */
+	STATE_BUSY,	/* TSI busy sampling */
+	STATE_STOP,	/* sample available */
+	STATE_WAIT,	/* Wait to start next sample */
+};
+
+enum {
+	EVENT_PEN_DOWN,
+	EVENT_PEN_UP,
+	EVENT_TSI_READY,
+	EVENT_TIMEDOUT,
+};
+
+struct da9034_touch {
+	struct device		*da9034_dev;
+	struct input_dev	*input_dev;
+
+	struct delayed_work	tsi_work;
+	struct notifier_block	notifier;
+
+	int	state;
+
+	int	interval_ms;
+	int	x_inverted;
+	int	y_inverted;
+
+	int	last_x;
+	int	last_y;
+};
+
+static inline int is_pen_down(struct da9034_touch *touch)
+{
+	return da903x_query_status(touch->da9034_dev, DA9034_STATUS_PEN_DOWN);
+}
+
+static inline int detect_pen_down(struct da9034_touch *touch, int on)
+{
+	if (on)
+		return da903x_set_bits(touch->da9034_dev,
+				DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
+	else
+		return da903x_clr_bits(touch->da9034_dev,
+				DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
+}
+
+static int read_tsi(struct da9034_touch *touch)
+{
+	uint8_t _x, _y, _v;
+	int ret;
+
+	ret = da903x_read(touch->da9034_dev, DA9034_TSI_X_MSB, &_x);
+	if (ret)
+		return ret;
+
+	ret = da903x_read(touch->da9034_dev, DA9034_TSI_Y_MSB, &_y);
+	if (ret)
+		return ret;
+
+	ret = da903x_read(touch->da9034_dev, DA9034_TSI_XY_LSB, &_v);
+	if (ret)
+		return ret;
+
+	touch->last_x = ((_x << 2) & 0x3fc) | (_v & 0x3);
+	touch->last_y = ((_y << 2) & 0x3fc) | ((_v & 0xc) >> 2);
+
+	return 0;
+}
+
+static inline int start_tsi(struct da9034_touch *touch)
+{
+	return da903x_set_bits(touch->da9034_dev,
+			DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
+}
+
+static inline int stop_tsi(struct da9034_touch *touch)
+{
+	return da903x_clr_bits(touch->da9034_dev,
+			DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
+}
+
+static inline void report_pen_down(struct da9034_touch *touch)
+{
+	int x = touch->last_x;
+	int y = touch->last_y;
+
+	x &= 0xfff;
+	if (touch->x_inverted)
+		x = 1024 - x;
+	y &= 0xfff;
+	if (touch->y_inverted)
+		y = 1024 - y;
+
+	input_report_abs(touch->input_dev, ABS_X, x);
+	input_report_abs(touch->input_dev, ABS_Y, y);
+	input_report_key(touch->input_dev, BTN_TOUCH, 1);
+
+	input_sync(touch->input_dev);
+}
+
+static inline void report_pen_up(struct da9034_touch *touch)
+{
+	input_report_key(touch->input_dev, BTN_TOUCH, 0);
+	input_sync(touch->input_dev);
+}
+
+static void da9034_event_handler(struct da9034_touch *touch, int event)
+{
+	int err;
+
+	switch (touch->state) {
+	case STATE_IDLE:
+		if (event != EVENT_PEN_DOWN)
+			break;
+
+		/* Enable auto measurement of the TSI, this will
+		 * automatically disable pen down detection
+		 */
+		err = start_tsi(touch);
+		if (err)
+			goto err_reset;
+
+		touch->state = STATE_BUSY;
+		break;
+
+	case STATE_BUSY:
+		if (event != EVENT_TSI_READY)
+			break;
+
+		err = read_tsi(touch);
+		if (err)
+			goto err_reset;
+
+		/* Disable auto measurement of the TSI, so that
+		 * pen down status will be available
+		 */
+		err = stop_tsi(touch);
+		if (err)
+			goto err_reset;
+
+		touch->state = STATE_STOP;
+
+		/* FIXME: PEN_{UP/DOWN} events are expected to be
+		 * available by stopping TSI, but this is found not
+		 * always true, delay and simulate such an event
+		 * here is more reliable
+		 */
+		mdelay(1);
+		da9034_event_handler(touch,
+				     is_pen_down(touch) ? EVENT_PEN_DOWN :
+							  EVENT_PEN_UP);
+		break;
+
+	case STATE_STOP:
+		if (event == EVENT_PEN_DOWN) {
+			report_pen_down(touch);
+			schedule_delayed_work(&touch->tsi_work,
+				msecs_to_jiffies(touch->interval_ms));
+			touch->state = STATE_WAIT;
+		}
+
+		if (event == EVENT_PEN_UP) {
+			report_pen_up(touch);
+			touch->state = STATE_IDLE;
+		}
+		break;
+
+	case STATE_WAIT:
+		if (event != EVENT_TIMEDOUT)
+			break;
+
+		if (is_pen_down(touch)) {
+			start_tsi(touch);
+			touch->state = STATE_BUSY;
+		} else {
+			report_pen_up(touch);
+			touch->state = STATE_IDLE;
+		}
+		break;
+	}
+	return;
+
+err_reset:
+	touch->state = STATE_IDLE;
+	stop_tsi(touch);
+	detect_pen_down(touch, 1);
+}
+
+static void da9034_tsi_work(struct work_struct *work)
+{
+	struct da9034_touch *touch =
+		container_of(work, struct da9034_touch, tsi_work.work);
+
+	da9034_event_handler(touch, EVENT_TIMEDOUT);
+}
+
+static int da9034_touch_notifier(struct notifier_block *nb,
+				 unsigned long event, void *data)
+{
+	struct da9034_touch *touch =
+		container_of(nb, struct da9034_touch, notifier);
+
+	if (event & DA9034_EVENT_TSI_READY)
+		da9034_event_handler(touch, EVENT_TSI_READY);
+
+	if ((event & DA9034_EVENT_PEN_DOWN) && touch->state == STATE_IDLE)
+		da9034_event_handler(touch, EVENT_PEN_DOWN);
+
+	return 0;
+}
+
+static int da9034_touch_open(struct input_dev *dev)
+{
+	struct da9034_touch *touch = input_get_drvdata(dev);
+	int ret;
+
+	ret = da903x_register_notifier(touch->da9034_dev, &touch->notifier,
+			DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
+	if (ret)
+		return -EBUSY;
+
+	/* Enable ADC LDO */
+	ret = da903x_set_bits(touch->da9034_dev,
+			DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
+	if (ret)
+		return ret;
+
+	/* TSI_DELAY: 3 slots, TSI_SKIP: 3 slots */
+	ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL1, 0x1b);
+	if (ret)
+		return ret;
+
+	ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL2, 0x00);
+	if (ret)
+		return ret;
+
+	touch->state = STATE_IDLE;
+	detect_pen_down(touch, 1);
+
+	return 0;
+}
+
+static void da9034_touch_close(struct input_dev *dev)
+{
+	struct da9034_touch *touch = input_get_drvdata(dev);
+
+	da903x_unregister_notifier(touch->da9034_dev, &touch->notifier,
+			DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
+
+	cancel_delayed_work_sync(&touch->tsi_work);
+
+	touch->state = STATE_IDLE;
+	stop_tsi(touch);
+	detect_pen_down(touch, 0);
+
+	/* Disable ADC LDO */
+	da903x_clr_bits(touch->da9034_dev,
+			DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
+}
+
+
+static int da9034_touch_probe(struct platform_device *pdev)
+{
+	struct da9034_touch_pdata *pdata = dev_get_platdata(&pdev->dev);
+	struct da9034_touch *touch;
+	struct input_dev *input_dev;
+	int error;
+
+	touch = devm_kzalloc(&pdev->dev, sizeof(struct da9034_touch),
+			     GFP_KERNEL);
+	if (!touch) {
+		dev_err(&pdev->dev, "failed to allocate driver data\n");
+		return -ENOMEM;
+	}
+
+	touch->da9034_dev = pdev->dev.parent;
+
+	if (pdata) {
+		touch->interval_ms	= pdata->interval_ms;
+		touch->x_inverted	= pdata->x_inverted;
+		touch->y_inverted	= pdata->y_inverted;
+	} else {
+		/* fallback into default */
+		touch->interval_ms	= 10;
+	}
+
+	INIT_DELAYED_WORK(&touch->tsi_work, da9034_tsi_work);
+	touch->notifier.notifier_call = da9034_touch_notifier;
+
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!input_dev) {
+		dev_err(&pdev->dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	input_dev->name		= pdev->name;
+	input_dev->open		= da9034_touch_open;
+	input_dev->close	= da9034_touch_close;
+	input_dev->dev.parent	= &pdev->dev;
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(ABS_X, input_dev->absbit);
+	__set_bit(ABS_Y, input_dev->absbit);
+	input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
+
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+
+	touch->input_dev = input_dev;
+	input_set_drvdata(input_dev, touch);
+
+	error = input_register_device(input_dev);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static struct platform_driver da9034_touch_driver = {
+	.driver	= {
+		.name	= "da9034-touch",
+	},
+	.probe		= da9034_touch_probe,
+};
+module_platform_driver(da9034_touch_driver);
+
+MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034");
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>, Bin Yang <bin.yang@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9034-touch");
diff --git a/drivers/input/touchscreen/da9052_tsi.c b/drivers/input/touchscreen/da9052_tsi.c
new file mode 100644
index 0000000..5a013bb
--- /dev/null
+++ b/drivers/input/touchscreen/da9052_tsi.c
@@ -0,0 +1,348 @@
+/*
+ * TSI driver for Dialog DA9052
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ *  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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/da9052.h>
+
+#define TSI_PEN_DOWN_STATUS 0x40
+
+struct da9052_tsi {
+	struct da9052 *da9052;
+	struct input_dev *dev;
+	struct delayed_work ts_pen_work;
+	struct mutex mutex;
+	bool stopped;
+	bool adc_on;
+};
+
+static void da9052_ts_adc_toggle(struct da9052_tsi *tsi, bool on)
+{
+	da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 0, on);
+	tsi->adc_on = on;
+}
+
+static irqreturn_t da9052_ts_pendwn_irq(int irq, void *data)
+{
+	struct da9052_tsi *tsi = data;
+
+	if (!tsi->stopped) {
+		/* Mask PEN_DOWN event and unmask TSI_READY event */
+		da9052_disable_irq_nosync(tsi->da9052, DA9052_IRQ_PENDOWN);
+		da9052_enable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
+
+		da9052_ts_adc_toggle(tsi, true);
+
+		schedule_delayed_work(&tsi->ts_pen_work, HZ / 50);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void da9052_ts_read(struct da9052_tsi *tsi)
+{
+	struct input_dev *input = tsi->dev;
+	int ret;
+	u16 x, y, z;
+	u8 v;
+
+	ret = da9052_reg_read(tsi->da9052, DA9052_TSI_X_MSB_REG);
+	if (ret < 0)
+		return;
+
+	x = (u16) ret;
+
+	ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Y_MSB_REG);
+	if (ret < 0)
+		return;
+
+	y = (u16) ret;
+
+	ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Z_MSB_REG);
+	if (ret < 0)
+		return;
+
+	z = (u16) ret;
+
+	ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG);
+	if (ret < 0)
+		return;
+
+	v = (u8) ret;
+
+	x = ((x << 2) & 0x3fc) | (v & 0x3);
+	y = ((y << 2) & 0x3fc) | ((v & 0xc) >> 2);
+	z = ((z << 2) & 0x3fc) | ((v & 0x30) >> 4);
+
+	input_report_key(input, BTN_TOUCH, 1);
+	input_report_abs(input, ABS_X, x);
+	input_report_abs(input, ABS_Y, y);
+	input_report_abs(input, ABS_PRESSURE, z);
+	input_sync(input);
+}
+
+static irqreturn_t da9052_ts_datardy_irq(int irq, void *data)
+{
+	struct da9052_tsi *tsi = data;
+
+	da9052_ts_read(tsi);
+
+	return IRQ_HANDLED;
+}
+
+static void da9052_ts_pen_work(struct work_struct *work)
+{
+	struct da9052_tsi *tsi = container_of(work, struct da9052_tsi,
+					      ts_pen_work.work);
+	if (!tsi->stopped) {
+		int ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG);
+		if (ret < 0 || (ret & TSI_PEN_DOWN_STATUS)) {
+			/* Pen is still DOWN (or read error) */
+			schedule_delayed_work(&tsi->ts_pen_work, HZ / 50);
+		} else {
+			struct input_dev *input = tsi->dev;
+
+			/* Pen UP */
+			da9052_ts_adc_toggle(tsi, false);
+
+			/* Report Pen UP */
+			input_report_key(input, BTN_TOUCH, 0);
+			input_report_abs(input, ABS_PRESSURE, 0);
+			input_sync(input);
+
+			/*
+			 * FIXME: Fixes the unhandled irq issue when quick
+			 * pen down and pen up events occurs
+			 */
+			ret = da9052_reg_update(tsi->da9052,
+						DA9052_EVENT_B_REG, 0xC0, 0xC0);
+			if (ret < 0)
+				return;
+
+			/* Mask TSI_READY event and unmask PEN_DOWN event */
+			da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
+			da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
+		}
+	}
+}
+
+static int da9052_ts_configure_gpio(struct da9052 *da9052)
+{
+	int error;
+
+	error = da9052_reg_update(da9052, DA9052_GPIO_2_3_REG, 0x30, 0);
+	if (error < 0)
+		return error;
+
+	error = da9052_reg_update(da9052, DA9052_GPIO_4_5_REG, 0x33, 0);
+	if (error < 0)
+		return error;
+
+	error = da9052_reg_update(da9052, DA9052_GPIO_6_7_REG, 0x33, 0);
+	if (error < 0)
+		return error;
+
+	return 0;
+}
+
+static int da9052_configure_tsi(struct da9052_tsi *tsi)
+{
+	int error;
+
+	error = da9052_ts_configure_gpio(tsi->da9052);
+	if (error)
+		return error;
+
+	/* Measure TSI sample every 1ms */
+	error = da9052_reg_update(tsi->da9052, DA9052_ADC_CONT_REG,
+				  1 << 6, 1 << 6);
+	if (error < 0)
+		return error;
+
+	/* TSI_DELAY: 3 slots, TSI_SKIP: 0 slots, TSI_MODE: XYZP */
+	error = da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 0xFC, 0xC0);
+	if (error < 0)
+		return error;
+
+	/* Supply TSIRef through LD09 */
+	error = da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x59);
+	if (error < 0)
+		return error;
+
+	return 0;
+}
+
+static int da9052_ts_input_open(struct input_dev *input_dev)
+{
+	struct da9052_tsi *tsi = input_get_drvdata(input_dev);
+
+	tsi->stopped = false;
+	mb();
+
+	/* Unmask PEN_DOWN event */
+	da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
+
+	/* Enable Pen Detect Circuit */
+	return da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG,
+				 1 << 1, 1 << 1);
+}
+
+static void da9052_ts_input_close(struct input_dev *input_dev)
+{
+	struct da9052_tsi *tsi = input_get_drvdata(input_dev);
+
+	tsi->stopped = true;
+	mb();
+	da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
+	cancel_delayed_work_sync(&tsi->ts_pen_work);
+
+	if (tsi->adc_on) {
+		da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
+		da9052_ts_adc_toggle(tsi, false);
+
+		/*
+		 * If ADC was on that means that pendwn IRQ was disabled
+		 * twice and we need to enable it to keep enable/disable
+		 * counter balanced. IRQ is still off though.
+		 */
+		da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
+	}
+
+	/* Disable Pen Detect Circuit */
+	da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0);
+}
+
+static int da9052_ts_probe(struct platform_device *pdev)
+{
+	struct da9052 *da9052;
+	struct da9052_tsi *tsi;
+	struct input_dev *input_dev;
+	int error;
+
+	da9052 = dev_get_drvdata(pdev->dev.parent);
+	if (!da9052)
+		return -EINVAL;
+
+	tsi = kzalloc(sizeof(struct da9052_tsi), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!tsi || !input_dev) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	tsi->da9052 = da9052;
+	tsi->dev = input_dev;
+	tsi->stopped = true;
+	INIT_DELAYED_WORK(&tsi->ts_pen_work, da9052_ts_pen_work);
+
+	input_dev->id.version = 0x0101;
+	input_dev->id.vendor = 0x15B6;
+	input_dev->id.product = 0x9052;
+	input_dev->name = "Dialog DA9052 TouchScreen Driver";
+	input_dev->dev.parent = &pdev->dev;
+	input_dev->open = da9052_ts_input_open;
+	input_dev->close = da9052_ts_input_close;
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+
+	input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1023, 0, 0);
+
+	input_set_drvdata(input_dev, tsi);
+
+	/* Disable Pen Detect Circuit */
+	da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0);
+
+	/* Disable ADC */
+	da9052_ts_adc_toggle(tsi, false);
+
+	error = da9052_request_irq(tsi->da9052, DA9052_IRQ_PENDOWN,
+				"pendown-irq", da9052_ts_pendwn_irq, tsi);
+	if (error) {
+		dev_err(tsi->da9052->dev,
+			"Failed to register PENDWN IRQ: %d\n", error);
+		goto err_free_mem;
+	}
+
+	error = da9052_request_irq(tsi->da9052, DA9052_IRQ_TSIREADY,
+				"tsiready-irq", da9052_ts_datardy_irq, tsi);
+	if (error) {
+		dev_err(tsi->da9052->dev,
+			"Failed to register TSIRDY IRQ :%d\n", error);
+		goto err_free_pendwn_irq;
+	}
+
+	/* Mask PEN_DOWN and TSI_READY events */
+	da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
+	da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
+
+	error = da9052_configure_tsi(tsi);
+	if (error)
+		goto err_free_datardy_irq;
+
+	error = input_register_device(tsi->dev);
+	if (error)
+		goto err_free_datardy_irq;
+
+	platform_set_drvdata(pdev, tsi);
+
+	return 0;
+
+err_free_datardy_irq:
+	da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi);
+err_free_pendwn_irq:
+	da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi);
+err_free_mem:
+	kfree(tsi);
+	input_free_device(input_dev);
+
+	return error;
+}
+
+static int  da9052_ts_remove(struct platform_device *pdev)
+{
+	struct da9052_tsi *tsi = platform_get_drvdata(pdev);
+
+	da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x19);
+
+	da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi);
+	da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi);
+
+	input_unregister_device(tsi->dev);
+	kfree(tsi);
+
+	return 0;
+}
+
+static struct platform_driver da9052_tsi_driver = {
+	.probe	= da9052_ts_probe,
+	.remove	= da9052_ts_remove,
+	.driver	= {
+		.name	= "da9052-tsi",
+	},
+};
+
+module_platform_driver(da9052_tsi_driver);
+
+MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9052");
+MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-tsi");
diff --git a/drivers/input/touchscreen/dynapro.c b/drivers/input/touchscreen/dynapro.c
new file mode 100644
index 0000000..86237a9
--- /dev/null
+++ b/drivers/input/touchscreen/dynapro.c
@@ -0,0 +1,190 @@
+/*
+ * Dynapro serial touchscreen driver
+ *
+ * Copyright (c) 2009 Tias Guns
+ * Based on the inexio driver (c) Vojtech Pavlik and Dan Streetman and
+ * Richard Lemon
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * 2009/09/19 Tias Guns <tias@ulyssis.org>
+ *   Copied inexio.c and edited for Dynapro protocol (from retired Xorg module)
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Dynapro serial touchscreen driver"
+
+MODULE_AUTHOR("Tias Guns <tias@ulyssis.org>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define DYNAPRO_FORMAT_TOUCH_BIT 0x40
+#define DYNAPRO_FORMAT_LENGTH 3
+#define DYNAPRO_RESPONSE_BEGIN_BYTE 0x80
+
+#define DYNAPRO_MIN_XC 0
+#define DYNAPRO_MAX_XC 0x3ff
+#define DYNAPRO_MIN_YC 0
+#define DYNAPRO_MAX_YC 0x3ff
+
+#define DYNAPRO_GET_XC(data) (data[1] | ((data[0] & 0x38) << 4))
+#define DYNAPRO_GET_YC(data) (data[2] | ((data[0] & 0x07) << 7))
+#define DYNAPRO_GET_TOUCHED(data) (DYNAPRO_FORMAT_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct dynapro {
+	struct input_dev *dev;
+	struct serio *serio;
+	int idx;
+	unsigned char data[DYNAPRO_FORMAT_LENGTH];
+	char phys[32];
+};
+
+static void dynapro_process_data(struct dynapro *pdynapro)
+{
+	struct input_dev *dev = pdynapro->dev;
+
+	if (DYNAPRO_FORMAT_LENGTH == ++pdynapro->idx) {
+		input_report_abs(dev, ABS_X, DYNAPRO_GET_XC(pdynapro->data));
+		input_report_abs(dev, ABS_Y, DYNAPRO_GET_YC(pdynapro->data));
+		input_report_key(dev, BTN_TOUCH,
+				 DYNAPRO_GET_TOUCHED(pdynapro->data));
+		input_sync(dev);
+
+		pdynapro->idx = 0;
+	}
+}
+
+static irqreturn_t dynapro_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct dynapro *pdynapro = serio_get_drvdata(serio);
+
+	pdynapro->data[pdynapro->idx] = data;
+
+	if (DYNAPRO_RESPONSE_BEGIN_BYTE & pdynapro->data[0])
+		dynapro_process_data(pdynapro);
+	else
+		dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
+			pdynapro->data[0]);
+
+	return IRQ_HANDLED;
+}
+
+static void dynapro_disconnect(struct serio *serio)
+{
+	struct dynapro *pdynapro = serio_get_drvdata(serio);
+
+	input_get_device(pdynapro->dev);
+	input_unregister_device(pdynapro->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_put_device(pdynapro->dev);
+	kfree(pdynapro);
+}
+
+/*
+ * dynapro_connect() is the routine that is called when someone adds a
+ * new serio device that supports dynapro protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+
+static int dynapro_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct dynapro *pdynapro;
+	struct input_dev *input_dev;
+	int err;
+
+	pdynapro = kzalloc(sizeof(struct dynapro), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!pdynapro || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	pdynapro->serio = serio;
+	pdynapro->dev = input_dev;
+	snprintf(pdynapro->phys, sizeof(pdynapro->phys),
+		 "%s/input0", serio->phys);
+
+	input_dev->name = "Dynapro Serial TouchScreen";
+	input_dev->phys = pdynapro->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_DYNAPRO;
+	input_dev->id.product = 0;
+	input_dev->id.version = 0x0001;
+	input_dev->dev.parent = &serio->dev;
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(pdynapro->dev, ABS_X,
+			     DYNAPRO_MIN_XC, DYNAPRO_MAX_XC, 0, 0);
+	input_set_abs_params(pdynapro->dev, ABS_Y,
+			     DYNAPRO_MIN_YC, DYNAPRO_MAX_YC, 0, 0);
+
+	serio_set_drvdata(serio, pdynapro);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(pdynapro->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(pdynapro);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id dynapro_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_DYNAPRO,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, dynapro_serio_ids);
+
+static struct serio_driver dynapro_drv = {
+	.driver		= {
+		.name	= "dynapro",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= dynapro_serio_ids,
+	.interrupt	= dynapro_interrupt,
+	.connect	= dynapro_connect,
+	.disconnect	= dynapro_disconnect,
+};
+
+module_serio_driver(dynapro_drv);
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
new file mode 100644
index 0000000..0b0f8c1
--- /dev/null
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -0,0 +1,1092 @@
+/*
+ * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de>
+ * Daniel Wagener <daniel.wagener@kernelconcepts.de> (M09 firmware support)
+ * Lothar Waßmann <LW@KARO-electronics.de> (DT support)
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ * This is a driver for the EDT "Polytouch" family of touch controllers
+ * based on the FocalTech FT5x06 line of chips.
+ *
+ * Development of this driver has been sponsored by Glyn:
+ *    http://www.glyn.com/Products/Displays
+ */
+
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/of_device.h>
+
+#define WORK_REGISTER_THRESHOLD		0x00
+#define WORK_REGISTER_REPORT_RATE	0x08
+#define WORK_REGISTER_GAIN		0x30
+#define WORK_REGISTER_OFFSET		0x31
+#define WORK_REGISTER_NUM_X		0x33
+#define WORK_REGISTER_NUM_Y		0x34
+
+#define M09_REGISTER_THRESHOLD		0x80
+#define M09_REGISTER_GAIN		0x92
+#define M09_REGISTER_OFFSET		0x93
+#define M09_REGISTER_NUM_X		0x94
+#define M09_REGISTER_NUM_Y		0x95
+
+#define NO_REGISTER			0xff
+
+#define WORK_REGISTER_OPMODE		0x3c
+#define FACTORY_REGISTER_OPMODE		0x01
+
+#define TOUCH_EVENT_DOWN		0x00
+#define TOUCH_EVENT_UP			0x01
+#define TOUCH_EVENT_ON			0x02
+#define TOUCH_EVENT_RESERVED		0x03
+
+#define EDT_NAME_LEN			23
+#define EDT_SWITCH_MODE_RETRIES		10
+#define EDT_SWITCH_MODE_DELAY		5 /* msec */
+#define EDT_RAW_DATA_RETRIES		100
+#define EDT_RAW_DATA_DELAY		1 /* msec */
+
+enum edt_ver {
+	M06,
+	M09,
+};
+
+struct edt_reg_addr {
+	int reg_threshold;
+	int reg_report_rate;
+	int reg_gain;
+	int reg_offset;
+	int reg_num_x;
+	int reg_num_y;
+};
+
+struct edt_ft5x06_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+	u16 num_x;
+	u16 num_y;
+
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *wake_gpio;
+
+#if defined(CONFIG_DEBUG_FS)
+	struct dentry *debug_dir;
+	u8 *raw_buffer;
+	size_t raw_bufsize;
+#endif
+
+	struct mutex mutex;
+	bool factory_mode;
+	int threshold;
+	int gain;
+	int offset;
+	int report_rate;
+	int max_support_points;
+
+	char name[EDT_NAME_LEN];
+
+	struct edt_reg_addr reg_addr;
+	enum edt_ver version;
+};
+
+struct edt_i2c_chip_data {
+	int  max_support_points;
+};
+
+static int edt_ft5x06_ts_readwrite(struct i2c_client *client,
+				   u16 wr_len, u8 *wr_buf,
+				   u16 rd_len, u8 *rd_buf)
+{
+	struct i2c_msg wrmsg[2];
+	int i = 0;
+	int ret;
+
+	if (wr_len) {
+		wrmsg[i].addr  = client->addr;
+		wrmsg[i].flags = 0;
+		wrmsg[i].len = wr_len;
+		wrmsg[i].buf = wr_buf;
+		i++;
+	}
+	if (rd_len) {
+		wrmsg[i].addr  = client->addr;
+		wrmsg[i].flags = I2C_M_RD;
+		wrmsg[i].len = rd_len;
+		wrmsg[i].buf = rd_buf;
+		i++;
+	}
+
+	ret = i2c_transfer(client->adapter, wrmsg, i);
+	if (ret < 0)
+		return ret;
+	if (ret != i)
+		return -EIO;
+
+	return 0;
+}
+
+static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata,
+				    u8 *buf, int buflen)
+{
+	int i;
+	u8 crc = 0;
+
+	for (i = 0; i < buflen - 1; i++)
+		crc ^= buf[i];
+
+	if (crc != buf[buflen-1]) {
+		dev_err_ratelimited(&tsdata->client->dev,
+				    "crc error: 0x%02x expected, got 0x%02x\n",
+				    crc, buf[buflen-1]);
+		return false;
+	}
+
+	return true;
+}
+
+static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
+{
+	struct edt_ft5x06_ts_data *tsdata = dev_id;
+	struct device *dev = &tsdata->client->dev;
+	u8 cmd;
+	u8 rdbuf[63];
+	int i, type, x, y, id;
+	int offset, tplen, datalen, crclen;
+	int error;
+
+	switch (tsdata->version) {
+	case M06:
+		cmd = 0xf9; /* tell the controller to send touch data */
+		offset = 5; /* where the actual touch data starts */
+		tplen = 4;  /* data comes in so called frames */
+		crclen = 1; /* length of the crc data */
+		break;
+
+	case M09:
+		cmd = 0x0;
+		offset = 3;
+		tplen = 6;
+		crclen = 0;
+		break;
+
+	default:
+		goto out;
+	}
+
+	memset(rdbuf, 0, sizeof(rdbuf));
+	datalen = tplen * tsdata->max_support_points + offset + crclen;
+
+	error = edt_ft5x06_ts_readwrite(tsdata->client,
+					sizeof(cmd), &cmd,
+					datalen, rdbuf);
+	if (error) {
+		dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",
+				    error);
+		goto out;
+	}
+
+	/* M09 does not send header or CRC */
+	if (tsdata->version == M06) {
+		if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa ||
+			rdbuf[2] != datalen) {
+			dev_err_ratelimited(dev,
+					"Unexpected header: %02x%02x%02x!\n",
+					rdbuf[0], rdbuf[1], rdbuf[2]);
+			goto out;
+		}
+
+		if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen))
+			goto out;
+	}
+
+	for (i = 0; i < tsdata->max_support_points; i++) {
+		u8 *buf = &rdbuf[i * tplen + offset];
+		bool down;
+
+		type = buf[0] >> 6;
+		/* ignore Reserved events */
+		if (type == TOUCH_EVENT_RESERVED)
+			continue;
+
+		/* M06 sometimes sends bogus coordinates in TOUCH_DOWN */
+		if (tsdata->version == M06 && type == TOUCH_EVENT_DOWN)
+			continue;
+
+		x = ((buf[0] << 8) | buf[1]) & 0x0fff;
+		y = ((buf[2] << 8) | buf[3]) & 0x0fff;
+		id = (buf[2] >> 4) & 0x0f;
+		down = type != TOUCH_EVENT_UP;
+
+		input_mt_slot(tsdata->input, id);
+		input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down);
+
+		if (!down)
+			continue;
+
+		input_report_abs(tsdata->input, ABS_MT_POSITION_X, x);
+		input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y);
+	}
+
+	input_mt_report_pointer_emulation(tsdata->input, true);
+	input_sync(tsdata->input);
+
+out:
+	return IRQ_HANDLED;
+}
+
+static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
+				     u8 addr, u8 value)
+{
+	u8 wrbuf[4];
+
+	switch (tsdata->version) {
+	case M06:
+		wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+		wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+		wrbuf[2] = value;
+		wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
+		return edt_ft5x06_ts_readwrite(tsdata->client, 4,
+					wrbuf, 0, NULL);
+	case M09:
+		wrbuf[0] = addr;
+		wrbuf[1] = value;
+
+		return edt_ft5x06_ts_readwrite(tsdata->client, 2,
+					wrbuf, 0, NULL);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
+				    u8 addr)
+{
+	u8 wrbuf[2], rdbuf[2];
+	int error;
+
+	switch (tsdata->version) {
+	case M06:
+		wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc;
+		wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f;
+		wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40;
+
+		error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2,
+						rdbuf);
+		if (error)
+			return error;
+
+		if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) {
+			dev_err(&tsdata->client->dev,
+				"crc error: 0x%02x expected, got 0x%02x\n",
+				wrbuf[0] ^ wrbuf[1] ^ rdbuf[0],
+				rdbuf[1]);
+			return -EIO;
+		}
+		break;
+
+	case M09:
+		wrbuf[0] = addr;
+		error = edt_ft5x06_ts_readwrite(tsdata->client, 1,
+						wrbuf, 1, rdbuf);
+		if (error)
+			return error;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return rdbuf[0];
+}
+
+struct edt_ft5x06_attribute {
+	struct device_attribute dattr;
+	size_t field_offset;
+	u8 limit_low;
+	u8 limit_high;
+	u8 addr_m06;
+	u8 addr_m09;
+};
+
+#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09,			\
+		_limit_low, _limit_high)				\
+	struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = {	\
+		.dattr = __ATTR(_field, _mode,				\
+				edt_ft5x06_setting_show,		\
+				edt_ft5x06_setting_store),		\
+		.field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \
+		.addr_m06 = _addr_m06,					\
+		.addr_m09 = _addr_m09,					\
+		.limit_low = _limit_low,				\
+		.limit_high = _limit_high,				\
+	}
+
+static ssize_t edt_ft5x06_setting_show(struct device *dev,
+				       struct device_attribute *dattr,
+				       char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+	struct edt_ft5x06_attribute *attr =
+			container_of(dattr, struct edt_ft5x06_attribute, dattr);
+	u8 *field = (u8 *)tsdata + attr->field_offset;
+	int val;
+	size_t count = 0;
+	int error = 0;
+	u8 addr;
+
+	mutex_lock(&tsdata->mutex);
+
+	if (tsdata->factory_mode) {
+		error = -EIO;
+		goto out;
+	}
+
+	switch (tsdata->version) {
+	case M06:
+		addr = attr->addr_m06;
+		break;
+
+	case M09:
+		addr = attr->addr_m09;
+		break;
+
+	default:
+		error = -ENODEV;
+		goto out;
+	}
+
+	if (addr != NO_REGISTER) {
+		val = edt_ft5x06_register_read(tsdata, addr);
+		if (val < 0) {
+			error = val;
+			dev_err(&tsdata->client->dev,
+				"Failed to fetch attribute %s, error %d\n",
+				dattr->attr.name, error);
+			goto out;
+		}
+	} else {
+		val = *field;
+	}
+
+	if (val != *field) {
+		dev_warn(&tsdata->client->dev,
+			 "%s: read (%d) and stored value (%d) differ\n",
+			 dattr->attr.name, val, *field);
+		*field = val;
+	}
+
+	count = scnprintf(buf, PAGE_SIZE, "%d\n", val);
+out:
+	mutex_unlock(&tsdata->mutex);
+	return error ?: count;
+}
+
+static ssize_t edt_ft5x06_setting_store(struct device *dev,
+					struct device_attribute *dattr,
+					const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+	struct edt_ft5x06_attribute *attr =
+			container_of(dattr, struct edt_ft5x06_attribute, dattr);
+	u8 *field = (u8 *)tsdata + attr->field_offset;
+	unsigned int val;
+	int error;
+	u8 addr;
+
+	mutex_lock(&tsdata->mutex);
+
+	if (tsdata->factory_mode) {
+		error = -EIO;
+		goto out;
+	}
+
+	error = kstrtouint(buf, 0, &val);
+	if (error)
+		goto out;
+
+	if (val < attr->limit_low || val > attr->limit_high) {
+		error = -ERANGE;
+		goto out;
+	}
+
+	switch (tsdata->version) {
+	case M06:
+		addr = attr->addr_m06;
+		break;
+
+	case M09:
+		addr = attr->addr_m09;
+		break;
+
+	default:
+		error = -ENODEV;
+		goto out;
+	}
+
+	if (addr != NO_REGISTER) {
+		error = edt_ft5x06_register_write(tsdata, addr, val);
+		if (error) {
+			dev_err(&tsdata->client->dev,
+				"Failed to update attribute %s, error: %d\n",
+				dattr->attr.name, error);
+			goto out;
+		}
+	}
+	*field = val;
+
+out:
+	mutex_unlock(&tsdata->mutex);
+	return error ?: count;
+}
+
+static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN,
+		M09_REGISTER_GAIN, 0, 31);
+static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET,
+		M09_REGISTER_OFFSET, 0, 31);
+static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD,
+		M09_REGISTER_THRESHOLD, 20, 80);
+static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE,
+		NO_REGISTER, 3, 14);
+
+static struct attribute *edt_ft5x06_attrs[] = {
+	&edt_ft5x06_attr_gain.dattr.attr,
+	&edt_ft5x06_attr_offset.dattr.attr,
+	&edt_ft5x06_attr_threshold.dattr.attr,
+	&edt_ft5x06_attr_report_rate.dattr.attr,
+	NULL
+};
+
+static const struct attribute_group edt_ft5x06_attr_group = {
+	.attrs = edt_ft5x06_attrs,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
+{
+	struct i2c_client *client = tsdata->client;
+	int retries = EDT_SWITCH_MODE_RETRIES;
+	int ret;
+	int error;
+
+	disable_irq(client->irq);
+
+	if (!tsdata->raw_buffer) {
+		tsdata->raw_bufsize = tsdata->num_x * tsdata->num_y *
+				      sizeof(u16);
+		tsdata->raw_buffer = kzalloc(tsdata->raw_bufsize, GFP_KERNEL);
+		if (!tsdata->raw_buffer) {
+			error = -ENOMEM;
+			goto err_out;
+		}
+	}
+
+	/* mode register is 0x3c when in the work mode */
+	if (tsdata->version == M09)
+		goto m09_out;
+
+	error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to switch to factory mode, error %d\n", error);
+		goto err_out;
+	}
+
+	tsdata->factory_mode = true;
+	do {
+		mdelay(EDT_SWITCH_MODE_DELAY);
+		/* mode register is 0x01 when in factory mode */
+		ret = edt_ft5x06_register_read(tsdata, FACTORY_REGISTER_OPMODE);
+		if (ret == 0x03)
+			break;
+	} while (--retries > 0);
+
+	if (retries == 0) {
+		dev_err(&client->dev, "not in factory mode after %dms.\n",
+			EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
+		error = -EIO;
+		goto err_out;
+	}
+
+	return 0;
+
+err_out:
+	kfree(tsdata->raw_buffer);
+	tsdata->raw_buffer = NULL;
+	tsdata->factory_mode = false;
+	enable_irq(client->irq);
+
+	return error;
+
+m09_out:
+	dev_err(&client->dev, "No factory mode support for M09\n");
+	return -EINVAL;
+
+}
+
+static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
+{
+	struct i2c_client *client = tsdata->client;
+	int retries = EDT_SWITCH_MODE_RETRIES;
+	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+	int ret;
+	int error;
+
+	/* mode register is 0x01 when in the factory mode */
+	error = edt_ft5x06_register_write(tsdata, FACTORY_REGISTER_OPMODE, 0x1);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to switch to work mode, error: %d\n", error);
+		return error;
+	}
+
+	tsdata->factory_mode = false;
+
+	do {
+		mdelay(EDT_SWITCH_MODE_DELAY);
+		/* mode register is 0x01 when in factory mode */
+		ret = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OPMODE);
+		if (ret == 0x01)
+			break;
+	} while (--retries > 0);
+
+	if (retries == 0) {
+		dev_err(&client->dev, "not in work mode after %dms.\n",
+			EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
+		tsdata->factory_mode = true;
+		return -EIO;
+	}
+
+	kfree(tsdata->raw_buffer);
+	tsdata->raw_buffer = NULL;
+
+	/* restore parameters */
+	edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold,
+				  tsdata->threshold);
+	edt_ft5x06_register_write(tsdata, reg_addr->reg_gain,
+				  tsdata->gain);
+	edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
+				  tsdata->offset);
+	if (reg_addr->reg_report_rate)
+		edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
+				  tsdata->report_rate);
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode)
+{
+	struct edt_ft5x06_ts_data *tsdata = data;
+
+	*mode = tsdata->factory_mode;
+
+	return 0;
+};
+
+static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode)
+{
+	struct edt_ft5x06_ts_data *tsdata = data;
+	int retval = 0;
+
+	if (mode > 1)
+		return -ERANGE;
+
+	mutex_lock(&tsdata->mutex);
+
+	if (mode != tsdata->factory_mode) {
+		retval = mode ? edt_ft5x06_factory_mode(tsdata) :
+				edt_ft5x06_work_mode(tsdata);
+	}
+
+	mutex_unlock(&tsdata->mutex);
+
+	return retval;
+};
+
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get,
+			edt_ft5x06_debugfs_mode_set, "%llu\n");
+
+static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
+				char __user *buf, size_t count, loff_t *off)
+{
+	struct edt_ft5x06_ts_data *tsdata = file->private_data;
+	struct i2c_client *client = tsdata->client;
+	int retries  = EDT_RAW_DATA_RETRIES;
+	int val, i, error;
+	size_t read = 0;
+	int colbytes;
+	char wrbuf[3];
+	u8 *rdbuf;
+
+	if (*off < 0 || *off >= tsdata->raw_bufsize)
+		return 0;
+
+	mutex_lock(&tsdata->mutex);
+
+	if (!tsdata->factory_mode || !tsdata->raw_buffer) {
+		error = -EIO;
+		goto out;
+	}
+
+	error = edt_ft5x06_register_write(tsdata, 0x08, 0x01);
+	if (error) {
+		dev_dbg(&client->dev,
+			"failed to write 0x08 register, error %d\n", error);
+		goto out;
+	}
+
+	do {
+		msleep(EDT_RAW_DATA_DELAY);
+		val = edt_ft5x06_register_read(tsdata, 0x08);
+		if (val < 1)
+			break;
+	} while (--retries > 0);
+
+	if (val < 0) {
+		error = val;
+		dev_dbg(&client->dev,
+			"failed to read 0x08 register, error %d\n", error);
+		goto out;
+	}
+
+	if (retries == 0) {
+		dev_dbg(&client->dev,
+			"timed out waiting for register to settle\n");
+		error = -ETIMEDOUT;
+		goto out;
+	}
+
+	rdbuf = tsdata->raw_buffer;
+	colbytes = tsdata->num_y * sizeof(u16);
+
+	wrbuf[0] = 0xf5;
+	wrbuf[1] = 0x0e;
+	for (i = 0; i < tsdata->num_x; i++) {
+		wrbuf[2] = i;  /* column index */
+		error = edt_ft5x06_ts_readwrite(tsdata->client,
+						sizeof(wrbuf), wrbuf,
+						colbytes, rdbuf);
+		if (error)
+			goto out;
+
+		rdbuf += colbytes;
+	}
+
+	read = min_t(size_t, count, tsdata->raw_bufsize - *off);
+	if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) {
+		error = -EFAULT;
+		goto out;
+	}
+
+	*off += read;
+out:
+	mutex_unlock(&tsdata->mutex);
+	return error ?: read;
+};
+
+static const struct file_operations debugfs_raw_data_fops = {
+	.open = simple_open,
+	.read = edt_ft5x06_debugfs_raw_data_read,
+};
+
+static void
+edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
+			      const char *debugfs_name)
+{
+	tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL);
+	if (!tsdata->debug_dir)
+		return;
+
+	debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x);
+	debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y);
+
+	debugfs_create_file("mode", S_IRUSR | S_IWUSR,
+			    tsdata->debug_dir, tsdata, &debugfs_mode_fops);
+	debugfs_create_file("raw_data", S_IRUSR,
+			    tsdata->debug_dir, tsdata, &debugfs_raw_data_fops);
+}
+
+static void
+edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
+{
+	debugfs_remove_recursive(tsdata->debug_dir);
+	kfree(tsdata->raw_buffer);
+}
+
+#else
+
+static inline void
+edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
+			      const char *debugfs_name)
+{
+}
+
+static inline void
+edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
+{
+}
+
+#endif /* CONFIG_DEBUGFS */
+
+static int edt_ft5x06_ts_identify(struct i2c_client *client,
+					struct edt_ft5x06_ts_data *tsdata,
+					char *fw_version)
+{
+	u8 rdbuf[EDT_NAME_LEN];
+	char *p;
+	int error;
+	char *model_name = tsdata->name;
+
+	/* see what we find if we assume it is a M06 *
+	 * if we get less than EDT_NAME_LEN, we don't want
+	 * to have garbage in there
+	 */
+	memset(rdbuf, 0, sizeof(rdbuf));
+	error = edt_ft5x06_ts_readwrite(client, 1, "\xbb",
+					EDT_NAME_LEN - 1, rdbuf);
+	if (error)
+		return error;
+
+	/* if we find something consistent, stay with that assumption
+	 * at least M09 won't send 3 bytes here
+	 */
+	if (!(strncasecmp(rdbuf + 1, "EP0", 3))) {
+		tsdata->version = M06;
+
+		/* remove last '$' end marker */
+		rdbuf[EDT_NAME_LEN - 1] = '\0';
+		if (rdbuf[EDT_NAME_LEN - 2] == '$')
+			rdbuf[EDT_NAME_LEN - 2] = '\0';
+
+		/* look for Model/Version separator */
+		p = strchr(rdbuf, '*');
+		if (p)
+			*p++ = '\0';
+		strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN);
+		strlcpy(fw_version, p ? p : "", EDT_NAME_LEN);
+	} else {
+		/* since there are only two versions around (M06, M09) */
+		tsdata->version = M09;
+
+		error = edt_ft5x06_ts_readwrite(client, 1, "\xA6",
+						2, rdbuf);
+		if (error)
+			return error;
+
+		strlcpy(fw_version, rdbuf, 2);
+
+		error = edt_ft5x06_ts_readwrite(client, 1, "\xA8",
+						1, rdbuf);
+		if (error)
+			return error;
+
+		snprintf(model_name, EDT_NAME_LEN, "EP0%i%i0M09",
+			rdbuf[0] >> 4, rdbuf[0] & 0x0F);
+	}
+
+	return 0;
+}
+
+static void edt_ft5x06_ts_get_defaults(struct device *dev,
+				       struct edt_ft5x06_ts_data *tsdata)
+{
+	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+	u32 val;
+	int error;
+
+	error = device_property_read_u32(dev, "threshold", &val);
+	if (!error)
+		reg_addr->reg_threshold = val;
+
+	error = device_property_read_u32(dev, "gain", &val);
+	if (!error)
+		reg_addr->reg_gain = val;
+
+	error = device_property_read_u32(dev, "offset", &val);
+	if (!error)
+		reg_addr->reg_offset = val;
+}
+
+static void
+edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
+{
+	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
+	tsdata->threshold = edt_ft5x06_register_read(tsdata,
+						     reg_addr->reg_threshold);
+	tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain);
+	tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset);
+	if (reg_addr->reg_report_rate != NO_REGISTER)
+		tsdata->report_rate = edt_ft5x06_register_read(tsdata,
+						reg_addr->reg_report_rate);
+	tsdata->num_x = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_x);
+	tsdata->num_y = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_y);
+}
+
+static void
+edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
+{
+	struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
+
+	switch (tsdata->version) {
+	case M06:
+		reg_addr->reg_threshold = WORK_REGISTER_THRESHOLD;
+		reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE;
+		reg_addr->reg_gain = WORK_REGISTER_GAIN;
+		reg_addr->reg_offset = WORK_REGISTER_OFFSET;
+		reg_addr->reg_num_x = WORK_REGISTER_NUM_X;
+		reg_addr->reg_num_y = WORK_REGISTER_NUM_Y;
+		break;
+
+	case M09:
+		reg_addr->reg_threshold = M09_REGISTER_THRESHOLD;
+		reg_addr->reg_gain = M09_REGISTER_GAIN;
+		reg_addr->reg_offset = M09_REGISTER_OFFSET;
+		reg_addr->reg_num_x = M09_REGISTER_NUM_X;
+		reg_addr->reg_num_y = M09_REGISTER_NUM_Y;
+		break;
+	}
+}
+
+static int edt_ft5x06_ts_probe(struct i2c_client *client,
+					 const struct i2c_device_id *id)
+{
+	const struct edt_i2c_chip_data *chip_data;
+	struct edt_ft5x06_ts_data *tsdata;
+	struct input_dev *input;
+	unsigned long irq_flags;
+	int error;
+	char fw_version[EDT_NAME_LEN];
+
+	dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
+
+	tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
+	if (!tsdata) {
+		dev_err(&client->dev, "failed to allocate driver data.\n");
+		return -ENOMEM;
+	}
+
+	chip_data = of_device_get_match_data(&client->dev);
+	if (!chip_data)
+		chip_data = (const struct edt_i2c_chip_data *)id->driver_data;
+	if (!chip_data || !chip_data->max_support_points) {
+		dev_err(&client->dev, "invalid or missing chip data\n");
+		return -EINVAL;
+	}
+
+	tsdata->max_support_points = chip_data->max_support_points;
+
+	tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev,
+						     "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(tsdata->reset_gpio)) {
+		error = PTR_ERR(tsdata->reset_gpio);
+		dev_err(&client->dev,
+			"Failed to request GPIO reset pin, error %d\n", error);
+		return error;
+	}
+
+	tsdata->wake_gpio = devm_gpiod_get_optional(&client->dev,
+						    "wake", GPIOD_OUT_LOW);
+	if (IS_ERR(tsdata->wake_gpio)) {
+		error = PTR_ERR(tsdata->wake_gpio);
+		dev_err(&client->dev,
+			"Failed to request GPIO wake pin, error %d\n", error);
+		return error;
+	}
+
+	if (tsdata->wake_gpio) {
+		usleep_range(5000, 6000);
+		gpiod_set_value_cansleep(tsdata->wake_gpio, 1);
+	}
+
+	if (tsdata->reset_gpio) {
+		usleep_range(5000, 6000);
+		gpiod_set_value_cansleep(tsdata->reset_gpio, 0);
+		msleep(300);
+	}
+
+	input = devm_input_allocate_device(&client->dev);
+	if (!input) {
+		dev_err(&client->dev, "failed to allocate input device.\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&tsdata->mutex);
+	tsdata->client = client;
+	tsdata->input = input;
+	tsdata->factory_mode = false;
+
+	error = edt_ft5x06_ts_identify(client, tsdata, fw_version);
+	if (error) {
+		dev_err(&client->dev, "touchscreen probe failed\n");
+		return error;
+	}
+
+	edt_ft5x06_ts_set_regs(tsdata);
+	edt_ft5x06_ts_get_defaults(&client->dev, tsdata);
+	edt_ft5x06_ts_get_parameters(tsdata);
+
+	dev_dbg(&client->dev,
+		"Model \"%s\", Rev. \"%s\", %dx%d sensors\n",
+		tsdata->name, fw_version, tsdata->num_x, tsdata->num_y);
+
+	input->name = tsdata->name;
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &client->dev;
+
+	input_set_abs_params(input, ABS_MT_POSITION_X,
+			     0, tsdata->num_x * 64 - 1, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y,
+			     0, tsdata->num_y * 64 - 1, 0, 0);
+
+	touchscreen_parse_properties(input, true);
+
+	error = input_mt_init_slots(input, tsdata->max_support_points,
+				INPUT_MT_DIRECT);
+	if (error) {
+		dev_err(&client->dev, "Unable to init MT slots.\n");
+		return error;
+	}
+
+	input_set_drvdata(input, tsdata);
+	i2c_set_clientdata(client, tsdata);
+
+	irq_flags = irq_get_trigger_type(client->irq);
+	if (irq_flags == IRQF_TRIGGER_NONE)
+		irq_flags = IRQF_TRIGGER_FALLING;
+	irq_flags |= IRQF_ONESHOT;
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					NULL, edt_ft5x06_ts_isr, irq_flags,
+					client->name, tsdata);
+	if (error) {
+		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+		return error;
+	}
+
+	error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+	if (error)
+		return error;
+
+	error = input_register_device(input);
+	if (error)
+		goto err_remove_attrs;
+
+	edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev));
+	device_init_wakeup(&client->dev, 1);
+
+	dev_dbg(&client->dev,
+		"EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n",
+		client->irq,
+		tsdata->wake_gpio ? desc_to_gpio(tsdata->wake_gpio) : -1,
+		tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1);
+
+	return 0;
+
+err_remove_attrs:
+	sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+	return error;
+}
+
+static int edt_ft5x06_ts_remove(struct i2c_client *client)
+{
+	struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+
+	edt_ft5x06_ts_teardown_debugfs(tsdata);
+	sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group);
+
+	return 0;
+}
+
+static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(client->irq);
+
+	return 0;
+}
+
+static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
+			 edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume);
+
+static const struct edt_i2c_chip_data edt_ft5x06_data = {
+	.max_support_points = 5,
+};
+
+static const struct edt_i2c_chip_data edt_ft5506_data = {
+	.max_support_points = 10,
+};
+
+static const struct i2c_device_id edt_ft5x06_ts_id[] = {
+	{ .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data },
+	{ .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id edt_ft5x06_of_match[] = {
+	{ .compatible = "edt,edt-ft5206", .data = &edt_ft5x06_data },
+	{ .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data },
+	{ .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data },
+	{ .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
+#endif
+
+static struct i2c_driver edt_ft5x06_ts_driver = {
+	.driver = {
+		.name = "edt_ft5x06",
+		.of_match_table = of_match_ptr(edt_ft5x06_of_match),
+		.pm = &edt_ft5x06_ts_pm_ops,
+	},
+	.id_table = edt_ft5x06_ts_id,
+	.probe    = edt_ft5x06_ts_probe,
+	.remove   = edt_ft5x06_ts_remove,
+};
+
+module_i2c_driver(edt_ft5x06_ts_driver);
+
+MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>");
+MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c
new file mode 100644
index 0000000..09be6ce
--- /dev/null
+++ b/drivers/input/touchscreen/eeti_ts.c
@@ -0,0 +1,327 @@
+/*
+ * Touch Screen driver for EETI's I2C connected touch screen panels
+ *   Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * See EETI's software guide for the protocol specification:
+ *   http://home.eeti.com.tw/web20/eg/guide.htm
+ *
+ * Based on migor_ts.c
+ *   Copyright (c) 2008 Magnus Damm
+ *   Copyright (c) 2007 Ujjwal Pande <ujjwal@kenati.com>
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/timer.h>
+#include <linux/gpio.h>
+#include <linux/input/eeti_ts.h>
+#include <linux/slab.h>
+
+static bool flip_x;
+module_param(flip_x, bool, 0644);
+MODULE_PARM_DESC(flip_x, "flip x coordinate");
+
+static bool flip_y;
+module_param(flip_y, bool, 0644);
+MODULE_PARM_DESC(flip_y, "flip y coordinate");
+
+struct eeti_ts_priv {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct work_struct work;
+	struct mutex mutex;
+	int irq_gpio, irq, irq_active_high;
+};
+
+#define EETI_TS_BITDEPTH	(11)
+#define EETI_MAXVAL		((1 << (EETI_TS_BITDEPTH + 1)) - 1)
+
+#define REPORT_BIT_PRESSED	(1 << 0)
+#define REPORT_BIT_AD0		(1 << 1)
+#define REPORT_BIT_AD1		(1 << 2)
+#define REPORT_BIT_HAS_PRESSURE	(1 << 6)
+#define REPORT_RES_BITS(v)	(((v) >> 1) + EETI_TS_BITDEPTH)
+
+static inline int eeti_ts_irq_active(struct eeti_ts_priv *priv)
+{
+	return gpio_get_value(priv->irq_gpio) == priv->irq_active_high;
+}
+
+static void eeti_ts_read(struct work_struct *work)
+{
+	char buf[6];
+	unsigned int x, y, res, pressed, to = 100;
+	struct eeti_ts_priv *priv =
+		container_of(work, struct eeti_ts_priv, work);
+
+	mutex_lock(&priv->mutex);
+
+	while (eeti_ts_irq_active(priv) && --to)
+		i2c_master_recv(priv->client, buf, sizeof(buf));
+
+	if (!to) {
+		dev_err(&priv->client->dev,
+			"unable to clear IRQ - line stuck?\n");
+		goto out;
+	}
+
+	/* drop non-report packets */
+	if (!(buf[0] & 0x80))
+		goto out;
+
+	pressed = buf[0] & REPORT_BIT_PRESSED;
+	res = REPORT_RES_BITS(buf[0] & (REPORT_BIT_AD0 | REPORT_BIT_AD1));
+	x = buf[2] | (buf[1] << 8);
+	y = buf[4] | (buf[3] << 8);
+
+	/* fix the range to 11 bits */
+	x >>= res - EETI_TS_BITDEPTH;
+	y >>= res - EETI_TS_BITDEPTH;
+
+	if (flip_x)
+		x = EETI_MAXVAL - x;
+
+	if (flip_y)
+		y = EETI_MAXVAL - y;
+
+	if (buf[0] & REPORT_BIT_HAS_PRESSURE)
+		input_report_abs(priv->input, ABS_PRESSURE, buf[5]);
+
+	input_report_abs(priv->input, ABS_X, x);
+	input_report_abs(priv->input, ABS_Y, y);
+	input_report_key(priv->input, BTN_TOUCH, !!pressed);
+	input_sync(priv->input);
+
+out:
+	mutex_unlock(&priv->mutex);
+}
+
+static irqreturn_t eeti_ts_isr(int irq, void *dev_id)
+{
+	struct eeti_ts_priv *priv = dev_id;
+
+	 /* postpone I2C transactions as we are atomic */
+	schedule_work(&priv->work);
+
+	return IRQ_HANDLED;
+}
+
+static void eeti_ts_start(struct eeti_ts_priv *priv)
+{
+	enable_irq(priv->irq);
+
+	/* Read the events once to arm the IRQ */
+	eeti_ts_read(&priv->work);
+}
+
+static void eeti_ts_stop(struct eeti_ts_priv *priv)
+{
+	disable_irq(priv->irq);
+	cancel_work_sync(&priv->work);
+}
+
+static int eeti_ts_open(struct input_dev *dev)
+{
+	struct eeti_ts_priv *priv = input_get_drvdata(dev);
+
+	eeti_ts_start(priv);
+
+	return 0;
+}
+
+static void eeti_ts_close(struct input_dev *dev)
+{
+	struct eeti_ts_priv *priv = input_get_drvdata(dev);
+
+	eeti_ts_stop(priv);
+}
+
+static int eeti_ts_probe(struct i2c_client *client,
+				   const struct i2c_device_id *idp)
+{
+	struct eeti_ts_platform_data *pdata = dev_get_platdata(&client->dev);
+	struct eeti_ts_priv *priv;
+	struct input_dev *input;
+	unsigned int irq_flags;
+	int err = -ENOMEM;
+
+	/*
+	 * In contrast to what's described in the datasheet, there seems
+	 * to be no way of probing the presence of that device using I2C
+	 * commands. So we need to blindly believe it is there, and wait
+	 * for interrupts to occur.
+	 */
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&client->dev, "failed to allocate driver data\n");
+		goto err0;
+	}
+
+	mutex_init(&priv->mutex);
+	input = input_allocate_device();
+
+	if (!input) {
+		dev_err(&client->dev, "Failed to allocate input device.\n");
+		goto err1;
+	}
+
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_set_abs_params(input, ABS_X, 0, EETI_MAXVAL, 0, 0);
+	input_set_abs_params(input, ABS_Y, 0, EETI_MAXVAL, 0, 0);
+	input_set_abs_params(input, ABS_PRESSURE, 0, 0xff, 0, 0);
+
+	input->name = client->name;
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &client->dev;
+	input->open = eeti_ts_open;
+	input->close = eeti_ts_close;
+
+	priv->client = client;
+	priv->input = input;
+	priv->irq_gpio = pdata->irq_gpio;
+	priv->irq = gpio_to_irq(pdata->irq_gpio);
+
+	err = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name);
+	if (err < 0)
+		goto err1;
+
+	priv->irq_active_high = pdata->irq_active_high;
+
+	irq_flags = priv->irq_active_high ?
+		IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+
+	INIT_WORK(&priv->work, eeti_ts_read);
+	i2c_set_clientdata(client, priv);
+	input_set_drvdata(input, priv);
+
+	err = input_register_device(input);
+	if (err)
+		goto err2;
+
+	err = request_irq(priv->irq, eeti_ts_isr, irq_flags,
+			  client->name, priv);
+	if (err) {
+		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+		goto err3;
+	}
+
+	/*
+	 * Disable the device for now. It will be enabled once the
+	 * input device is opened.
+	 */
+	eeti_ts_stop(priv);
+
+	device_init_wakeup(&client->dev, 0);
+	return 0;
+
+err3:
+	input_unregister_device(input);
+	input = NULL; /* so we dont try to free it below */
+err2:
+	gpio_free(pdata->irq_gpio);
+err1:
+	input_free_device(input);
+	kfree(priv);
+err0:
+	return err;
+}
+
+static int eeti_ts_remove(struct i2c_client *client)
+{
+	struct eeti_ts_priv *priv = i2c_get_clientdata(client);
+
+	free_irq(priv->irq, priv);
+	/*
+	 * eeti_ts_stop() leaves IRQ disabled. We need to re-enable it
+	 * so that device still works if we reload the driver.
+	 */
+	enable_irq(priv->irq);
+
+	input_unregister_device(priv->input);
+	kfree(priv);
+
+	return 0;
+}
+
+static int __maybe_unused eeti_ts_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct eeti_ts_priv *priv = i2c_get_clientdata(client);
+	struct input_dev *input_dev = priv->input;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users)
+		eeti_ts_stop(priv);
+
+	mutex_unlock(&input_dev->mutex);
+
+	if (device_may_wakeup(&client->dev))
+		enable_irq_wake(priv->irq);
+
+	return 0;
+}
+
+static int __maybe_unused eeti_ts_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct eeti_ts_priv *priv = i2c_get_clientdata(client);
+	struct input_dev *input_dev = priv->input;
+
+	if (device_may_wakeup(&client->dev))
+		disable_irq_wake(priv->irq);
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users)
+		eeti_ts_start(priv);
+
+	mutex_unlock(&input_dev->mutex);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(eeti_ts_pm, eeti_ts_suspend, eeti_ts_resume);
+
+static const struct i2c_device_id eeti_ts_id[] = {
+	{ "eeti_ts", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, eeti_ts_id);
+
+static struct i2c_driver eeti_ts_driver = {
+	.driver = {
+		.name = "eeti_ts",
+		.pm = &eeti_ts_pm,
+	},
+	.probe = eeti_ts_probe,
+	.remove = eeti_ts_remove,
+	.id_table = eeti_ts_id,
+};
+
+module_i2c_driver(eeti_ts_driver);
+
+MODULE_DESCRIPTION("EETI Touchscreen driver");
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
new file mode 100644
index 0000000..1afc08b
--- /dev/null
+++ b/drivers/input/touchscreen/egalax_ts.c
@@ -0,0 +1,283 @@
+/*
+ * Driver for EETI eGalax Multiple Touch Controller
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ *
+ * based on max11801_ts.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* EETI eGalax serial touch screen controller is a I2C based multiple
+ * touch screen controller, it supports 5 point multiple touch. */
+
+/* TODO:
+  - auto idle mode support
+*/
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/input/mt.h>
+#include <linux/of_gpio.h>
+
+/*
+ * Mouse Mode: some panel may configure the controller to mouse mode,
+ * which can only report one point at a given time.
+ * This driver will ignore events in this mode.
+ */
+#define REPORT_MODE_MOUSE		0x1
+/*
+ * Vendor Mode: this mode is used to transfer some vendor specific
+ * messages.
+ * This driver will ignore events in this mode.
+ */
+#define REPORT_MODE_VENDOR		0x3
+/* Multiple Touch Mode */
+#define REPORT_MODE_MTTOUCH		0x4
+
+#define MAX_SUPPORT_POINTS		5
+
+#define EVENT_VALID_OFFSET	7
+#define EVENT_VALID_MASK	(0x1 << EVENT_VALID_OFFSET)
+#define EVENT_ID_OFFSET		2
+#define EVENT_ID_MASK		(0xf << EVENT_ID_OFFSET)
+#define EVENT_IN_RANGE		(0x1 << 1)
+#define EVENT_DOWN_UP		(0X1 << 0)
+
+#define MAX_I2C_DATA_LEN	10
+
+#define EGALAX_MAX_X	32760
+#define EGALAX_MAX_Y	32760
+#define EGALAX_MAX_TRIES 100
+
+struct egalax_ts {
+	struct i2c_client		*client;
+	struct input_dev		*input_dev;
+};
+
+static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
+{
+	struct egalax_ts *ts = dev_id;
+	struct input_dev *input_dev = ts->input_dev;
+	struct i2c_client *client = ts->client;
+	u8 buf[MAX_I2C_DATA_LEN];
+	int id, ret, x, y, z;
+	int tries = 0;
+	bool down, valid;
+	u8 state;
+
+	do {
+		ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN);
+	} while (ret == -EAGAIN && tries++ < EGALAX_MAX_TRIES);
+
+	if (ret < 0)
+		return IRQ_HANDLED;
+
+	if (buf[0] != REPORT_MODE_MTTOUCH) {
+		/* ignore mouse events and vendor events */
+		return IRQ_HANDLED;
+	}
+
+	state = buf[1];
+	x = (buf[3] << 8) | buf[2];
+	y = (buf[5] << 8) | buf[4];
+	z = (buf[7] << 8) | buf[6];
+
+	valid = state & EVENT_VALID_MASK;
+	id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET;
+	down = state & EVENT_DOWN_UP;
+
+	if (!valid || id > MAX_SUPPORT_POINTS) {
+		dev_dbg(&client->dev, "point invalid\n");
+		return IRQ_HANDLED;
+	}
+
+	input_mt_slot(input_dev, id);
+	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down);
+
+	dev_dbg(&client->dev, "%s id:%d x:%d y:%d z:%d",
+		down ? "down" : "up", id, x, y, z);
+
+	if (down) {
+		input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+		input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+		input_report_abs(input_dev, ABS_MT_PRESSURE, z);
+	}
+
+	input_mt_report_pointer_emulation(input_dev, true);
+	input_sync(input_dev);
+
+	return IRQ_HANDLED;
+}
+
+/* wake up controller by an falling edge of interrupt gpio.  */
+static int egalax_wake_up_device(struct i2c_client *client)
+{
+	struct device_node *np = client->dev.of_node;
+	int gpio;
+	int ret;
+
+	if (!np)
+		return -ENODEV;
+
+	gpio = of_get_named_gpio(np, "wakeup-gpios", 0);
+	if (!gpio_is_valid(gpio))
+		return -ENODEV;
+
+	ret = gpio_request(gpio, "egalax_irq");
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"request gpio failed, cannot wake up controller: %d\n",
+			ret);
+		return ret;
+	}
+
+	/* wake up controller via an falling edge on IRQ gpio. */
+	gpio_direction_output(gpio, 0);
+	gpio_set_value(gpio, 1);
+
+	/* controller should be waken up, return irq.  */
+	gpio_direction_input(gpio);
+	gpio_free(gpio);
+
+	return 0;
+}
+
+static int egalax_firmware_version(struct i2c_client *client)
+{
+	static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 };
+	int ret;
+
+	ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int egalax_ts_probe(struct i2c_client *client,
+			   const struct i2c_device_id *id)
+{
+	struct egalax_ts *ts;
+	struct input_dev *input_dev;
+	int error;
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct egalax_ts), GFP_KERNEL);
+	if (!ts) {
+		dev_err(&client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	input_dev = devm_input_allocate_device(&client->dev);
+	if (!input_dev) {
+		dev_err(&client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	ts->client = client;
+	ts->input_dev = input_dev;
+
+	/* controller may be in sleep, wake it up. */
+	error = egalax_wake_up_device(client);
+	if (error) {
+		dev_err(&client->dev, "Failed to wake up the controller\n");
+		return error;
+	}
+
+	error = egalax_firmware_version(client);
+	if (error < 0) {
+		dev_err(&client->dev, "Failed to read firmware version\n");
+		return error;
+	}
+
+	input_dev->name = "EETI eGalax Touch Screen";
+	input_dev->id.bustype = BUS_I2C;
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+
+	input_set_abs_params(input_dev, ABS_X, 0, EGALAX_MAX_X, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, EGALAX_MAX_Y, 0, 0);
+	input_set_abs_params(input_dev,
+			     ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0);
+	input_set_abs_params(input_dev,
+			     ABS_MT_POSITION_Y, 0, EGALAX_MAX_Y, 0, 0);
+	input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0);
+
+	input_set_drvdata(input_dev, ts);
+
+	error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+					  egalax_ts_interrupt,
+					  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					  "egalax_ts", ts);
+	if (error < 0) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	error = input_register_device(ts->input_dev);
+	if (error)
+		return error;
+
+	i2c_set_clientdata(client, ts);
+	return 0;
+}
+
+static const struct i2c_device_id egalax_ts_id[] = {
+	{ "egalax_ts", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, egalax_ts_id);
+
+static int __maybe_unused egalax_ts_suspend(struct device *dev)
+{
+	static const u8 suspend_cmd[MAX_I2C_DATA_LEN] = {
+		0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0
+	};
+	struct i2c_client *client = to_i2c_client(dev);
+	int ret;
+
+	ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN);
+	return ret > 0 ? 0 : ret;
+}
+
+static int __maybe_unused egalax_ts_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	return egalax_wake_up_device(client);
+}
+
+static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume);
+
+static const struct of_device_id egalax_ts_dt_ids[] = {
+	{ .compatible = "eeti,egalax_ts" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, egalax_ts_dt_ids);
+
+static struct i2c_driver egalax_ts_driver = {
+	.driver = {
+		.name	= "egalax_ts",
+		.pm	= &egalax_ts_pm_ops,
+		.of_match_table	= egalax_ts_dt_ids,
+	},
+	.id_table	= egalax_ts_id,
+	.probe		= egalax_ts_probe,
+};
+
+module_i2c_driver(egalax_ts_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
new file mode 100644
index 0000000..486f8fe
--- /dev/null
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -0,0 +1,1416 @@
+/*
+ * Elan Microelectronics touch panels with I2C interface
+ *
+ * Copyright (C) 2014 Elan Microelectronics Corporation.
+ * Scott Liu <scott.liu@emc.com.tw>
+ *
+ * This code is partly based on hid-multitouch.c:
+ *
+ *  Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr>
+ *  Copyright (c) 2010-2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ *  Copyright (c) 2010-2012 Ecole Nationale de l'Aviation Civile, France
+ *
+ *
+ * This code is partly based on i2c-hid.c:
+ *
+ * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
+ * Copyright (c) 2012 Red Hat, Inc
+ */
+
+/*
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/async.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/buffer_head.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+
+/* Device, Driver information */
+#define DEVICE_NAME	"elants_i2c"
+#define DRV_VERSION	"1.0.9"
+
+/* Convert from rows or columns into resolution */
+#define ELAN_TS_RESOLUTION(n, m)   (((n) - 1) * (m))
+
+/* FW header data */
+#define HEADER_SIZE		4
+#define FW_HDR_TYPE		0
+#define FW_HDR_COUNT		1
+#define FW_HDR_LENGTH		2
+
+/* Buffer mode Queue Header information */
+#define QUEUE_HEADER_SINGLE	0x62
+#define QUEUE_HEADER_NORMAL	0X63
+#define QUEUE_HEADER_WAIT	0x64
+
+/* Command header definition */
+#define CMD_HEADER_WRITE	0x54
+#define CMD_HEADER_READ		0x53
+#define CMD_HEADER_6B_READ	0x5B
+#define CMD_HEADER_RESP		0x52
+#define CMD_HEADER_6B_RESP	0x9B
+#define CMD_HEADER_HELLO	0x55
+#define CMD_HEADER_REK		0x66
+
+/* FW position data */
+#define PACKET_SIZE		55
+#define MAX_CONTACT_NUM		10
+#define FW_POS_HEADER		0
+#define FW_POS_STATE		1
+#define FW_POS_TOTAL		2
+#define FW_POS_XY		3
+#define FW_POS_CHECKSUM		34
+#define FW_POS_WIDTH		35
+#define FW_POS_PRESSURE		45
+
+#define HEADER_REPORT_10_FINGER	0x62
+
+/* Header (4 bytes) plus 3 fill 10-finger packets */
+#define MAX_PACKET_SIZE		169
+
+#define BOOT_TIME_DELAY_MS	50
+
+/* FW read command, 0x53 0x?? 0x0, 0x01 */
+#define E_ELAN_INFO_FW_VER	0x00
+#define E_ELAN_INFO_BC_VER	0x10
+#define E_ELAN_INFO_TEST_VER	0xE0
+#define E_ELAN_INFO_FW_ID	0xF0
+#define E_INFO_OSR		0xD6
+#define E_INFO_PHY_SCAN		0xD7
+#define E_INFO_PHY_DRIVER	0xD8
+
+#define MAX_RETRIES		3
+#define MAX_FW_UPDATE_RETRIES	30
+
+#define ELAN_FW_PAGESIZE	132
+
+/* calibration timeout definition */
+#define ELAN_CALI_TIMEOUT_MSEC	12000
+
+#define ELAN_POWERON_DELAY_USEC	500
+#define ELAN_RESET_DELAY_MSEC	20
+
+enum elants_state {
+	ELAN_STATE_NORMAL,
+	ELAN_WAIT_QUEUE_HEADER,
+	ELAN_WAIT_RECALIBRATION,
+};
+
+enum elants_iap_mode {
+	ELAN_IAP_OPERATIONAL,
+	ELAN_IAP_RECOVERY,
+};
+
+/* struct elants_data - represents state of Elan touchscreen device */
+struct elants_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	struct regulator *vcc33;
+	struct regulator *vccio;
+	struct gpio_desc *reset_gpio;
+
+	u16 fw_version;
+	u8 test_version;
+	u8 solution_version;
+	u8 bc_version;
+	u8 iap_version;
+	u16 hw_version;
+	unsigned int x_res;	/* resolution in units/mm */
+	unsigned int y_res;
+	unsigned int x_max;
+	unsigned int y_max;
+
+	enum elants_state state;
+	enum elants_iap_mode iap_mode;
+
+	/* Guards against concurrent access to the device via sysfs */
+	struct mutex sysfs_mutex;
+
+	u8 cmd_resp[HEADER_SIZE];
+	struct completion cmd_done;
+
+	u8 buf[MAX_PACKET_SIZE];
+
+	bool wake_irq_enabled;
+	bool keep_power_in_suspend;
+};
+
+static int elants_i2c_send(struct i2c_client *client,
+			   const void *data, size_t size)
+{
+	int ret;
+
+	ret = i2c_master_send(client, data, size);
+	if (ret == size)
+		return 0;
+
+	if (ret >= 0)
+		ret = -EIO;
+
+	dev_err(&client->dev, "%s failed (%*ph): %d\n",
+		__func__, (int)size, data, ret);
+
+	return ret;
+}
+
+static int elants_i2c_read(struct i2c_client *client, void *data, size_t size)
+{
+	int ret;
+
+	ret = i2c_master_recv(client, data, size);
+	if (ret == size)
+		return 0;
+
+	if (ret >= 0)
+		ret = -EIO;
+
+	dev_err(&client->dev, "%s failed: %d\n", __func__, ret);
+
+	return ret;
+}
+
+static int elants_i2c_execute_command(struct i2c_client *client,
+				      const u8 *cmd, size_t cmd_size,
+				      u8 *resp, size_t resp_size)
+{
+	struct i2c_msg msgs[2];
+	int ret;
+	u8 expected_response;
+
+	switch (cmd[0]) {
+	case CMD_HEADER_READ:
+		expected_response = CMD_HEADER_RESP;
+		break;
+
+	case CMD_HEADER_6B_READ:
+		expected_response = CMD_HEADER_6B_RESP;
+		break;
+
+	default:
+		dev_err(&client->dev, "%s: invalid command %*ph\n",
+			__func__, (int)cmd_size, cmd);
+		return -EINVAL;
+	}
+
+	msgs[0].addr = client->addr;
+	msgs[0].flags = client->flags & I2C_M_TEN;
+	msgs[0].len = cmd_size;
+	msgs[0].buf = (u8 *)cmd;
+
+	msgs[1].addr = client->addr;
+	msgs[1].flags = client->flags & I2C_M_TEN;
+	msgs[1].flags |= I2C_M_RD;
+	msgs[1].len = resp_size;
+	msgs[1].buf = resp;
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret < 0)
+		return ret;
+
+	if (ret != ARRAY_SIZE(msgs) || resp[FW_HDR_TYPE] != expected_response)
+		return -EIO;
+
+	return 0;
+}
+
+static int elants_i2c_calibrate(struct elants_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int ret, error;
+	static const u8 w_flashkey[] = { 0x54, 0xC0, 0xE1, 0x5A };
+	static const u8 rek[] = { 0x54, 0x29, 0x00, 0x01 };
+	static const u8 rek_resp[] = { CMD_HEADER_REK, 0x66, 0x66, 0x66 };
+
+	disable_irq(client->irq);
+
+	ts->state = ELAN_WAIT_RECALIBRATION;
+	reinit_completion(&ts->cmd_done);
+
+	elants_i2c_send(client, w_flashkey, sizeof(w_flashkey));
+	elants_i2c_send(client, rek, sizeof(rek));
+
+	enable_irq(client->irq);
+
+	ret = wait_for_completion_interruptible_timeout(&ts->cmd_done,
+				msecs_to_jiffies(ELAN_CALI_TIMEOUT_MSEC));
+
+	ts->state = ELAN_STATE_NORMAL;
+
+	if (ret <= 0) {
+		error = ret < 0 ? ret : -ETIMEDOUT;
+		dev_err(&client->dev,
+			"error while waiting for calibration to complete: %d\n",
+			error);
+		return error;
+	}
+
+	if (memcmp(rek_resp, ts->cmd_resp, sizeof(rek_resp))) {
+		dev_err(&client->dev,
+			"unexpected calibration response: %*ph\n",
+			(int)sizeof(ts->cmd_resp), ts->cmd_resp);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int elants_i2c_sw_reset(struct i2c_client *client)
+{
+	const u8 soft_rst_cmd[] = { 0x77, 0x77, 0x77, 0x77 };
+	int error;
+
+	error = elants_i2c_send(client, soft_rst_cmd,
+				sizeof(soft_rst_cmd));
+	if (error) {
+		dev_err(&client->dev, "software reset failed: %d\n", error);
+		return error;
+	}
+
+	/*
+	 * We should wait at least 10 msec (but no more than 40) before
+	 * sending fastboot or IAP command to the device.
+	 */
+	msleep(30);
+
+	return 0;
+}
+
+static u16 elants_i2c_parse_version(u8 *buf)
+{
+	return get_unaligned_be32(buf) >> 4;
+}
+
+static int elants_i2c_query_fw_id(struct elants_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+	const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_FW_ID, 0x00, 0x01 };
+	u8 resp[HEADER_SIZE];
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
+						   resp, sizeof(resp));
+		if (!error) {
+			ts->hw_version = elants_i2c_parse_version(resp);
+			if (ts->hw_version != 0xffff)
+				return 0;
+		}
+
+		dev_dbg(&client->dev, "read fw id error=%d, buf=%*phC\n",
+			error, (int)sizeof(resp), resp);
+	}
+
+	dev_err(&client->dev,
+		"Failed to read fw id or fw id is invalid\n");
+
+	return -EINVAL;
+}
+
+static int elants_i2c_query_fw_version(struct elants_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+	const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_FW_VER, 0x00, 0x01 };
+	u8 resp[HEADER_SIZE];
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
+						   resp, sizeof(resp));
+		if (!error) {
+			ts->fw_version = elants_i2c_parse_version(resp);
+			if (ts->fw_version != 0x0000 &&
+			    ts->fw_version != 0xffff)
+				return 0;
+		}
+
+		dev_dbg(&client->dev, "read fw version error=%d, buf=%*phC\n",
+			error, (int)sizeof(resp), resp);
+	}
+
+	dev_err(&client->dev,
+		"Failed to read fw version or fw version is invalid\n");
+
+	return -EINVAL;
+}
+
+static int elants_i2c_query_test_version(struct elants_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+	u16 version;
+	const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_TEST_VER, 0x00, 0x01 };
+	u8 resp[HEADER_SIZE];
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
+						   resp, sizeof(resp));
+		if (!error) {
+			version = elants_i2c_parse_version(resp);
+			ts->test_version = version >> 8;
+			ts->solution_version = version & 0xff;
+
+			return 0;
+		}
+
+		dev_dbg(&client->dev,
+			"read test version error rc=%d, buf=%*phC\n",
+			error, (int)sizeof(resp), resp);
+	}
+
+	dev_err(&client->dev, "Failed to read test version\n");
+
+	return -EINVAL;
+}
+
+static int elants_i2c_query_bc_version(struct elants_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_BC_VER, 0x00, 0x01 };
+	u8 resp[HEADER_SIZE];
+	u16 version;
+	int error;
+
+	error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
+					   resp, sizeof(resp));
+	if (error) {
+		dev_err(&client->dev,
+			"read BC version error=%d, buf=%*phC\n",
+			error, (int)sizeof(resp), resp);
+		return error;
+	}
+
+	version = elants_i2c_parse_version(resp);
+	ts->bc_version = version >> 8;
+	ts->iap_version = version & 0xff;
+
+	return 0;
+}
+
+static int elants_i2c_query_ts_info(struct elants_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error;
+	u8 resp[17];
+	u16 phy_x, phy_y, rows, cols, osr;
+	const u8 get_resolution_cmd[] = {
+		CMD_HEADER_6B_READ, 0x00, 0x00, 0x00, 0x00, 0x00
+	};
+	const u8 get_osr_cmd[] = {
+		CMD_HEADER_READ, E_INFO_OSR, 0x00, 0x01
+	};
+	const u8 get_physical_scan_cmd[] = {
+		CMD_HEADER_READ, E_INFO_PHY_SCAN, 0x00, 0x01
+	};
+	const u8 get_physical_drive_cmd[] = {
+		CMD_HEADER_READ, E_INFO_PHY_DRIVER, 0x00, 0x01
+	};
+
+	/* Get trace number */
+	error = elants_i2c_execute_command(client,
+					   get_resolution_cmd,
+					   sizeof(get_resolution_cmd),
+					   resp, sizeof(resp));
+	if (error) {
+		dev_err(&client->dev, "get resolution command failed: %d\n",
+			error);
+		return error;
+	}
+
+	rows = resp[2] + resp[6] + resp[10];
+	cols = resp[3] + resp[7] + resp[11];
+
+	/* Process mm_to_pixel information */
+	error = elants_i2c_execute_command(client,
+					   get_osr_cmd, sizeof(get_osr_cmd),
+					   resp, sizeof(resp));
+	if (error) {
+		dev_err(&client->dev, "get osr command failed: %d\n",
+			error);
+		return error;
+	}
+
+	osr = resp[3];
+
+	error = elants_i2c_execute_command(client,
+					   get_physical_scan_cmd,
+					   sizeof(get_physical_scan_cmd),
+					   resp, sizeof(resp));
+	if (error) {
+		dev_err(&client->dev, "get physical scan command failed: %d\n",
+			error);
+		return error;
+	}
+
+	phy_x = get_unaligned_be16(&resp[2]);
+
+	error = elants_i2c_execute_command(client,
+					   get_physical_drive_cmd,
+					   sizeof(get_physical_drive_cmd),
+					   resp, sizeof(resp));
+	if (error) {
+		dev_err(&client->dev, "get physical drive command failed: %d\n",
+			error);
+		return error;
+	}
+
+	phy_y = get_unaligned_be16(&resp[2]);
+
+	dev_dbg(&client->dev, "phy_x=%d, phy_y=%d\n", phy_x, phy_y);
+
+	if (rows == 0 || cols == 0 || osr == 0) {
+		dev_warn(&client->dev,
+			 "invalid trace number data: %d, %d, %d\n",
+			 rows, cols, osr);
+	} else {
+		/* translate trace number to TS resolution */
+		ts->x_max = ELAN_TS_RESOLUTION(rows, osr);
+		ts->x_res = DIV_ROUND_CLOSEST(ts->x_max, phy_x);
+		ts->y_max = ELAN_TS_RESOLUTION(cols, osr);
+		ts->y_res = DIV_ROUND_CLOSEST(ts->y_max, phy_y);
+	}
+
+	return 0;
+}
+
+static int elants_i2c_fastboot(struct i2c_client *client)
+{
+	const u8 boot_cmd[] = { 0x4D, 0x61, 0x69, 0x6E };
+	int error;
+
+	error = elants_i2c_send(client, boot_cmd, sizeof(boot_cmd));
+	if (error) {
+		dev_err(&client->dev, "boot failed: %d\n", error);
+		return error;
+	}
+
+	dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr);
+	return 0;
+}
+
+static int elants_i2c_initialize(struct elants_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	int error, retry_cnt;
+	const u8 hello_packet[] = { 0x55, 0x55, 0x55, 0x55 };
+	const u8 recov_packet[] = { 0x55, 0x55, 0x80, 0x80 };
+	u8 buf[HEADER_SIZE];
+
+	for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+		error = elants_i2c_sw_reset(client);
+		if (error) {
+			/* Continue initializing if it's the last try */
+			if (retry_cnt < MAX_RETRIES - 1)
+				continue;
+		}
+
+		error = elants_i2c_fastboot(client);
+		if (error) {
+			/* Continue initializing if it's the last try */
+			if (retry_cnt < MAX_RETRIES - 1)
+				continue;
+		}
+
+		/* Wait for Hello packet */
+		msleep(BOOT_TIME_DELAY_MS);
+
+		error = elants_i2c_read(client, buf, sizeof(buf));
+		if (error) {
+			dev_err(&client->dev,
+				"failed to read 'hello' packet: %d\n", error);
+		} else if (!memcmp(buf, hello_packet, sizeof(hello_packet))) {
+			ts->iap_mode = ELAN_IAP_OPERATIONAL;
+			break;
+		} else if (!memcmp(buf, recov_packet, sizeof(recov_packet))) {
+			/*
+			 * Setting error code will mark device
+			 * in recovery mode below.
+			 */
+			error = -EIO;
+			break;
+		} else {
+			error = -EINVAL;
+			dev_err(&client->dev,
+				"invalid 'hello' packet: %*ph\n",
+				(int)sizeof(buf), buf);
+		}
+	}
+
+	if (!error)
+		error = elants_i2c_query_fw_id(ts);
+	if (!error)
+		error = elants_i2c_query_fw_version(ts);
+
+	if (error) {
+		ts->iap_mode = ELAN_IAP_RECOVERY;
+	} else {
+		elants_i2c_query_test_version(ts);
+		elants_i2c_query_bc_version(ts);
+		elants_i2c_query_ts_info(ts);
+	}
+
+	return 0;
+}
+
+/*
+ * Firmware update interface.
+ */
+
+static int elants_i2c_fw_write_page(struct i2c_client *client,
+				    const void *page)
+{
+	const u8 ack_ok[] = { 0xaa, 0xaa };
+	u8 buf[2];
+	int retry;
+	int error;
+
+	for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) {
+		error = elants_i2c_send(client, page, ELAN_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"IAP Write Page failed: %d\n", error);
+			continue;
+		}
+
+		error = elants_i2c_read(client, buf, 2);
+		if (error) {
+			dev_err(&client->dev,
+				"IAP Ack read failed: %d\n", error);
+			return error;
+		}
+
+		if (!memcmp(buf, ack_ok, sizeof(ack_ok)))
+			return 0;
+
+		error = -EIO;
+		dev_err(&client->dev,
+			"IAP Get Ack Error [%02x:%02x]\n",
+			buf[0], buf[1]);
+	}
+
+	return error;
+}
+
+static int elants_i2c_do_update_firmware(struct i2c_client *client,
+					 const struct firmware *fw,
+					 bool force)
+{
+	const u8 enter_iap[] = { 0x45, 0x49, 0x41, 0x50 };
+	const u8 enter_iap2[] = { 0x54, 0x00, 0x12, 0x34 };
+	const u8 iap_ack[] = { 0x55, 0xaa, 0x33, 0xcc };
+	const u8 close_idle[] = {0x54, 0x2c, 0x01, 0x01};
+	u8 buf[HEADER_SIZE];
+	u16 send_id;
+	int page, n_fw_pages;
+	int error;
+
+	/* Recovery mode detection! */
+	if (force) {
+		dev_dbg(&client->dev, "Recovery mode procedure\n");
+		error = elants_i2c_send(client, enter_iap2, sizeof(enter_iap2));
+	} else {
+		/* Start IAP Procedure */
+		dev_dbg(&client->dev, "Normal IAP procedure\n");
+		/* Close idle mode */
+		error = elants_i2c_send(client, close_idle, sizeof(close_idle));
+		if (error)
+			dev_err(&client->dev, "Failed close idle: %d\n", error);
+		msleep(60);
+		elants_i2c_sw_reset(client);
+		msleep(20);
+		error = elants_i2c_send(client, enter_iap, sizeof(enter_iap));
+	}
+
+	if (error) {
+		dev_err(&client->dev, "failed to enter IAP mode: %d\n", error);
+		return error;
+	}
+
+	msleep(20);
+
+	/* check IAP state */
+	error = elants_i2c_read(client, buf, 4);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to read IAP acknowledgement: %d\n",
+			error);
+		return error;
+	}
+
+	if (memcmp(buf, iap_ack, sizeof(iap_ack))) {
+		dev_err(&client->dev,
+			"failed to enter IAP: %*ph (expected %*ph)\n",
+			(int)sizeof(buf), buf, (int)sizeof(iap_ack), iap_ack);
+		return -EIO;
+	}
+
+	dev_info(&client->dev, "successfully entered IAP mode");
+
+	send_id = client->addr;
+	error = elants_i2c_send(client, &send_id, 1);
+	if (error) {
+		dev_err(&client->dev, "sending dummy byte failed: %d\n",
+			error);
+		return error;
+	}
+
+	/* Clear the last page of Master */
+	error = elants_i2c_send(client, fw->data, ELAN_FW_PAGESIZE);
+	if (error) {
+		dev_err(&client->dev, "clearing of the last page failed: %d\n",
+			error);
+		return error;
+	}
+
+	error = elants_i2c_read(client, buf, 2);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to read ACK for clearing the last page: %d\n",
+			error);
+		return error;
+	}
+
+	n_fw_pages = fw->size / ELAN_FW_PAGESIZE;
+	dev_dbg(&client->dev, "IAP Pages = %d\n", n_fw_pages);
+
+	for (page = 0; page < n_fw_pages; page++) {
+		error = elants_i2c_fw_write_page(client,
+					fw->data + page * ELAN_FW_PAGESIZE);
+		if (error) {
+			dev_err(&client->dev,
+				"failed to write FW page %d: %d\n",
+				page, error);
+			return error;
+		}
+	}
+
+	/* Old iap needs to wait 200ms for WDT and rest is for hello packets */
+	msleep(300);
+
+	dev_info(&client->dev, "firmware update completed\n");
+	return 0;
+}
+
+static int elants_i2c_fw_update(struct elants_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	const struct firmware *fw;
+	char *fw_name;
+	int error;
+
+	fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%04x.bin", ts->hw_version);
+	if (!fw_name)
+		return -ENOMEM;
+
+	dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
+	error = request_firmware(&fw, fw_name, &client->dev);
+	kfree(fw_name);
+	if (error) {
+		dev_err(&client->dev, "failed to request firmware: %d\n",
+			error);
+		return error;
+	}
+
+	if (fw->size % ELAN_FW_PAGESIZE) {
+		dev_err(&client->dev, "invalid firmware length: %zu\n",
+			fw->size);
+		error = -EINVAL;
+		goto out;
+	}
+
+	disable_irq(client->irq);
+
+	error = elants_i2c_do_update_firmware(client, fw,
+					ts->iap_mode == ELAN_IAP_RECOVERY);
+	if (error) {
+		dev_err(&client->dev, "firmware update failed: %d\n", error);
+		ts->iap_mode = ELAN_IAP_RECOVERY;
+		goto out_enable_irq;
+	}
+
+	error = elants_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize device after firmware update: %d\n",
+			error);
+		ts->iap_mode = ELAN_IAP_RECOVERY;
+		goto out_enable_irq;
+	}
+
+	ts->iap_mode = ELAN_IAP_OPERATIONAL;
+
+out_enable_irq:
+	ts->state = ELAN_STATE_NORMAL;
+	enable_irq(client->irq);
+	msleep(100);
+
+	if (!error)
+		elants_i2c_calibrate(ts);
+out:
+	release_firmware(fw);
+	return error;
+}
+
+/*
+ * Event reporting.
+ */
+
+static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf)
+{
+	struct input_dev *input = ts->input;
+	unsigned int n_fingers;
+	u16 finger_state;
+	int i;
+
+	n_fingers = buf[FW_POS_STATE + 1] & 0x0f;
+	finger_state = ((buf[FW_POS_STATE + 1] & 0x30) << 4) |
+			buf[FW_POS_STATE];
+
+	dev_dbg(&ts->client->dev,
+		"n_fingers: %u, state: %04x\n",  n_fingers, finger_state);
+
+	for (i = 0; i < MAX_CONTACT_NUM && n_fingers; i++) {
+		if (finger_state & 1) {
+			unsigned int x, y, p, w;
+			u8 *pos;
+
+			pos = &buf[FW_POS_XY + i * 3];
+			x = (((u16)pos[0] & 0xf0) << 4) | pos[1];
+			y = (((u16)pos[0] & 0x0f) << 8) | pos[2];
+			p = buf[FW_POS_PRESSURE + i];
+			w = buf[FW_POS_WIDTH + i];
+
+			dev_dbg(&ts->client->dev, "i=%d x=%d y=%d p=%d w=%d\n",
+				i, x, y, p, w);
+
+			input_mt_slot(input, i);
+			input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+			input_event(input, EV_ABS, ABS_MT_POSITION_X, x);
+			input_event(input, EV_ABS, ABS_MT_POSITION_Y, y);
+			input_event(input, EV_ABS, ABS_MT_PRESSURE, p);
+			input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, w);
+
+			n_fingers--;
+		}
+
+		finger_state >>= 1;
+	}
+
+	input_mt_sync_frame(input);
+	input_sync(input);
+}
+
+static u8 elants_i2c_calculate_checksum(u8 *buf)
+{
+	u8 checksum = 0;
+	u8 i;
+
+	for (i = 0; i < FW_POS_CHECKSUM; i++)
+		checksum += buf[i];
+
+	return checksum;
+}
+
+static void elants_i2c_event(struct elants_data *ts, u8 *buf)
+{
+	u8 checksum = elants_i2c_calculate_checksum(buf);
+
+	if (unlikely(buf[FW_POS_CHECKSUM] != checksum))
+		dev_warn(&ts->client->dev,
+			 "%s: invalid checksum for packet %02x: %02x vs. %02x\n",
+			 __func__, buf[FW_POS_HEADER],
+			 checksum, buf[FW_POS_CHECKSUM]);
+	else if (unlikely(buf[FW_POS_HEADER] != HEADER_REPORT_10_FINGER))
+		dev_warn(&ts->client->dev,
+			 "%s: unknown packet type: %02x\n",
+			 __func__, buf[FW_POS_HEADER]);
+	else
+		elants_i2c_mt_event(ts, buf);
+}
+
+static irqreturn_t elants_i2c_irq(int irq, void *_dev)
+{
+	const u8 wait_packet[] = { 0x64, 0x64, 0x64, 0x64 };
+	struct elants_data *ts = _dev;
+	struct i2c_client *client = ts->client;
+	int report_count, report_len;
+	int i;
+	int len;
+
+	len = i2c_master_recv(client, ts->buf, sizeof(ts->buf));
+	if (len < 0) {
+		dev_err(&client->dev, "%s: failed to read data: %d\n",
+			__func__, len);
+		goto out;
+	}
+
+	dev_dbg(&client->dev, "%s: packet %*ph\n",
+		__func__, HEADER_SIZE, ts->buf);
+
+	switch (ts->state) {
+	case ELAN_WAIT_RECALIBRATION:
+		if (ts->buf[FW_HDR_TYPE] == CMD_HEADER_REK) {
+			memcpy(ts->cmd_resp, ts->buf, sizeof(ts->cmd_resp));
+			complete(&ts->cmd_done);
+			ts->state = ELAN_STATE_NORMAL;
+		}
+		break;
+
+	case ELAN_WAIT_QUEUE_HEADER:
+		if (ts->buf[FW_HDR_TYPE] != QUEUE_HEADER_NORMAL)
+			break;
+
+		ts->state = ELAN_STATE_NORMAL;
+		/* fall through */
+
+	case ELAN_STATE_NORMAL:
+
+		switch (ts->buf[FW_HDR_TYPE]) {
+		case CMD_HEADER_HELLO:
+		case CMD_HEADER_RESP:
+		case CMD_HEADER_REK:
+			break;
+
+		case QUEUE_HEADER_WAIT:
+			if (memcmp(ts->buf, wait_packet, sizeof(wait_packet))) {
+				dev_err(&client->dev,
+					"invalid wait packet %*ph\n",
+					HEADER_SIZE, ts->buf);
+			} else {
+				ts->state = ELAN_WAIT_QUEUE_HEADER;
+				udelay(30);
+			}
+			break;
+
+		case QUEUE_HEADER_SINGLE:
+			elants_i2c_event(ts, &ts->buf[HEADER_SIZE]);
+			break;
+
+		case QUEUE_HEADER_NORMAL:
+			report_count = ts->buf[FW_HDR_COUNT];
+			if (report_count == 0 || report_count > 3) {
+				dev_err(&client->dev,
+					"bad report count: %*ph\n",
+					HEADER_SIZE, ts->buf);
+				break;
+			}
+
+			report_len = ts->buf[FW_HDR_LENGTH] / report_count;
+			if (report_len != PACKET_SIZE) {
+				dev_err(&client->dev,
+					"mismatching report length: %*ph\n",
+					HEADER_SIZE, ts->buf);
+				break;
+			}
+
+			for (i = 0; i < report_count; i++) {
+				u8 *buf = ts->buf + HEADER_SIZE +
+							i * PACKET_SIZE;
+				elants_i2c_event(ts, buf);
+			}
+			break;
+
+		default:
+			dev_err(&client->dev, "unknown packet %*ph\n",
+				HEADER_SIZE, ts->buf);
+			break;
+		}
+		break;
+	}
+
+out:
+	return IRQ_HANDLED;
+}
+
+/*
+ * sysfs interface
+ */
+static ssize_t calibrate_store(struct device *dev,
+			       struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elants_data *ts = i2c_get_clientdata(client);
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = elants_i2c_calibrate(ts);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static ssize_t write_update_fw(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elants_data *ts = i2c_get_clientdata(client);
+	int error;
+
+	error = mutex_lock_interruptible(&ts->sysfs_mutex);
+	if (error)
+		return error;
+
+	error = elants_i2c_fw_update(ts);
+	dev_dbg(dev, "firmware update result: %d\n", error);
+
+	mutex_unlock(&ts->sysfs_mutex);
+	return error ?: count;
+}
+
+static ssize_t show_iap_mode(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elants_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%s\n",
+		       ts->iap_mode == ELAN_IAP_OPERATIONAL ?
+				"Normal" : "Recovery");
+}
+
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, calibrate_store);
+static DEVICE_ATTR(iap_mode, S_IRUGO, show_iap_mode, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw);
+
+struct elants_version_attribute {
+	struct device_attribute dattr;
+	size_t field_offset;
+	size_t field_size;
+};
+
+#define __ELANTS_FIELD_SIZE(_field)					\
+	sizeof(((struct elants_data *)NULL)->_field)
+#define __ELANTS_VERIFY_SIZE(_field)					\
+	(BUILD_BUG_ON_ZERO(__ELANTS_FIELD_SIZE(_field) > 2) +		\
+	 __ELANTS_FIELD_SIZE(_field))
+#define ELANTS_VERSION_ATTR(_field)					\
+	struct elants_version_attribute elants_ver_attr_##_field = {	\
+		.dattr = __ATTR(_field, S_IRUGO,			\
+				elants_version_attribute_show, NULL),	\
+		.field_offset = offsetof(struct elants_data, _field),	\
+		.field_size = __ELANTS_VERIFY_SIZE(_field),		\
+	}
+
+static ssize_t elants_version_attribute_show(struct device *dev,
+					     struct device_attribute *dattr,
+					     char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elants_data *ts = i2c_get_clientdata(client);
+	struct elants_version_attribute *attr =
+		container_of(dattr, struct elants_version_attribute, dattr);
+	u8 *field = (u8 *)((char *)ts + attr->field_offset);
+	unsigned int fmt_size;
+	unsigned int val;
+
+	if (attr->field_size == 1) {
+		val = *field;
+		fmt_size = 2; /* 2 HEX digits */
+	} else {
+		val = *(u16 *)field;
+		fmt_size = 4; /* 4 HEX digits */
+	}
+
+	return sprintf(buf, "%0*x\n", fmt_size, val);
+}
+
+static ELANTS_VERSION_ATTR(fw_version);
+static ELANTS_VERSION_ATTR(hw_version);
+static ELANTS_VERSION_ATTR(test_version);
+static ELANTS_VERSION_ATTR(solution_version);
+static ELANTS_VERSION_ATTR(bc_version);
+static ELANTS_VERSION_ATTR(iap_version);
+
+static struct attribute *elants_attributes[] = {
+	&dev_attr_calibrate.attr,
+	&dev_attr_update_fw.attr,
+	&dev_attr_iap_mode.attr,
+
+	&elants_ver_attr_fw_version.dattr.attr,
+	&elants_ver_attr_hw_version.dattr.attr,
+	&elants_ver_attr_test_version.dattr.attr,
+	&elants_ver_attr_solution_version.dattr.attr,
+	&elants_ver_attr_bc_version.dattr.attr,
+	&elants_ver_attr_iap_version.dattr.attr,
+	NULL
+};
+
+static struct attribute_group elants_attribute_group = {
+	.attrs = elants_attributes,
+};
+
+static void elants_i2c_remove_sysfs_group(void *_data)
+{
+	struct elants_data *ts = _data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &elants_attribute_group);
+}
+
+static int elants_i2c_power_on(struct elants_data *ts)
+{
+	int error;
+
+	/*
+	 * If we do not have reset gpio assume platform firmware
+	 * controls regulators and does power them on for us.
+	 */
+	if (IS_ERR_OR_NULL(ts->reset_gpio))
+		return 0;
+
+	gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+	error = regulator_enable(ts->vcc33);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable vcc33 regulator: %d\n",
+			error);
+		goto release_reset_gpio;
+	}
+
+	error = regulator_enable(ts->vccio);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"failed to enable vccio regulator: %d\n",
+			error);
+		regulator_disable(ts->vcc33);
+		goto release_reset_gpio;
+	}
+
+	/*
+	 * We need to wait a bit after powering on controller before
+	 * we are allowed to release reset GPIO.
+	 */
+	udelay(ELAN_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+	gpiod_set_value_cansleep(ts->reset_gpio, 0);
+	if (error)
+		return error;
+
+	msleep(ELAN_RESET_DELAY_MSEC);
+
+	return 0;
+}
+
+static void elants_i2c_power_off(void *_data)
+{
+	struct elants_data *ts = _data;
+
+	if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
+		/*
+		 * Activate reset gpio to prevent leakage through the
+		 * pin once we shut off power to the controller.
+		 */
+		gpiod_set_value_cansleep(ts->reset_gpio, 1);
+		regulator_disable(ts->vccio);
+		regulator_disable(ts->vcc33);
+	}
+}
+
+static int elants_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	union i2c_smbus_data dummy;
+	struct elants_data *ts;
+	unsigned long irqflags;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"%s: i2c check functionality error\n", DEVICE_NAME);
+		return -ENXIO;
+	}
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct elants_data), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	mutex_init(&ts->sysfs_mutex);
+	init_completion(&ts->cmd_done);
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	ts->vcc33 = devm_regulator_get(&client->dev, "vcc33");
+	if (IS_ERR(ts->vcc33)) {
+		error = PTR_ERR(ts->vcc33);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vcc33' regulator: %d\n",
+				error);
+		return error;
+	}
+
+	ts->vccio = devm_regulator_get(&client->dev, "vccio");
+	if (IS_ERR(ts->vccio)) {
+		error = PTR_ERR(ts->vccio);
+		if (error != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Failed to get 'vccio' regulator: %d\n",
+				error);
+		return error;
+	}
+
+	ts->reset_gpio = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(ts->reset_gpio)) {
+		error = PTR_ERR(ts->reset_gpio);
+
+		if (error == -EPROBE_DEFER)
+			return error;
+
+		if (error != -ENOENT && error != -ENOSYS) {
+			dev_err(&client->dev,
+				"failed to get reset gpio: %d\n",
+				error);
+			return error;
+		}
+
+		ts->keep_power_in_suspend = true;
+	}
+
+	error = elants_i2c_power_on(ts);
+	if (error)
+		return error;
+
+	error = devm_add_action(&client->dev, elants_i2c_power_off, ts);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to install power off action: %d\n", error);
+		elants_i2c_power_off(ts);
+		return error;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			   I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+		dev_err(&client->dev, "nothing at this address\n");
+		return -ENXIO;
+	}
+
+	error = elants_i2c_initialize(ts);
+	if (error) {
+		dev_err(&client->dev, "failed to initialize: %d\n", error);
+		return error;
+	}
+
+	ts->input = devm_input_allocate_device(&client->dev);
+	if (!ts->input) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->input->name = "Elan Touchscreen";
+	ts->input->id.bustype = BUS_I2C;
+
+	__set_bit(BTN_TOUCH, ts->input->keybit);
+	__set_bit(EV_ABS, ts->input->evbit);
+	__set_bit(EV_KEY, ts->input->evbit);
+
+	/* Single touch input params setup */
+	input_set_abs_params(ts->input, ABS_X, 0, ts->x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_Y, 0, ts->y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_X, ts->x_res);
+	input_abs_set_res(ts->input, ABS_Y, ts->y_res);
+
+	/* Multitouch input params setup */
+	error = input_mt_init_slots(ts->input, MAX_CONTACT_NUM,
+				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to initialize MT slots: %d\n", error);
+		return error;
+	}
+
+	input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->x_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->y_max, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res);
+	input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res);
+
+	input_set_drvdata(ts->input, ts);
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&client->dev,
+			"unable to register input device: %d\n", error);
+		return error;
+	}
+
+	/*
+	 * Systems using device tree should set up interrupt via DTS,
+	 * the rest will use the default falling edge interrupts.
+	 */
+	irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING;
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, elants_i2c_irq,
+					  irqflags | IRQF_ONESHOT,
+					  client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	/*
+	 * Systems using device tree should set up wakeup via DTS,
+	 * the rest will configure device as wakeup source by default.
+	 */
+	if (!client->dev.of_node)
+		device_init_wakeup(&client->dev, true);
+
+	error = sysfs_create_group(&client->dev.kobj, &elants_attribute_group);
+	if (error) {
+		dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+			error);
+		return error;
+	}
+
+	error = devm_add_action(&client->dev,
+				elants_i2c_remove_sysfs_group, ts);
+	if (error) {
+		elants_i2c_remove_sysfs_group(ts);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused elants_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elants_data *ts = i2c_get_clientdata(client);
+	const u8 set_sleep_cmd[] = { 0x54, 0x50, 0x00, 0x01 };
+	int retry_cnt;
+	int error;
+
+	/* Command not support in IAP recovery mode */
+	if (ts->iap_mode != ELAN_IAP_OPERATIONAL)
+		return -EBUSY;
+
+	disable_irq(client->irq);
+
+	if (device_may_wakeup(dev)) {
+		/*
+		 * The device will automatically enter idle mode
+		 * that has reduced power consumption.
+		 */
+		ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+	} else if (ts->keep_power_in_suspend) {
+		for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+			error = elants_i2c_send(client, set_sleep_cmd,
+						sizeof(set_sleep_cmd));
+			if (!error)
+				break;
+
+			dev_err(&client->dev,
+				"suspend command failed: %d\n", error);
+		}
+	} else {
+		elants_i2c_power_off(ts);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused elants_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct elants_data *ts = i2c_get_clientdata(client);
+	const u8 set_active_cmd[] = { 0x54, 0x58, 0x00, 0x01 };
+	int retry_cnt;
+	int error;
+
+	if (device_may_wakeup(dev)) {
+		if (ts->wake_irq_enabled)
+			disable_irq_wake(client->irq);
+		elants_i2c_sw_reset(client);
+	} else if (ts->keep_power_in_suspend) {
+		for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
+			error = elants_i2c_send(client, set_active_cmd,
+						sizeof(set_active_cmd));
+			if (!error)
+				break;
+
+			dev_err(&client->dev,
+				"resume command failed: %d\n", error);
+		}
+	} else {
+		elants_i2c_power_on(ts);
+		elants_i2c_initialize(ts);
+	}
+
+	ts->state = ELAN_STATE_NORMAL;
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(elants_i2c_pm_ops,
+			 elants_i2c_suspend, elants_i2c_resume);
+
+static const struct i2c_device_id elants_i2c_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, elants_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id elants_acpi_id[] = {
+	{ "ELAN0001", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, elants_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id elants_of_match[] = {
+	{ .compatible = "elan,ekth3500" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, elants_of_match);
+#endif
+
+static struct i2c_driver elants_i2c_driver = {
+	.probe = elants_i2c_probe,
+	.id_table = elants_i2c_id,
+	.driver = {
+		.name = DEVICE_NAME,
+		.pm = &elants_i2c_pm_ops,
+		.acpi_match_table = ACPI_PTR(elants_acpi_id),
+		.of_match_table = of_match_ptr(elants_of_match),
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+};
+module_i2c_driver(elants_i2c_driver);
+
+MODULE_AUTHOR("Scott Liu <scott.liu@emc.com.tw>");
+MODULE_DESCRIPTION("Elan I2c Touchscreen driver");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c
new file mode 100644
index 0000000..8051a4b
--- /dev/null
+++ b/drivers/input/touchscreen/elo.c
@@ -0,0 +1,407 @@
+/*
+ * Elo serial touchscreen driver
+ *
+ * Copyright (c) 2004 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This driver can handle serial Elo touchscreens using either the Elo standard
+ * 'E271-2210' 10-byte protocol, Elo legacy 'E281A-4002' 6-byte protocol, Elo
+ * legacy 'E271-140' 4-byte protocol and Elo legacy 'E261-280' 3-byte protocol.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/ctype.h>
+
+#define DRIVER_DESC	"Elo serial touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define ELO_MAX_LENGTH		10
+
+#define ELO10_PACKET_LEN	8
+#define ELO10_TOUCH		0x03
+#define ELO10_PRESSURE		0x80
+
+#define ELO10_LEAD_BYTE		'U'
+
+#define ELO10_ID_CMD		'i'
+
+#define ELO10_TOUCH_PACKET	'T'
+#define ELO10_ACK_PACKET	'A'
+#define ELI10_ID_PACKET		'I'
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct elo {
+	struct input_dev *dev;
+	struct serio *serio;
+	struct mutex cmd_mutex;
+	struct completion cmd_done;
+	int id;
+	int idx;
+	unsigned char expected_packet;
+	unsigned char csum;
+	unsigned char data[ELO_MAX_LENGTH];
+	unsigned char response[ELO10_PACKET_LEN];
+	char phys[32];
+};
+
+static void elo_process_data_10(struct elo *elo, unsigned char data)
+{
+	struct input_dev *dev = elo->dev;
+
+	elo->data[elo->idx] = data;
+
+	switch (elo->idx++) {
+	case 0:
+		elo->csum = 0xaa;
+		if (data != ELO10_LEAD_BYTE) {
+			dev_dbg(&elo->serio->dev,
+				"unsynchronized data: 0x%02x\n", data);
+			elo->idx = 0;
+		}
+		break;
+
+	case 9:
+		elo->idx = 0;
+		if (data != elo->csum) {
+			dev_dbg(&elo->serio->dev,
+				"bad checksum: 0x%02x, expected 0x%02x\n",
+				 data, elo->csum);
+			break;
+		}
+		if (elo->data[1] != elo->expected_packet) {
+			if (elo->data[1] != ELO10_TOUCH_PACKET)
+				dev_dbg(&elo->serio->dev,
+					"unexpected packet: 0x%02x\n",
+					 elo->data[1]);
+			break;
+		}
+		if (likely(elo->data[1] == ELO10_TOUCH_PACKET)) {
+			input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]);
+			input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]);
+			if (elo->data[2] & ELO10_PRESSURE)
+				input_report_abs(dev, ABS_PRESSURE,
+						(elo->data[8] << 8) | elo->data[7]);
+			input_report_key(dev, BTN_TOUCH, elo->data[2] & ELO10_TOUCH);
+			input_sync(dev);
+		} else if (elo->data[1] == ELO10_ACK_PACKET) {
+			if (elo->data[2] == '0')
+				elo->expected_packet = ELO10_TOUCH_PACKET;
+			complete(&elo->cmd_done);
+		} else {
+			memcpy(elo->response, &elo->data[1], ELO10_PACKET_LEN);
+			elo->expected_packet = ELO10_ACK_PACKET;
+		}
+		break;
+	}
+	elo->csum += data;
+}
+
+static void elo_process_data_6(struct elo *elo, unsigned char data)
+{
+	struct input_dev *dev = elo->dev;
+
+	elo->data[elo->idx] = data;
+
+	switch (elo->idx++) {
+
+	case 0:
+		if ((data & 0xc0) != 0xc0)
+			elo->idx = 0;
+		break;
+
+	case 1:
+		if ((data & 0xc0) != 0x80)
+			elo->idx = 0;
+		break;
+
+	case 2:
+		if ((data & 0xc0) != 0x40)
+			elo->idx = 0;
+		break;
+
+	case 3:
+		if (data & 0xc0) {
+			elo->idx = 0;
+			break;
+		}
+
+		input_report_abs(dev, ABS_X, ((elo->data[0] & 0x3f) << 6) | (elo->data[1] & 0x3f));
+		input_report_abs(dev, ABS_Y, ((elo->data[2] & 0x3f) << 6) | (elo->data[3] & 0x3f));
+
+		if (elo->id == 2) {
+			input_report_key(dev, BTN_TOUCH, 1);
+			input_sync(dev);
+			elo->idx = 0;
+		}
+
+		break;
+
+	case 4:
+		if (data) {
+			input_sync(dev);
+			elo->idx = 0;
+		}
+		break;
+
+	case 5:
+		if ((data & 0xf0) == 0) {
+			input_report_abs(dev, ABS_PRESSURE, elo->data[5]);
+			input_report_key(dev, BTN_TOUCH, !!elo->data[5]);
+		}
+		input_sync(dev);
+		elo->idx = 0;
+		break;
+	}
+}
+
+static void elo_process_data_3(struct elo *elo, unsigned char data)
+{
+	struct input_dev *dev = elo->dev;
+
+	elo->data[elo->idx] = data;
+
+	switch (elo->idx++) {
+
+	case 0:
+		if ((data & 0x7f) != 0x01)
+			elo->idx = 0;
+		break;
+	case 2:
+		input_report_key(dev, BTN_TOUCH, !(elo->data[1] & 0x80));
+		input_report_abs(dev, ABS_X, elo->data[1]);
+		input_report_abs(dev, ABS_Y, elo->data[2]);
+		input_sync(dev);
+		elo->idx = 0;
+		break;
+	}
+}
+
+static irqreturn_t elo_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct elo *elo = serio_get_drvdata(serio);
+
+	switch (elo->id) {
+	case 0:
+		elo_process_data_10(elo, data);
+		break;
+
+	case 1:
+	case 2:
+		elo_process_data_6(elo, data);
+		break;
+
+	case 3:
+		elo_process_data_3(elo, data);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int elo_command_10(struct elo *elo, unsigned char *packet)
+{
+	int rc = -1;
+	int i;
+	unsigned char csum = 0xaa + ELO10_LEAD_BYTE;
+
+	mutex_lock(&elo->cmd_mutex);
+
+	serio_pause_rx(elo->serio);
+	elo->expected_packet = toupper(packet[0]);
+	init_completion(&elo->cmd_done);
+	serio_continue_rx(elo->serio);
+
+	if (serio_write(elo->serio, ELO10_LEAD_BYTE))
+		goto out;
+
+	for (i = 0; i < ELO10_PACKET_LEN; i++) {
+		csum += packet[i];
+		if (serio_write(elo->serio, packet[i]))
+			goto out;
+	}
+
+	if (serio_write(elo->serio, csum))
+		goto out;
+
+	wait_for_completion_timeout(&elo->cmd_done, HZ);
+
+	if (elo->expected_packet == ELO10_TOUCH_PACKET) {
+		/* We are back in reporting mode, the command was ACKed */
+		memcpy(packet, elo->response, ELO10_PACKET_LEN);
+		rc = 0;
+	}
+
+ out:
+	mutex_unlock(&elo->cmd_mutex);
+	return rc;
+}
+
+static int elo_setup_10(struct elo *elo)
+{
+	static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" };
+	struct input_dev *dev = elo->dev;
+	unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD };
+
+	if (elo_command_10(elo, packet))
+		return -1;
+
+	dev->id.version = (packet[5] << 8) | packet[4];
+
+	input_set_abs_params(dev, ABS_X, 96, 4000, 0, 0);
+	input_set_abs_params(dev, ABS_Y, 96, 4000, 0, 0);
+	if (packet[3] & ELO10_PRESSURE)
+		input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+
+	dev_info(&elo->serio->dev,
+		 "%sTouch touchscreen, fw: %02x.%02x, features: 0x%02x, controller: 0x%02x\n",
+		 elo_types[(packet[1] -'0') & 0x03],
+		 packet[5], packet[4], packet[3], packet[7]);
+
+	return 0;
+}
+
+/*
+ * elo_disconnect() is the opposite of elo_connect()
+ */
+
+static void elo_disconnect(struct serio *serio)
+{
+	struct elo *elo = serio_get_drvdata(serio);
+
+	input_get_device(elo->dev);
+	input_unregister_device(elo->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_put_device(elo->dev);
+	kfree(elo);
+}
+
+/*
+ * elo_connect() is the routine that is called when someone adds a
+ * new serio device that supports Gunze protocol and registers it as
+ * an input device.
+ */
+
+static int elo_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct elo *elo;
+	struct input_dev *input_dev;
+	int err;
+
+	elo = kzalloc(sizeof(struct elo), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!elo || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	elo->serio = serio;
+	elo->id = serio->id.id;
+	elo->dev = input_dev;
+	elo->expected_packet = ELO10_TOUCH_PACKET;
+	mutex_init(&elo->cmd_mutex);
+	init_completion(&elo->cmd_done);
+	snprintf(elo->phys, sizeof(elo->phys), "%s/input0", serio->phys);
+
+	input_dev->name = "Elo Serial TouchScreen";
+	input_dev->phys = elo->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_ELO;
+	input_dev->id.product = elo->id;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	serio_set_drvdata(serio, elo);
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	switch (elo->id) {
+
+	case 0: /* 10-byte protocol */
+		if (elo_setup_10(elo))
+			goto fail3;
+
+		break;
+
+	case 1: /* 6-byte protocol */
+		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 15, 0, 0);
+
+	case 2: /* 4-byte protocol */
+		input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0);
+		input_set_abs_params(input_dev, ABS_Y, 96, 4000, 0, 0);
+		break;
+
+	case 3: /* 3-byte protocol */
+		input_set_abs_params(input_dev, ABS_X, 0, 255, 0, 0);
+		input_set_abs_params(input_dev, ABS_Y, 0, 255, 0, 0);
+		break;
+	}
+
+	err = input_register_device(elo->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3: serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(elo);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id elo_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_ELO,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, elo_serio_ids);
+
+static struct serio_driver elo_drv = {
+	.driver		= {
+		.name	= "elo",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= elo_serio_ids,
+	.interrupt	= elo_interrupt,
+	.connect	= elo_connect,
+	.disconnect	= elo_disconnect,
+};
+
+module_serio_driver(elo_drv);
diff --git a/drivers/input/touchscreen/ft6236.c b/drivers/input/touchscreen/ft6236.c
new file mode 100644
index 0000000..d240d2e
--- /dev/null
+++ b/drivers/input/touchscreen/ft6236.c
@@ -0,0 +1,326 @@
+/*
+ * FocalTech FT6236 TouchScreen driver.
+ *
+ * Copyright (c) 2010  Focal tech Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/property.h>
+
+#define FT6236_MAX_TOUCH_POINTS		2
+
+#define FT6236_REG_TH_GROUP		0x80
+#define FT6236_REG_PERIODACTIVE		0x88
+#define FT6236_REG_LIB_VER_H		0xa1
+#define FT6236_REG_LIB_VER_L		0xa2
+#define FT6236_REG_CIPHER		0xa3
+#define FT6236_REG_FIRMID		0xa6
+#define FT6236_REG_FOCALTECH_ID		0xa8
+#define FT6236_REG_RELEASE_CODE_ID	0xaf
+
+#define FT6236_EVENT_PRESS_DOWN		0
+#define FT6236_EVENT_LIFT_UP		1
+#define FT6236_EVENT_CONTACT		2
+#define FT6236_EVENT_NO_EVENT		3
+
+struct ft6236_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct gpio_desc *reset_gpio;
+	u32 max_x;
+	u32 max_y;
+	bool invert_x;
+	bool invert_y;
+	bool swap_xy;
+};
+
+/*
+ * This struct is a touchpoint as stored in hardware.  Note that the id,
+ * as well as the event, are stored in the upper nybble of the hi byte.
+ */
+struct ft6236_touchpoint {
+	union {
+		u8 xhi;
+		u8 event;
+	};
+	u8 xlo;
+	union {
+		u8 yhi;
+		u8 id;
+	};
+	u8 ylo;
+	u8 weight;
+	u8 misc;
+} __packed;
+
+/* This packet represents the register map as read from offset 0 */
+struct ft6236_packet {
+	u8 dev_mode;
+	u8 gest_id;
+	u8 touches;
+	struct ft6236_touchpoint points[FT6236_MAX_TOUCH_POINTS];
+} __packed;
+
+static int ft6236_read(struct i2c_client *client, u8 reg, u8 len, void *data)
+{
+	int error;
+
+	error = i2c_smbus_read_i2c_block_data(client, reg, len, data);
+	if (error < 0)
+		return error;
+
+	if (error != len)
+		return -EIO;
+
+	return 0;
+}
+
+static irqreturn_t ft6236_interrupt(int irq, void *dev_id)
+{
+	struct ft6236_data *ft6236 = dev_id;
+	struct device *dev = &ft6236->client->dev;
+	struct input_dev *input = ft6236->input;
+	struct ft6236_packet buf;
+	u8 touches;
+	int i, error;
+
+	error = ft6236_read(ft6236->client, 0, sizeof(buf), &buf);
+	if (error) {
+		dev_err(dev, "read touchdata failed %d\n", error);
+		return IRQ_HANDLED;
+	}
+
+	touches = buf.touches & 0xf;
+	if (touches > FT6236_MAX_TOUCH_POINTS) {
+		dev_dbg(dev,
+			"%d touch points reported, only %d are supported\n",
+			touches, FT6236_MAX_TOUCH_POINTS);
+		touches = FT6236_MAX_TOUCH_POINTS;
+	}
+
+	for (i = 0; i < touches; i++) {
+		struct ft6236_touchpoint *point = &buf.points[i];
+		u16 x = ((point->xhi & 0xf) << 8) | buf.points[i].xlo;
+		u16 y = ((point->yhi & 0xf) << 8) | buf.points[i].ylo;
+		u8 event = point->event >> 6;
+		u8 id = point->id >> 4;
+		bool act = (event == FT6236_EVENT_PRESS_DOWN ||
+			    event == FT6236_EVENT_CONTACT);
+
+		input_mt_slot(input, id);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, act);
+		if (!act)
+			continue;
+
+		if (ft6236->invert_x)
+			x = ft6236->max_x - x;
+
+		if (ft6236->invert_y)
+			y = ft6236->max_y - y;
+
+		if (ft6236->swap_xy) {
+			input_report_abs(input, ABS_MT_POSITION_X, y);
+			input_report_abs(input, ABS_MT_POSITION_Y, x);
+		} else {
+			input_report_abs(input, ABS_MT_POSITION_X, x);
+			input_report_abs(input, ABS_MT_POSITION_Y, y);
+		}
+	}
+
+	input_mt_sync_frame(input);
+	input_sync(input);
+
+	return IRQ_HANDLED;
+}
+
+static u8 ft6236_debug_read_byte(struct ft6236_data *ft6236, u8 reg)
+{
+	struct i2c_client *client = ft6236->client;
+	u8 val = 0;
+	int error;
+
+	error = ft6236_read(client, reg, 1, &val);
+	if (error)
+		dev_dbg(&client->dev,
+			"error reading register 0x%02x: %d\n", reg, error);
+
+	return val;
+}
+
+static void ft6236_debug_info(struct ft6236_data *ft6236)
+{
+	struct device *dev = &ft6236->client->dev;
+
+	dev_dbg(dev, "Touch threshold is %d\n",
+		ft6236_debug_read_byte(ft6236, FT6236_REG_TH_GROUP) * 4);
+	dev_dbg(dev, "Report rate is %dHz\n",
+		ft6236_debug_read_byte(ft6236, FT6236_REG_PERIODACTIVE) * 10);
+	dev_dbg(dev, "Firmware library version 0x%02x%02x\n",
+		ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_H),
+		ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_L));
+	dev_dbg(dev, "Firmware version 0x%02x\n",
+		ft6236_debug_read_byte(ft6236, FT6236_REG_FIRMID));
+	dev_dbg(dev, "Chip vendor ID 0x%02x\n",
+		ft6236_debug_read_byte(ft6236, FT6236_REG_CIPHER));
+	dev_dbg(dev, "CTPM vendor ID 0x%02x\n",
+		ft6236_debug_read_byte(ft6236, FT6236_REG_FOCALTECH_ID));
+	dev_dbg(dev, "Release code version 0x%02x\n",
+		ft6236_debug_read_byte(ft6236, FT6236_REG_RELEASE_CODE_ID));
+}
+
+static void ft6236_reset(struct ft6236_data *ft6236)
+{
+	if (!ft6236->reset_gpio)
+		return;
+
+	gpiod_set_value_cansleep(ft6236->reset_gpio, 1);
+	usleep_range(5000, 20000);
+	gpiod_set_value_cansleep(ft6236->reset_gpio, 0);
+	msleep(300);
+}
+
+static int ft6236_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct ft6236_data *ft6236;
+	struct input_dev *input;
+	u32 fuzz_x = 0, fuzz_y = 0;
+	u8 val;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -ENXIO;
+
+	if (!client->irq) {
+		dev_err(dev, "irq is missing\n");
+		return -EINVAL;
+	}
+
+	ft6236 = devm_kzalloc(dev, sizeof(*ft6236), GFP_KERNEL);
+	if (!ft6236)
+		return -ENOMEM;
+
+	ft6236->client = client;
+	ft6236->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+						     GPIOD_OUT_LOW);
+	if (IS_ERR(ft6236->reset_gpio)) {
+		error = PTR_ERR(ft6236->reset_gpio);
+		if (error != -EPROBE_DEFER)
+			dev_err(dev, "error getting reset gpio: %d\n", error);
+		return error;
+	}
+
+	ft6236_reset(ft6236);
+
+	/* verify that the controller is present */
+	error = ft6236_read(client, 0x00, 1, &val);
+	if (error) {
+		dev_err(dev, "failed to read from controller: %d\n", error);
+		return error;
+	}
+
+	ft6236_debug_info(ft6236);
+
+	input = devm_input_allocate_device(dev);
+	if (!input)
+		return -ENOMEM;
+
+	ft6236->input = input;
+	input->name = client->name;
+	input->id.bustype = BUS_I2C;
+
+	if (device_property_read_u32(dev, "touchscreen-size-x",
+				     &ft6236->max_x) ||
+	    device_property_read_u32(dev, "touchscreen-size-y",
+				     &ft6236->max_y)) {
+		dev_err(dev, "touchscreen-size-x and/or -y missing\n");
+		return -EINVAL;
+	}
+
+	device_property_read_u32(dev, "touchscreen-fuzz-x", &fuzz_x);
+	device_property_read_u32(dev, "touchscreen-fuzz-y", &fuzz_y);
+	ft6236->invert_x = device_property_read_bool(dev,
+						     "touchscreen-inverted-x");
+	ft6236->invert_y = device_property_read_bool(dev,
+						     "touchscreen-inverted-y");
+	ft6236->swap_xy = device_property_read_bool(dev,
+						    "touchscreen-swapped-x-y");
+
+	if (ft6236->swap_xy) {
+		input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+				     ft6236->max_y, fuzz_y, 0);
+		input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+				     ft6236->max_x, fuzz_x, 0);
+	} else {
+		input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+				     ft6236->max_x, fuzz_x, 0);
+		input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+				     ft6236->max_y, fuzz_y, 0);
+	}
+
+	error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS,
+				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error)
+		return error;
+
+	error = devm_request_threaded_irq(dev, client->irq, NULL,
+					  ft6236_interrupt, IRQF_ONESHOT,
+					  client->name, ft6236);
+	if (error) {
+		dev_err(dev, "request irq %d failed: %d\n", client->irq, error);
+		return error;
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(dev, "failed to register input device: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ft6236_of_match[] = {
+	{ .compatible = "focaltech,ft6236", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ft6236_of_match);
+#endif
+
+static const struct i2c_device_id ft6236_id[] = {
+	{ "ft6236", },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ft6236_id);
+
+static struct i2c_driver ft6236_driver = {
+	.driver = {
+		.name = "ft6236",
+		.of_match_table = of_match_ptr(ft6236_of_match),
+	},
+	.probe = ft6236_probe,
+	.id_table = ft6236_id,
+};
+module_i2c_driver(ft6236_driver);
+
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_AUTHOR("Noralf Trønnes <noralf@tronnes.org>");
+MODULE_DESCRIPTION("FocalTech FT6236 TouchScreen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/fujitsu_ts.c b/drivers/input/touchscreen/fujitsu_ts.c
new file mode 100644
index 0000000..d0e46a7
--- /dev/null
+++ b/drivers/input/touchscreen/fujitsu_ts.c
@@ -0,0 +1,177 @@
+/*
+ * Fujitsu serial touchscreen driver
+ *
+ * Copyright (c) Dmitry Torokhov <dtor@mail.ru>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Fujitsu serial touchscreen driver"
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define FUJITSU_LENGTH 5
+
+/*
+ * Per-touchscreen data.
+ */
+struct fujitsu {
+	struct input_dev *dev;
+	struct serio *serio;
+	int idx;
+	unsigned char data[FUJITSU_LENGTH];
+	char phys[32];
+};
+
+/*
+ * Decode serial data (5 bytes per packet)
+ * First byte
+ * 1 C 0 0 R S S S
+ * Where C is 1 while in calibration mode (which we don't use)
+ * R is 1 when no coordinate corection was done.
+ * S are button state
+ */
+static irqreturn_t fujitsu_interrupt(struct serio *serio,
+				     unsigned char data, unsigned int flags)
+{
+	struct fujitsu *fujitsu = serio_get_drvdata(serio);
+	struct input_dev *dev = fujitsu->dev;
+
+	if (fujitsu->idx == 0) {
+		/* resync skip until start of frame */
+		if ((data & 0xf0) != 0x80)
+			return IRQ_HANDLED;
+	} else {
+		/* resync skip garbage */
+		if (data & 0x80) {
+			fujitsu->idx = 0;
+			return IRQ_HANDLED;
+		}
+	}
+
+	fujitsu->data[fujitsu->idx++] = data;
+	if (fujitsu->idx == FUJITSU_LENGTH) {
+		input_report_abs(dev, ABS_X,
+				 (fujitsu->data[2] << 7) | fujitsu->data[1]);
+		input_report_abs(dev, ABS_Y,
+				 (fujitsu->data[4] << 7) | fujitsu->data[3]);
+		input_report_key(dev, BTN_TOUCH,
+				 (fujitsu->data[0] & 0x03) != 2);
+		input_sync(dev);
+		fujitsu->idx = 0;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * fujitsu_disconnect() is the opposite of fujitsu_connect()
+ */
+static void fujitsu_disconnect(struct serio *serio)
+{
+	struct fujitsu *fujitsu = serio_get_drvdata(serio);
+
+	input_get_device(fujitsu->dev);
+	input_unregister_device(fujitsu->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_put_device(fujitsu->dev);
+	kfree(fujitsu);
+}
+
+/*
+ * fujitsu_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Fujitsu protocol and registers it
+ * as input device.
+ */
+static int fujitsu_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct fujitsu *fujitsu;
+	struct input_dev *input_dev;
+	int err;
+
+	fujitsu = kzalloc(sizeof(struct fujitsu), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!fujitsu || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	fujitsu->serio = serio;
+	fujitsu->dev = input_dev;
+	snprintf(fujitsu->phys, sizeof(fujitsu->phys),
+		 "%s/input0", serio->phys);
+
+	input_dev->name = "Fujitsu Serial Touchscreen";
+	input_dev->phys = fujitsu->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_FUJITSU;
+	input_dev->id.product = 0;
+	input_dev->id.version = 0x0100;
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_set_abs_params(input_dev, ABS_X, 0, 4096, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, 4096, 0, 0);
+	serio_set_drvdata(serio, fujitsu);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(fujitsu->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:
+	serio_close(serio);
+ fail2:
+	serio_set_drvdata(serio, NULL);
+ fail1:
+	input_free_device(input_dev);
+	kfree(fujitsu);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+static struct serio_device_id fujitsu_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_FUJITSU,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, fujitsu_serio_ids);
+
+static struct serio_driver fujitsu_drv = {
+	.driver		= {
+		.name	= "fujitsu_ts",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= fujitsu_serio_ids,
+	.interrupt	= fujitsu_interrupt,
+	.connect	= fujitsu_connect,
+	.disconnect	= fujitsu_disconnect,
+};
+
+module_serio_driver(fujitsu_drv);
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
new file mode 100644
index 0000000..4d113c9
--- /dev/null
+++ b/drivers/input/touchscreen/goodix.c
@@ -0,0 +1,461 @@
+/*
+ *  Driver for Goodix Touchscreens
+ *
+ *  Copyright (c) 2014 Red Hat Inc.
+ *
+ *  This code is based on gt9xx.c authored by andrew@goodix.com:
+ *
+ *  2010 - 2012 Goodix Technology.
+ */
+
+/*
+ * 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; version 2 of the License.
+ */
+
+#include <linux/kernel.h>
+#include <linux/dmi.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <asm/unaligned.h>
+
+struct goodix_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	int abs_x_max;
+	int abs_y_max;
+	unsigned int max_touch_num;
+	unsigned int int_trigger_type;
+	bool rotated_screen;
+};
+
+#define GOODIX_MAX_HEIGHT		4096
+#define GOODIX_MAX_WIDTH		4096
+#define GOODIX_INT_TRIGGER		1
+#define GOODIX_CONTACT_SIZE		8
+#define GOODIX_MAX_CONTACTS		10
+
+#define GOODIX_CONFIG_MAX_LENGTH	240
+
+/* Register defines */
+#define GOODIX_READ_COOR_ADDR		0x814E
+#define GOODIX_REG_CONFIG_DATA		0x8047
+#define GOODIX_REG_ID			0x8140
+
+#define RESOLUTION_LOC		1
+#define MAX_CONTACTS_LOC	5
+#define TRIGGER_LOC		6
+
+static const unsigned long goodix_irq_flags[] = {
+	IRQ_TYPE_EDGE_RISING,
+	IRQ_TYPE_EDGE_FALLING,
+	IRQ_TYPE_LEVEL_LOW,
+	IRQ_TYPE_LEVEL_HIGH,
+};
+
+/*
+ * Those tablets have their coordinates origin at the bottom right
+ * of the tablet, as if rotated 180 degrees
+ */
+static const struct dmi_system_id rotated_screen[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+	{
+		.ident = "WinBook TW100",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TW100")
+		}
+	},
+	{
+		.ident = "WinBook TW700",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TW700")
+		},
+	},
+#endif
+	{}
+};
+
+/**
+ * goodix_i2c_read - read data from a register of the i2c slave device.
+ *
+ * @client: i2c device.
+ * @reg: the register to read from.
+ * @buf: raw write data buffer.
+ * @len: length of the buffer to write
+ */
+static int goodix_i2c_read(struct i2c_client *client,
+			   u16 reg, u8 *buf, int len)
+{
+	struct i2c_msg msgs[2];
+	u16 wbuf = cpu_to_be16(reg);
+	int ret;
+
+	msgs[0].flags = 0;
+	msgs[0].addr  = client->addr;
+	msgs[0].len   = 2;
+	msgs[0].buf   = (u8 *)&wbuf;
+
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].addr  = client->addr;
+	msgs[1].len   = len;
+	msgs[1].buf   = buf;
+
+	ret = i2c_transfer(client->adapter, msgs, 2);
+	return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
+}
+
+static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
+{
+	int touch_num;
+	int error;
+
+	error = goodix_i2c_read(ts->client, GOODIX_READ_COOR_ADDR, data,
+				GOODIX_CONTACT_SIZE + 1);
+	if (error) {
+		dev_err(&ts->client->dev, "I2C transfer error: %d\n", error);
+		return error;
+	}
+
+	if (!(data[0] & 0x80))
+		return -EAGAIN;
+
+	touch_num = data[0] & 0x0f;
+	if (touch_num > ts->max_touch_num)
+		return -EPROTO;
+
+	if (touch_num > 1) {
+		data += 1 + GOODIX_CONTACT_SIZE;
+		error = goodix_i2c_read(ts->client,
+					GOODIX_READ_COOR_ADDR +
+						1 + GOODIX_CONTACT_SIZE,
+					data,
+					GOODIX_CONTACT_SIZE * (touch_num - 1));
+		if (error)
+			return error;
+	}
+
+	return touch_num;
+}
+
+static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data)
+{
+	int id = coor_data[0] & 0x0F;
+	int input_x = get_unaligned_le16(&coor_data[1]);
+	int input_y = get_unaligned_le16(&coor_data[3]);
+	int input_w = get_unaligned_le16(&coor_data[5]);
+
+	if (ts->rotated_screen) {
+		input_x = ts->abs_x_max - input_x;
+		input_y = ts->abs_y_max - input_y;
+	}
+
+	input_mt_slot(ts->input_dev, id);
+	input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
+	input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
+	input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
+	input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
+	input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);
+}
+
+/**
+ * goodix_process_events - Process incoming events
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * Called when the IRQ is triggered. Read the current device state, and push
+ * the input events to the user space.
+ */
+static void goodix_process_events(struct goodix_ts_data *ts)
+{
+	u8  point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
+	int touch_num;
+	int i;
+
+	touch_num = goodix_ts_read_input_report(ts, point_data);
+	if (touch_num < 0)
+		return;
+
+	for (i = 0; i < touch_num; i++)
+		goodix_ts_report_touch(ts,
+				&point_data[1 + GOODIX_CONTACT_SIZE * i]);
+
+	input_mt_sync_frame(ts->input_dev);
+	input_sync(ts->input_dev);
+}
+
+/**
+ * goodix_ts_irq_handler - The IRQ handler
+ *
+ * @irq: interrupt number.
+ * @dev_id: private data pointer.
+ */
+static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
+{
+	static const u8 end_cmd[] = {
+		GOODIX_READ_COOR_ADDR >> 8,
+		GOODIX_READ_COOR_ADDR & 0xff,
+		0
+	};
+	struct goodix_ts_data *ts = dev_id;
+
+	goodix_process_events(ts);
+
+	if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0)
+		dev_err(&ts->client->dev, "I2C write end_cmd error\n");
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * goodix_read_config - Read the embedded configuration of the panel
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * Must be called during probe
+ */
+static void goodix_read_config(struct goodix_ts_data *ts)
+{
+	u8 config[GOODIX_CONFIG_MAX_LENGTH];
+	int error;
+
+	error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
+				config,
+				GOODIX_CONFIG_MAX_LENGTH);
+	if (error) {
+		dev_warn(&ts->client->dev,
+			 "Error reading config (%d), using defaults\n",
+			 error);
+		ts->abs_x_max = GOODIX_MAX_WIDTH;
+		ts->abs_y_max = GOODIX_MAX_HEIGHT;
+		ts->int_trigger_type = GOODIX_INT_TRIGGER;
+		ts->max_touch_num = GOODIX_MAX_CONTACTS;
+		return;
+	}
+
+	ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]);
+	ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]);
+	ts->int_trigger_type = config[TRIGGER_LOC] & 0x03;
+	ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f;
+	if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) {
+		dev_err(&ts->client->dev,
+			"Invalid config, using defaults\n");
+		ts->abs_x_max = GOODIX_MAX_WIDTH;
+		ts->abs_y_max = GOODIX_MAX_HEIGHT;
+		ts->max_touch_num = GOODIX_MAX_CONTACTS;
+	}
+
+	ts->rotated_screen = dmi_check_system(rotated_screen);
+	if (ts->rotated_screen)
+		dev_dbg(&ts->client->dev,
+			 "Applying '180 degrees rotated screen' quirk\n");
+}
+
+/**
+ * goodix_read_version - Read goodix touchscreen version
+ *
+ * @client: the i2c client
+ * @version: output buffer containing the version on success
+ * @id: output buffer containing the id on success
+ */
+static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id)
+{
+	int error;
+	u8 buf[6];
+	char id_str[5];
+
+	error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf));
+	if (error) {
+		dev_err(&client->dev, "read version failed: %d\n", error);
+		return error;
+	}
+
+	memcpy(id_str, buf, 4);
+	id_str[4] = 0;
+	if (kstrtou16(id_str, 10, id))
+		*id = 0x1001;
+
+	*version = get_unaligned_le16(&buf[4]);
+
+	dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version);
+
+	return 0;
+}
+
+/**
+ * goodix_i2c_test - I2C test function to check if the device answers.
+ *
+ * @client: the i2c client
+ */
+static int goodix_i2c_test(struct i2c_client *client)
+{
+	int retry = 0;
+	int error;
+	u8 test;
+
+	while (retry++ < 2) {
+		error = goodix_i2c_read(client, GOODIX_REG_CONFIG_DATA,
+					&test, 1);
+		if (!error)
+			return 0;
+
+		dev_err(&client->dev, "i2c test failed attempt %d: %d\n",
+			retry, error);
+		msleep(20);
+	}
+
+	return error;
+}
+
+/**
+ * goodix_request_input_dev - Allocate, populate and register the input device
+ *
+ * @ts: our goodix_ts_data pointer
+ * @version: device firmware version
+ * @id: device ID
+ *
+ * Must be called during probe
+ */
+static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
+				    u16 id)
+{
+	int error;
+
+	ts->input_dev = devm_input_allocate_device(&ts->client->dev);
+	if (!ts->input_dev) {
+		dev_err(&ts->client->dev, "Failed to allocate input device.");
+		return -ENOMEM;
+	}
+
+	input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X,
+			     0, ts->abs_x_max, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y,
+			     0, ts->abs_y_max, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+	input_mt_init_slots(ts->input_dev, ts->max_touch_num,
+			    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+
+	ts->input_dev->name = "Goodix Capacitive TouchScreen";
+	ts->input_dev->phys = "input/ts";
+	ts->input_dev->id.bustype = BUS_I2C;
+	ts->input_dev->id.vendor = 0x0416;
+	ts->input_dev->id.product = id;
+	ts->input_dev->id.version = version;
+
+	error = input_register_device(ts->input_dev);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"Failed to register input device: %d", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int goodix_ts_probe(struct i2c_client *client,
+			   const struct i2c_device_id *id)
+{
+	struct goodix_ts_data *ts;
+	unsigned long irq_flags;
+	int error;
+	u16 version_info, id_info;
+
+	dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "I2C check functionality failed.\n");
+		return -ENXIO;
+	}
+
+	ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	error = goodix_i2c_test(client);
+	if (error) {
+		dev_err(&client->dev, "I2C communication failure: %d\n", error);
+		return error;
+	}
+
+	error = goodix_read_version(client, &version_info, &id_info);
+	if (error) {
+		dev_err(&client->dev, "Read version failed.\n");
+		return error;
+	}
+
+	goodix_read_config(ts);
+
+	error = goodix_request_input_dev(ts, version_info, id_info);
+	if (error)
+		return error;
+
+	irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
+	error = devm_request_threaded_irq(&ts->client->dev, client->irq,
+					  NULL, goodix_ts_irq_handler,
+					  irq_flags, client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "request IRQ failed: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static const struct i2c_device_id goodix_ts_id[] = {
+	{ "GDIX1001:00", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, goodix_ts_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id goodix_acpi_match[] = {
+	{ "GDIX1001", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id goodix_of_match[] = {
+	{ .compatible = "goodix,gt911" },
+	{ .compatible = "goodix,gt9110" },
+	{ .compatible = "goodix,gt912" },
+	{ .compatible = "goodix,gt927" },
+	{ .compatible = "goodix,gt9271" },
+	{ .compatible = "goodix,gt928" },
+	{ .compatible = "goodix,gt967" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, goodix_of_match);
+#endif
+
+static struct i2c_driver goodix_ts_driver = {
+	.probe = goodix_ts_probe,
+	.id_table = goodix_ts_id,
+	.driver = {
+		.name = "Goodix-TS",
+		.acpi_match_table = ACPI_PTR(goodix_acpi_match),
+		.of_match_table = of_match_ptr(goodix_of_match),
+	},
+};
+module_i2c_driver(goodix_ts_driver);
+
+MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
+MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
+MODULE_DESCRIPTION("Goodix touchscreen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c
new file mode 100644
index 0000000..e2ee626
--- /dev/null
+++ b/drivers/input/touchscreen/gunze.c
@@ -0,0 +1,188 @@
+/*
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ */
+
+/*
+ * Gunze AHL-51S touchscreen driver for Linux
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Gunze AHL-51S touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define	GUNZE_MAX_LENGTH	10
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct gunze {
+	struct input_dev *dev;
+	struct serio *serio;
+	int idx;
+	unsigned char data[GUNZE_MAX_LENGTH];
+	char phys[32];
+};
+
+static void gunze_process_packet(struct gunze* gunze)
+{
+	struct input_dev *dev = gunze->dev;
+
+	if (gunze->idx != GUNZE_MAX_LENGTH || gunze->data[5] != ',' ||
+		(gunze->data[0] != 'T' && gunze->data[0] != 'R')) {
+		printk(KERN_WARNING "gunze.c: bad packet: >%.*s<\n", GUNZE_MAX_LENGTH, gunze->data);
+		return;
+	}
+
+	input_report_abs(dev, ABS_X, simple_strtoul(gunze->data + 1, NULL, 10));
+	input_report_abs(dev, ABS_Y, 1024 - simple_strtoul(gunze->data + 6, NULL, 10));
+	input_report_key(dev, BTN_TOUCH, gunze->data[0] == 'T');
+	input_sync(dev);
+}
+
+static irqreturn_t gunze_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct gunze* gunze = serio_get_drvdata(serio);
+
+	if (data == '\r') {
+		gunze_process_packet(gunze);
+		gunze->idx = 0;
+	} else {
+		if (gunze->idx < GUNZE_MAX_LENGTH)
+			gunze->data[gunze->idx++] = data;
+	}
+	return IRQ_HANDLED;
+}
+
+/*
+ * gunze_disconnect() is the opposite of gunze_connect()
+ */
+
+static void gunze_disconnect(struct serio *serio)
+{
+	struct gunze *gunze = serio_get_drvdata(serio);
+
+	input_get_device(gunze->dev);
+	input_unregister_device(gunze->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_put_device(gunze->dev);
+	kfree(gunze);
+}
+
+/*
+ * gunze_connect() is the routine that is called when someone adds a
+ * new serio device that supports Gunze protocol and registers it as
+ * an input device.
+ */
+
+static int gunze_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct gunze *gunze;
+	struct input_dev *input_dev;
+	int err;
+
+	gunze = kzalloc(sizeof(struct gunze), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!gunze || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	gunze->serio = serio;
+	gunze->dev = input_dev;
+	snprintf(gunze->phys, sizeof(serio->phys), "%s/input0", serio->phys);
+
+	input_dev->name = "Gunze AHL-51S TouchScreen";
+	input_dev->phys = gunze->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_GUNZE;
+	input_dev->id.product = 0x0051;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(input_dev, ABS_X, 24, 1000, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 24, 1000, 0, 0);
+
+	serio_set_drvdata(serio, gunze);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(gunze->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(gunze);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id gunze_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_GUNZE,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, gunze_serio_ids);
+
+static struct serio_driver gunze_drv = {
+	.driver		= {
+		.name	= "gunze",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= gunze_serio_ids,
+	.interrupt	= gunze_interrupt,
+	.connect	= gunze_connect,
+	.disconnect	= gunze_disconnect,
+};
+
+module_serio_driver(gunze_drv);
diff --git a/drivers/input/touchscreen/hampshire.c b/drivers/input/touchscreen/hampshire.c
new file mode 100644
index 0000000..ecb1e0e
--- /dev/null
+++ b/drivers/input/touchscreen/hampshire.c
@@ -0,0 +1,189 @@
+/*
+ * Hampshire serial touchscreen driver
+ *
+ * Copyright (c) 2010 Adam Bennett
+ * Based on the dynapro driver (c) Tias Guns
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * 2010/04/08 Adam Bennett <abennett72@gmail.com>
+ *   Copied dynapro.c and edited for Hampshire 4-byte protocol
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Hampshire serial touchscreen driver"
+
+MODULE_AUTHOR("Adam Bennett <abennett72@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define HAMPSHIRE_FORMAT_TOUCH_BIT 0x40
+#define HAMPSHIRE_FORMAT_LENGTH 4
+#define HAMPSHIRE_RESPONSE_BEGIN_BYTE 0x80
+
+#define HAMPSHIRE_MIN_XC 0
+#define HAMPSHIRE_MAX_XC 0x1000
+#define HAMPSHIRE_MIN_YC 0
+#define HAMPSHIRE_MAX_YC 0x1000
+
+#define HAMPSHIRE_GET_XC(data) (((data[3] & 0x0c) >> 2) | (data[1] << 2) | ((data[0] & 0x38) << 6))
+#define HAMPSHIRE_GET_YC(data) ((data[3] & 0x03) | (data[2] << 2) | ((data[0] & 0x07) << 9))
+#define HAMPSHIRE_GET_TOUCHED(data) (HAMPSHIRE_FORMAT_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct hampshire {
+	struct input_dev *dev;
+	struct serio *serio;
+	int idx;
+	unsigned char data[HAMPSHIRE_FORMAT_LENGTH];
+	char phys[32];
+};
+
+static void hampshire_process_data(struct hampshire *phampshire)
+{
+	struct input_dev *dev = phampshire->dev;
+
+	if (HAMPSHIRE_FORMAT_LENGTH == ++phampshire->idx) {
+		input_report_abs(dev, ABS_X, HAMPSHIRE_GET_XC(phampshire->data));
+		input_report_abs(dev, ABS_Y, HAMPSHIRE_GET_YC(phampshire->data));
+		input_report_key(dev, BTN_TOUCH,
+				 HAMPSHIRE_GET_TOUCHED(phampshire->data));
+		input_sync(dev);
+
+		phampshire->idx = 0;
+	}
+}
+
+static irqreturn_t hampshire_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct hampshire *phampshire = serio_get_drvdata(serio);
+
+	phampshire->data[phampshire->idx] = data;
+
+	if (HAMPSHIRE_RESPONSE_BEGIN_BYTE & phampshire->data[0])
+		hampshire_process_data(phampshire);
+	else
+		dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
+			phampshire->data[0]);
+
+	return IRQ_HANDLED;
+}
+
+static void hampshire_disconnect(struct serio *serio)
+{
+	struct hampshire *phampshire = serio_get_drvdata(serio);
+
+	input_get_device(phampshire->dev);
+	input_unregister_device(phampshire->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_put_device(phampshire->dev);
+	kfree(phampshire);
+}
+
+/*
+ * hampshire_connect() is the routine that is called when someone adds a
+ * new serio device that supports hampshire protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+
+static int hampshire_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct hampshire *phampshire;
+	struct input_dev *input_dev;
+	int err;
+
+	phampshire = kzalloc(sizeof(struct hampshire), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!phampshire || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	phampshire->serio = serio;
+	phampshire->dev = input_dev;
+	snprintf(phampshire->phys, sizeof(phampshire->phys),
+		 "%s/input0", serio->phys);
+
+	input_dev->name = "Hampshire Serial TouchScreen";
+	input_dev->phys = phampshire->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_HAMPSHIRE;
+	input_dev->id.product = 0;
+	input_dev->id.version = 0x0001;
+	input_dev->dev.parent = &serio->dev;
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(phampshire->dev, ABS_X,
+			     HAMPSHIRE_MIN_XC, HAMPSHIRE_MAX_XC, 0, 0);
+	input_set_abs_params(phampshire->dev, ABS_Y,
+			     HAMPSHIRE_MIN_YC, HAMPSHIRE_MAX_YC, 0, 0);
+
+	serio_set_drvdata(serio, phampshire);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(phampshire->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(phampshire);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id hampshire_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_HAMPSHIRE,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, hampshire_serio_ids);
+
+static struct serio_driver hampshire_drv = {
+	.driver		= {
+		.name	= "hampshire",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= hampshire_serio_ids,
+	.interrupt	= hampshire_interrupt,
+	.connect	= hampshire_connect,
+	.disconnect	= hampshire_disconnect,
+};
+
+module_serio_driver(hampshire_drv);
diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c
new file mode 100644
index 0000000..85cf9be
--- /dev/null
+++ b/drivers/input/touchscreen/hp680_ts_input.c
@@ -0,0 +1,127 @@
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/adc.h>
+#include <mach/hp6xx.h>
+
+#define MODNAME "hp680_ts_input"
+
+#define HP680_TS_ABS_X_MIN	40
+#define HP680_TS_ABS_X_MAX	950
+#define HP680_TS_ABS_Y_MIN	80
+#define HP680_TS_ABS_Y_MAX	910
+
+#define	PHDR	0xa400012e
+#define SCPDR	0xa4000136
+
+static void do_softint(struct work_struct *work);
+
+static struct input_dev *hp680_ts_dev;
+static DECLARE_DELAYED_WORK(work, do_softint);
+
+static void do_softint(struct work_struct *work)
+{
+	int absx = 0, absy = 0;
+	u8 scpdr;
+	int touched = 0;
+
+	if (__raw_readb(PHDR) & PHDR_TS_PEN_DOWN) {
+		scpdr = __raw_readb(SCPDR);
+		scpdr |= SCPDR_TS_SCAN_ENABLE;
+		scpdr &= ~SCPDR_TS_SCAN_Y;
+		__raw_writeb(scpdr, SCPDR);
+		udelay(30);
+
+		absy = adc_single(ADC_CHANNEL_TS_Y);
+
+		scpdr = __raw_readb(SCPDR);
+		scpdr |= SCPDR_TS_SCAN_Y;
+		scpdr &= ~SCPDR_TS_SCAN_X;
+		__raw_writeb(scpdr, SCPDR);
+		udelay(30);
+
+		absx = adc_single(ADC_CHANNEL_TS_X);
+
+		scpdr = __raw_readb(SCPDR);
+		scpdr |= SCPDR_TS_SCAN_X;
+		scpdr &= ~SCPDR_TS_SCAN_ENABLE;
+		__raw_writeb(scpdr, SCPDR);
+		udelay(100);
+		touched = __raw_readb(PHDR) & PHDR_TS_PEN_DOWN;
+	}
+
+	if (touched) {
+		input_report_key(hp680_ts_dev, BTN_TOUCH, 1);
+		input_report_abs(hp680_ts_dev, ABS_X, absx);
+		input_report_abs(hp680_ts_dev, ABS_Y, absy);
+	} else {
+		input_report_key(hp680_ts_dev, BTN_TOUCH, 0);
+	}
+
+	input_sync(hp680_ts_dev);
+	enable_irq(HP680_TS_IRQ);
+}
+
+static irqreturn_t hp680_ts_interrupt(int irq, void *dev)
+{
+	disable_irq_nosync(irq);
+	schedule_delayed_work(&work, HZ / 20);
+
+	return IRQ_HANDLED;
+}
+
+static int __init hp680_ts_init(void)
+{
+	int err;
+
+	hp680_ts_dev = input_allocate_device();
+	if (!hp680_ts_dev)
+		return -ENOMEM;
+
+	hp680_ts_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
+	hp680_ts_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_set_abs_params(hp680_ts_dev, ABS_X,
+		HP680_TS_ABS_X_MIN, HP680_TS_ABS_X_MAX, 0, 0);
+	input_set_abs_params(hp680_ts_dev, ABS_Y,
+		HP680_TS_ABS_Y_MIN, HP680_TS_ABS_Y_MAX, 0, 0);
+
+	hp680_ts_dev->name = "HP Jornada touchscreen";
+	hp680_ts_dev->phys = "hp680_ts/input0";
+
+	if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt,
+			0, MODNAME, NULL) < 0) {
+		printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n",
+		       HP680_TS_IRQ);
+		err = -EBUSY;
+		goto fail1;
+	}
+
+	err = input_register_device(hp680_ts_dev);
+	if (err)
+		goto fail2;
+
+	return 0;
+
+ fail2:	free_irq(HP680_TS_IRQ, NULL);
+	cancel_delayed_work_sync(&work);
+ fail1:	input_free_device(hp680_ts_dev);
+	return err;
+}
+
+static void __exit hp680_ts_exit(void)
+{
+	free_irq(HP680_TS_IRQ, NULL);
+	cancel_delayed_work_sync(&work);
+	input_unregister_device(hp680_ts_dev);
+}
+
+module_init(hp680_ts_init);
+module_exit(hp680_ts_exit);
+
+MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
+MODULE_DESCRIPTION("HP Jornada 680 touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/htcpen.c b/drivers/input/touchscreen/htcpen.c
new file mode 100644
index 0000000..92e2243
--- /dev/null
+++ b/drivers/input/touchscreen/htcpen.c
@@ -0,0 +1,248 @@
+/*
+ * HTC Shift touchscreen driver
+ *
+ * Copyright (C) 2008 Pau Oliva Fora <pof@eslack.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/isa.h>
+#include <linux/ioport.h>
+#include <linux/dmi.h>
+
+MODULE_AUTHOR("Pau Oliva Fora <pau@eslack.org>");
+MODULE_DESCRIPTION("HTC Shift touchscreen driver");
+MODULE_LICENSE("GPL");
+
+#define HTCPEN_PORT_IRQ_CLEAR	0x068
+#define HTCPEN_PORT_INIT	0x06c
+#define HTCPEN_PORT_INDEX	0x0250
+#define HTCPEN_PORT_DATA	0x0251
+#define HTCPEN_IRQ		3
+
+#define DEVICE_ENABLE		0xa2
+#define DEVICE_DISABLE		0xa3
+
+#define X_INDEX			3
+#define Y_INDEX			5
+#define TOUCH_INDEX		0xb
+#define LSB_XY_INDEX		0xc
+#define X_AXIS_MAX		2040
+#define Y_AXIS_MAX		2040
+
+static bool invert_x;
+module_param(invert_x, bool, 0644);
+MODULE_PARM_DESC(invert_x, "If set, X axis is inverted");
+static bool invert_y;
+module_param(invert_y, bool, 0644);
+MODULE_PARM_DESC(invert_y, "If set, Y axis is inverted");
+
+static irqreturn_t htcpen_interrupt(int irq, void *handle)
+{
+	struct input_dev *htcpen_dev = handle;
+	unsigned short x, y, xy;
+
+	/* 0 = press; 1 = release */
+	outb_p(TOUCH_INDEX, HTCPEN_PORT_INDEX);
+
+	if (inb_p(HTCPEN_PORT_DATA)) {
+		input_report_key(htcpen_dev, BTN_TOUCH, 0);
+	} else {
+		outb_p(X_INDEX, HTCPEN_PORT_INDEX);
+		x = inb_p(HTCPEN_PORT_DATA);
+
+		outb_p(Y_INDEX, HTCPEN_PORT_INDEX);
+		y = inb_p(HTCPEN_PORT_DATA);
+
+		outb_p(LSB_XY_INDEX, HTCPEN_PORT_INDEX);
+		xy = inb_p(HTCPEN_PORT_DATA);
+
+		/* get high resolution value of X and Y using LSB */
+		x = X_AXIS_MAX - ((x * 8) + ((xy >> 4) & 0xf));
+		y = (y * 8) + (xy & 0xf);
+		if (invert_x)
+			x = X_AXIS_MAX - x;
+		if (invert_y)
+			y = Y_AXIS_MAX - y;
+
+		if (x != X_AXIS_MAX && x != 0) {
+			input_report_key(htcpen_dev, BTN_TOUCH, 1);
+			input_report_abs(htcpen_dev, ABS_X, x);
+			input_report_abs(htcpen_dev, ABS_Y, y);
+		}
+	}
+
+	input_sync(htcpen_dev);
+
+	inb_p(HTCPEN_PORT_IRQ_CLEAR);
+
+	return IRQ_HANDLED;
+}
+
+static int htcpen_open(struct input_dev *dev)
+{
+	outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
+
+	return 0;
+}
+
+static void htcpen_close(struct input_dev *dev)
+{
+	outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
+	synchronize_irq(HTCPEN_IRQ);
+}
+
+static int htcpen_isa_probe(struct device *dev, unsigned int id)
+{
+	struct input_dev *htcpen_dev;
+	int err = -EBUSY;
+
+	if (!request_region(HTCPEN_PORT_IRQ_CLEAR, 1, "htcpen")) {
+		printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
+			HTCPEN_PORT_IRQ_CLEAR);
+		goto request_region1_failed;
+	}
+
+	if (!request_region(HTCPEN_PORT_INIT, 1, "htcpen")) {
+		printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
+			HTCPEN_PORT_INIT);
+		goto request_region2_failed;
+	}
+
+	if (!request_region(HTCPEN_PORT_INDEX, 2, "htcpen")) {
+		printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
+			HTCPEN_PORT_INDEX);
+		goto request_region3_failed;
+	}
+
+	htcpen_dev = input_allocate_device();
+	if (!htcpen_dev) {
+		printk(KERN_ERR "htcpen: can't allocate device\n");
+		err = -ENOMEM;
+		goto input_alloc_failed;
+	}
+
+	htcpen_dev->name = "HTC Shift EC TouchScreen";
+	htcpen_dev->id.bustype = BUS_ISA;
+
+	htcpen_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
+	htcpen_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(htcpen_dev, ABS_X, 0, X_AXIS_MAX, 0, 0);
+	input_set_abs_params(htcpen_dev, ABS_Y, 0, Y_AXIS_MAX, 0, 0);
+
+	htcpen_dev->open = htcpen_open;
+	htcpen_dev->close = htcpen_close;
+
+	err = request_irq(HTCPEN_IRQ, htcpen_interrupt, 0, "htcpen",
+			htcpen_dev);
+	if (err) {
+		printk(KERN_ERR "htcpen: irq busy\n");
+		goto request_irq_failed;
+	}
+
+	inb_p(HTCPEN_PORT_IRQ_CLEAR);
+
+	err = input_register_device(htcpen_dev);
+	if (err)
+		goto input_register_failed;
+
+	dev_set_drvdata(dev, htcpen_dev);
+
+	return 0;
+
+ input_register_failed:
+	free_irq(HTCPEN_IRQ, htcpen_dev);
+ request_irq_failed:
+	input_free_device(htcpen_dev);
+ input_alloc_failed:
+	release_region(HTCPEN_PORT_INDEX, 2);
+ request_region3_failed:
+	release_region(HTCPEN_PORT_INIT, 1);
+ request_region2_failed:
+	release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
+ request_region1_failed:
+	return err;
+}
+
+static int htcpen_isa_remove(struct device *dev, unsigned int id)
+{
+	struct input_dev *htcpen_dev = dev_get_drvdata(dev);
+
+	input_unregister_device(htcpen_dev);
+
+	free_irq(HTCPEN_IRQ, htcpen_dev);
+
+	release_region(HTCPEN_PORT_INDEX, 2);
+	release_region(HTCPEN_PORT_INIT, 1);
+	release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int htcpen_isa_suspend(struct device *dev, unsigned int n,
+				pm_message_t state)
+{
+	outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
+
+	return 0;
+}
+
+static int htcpen_isa_resume(struct device *dev, unsigned int n)
+{
+	outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
+
+	return 0;
+}
+#endif
+
+static struct isa_driver htcpen_isa_driver = {
+	.probe		= htcpen_isa_probe,
+	.remove		= htcpen_isa_remove,
+#ifdef CONFIG_PM
+	.suspend	= htcpen_isa_suspend,
+	.resume		= htcpen_isa_resume,
+#endif
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "htcpen",
+	}
+};
+
+static struct dmi_system_id htcshift_dmi_table[] __initdata = {
+	{
+		.ident = "Shift",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "High Tech Computer Corp"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Shift"),
+		},
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(dmi, htcshift_dmi_table);
+
+static int __init htcpen_isa_init(void)
+{
+	if (!dmi_check_system(htcshift_dmi_table))
+		return -ENODEV;
+
+	return isa_register_driver(&htcpen_isa_driver, 1);
+}
+
+static void __exit htcpen_isa_exit(void)
+{
+	isa_unregister_driver(&htcpen_isa_driver);
+}
+
+module_init(htcpen_isa_init);
+module_exit(htcpen_isa_exit);
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c
new file mode 100644
index 0000000..ddf694b
--- /dev/null
+++ b/drivers/input/touchscreen/ili210x.c
@@ -0,0 +1,357 @@
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/input/ili210x.h>
+
+#define MAX_TOUCHES		2
+#define DEFAULT_POLL_PERIOD	20
+
+/* Touchscreen commands */
+#define REG_TOUCHDATA		0x10
+#define REG_PANEL_INFO		0x20
+#define REG_FIRMWARE_VERSION	0x40
+#define REG_CALIBRATE		0xcc
+
+struct finger {
+	u8 x_low;
+	u8 x_high;
+	u8 y_low;
+	u8 y_high;
+} __packed;
+
+struct touchdata {
+	u8 status;
+	struct finger finger[MAX_TOUCHES];
+} __packed;
+
+struct panel_info {
+	struct finger finger_max;
+	u8 xchannel_num;
+	u8 ychannel_num;
+} __packed;
+
+struct firmware_version {
+	u8 id;
+	u8 major;
+	u8 minor;
+} __packed;
+
+struct ili210x {
+	struct i2c_client *client;
+	struct input_dev *input;
+	bool (*get_pendown_state)(void);
+	unsigned int poll_period;
+	struct delayed_work dwork;
+};
+
+static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
+			    size_t len)
+{
+	struct i2c_msg msg[2] = {
+		{
+			.addr	= client->addr,
+			.flags	= 0,
+			.len	= 1,
+			.buf	= &reg,
+		},
+		{
+			.addr	= client->addr,
+			.flags	= I2C_M_RD,
+			.len	= len,
+			.buf	= buf,
+		}
+	};
+
+	if (i2c_transfer(client->adapter, msg, 2) != 2) {
+		dev_err(&client->dev, "i2c transfer failed\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void ili210x_report_events(struct input_dev *input,
+				  const struct touchdata *touchdata)
+{
+	int i;
+	bool touch;
+	unsigned int x, y;
+	const struct finger *finger;
+
+	for (i = 0; i < MAX_TOUCHES; i++) {
+		input_mt_slot(input, i);
+
+		finger = &touchdata->finger[i];
+
+		touch = touchdata->status & (1 << i);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+		if (touch) {
+			x = finger->x_low | (finger->x_high << 8);
+			y = finger->y_low | (finger->y_high << 8);
+
+			input_report_abs(input, ABS_MT_POSITION_X, x);
+			input_report_abs(input, ABS_MT_POSITION_Y, y);
+		}
+	}
+
+	input_mt_report_pointer_emulation(input, false);
+	input_sync(input);
+}
+
+static bool get_pendown_state(const struct ili210x *priv)
+{
+	bool state = false;
+
+	if (priv->get_pendown_state)
+		state = priv->get_pendown_state();
+
+	return state;
+}
+
+static void ili210x_work(struct work_struct *work)
+{
+	struct ili210x *priv = container_of(work, struct ili210x,
+					    dwork.work);
+	struct i2c_client *client = priv->client;
+	struct touchdata touchdata;
+	int error;
+
+	error = ili210x_read_reg(client, REG_TOUCHDATA,
+				 &touchdata, sizeof(touchdata));
+	if (error) {
+		dev_err(&client->dev,
+			"Unable to get touchdata, err = %d\n", error);
+		return;
+	}
+
+	ili210x_report_events(priv->input, &touchdata);
+
+	if ((touchdata.status & 0xf3) || get_pendown_state(priv))
+		schedule_delayed_work(&priv->dwork,
+				      msecs_to_jiffies(priv->poll_period));
+}
+
+static irqreturn_t ili210x_irq(int irq, void *irq_data)
+{
+	struct ili210x *priv = irq_data;
+
+	schedule_delayed_work(&priv->dwork, 0);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t ili210x_calibrate(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ili210x *priv = i2c_get_clientdata(client);
+	unsigned long calibrate;
+	int rc;
+	u8 cmd = REG_CALIBRATE;
+
+	if (kstrtoul(buf, 10, &calibrate))
+		return -EINVAL;
+
+	if (calibrate > 1)
+		return -EINVAL;
+
+	if (calibrate) {
+		rc = i2c_master_send(priv->client, &cmd, sizeof(cmd));
+		if (rc != sizeof(cmd))
+			return -EIO;
+	}
+
+	return count;
+}
+static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate);
+
+static struct attribute *ili210x_attributes[] = {
+	&dev_attr_calibrate.attr,
+	NULL,
+};
+
+static const struct attribute_group ili210x_attr_group = {
+	.attrs = ili210x_attributes,
+};
+
+static int ili210x_i2c_probe(struct i2c_client *client,
+				       const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	const struct ili210x_platform_data *pdata = dev_get_platdata(dev);
+	struct ili210x *priv;
+	struct input_dev *input;
+	struct panel_info panel;
+	struct firmware_version firmware;
+	int xmax, ymax;
+	int error;
+
+	dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
+
+	if (!pdata) {
+		dev_err(dev, "No platform data!\n");
+		return -EINVAL;
+	}
+
+	if (client->irq <= 0) {
+		dev_err(dev, "No IRQ!\n");
+		return -EINVAL;
+	}
+
+	/* Get firmware version */
+	error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
+				 &firmware, sizeof(firmware));
+	if (error) {
+		dev_err(dev, "Failed to get firmware version, err: %d\n",
+			error);
+		return error;
+	}
+
+	/* get panel info */
+	error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel));
+	if (error) {
+		dev_err(dev, "Failed to get panel information, err: %d\n",
+			error);
+		return error;
+	}
+
+	xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8);
+	ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8);
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	input = input_allocate_device();
+	if (!priv || !input) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	priv->client = client;
+	priv->input = input;
+	priv->get_pendown_state = pdata->get_pendown_state;
+	priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD;
+	INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
+
+	/* Setup input device */
+	input->name = "ILI210x Touchscreen";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = dev;
+
+	__set_bit(EV_SYN, input->evbit);
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(EV_ABS, input->evbit);
+	__set_bit(BTN_TOUCH, input->keybit);
+
+	/* Single touch */
+	input_set_abs_params(input, ABS_X, 0, xmax, 0, 0);
+	input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
+
+	/* Multi touch */
+	input_mt_init_slots(input, MAX_TOUCHES, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
+
+	input_set_drvdata(input, priv);
+	i2c_set_clientdata(client, priv);
+
+	error = request_irq(client->irq, ili210x_irq, pdata->irq_flags,
+			    client->name, priv);
+	if (error) {
+		dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
+			error);
+		goto err_free_mem;
+	}
+
+	error = sysfs_create_group(&dev->kobj, &ili210x_attr_group);
+	if (error) {
+		dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
+			error);
+		goto err_free_irq;
+	}
+
+	error = input_register_device(priv->input);
+	if (error) {
+		dev_err(dev, "Cannot register input device, err: %d\n", error);
+		goto err_remove_sysfs;
+	}
+
+	device_init_wakeup(&client->dev, 1);
+
+	dev_dbg(dev,
+		"ILI210x initialized (IRQ: %d), firmware version %d.%d.%d",
+		client->irq, firmware.id, firmware.major, firmware.minor);
+
+	return 0;
+
+err_remove_sysfs:
+	sysfs_remove_group(&dev->kobj, &ili210x_attr_group);
+err_free_irq:
+	free_irq(client->irq, priv);
+err_free_mem:
+	input_free_device(input);
+	kfree(priv);
+	return error;
+}
+
+static int ili210x_i2c_remove(struct i2c_client *client)
+{
+	struct ili210x *priv = i2c_get_clientdata(client);
+
+	sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group);
+	free_irq(priv->client->irq, priv);
+	cancel_delayed_work_sync(&priv->dwork);
+	input_unregister_device(priv->input);
+	kfree(priv);
+
+	return 0;
+}
+
+static int __maybe_unused ili210x_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	if (device_may_wakeup(&client->dev))
+		enable_irq_wake(client->irq);
+
+	return 0;
+}
+
+static int __maybe_unused ili210x_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	if (device_may_wakeup(&client->dev))
+		disable_irq_wake(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm,
+			 ili210x_i2c_suspend, ili210x_i2c_resume);
+
+static const struct i2c_device_id ili210x_i2c_id[] = {
+	{ "ili210x", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
+
+static struct i2c_driver ili210x_ts_driver = {
+	.driver = {
+		.name = "ili210x_i2c",
+		.pm = &ili210x_i2c_pm,
+	},
+	.id_table = ili210x_i2c_id,
+	.probe = ili210x_i2c_probe,
+	.remove = ili210x_i2c_remove,
+};
+
+module_i2c_driver(ili210x_ts_driver);
+
+MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>");
+MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c
new file mode 100644
index 0000000..8275267
--- /dev/null
+++ b/drivers/input/touchscreen/imx6ul_tsc.c
@@ -0,0 +1,533 @@
+/*
+ * Freescale i.MX6UL touchscreen controller driver
+ *
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+/* ADC configuration registers field define */
+#define ADC_AIEN		(0x1 << 7)
+#define ADC_CONV_DISABLE	0x1F
+#define ADC_CAL			(0x1 << 7)
+#define ADC_CALF		0x2
+#define ADC_12BIT_MODE		(0x2 << 2)
+#define ADC_IPG_CLK		0x00
+#define ADC_CLK_DIV_8		(0x03 << 5)
+#define ADC_SHORT_SAMPLE_MODE	(0x0 << 4)
+#define ADC_HARDWARE_TRIGGER	(0x1 << 13)
+#define SELECT_CHANNEL_4	0x04
+#define SELECT_CHANNEL_1	0x01
+#define DISABLE_CONVERSION_INT	(0x0 << 7)
+
+/* ADC registers */
+#define REG_ADC_HC0		0x00
+#define REG_ADC_HC1		0x04
+#define REG_ADC_HC2		0x08
+#define REG_ADC_HC3		0x0C
+#define REG_ADC_HC4		0x10
+#define REG_ADC_HS		0x14
+#define REG_ADC_R0		0x18
+#define REG_ADC_CFG		0x2C
+#define REG_ADC_GC		0x30
+#define REG_ADC_GS		0x34
+
+#define ADC_TIMEOUT		msecs_to_jiffies(100)
+
+/* TSC registers */
+#define REG_TSC_BASIC_SETING	0x00
+#define REG_TSC_PRE_CHARGE_TIME	0x10
+#define REG_TSC_FLOW_CONTROL	0x20
+#define REG_TSC_MEASURE_VALUE	0x30
+#define REG_TSC_INT_EN		0x40
+#define REG_TSC_INT_SIG_EN	0x50
+#define REG_TSC_INT_STATUS	0x60
+#define REG_TSC_DEBUG_MODE	0x70
+#define REG_TSC_DEBUG_MODE2	0x80
+
+/* TSC configuration registers field define */
+#define DETECT_4_WIRE_MODE	(0x0 << 4)
+#define AUTO_MEASURE		0x1
+#define MEASURE_SIGNAL		0x1
+#define DETECT_SIGNAL		(0x1 << 4)
+#define VALID_SIGNAL		(0x1 << 8)
+#define MEASURE_INT_EN		0x1
+#define MEASURE_SIG_EN		0x1
+#define VALID_SIG_EN		(0x1 << 8)
+#define DE_GLITCH_2		(0x2 << 29)
+#define START_SENSE		(0x1 << 12)
+#define TSC_DISABLE		(0x1 << 16)
+#define DETECT_MODE		0x2
+
+struct imx6ul_tsc {
+	struct device *dev;
+	struct input_dev *input;
+	void __iomem *tsc_regs;
+	void __iomem *adc_regs;
+	struct clk *tsc_clk;
+	struct clk *adc_clk;
+	struct gpio_desc *xnur_gpio;
+
+	int measure_delay_time;
+	int pre_charge_time;
+
+	struct completion completion;
+};
+
+/*
+ * TSC module need ADC to get the measure value. So
+ * before config TSC, we should initialize ADC module.
+ */
+static int imx6ul_adc_init(struct imx6ul_tsc *tsc)
+{
+	int adc_hc = 0;
+	int adc_gc;
+	int adc_gs;
+	int adc_cfg;
+	int timeout;
+
+	reinit_completion(&tsc->completion);
+
+	adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG);
+	adc_cfg |= ADC_12BIT_MODE | ADC_IPG_CLK;
+	adc_cfg |= ADC_CLK_DIV_8 | ADC_SHORT_SAMPLE_MODE;
+	adc_cfg &= ~ADC_HARDWARE_TRIGGER;
+	writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG);
+
+	/* enable calibration interrupt */
+	adc_hc |= ADC_AIEN;
+	adc_hc |= ADC_CONV_DISABLE;
+	writel(adc_hc, tsc->adc_regs + REG_ADC_HC0);
+
+	/* start ADC calibration */
+	adc_gc = readl(tsc->adc_regs + REG_ADC_GC);
+	adc_gc |= ADC_CAL;
+	writel(adc_gc, tsc->adc_regs + REG_ADC_GC);
+
+	timeout = wait_for_completion_timeout
+			(&tsc->completion, ADC_TIMEOUT);
+	if (timeout == 0) {
+		dev_err(tsc->dev, "Timeout for adc calibration\n");
+		return -ETIMEDOUT;
+	}
+
+	adc_gs = readl(tsc->adc_regs + REG_ADC_GS);
+	if (adc_gs & ADC_CALF) {
+		dev_err(tsc->dev, "ADC calibration failed\n");
+		return -EINVAL;
+	}
+
+	/* TSC need the ADC work in hardware trigger */
+	adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG);
+	adc_cfg |= ADC_HARDWARE_TRIGGER;
+	writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG);
+
+	return 0;
+}
+
+/*
+ * This is a TSC workaround. Currently TSC misconnect two
+ * ADC channels, this function remap channel configure for
+ * hardware trigger.
+ */
+static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc)
+{
+	int adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4;
+
+	adc_hc0 = DISABLE_CONVERSION_INT;
+	writel(adc_hc0, tsc->adc_regs + REG_ADC_HC0);
+
+	adc_hc1 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_4;
+	writel(adc_hc1, tsc->adc_regs + REG_ADC_HC1);
+
+	adc_hc2 = DISABLE_CONVERSION_INT;
+	writel(adc_hc2, tsc->adc_regs + REG_ADC_HC2);
+
+	adc_hc3 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_1;
+	writel(adc_hc3, tsc->adc_regs + REG_ADC_HC3);
+
+	adc_hc4 = DISABLE_CONVERSION_INT;
+	writel(adc_hc4, tsc->adc_regs + REG_ADC_HC4);
+}
+
+/*
+ * TSC setting, confige the pre-charge time and measure delay time.
+ * different touch screen may need different pre-charge time and
+ * measure delay time.
+ */
+static void imx6ul_tsc_set(struct imx6ul_tsc *tsc)
+{
+	int basic_setting = 0;
+	int start;
+
+	basic_setting |= tsc->measure_delay_time << 8;
+	basic_setting |= DETECT_4_WIRE_MODE | AUTO_MEASURE;
+	writel(basic_setting, tsc->tsc_regs + REG_TSC_BASIC_SETING);
+
+	writel(DE_GLITCH_2, tsc->tsc_regs + REG_TSC_DEBUG_MODE2);
+
+	writel(tsc->pre_charge_time, tsc->tsc_regs + REG_TSC_PRE_CHARGE_TIME);
+	writel(MEASURE_INT_EN, tsc->tsc_regs + REG_TSC_INT_EN);
+	writel(MEASURE_SIG_EN | VALID_SIG_EN,
+		tsc->tsc_regs + REG_TSC_INT_SIG_EN);
+
+	/* start sense detection */
+	start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
+	start |= START_SENSE;
+	start &= ~TSC_DISABLE;
+	writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
+}
+
+static int imx6ul_tsc_init(struct imx6ul_tsc *tsc)
+{
+	int err;
+
+	err = imx6ul_adc_init(tsc);
+	if (err)
+		return err;
+	imx6ul_tsc_channel_config(tsc);
+	imx6ul_tsc_set(tsc);
+
+	return 0;
+}
+
+static void imx6ul_tsc_disable(struct imx6ul_tsc *tsc)
+{
+	int tsc_flow;
+	int adc_cfg;
+
+	/* TSC controller enters to idle status */
+	tsc_flow = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
+	tsc_flow |= TSC_DISABLE;
+	writel(tsc_flow, tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
+
+	/* ADC controller enters to stop mode */
+	adc_cfg = readl(tsc->adc_regs + REG_ADC_HC0);
+	adc_cfg |= ADC_CONV_DISABLE;
+	writel(adc_cfg, tsc->adc_regs + REG_ADC_HC0);
+}
+
+/* Delay some time (max 2ms), wait the pre-charge done. */
+static bool tsc_wait_detect_mode(struct imx6ul_tsc *tsc)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(2);
+	int state_machine;
+	int debug_mode2;
+
+	do {
+		if (time_after(jiffies, timeout))
+			return false;
+
+		usleep_range(200, 400);
+		debug_mode2 = readl(tsc->tsc_regs + REG_TSC_DEBUG_MODE2);
+		state_machine = (debug_mode2 >> 20) & 0x7;
+	} while (state_machine != DETECT_MODE);
+
+	usleep_range(200, 400);
+	return true;
+}
+
+static irqreturn_t tsc_irq_fn(int irq, void *dev_id)
+{
+	struct imx6ul_tsc *tsc = dev_id;
+	int status;
+	int value;
+	int x, y;
+	int start;
+
+	status = readl(tsc->tsc_regs + REG_TSC_INT_STATUS);
+
+	/* write 1 to clear the bit measure-signal */
+	writel(MEASURE_SIGNAL | DETECT_SIGNAL,
+		tsc->tsc_regs + REG_TSC_INT_STATUS);
+
+	/* It's a HW self-clean bit. Set this bit and start sense detection */
+	start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
+	start |= START_SENSE;
+	writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
+
+	if (status & MEASURE_SIGNAL) {
+		value = readl(tsc->tsc_regs + REG_TSC_MEASURE_VALUE);
+		x = (value >> 16) & 0x0fff;
+		y = value & 0x0fff;
+
+		/*
+		 * In detect mode, we can get the xnur gpio value,
+		 * otherwise assume contact is stiull active.
+		 */
+		if (!tsc_wait_detect_mode(tsc) ||
+		    gpiod_get_value_cansleep(tsc->xnur_gpio)) {
+			input_report_key(tsc->input, BTN_TOUCH, 1);
+			input_report_abs(tsc->input, ABS_X, x);
+			input_report_abs(tsc->input, ABS_Y, y);
+		} else {
+			input_report_key(tsc->input, BTN_TOUCH, 0);
+		}
+
+		input_sync(tsc->input);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t adc_irq_fn(int irq, void *dev_id)
+{
+	struct imx6ul_tsc *tsc = dev_id;
+	int coco;
+	int value;
+
+	coco = readl(tsc->adc_regs + REG_ADC_HS);
+	if (coco & 0x01) {
+		value = readl(tsc->adc_regs + REG_ADC_R0);
+		complete(&tsc->completion);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int imx6ul_tsc_open(struct input_dev *input_dev)
+{
+	struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
+	int err;
+
+	err = clk_prepare_enable(tsc->adc_clk);
+	if (err) {
+		dev_err(tsc->dev,
+			"Could not prepare or enable the adc clock: %d\n",
+			err);
+		return err;
+	}
+
+	err = clk_prepare_enable(tsc->tsc_clk);
+	if (err) {
+		dev_err(tsc->dev,
+			"Could not prepare or enable the tsc clock: %d\n",
+			err);
+		clk_disable_unprepare(tsc->adc_clk);
+		return err;
+	}
+
+	return imx6ul_tsc_init(tsc);
+}
+
+static void imx6ul_tsc_close(struct input_dev *input_dev)
+{
+	struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
+
+	imx6ul_tsc_disable(tsc);
+
+	clk_disable_unprepare(tsc->tsc_clk);
+	clk_disable_unprepare(tsc->adc_clk);
+}
+
+static int imx6ul_tsc_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct imx6ul_tsc *tsc;
+	struct input_dev *input_dev;
+	struct resource *tsc_mem;
+	struct resource *adc_mem;
+	int err;
+	int tsc_irq;
+	int adc_irq;
+
+	tsc = devm_kzalloc(&pdev->dev, sizeof(*tsc), GFP_KERNEL);
+	if (!tsc)
+		return -ENOMEM;
+
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!input_dev)
+		return -ENOMEM;
+
+	input_dev->name = "iMX6UL Touchscreen Controller";
+	input_dev->id.bustype = BUS_HOST;
+
+	input_dev->open = imx6ul_tsc_open;
+	input_dev->close = imx6ul_tsc_close;
+
+	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+	input_set_abs_params(input_dev, ABS_X, 0, 0xFFF, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, 0xFFF, 0, 0);
+
+	input_set_drvdata(input_dev, tsc);
+
+	tsc->dev = &pdev->dev;
+	tsc->input = input_dev;
+	init_completion(&tsc->completion);
+
+	tsc->xnur_gpio = devm_gpiod_get(&pdev->dev, "xnur", GPIOD_IN);
+	if (IS_ERR(tsc->xnur_gpio)) {
+		err = PTR_ERR(tsc->xnur_gpio);
+		dev_err(&pdev->dev,
+			"failed to request GPIO tsc_X- (xnur): %d\n", err);
+		return err;
+	}
+
+	tsc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	tsc->tsc_regs = devm_ioremap_resource(&pdev->dev, tsc_mem);
+	if (IS_ERR(tsc->tsc_regs)) {
+		err = PTR_ERR(tsc->tsc_regs);
+		dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err);
+		return err;
+	}
+
+	adc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	tsc->adc_regs = devm_ioremap_resource(&pdev->dev, adc_mem);
+	if (IS_ERR(tsc->adc_regs)) {
+		err = PTR_ERR(tsc->adc_regs);
+		dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err);
+		return err;
+	}
+
+	tsc->tsc_clk = devm_clk_get(&pdev->dev, "tsc");
+	if (IS_ERR(tsc->tsc_clk)) {
+		err = PTR_ERR(tsc->tsc_clk);
+		dev_err(&pdev->dev, "failed getting tsc clock: %d\n", err);
+		return err;
+	}
+
+	tsc->adc_clk = devm_clk_get(&pdev->dev, "adc");
+	if (IS_ERR(tsc->adc_clk)) {
+		err = PTR_ERR(tsc->adc_clk);
+		dev_err(&pdev->dev, "failed getting adc clock: %d\n", err);
+		return err;
+	}
+
+	tsc_irq = platform_get_irq(pdev, 0);
+	if (tsc_irq < 0) {
+		dev_err(&pdev->dev, "no tsc irq resource?\n");
+		return tsc_irq;
+	}
+
+	adc_irq = platform_get_irq(pdev, 1);
+	if (adc_irq < 0) {
+		dev_err(&pdev->dev, "no adc irq resource?\n");
+		return adc_irq;
+	}
+
+	err = devm_request_threaded_irq(tsc->dev, tsc_irq,
+					NULL, tsc_irq_fn, IRQF_ONESHOT,
+					dev_name(&pdev->dev), tsc);
+	if (err) {
+		dev_err(&pdev->dev,
+			"failed requesting tsc irq %d: %d\n",
+			tsc_irq, err);
+		return err;
+	}
+
+	err = devm_request_irq(tsc->dev, adc_irq, adc_irq_fn, 0,
+				dev_name(&pdev->dev), tsc);
+	if (err) {
+		dev_err(&pdev->dev,
+			"failed requesting adc irq %d: %d\n",
+			adc_irq, err);
+		return err;
+	}
+
+	err = of_property_read_u32(np, "measure-delay-time",
+				   &tsc->measure_delay_time);
+	if (err)
+		tsc->measure_delay_time = 0xffff;
+
+	err = of_property_read_u32(np, "pre-charge-time",
+				   &tsc->pre_charge_time);
+	if (err)
+		tsc->pre_charge_time = 0xfff;
+
+	err = input_register_device(tsc->input);
+	if (err) {
+		dev_err(&pdev->dev,
+			"failed to register input device: %d\n", err);
+		return err;
+	}
+
+	platform_set_drvdata(pdev, tsc);
+	return 0;
+}
+
+static int __maybe_unused imx6ul_tsc_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct imx6ul_tsc *tsc = platform_get_drvdata(pdev);
+	struct input_dev *input_dev = tsc->input;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users) {
+		imx6ul_tsc_disable(tsc);
+
+		clk_disable_unprepare(tsc->tsc_clk);
+		clk_disable_unprepare(tsc->adc_clk);
+	}
+
+	mutex_unlock(&input_dev->mutex);
+
+	return 0;
+}
+
+static int __maybe_unused imx6ul_tsc_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct imx6ul_tsc *tsc = platform_get_drvdata(pdev);
+	struct input_dev *input_dev = tsc->input;
+	int retval = 0;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users) {
+		retval = clk_prepare_enable(tsc->adc_clk);
+		if (retval)
+			goto out;
+
+		retval = clk_prepare_enable(tsc->tsc_clk);
+		if (retval) {
+			clk_disable_unprepare(tsc->adc_clk);
+			goto out;
+		}
+
+		retval = imx6ul_tsc_init(tsc);
+	}
+
+out:
+	mutex_unlock(&input_dev->mutex);
+	return retval;
+}
+
+static SIMPLE_DEV_PM_OPS(imx6ul_tsc_pm_ops,
+			 imx6ul_tsc_suspend, imx6ul_tsc_resume);
+
+static const struct of_device_id imx6ul_tsc_match[] = {
+	{ .compatible = "fsl,imx6ul-tsc", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx6ul_tsc_match);
+
+static struct platform_driver imx6ul_tsc_driver = {
+	.driver		= {
+		.name	= "imx6ul-tsc",
+		.of_match_table	= imx6ul_tsc_match,
+		.pm	= &imx6ul_tsc_pm_ops,
+	},
+	.probe		= imx6ul_tsc_probe,
+};
+module_platform_driver(imx6ul_tsc_driver);
+
+MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
+MODULE_DESCRIPTION("Freescale i.MX6UL Touchscreen controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/inexio.c b/drivers/input/touchscreen/inexio.c
new file mode 100644
index 0000000..adb80b6
--- /dev/null
+++ b/drivers/input/touchscreen/inexio.c
@@ -0,0 +1,191 @@
+/*
+ * iNexio serial touchscreen driver
+ *
+ * Copyright (c) 2008 Richard Lemon
+ * Based on the mtouch driver (c) Vojtech Pavlik and Dan Streetman
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * 2008/06/19 Richard Lemon <richard@codelemon.com>
+ *   Copied mtouch.c and edited for iNexio protocol
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"iNexio serial touchscreen driver"
+
+MODULE_AUTHOR("Richard Lemon <richard@codelemon.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define INEXIO_FORMAT_TOUCH_BIT 0x01
+#define INEXIO_FORMAT_LENGTH 5
+#define INEXIO_RESPONSE_BEGIN_BYTE 0x80
+
+/* todo: check specs for max length of all responses */
+#define INEXIO_MAX_LENGTH 16
+
+#define INEXIO_MIN_XC 0
+#define INEXIO_MAX_XC 0x3fff
+#define INEXIO_MIN_YC 0
+#define INEXIO_MAX_YC 0x3fff
+
+#define INEXIO_GET_XC(data) (((data[1])<<7) | data[2])
+#define INEXIO_GET_YC(data) (((data[3])<<7) | data[4])
+#define INEXIO_GET_TOUCHED(data) (INEXIO_FORMAT_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct inexio {
+	struct input_dev *dev;
+	struct serio *serio;
+	int idx;
+	unsigned char data[INEXIO_MAX_LENGTH];
+	char phys[32];
+};
+
+static void inexio_process_data(struct inexio *pinexio)
+{
+	struct input_dev *dev = pinexio->dev;
+
+	if (INEXIO_FORMAT_LENGTH == ++pinexio->idx) {
+		input_report_abs(dev, ABS_X, INEXIO_GET_XC(pinexio->data));
+		input_report_abs(dev, ABS_Y, INEXIO_GET_YC(pinexio->data));
+		input_report_key(dev, BTN_TOUCH, INEXIO_GET_TOUCHED(pinexio->data));
+		input_sync(dev);
+
+		pinexio->idx = 0;
+	}
+}
+
+static irqreturn_t inexio_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct inexio* pinexio = serio_get_drvdata(serio);
+
+	pinexio->data[pinexio->idx] = data;
+
+	if (INEXIO_RESPONSE_BEGIN_BYTE&pinexio->data[0])
+		inexio_process_data(pinexio);
+	else
+		printk(KERN_DEBUG "inexio.c: unknown/unsynchronized data from device, byte %x\n",pinexio->data[0]);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * inexio_disconnect() is the opposite of inexio_connect()
+ */
+
+static void inexio_disconnect(struct serio *serio)
+{
+	struct inexio* pinexio = serio_get_drvdata(serio);
+
+	input_get_device(pinexio->dev);
+	input_unregister_device(pinexio->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_put_device(pinexio->dev);
+	kfree(pinexio);
+}
+
+/*
+ * inexio_connect() is the routine that is called when someone adds a
+ * new serio device that supports iNexio protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+
+static int inexio_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct inexio *pinexio;
+	struct input_dev *input_dev;
+	int err;
+
+	pinexio = kzalloc(sizeof(struct inexio), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!pinexio || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	pinexio->serio = serio;
+	pinexio->dev = input_dev;
+	snprintf(pinexio->phys, sizeof(pinexio->phys), "%s/input0", serio->phys);
+
+	input_dev->name = "iNexio Serial TouchScreen";
+	input_dev->phys = pinexio->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_INEXIO;
+	input_dev->id.product = 0;
+	input_dev->id.version = 0x0001;
+	input_dev->dev.parent = &serio->dev;
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(pinexio->dev, ABS_X, INEXIO_MIN_XC, INEXIO_MAX_XC, 0, 0);
+	input_set_abs_params(pinexio->dev, ABS_Y, INEXIO_MIN_YC, INEXIO_MAX_YC, 0, 0);
+
+	serio_set_drvdata(serio, pinexio);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(pinexio->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(pinexio);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id inexio_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_INEXIO,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, inexio_serio_ids);
+
+static struct serio_driver inexio_drv = {
+	.driver		= {
+		.name	= "inexio",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= inexio_serio_ids,
+	.interrupt	= inexio_interrupt,
+	.connect	= inexio_connect,
+	.disconnect	= inexio_disconnect,
+};
+
+module_serio_driver(inexio_drv);
diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c
new file mode 100644
index 0000000..b4f0725
--- /dev/null
+++ b/drivers/input/touchscreen/intel-mid-touch.c
@@ -0,0 +1,654 @@
+/*
+ * Intel MID Resistive Touch Screen Driver
+ *
+ * Copyright (C) 2008 Intel Corp
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com)
+ *			    Ramesh Agarwal (ramesh.agarwal@intel.com)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * TODO:
+ *	review conversion of r/m/w sequences
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/param.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <asm/intel_scu_ipc.h>
+#include <linux/device.h>
+
+/* PMIC Interrupt registers */
+#define PMIC_REG_ID1		0x00 /* PMIC ID1 register */
+
+/* PMIC Interrupt registers */
+#define PMIC_REG_INT		0x04 /* PMIC interrupt register */
+#define PMIC_REG_MINT		0x05 /* PMIC interrupt mask register */
+
+/* ADC Interrupt registers */
+#define PMIC_REG_ADCINT		0x5F /* ADC interrupt register */
+#define PMIC_REG_MADCINT	0x60 /* ADC interrupt mask register */
+
+/* ADC Control registers */
+#define PMIC_REG_ADCCNTL1	0x61 /* ADC control register */
+
+/* ADC Channel Selection registers */
+#define PMICADDR0		0xA4
+#define END_OF_CHANNEL		0x1F
+
+/* ADC Result register */
+#define PMIC_REG_ADCSNS0H	0x64
+
+/* ADC channels for touch screen */
+#define MRST_TS_CHAN10		0xA /* Touch screen X+ connection */
+#define MRST_TS_CHAN11		0xB /* Touch screen X- connection */
+#define MRST_TS_CHAN12		0xC /* Touch screen Y+ connection */
+#define MRST_TS_CHAN13		0xD /* Touch screen Y- connection */
+
+/* Touch screen channel BIAS constants */
+#define MRST_XBIAS		0x20
+#define MRST_YBIAS		0x40
+#define MRST_ZBIAS		0x80
+
+/* Touch screen coordinates */
+#define MRST_X_MIN		10
+#define MRST_X_MAX		1024
+#define MRST_X_FUZZ		5
+#define MRST_Y_MIN		10
+#define MRST_Y_MAX		1024
+#define MRST_Y_FUZZ		5
+#define MRST_PRESSURE_MIN	0
+#define MRST_PRESSURE_NOMINAL	50
+#define MRST_PRESSURE_MAX	100
+
+#define WAIT_ADC_COMPLETION	10 /* msec */
+
+/* PMIC ADC round robin delays */
+#define ADC_LOOP_DELAY0		0x0 /* Continuous loop */
+#define ADC_LOOP_DELAY1		0x1 /* 4.5  ms approximate */
+
+/* PMIC Vendor Identifiers */
+#define PMIC_VENDOR_FS		0 /* PMIC vendor FreeScale */
+#define PMIC_VENDOR_MAXIM	1 /* PMIC vendor MAXIM */
+#define PMIC_VENDOR_NEC		2 /* PMIC vendor NEC */
+#define MRSTOUCH_MAX_CHANNELS	32 /* Maximum ADC channels */
+
+/* Touch screen device structure */
+struct mrstouch_dev {
+	struct device *dev; /* device associated with touch screen */
+	struct input_dev *input;
+	char phys[32];
+	u16 asr;		/* Address selection register */
+	int irq;
+	unsigned int vendor;	/* PMIC vendor */
+	unsigned int rev;	/* PMIC revision */
+
+	int (*read_prepare)(struct mrstouch_dev *tsdev);
+	int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z);
+	int (*read_finish)(struct mrstouch_dev *tsdev);
+};
+
+
+/*************************** NEC and Maxim Interface ************************/
+
+static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev)
+{
+	return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20);
+}
+
+/*
+ * Enables PENDET interrupt.
+ */
+static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev)
+{
+	int err;
+
+	err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20);
+	if (!err)
+		err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05);
+
+	return err;
+}
+
+/*
+ * Reads PMIC ADC touch screen result
+ * Reads ADC storage registers for higher 7 and lower 3 bits and
+ * converts the two readings into a single value and turns off gain bit
+ */
+static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm)
+{
+	int err;
+	u16 result;
+	u32 res;
+
+	result = PMIC_REG_ADCSNS0H + offset;
+
+	if (chan == MRST_TS_CHAN12)
+		result += 4;
+
+	err = intel_scu_ipc_ioread32(result, &res);
+	if (err)
+		return err;
+
+	/* Mash the bits up */
+
+	*vp = (res & 0xFF) << 3;	/* Highest 7 bits */
+	*vp |= (res >> 8) & 0x07;	/* Lower 3 bits */
+	*vp &= 0x3FF;
+
+	res >>= 16;
+
+	*vm = (res & 0xFF) << 3;	/* Highest 7 bits */
+	*vm |= (res >> 8) & 0x07;	/* Lower 3 bits */
+	*vm &= 0x3FF;
+
+	return 0;
+}
+
+/*
+ * Enables X, Y and Z bias values
+ * Enables YPYM for X channels and XPXM for Y channels
+ */
+static int mrstouch_ts_bias_set(uint offset, uint bias)
+{
+	int count;
+	u16 chan, start;
+	u16 reg[4];
+	u8 data[4];
+
+	chan = PMICADDR0 + offset;
+	start = MRST_TS_CHAN10;
+
+	for (count = 0; count <= 3; count++) {
+		reg[count] = chan++;
+		data[count] = bias | (start + count);
+	}
+
+	return intel_scu_ipc_writev(reg, data, 4);
+}
+
+/* To read touch screen channel values */
+static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev,
+				 u16 *x, u16 *y, u16 *z)
+{
+	int err;
+	u16 xm, ym, zm;
+
+	/* configure Y bias for X channels */
+	err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS);
+	if (err)
+		goto ipc_error;
+
+	msleep(WAIT_ADC_COMPLETION);
+
+	/* read x+ and x- channels */
+	err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm);
+	if (err)
+		goto ipc_error;
+
+	/* configure x bias for y channels */
+	err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS);
+	if (err)
+		goto ipc_error;
+
+	msleep(WAIT_ADC_COMPLETION);
+
+	/* read y+ and y- channels */
+	err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym);
+	if (err)
+		goto ipc_error;
+
+	/* configure z bias for x and y channels */
+	err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS);
+	if (err)
+		goto ipc_error;
+
+	msleep(WAIT_ADC_COMPLETION);
+
+	/* read z+ and z- channels */
+	err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm);
+	if (err)
+		goto ipc_error;
+
+	return 0;
+
+ipc_error:
+	dev_err(tsdev->dev, "ipc error during adc read\n");
+	return err;
+}
+
+
+/*************************** Freescale Interface ************************/
+
+static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev)
+{
+	int err, count;
+	u16 chan;
+	u16 reg[5];
+	u8 data[5];
+
+	/* Stop the ADC */
+	err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02);
+	if (err)
+		goto ipc_error;
+
+	chan = PMICADDR0 + tsdev->asr;
+
+	/* Set X BIAS */
+	for (count = 0; count <= 3; count++) {
+		reg[count] = chan++;
+		data[count] = 0x2A;
+	}
+	reg[count] =  chan++; /* Dummy */
+	data[count] = 0;
+
+	err = intel_scu_ipc_writev(reg, data, 5);
+	if (err)
+		goto ipc_error;
+
+	msleep(WAIT_ADC_COMPLETION);
+
+	/* Set Y BIAS */
+	for (count = 0; count <= 3; count++) {
+		reg[count] = chan++;
+		data[count] = 0x4A;
+	}
+	reg[count] = chan++; /* Dummy */
+	data[count] = 0;
+
+	err = intel_scu_ipc_writev(reg, data, 5);
+	if (err)
+		goto ipc_error;
+
+	msleep(WAIT_ADC_COMPLETION);
+
+	/* Set Z BIAS */
+	err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A);
+	if (err)
+		goto ipc_error;
+
+	msleep(WAIT_ADC_COMPLETION);
+
+	return 0;
+
+ipc_error:
+	dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+	return err;
+}
+
+static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev,
+				u16 *x, u16 *y, u16 *z)
+{
+	int err;
+	u16 result;
+	u16 reg[4];
+	u8 data[4];
+
+	result = PMIC_REG_ADCSNS0H + tsdev->asr;
+
+	reg[0] = result + 4;
+	reg[1] = result + 5;
+	reg[2] = result + 16;
+	reg[3] = result + 17;
+
+	err = intel_scu_ipc_readv(reg, data, 4);
+	if (err)
+		goto ipc_error;
+
+	*x = data[0] << 3; /* Higher 7 bits */
+	*x |= data[1] & 0x7; /* Lower 3 bits */
+	*x &= 0x3FF;
+
+	*y = data[2] << 3; /* Higher 7 bits */
+	*y |= data[3] & 0x7; /* Lower 3 bits */
+	*y &= 0x3FF;
+
+	/* Read Z value */
+	reg[0] = result + 28;
+	reg[1] = result + 29;
+
+	err = intel_scu_ipc_readv(reg, data, 4);
+	if (err)
+		goto ipc_error;
+
+	*z = data[0] << 3; /* Higher 7 bits */
+	*z |= data[1] & 0x7; /* Lower 3 bits */
+	*z &= 0x3FF;
+
+	return 0;
+
+ipc_error:
+	dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+	return err;
+}
+
+static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev)
+{
+	int err, count;
+	u16 chan;
+	u16 reg[5];
+	u8 data[5];
+
+	/* Clear all TS channels */
+	chan = PMICADDR0 + tsdev->asr;
+	for (count = 0; count <= 4; count++) {
+		reg[count] = chan++;
+		data[count] = 0;
+	}
+	err = intel_scu_ipc_writev(reg, data, 5);
+	if (err)
+		goto ipc_error;
+
+	for (count = 0; count <= 4; count++) {
+		reg[count] = chan++;
+		data[count] = 0;
+	}
+	err = intel_scu_ipc_writev(reg, data, 5);
+	if (err)
+		goto ipc_error;
+
+	err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000);
+	if (err)
+		goto ipc_error;
+
+	/* Start ADC */
+	err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02);
+	if (err)
+		goto ipc_error;
+
+	return 0;
+
+ipc_error:
+	dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+	return err;
+}
+
+static void mrstouch_report_event(struct input_dev *input,
+			unsigned int x, unsigned int y, unsigned int z)
+{
+	if (z > MRST_PRESSURE_NOMINAL) {
+		/* Pen touched, report button touch and coordinates */
+		input_report_key(input, BTN_TOUCH, 1);
+		input_report_abs(input, ABS_X, x);
+		input_report_abs(input, ABS_Y, y);
+	} else {
+		input_report_key(input, BTN_TOUCH, 0);
+	}
+
+	input_report_abs(input, ABS_PRESSURE, z);
+	input_sync(input);
+}
+
+/* PENDET interrupt handler */
+static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id)
+{
+	struct mrstouch_dev *tsdev = dev_id;
+	u16 x, y, z;
+
+	/*
+	 * Should we lower thread priority? Probably not, since we are
+	 * not spinning but sleeping...
+	 */
+
+	if (tsdev->read_prepare(tsdev))
+		goto out;
+
+	do {
+		if (tsdev->read(tsdev, &x, &y, &z))
+			break;
+
+		mrstouch_report_event(tsdev->input, x, y, z);
+	} while (z > MRST_PRESSURE_NOMINAL);
+
+	tsdev->read_finish(tsdev);
+
+out:
+	return IRQ_HANDLED;
+}
+
+/* Utility to read PMIC ID */
+static int mrstouch_read_pmic_id(uint *vendor, uint *rev)
+{
+	int err;
+	u8 r;
+
+	err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r);
+	if (err)
+		return err;
+
+	*vendor = r & 0x7;
+	*rev = (r >> 3) & 0x7;
+
+	return 0;
+}
+
+/*
+ * Parse ADC channels to find end of the channel configured by other ADC user
+ * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels
+ */
+static int mrstouch_chan_parse(struct mrstouch_dev *tsdev)
+{
+	int found = 0;
+	int err, i;
+	u8 r8;
+
+	for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {
+		err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8);
+		if (err)
+			return err;
+
+		if (r8 == END_OF_CHANNEL) {
+			found = i;
+			break;
+		}
+	}
+
+	if (tsdev->vendor == PMIC_VENDOR_FS) {
+		if (found > MRSTOUCH_MAX_CHANNELS - 18)
+			return -ENOSPC;
+	} else {
+		if (found > MRSTOUCH_MAX_CHANNELS - 4)
+			return -ENOSPC;
+	}
+
+	return found;
+}
+
+
+/*
+ * Writes touch screen channels to ADC address selection registers
+ */
+static int mrstouch_ts_chan_set(uint offset)
+{
+	u16 chan;
+
+	int ret, count;
+
+	chan = PMICADDR0 + offset;
+	for (count = 0; count <= 3; count++) {
+		ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count);
+		if (ret)
+			return ret;
+	}
+	return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL);
+}
+
+/* Initialize ADC */
+static int mrstouch_adc_init(struct mrstouch_dev *tsdev)
+{
+	int err, start;
+	u8 ra, rm;
+
+	err = mrstouch_read_pmic_id(&tsdev->vendor, &tsdev->rev);
+	if (err) {
+		dev_err(tsdev->dev, "Unable to read PMIC id\n");
+		return err;
+	}
+
+	switch (tsdev->vendor) {
+	case PMIC_VENDOR_NEC:
+	case PMIC_VENDOR_MAXIM:
+		tsdev->read_prepare = mrstouch_nec_adc_read_prepare;
+		tsdev->read = mrstouch_nec_adc_read;
+		tsdev->read_finish = mrstouch_nec_adc_read_finish;
+		break;
+
+	case PMIC_VENDOR_FS:
+		tsdev->read_prepare = mrstouch_fs_adc_read_prepare;
+		tsdev->read = mrstouch_fs_adc_read;
+		tsdev->read_finish = mrstouch_fs_adc_read_finish;
+		break;
+
+	default:
+		dev_err(tsdev->dev,
+			"Unsupported touchscreen: %d\n", tsdev->vendor);
+		return -ENXIO;
+	}
+
+	start = mrstouch_chan_parse(tsdev);
+	if (start < 0) {
+		dev_err(tsdev->dev, "Unable to parse channels\n");
+		return start;
+	}
+
+	tsdev->asr = start;
+
+	/*
+	 * ADC power on, start, enable PENDET and set loop delay
+	 * ADC loop delay is set to 4.5 ms approximately
+	 * Loop delay more than this results in jitter in adc readings
+	 * Setting loop delay to 0 (continuous loop) in MAXIM stops PENDET
+	 * interrupt generation sometimes.
+	 */
+
+	if (tsdev->vendor == PMIC_VENDOR_FS) {
+		ra = 0xE0 | ADC_LOOP_DELAY0;
+		rm = 0x5;
+	} else {
+		/* NEC and MAXIm not consistent with loop delay 0 */
+		ra = 0xE0 | ADC_LOOP_DELAY1;
+		rm = 0x0;
+
+		/* configure touch screen channels */
+		err = mrstouch_ts_chan_set(tsdev->asr);
+		if (err)
+			return err;
+	}
+
+	err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7);
+	if (err)
+		return err;
+
+	err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+
+/* Probe function for touch screen driver */
+static int mrstouch_probe(struct platform_device *pdev)
+{
+	struct mrstouch_dev *tsdev;
+	struct input_dev *input;
+	int err;
+	int irq;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no interrupt assigned\n");
+		return -EINVAL;
+	}
+
+	tsdev = devm_kzalloc(&pdev->dev, sizeof(struct mrstouch_dev),
+			     GFP_KERNEL);
+	if (!tsdev) {
+		dev_err(&pdev->dev, "unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input) {
+		dev_err(&pdev->dev, "unable to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	tsdev->dev = &pdev->dev;
+	tsdev->input = input;
+	tsdev->irq = irq;
+
+	snprintf(tsdev->phys, sizeof(tsdev->phys),
+		 "%s/input0", dev_name(tsdev->dev));
+
+	err = mrstouch_adc_init(tsdev);
+	if (err) {
+		dev_err(&pdev->dev, "ADC initialization failed\n");
+		return err;
+	}
+
+	input->name = "mrst_touchscreen";
+	input->phys = tsdev->phys;
+	input->dev.parent = tsdev->dev;
+
+	input->id.vendor = tsdev->vendor;
+	input->id.version = tsdev->rev;
+
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_set_abs_params(tsdev->input, ABS_X,
+			     MRST_X_MIN, MRST_X_MAX, MRST_X_FUZZ, 0);
+	input_set_abs_params(tsdev->input, ABS_Y,
+			     MRST_Y_MIN, MRST_Y_MAX, MRST_Y_FUZZ, 0);
+	input_set_abs_params(tsdev->input, ABS_PRESSURE,
+			     MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0);
+
+	err = devm_request_threaded_irq(&pdev->dev, tsdev->irq, NULL,
+					mrstouch_pendet_irq, IRQF_ONESHOT,
+					"mrstouch", tsdev);
+	if (err) {
+		dev_err(tsdev->dev, "unable to allocate irq\n");
+		return err;
+	}
+
+	err = input_register_device(tsdev->input);
+	if (err) {
+		dev_err(tsdev->dev, "unable to register input device\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static struct platform_driver mrstouch_driver = {
+	.driver = {
+		.name	= "pmic_touch",
+	},
+	.probe		= mrstouch_probe,
+};
+module_platform_driver(mrstouch_driver);
+
+MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com");
+MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ipaq-micro-ts.c b/drivers/input/touchscreen/ipaq-micro-ts.c
new file mode 100644
index 0000000..33c1348
--- /dev/null
+++ b/drivers/input/touchscreen/ipaq-micro-ts.c
@@ -0,0 +1,164 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * h3600 atmel micro companion support, touchscreen subdevice
+ * Author : Alessandro Gardich <gremlin@gremlin.it>
+ * Author : Dmitry Artamonow <mad_soft@inbox.ru>
+ * Author : Linus Walleij <linus.walleij@linaro.org>
+ *
+ */
+
+#include <asm/byteorder.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/ipaq-micro.h>
+
+struct touchscreen_data {
+	struct input_dev *input;
+	struct ipaq_micro *micro;
+};
+
+static void micro_ts_receive(void *data, int len, unsigned char *msg)
+{
+	struct touchscreen_data *ts = data;
+
+	if (len == 4) {
+		input_report_abs(ts->input, ABS_X,
+				 be16_to_cpup((__be16 *) &msg[2]));
+		input_report_abs(ts->input, ABS_Y,
+				 be16_to_cpup((__be16 *) &msg[0]));
+		input_report_key(ts->input, BTN_TOUCH, 1);
+		input_sync(ts->input);
+	} else if (len == 0) {
+		input_report_abs(ts->input, ABS_X, 0);
+		input_report_abs(ts->input, ABS_Y, 0);
+		input_report_key(ts->input, BTN_TOUCH, 0);
+		input_sync(ts->input);
+	}
+}
+
+static void micro_ts_toggle_receive(struct touchscreen_data *ts, bool enable)
+{
+	struct ipaq_micro *micro = ts->micro;
+
+	spin_lock_irq(&micro->lock);
+
+	if (enable) {
+		micro->ts = micro_ts_receive;
+		micro->ts_data = ts;
+	} else {
+		micro->ts = NULL;
+		micro->ts_data = NULL;
+	}
+
+	spin_unlock_irq(&ts->micro->lock);
+}
+
+static int micro_ts_open(struct input_dev *input)
+{
+	struct touchscreen_data *ts = input_get_drvdata(input);
+
+	micro_ts_toggle_receive(ts, true);
+
+	return 0;
+}
+
+static void micro_ts_close(struct input_dev *input)
+{
+	struct touchscreen_data *ts = input_get_drvdata(input);
+
+	micro_ts_toggle_receive(ts, false);
+}
+
+static int micro_ts_probe(struct platform_device *pdev)
+{
+	struct ipaq_micro *micro = dev_get_drvdata(pdev->dev.parent);
+	struct touchscreen_data *ts;
+	int error;
+
+	ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	ts->micro = micro;
+
+	ts->input = devm_input_allocate_device(&pdev->dev);
+	if (!ts->input) {
+		dev_err(&pdev->dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	ts->input->name = "ipaq micro ts";
+	ts->input->open = micro_ts_open;
+	ts->input->close = micro_ts_close;
+
+	input_set_drvdata(ts->input, ts);
+
+	input_set_capability(ts->input, EV_KEY, BTN_TOUCH);
+	input_set_capability(ts->input, EV_ABS, ABS_X);
+	input_set_capability(ts->input, EV_ABS, ABS_Y);
+	input_set_abs_params(ts->input, ABS_X, 0, 1023, 0, 0);
+	input_set_abs_params(ts->input, ABS_Y, 0, 1023, 0, 0);
+
+	error = input_register_device(ts->input);
+	if (error) {
+		dev_err(&pdev->dev, "error registering touch input\n");
+		return error;
+	}
+
+	platform_set_drvdata(pdev, ts);
+
+	dev_info(&pdev->dev, "iPAQ micro touchscreen\n");
+
+	return 0;
+}
+
+static int __maybe_unused micro_ts_suspend(struct device *dev)
+{
+	struct touchscreen_data *ts = dev_get_drvdata(dev);
+
+	micro_ts_toggle_receive(ts, false);
+
+	return 0;
+}
+
+static int __maybe_unused micro_ts_resume(struct device *dev)
+{
+	struct touchscreen_data *ts = dev_get_drvdata(dev);
+	struct input_dev *input = ts->input;
+
+	mutex_lock(&input->mutex);
+
+	if (input->users)
+		micro_ts_toggle_receive(ts, true);
+
+	mutex_unlock(&input->mutex);
+
+	return 0;
+}
+
+static const struct dev_pm_ops micro_ts_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(micro_ts_suspend, micro_ts_resume)
+};
+
+static struct platform_driver micro_ts_device_driver = {
+	.driver	= {
+		.name	= "ipaq-micro-ts",
+		.pm	= &micro_ts_dev_pm_ops,
+	},
+	.probe	= micro_ts_probe,
+};
+module_platform_driver(micro_ts_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("driver for iPAQ Atmel micro touchscreen");
+MODULE_ALIAS("platform:ipaq-micro-ts");
diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c
new file mode 100644
index 0000000..ea3b6a5
--- /dev/null
+++ b/drivers/input/touchscreen/jornada720_ts.c
@@ -0,0 +1,153 @@
+/*
+ * drivers/input/touchscreen/jornada720_ts.c
+ *
+ * Copyright (C) 2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
+ *
+ *  Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski@tefnet.pl>
+ *  based on HP Jornada 56x touchscreen driver by Alex Lange <chicken@handhelds.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * HP Jornada 710/720/729 Touchscreen Driver
+ */
+
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/jornada720.h>
+#include <mach/irqs.h>
+
+MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
+MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver");
+MODULE_LICENSE("GPL v2");
+
+struct jornada_ts {
+	struct input_dev *dev;
+	int x_data[4];		/* X sample values */
+	int y_data[4];		/* Y sample values */
+};
+
+static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts)
+{
+	/* 3 low word X samples */
+	jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
+	jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
+	jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
+
+	/* 3 low word Y samples */
+	jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
+	jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
+	jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
+
+	/* combined x samples bits */
+	jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
+
+	/* combined y samples bits */
+	jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
+}
+
+static int jornada720_ts_average(int coords[4])
+{
+	int coord, high_bits = coords[3];
+
+	coord  = coords[0] | ((high_bits & 0x03) << 8);
+	coord += coords[1] | ((high_bits & 0x0c) << 6);
+	coord += coords[2] | ((high_bits & 0x30) << 4);
+
+	return coord / 3;
+}
+
+static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id)
+{
+	struct platform_device *pdev = dev_id;
+	struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
+	struct input_dev *input = jornada_ts->dev;
+	int x, y;
+
+	/* If GPIO_GPIO9 is set to high then report pen up */
+	if (GPLR & GPIO_GPIO(9)) {
+		input_report_key(input, BTN_TOUCH, 0);
+		input_sync(input);
+	} else {
+		jornada_ssp_start();
+
+		/* proper reply to request is always TXDUMMY */
+		if (jornada_ssp_inout(GETTOUCHSAMPLES) == TXDUMMY) {
+			jornada720_ts_collect_data(jornada_ts);
+
+			x = jornada720_ts_average(jornada_ts->x_data);
+			y = jornada720_ts_average(jornada_ts->y_data);
+
+			input_report_key(input, BTN_TOUCH, 1);
+			input_report_abs(input, ABS_X, x);
+			input_report_abs(input, ABS_Y, y);
+			input_sync(input);
+		}
+
+		jornada_ssp_end();
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int jornada720_ts_probe(struct platform_device *pdev)
+{
+	struct jornada_ts *jornada_ts;
+	struct input_dev *input_dev;
+	int error;
+
+	jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL);
+	if (!jornada_ts)
+		return -ENOMEM;
+
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!input_dev)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, jornada_ts);
+
+	jornada_ts->dev = input_dev;
+
+	input_dev->name = "HP Jornada 7xx Touchscreen";
+	input_dev->phys = "jornadats/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->dev.parent = &pdev->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0);
+
+	error = devm_request_irq(&pdev->dev, IRQ_GPIO9,
+				 jornada720_ts_interrupt,
+				 IRQF_TRIGGER_RISING,
+				 "HP7XX Touchscreen driver", pdev);
+	if (error) {
+		dev_err(&pdev->dev, "HP7XX TS : Unable to acquire irq!\n");
+		return error;
+	}
+
+	error = input_register_device(jornada_ts->dev);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:jornada_ts");
+
+static struct platform_driver jornada720_ts_driver = {
+	.probe		= jornada720_ts_probe,
+	.driver		= {
+		.name	= "jornada_ts",
+	},
+};
+module_platform_driver(jornada720_ts_driver);
diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c
new file mode 100644
index 0000000..7fbb3b0
--- /dev/null
+++ b/drivers/input/touchscreen/lpc32xx_ts.c
@@ -0,0 +1,408 @@
+/*
+ * LPC32xx built-in touchscreen driver
+ *
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+/*
+ * Touchscreen controller register offsets
+ */
+#define LPC32XX_TSC_STAT			0x00
+#define LPC32XX_TSC_SEL				0x04
+#define LPC32XX_TSC_CON				0x08
+#define LPC32XX_TSC_FIFO			0x0C
+#define LPC32XX_TSC_DTR				0x10
+#define LPC32XX_TSC_RTR				0x14
+#define LPC32XX_TSC_UTR				0x18
+#define LPC32XX_TSC_TTR				0x1C
+#define LPC32XX_TSC_DXP				0x20
+#define LPC32XX_TSC_MIN_X			0x24
+#define LPC32XX_TSC_MAX_X			0x28
+#define LPC32XX_TSC_MIN_Y			0x2C
+#define LPC32XX_TSC_MAX_Y			0x30
+#define LPC32XX_TSC_AUX_UTR			0x34
+#define LPC32XX_TSC_AUX_MIN			0x38
+#define LPC32XX_TSC_AUX_MAX			0x3C
+
+#define LPC32XX_TSC_STAT_FIFO_OVRRN		(1 << 8)
+#define LPC32XX_TSC_STAT_FIFO_EMPTY		(1 << 7)
+
+#define LPC32XX_TSC_SEL_DEFVAL			0x0284
+
+#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4	(0x1 << 11)
+#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s)	((10 - (s)) << 7)
+#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s)	((10 - (s)) << 4)
+#define LPC32XX_TSC_ADCCON_POWER_UP		(1 << 2)
+#define LPC32XX_TSC_ADCCON_AUTO_EN		(1 << 0)
+
+#define LPC32XX_TSC_FIFO_TS_P_LEVEL		(1 << 31)
+#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x)	(((x) & 0x03FF0000) >> 16)
+#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y)	((y) & 0x000003FF)
+
+#define LPC32XX_TSC_ADCDAT_VALUE_MASK		0x000003FF
+
+#define LPC32XX_TSC_MIN_XY_VAL			0x0
+#define LPC32XX_TSC_MAX_XY_VAL			0x3FF
+
+#define MOD_NAME "ts-lpc32xx"
+
+#define tsc_readl(dev, reg) \
+	__raw_readl((dev)->tsc_base + (reg))
+#define tsc_writel(dev, reg, val) \
+	__raw_writel((val), (dev)->tsc_base + (reg))
+
+struct lpc32xx_tsc {
+	struct input_dev *dev;
+	void __iomem *tsc_base;
+	int irq;
+	struct clk *clk;
+};
+
+static void lpc32xx_fifo_clear(struct lpc32xx_tsc *tsc)
+{
+	while (!(tsc_readl(tsc, LPC32XX_TSC_STAT) &
+			LPC32XX_TSC_STAT_FIFO_EMPTY))
+		tsc_readl(tsc, LPC32XX_TSC_FIFO);
+}
+
+static irqreturn_t lpc32xx_ts_interrupt(int irq, void *dev_id)
+{
+	u32 tmp, rv[4], xs[4], ys[4];
+	int idx;
+	struct lpc32xx_tsc *tsc = dev_id;
+	struct input_dev *input = tsc->dev;
+
+	tmp = tsc_readl(tsc, LPC32XX_TSC_STAT);
+
+	if (tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) {
+		/* FIFO overflow - throw away samples */
+		lpc32xx_fifo_clear(tsc);
+		return IRQ_HANDLED;
+	}
+
+	/*
+	 * Gather and normalize 4 samples. Pen-up events may have less
+	 * than 4 samples, but its ok to pop 4 and let the last sample
+	 * pen status check drop the samples.
+	 */
+	idx = 0;
+	while (idx < 4 &&
+	       !(tsc_readl(tsc, LPC32XX_TSC_STAT) &
+			LPC32XX_TSC_STAT_FIFO_EMPTY)) {
+		tmp = tsc_readl(tsc, LPC32XX_TSC_FIFO);
+		xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
+			LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp);
+		ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
+			LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp);
+		rv[idx] = tmp;
+		idx++;
+	}
+
+	/* Data is only valid if pen is still down in last sample */
+	if (!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && idx == 4) {
+		/* Use average of 2nd and 3rd sample for position */
+		input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2);
+		input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2);
+		input_report_key(input, BTN_TOUCH, 1);
+	} else {
+		input_report_key(input, BTN_TOUCH, 0);
+	}
+
+	input_sync(input);
+
+	return IRQ_HANDLED;
+}
+
+static void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc)
+{
+	/* Disable auto mode */
+	tsc_writel(tsc, LPC32XX_TSC_CON,
+		   tsc_readl(tsc, LPC32XX_TSC_CON) &
+			     ~LPC32XX_TSC_ADCCON_AUTO_EN);
+
+	clk_disable_unprepare(tsc->clk);
+}
+
+static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc)
+{
+	u32 tmp;
+
+	clk_prepare_enable(tsc->clk);
+
+	tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP;
+
+	/* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */
+	tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 |
+	      LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) |
+	      LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10);
+	tsc_writel(tsc, LPC32XX_TSC_CON, tmp);
+
+	/* These values are all preset */
+	tsc_writel(tsc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL);
+	tsc_writel(tsc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL);
+	tsc_writel(tsc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL);
+	tsc_writel(tsc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL);
+	tsc_writel(tsc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL);
+
+	/* Aux support is not used */
+	tsc_writel(tsc, LPC32XX_TSC_AUX_UTR, 0);
+	tsc_writel(tsc, LPC32XX_TSC_AUX_MIN, 0);
+	tsc_writel(tsc, LPC32XX_TSC_AUX_MAX, 0);
+
+	/*
+	 * Set sample rate to about 240Hz per X/Y pair. A single measurement
+	 * consists of 4 pairs which gives about a 60Hz sample rate based on
+	 * a stable 32768Hz clock source. Values are in clocks.
+	 * Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4
+	 */
+	tsc_writel(tsc, LPC32XX_TSC_RTR, 0x2);
+	tsc_writel(tsc, LPC32XX_TSC_DTR, 0x2);
+	tsc_writel(tsc, LPC32XX_TSC_TTR, 0x10);
+	tsc_writel(tsc, LPC32XX_TSC_DXP, 0x4);
+	tsc_writel(tsc, LPC32XX_TSC_UTR, 88);
+
+	lpc32xx_fifo_clear(tsc);
+
+	/* Enable automatic ts event capture */
+	tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN);
+}
+
+static int lpc32xx_ts_open(struct input_dev *dev)
+{
+	struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
+
+	lpc32xx_setup_tsc(tsc);
+
+	return 0;
+}
+
+static void lpc32xx_ts_close(struct input_dev *dev)
+{
+	struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
+
+	lpc32xx_stop_tsc(tsc);
+}
+
+static int lpc32xx_ts_probe(struct platform_device *pdev)
+{
+	struct lpc32xx_tsc *tsc;
+	struct input_dev *input;
+	struct resource *res;
+	resource_size_t size;
+	int irq;
+	int error;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Can't get memory resource\n");
+		return -ENOENT;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "Can't get interrupt resource\n");
+		return irq;
+	}
+
+	tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
+	input = input_allocate_device();
+	if (!tsc || !input) {
+		dev_err(&pdev->dev, "failed allocating memory\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	tsc->dev = input;
+	tsc->irq = irq;
+
+	size = resource_size(res);
+
+	if (!request_mem_region(res->start, size, pdev->name)) {
+		dev_err(&pdev->dev, "TSC registers are not free\n");
+		error = -EBUSY;
+		goto err_free_mem;
+	}
+
+	tsc->tsc_base = ioremap(res->start, size);
+	if (!tsc->tsc_base) {
+		dev_err(&pdev->dev, "Can't map memory\n");
+		error = -ENOMEM;
+		goto err_release_mem;
+	}
+
+	tsc->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(tsc->clk)) {
+		dev_err(&pdev->dev, "failed getting clock\n");
+		error = PTR_ERR(tsc->clk);
+		goto err_unmap;
+	}
+
+	input->name = MOD_NAME;
+	input->phys = "lpc32xx/input0";
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0002;
+	input->id.version = 0x0100;
+	input->dev.parent = &pdev->dev;
+	input->open = lpc32xx_ts_open;
+	input->close = lpc32xx_ts_close;
+
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL,
+			     LPC32XX_TSC_MAX_XY_VAL, 0, 0);
+	input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL,
+			     LPC32XX_TSC_MAX_XY_VAL, 0, 0);
+
+	input_set_drvdata(input, tsc);
+
+	error = request_irq(tsc->irq, lpc32xx_ts_interrupt,
+			    0, pdev->name, tsc);
+	if (error) {
+		dev_err(&pdev->dev, "failed requesting interrupt\n");
+		goto err_put_clock;
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(&pdev->dev, "failed registering input device\n");
+		goto err_free_irq;
+	}
+
+	platform_set_drvdata(pdev, tsc);
+	device_init_wakeup(&pdev->dev, 1);
+
+	return 0;
+
+err_free_irq:
+	free_irq(tsc->irq, tsc);
+err_put_clock:
+	clk_put(tsc->clk);
+err_unmap:
+	iounmap(tsc->tsc_base);
+err_release_mem:
+	release_mem_region(res->start, size);
+err_free_mem:
+	input_free_device(input);
+	kfree(tsc);
+
+	return error;
+}
+
+static int lpc32xx_ts_remove(struct platform_device *pdev)
+{
+	struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev);
+	struct resource *res;
+
+	device_init_wakeup(&pdev->dev, 0);
+	free_irq(tsc->irq, tsc);
+
+	input_unregister_device(tsc->dev);
+
+	clk_put(tsc->clk);
+
+	iounmap(tsc->tsc_base);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(tsc);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int lpc32xx_ts_suspend(struct device *dev)
+{
+	struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
+	struct input_dev *input = tsc->dev;
+
+	/*
+	 * Suspend and resume can be called when the device hasn't been
+	 * enabled. If there are no users that have the device open, then
+	 * avoid calling the TSC stop and start functions as the TSC
+	 * isn't yet clocked.
+	 */
+	mutex_lock(&input->mutex);
+
+	if (input->users) {
+		if (device_may_wakeup(dev))
+			enable_irq_wake(tsc->irq);
+		else
+			lpc32xx_stop_tsc(tsc);
+	}
+
+	mutex_unlock(&input->mutex);
+
+	return 0;
+}
+
+static int lpc32xx_ts_resume(struct device *dev)
+{
+	struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
+	struct input_dev *input = tsc->dev;
+
+	mutex_lock(&input->mutex);
+
+	if (input->users) {
+		if (device_may_wakeup(dev))
+			disable_irq_wake(tsc->irq);
+		else
+			lpc32xx_setup_tsc(tsc);
+	}
+
+	mutex_unlock(&input->mutex);
+
+	return 0;
+}
+
+static const struct dev_pm_ops lpc32xx_ts_pm_ops = {
+	.suspend	= lpc32xx_ts_suspend,
+	.resume		= lpc32xx_ts_resume,
+};
+#define LPC32XX_TS_PM_OPS (&lpc32xx_ts_pm_ops)
+#else
+#define LPC32XX_TS_PM_OPS NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id lpc32xx_tsc_of_match[] = {
+	{ .compatible = "nxp,lpc3220-tsc", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_tsc_of_match);
+#endif
+
+static struct platform_driver lpc32xx_ts_driver = {
+	.probe		= lpc32xx_ts_probe,
+	.remove		= lpc32xx_ts_remove,
+	.driver		= {
+		.name	= MOD_NAME,
+		.pm	= LPC32XX_TS_PM_OPS,
+		.of_match_table = of_match_ptr(lpc32xx_tsc_of_match),
+	},
+};
+module_platform_driver(lpc32xx_ts_driver);
+
+MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com");
+MODULE_DESCRIPTION("LPC32XX TSC Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lpc32xx_ts");
diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c
new file mode 100644
index 0000000..0786010
--- /dev/null
+++ b/drivers/input/touchscreen/mainstone-wm97xx.c
@@ -0,0 +1,309 @@
+/*
+ * mainstone-wm97xx.c  --  Mainstone Continuous Touch screen driver for
+ *                         Wolfson WM97xx AC97 Codecs.
+ *
+ * Copyright 2004, 2007 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *
+ *  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.
+ *
+ * Notes:
+ *     This is a wm97xx extended touch driver to capture touch
+ *     data in a continuous manner on the Intel XScale architecture
+ *
+ *  Features:
+ *       - codecs supported:- WM9705, WM9712, WM9713
+ *       - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/wm97xx.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+
+#include <mach/regs-ac97.h>
+
+#include <asm/mach-types.h>
+
+struct continuous {
+	u16 id;    /* codec id */
+	u8 code;   /* continuous code */
+	u8 reads;  /* number of coord reads per read cycle */
+	u32 speed; /* number of coords per second */
+};
+
+#define WM_READS(sp) ((sp / HZ) + 1)
+
+static const struct continuous cinfo[] = {
+	{WM9705_ID2, 0, WM_READS(94), 94},
+	{WM9705_ID2, 1, WM_READS(188), 188},
+	{WM9705_ID2, 2, WM_READS(375), 375},
+	{WM9705_ID2, 3, WM_READS(750), 750},
+	{WM9712_ID2, 0, WM_READS(94), 94},
+	{WM9712_ID2, 1, WM_READS(188), 188},
+	{WM9712_ID2, 2, WM_READS(375), 375},
+	{WM9712_ID2, 3, WM_READS(750), 750},
+	{WM9713_ID2, 0, WM_READS(94), 94},
+	{WM9713_ID2, 1, WM_READS(120), 120},
+	{WM9713_ID2, 2, WM_READS(154), 154},
+	{WM9713_ID2, 3, WM_READS(188), 188},
+};
+
+/* continuous speed index */
+static int sp_idx;
+static u16 last, tries;
+static int irq;
+
+/*
+ * Pen sampling frequency (Hz) in continuous mode.
+ */
+static int cont_rate = 200;
+module_param(cont_rate, int, 0);
+MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
+
+/*
+ * Pen down detection.
+ *
+ * This driver can either poll or use an interrupt to indicate a pen down
+ * event. If the irq request fails then it will fall back to polling mode.
+ */
+static int pen_int;
+module_param(pen_int, int, 0);
+MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)");
+
+/*
+ * Pressure readback.
+ *
+ * Set to 1 to read back pen down pressure
+ */
+static int pressure;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
+
+/*
+ * AC97 touch data slot.
+ *
+ * Touch screen readback data ac97 slot
+ */
+static int ac97_touch_slot = 5;
+module_param(ac97_touch_slot, int, 0);
+MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
+
+
+/* flush AC97 slot 5 FIFO on pxa machines */
+#ifdef CONFIG_PXA27x
+static void wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+	schedule_timeout_uninterruptible(1);
+
+	while (MISR & (1 << 2))
+		MODR;
+}
+#else
+static void wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+	unsigned int count;
+
+	schedule_timeout_uninterruptible(1);
+
+	for (count = 0; count < 16; count++)
+		MODR;
+}
+#endif
+
+static int wm97xx_acc_pen_down(struct wm97xx *wm)
+{
+	u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES;
+	int reads = 0;
+
+	/* When the AC97 queue has been drained we need to allow time
+	 * to buffer up samples otherwise we end up spinning polling
+	 * for samples.  The controller can't have a suitably low
+	 * threshold set to use the notifications it gives.
+	 */
+	schedule_timeout_uninterruptible(1);
+
+	if (tries > 5) {
+		tries = 0;
+		return RC_PENUP;
+	}
+
+	x = MODR;
+	if (x == last) {
+		tries++;
+		return RC_AGAIN;
+	}
+	last = x;
+	do {
+		if (reads)
+			x = MODR;
+		y = MODR;
+		if (pressure)
+			p = MODR;
+
+		dev_dbg(wm->dev, "Raw coordinates: x=%x, y=%x, p=%x\n",
+			x, y, p);
+
+		/* are samples valid */
+		if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X ||
+		    (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y ||
+		    (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES)
+			goto up;
+
+		/* coordinate is good */
+		tries = 0;
+		input_report_abs(wm->input_dev, ABS_X, x & 0xfff);
+		input_report_abs(wm->input_dev, ABS_Y, y & 0xfff);
+		input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff);
+		input_report_key(wm->input_dev, BTN_TOUCH, (p != 0));
+		input_sync(wm->input_dev);
+		reads++;
+	} while (reads < cinfo[sp_idx].reads);
+up:
+	return RC_PENDOWN | RC_AGAIN;
+}
+
+static int wm97xx_acc_startup(struct wm97xx *wm)
+{
+	int idx = 0, ret = 0;
+
+	/* check we have a codec */
+	if (wm->ac97 == NULL)
+		return -ENODEV;
+
+	/* Go you big red fire engine */
+	for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
+		if (wm->id != cinfo[idx].id)
+			continue;
+		sp_idx = idx;
+		if (cont_rate <= cinfo[idx].speed)
+			break;
+	}
+	wm->acc_rate = cinfo[sp_idx].code;
+	wm->acc_slot = ac97_touch_slot;
+	dev_info(wm->dev,
+		 "mainstone accelerated touchscreen driver, %d samples/sec\n",
+		 cinfo[sp_idx].speed);
+
+	/* IRQ driven touchscreen is used on Palm hardware */
+	if (machine_is_palmt5() || machine_is_palmtx() || machine_is_palmld()) {
+		pen_int = 1;
+		irq = 27;
+		/* There is some obscure mutant of WM9712 interbred with WM9713
+		 * used on Palm HW */
+		wm->variant = WM97xx_WM1613;
+	} else if (machine_is_mainstone() && pen_int)
+		irq = 4;
+
+	if (irq) {
+		ret = gpio_request(irq, "Touchscreen IRQ");
+		if (ret)
+			goto out;
+
+		ret = gpio_direction_input(irq);
+		if (ret) {
+			gpio_free(irq);
+			goto out;
+		}
+
+		wm->pen_irq = gpio_to_irq(irq);
+		irq_set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH);
+	} else /* pen irq not supported */
+		pen_int = 0;
+
+	/* codec specific irq config */
+	if (pen_int) {
+		switch (wm->id) {
+		case WM9705_ID2:
+			break;
+		case WM9712_ID2:
+		case WM9713_ID2:
+			/* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */
+			wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
+					   WM97XX_GPIO_POL_HIGH,
+					   WM97XX_GPIO_STICKY,
+					   WM97XX_GPIO_WAKE);
+			wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT,
+					   WM97XX_GPIO_POL_HIGH,
+					   WM97XX_GPIO_NOTSTICKY,
+					   WM97XX_GPIO_NOWAKE);
+			break;
+		default:
+			dev_err(wm->dev,
+				"pen down irq not supported on this device\n");
+			pen_int = 0;
+			break;
+		}
+	}
+
+out:
+	return ret;
+}
+
+static void wm97xx_acc_shutdown(struct wm97xx *wm)
+{
+	/* codec specific deconfig */
+	if (pen_int) {
+		if (irq)
+			gpio_free(irq);
+		wm->pen_irq = 0;
+	}
+}
+
+static void wm97xx_irq_enable(struct wm97xx *wm, int enable)
+{
+	if (enable)
+		enable_irq(wm->pen_irq);
+	else
+		disable_irq_nosync(wm->pen_irq);
+}
+
+static struct wm97xx_mach_ops mainstone_mach_ops = {
+	.acc_enabled = 1,
+	.acc_pen_up = wm97xx_acc_pen_up,
+	.acc_pen_down = wm97xx_acc_pen_down,
+	.acc_startup = wm97xx_acc_startup,
+	.acc_shutdown = wm97xx_acc_shutdown,
+	.irq_enable = wm97xx_irq_enable,
+	.irq_gpio = WM97XX_GPIO_2,
+};
+
+static int mainstone_wm97xx_probe(struct platform_device *pdev)
+{
+	struct wm97xx *wm = platform_get_drvdata(pdev);
+
+	return wm97xx_register_mach_ops(wm, &mainstone_mach_ops);
+}
+
+static int mainstone_wm97xx_remove(struct platform_device *pdev)
+{
+	struct wm97xx *wm = platform_get_drvdata(pdev);
+
+	wm97xx_unregister_mach_ops(wm);
+	return 0;
+}
+
+static struct platform_driver mainstone_wm97xx_driver = {
+	.probe = mainstone_wm97xx_probe,
+	.remove = mainstone_wm97xx_remove,
+	.driver = {
+		.name = "wm97xx-touch",
+	},
+};
+module_platform_driver(mainstone_wm97xx_driver);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("wm97xx continuous touch driver for mainstone");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c
new file mode 100644
index 0000000..82079cd
--- /dev/null
+++ b/drivers/input/touchscreen/max11801_ts.c
@@ -0,0 +1,241 @@
+/*
+ * Driver for MAXI MAX11801 - A Resistive touch screen controller with
+ * i2c interface
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * Author: Zhang Jiejing <jiejing.zhang@freescale.com>
+ *
+ * Based on mcs5000_ts.c
+ *
+ * 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.
+ */
+
+/*
+ * This driver aims to support the series of MAXI touch chips max11801
+ * through max11803. The main difference between these 4 chips can be
+ * found in the table below:
+ * -----------------------------------------------------
+ * | CHIP     |  AUTO MODE SUPPORT(FIFO) | INTERFACE    |
+ * |----------------------------------------------------|
+ * | max11800 |  YES                     |   SPI        |
+ * | max11801 |  YES                     |   I2C        |
+ * | max11802 |  NO                      |   SPI        |
+ * | max11803 |  NO                      |   I2C        |
+ * ------------------------------------------------------
+ *
+ * Currently, this driver only supports max11801.
+ *
+ * Data Sheet:
+ * http://www.maxim-ic.com/datasheet/index.mvp/id/5943
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+
+/* Register Address define */
+#define GENERNAL_STATUS_REG		0x00
+#define GENERNAL_CONF_REG		0x01
+#define MESURE_RES_CONF_REG		0x02
+#define MESURE_AVER_CONF_REG		0x03
+#define ADC_SAMPLE_TIME_CONF_REG	0x04
+#define PANEL_SETUPTIME_CONF_REG	0x05
+#define DELAY_CONVERSION_CONF_REG	0x06
+#define TOUCH_DETECT_PULLUP_CONF_REG	0x07
+#define AUTO_MODE_TIME_CONF_REG		0x08 /* only for max11800/max11801 */
+#define APERTURE_CONF_REG		0x09 /* only for max11800/max11801 */
+#define AUX_MESURE_CONF_REG		0x0a
+#define OP_MODE_CONF_REG		0x0b
+
+/* FIFO is found only in max11800 and max11801 */
+#define FIFO_RD_CMD			(0x50 << 1)
+#define MAX11801_FIFO_INT		(1 << 2)
+#define MAX11801_FIFO_OVERFLOW		(1 << 3)
+
+#define XY_BUFSIZE			4
+#define XY_BUF_OFFSET			4
+
+#define MAX11801_MAX_X			0xfff
+#define MAX11801_MAX_Y			0xfff
+
+#define MEASURE_TAG_OFFSET		2
+#define MEASURE_TAG_MASK		(3 << MEASURE_TAG_OFFSET)
+#define EVENT_TAG_OFFSET		0
+#define EVENT_TAG_MASK			(3 << EVENT_TAG_OFFSET)
+#define MEASURE_X_TAG			(0 << MEASURE_TAG_OFFSET)
+#define MEASURE_Y_TAG			(1 << MEASURE_TAG_OFFSET)
+
+/* These are the state of touch event state machine */
+enum {
+	EVENT_INIT,
+	EVENT_MIDDLE,
+	EVENT_RELEASE,
+	EVENT_FIFO_END
+};
+
+struct max11801_data {
+	struct i2c_client		*client;
+	struct input_dev		*input_dev;
+};
+
+static u8 read_register(struct i2c_client *client, int addr)
+{
+	/* XXX: The chip ignores LSB of register address */
+	return i2c_smbus_read_byte_data(client, addr << 1);
+}
+
+static int max11801_write_reg(struct i2c_client *client, int addr, int data)
+{
+	/* XXX: The chip ignores LSB of register address */
+	return i2c_smbus_write_byte_data(client, addr << 1, data);
+}
+
+static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id)
+{
+	struct max11801_data *data = dev_id;
+	struct i2c_client *client = data->client;
+	int status, i, ret;
+	u8 buf[XY_BUFSIZE];
+	int x = -1;
+	int y = -1;
+
+	status = read_register(data->client, GENERNAL_STATUS_REG);
+
+	if (status & (MAX11801_FIFO_INT | MAX11801_FIFO_OVERFLOW)) {
+		status = read_register(data->client, GENERNAL_STATUS_REG);
+
+		ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD,
+						    XY_BUFSIZE, buf);
+
+		/*
+		 * We should get 4 bytes buffer that contains X,Y
+		 * and event tag
+		 */
+		if (ret < XY_BUFSIZE)
+			goto out;
+
+		for (i = 0; i < XY_BUFSIZE; i += XY_BUFSIZE / 2) {
+			if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_X_TAG)
+				x = (buf[i] << XY_BUF_OFFSET) +
+				    (buf[i + 1] >> XY_BUF_OFFSET);
+			else if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_Y_TAG)
+				y = (buf[i] << XY_BUF_OFFSET) +
+				    (buf[i + 1] >> XY_BUF_OFFSET);
+		}
+
+		if ((buf[1] & EVENT_TAG_MASK) != (buf[3] & EVENT_TAG_MASK))
+			goto out;
+
+		switch (buf[1] & EVENT_TAG_MASK) {
+		case EVENT_INIT:
+			/* fall through */
+		case EVENT_MIDDLE:
+			input_report_abs(data->input_dev, ABS_X, x);
+			input_report_abs(data->input_dev, ABS_Y, y);
+			input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1);
+			input_sync(data->input_dev);
+			break;
+
+		case EVENT_RELEASE:
+			input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0);
+			input_sync(data->input_dev);
+			break;
+
+		case EVENT_FIFO_END:
+			break;
+		}
+	}
+out:
+	return IRQ_HANDLED;
+}
+
+static void max11801_ts_phy_init(struct max11801_data *data)
+{
+	struct i2c_client *client = data->client;
+
+	/* Average X,Y, take 16 samples, average eight media sample */
+	max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff);
+	/* X,Y panel setup time set to 20us */
+	max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11);
+	/* Rough pullup time (2uS), Fine pullup time (10us)  */
+	max11801_write_reg(client, TOUCH_DETECT_PULLUP_CONF_REG, 0x10);
+	/* Auto mode init period = 5ms , scan period = 5ms*/
+	max11801_write_reg(client, AUTO_MODE_TIME_CONF_REG, 0xaa);
+	/* Aperture X,Y set to +- 4LSB */
+	max11801_write_reg(client, APERTURE_CONF_REG, 0x33);
+	/* Enable Power, enable Automode, enable Aperture, enable Average X,Y */
+	max11801_write_reg(client, OP_MODE_CONF_REG, 0x36);
+}
+
+static int max11801_ts_probe(struct i2c_client *client,
+				       const struct i2c_device_id *id)
+{
+	struct max11801_data *data;
+	struct input_dev *input_dev;
+	int error;
+
+	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+	input_dev = devm_input_allocate_device(&client->dev);
+	if (!data || !input_dev) {
+		dev_err(&client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	data->client = client;
+	data->input_dev = input_dev;
+
+	input_dev->name = "max11801_ts";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &client->dev;
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+	input_set_abs_params(input_dev, ABS_X, 0, MAX11801_MAX_X, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, MAX11801_MAX_Y, 0, 0);
+	input_set_drvdata(input_dev, data);
+
+	max11801_ts_phy_init(data);
+
+	error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+					  max11801_ts_interrupt,
+					  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					  "max11801_ts", data);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	error = input_register_device(data->input_dev);
+	if (error)
+		return error;
+
+	i2c_set_clientdata(client, data);
+	return 0;
+}
+
+static const struct i2c_device_id max11801_ts_id[] = {
+	{"max11801", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max11801_ts_id);
+
+static struct i2c_driver max11801_ts_driver = {
+	.driver = {
+		.name	= "max11801_ts",
+	},
+	.id_table	= max11801_ts_id,
+	.probe		= max11801_ts_probe,
+};
+
+module_i2c_driver(max11801_ts_driver);
+
+MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>");
+MODULE_DESCRIPTION("Touchscreen driver for MAXI MAX11801 controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c
new file mode 100644
index 0000000..913e25a
--- /dev/null
+++ b/drivers/input/touchscreen/mc13783_ts.c
@@ -0,0 +1,255 @@
+/*
+ * Driver for the Freescale Semiconductor MC13783 touchscreen.
+ *
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2009 Sascha Hauer, Pengutronix
+ *
+ * Initial development of this code was funded by
+ * Phytec Messtechnik GmbH, http://www.phytec.de/
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/platform_device.h>
+#include <linux/mfd/mc13783.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#define MC13783_TS_NAME	"mc13783-ts"
+
+#define DEFAULT_SAMPLE_TOLERANCE 300
+
+static unsigned int sample_tolerance = DEFAULT_SAMPLE_TOLERANCE;
+module_param(sample_tolerance, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(sample_tolerance,
+		"If the minimal and maximal value read out for one axis (out "
+		"of three) differ by this value (default: "
+		__stringify(DEFAULT_SAMPLE_TOLERANCE) ") or more, the reading "
+		"is supposed to be wrong and is discarded.  Set to 0 to "
+		"disable this check.");
+
+struct mc13783_ts_priv {
+	struct input_dev *idev;
+	struct mc13xxx *mc13xxx;
+	struct delayed_work work;
+	struct workqueue_struct *workq;
+	unsigned int sample[4];
+	struct mc13xxx_ts_platform_data *touch;
+};
+
+static irqreturn_t mc13783_ts_handler(int irq, void *data)
+{
+	struct mc13783_ts_priv *priv = data;
+
+	mc13xxx_irq_ack(priv->mc13xxx, irq);
+
+	/*
+	 * Kick off reading coordinates. Note that if work happens already
+	 * be queued for future execution (it rearms itself) it will not
+	 * be rescheduled for immediate execution here. However the rearm
+	 * delay is HZ / 50 which is acceptable.
+	 */
+	queue_delayed_work(priv->workq, &priv->work, 0);
+
+	return IRQ_HANDLED;
+}
+
+#define sort3(a0, a1, a2) ({						\
+		if (a0 > a1)						\
+			swap(a0, a1);					\
+		if (a1 > a2)						\
+			swap(a1, a2);					\
+		if (a0 > a1)						\
+			swap(a0, a1);					\
+		})
+
+static void mc13783_ts_report_sample(struct mc13783_ts_priv *priv)
+{
+	struct input_dev *idev = priv->idev;
+	int x0, x1, x2, y0, y1, y2;
+	int cr0, cr1;
+
+	/*
+	 * the values are 10-bit wide only, but the two least significant
+	 * bits are for future 12 bit use and reading yields 0
+	 */
+	x0 = priv->sample[0] & 0xfff;
+	x1 = priv->sample[1] & 0xfff;
+	x2 = priv->sample[2] & 0xfff;
+	y0 = priv->sample[3] & 0xfff;
+	y1 = (priv->sample[0] >> 12) & 0xfff;
+	y2 = (priv->sample[1] >> 12) & 0xfff;
+	cr0 = (priv->sample[2] >> 12) & 0xfff;
+	cr1 = (priv->sample[3] >> 12) & 0xfff;
+
+	dev_dbg(&idev->dev,
+		"x: (% 4d,% 4d,% 4d) y: (% 4d, % 4d,% 4d) cr: (% 4d, % 4d)\n",
+		x0, x1, x2, y0, y1, y2, cr0, cr1);
+
+	sort3(x0, x1, x2);
+	sort3(y0, y1, y2);
+
+	cr0 = (cr0 + cr1) / 2;
+
+	if (!cr0 || !sample_tolerance ||
+			(x2 - x0 < sample_tolerance &&
+			 y2 - y0 < sample_tolerance)) {
+		/* report the median coordinate and average pressure */
+		if (cr0) {
+			input_report_abs(idev, ABS_X, x1);
+			input_report_abs(idev, ABS_Y, y1);
+
+			dev_dbg(&idev->dev, "report (%d, %d, %d)\n",
+					x1, y1, 0x1000 - cr0);
+			queue_delayed_work(priv->workq, &priv->work, HZ / 50);
+		} else
+			dev_dbg(&idev->dev, "report release\n");
+
+		input_report_abs(idev, ABS_PRESSURE,
+				cr0 ? 0x1000 - cr0 : cr0);
+		input_report_key(idev, BTN_TOUCH, cr0);
+		input_sync(idev);
+	} else
+		dev_dbg(&idev->dev, "discard event\n");
+}
+
+static void mc13783_ts_work(struct work_struct *work)
+{
+	struct mc13783_ts_priv *priv =
+		container_of(work, struct mc13783_ts_priv, work.work);
+	unsigned int mode = MC13XXX_ADC_MODE_TS;
+	unsigned int channel = 12;
+
+	if (mc13xxx_adc_do_conversion(priv->mc13xxx,
+				mode, channel,
+				priv->touch->ato, priv->touch->atox,
+				priv->sample) == 0)
+		mc13783_ts_report_sample(priv);
+}
+
+static int mc13783_ts_open(struct input_dev *dev)
+{
+	struct mc13783_ts_priv *priv = input_get_drvdata(dev);
+	int ret;
+
+	mc13xxx_lock(priv->mc13xxx);
+
+	mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TS);
+
+	ret = mc13xxx_irq_request(priv->mc13xxx, MC13XXX_IRQ_TS,
+		mc13783_ts_handler, MC13783_TS_NAME, priv);
+	if (ret)
+		goto out;
+
+	ret = mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0,
+			MC13XXX_ADC0_TSMOD_MASK, MC13XXX_ADC0_TSMOD0);
+	if (ret)
+		mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv);
+out:
+	mc13xxx_unlock(priv->mc13xxx);
+	return ret;
+}
+
+static void mc13783_ts_close(struct input_dev *dev)
+{
+	struct mc13783_ts_priv *priv = input_get_drvdata(dev);
+
+	mc13xxx_lock(priv->mc13xxx);
+	mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0,
+			MC13XXX_ADC0_TSMOD_MASK, 0);
+	mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv);
+	mc13xxx_unlock(priv->mc13xxx);
+
+	cancel_delayed_work_sync(&priv->work);
+}
+
+static int __init mc13783_ts_probe(struct platform_device *pdev)
+{
+	struct mc13783_ts_priv *priv;
+	struct input_dev *idev;
+	int ret = -ENOMEM;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	idev = input_allocate_device();
+	if (!priv || !idev)
+		goto err_free_mem;
+
+	INIT_DELAYED_WORK(&priv->work, mc13783_ts_work);
+	priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
+	priv->idev = idev;
+	priv->touch = dev_get_platdata(&pdev->dev);
+	if (!priv->touch) {
+		dev_err(&pdev->dev, "missing platform data\n");
+		ret = -ENODEV;
+		goto err_free_mem;
+	}
+
+	/*
+	 * We need separate workqueue because mc13783_adc_do_conversion
+	 * uses keventd and thus would deadlock.
+	 */
+	priv->workq = create_singlethread_workqueue("mc13783_ts");
+	if (!priv->workq)
+		goto err_free_mem;
+
+	idev->name = MC13783_TS_NAME;
+	idev->dev.parent = &pdev->dev;
+
+	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
+	input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
+	input_set_abs_params(idev, ABS_PRESSURE, 0, 0xfff, 0, 0);
+
+	idev->open = mc13783_ts_open;
+	idev->close = mc13783_ts_close;
+
+	input_set_drvdata(idev, priv);
+
+	ret = input_register_device(priv->idev);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"register input device failed with %d\n", ret);
+		goto err_destroy_wq;
+	}
+
+	platform_set_drvdata(pdev, priv);
+	return 0;
+
+err_destroy_wq:
+	destroy_workqueue(priv->workq);
+err_free_mem:
+	input_free_device(idev);
+	kfree(priv);
+	return ret;
+}
+
+static int mc13783_ts_remove(struct platform_device *pdev)
+{
+	struct mc13783_ts_priv *priv = platform_get_drvdata(pdev);
+
+	destroy_workqueue(priv->workq);
+	input_unregister_device(priv->idev);
+	kfree(priv);
+
+	return 0;
+}
+
+static struct platform_driver mc13783_ts_driver = {
+	.remove		= mc13783_ts_remove,
+	.driver		= {
+		.name	= MC13783_TS_NAME,
+	},
+};
+
+module_platform_driver_probe(mc13783_ts_driver, mc13783_ts_probe);
+
+MODULE_DESCRIPTION("MC13783 input touchscreen driver");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" MC13783_TS_NAME);
diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c
new file mode 100644
index 0000000..8b47e1f
--- /dev/null
+++ b/drivers/input/touchscreen/mcs5000_ts.c
@@ -0,0 +1,294 @@
+/*
+ * mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * Based on wm97xx-core.c
+ *
+ *  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c/mcs.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+
+/* Registers */
+#define MCS5000_TS_STATUS		0x00
+#define STATUS_OFFSET			0
+#define STATUS_NO			(0 << STATUS_OFFSET)
+#define STATUS_INIT			(1 << STATUS_OFFSET)
+#define STATUS_SENSING			(2 << STATUS_OFFSET)
+#define STATUS_COORD			(3 << STATUS_OFFSET)
+#define STATUS_GESTURE			(4 << STATUS_OFFSET)
+#define ERROR_OFFSET			4
+#define ERROR_NO			(0 << ERROR_OFFSET)
+#define ERROR_POWER_ON_RESET		(1 << ERROR_OFFSET)
+#define ERROR_INT_RESET			(2 << ERROR_OFFSET)
+#define ERROR_EXT_RESET			(3 << ERROR_OFFSET)
+#define ERROR_INVALID_REG_ADDRESS	(8 << ERROR_OFFSET)
+#define ERROR_INVALID_REG_VALUE		(9 << ERROR_OFFSET)
+
+#define MCS5000_TS_OP_MODE		0x01
+#define RESET_OFFSET			0
+#define RESET_NO			(0 << RESET_OFFSET)
+#define RESET_EXT_SOFT			(1 << RESET_OFFSET)
+#define OP_MODE_OFFSET			1
+#define OP_MODE_SLEEP			(0 << OP_MODE_OFFSET)
+#define OP_MODE_ACTIVE			(1 << OP_MODE_OFFSET)
+#define GESTURE_OFFSET			4
+#define GESTURE_DISABLE			(0 << GESTURE_OFFSET)
+#define GESTURE_ENABLE			(1 << GESTURE_OFFSET)
+#define PROXIMITY_OFFSET		5
+#define PROXIMITY_DISABLE		(0 << PROXIMITY_OFFSET)
+#define PROXIMITY_ENABLE		(1 << PROXIMITY_OFFSET)
+#define SCAN_MODE_OFFSET		6
+#define SCAN_MODE_INTERRUPT		(0 << SCAN_MODE_OFFSET)
+#define SCAN_MODE_POLLING		(1 << SCAN_MODE_OFFSET)
+#define REPORT_RATE_OFFSET		7
+#define REPORT_RATE_40			(0 << REPORT_RATE_OFFSET)
+#define REPORT_RATE_80			(1 << REPORT_RATE_OFFSET)
+
+#define MCS5000_TS_SENS_CTL		0x02
+#define MCS5000_TS_FILTER_CTL		0x03
+#define PRI_FILTER_OFFSET		0
+#define SEC_FILTER_OFFSET		4
+
+#define MCS5000_TS_X_SIZE_UPPER		0x08
+#define MCS5000_TS_X_SIZE_LOWER		0x09
+#define MCS5000_TS_Y_SIZE_UPPER		0x0A
+#define MCS5000_TS_Y_SIZE_LOWER		0x0B
+
+#define MCS5000_TS_INPUT_INFO		0x10
+#define INPUT_TYPE_OFFSET		0
+#define INPUT_TYPE_NONTOUCH		(0 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_SINGLE		(1 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_DUAL			(2 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_PALM			(3 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_PROXIMITY		(7 << INPUT_TYPE_OFFSET)
+#define GESTURE_CODE_OFFSET		3
+#define GESTURE_CODE_NO			(0 << GESTURE_CODE_OFFSET)
+
+#define MCS5000_TS_X_POS_UPPER		0x11
+#define MCS5000_TS_X_POS_LOWER		0x12
+#define MCS5000_TS_Y_POS_UPPER		0x13
+#define MCS5000_TS_Y_POS_LOWER		0x14
+#define MCS5000_TS_Z_POS		0x15
+#define MCS5000_TS_WIDTH		0x16
+#define MCS5000_TS_GESTURE_VAL		0x17
+#define MCS5000_TS_MODULE_REV		0x20
+#define MCS5000_TS_FIRMWARE_VER		0x21
+
+/* Touchscreen absolute values */
+#define MCS5000_MAX_XC			0x3ff
+#define MCS5000_MAX_YC			0x3ff
+
+enum mcs5000_ts_read_offset {
+	READ_INPUT_INFO,
+	READ_X_POS_UPPER,
+	READ_X_POS_LOWER,
+	READ_Y_POS_UPPER,
+	READ_Y_POS_LOWER,
+	READ_BLOCK_SIZE,
+};
+
+/* Each client has this additional data */
+struct mcs5000_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	const struct mcs_platform_data *platform_data;
+};
+
+static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id)
+{
+	struct mcs5000_ts_data *data = dev_id;
+	struct i2c_client *client = data->client;
+	u8 buffer[READ_BLOCK_SIZE];
+	int err;
+	int x;
+	int y;
+
+	err = i2c_smbus_read_i2c_block_data(client, MCS5000_TS_INPUT_INFO,
+			READ_BLOCK_SIZE, buffer);
+	if (err < 0) {
+		dev_err(&client->dev, "%s, err[%d]\n", __func__, err);
+		goto out;
+	}
+
+	switch (buffer[READ_INPUT_INFO]) {
+	case INPUT_TYPE_NONTOUCH:
+		input_report_key(data->input_dev, BTN_TOUCH, 0);
+		input_sync(data->input_dev);
+		break;
+
+	case INPUT_TYPE_SINGLE:
+		x = (buffer[READ_X_POS_UPPER] << 8) | buffer[READ_X_POS_LOWER];
+		y = (buffer[READ_Y_POS_UPPER] << 8) | buffer[READ_Y_POS_LOWER];
+
+		input_report_key(data->input_dev, BTN_TOUCH, 1);
+		input_report_abs(data->input_dev, ABS_X, x);
+		input_report_abs(data->input_dev, ABS_Y, y);
+		input_sync(data->input_dev);
+		break;
+
+	case INPUT_TYPE_DUAL:
+		/* TODO */
+		break;
+
+	case INPUT_TYPE_PALM:
+		/* TODO */
+		break;
+
+	case INPUT_TYPE_PROXIMITY:
+		/* TODO */
+		break;
+
+	default:
+		dev_err(&client->dev, "Unknown ts input type %d\n",
+				buffer[READ_INPUT_INFO]);
+		break;
+	}
+
+ out:
+	return IRQ_HANDLED;
+}
+
+static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data,
+				 const struct mcs_platform_data *platform_data)
+{
+	struct i2c_client *client = data->client;
+
+	/* Touch reset & sleep mode */
+	i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE,
+			RESET_EXT_SOFT | OP_MODE_SLEEP);
+
+	/* Touch size */
+	i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER,
+			platform_data->x_size >> 8);
+	i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER,
+			platform_data->x_size & 0xff);
+	i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER,
+			platform_data->y_size >> 8);
+	i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER,
+			platform_data->y_size & 0xff);
+
+	/* Touch active mode & 80 report rate */
+	i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE,
+			OP_MODE_ACTIVE | REPORT_RATE_80);
+}
+
+static int mcs5000_ts_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	const struct mcs_platform_data *pdata;
+	struct mcs5000_ts_data *data;
+	struct input_dev *input_dev;
+	int error;
+
+	pdata = dev_get_platdata(&client->dev);
+	if (!pdata)
+		return -EINVAL;
+
+	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		dev_err(&client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	data->client = client;
+
+	input_dev = devm_input_allocate_device(&client->dev);
+	if (!input_dev) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	input_dev->name = "MELFAS MCS-5000 Touchscreen";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &client->dev;
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+	input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0);
+
+	input_set_drvdata(input_dev, data);
+	data->input_dev = input_dev;
+
+	if (pdata->cfg_pin)
+		pdata->cfg_pin();
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, mcs5000_ts_interrupt,
+					  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					  "mcs5000_ts", data);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	error = input_register_device(data->input_dev);
+	if (error) {
+		dev_err(&client->dev, "Failed to register input device\n");
+		return error;
+	}
+
+	mcs5000_ts_phys_init(data, pdata);
+	i2c_set_clientdata(client, data);
+
+	return 0;
+}
+
+static int __maybe_unused mcs5000_ts_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	/* Touch sleep mode */
+	i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP);
+
+	return 0;
+}
+
+static int __maybe_unused mcs5000_ts_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mcs5000_ts_data *data = i2c_get_clientdata(client);
+	const struct mcs_platform_data *pdata = dev_get_platdata(dev);
+
+	mcs5000_ts_phys_init(data, pdata);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume);
+
+static const struct i2c_device_id mcs5000_ts_id[] = {
+	{ "mcs5000_ts", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id);
+
+static struct i2c_driver mcs5000_ts_driver = {
+	.probe		= mcs5000_ts_probe,
+	.driver = {
+		.name = "mcs5000_ts",
+		.pm   = &mcs5000_ts_pm,
+	},
+	.id_table	= mcs5000_ts_id,
+};
+
+module_i2c_driver(mcs5000_ts_driver);
+
+/* Module information */
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/migor_ts.c b/drivers/input/touchscreen/migor_ts.c
new file mode 100644
index 0000000..c038db9
--- /dev/null
+++ b/drivers/input/touchscreen/migor_ts.c
@@ -0,0 +1,249 @@
+/*
+ * Touch Screen driver for Renesas MIGO-R Platform
+ *
+ * Copyright (c) 2008 Magnus Damm
+ * Copyright (c) 2007 Ujjwal Pande <ujjwal@kenati.com>,
+ *  Kenati Technologies Pvt Ltd.
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <linux/i2c.h>
+#include <linux/timer.h>
+
+#define EVENT_PENDOWN 1
+#define EVENT_REPEAT  2
+#define EVENT_PENUP   3
+
+struct migor_ts_priv {
+	struct i2c_client *client;
+	struct input_dev *input;
+	int irq;
+};
+
+static const u_int8_t migor_ts_ena_seq[17] = { 0x33, 0x22, 0x11,
+					       0x01, 0x06, 0x07, };
+static const u_int8_t migor_ts_dis_seq[17] = { };
+
+static irqreturn_t migor_ts_isr(int irq, void *dev_id)
+{
+	struct migor_ts_priv *priv = dev_id;
+	unsigned short xpos, ypos;
+	unsigned char event;
+	u_int8_t buf[16];
+
+	/*
+	 * The touch screen controller chip is hooked up to the CPU
+	 * using I2C and a single interrupt line. The interrupt line
+	 * is pulled low whenever someone taps the screen. To deassert
+	 * the interrupt line we need to acknowledge the interrupt by
+	 * communicating with the controller over the slow i2c bus.
+	 *
+	 * Since I2C bus controller may sleep we are using threaded
+	 * IRQ here.
+	 */
+
+	memset(buf, 0, sizeof(buf));
+
+	/* Set Index 0 */
+	buf[0] = 0;
+	if (i2c_master_send(priv->client, buf, 1) != 1) {
+		dev_err(&priv->client->dev, "Unable to write i2c index\n");
+		goto out;
+	}
+
+	/* Now do Page Read */
+	if (i2c_master_recv(priv->client, buf, sizeof(buf)) != sizeof(buf)) {
+		dev_err(&priv->client->dev, "Unable to read i2c page\n");
+		goto out;
+	}
+
+	ypos = ((buf[9] & 0x03) << 8 | buf[8]);
+	xpos = ((buf[11] & 0x03) << 8 | buf[10]);
+	event = buf[12];
+
+	switch (event) {
+	case EVENT_PENDOWN:
+	case EVENT_REPEAT:
+		input_report_key(priv->input, BTN_TOUCH, 1);
+		input_report_abs(priv->input, ABS_X, ypos); /*X-Y swap*/
+		input_report_abs(priv->input, ABS_Y, xpos);
+		input_sync(priv->input);
+		break;
+
+	case EVENT_PENUP:
+		input_report_key(priv->input, BTN_TOUCH, 0);
+		input_sync(priv->input);
+		break;
+	}
+
+ out:
+	return IRQ_HANDLED;
+}
+
+static int migor_ts_open(struct input_dev *dev)
+{
+	struct migor_ts_priv *priv = input_get_drvdata(dev);
+	struct i2c_client *client = priv->client;
+	int count;
+
+	/* enable controller */
+	count = i2c_master_send(client, migor_ts_ena_seq,
+				sizeof(migor_ts_ena_seq));
+	if (count != sizeof(migor_ts_ena_seq)) {
+		dev_err(&client->dev, "Unable to enable touchscreen.\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void migor_ts_close(struct input_dev *dev)
+{
+	struct migor_ts_priv *priv = input_get_drvdata(dev);
+	struct i2c_client *client = priv->client;
+
+	disable_irq(priv->irq);
+
+	/* disable controller */
+	i2c_master_send(client, migor_ts_dis_seq, sizeof(migor_ts_dis_seq));
+
+	enable_irq(priv->irq);
+}
+
+static int migor_ts_probe(struct i2c_client *client,
+			  const struct i2c_device_id *idp)
+{
+	struct migor_ts_priv *priv;
+	struct input_dev *input;
+	int error;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	input = input_allocate_device();
+	if (!priv || !input) {
+		dev_err(&client->dev, "failed to allocate memory\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	priv->client = client;
+	priv->input = input;
+	priv->irq = client->irq;
+
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	__set_bit(BTN_TOUCH, input->keybit);
+
+	input_set_abs_params(input, ABS_X, 95, 955, 0, 0);
+	input_set_abs_params(input, ABS_Y, 85, 935, 0, 0);
+
+	input->name = client->name;
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &client->dev;
+
+	input->open = migor_ts_open;
+	input->close = migor_ts_close;
+
+	input_set_drvdata(input, priv);
+
+	error = request_threaded_irq(priv->irq, NULL, migor_ts_isr,
+                                     IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                     client->name, priv);
+	if (error) {
+		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+		goto err_free_mem;
+	}
+
+	error = input_register_device(input);
+	if (error)
+		goto err_free_irq;
+
+	i2c_set_clientdata(client, priv);
+	device_init_wakeup(&client->dev, 1);
+
+	return 0;
+
+ err_free_irq:
+	free_irq(priv->irq, priv);
+ err_free_mem:
+	input_free_device(input);
+	kfree(priv);
+	return error;
+}
+
+static int migor_ts_remove(struct i2c_client *client)
+{
+	struct migor_ts_priv *priv = i2c_get_clientdata(client);
+
+	free_irq(priv->irq, priv);
+	input_unregister_device(priv->input);
+	kfree(priv);
+
+	dev_set_drvdata(&client->dev, NULL);
+
+	return 0;
+}
+
+static int migor_ts_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct migor_ts_priv *priv = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(&client->dev))
+		enable_irq_wake(priv->irq);
+
+	return 0;
+}
+
+static int migor_ts_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct migor_ts_priv *priv = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(&client->dev))
+		disable_irq_wake(priv->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(migor_ts_pm, migor_ts_suspend, migor_ts_resume);
+
+static const struct i2c_device_id migor_ts_id[] = {
+	{ "migor_ts", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, migor_ts);
+
+static struct i2c_driver migor_ts_driver = {
+	.driver = {
+		.name = "migor_ts",
+		.pm = &migor_ts_pm,
+	},
+	.probe = migor_ts_probe,
+	.remove = migor_ts_remove,
+	.id_table = migor_ts_id,
+};
+
+module_i2c_driver(migor_ts_driver);
+
+MODULE_DESCRIPTION("MigoR Touchscreen driver");
+MODULE_AUTHOR("Magnus Damm <damm@opensource.se>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c
new file mode 100644
index 0000000..36e57de
--- /dev/null
+++ b/drivers/input/touchscreen/mk712.c
@@ -0,0 +1,219 @@
+/*
+ * ICS MK712 touchscreen controller driver
+ *
+ * Copyright (c) 1999-2002 Transmeta Corporation
+ * Copyright (c) 2005 Rick Koch <n1gp@hotmail.com>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This driver supports the ICS MicroClock MK712 TouchScreen controller,
+ * found in Gateway AOL Connected Touchpad computers.
+ *
+ * Documentation for ICS MK712 can be found at:
+ *	http://www.idt.com/products/getDoc.cfm?docID=18713923
+ */
+
+/*
+ * 1999-12-18: original version, Daniel Quinlan
+ * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll
+ *             to use queue_empty, Nathan Laredo
+ * 1999-12-20: improved random point rejection, Nathan Laredo
+ * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed
+ *             queue code, added module options, other fixes, Daniel Quinlan
+ * 2002-03-15: Clean up for kernel merge <alan@redhat.com>
+ *             Fixed multi open race, fixed memory checks, fixed resource
+ *             allocation, fixed close/powerdown bug, switched to new init
+ * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch
+ * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Daniel Quinlan <quinlan@pathname.com>, Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver");
+MODULE_LICENSE("GPL");
+
+static unsigned int mk712_io = 0x260;	/* Also 0x200, 0x208, 0x300 */
+module_param_named(io, mk712_io, uint, 0);
+MODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller");
+
+static unsigned int mk712_irq = 10;	/* Also 12, 14, 15 */
+module_param_named(irq, mk712_irq, uint, 0);
+MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller");
+
+/* eight 8-bit registers */
+#define MK712_STATUS		0
+#define MK712_X			2
+#define MK712_Y			4
+#define MK712_CONTROL		6
+#define MK712_RATE		7
+
+/* status */
+#define	MK712_STATUS_TOUCH			0x10
+#define	MK712_CONVERSION_COMPLETE		0x80
+
+/* control */
+#define MK712_ENABLE_INT			0x01
+#define MK712_INT_ON_CONVERSION_COMPLETE	0x02
+#define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS	0x04
+#define MK712_ENABLE_PERIODIC_CONVERSIONS	0x10
+#define MK712_READ_ONE_POINT			0x20
+#define MK712_POWERUP				0x40
+
+static struct input_dev *mk712_dev;
+static DEFINE_SPINLOCK(mk712_lock);
+
+static irqreturn_t mk712_interrupt(int irq, void *dev_id)
+{
+	unsigned char status;
+	static int debounce = 1;
+	static unsigned short last_x;
+	static unsigned short last_y;
+
+	spin_lock(&mk712_lock);
+
+	status = inb(mk712_io + MK712_STATUS);
+
+	if (~status & MK712_CONVERSION_COMPLETE) {
+		debounce = 1;
+		goto end;
+	}
+
+	if (~status & MK712_STATUS_TOUCH) {
+		debounce = 1;
+		input_report_key(mk712_dev, BTN_TOUCH, 0);
+		goto end;
+	}
+
+	if (debounce) {
+		debounce = 0;
+		goto end;
+	}
+
+	input_report_key(mk712_dev, BTN_TOUCH, 1);
+	input_report_abs(mk712_dev, ABS_X, last_x);
+	input_report_abs(mk712_dev, ABS_Y, last_y);
+
+ end:
+	last_x = inw(mk712_io + MK712_X) & 0x0fff;
+	last_y = inw(mk712_io + MK712_Y) & 0x0fff;
+	input_sync(mk712_dev);
+	spin_unlock(&mk712_lock);
+	return IRQ_HANDLED;
+}
+
+static int mk712_open(struct input_dev *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mk712_lock, flags);
+
+	outb(0, mk712_io + MK712_CONTROL); /* Reset */
+
+	outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE |
+		MK712_INT_ON_CHANGE_IN_TOUCH_STATUS |
+		MK712_ENABLE_PERIODIC_CONVERSIONS |
+		MK712_POWERUP, mk712_io + MK712_CONTROL);
+
+	outb(10, mk712_io + MK712_RATE); /* 187 points per second */
+
+	spin_unlock_irqrestore(&mk712_lock, flags);
+
+	return 0;
+}
+
+static void mk712_close(struct input_dev *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mk712_lock, flags);
+
+	outb(0, mk712_io + MK712_CONTROL);
+
+	spin_unlock_irqrestore(&mk712_lock, flags);
+}
+
+static int __init mk712_init(void)
+{
+	int err;
+
+	if (!request_region(mk712_io, 8, "mk712")) {
+		printk(KERN_WARNING "mk712: unable to get IO region\n");
+		return -ENODEV;
+	}
+
+	outb(0, mk712_io + MK712_CONTROL);
+
+	if ((inw(mk712_io + MK712_X) & 0xf000) ||	/* Sanity check */
+	    (inw(mk712_io + MK712_Y) & 0xf000) ||
+	    (inw(mk712_io + MK712_STATUS) & 0xf333)) {
+		printk(KERN_WARNING "mk712: device not present\n");
+		err = -ENODEV;
+		goto fail1;
+	}
+
+	mk712_dev = input_allocate_device();
+	if (!mk712_dev) {
+		printk(KERN_ERR "mk712: not enough memory\n");
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	mk712_dev->name = "ICS MicroClock MK712 TouchScreen";
+	mk712_dev->phys = "isa0260/input0";
+	mk712_dev->id.bustype = BUS_ISA;
+	mk712_dev->id.vendor  = 0x0005;
+	mk712_dev->id.product = 0x0001;
+	mk712_dev->id.version = 0x0100;
+
+	mk712_dev->open    = mk712_open;
+	mk712_dev->close   = mk712_close;
+
+	mk712_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	mk712_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(mk712_dev, ABS_X, 0, 0xfff, 88, 0);
+	input_set_abs_params(mk712_dev, ABS_Y, 0, 0xfff, 88, 0);
+
+	if (request_irq(mk712_irq, mk712_interrupt, 0, "mk712", mk712_dev)) {
+		printk(KERN_WARNING "mk712: unable to get IRQ\n");
+		err = -EBUSY;
+		goto fail1;
+	}
+
+	err = input_register_device(mk712_dev);
+	if (err)
+		goto fail2;
+
+	return 0;
+
+ fail2:	free_irq(mk712_irq, mk712_dev);
+ fail1:	input_free_device(mk712_dev);
+	release_region(mk712_io, 8);
+	return err;
+}
+
+static void __exit mk712_exit(void)
+{
+	input_unregister_device(mk712_dev);
+	free_irq(mk712_irq, mk712_dev);
+	release_region(mk712_io, 8);
+}
+
+module_init(mk712_init);
+module_exit(mk712_exit);
diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c
new file mode 100644
index 0000000..1fafc9f
--- /dev/null
+++ b/drivers/input/touchscreen/mms114.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/i2c/mms114.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+/* Write only registers */
+#define MMS114_MODE_CONTROL		0x01
+#define MMS114_OPERATION_MODE_MASK	0xE
+#define MMS114_ACTIVE			(1 << 1)
+
+#define MMS114_XY_RESOLUTION_H		0x02
+#define MMS114_X_RESOLUTION		0x03
+#define MMS114_Y_RESOLUTION		0x04
+#define MMS114_CONTACT_THRESHOLD	0x05
+#define MMS114_MOVING_THRESHOLD		0x06
+
+/* Read only registers */
+#define MMS114_PACKET_SIZE		0x0F
+#define MMS114_INFOMATION		0x10
+#define MMS114_TSP_REV			0xF0
+
+/* Minimum delay time is 50us between stop and start signal of i2c */
+#define MMS114_I2C_DELAY		50
+
+/* 200ms needs after power on */
+#define MMS114_POWERON_DELAY		200
+
+/* Touchscreen absolute values */
+#define MMS114_MAX_AREA			0xff
+
+#define MMS114_MAX_TOUCH		10
+#define MMS114_PACKET_NUM		8
+
+/* Touch type */
+#define MMS114_TYPE_NONE		0
+#define MMS114_TYPE_TOUCHSCREEN		1
+#define MMS114_TYPE_TOUCHKEY		2
+
+struct mms114_data {
+	struct i2c_client	*client;
+	struct input_dev	*input_dev;
+	struct regulator	*core_reg;
+	struct regulator	*io_reg;
+	const struct mms114_platform_data	*pdata;
+
+	/* Use cache data for mode control register(write only) */
+	u8			cache_mode_control;
+};
+
+struct mms114_touch {
+	u8 id:4, reserved_bit4:1, type:2, pressed:1;
+	u8 x_hi:4, y_hi:4;
+	u8 x_lo;
+	u8 y_lo;
+	u8 width;
+	u8 strength;
+	u8 reserved[2];
+} __packed;
+
+static int __mms114_read_reg(struct mms114_data *data, unsigned int reg,
+			     unsigned int len, u8 *val)
+{
+	struct i2c_client *client = data->client;
+	struct i2c_msg xfer[2];
+	u8 buf = reg & 0xff;
+	int error;
+
+	if (reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL)
+		BUG();
+
+	/* Write register: use repeated start */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = I2C_M_TEN | I2C_M_NOSTART;
+	xfer[0].len = 1;
+	xfer[0].buf = &buf;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = len;
+	xfer[1].buf = val;
+
+	error = i2c_transfer(client->adapter, xfer, 2);
+	if (error != 2) {
+		dev_err(&client->dev,
+			"%s: i2c transfer failed (%d)\n", __func__, error);
+		return error < 0 ? error : -EIO;
+	}
+	udelay(MMS114_I2C_DELAY);
+
+	return 0;
+}
+
+static int mms114_read_reg(struct mms114_data *data, unsigned int reg)
+{
+	u8 val;
+	int error;
+
+	if (reg == MMS114_MODE_CONTROL)
+		return data->cache_mode_control;
+
+	error = __mms114_read_reg(data, reg, 1, &val);
+	return error < 0 ? error : val;
+}
+
+static int mms114_write_reg(struct mms114_data *data, unsigned int reg,
+			    unsigned int val)
+{
+	struct i2c_client *client = data->client;
+	u8 buf[2];
+	int error;
+
+	buf[0] = reg & 0xff;
+	buf[1] = val & 0xff;
+
+	error = i2c_master_send(client, buf, 2);
+	if (error != 2) {
+		dev_err(&client->dev,
+			"%s: i2c send failed (%d)\n", __func__, error);
+		return error < 0 ? error : -EIO;
+	}
+	udelay(MMS114_I2C_DELAY);
+
+	if (reg == MMS114_MODE_CONTROL)
+		data->cache_mode_control = val;
+
+	return 0;
+}
+
+static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *touch)
+{
+	const struct mms114_platform_data *pdata = data->pdata;
+	struct i2c_client *client = data->client;
+	struct input_dev *input_dev = data->input_dev;
+	unsigned int id;
+	unsigned int x;
+	unsigned int y;
+
+	if (touch->id > MMS114_MAX_TOUCH) {
+		dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id);
+		return;
+	}
+
+	if (touch->type != MMS114_TYPE_TOUCHSCREEN) {
+		dev_err(&client->dev, "Wrong touch type (%d)\n", touch->type);
+		return;
+	}
+
+	id = touch->id - 1;
+	x = touch->x_lo | touch->x_hi << 8;
+	y = touch->y_lo | touch->y_hi << 8;
+	if (x > pdata->x_size || y > pdata->y_size) {
+		dev_dbg(&client->dev,
+			"Wrong touch coordinates (%d, %d)\n", x, y);
+		return;
+	}
+
+	if (pdata->x_invert)
+		x = pdata->x_size - x;
+	if (pdata->y_invert)
+		y = pdata->y_size - y;
+
+	dev_dbg(&client->dev,
+		"id: %d, type: %d, pressed: %d, x: %d, y: %d, width: %d, strength: %d\n",
+		id, touch->type, touch->pressed,
+		x, y, touch->width, touch->strength);
+
+	input_mt_slot(input_dev, id);
+	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, touch->pressed);
+
+	if (touch->pressed) {
+		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, touch->width);
+		input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+		input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+		input_report_abs(input_dev, ABS_MT_PRESSURE, touch->strength);
+	}
+}
+
+static irqreturn_t mms114_interrupt(int irq, void *dev_id)
+{
+	struct mms114_data *data = dev_id;
+	struct input_dev *input_dev = data->input_dev;
+	struct mms114_touch touch[MMS114_MAX_TOUCH];
+	int packet_size;
+	int touch_size;
+	int index;
+	int error;
+
+	mutex_lock(&input_dev->mutex);
+	if (!input_dev->users) {
+		mutex_unlock(&input_dev->mutex);
+		goto out;
+	}
+	mutex_unlock(&input_dev->mutex);
+
+	packet_size = mms114_read_reg(data, MMS114_PACKET_SIZE);
+	if (packet_size <= 0)
+		goto out;
+
+	touch_size = packet_size / MMS114_PACKET_NUM;
+
+	error = __mms114_read_reg(data, MMS114_INFOMATION, packet_size,
+			(u8 *)touch);
+	if (error < 0)
+		goto out;
+
+	for (index = 0; index < touch_size; index++)
+		mms114_process_mt(data, touch + index);
+
+	input_mt_report_pointer_emulation(data->input_dev, true);
+	input_sync(data->input_dev);
+
+out:
+	return IRQ_HANDLED;
+}
+
+static int mms114_set_active(struct mms114_data *data, bool active)
+{
+	int val;
+
+	val = mms114_read_reg(data, MMS114_MODE_CONTROL);
+	if (val < 0)
+		return val;
+
+	val &= ~MMS114_OPERATION_MODE_MASK;
+
+	/* If active is false, sleep mode */
+	if (active)
+		val |= MMS114_ACTIVE;
+
+	return mms114_write_reg(data, MMS114_MODE_CONTROL, val);
+}
+
+static int mms114_get_version(struct mms114_data *data)
+{
+	struct device *dev = &data->client->dev;
+	u8 buf[6];
+	int error;
+
+	error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf);
+	if (error < 0)
+		return error;
+
+	dev_info(dev, "TSP Rev: 0x%x, HW Rev: 0x%x, Firmware Ver: 0x%x\n",
+		 buf[0], buf[1], buf[3]);
+
+	return 0;
+}
+
+static int mms114_setup_regs(struct mms114_data *data)
+{
+	const struct mms114_platform_data *pdata = data->pdata;
+	int val;
+	int error;
+
+	error = mms114_get_version(data);
+	if (error < 0)
+		return error;
+
+	error = mms114_set_active(data, true);
+	if (error < 0)
+		return error;
+
+	val = (pdata->x_size >> 8) & 0xf;
+	val |= ((pdata->y_size >> 8) & 0xf) << 4;
+	error = mms114_write_reg(data, MMS114_XY_RESOLUTION_H, val);
+	if (error < 0)
+		return error;
+
+	val = pdata->x_size & 0xff;
+	error = mms114_write_reg(data, MMS114_X_RESOLUTION, val);
+	if (error < 0)
+		return error;
+
+	val = pdata->y_size & 0xff;
+	error = mms114_write_reg(data, MMS114_Y_RESOLUTION, val);
+	if (error < 0)
+		return error;
+
+	if (pdata->contact_threshold) {
+		error = mms114_write_reg(data, MMS114_CONTACT_THRESHOLD,
+				pdata->contact_threshold);
+		if (error < 0)
+			return error;
+	}
+
+	if (pdata->moving_threshold) {
+		error = mms114_write_reg(data, MMS114_MOVING_THRESHOLD,
+				pdata->moving_threshold);
+		if (error < 0)
+			return error;
+	}
+
+	return 0;
+}
+
+static int mms114_start(struct mms114_data *data)
+{
+	struct i2c_client *client = data->client;
+	int error;
+
+	error = regulator_enable(data->core_reg);
+	if (error) {
+		dev_err(&client->dev, "Failed to enable avdd: %d\n", error);
+		return error;
+	}
+
+	error = regulator_enable(data->io_reg);
+	if (error) {
+		dev_err(&client->dev, "Failed to enable vdd: %d\n", error);
+		regulator_disable(data->core_reg);
+		return error;
+	}
+
+	mdelay(MMS114_POWERON_DELAY);
+
+	error = mms114_setup_regs(data);
+	if (error < 0) {
+		regulator_disable(data->io_reg);
+		regulator_disable(data->core_reg);
+		return error;
+	}
+
+	if (data->pdata->cfg_pin)
+		data->pdata->cfg_pin(true);
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static void mms114_stop(struct mms114_data *data)
+{
+	struct i2c_client *client = data->client;
+	int error;
+
+	disable_irq(client->irq);
+
+	if (data->pdata->cfg_pin)
+		data->pdata->cfg_pin(false);
+
+	error = regulator_disable(data->io_reg);
+	if (error)
+		dev_warn(&client->dev, "Failed to disable vdd: %d\n", error);
+
+	error = regulator_disable(data->core_reg);
+	if (error)
+		dev_warn(&client->dev, "Failed to disable avdd: %d\n", error);
+}
+
+static int mms114_input_open(struct input_dev *dev)
+{
+	struct mms114_data *data = input_get_drvdata(dev);
+
+	return mms114_start(data);
+}
+
+static void mms114_input_close(struct input_dev *dev)
+{
+	struct mms114_data *data = input_get_drvdata(dev);
+
+	mms114_stop(data);
+}
+
+#ifdef CONFIG_OF
+static struct mms114_platform_data *mms114_parse_dt(struct device *dev)
+{
+	struct mms114_platform_data *pdata;
+	struct device_node *np = dev->of_node;
+
+	if (!np)
+		return NULL;
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		dev_err(dev, "failed to allocate platform data\n");
+		return NULL;
+	}
+
+	if (of_property_read_u32(np, "x-size", &pdata->x_size)) {
+		dev_err(dev, "failed to get x-size property\n");
+		return NULL;
+	}
+
+	if (of_property_read_u32(np, "y-size", &pdata->y_size)) {
+		dev_err(dev, "failed to get y-size property\n");
+		return NULL;
+	}
+
+	of_property_read_u32(np, "contact-threshold",
+				&pdata->contact_threshold);
+	of_property_read_u32(np, "moving-threshold",
+				&pdata->moving_threshold);
+
+	if (of_find_property(np, "x-invert", NULL))
+		pdata->x_invert = true;
+	if (of_find_property(np, "y-invert", NULL))
+		pdata->y_invert = true;
+
+	return pdata;
+}
+#else
+static inline struct mms114_platform_data *mms114_parse_dt(struct device *dev)
+{
+	return NULL;
+}
+#endif
+
+static int mms114_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	const struct mms114_platform_data *pdata;
+	struct mms114_data *data;
+	struct input_dev *input_dev;
+	int error;
+
+	pdata = dev_get_platdata(&client->dev);
+	if (!pdata)
+		pdata = mms114_parse_dt(&client->dev);
+
+	if (!pdata) {
+		dev_err(&client->dev, "Need platform data\n");
+		return -EINVAL;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_PROTOCOL_MANGLING)) {
+		dev_err(&client->dev,
+			"Need i2c bus that supports protocol mangling\n");
+		return -ENODEV;
+	}
+
+	data = devm_kzalloc(&client->dev, sizeof(struct mms114_data),
+			    GFP_KERNEL);
+	input_dev = devm_input_allocate_device(&client->dev);
+	if (!data || !input_dev) {
+		dev_err(&client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	data->client = client;
+	data->input_dev = input_dev;
+	data->pdata = pdata;
+
+	input_dev->name = "MELFAS MMS114 Touchscreen";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &client->dev;
+	input_dev->open = mms114_input_open;
+	input_dev->close = mms114_input_close;
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+	input_set_abs_params(input_dev, ABS_X, 0, data->pdata->x_size, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0);
+
+	/* For multi touch */
+	input_mt_init_slots(input_dev, MMS114_MAX_TOUCH, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+			     0, MMS114_MAX_AREA, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+			     0, data->pdata->x_size, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+			     0, data->pdata->y_size, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+	input_set_drvdata(input_dev, data);
+	i2c_set_clientdata(client, data);
+
+	data->core_reg = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(data->core_reg)) {
+		error = PTR_ERR(data->core_reg);
+		dev_err(&client->dev,
+			"Unable to get the Core regulator (%d)\n", error);
+		return error;
+	}
+
+	data->io_reg = devm_regulator_get(&client->dev, "vdd");
+	if (IS_ERR(data->io_reg)) {
+		error = PTR_ERR(data->io_reg);
+		dev_err(&client->dev,
+			"Unable to get the IO regulator (%d)\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+			mms114_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+			dev_name(&client->dev), data);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+	disable_irq(client->irq);
+
+	error = input_register_device(data->input_dev);
+	if (error) {
+		dev_err(&client->dev, "Failed to register input device\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused mms114_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mms114_data *data = i2c_get_clientdata(client);
+	struct input_dev *input_dev = data->input_dev;
+	int id;
+
+	/* Release all touch */
+	for (id = 0; id < MMS114_MAX_TOUCH; id++) {
+		input_mt_slot(input_dev, id);
+		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
+	}
+
+	input_mt_report_pointer_emulation(input_dev, true);
+	input_sync(input_dev);
+
+	mutex_lock(&input_dev->mutex);
+	if (input_dev->users)
+		mms114_stop(data);
+	mutex_unlock(&input_dev->mutex);
+
+	return 0;
+}
+
+static int __maybe_unused mms114_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mms114_data *data = i2c_get_clientdata(client);
+	struct input_dev *input_dev = data->input_dev;
+	int error;
+
+	mutex_lock(&input_dev->mutex);
+	if (input_dev->users) {
+		error = mms114_start(data);
+		if (error < 0) {
+			mutex_unlock(&input_dev->mutex);
+			return error;
+		}
+	}
+	mutex_unlock(&input_dev->mutex);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mms114_pm_ops, mms114_suspend, mms114_resume);
+
+static const struct i2c_device_id mms114_id[] = {
+	{ "mms114", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, mms114_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id mms114_dt_match[] = {
+	{ .compatible = "melfas,mms114" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, mms114_dt_match);
+#endif
+
+static struct i2c_driver mms114_driver = {
+	.driver = {
+		.name	= "mms114",
+		.pm	= &mms114_pm_ops,
+		.of_match_table = of_match_ptr(mms114_dt_match),
+	},
+	.probe		= mms114_probe,
+	.id_table	= mms114_id,
+};
+
+module_i2c_driver(mms114_driver);
+
+/* Module information */
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_DESCRIPTION("MELFAS mms114 Touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mtouch.c b/drivers/input/touchscreen/mtouch.c
new file mode 100644
index 0000000..9b5552a
--- /dev/null
+++ b/drivers/input/touchscreen/mtouch.c
@@ -0,0 +1,204 @@
+/*
+ * MicroTouch (3M) serial touchscreen driver
+ *
+ * Copyright (c) 2004 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * 2005/02/19 Dan Streetman <ddstreet@ieee.org>
+ *   Copied elo.c and edited for MicroTouch protocol
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"MicroTouch serial touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define MTOUCH_FORMAT_TABLET_STATUS_BIT 0x80
+#define MTOUCH_FORMAT_TABLET_TOUCH_BIT 0x40
+#define MTOUCH_FORMAT_TABLET_LENGTH 5
+#define MTOUCH_RESPONSE_BEGIN_BYTE 0x01
+#define MTOUCH_RESPONSE_END_BYTE 0x0d
+
+/* todo: check specs for max length of all responses */
+#define MTOUCH_MAX_LENGTH 16
+
+#define MTOUCH_MIN_XC 0
+#define MTOUCH_MAX_XC 0x3fff
+#define MTOUCH_MIN_YC 0
+#define MTOUCH_MAX_YC 0x3fff
+
+#define MTOUCH_GET_XC(data) (((data[2])<<7) | data[1])
+#define MTOUCH_GET_YC(data) (((data[4])<<7) | data[3])
+#define MTOUCH_GET_TOUCHED(data) (MTOUCH_FORMAT_TABLET_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct mtouch {
+	struct input_dev *dev;
+	struct serio *serio;
+	int idx;
+	unsigned char data[MTOUCH_MAX_LENGTH];
+	char phys[32];
+};
+
+static void mtouch_process_format_tablet(struct mtouch *mtouch)
+{
+	struct input_dev *dev = mtouch->dev;
+
+	if (MTOUCH_FORMAT_TABLET_LENGTH == ++mtouch->idx) {
+		input_report_abs(dev, ABS_X, MTOUCH_GET_XC(mtouch->data));
+		input_report_abs(dev, ABS_Y, MTOUCH_MAX_YC - MTOUCH_GET_YC(mtouch->data));
+		input_report_key(dev, BTN_TOUCH, MTOUCH_GET_TOUCHED(mtouch->data));
+		input_sync(dev);
+
+		mtouch->idx = 0;
+	}
+}
+
+static void mtouch_process_response(struct mtouch *mtouch)
+{
+	if (MTOUCH_RESPONSE_END_BYTE == mtouch->data[mtouch->idx++]) {
+		/* FIXME - process response */
+		mtouch->idx = 0;
+	} else if (MTOUCH_MAX_LENGTH == mtouch->idx) {
+		printk(KERN_ERR "mtouch.c: too many response bytes\n");
+		mtouch->idx = 0;
+	}
+}
+
+static irqreturn_t mtouch_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct mtouch* mtouch = serio_get_drvdata(serio);
+
+	mtouch->data[mtouch->idx] = data;
+
+	if (MTOUCH_FORMAT_TABLET_STATUS_BIT & mtouch->data[0])
+		mtouch_process_format_tablet(mtouch);
+	else if (MTOUCH_RESPONSE_BEGIN_BYTE == mtouch->data[0])
+		mtouch_process_response(mtouch);
+	else
+		printk(KERN_DEBUG "mtouch.c: unknown/unsynchronized data from device, byte %x\n",mtouch->data[0]);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * mtouch_disconnect() is the opposite of mtouch_connect()
+ */
+
+static void mtouch_disconnect(struct serio *serio)
+{
+	struct mtouch* mtouch = serio_get_drvdata(serio);
+
+	input_get_device(mtouch->dev);
+	input_unregister_device(mtouch->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_put_device(mtouch->dev);
+	kfree(mtouch);
+}
+
+/*
+ * mtouch_connect() is the routine that is called when someone adds a
+ * new serio device that supports MicroTouch (Format Tablet) protocol and registers it as
+ * an input device.
+ */
+
+static int mtouch_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct mtouch *mtouch;
+	struct input_dev *input_dev;
+	int err;
+
+	mtouch = kzalloc(sizeof(struct mtouch), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!mtouch || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	mtouch->serio = serio;
+	mtouch->dev = input_dev;
+	snprintf(mtouch->phys, sizeof(mtouch->phys), "%s/input0", serio->phys);
+
+	input_dev->name = "MicroTouch Serial TouchScreen";
+	input_dev->phys = mtouch->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_MICROTOUCH;
+	input_dev->id.product = 0;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(mtouch->dev, ABS_X, MTOUCH_MIN_XC, MTOUCH_MAX_XC, 0, 0);
+	input_set_abs_params(mtouch->dev, ABS_Y, MTOUCH_MIN_YC, MTOUCH_MAX_YC, 0, 0);
+
+	serio_set_drvdata(serio, mtouch);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(mtouch->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(mtouch);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id mtouch_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MICROTOUCH,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, mtouch_serio_ids);
+
+static struct serio_driver mtouch_drv = {
+	.driver		= {
+		.name	= "mtouch",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= mtouch_serio_ids,
+	.interrupt	= mtouch_interrupt,
+	.connect	= mtouch_connect,
+	.disconnect	= mtouch_disconnect,
+};
+
+module_serio_driver(mtouch_drv);
diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c
new file mode 100644
index 0000000..bb6f2fe
--- /dev/null
+++ b/drivers/input/touchscreen/of_touchscreen.c
@@ -0,0 +1,108 @@
+/*
+ *  Generic DT helper functions for touchscreen devices
+ *
+ *  Copyright (c) 2014 Sebastian Reichel <sre@kernel.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/property.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+
+static bool touchscreen_get_prop_u32(struct device *dev,
+				     const char *property,
+				     unsigned int default_value,
+				     unsigned int *value)
+{
+	u32 val;
+	int error;
+
+	error = device_property_read_u32(dev, property, &val);
+	if (error) {
+		*value = default_value;
+		return false;
+	}
+
+	*value = val;
+	return true;
+}
+
+static void touchscreen_set_params(struct input_dev *dev,
+				   unsigned long axis,
+				   int max, int fuzz)
+{
+	struct input_absinfo *absinfo;
+
+	if (!test_bit(axis, dev->absbit)) {
+		dev_warn(&dev->dev,
+			 "DT specifies parameters but the axis %lu is not set up\n",
+			 axis);
+		return;
+	}
+
+	absinfo = &dev->absinfo[axis];
+	absinfo->maximum = max;
+	absinfo->fuzz = fuzz;
+}
+
+/**
+ * touchscreen_parse_properties - parse common touchscreen DT properties
+ * @input: input device that should be parsed
+ * @multitouch: specifies whether parsed properties should be applied to
+ *	single-touch or multi-touch axes
+ *
+ * This function parses common DT properties for touchscreens and setups the
+ * input device accordingly. The function keeps previously set up default
+ * values if no value is specified via DT.
+ */
+void touchscreen_parse_properties(struct input_dev *input, bool multitouch)
+{
+	struct device *dev = input->dev.parent;
+	unsigned int axis;
+	unsigned int maximum, fuzz;
+	bool data_present;
+
+	input_alloc_absinfo(input);
+	if (!input->absinfo)
+		return;
+
+	axis = multitouch ? ABS_MT_POSITION_X : ABS_X;
+	data_present = touchscreen_get_prop_u32(dev, "touchscreen-size-x",
+						input_abs_get_max(input,
+								  axis) + 1,
+						&maximum) |
+		       touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x",
+						input_abs_get_fuzz(input, axis),
+						&fuzz);
+	if (data_present)
+		touchscreen_set_params(input, axis, maximum - 1, fuzz);
+
+	axis = multitouch ? ABS_MT_POSITION_Y : ABS_Y;
+	data_present = touchscreen_get_prop_u32(dev, "touchscreen-size-y",
+						input_abs_get_max(input,
+								  axis) + 1,
+						&maximum) |
+		       touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y",
+						input_abs_get_fuzz(input, axis),
+						&fuzz);
+	if (data_present)
+		touchscreen_set_params(input, axis, maximum - 1, fuzz);
+
+	axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE;
+	data_present = touchscreen_get_prop_u32(dev,
+						"touchscreen-max-pressure",
+						input_abs_get_max(input, axis),
+						&maximum) |
+		       touchscreen_get_prop_u32(dev,
+						"touchscreen-fuzz-pressure",
+						input_abs_get_fuzz(input, axis),
+						&fuzz);
+	if (data_present)
+		touchscreen_set_params(input, axis, maximum, fuzz);
+}
+EXPORT_SYMBOL(touchscreen_parse_properties);
diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c
new file mode 100644
index 0000000..23a354a
--- /dev/null
+++ b/drivers/input/touchscreen/pcap_ts.c
@@ -0,0 +1,258 @@
+/*
+ * Driver for Motorola PCAP2 touchscreen as found in the EZX phone platform.
+ *
+ *  Copyright (C) 2006 Harald Welte <laforge@openezx.org>
+ *  Copyright (C) 2009 Daniel Ribeiro <drwyrm@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/mfd/ezx-pcap.h>
+
+struct pcap_ts {
+	struct pcap_chip *pcap;
+	struct input_dev *input;
+	struct delayed_work work;
+	u16 x, y;
+	u16 pressure;
+	u8 read_state;
+};
+
+#define SAMPLE_DELAY	20 /* msecs */
+
+#define X_AXIS_MIN	0
+#define X_AXIS_MAX	1023
+#define Y_AXIS_MAX	X_AXIS_MAX
+#define Y_AXIS_MIN	X_AXIS_MIN
+#define PRESSURE_MAX	X_AXIS_MAX
+#define PRESSURE_MIN	X_AXIS_MIN
+
+static void pcap_ts_read_xy(void *data, u16 res[2])
+{
+	struct pcap_ts *pcap_ts = data;
+
+	switch (pcap_ts->read_state) {
+	case PCAP_ADC_TS_M_PRESSURE:
+		/* pressure reading is unreliable */
+		if (res[0] > PRESSURE_MIN && res[0] < PRESSURE_MAX)
+			pcap_ts->pressure = res[0];
+		pcap_ts->read_state = PCAP_ADC_TS_M_XY;
+		schedule_delayed_work(&pcap_ts->work, 0);
+		break;
+	case PCAP_ADC_TS_M_XY:
+		pcap_ts->y = res[0];
+		pcap_ts->x = res[1];
+		if (pcap_ts->x <= X_AXIS_MIN || pcap_ts->x >= X_AXIS_MAX ||
+		    pcap_ts->y <= Y_AXIS_MIN || pcap_ts->y >= Y_AXIS_MAX) {
+			/* pen has been released */
+			input_report_abs(pcap_ts->input, ABS_PRESSURE, 0);
+			input_report_key(pcap_ts->input, BTN_TOUCH, 0);
+
+			pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY;
+			schedule_delayed_work(&pcap_ts->work, 0);
+		} else {
+			/* pen is touching the screen */
+			input_report_abs(pcap_ts->input, ABS_X, pcap_ts->x);
+			input_report_abs(pcap_ts->input, ABS_Y, pcap_ts->y);
+			input_report_key(pcap_ts->input, BTN_TOUCH, 1);
+			input_report_abs(pcap_ts->input, ABS_PRESSURE,
+						pcap_ts->pressure);
+
+			/* switch back to pressure read mode */
+			pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE;
+			schedule_delayed_work(&pcap_ts->work,
+					msecs_to_jiffies(SAMPLE_DELAY));
+		}
+		input_sync(pcap_ts->input);
+		break;
+	default:
+		dev_warn(&pcap_ts->input->dev,
+				"pcap_ts: Warning, unhandled read_state %d\n",
+				pcap_ts->read_state);
+		break;
+	}
+}
+
+static void pcap_ts_work(struct work_struct *work)
+{
+	struct delayed_work *dw = container_of(work, struct delayed_work, work);
+	struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work);
+	u8 ch[2];
+
+	pcap_set_ts_bits(pcap_ts->pcap,
+			pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+
+	if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY)
+		return;
+
+	/* start adc conversion */
+	ch[0] = PCAP_ADC_CH_TS_X1;
+	ch[1] = PCAP_ADC_CH_TS_Y1;
+	pcap_adc_async(pcap_ts->pcap, PCAP_ADC_BANK_1, 0, ch,
+						pcap_ts_read_xy, pcap_ts);
+}
+
+static irqreturn_t pcap_ts_event_touch(int pirq, void *data)
+{
+	struct pcap_ts *pcap_ts = data;
+
+	if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) {
+		pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE;
+		schedule_delayed_work(&pcap_ts->work, 0);
+	}
+	return IRQ_HANDLED;
+}
+
+static int pcap_ts_open(struct input_dev *dev)
+{
+	struct pcap_ts *pcap_ts = input_get_drvdata(dev);
+
+	pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY;
+	schedule_delayed_work(&pcap_ts->work, 0);
+
+	return 0;
+}
+
+static void pcap_ts_close(struct input_dev *dev)
+{
+	struct pcap_ts *pcap_ts = input_get_drvdata(dev);
+
+	cancel_delayed_work_sync(&pcap_ts->work);
+
+	pcap_ts->read_state = PCAP_ADC_TS_M_NONTS;
+	pcap_set_ts_bits(pcap_ts->pcap,
+				pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+}
+
+static int pcap_ts_probe(struct platform_device *pdev)
+{
+	struct input_dev *input_dev;
+	struct pcap_ts *pcap_ts;
+	int err = -ENOMEM;
+
+	pcap_ts = kzalloc(sizeof(*pcap_ts), GFP_KERNEL);
+	if (!pcap_ts)
+		return err;
+
+	pcap_ts->pcap = dev_get_drvdata(pdev->dev.parent);
+	platform_set_drvdata(pdev, pcap_ts);
+
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		goto fail;
+
+	INIT_DELAYED_WORK(&pcap_ts->work, pcap_ts_work);
+
+	pcap_ts->read_state = PCAP_ADC_TS_M_NONTS;
+	pcap_set_ts_bits(pcap_ts->pcap,
+				pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+
+	pcap_ts->input = input_dev;
+	input_set_drvdata(input_dev, pcap_ts);
+
+	input_dev->name = "pcap-touchscreen";
+	input_dev->phys = "pcap_ts/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0002;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &pdev->dev;
+	input_dev->open = pcap_ts_open;
+	input_dev->close = pcap_ts_close;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN,
+			     PRESSURE_MAX, 0, 0);
+
+	err = input_register_device(pcap_ts->input);
+	if (err)
+		goto fail_allocate;
+
+	err = request_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS),
+			pcap_ts_event_touch, 0, "Touch Screen", pcap_ts);
+	if (err)
+		goto fail_register;
+
+	return 0;
+
+fail_register:
+	input_unregister_device(input_dev);
+	goto fail;
+fail_allocate:
+	input_free_device(input_dev);
+fail:
+	kfree(pcap_ts);
+
+	return err;
+}
+
+static int pcap_ts_remove(struct platform_device *pdev)
+{
+	struct pcap_ts *pcap_ts = platform_get_drvdata(pdev);
+
+	free_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), pcap_ts);
+	cancel_delayed_work_sync(&pcap_ts->work);
+
+	input_unregister_device(pcap_ts->input);
+
+	kfree(pcap_ts);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int pcap_ts_suspend(struct device *dev)
+{
+	struct pcap_ts *pcap_ts = dev_get_drvdata(dev);
+
+	pcap_set_ts_bits(pcap_ts->pcap, PCAP_ADC_TS_REF_LOWPWR);
+	return 0;
+}
+
+static int pcap_ts_resume(struct device *dev)
+{
+	struct pcap_ts *pcap_ts = dev_get_drvdata(dev);
+
+	pcap_set_ts_bits(pcap_ts->pcap,
+				pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
+	return 0;
+}
+
+static const struct dev_pm_ops pcap_ts_pm_ops = {
+	.suspend	= pcap_ts_suspend,
+	.resume		= pcap_ts_resume,
+};
+#define PCAP_TS_PM_OPS (&pcap_ts_pm_ops)
+#else
+#define PCAP_TS_PM_OPS NULL
+#endif
+
+static struct platform_driver pcap_ts_driver = {
+	.probe		= pcap_ts_probe,
+	.remove		= pcap_ts_remove,
+	.driver		= {
+		.name	= "pcap-ts",
+		.pm	= PCAP_TS_PM_OPS,
+	},
+};
+module_platform_driver(pcap_ts_driver);
+
+MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver");
+MODULE_AUTHOR("Daniel Ribeiro / Harald Welte");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcap_ts");
diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c
new file mode 100644
index 0000000..417d873
--- /dev/null
+++ b/drivers/input/touchscreen/penmount.c
@@ -0,0 +1,319 @@
+/*
+ * Penmount serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ * Copyright (c) 2011 John Sung <penmount.touch@gmail.com>
+ *
+ * Based on ELO driver (drivers/input/touchscreen/elo.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"PenMount serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_AUTHOR("John Sung <penmount.touch@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define	PM_MAX_LENGTH	6
+#define	PM_MAX_MTSLOT	16
+#define	PM_3000_MTSLOT	2
+#define	PM_6250_MTSLOT	12
+
+/*
+ * Multi-touch slot
+ */
+
+struct mt_slot {
+	unsigned short x, y;
+	bool active; /* is the touch valid? */
+};
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct pm {
+	struct input_dev *dev;
+	struct serio *serio;
+	int idx;
+	unsigned char data[PM_MAX_LENGTH];
+	char phys[32];
+	unsigned char packetsize;
+	unsigned char maxcontacts;
+	struct mt_slot slots[PM_MAX_MTSLOT];
+	void (*parse_packet)(struct pm *);
+};
+
+/*
+ * pm_mtevent() sends mt events and also emulates pointer movement
+ */
+
+static void pm_mtevent(struct pm *pm, struct input_dev *input)
+{
+	int i;
+
+	for (i = 0; i < pm->maxcontacts; ++i) {
+		input_mt_slot(input, i);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER,
+				pm->slots[i].active);
+		if (pm->slots[i].active) {
+			input_event(input, EV_ABS, ABS_MT_POSITION_X, pm->slots[i].x);
+			input_event(input, EV_ABS, ABS_MT_POSITION_Y, pm->slots[i].y);
+		}
+	}
+
+	input_mt_report_pointer_emulation(input, true);
+	input_sync(input);
+}
+
+/*
+ * pm_checkpacket() checks if data packet is valid
+ */
+
+static bool pm_checkpacket(unsigned char *packet)
+{
+	int total = 0;
+	int i;
+
+	for (i = 0; i < 5; i++)
+		total += packet[i];
+
+	return packet[5] == (unsigned char)~(total & 0xff);
+}
+
+static void pm_parse_9000(struct pm *pm)
+{
+	struct input_dev *dev = pm->dev;
+
+	if ((pm->data[0] & 0x80) && pm->packetsize == ++pm->idx) {
+		input_report_abs(dev, ABS_X, pm->data[1] * 128 + pm->data[2]);
+		input_report_abs(dev, ABS_Y, pm->data[3] * 128 + pm->data[4]);
+		input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40));
+		input_sync(dev);
+		pm->idx = 0;
+	}
+}
+
+static void pm_parse_6000(struct pm *pm)
+{
+	struct input_dev *dev = pm->dev;
+
+	if ((pm->data[0] & 0xbf) == 0x30 && pm->packetsize == ++pm->idx) {
+		if (pm_checkpacket(pm->data)) {
+			input_report_abs(dev, ABS_X,
+					pm->data[2] * 256 + pm->data[1]);
+			input_report_abs(dev, ABS_Y,
+					pm->data[4] * 256 + pm->data[3]);
+			input_report_key(dev, BTN_TOUCH, pm->data[0] & 0x40);
+			input_sync(dev);
+		}
+		pm->idx = 0;
+	}
+}
+
+static void pm_parse_3000(struct pm *pm)
+{
+	struct input_dev *dev = pm->dev;
+
+	if ((pm->data[0] & 0xce) == 0x40 && pm->packetsize == ++pm->idx) {
+		if (pm_checkpacket(pm->data)) {
+			int slotnum = pm->data[0] & 0x0f;
+			pm->slots[slotnum].active = pm->data[0] & 0x30;
+			pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1];
+			pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3];
+			pm_mtevent(pm, dev);
+		}
+		pm->idx = 0;
+	}
+}
+
+static void pm_parse_6250(struct pm *pm)
+{
+	struct input_dev *dev = pm->dev;
+
+	if ((pm->data[0] & 0xb0) == 0x30 && pm->packetsize == ++pm->idx) {
+		if (pm_checkpacket(pm->data)) {
+			int slotnum = pm->data[0] & 0x0f;
+			pm->slots[slotnum].active = pm->data[0] & 0x40;
+			pm->slots[slotnum].x = pm->data[2] * 256 + pm->data[1];
+			pm->slots[slotnum].y = pm->data[4] * 256 + pm->data[3];
+			pm_mtevent(pm, dev);
+		}
+		pm->idx = 0;
+	}
+}
+
+static irqreturn_t pm_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct pm *pm = serio_get_drvdata(serio);
+
+	pm->data[pm->idx] = data;
+
+	pm->parse_packet(pm);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * pm_disconnect() is the opposite of pm_connect()
+ */
+
+static void pm_disconnect(struct serio *serio)
+{
+	struct pm *pm = serio_get_drvdata(serio);
+
+	serio_close(serio);
+
+	input_unregister_device(pm->dev);
+	kfree(pm);
+
+	serio_set_drvdata(serio, NULL);
+}
+
+/*
+ * pm_connect() is the routine that is called when someone adds a
+ * new serio device that supports PenMount protocol and registers it as
+ * an input device.
+ */
+
+static int pm_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct pm *pm;
+	struct input_dev *input_dev;
+	int max_x, max_y;
+	int err;
+
+	pm = kzalloc(sizeof(struct pm), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!pm || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	pm->serio = serio;
+	pm->dev = input_dev;
+	snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys);
+	pm->maxcontacts = 1;
+
+	input_dev->name = "PenMount Serial TouchScreen";
+	input_dev->phys = pm->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_PENMOUNT;
+	input_dev->id.product = 0;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	switch (serio->id.id) {
+	default:
+	case 0:
+		pm->packetsize = 5;
+		pm->parse_packet = pm_parse_9000;
+		input_dev->id.product = 0x9000;
+		max_x = max_y = 0x3ff;
+		break;
+
+	case 1:
+		pm->packetsize = 6;
+		pm->parse_packet = pm_parse_6000;
+		input_dev->id.product = 0x6000;
+		max_x = max_y = 0x3ff;
+		break;
+
+	case 2:
+		pm->packetsize = 6;
+		pm->parse_packet = pm_parse_3000;
+		input_dev->id.product = 0x3000;
+		max_x = max_y = 0x7ff;
+		pm->maxcontacts = PM_3000_MTSLOT;
+		break;
+
+	case 3:
+		pm->packetsize = 6;
+		pm->parse_packet = pm_parse_6250;
+		input_dev->id.product = 0x6250;
+		max_x = max_y = 0x3ff;
+		pm->maxcontacts = PM_6250_MTSLOT;
+		break;
+	}
+
+	input_set_abs_params(pm->dev, ABS_X, 0, max_x, 0, 0);
+	input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0);
+
+	if (pm->maxcontacts > 1) {
+		input_mt_init_slots(pm->dev, pm->maxcontacts, 0);
+		input_set_abs_params(pm->dev,
+				     ABS_MT_POSITION_X, 0, max_x, 0, 0);
+		input_set_abs_params(pm->dev,
+				     ABS_MT_POSITION_Y, 0, max_y, 0, 0);
+	}
+
+	serio_set_drvdata(serio, pm);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(pm->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(pm);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id pm_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_PENMOUNT,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, pm_serio_ids);
+
+static struct serio_driver pm_drv = {
+	.driver		= {
+		.name	= "serio-penmount",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= pm_serio_ids,
+	.interrupt	= pm_interrupt,
+	.connect	= pm_connect,
+	.disconnect	= pm_disconnect,
+};
+
+module_serio_driver(pm_drv);
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
new file mode 100644
index 0000000..4b961ad
--- /dev/null
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -0,0 +1,608 @@
+/*
+ * Driver for Pixcir I2C touchscreen controllers.
+ *
+ * Copyright (C) 2010-2011 Pixcir, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+/*#include <linux/of.h>*/
+#include <linux/of_device.h>
+#include <linux/platform_data/pixcir_i2c_ts.h>
+
+#define PIXCIR_MAX_SLOTS       5 /* Max fingers supported by driver */
+
+struct pixcir_i2c_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct gpio_desc *gpio_attb;
+	struct gpio_desc *gpio_reset;
+	const struct pixcir_i2c_chip_data *chip;
+	int max_fingers;	/* Max fingers supported in this instance */
+	bool running;
+};
+
+struct pixcir_touch {
+	int x;
+	int y;
+	int id;
+};
+
+struct pixcir_report_data {
+	int num_touches;
+	struct pixcir_touch touches[PIXCIR_MAX_SLOTS];
+};
+
+static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
+			    struct pixcir_report_data *report)
+{
+	u8 rdbuf[2 + PIXCIR_MAX_SLOTS * 5];
+	u8 wrbuf[1] = { 0 };
+	u8 *bufptr;
+	u8 touch;
+	int ret, i;
+	int readsize;
+	const struct pixcir_i2c_chip_data *chip = tsdata->chip;
+
+	memset(report, 0, sizeof(struct pixcir_report_data));
+
+	i = chip->has_hw_ids ? 1 : 0;
+	readsize = 2 + tsdata->max_fingers * (4 + i);
+	if (readsize > sizeof(rdbuf))
+		readsize = sizeof(rdbuf);
+
+	ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf));
+	if (ret != sizeof(wrbuf)) {
+		dev_err(&tsdata->client->dev,
+			"%s: i2c_master_send failed(), ret=%d\n",
+			__func__, ret);
+		return;
+	}
+
+	ret = i2c_master_recv(tsdata->client, rdbuf, readsize);
+	if (ret != readsize) {
+		dev_err(&tsdata->client->dev,
+			"%s: i2c_master_recv failed(), ret=%d\n",
+			__func__, ret);
+		return;
+	}
+
+	touch = rdbuf[0] & 0x7;
+	if (touch > tsdata->max_fingers)
+		touch = tsdata->max_fingers;
+
+	report->num_touches = touch;
+	bufptr = &rdbuf[2];
+
+	for (i = 0; i < touch; i++) {
+		report->touches[i].x = (bufptr[1] << 8) | bufptr[0];
+		report->touches[i].y = (bufptr[3] << 8) | bufptr[2];
+
+		if (chip->has_hw_ids) {
+			report->touches[i].id = bufptr[4];
+			bufptr = bufptr + 5;
+		} else {
+			bufptr = bufptr + 4;
+		}
+	}
+}
+
+static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
+			     struct pixcir_report_data *report)
+{
+	struct input_mt_pos pos[PIXCIR_MAX_SLOTS];
+	int slots[PIXCIR_MAX_SLOTS];
+	struct pixcir_touch *touch;
+	int n, i, slot;
+	struct device *dev = &ts->client->dev;
+	const struct pixcir_i2c_chip_data *chip = ts->chip;
+
+	n = report->num_touches;
+	if (n > PIXCIR_MAX_SLOTS)
+		n = PIXCIR_MAX_SLOTS;
+
+	if (!ts->chip->has_hw_ids) {
+		for (i = 0; i < n; i++) {
+			touch = &report->touches[i];
+			pos[i].x = touch->x;
+			pos[i].y = touch->y;
+		}
+
+		input_mt_assign_slots(ts->input, slots, pos, n, 0);
+	}
+
+	for (i = 0; i < n; i++) {
+		touch = &report->touches[i];
+
+		if (chip->has_hw_ids) {
+			slot = input_mt_get_slot_by_key(ts->input, touch->id);
+			if (slot < 0) {
+				dev_dbg(dev, "no free slot for id 0x%x\n",
+					touch->id);
+				continue;
+			}
+		} else {
+			slot = slots[i];
+		}
+
+		input_mt_slot(ts->input, slot);
+		input_mt_report_slot_state(ts->input,
+					   MT_TOOL_FINGER, true);
+
+		input_event(ts->input, EV_ABS, ABS_MT_POSITION_X, touch->x);
+		input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y, touch->y);
+
+		dev_dbg(dev, "%d: slot %d, x %d, y %d\n",
+			i, slot, touch->x, touch->y);
+	}
+
+	input_mt_sync_frame(ts->input);
+	input_sync(ts->input);
+}
+
+static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
+{
+	struct pixcir_i2c_ts_data *tsdata = dev_id;
+	struct pixcir_report_data report;
+
+	while (tsdata->running) {
+		/* parse packet */
+		pixcir_ts_parse(tsdata, &report);
+
+		/* report it */
+		pixcir_ts_report(tsdata, &report);
+
+		if (gpiod_get_value_cansleep(tsdata->gpio_attb)) {
+			if (report.num_touches) {
+				/*
+				 * Last report with no finger up?
+				 * Do it now then.
+				 */
+				input_mt_sync_frame(tsdata->input);
+				input_sync(tsdata->input);
+			}
+			break;
+		}
+
+		msleep(20);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void pixcir_reset(struct pixcir_i2c_ts_data *tsdata)
+{
+	if (!IS_ERR_OR_NULL(tsdata->gpio_reset)) {
+		gpiod_set_value_cansleep(tsdata->gpio_reset, 1);
+		ndelay(100);	/* datasheet section 1.2.3 says 80ns min. */
+		gpiod_set_value_cansleep(tsdata->gpio_reset, 0);
+		/* wait for controller ready. 100ms guess. */
+		msleep(100);
+	}
+}
+
+static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts,
+				 enum pixcir_power_mode mode)
+{
+	struct device *dev = &ts->client->dev;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE);
+	if (ret < 0) {
+		dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+			__func__, PIXCIR_REG_POWER_MODE, ret);
+		return ret;
+	}
+
+	ret &= ~PIXCIR_POWER_MODE_MASK;
+	ret |= mode;
+
+	/* Always AUTO_IDLE */
+	ret |= PIXCIR_POWER_ALLOW_IDLE;
+
+	ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_POWER_MODE, ret);
+	if (ret < 0) {
+		dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+			__func__, PIXCIR_REG_POWER_MODE, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Set the interrupt mode for the device i.e. ATTB line behaviour
+ *
+ * @polarity : 1 for active high, 0 for active low.
+ */
+static int pixcir_set_int_mode(struct pixcir_i2c_ts_data *ts,
+			       enum pixcir_int_mode mode, bool polarity)
+{
+	struct device *dev = &ts->client->dev;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE);
+	if (ret < 0) {
+		dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+			__func__, PIXCIR_REG_INT_MODE, ret);
+		return ret;
+	}
+
+	ret &= ~PIXCIR_INT_MODE_MASK;
+	ret |= mode;
+
+	if (polarity)
+		ret |= PIXCIR_INT_POL_HIGH;
+	else
+		ret &= ~PIXCIR_INT_POL_HIGH;
+
+	ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret);
+	if (ret < 0) {
+		dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+			__func__, PIXCIR_REG_INT_MODE, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Enable/disable interrupt generation
+ */
+static int pixcir_int_enable(struct pixcir_i2c_ts_data *ts, bool enable)
+{
+	struct device *dev = &ts->client->dev;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE);
+	if (ret < 0) {
+		dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+			__func__, PIXCIR_REG_INT_MODE, ret);
+		return ret;
+	}
+
+	if (enable)
+		ret |= PIXCIR_INT_ENABLE;
+	else
+		ret &= ~PIXCIR_INT_ENABLE;
+
+	ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret);
+	if (ret < 0) {
+		dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+			__func__, PIXCIR_REG_INT_MODE, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int pixcir_start(struct pixcir_i2c_ts_data *ts)
+{
+	struct device *dev = &ts->client->dev;
+	int error;
+
+	/* LEVEL_TOUCH interrupt with active low polarity */
+	error = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0);
+	if (error) {
+		dev_err(dev, "Failed to set interrupt mode: %d\n", error);
+		return error;
+	}
+
+	ts->running = true;
+	mb();	/* Update status before IRQ can fire */
+
+	/* enable interrupt generation */
+	error = pixcir_int_enable(ts, true);
+	if (error) {
+		dev_err(dev, "Failed to enable interrupt generation: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int pixcir_stop(struct pixcir_i2c_ts_data *ts)
+{
+	int error;
+
+	/* Disable interrupt generation */
+	error = pixcir_int_enable(ts, false);
+	if (error) {
+		dev_err(&ts->client->dev,
+			"Failed to disable interrupt generation: %d\n",
+			error);
+		return error;
+	}
+
+	/* Exit ISR if running, no more report parsing */
+	ts->running = false;
+	mb();	/* update status before we synchronize irq */
+
+	/* Wait till running ISR is complete */
+	synchronize_irq(ts->client->irq);
+
+	return 0;
+}
+
+static int pixcir_input_open(struct input_dev *dev)
+{
+	struct pixcir_i2c_ts_data *ts = input_get_drvdata(dev);
+
+	return pixcir_start(ts);
+}
+
+static void pixcir_input_close(struct input_dev *dev)
+{
+	struct pixcir_i2c_ts_data *ts = input_get_drvdata(dev);
+
+	pixcir_stop(ts);
+}
+
+static int __maybe_unused pixcir_i2c_ts_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client);
+	struct input_dev *input = ts->input;
+	int ret = 0;
+
+	mutex_lock(&input->mutex);
+
+	if (device_may_wakeup(&client->dev)) {
+		if (!input->users) {
+			ret = pixcir_start(ts);
+			if (ret) {
+				dev_err(dev, "Failed to start\n");
+				goto unlock;
+			}
+		}
+	} else if (input->users) {
+		ret = pixcir_stop(ts);
+	}
+
+unlock:
+	mutex_unlock(&input->mutex);
+
+	return ret;
+}
+
+static int __maybe_unused pixcir_i2c_ts_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client);
+	struct input_dev *input = ts->input;
+	int ret = 0;
+
+	mutex_lock(&input->mutex);
+
+	if (device_may_wakeup(&client->dev)) {
+
+		if (!input->users) {
+			ret = pixcir_stop(ts);
+			if (ret) {
+				dev_err(dev, "Failed to stop\n");
+				goto unlock;
+			}
+		}
+	} else if (input->users) {
+		ret = pixcir_start(ts);
+	}
+
+unlock:
+	mutex_unlock(&input->mutex);
+
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops,
+			 pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pixcir_of_match[];
+
+static int pixcir_parse_dt(struct device *dev,
+			   struct pixcir_i2c_ts_data *tsdata)
+{
+	const struct of_device_id *match;
+
+	match = of_match_device(of_match_ptr(pixcir_of_match), dev);
+	if (!match)
+		return -EINVAL;
+
+	tsdata->chip = (const struct pixcir_i2c_chip_data *)match->data;
+	if (!tsdata->chip)
+		return -EINVAL;
+
+	return 0;
+}
+#else
+static int pixcir_parse_dt(struct device *dev,
+			   struct pixcir_i2c_ts_data *tsdata)
+{
+	return -EINVAL;
+}
+#endif
+
+static int pixcir_i2c_ts_probe(struct i2c_client *client,
+			       const struct i2c_device_id *id)
+{
+	const struct pixcir_ts_platform_data *pdata =
+			dev_get_platdata(&client->dev);
+	struct device *dev = &client->dev;
+	struct pixcir_i2c_ts_data *tsdata;
+	struct input_dev *input;
+	int error;
+
+	tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL);
+	if (!tsdata)
+		return -ENOMEM;
+
+	if (pdata) {
+		tsdata->chip = &pdata->chip;
+	} else if (dev->of_node) {
+		error = pixcir_parse_dt(dev, tsdata);
+		if (error)
+			return error;
+	} else {
+		dev_err(&client->dev, "platform data not defined\n");
+		return -EINVAL;
+	}
+
+	if (!tsdata->chip->max_fingers) {
+		dev_err(dev, "Invalid max_fingers in chip data\n");
+		return -EINVAL;
+	}
+
+	input = devm_input_allocate_device(dev);
+	if (!input) {
+		dev_err(dev, "Failed to allocate input device\n");
+		return -ENOMEM;
+	}
+
+	tsdata->client = client;
+	tsdata->input = input;
+
+	input->name = client->name;
+	input->id.bustype = BUS_I2C;
+	input->open = pixcir_input_open;
+	input->close = pixcir_input_close;
+	input->dev.parent = &client->dev;
+
+	if (pdata) {
+		input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0);
+		input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0);
+	} else {
+		input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
+		input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
+		touchscreen_parse_properties(input, true);
+		if (!input_abs_get_max(input, ABS_MT_POSITION_X) ||
+		    !input_abs_get_max(input, ABS_MT_POSITION_Y)) {
+			dev_err(dev, "Touchscreen size is not specified\n");
+			return -EINVAL;
+		}
+	}
+
+	tsdata->max_fingers = tsdata->chip->max_fingers;
+	if (tsdata->max_fingers > PIXCIR_MAX_SLOTS) {
+		tsdata->max_fingers = PIXCIR_MAX_SLOTS;
+		dev_info(dev, "Limiting maximum fingers to %d\n",
+			 tsdata->max_fingers);
+	}
+
+	error = input_mt_init_slots(input, tsdata->max_fingers,
+				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(dev, "Error initializing Multi-Touch slots\n");
+		return error;
+	}
+
+	input_set_drvdata(input, tsdata);
+
+	tsdata->gpio_attb = devm_gpiod_get(dev, "attb", GPIOD_IN);
+	if (IS_ERR(tsdata->gpio_attb)) {
+		error = PTR_ERR(tsdata->gpio_attb);
+		dev_err(dev, "Failed to request ATTB gpio: %d\n", error);
+		return error;
+	}
+
+	tsdata->gpio_reset = devm_gpiod_get_optional(dev, "reset",
+						     GPIOD_OUT_LOW);
+	if (IS_ERR(tsdata->gpio_reset)) {
+		error = PTR_ERR(tsdata->gpio_reset);
+		dev_err(dev, "Failed to request RESET gpio: %d\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr,
+					  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					  client->name, tsdata);
+	if (error) {
+		dev_err(dev, "failed to request irq %d\n", client->irq);
+		return error;
+	}
+
+	pixcir_reset(tsdata);
+
+	/* Always be in IDLE mode to save power, device supports auto wake */
+	error = pixcir_set_power_mode(tsdata, PIXCIR_POWER_IDLE);
+	if (error) {
+		dev_err(dev, "Failed to set IDLE mode\n");
+		return error;
+	}
+
+	/* Stop device till opened */
+	error = pixcir_stop(tsdata);
+	if (error)
+		return error;
+
+	error = input_register_device(input);
+	if (error)
+		return error;
+
+	i2c_set_clientdata(client, tsdata);
+
+	return 0;
+}
+
+static const struct i2c_device_id pixcir_i2c_ts_id[] = {
+	{ "pixcir_ts", 0 },
+	{ "pixcir_tangoc", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id);
+
+#ifdef CONFIG_OF
+static const struct pixcir_i2c_chip_data pixcir_ts_data = {
+	.max_fingers = 2,
+	/* no hw id support */
+};
+
+static const struct pixcir_i2c_chip_data pixcir_tangoc_data = {
+	.max_fingers = 5,
+	.has_hw_ids = true,
+};
+
+static const struct of_device_id pixcir_of_match[] = {
+	{ .compatible = "pixcir,pixcir_ts", .data = &pixcir_ts_data },
+	{ .compatible = "pixcir,pixcir_tangoc", .data = &pixcir_tangoc_data },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pixcir_of_match);
+#endif
+
+static struct i2c_driver pixcir_i2c_ts_driver = {
+	.driver = {
+		.name	= "pixcir_ts",
+		.pm	= &pixcir_dev_pm_ops,
+		.of_match_table = of_match_ptr(pixcir_of_match),
+	},
+	.probe		= pixcir_i2c_ts_probe,
+	.id_table	= pixcir_i2c_ts_id,
+};
+
+module_i2c_driver(pixcir_i2c_ts_driver);
+
+MODULE_AUTHOR("Jianchun Bian <jcbian@pixcir.com.cn>, Dequan Meng <dqmeng@pixcir.com.cn>");
+MODULE_DESCRIPTION("Pixcir I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c
new file mode 100644
index 0000000..ba6024f
--- /dev/null
+++ b/drivers/input/touchscreen/rohm_bu21023.c
@@ -0,0 +1,1218 @@
+/*
+ * ROHM BU21023/24 Dual touch support resistive touch screen driver
+ * Copyright (C) 2012 ROHM CO.,LTD.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#define BU21023_NAME			"bu21023_ts"
+#define BU21023_FIRMWARE_NAME		"bu21023.bin"
+
+#define MAX_CONTACTS			2
+
+#define AXIS_ADJUST			4
+#define AXIS_OFFSET			8
+
+#define FIRMWARE_BLOCK_SIZE		32U
+#define FIRMWARE_RETRY_MAX		4
+
+#define SAMPLING_DELAY			12	/* msec */
+
+#define CALIBRATION_RETRY_MAX		6
+
+#define ROHM_TS_ABS_X_MIN		40
+#define ROHM_TS_ABS_X_MAX		990
+#define ROHM_TS_ABS_Y_MIN		160
+#define ROHM_TS_ABS_Y_MAX		920
+#define ROHM_TS_DISPLACEMENT_MAX	0	/* zero for infinite */
+
+/*
+ * BU21023GUL/BU21023MUV/BU21024FV-M registers map
+ */
+#define VADOUT_YP_H		0x00
+#define VADOUT_YP_L		0x01
+#define VADOUT_XP_H		0x02
+#define VADOUT_XP_L		0x03
+#define VADOUT_YN_H		0x04
+#define VADOUT_YN_L		0x05
+#define VADOUT_XN_H		0x06
+#define VADOUT_XN_L		0x07
+
+#define PRM1_X_H		0x08
+#define PRM1_X_L		0x09
+#define PRM1_Y_H		0x0a
+#define PRM1_Y_L		0x0b
+#define PRM2_X_H		0x0c
+#define PRM2_X_L		0x0d
+#define PRM2_Y_H		0x0e
+#define PRM2_Y_L		0x0f
+
+#define MLT_PRM_MONI_X		0x10
+#define MLT_PRM_MONI_Y		0x11
+
+#define DEBUG_MONI_1		0x12
+#define DEBUG_MONI_2		0x13
+
+#define VADOUT_ZX_H		0x14
+#define VADOUT_ZX_L		0x15
+#define VADOUT_ZY_H		0x16
+#define VADOUT_ZY_L		0x17
+
+#define Z_PARAM_H		0x18
+#define Z_PARAM_L		0x19
+
+/*
+ * Value for VADOUT_*_L
+ */
+#define VADOUT_L_MASK		0x01
+
+/*
+ * Value for PRM*_*_L
+ */
+#define PRM_L_MASK		0x01
+
+#define POS_X1_H		0x20
+#define POS_X1_L		0x21
+#define POS_Y1_H		0x22
+#define POS_Y1_L		0x23
+#define POS_X2_H		0x24
+#define POS_X2_L		0x25
+#define POS_Y2_H		0x26
+#define POS_Y2_L		0x27
+
+/*
+ * Value for POS_*_L
+ */
+#define POS_L_MASK		0x01
+
+#define TOUCH			0x28
+#define TOUCH_DETECT		0x01
+
+#define TOUCH_GESTURE		0x29
+#define SINGLE_TOUCH		0x01
+#define DUAL_TOUCH		0x03
+#define TOUCH_MASK		0x03
+#define CALIBRATION_REQUEST	0x04
+#define CALIBRATION_STATUS	0x08
+#define CALIBRATION_MASK	0x0c
+#define GESTURE_SPREAD		0x10
+#define GESTURE_PINCH		0x20
+#define GESTURE_ROTATE_R	0x40
+#define GESTURE_ROTATE_L	0x80
+
+#define INT_STATUS		0x2a
+#define INT_MASK		0x3d
+#define INT_CLEAR		0x3e
+
+/*
+ * Values for INT_*
+ */
+#define COORD_UPDATE		0x01
+#define CALIBRATION_DONE	0x02
+#define SLEEP_IN		0x04
+#define SLEEP_OUT		0x08
+#define PROGRAM_LOAD_DONE	0x10
+#define ERROR			0x80
+#define INT_ALL			0x9f
+
+#define ERR_STATUS		0x2b
+#define ERR_MASK		0x3f
+
+/*
+ * Values for ERR_*
+ */
+#define ADC_TIMEOUT		0x01
+#define CPU_TIMEOUT		0x02
+#define CALIBRATION_ERR		0x04
+#define PROGRAM_LOAD_ERR	0x10
+
+#define COMMON_SETUP1			0x30
+#define PROGRAM_LOAD_HOST		0x02
+#define PROGRAM_LOAD_EEPROM		0x03
+#define CENSOR_4PORT			0x04
+#define CENSOR_8PORT			0x00	/* Not supported by BU21023 */
+#define CALIBRATION_TYPE_DEFAULT	0x08
+#define CALIBRATION_TYPE_SPECIAL	0x00
+#define INT_ACTIVE_HIGH			0x10
+#define INT_ACTIVE_LOW			0x00
+#define AUTO_CALIBRATION		0x40
+#define MANUAL_CALIBRATION		0x00
+#define COMMON_SETUP1_DEFAULT		0x4e
+
+#define COMMON_SETUP2		0x31
+#define MAF_NONE		0x00
+#define MAF_1SAMPLE		0x01
+#define MAF_3SAMPLES		0x02
+#define MAF_5SAMPLES		0x03
+#define INV_Y			0x04
+#define INV_X			0x08
+#define SWAP_XY			0x10
+
+#define COMMON_SETUP3		0x32
+#define EN_SLEEP		0x01
+#define EN_MULTI		0x02
+#define EN_GESTURE		0x04
+#define EN_INTVL		0x08
+#define SEL_STEP		0x10
+#define SEL_MULTI		0x20
+#define SEL_TBL_DEFAULT		0x40
+
+#define INTERVAL_TIME		0x33
+#define INTERVAL_TIME_DEFAULT	0x10
+
+#define STEP_X			0x34
+#define STEP_X_DEFAULT		0x41
+
+#define STEP_Y			0x35
+#define STEP_Y_DEFAULT		0x8d
+
+#define OFFSET_X		0x38
+#define OFFSET_X_DEFAULT	0x0c
+
+#define OFFSET_Y		0x39
+#define OFFSET_Y_DEFAULT	0x0c
+
+#define THRESHOLD_TOUCH		0x3a
+#define THRESHOLD_TOUCH_DEFAULT	0xa0
+
+#define THRESHOLD_GESTURE		0x3b
+#define THRESHOLD_GESTURE_DEFAULT	0x17
+
+#define SYSTEM			0x40
+#define ANALOG_POWER_ON		0x01
+#define ANALOG_POWER_OFF	0x00
+#define CPU_POWER_ON		0x02
+#define CPU_POWER_OFF		0x00
+
+#define FORCE_CALIBRATION	0x42
+#define FORCE_CALIBRATION_ON	0x01
+#define FORCE_CALIBRATION_OFF	0x00
+
+#define CPU_FREQ		0x50	/* 10 / (reg + 1) MHz */
+#define CPU_FREQ_10MHZ		0x00
+#define CPU_FREQ_5MHZ		0x01
+#define CPU_FREQ_1MHZ		0x09
+
+#define EEPROM_ADDR		0x51
+
+#define CALIBRATION_ADJUST		0x52
+#define CALIBRATION_ADJUST_DEFAULT	0x00
+
+#define THRESHOLD_SLEEP_IN	0x53
+
+#define EVR_XY			0x56
+#define EVR_XY_DEFAULT		0x10
+
+#define PRM_SWOFF_TIME		0x57
+#define PRM_SWOFF_TIME_DEFAULT	0x04
+
+#define PROGRAM_VERSION		0x5f
+
+#define ADC_CTRL		0x60
+#define ADC_DIV_MASK		0x1f	/* The minimum value is 4 */
+#define ADC_DIV_DEFAULT		0x08
+
+#define ADC_WAIT		0x61
+#define ADC_WAIT_DEFAULT	0x0a
+
+#define SWCONT			0x62
+#define SWCONT_DEFAULT		0x0f
+
+#define EVR_X			0x63
+#define EVR_X_DEFAULT		0x86
+
+#define EVR_Y			0x64
+#define EVR_Y_DEFAULT		0x64
+
+#define TEST1			0x65
+#define DUALTOUCH_STABILIZE_ON	0x01
+#define DUALTOUCH_STABILIZE_OFF	0x00
+#define DUALTOUCH_REG_ON	0x20
+#define DUALTOUCH_REG_OFF	0x00
+
+#define CALIBRATION_REG1		0x68
+#define CALIBRATION_REG1_DEFAULT	0xd9
+
+#define CALIBRATION_REG2		0x69
+#define CALIBRATION_REG2_DEFAULT	0x36
+
+#define CALIBRATION_REG3		0x6a
+#define CALIBRATION_REG3_DEFAULT	0x32
+
+#define EX_ADDR_H		0x70
+#define EX_ADDR_L		0x71
+#define EX_WDAT			0x72
+#define EX_RDAT			0x73
+#define EX_CHK_SUM1		0x74
+#define EX_CHK_SUM2		0x75
+#define EX_CHK_SUM3		0x76
+
+struct rohm_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input;
+
+	bool initialized;
+
+	unsigned int contact_count[MAX_CONTACTS + 1];
+	int finger_count;
+
+	u8 setup2;
+};
+
+/*
+ * rohm_i2c_burst_read - execute combined I2C message for ROHM BU21023/24
+ * @client: Handle to ROHM BU21023/24
+ * @start: Where to start read address from ROHM BU21023/24
+ * @buf: Where to store read data from ROHM BU21023/24
+ * @len: How many bytes to read
+ *
+ * Returns negative errno, else zero on success.
+ *
+ * Note
+ * In BU21023/24 burst read, stop condition is needed after "address write".
+ * Therefore, transmission is performed in 2 steps.
+ */
+static int rohm_i2c_burst_read(struct i2c_client *client, u8 start, void *buf,
+			       size_t len)
+{
+	struct i2c_adapter *adap = client->adapter;
+	struct i2c_msg msg[2];
+	int i, ret = 0;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = &start;
+
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = len;
+	msg[1].buf = buf;
+
+	i2c_lock_adapter(adap);
+
+	for (i = 0; i < 2; i++) {
+		if (__i2c_transfer(adap, &msg[i], 1) < 0) {
+			ret = -EIO;
+			break;
+		}
+	}
+
+	i2c_unlock_adapter(adap);
+
+	return ret;
+}
+
+static int rohm_ts_manual_calibration(struct rohm_ts_data *ts)
+{
+	struct i2c_client *client = ts->client;
+	struct device *dev = &client->dev;
+	u8 buf[33];	/* for PRM1_X_H(0x08)-TOUCH(0x28) */
+
+	int retry;
+	bool success = false;
+	bool first_time = true;
+	bool calibration_done;
+
+	u8 reg1, reg2, reg3;
+	s32 reg1_orig, reg2_orig, reg3_orig;
+	s32 val;
+
+	int calib_x = 0, calib_y = 0;
+	int reg_x, reg_y;
+	int err_x, err_y;
+
+	int error, error2;
+	int i;
+
+	reg1_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG1);
+	if (reg1_orig < 0)
+		return reg1_orig;
+
+	reg2_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG2);
+	if (reg2_orig < 0)
+		return reg2_orig;
+
+	reg3_orig = i2c_smbus_read_byte_data(client, CALIBRATION_REG3);
+	if (reg3_orig < 0)
+		return reg3_orig;
+
+	error = i2c_smbus_write_byte_data(client, INT_MASK,
+					  COORD_UPDATE | SLEEP_IN | SLEEP_OUT |
+					  PROGRAM_LOAD_DONE);
+	if (error)
+		goto out;
+
+	error = i2c_smbus_write_byte_data(client, TEST1,
+					  DUALTOUCH_STABILIZE_ON);
+	if (error)
+		goto out;
+
+	for (retry = 0; retry < CALIBRATION_RETRY_MAX; retry++) {
+		/* wait 2 sampling for update */
+		mdelay(2 * SAMPLING_DELAY);
+
+#define READ_CALIB_BUF(reg)	buf[((reg) - PRM1_X_H)]
+
+		error = rohm_i2c_burst_read(client, PRM1_X_H, buf, sizeof(buf));
+		if (error)
+			goto out;
+
+		if (READ_CALIB_BUF(TOUCH) & TOUCH_DETECT)
+			continue;
+
+		if (first_time) {
+			/* generate calibration parameter */
+			calib_x = ((int)READ_CALIB_BUF(PRM1_X_H) << 2 |
+				READ_CALIB_BUF(PRM1_X_L)) - AXIS_OFFSET;
+			calib_y = ((int)READ_CALIB_BUF(PRM1_Y_H) << 2 |
+				READ_CALIB_BUF(PRM1_Y_L)) - AXIS_OFFSET;
+
+			error = i2c_smbus_write_byte_data(client, TEST1,
+				DUALTOUCH_STABILIZE_ON | DUALTOUCH_REG_ON);
+			if (error)
+				goto out;
+
+			first_time = false;
+		} else {
+			/* generate adjustment parameter */
+			err_x = (int)READ_CALIB_BUF(PRM1_X_H) << 2 |
+				READ_CALIB_BUF(PRM1_X_L);
+			err_y = (int)READ_CALIB_BUF(PRM1_Y_H) << 2 |
+				READ_CALIB_BUF(PRM1_Y_L);
+
+			/* X axis ajust */
+			if (err_x <= 4)
+				calib_x -= AXIS_ADJUST;
+			else if (err_x >= 60)
+				calib_x += AXIS_ADJUST;
+
+			/* Y axis ajust */
+			if (err_y <= 4)
+				calib_y -= AXIS_ADJUST;
+			else if (err_y >= 60)
+				calib_y += AXIS_ADJUST;
+		}
+
+		/* generate calibration setting value */
+		reg_x = calib_x + ((calib_x & 0x200) << 1);
+		reg_y = calib_y + ((calib_y & 0x200) << 1);
+
+		/* convert for register format */
+		reg1 = reg_x >> 3;
+		reg2 = (reg_y & 0x7) << 4 | (reg_x & 0x7);
+		reg3 = reg_y >> 3;
+
+		error = i2c_smbus_write_byte_data(client,
+						  CALIBRATION_REG1, reg1);
+		if (error)
+			goto out;
+
+		error = i2c_smbus_write_byte_data(client,
+						  CALIBRATION_REG2, reg2);
+		if (error)
+			goto out;
+
+		error = i2c_smbus_write_byte_data(client,
+						  CALIBRATION_REG3, reg3);
+		if (error)
+			goto out;
+
+		/*
+		 * force calibration sequcence
+		 */
+		error = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+						  FORCE_CALIBRATION_OFF);
+		if (error)
+			goto out;
+
+		error = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+						  FORCE_CALIBRATION_ON);
+		if (error)
+			goto out;
+
+		/* clear all interrupts */
+		error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+		if (error)
+			goto out;
+
+		/*
+		 * Wait for the status change of calibration, max 10 sampling
+		 */
+		calibration_done = false;
+
+		for (i = 0; i < 10; i++) {
+			mdelay(SAMPLING_DELAY);
+
+			val = i2c_smbus_read_byte_data(client, TOUCH_GESTURE);
+			if (!(val & CALIBRATION_MASK)) {
+				calibration_done = true;
+				break;
+			} else if (val < 0) {
+				error = val;
+				goto out;
+			}
+		}
+
+		if (calibration_done) {
+			val = i2c_smbus_read_byte_data(client, INT_STATUS);
+			if (val == CALIBRATION_DONE) {
+				success = true;
+				break;
+			} else if (val < 0) {
+				error = val;
+				goto out;
+			}
+		} else {
+			dev_warn(dev, "calibration timeout\n");
+		}
+	}
+
+	if (!success) {
+		error = i2c_smbus_write_byte_data(client, CALIBRATION_REG1,
+						  reg1_orig);
+		if (error)
+			goto out;
+
+		error = i2c_smbus_write_byte_data(client, CALIBRATION_REG2,
+						  reg2_orig);
+		if (error)
+			goto out;
+
+		error = i2c_smbus_write_byte_data(client, CALIBRATION_REG3,
+						  reg3_orig);
+		if (error)
+			goto out;
+
+		/* calibration data enable */
+		error = i2c_smbus_write_byte_data(client, TEST1,
+						  DUALTOUCH_STABILIZE_ON |
+						  DUALTOUCH_REG_ON);
+		if (error)
+			goto out;
+
+		/* wait 10 sampling */
+		mdelay(10 * SAMPLING_DELAY);
+
+		error = -EBUSY;
+	}
+
+out:
+	error2 = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL);
+	if (!error2)
+		/* Clear all interrupts */
+		error2 = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+
+	return error ? error : error2;
+}
+
+static const unsigned int untouch_threshold[3] = { 0, 1, 5 };
+static const unsigned int single_touch_threshold[3] = { 0, 0, 4 };
+static const unsigned int dual_touch_threshold[3] = { 10, 8, 0 };
+
+static irqreturn_t rohm_ts_soft_irq(int irq, void *dev_id)
+{
+	struct rohm_ts_data *ts = dev_id;
+	struct i2c_client *client = ts->client;
+	struct input_dev *input_dev = ts->input;
+	struct device *dev = &client->dev;
+
+	u8 buf[10];	/* for POS_X1_H(0x20)-TOUCH_GESTURE(0x29) */
+
+	struct input_mt_pos pos[MAX_CONTACTS];
+	int slots[MAX_CONTACTS];
+	u8 touch_flags;
+	unsigned int threshold;
+	int finger_count = -1;
+	int prev_finger_count = ts->finger_count;
+	int count;
+	int error;
+	int i;
+
+	error = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL);
+	if (error)
+		return IRQ_HANDLED;
+
+	/* Clear all interrupts */
+	error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+	if (error)
+		return IRQ_HANDLED;
+
+#define READ_POS_BUF(reg)	buf[((reg) - POS_X1_H)]
+
+	error = rohm_i2c_burst_read(client, POS_X1_H, buf, sizeof(buf));
+	if (error)
+		return IRQ_HANDLED;
+
+	touch_flags = READ_POS_BUF(TOUCH_GESTURE) & TOUCH_MASK;
+	if (touch_flags) {
+		/* generate coordinates */
+		pos[0].x = ((s16)READ_POS_BUF(POS_X1_H) << 2) |
+			   READ_POS_BUF(POS_X1_L);
+		pos[0].y = ((s16)READ_POS_BUF(POS_Y1_H) << 2) |
+			   READ_POS_BUF(POS_Y1_L);
+		pos[1].x = ((s16)READ_POS_BUF(POS_X2_H) << 2) |
+			   READ_POS_BUF(POS_X2_L);
+		pos[1].y = ((s16)READ_POS_BUF(POS_Y2_H) << 2) |
+			   READ_POS_BUF(POS_Y2_L);
+	}
+
+	switch (touch_flags) {
+	case 0:
+		threshold = untouch_threshold[prev_finger_count];
+		if (++ts->contact_count[0] >= threshold)
+			finger_count = 0;
+		break;
+
+	case SINGLE_TOUCH:
+		threshold = single_touch_threshold[prev_finger_count];
+		if (++ts->contact_count[1] >= threshold)
+			finger_count = 1;
+
+		if (finger_count == 1) {
+			if (pos[1].x != 0 && pos[1].y != 0) {
+				pos[0].x = pos[1].x;
+				pos[0].y = pos[1].y;
+				pos[1].x = 0;
+				pos[1].y = 0;
+			}
+		}
+		break;
+
+	case DUAL_TOUCH:
+		threshold = dual_touch_threshold[prev_finger_count];
+		if (++ts->contact_count[2] >= threshold)
+			finger_count = 2;
+		break;
+
+	default:
+		dev_dbg(dev,
+			"Three or more touches are not supported\n");
+		return IRQ_HANDLED;
+	}
+
+	if (finger_count >= 0) {
+		if (prev_finger_count != finger_count) {
+			count = ts->contact_count[finger_count];
+			memset(ts->contact_count, 0, sizeof(ts->contact_count));
+			ts->contact_count[finger_count] = count;
+		}
+
+		input_mt_assign_slots(input_dev, slots, pos,
+				      finger_count, ROHM_TS_DISPLACEMENT_MAX);
+
+		for (i = 0; i < finger_count; i++) {
+			input_mt_slot(input_dev, slots[i]);
+			input_mt_report_slot_state(input_dev,
+						   MT_TOOL_FINGER, true);
+			input_report_abs(input_dev,
+					 ABS_MT_POSITION_X, pos[i].x);
+			input_report_abs(input_dev,
+					 ABS_MT_POSITION_Y, pos[i].y);
+		}
+
+		input_mt_sync_frame(input_dev);
+		input_mt_report_pointer_emulation(input_dev, true);
+		input_sync(input_dev);
+
+		ts->finger_count = finger_count;
+	}
+
+	if (READ_POS_BUF(TOUCH_GESTURE) & CALIBRATION_REQUEST) {
+		error = rohm_ts_manual_calibration(ts);
+		if (error)
+			dev_warn(dev, "manual calibration failed: %d\n",
+				 error);
+	}
+
+	i2c_smbus_write_byte_data(client, INT_MASK,
+				  CALIBRATION_DONE | SLEEP_OUT | SLEEP_IN |
+				  PROGRAM_LOAD_DONE);
+
+	return IRQ_HANDLED;
+}
+
+static int rohm_ts_load_firmware(struct i2c_client *client,
+				 const char *firmware_name)
+{
+	struct device *dev = &client->dev;
+	const struct firmware *fw;
+	s32 status;
+	unsigned int offset, len, xfer_len;
+	unsigned int retry = 0;
+	int error, error2;
+
+	error = request_firmware(&fw, firmware_name, dev);
+	if (error) {
+		dev_err(dev, "unable to retrieve firmware %s: %d\n",
+			firmware_name, error);
+		return error;
+	}
+
+	error = i2c_smbus_write_byte_data(client, INT_MASK,
+					  COORD_UPDATE | CALIBRATION_DONE |
+					  SLEEP_IN | SLEEP_OUT);
+	if (error)
+		goto out;
+
+	do {
+		if (retry) {
+			dev_warn(dev, "retrying firmware load\n");
+
+			/* settings for retry */
+			error = i2c_smbus_write_byte_data(client, EX_WDAT, 0);
+			if (error)
+				goto out;
+		}
+
+		error = i2c_smbus_write_byte_data(client, EX_ADDR_H, 0);
+		if (error)
+			goto out;
+
+		error = i2c_smbus_write_byte_data(client, EX_ADDR_L, 0);
+		if (error)
+			goto out;
+
+		error = i2c_smbus_write_byte_data(client, COMMON_SETUP1,
+						  COMMON_SETUP1_DEFAULT);
+		if (error)
+			goto out;
+
+		/* firmware load to the device */
+		offset = 0;
+		len = fw->size;
+
+		while (len) {
+			xfer_len = min(FIRMWARE_BLOCK_SIZE, len);
+
+			error = i2c_smbus_write_i2c_block_data(client, EX_WDAT,
+						xfer_len, &fw->data[offset]);
+			if (error)
+				goto out;
+
+			len -= xfer_len;
+			offset += xfer_len;
+		}
+
+		/* check firmware load result */
+		status = i2c_smbus_read_byte_data(client, INT_STATUS);
+		if (status < 0) {
+			error = status;
+			goto out;
+		}
+
+		/* clear all interrupts */
+		error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+		if (error)
+			goto out;
+
+		if (status == PROGRAM_LOAD_DONE)
+			break;
+
+		error = -EIO;
+	} while (++retry >= FIRMWARE_RETRY_MAX);
+
+out:
+	error2 = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL);
+
+	release_firmware(fw);
+
+	return error ? error : error2;
+}
+
+static ssize_t swap_xy_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct rohm_ts_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%d\n", !!(ts->setup2 & SWAP_XY));
+}
+
+static ssize_t swap_xy_store(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct rohm_ts_data *ts = i2c_get_clientdata(client);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 0, &val);
+	if (error)
+		return error;
+
+	error = mutex_lock_interruptible(&ts->input->mutex);
+	if (error)
+		return error;
+
+	if (val)
+		ts->setup2 |= SWAP_XY;
+	else
+		ts->setup2 &= ~SWAP_XY;
+
+	if (ts->initialized)
+		error = i2c_smbus_write_byte_data(ts->client, COMMON_SETUP2,
+						  ts->setup2);
+
+	mutex_unlock(&ts->input->mutex);
+
+	return error ? error : count;
+}
+
+static ssize_t inv_x_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct rohm_ts_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%d\n", !!(ts->setup2 & INV_X));
+}
+
+static ssize_t inv_x_store(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct rohm_ts_data *ts = i2c_get_clientdata(client);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 0, &val);
+	if (error)
+		return error;
+
+	error = mutex_lock_interruptible(&ts->input->mutex);
+	if (error)
+		return error;
+
+	if (val)
+		ts->setup2 |= INV_X;
+	else
+		ts->setup2 &= ~INV_X;
+
+	if (ts->initialized)
+		error = i2c_smbus_write_byte_data(ts->client, COMMON_SETUP2,
+						  ts->setup2);
+
+	mutex_unlock(&ts->input->mutex);
+
+	return error ? error : count;
+}
+
+static ssize_t inv_y_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct rohm_ts_data *ts = i2c_get_clientdata(client);
+
+	return sprintf(buf, "%d\n", !!(ts->setup2 & INV_Y));
+}
+
+static ssize_t inv_y_store(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct rohm_ts_data *ts = i2c_get_clientdata(client);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 0, &val);
+	if (error)
+		return error;
+
+	error = mutex_lock_interruptible(&ts->input->mutex);
+	if (error)
+		return error;
+
+	if (val)
+		ts->setup2 |= INV_Y;
+	else
+		ts->setup2 &= ~INV_Y;
+
+	if (ts->initialized)
+		error = i2c_smbus_write_byte_data(client, COMMON_SETUP2,
+						  ts->setup2);
+
+	mutex_unlock(&ts->input->mutex);
+
+	return error ? error : count;
+}
+
+static DEVICE_ATTR_RW(swap_xy);
+static DEVICE_ATTR_RW(inv_x);
+static DEVICE_ATTR_RW(inv_y);
+
+static struct attribute *rohm_ts_attrs[] = {
+	&dev_attr_swap_xy.attr,
+	&dev_attr_inv_x.attr,
+	&dev_attr_inv_y.attr,
+	NULL,
+};
+
+static const struct attribute_group rohm_ts_attr_group = {
+	.attrs = rohm_ts_attrs,
+};
+
+static int rohm_ts_device_init(struct i2c_client *client, u8 setup2)
+{
+	struct device *dev = &client->dev;
+	int error;
+
+	disable_irq(client->irq);
+
+	/*
+	 * Wait 200usec for reset
+	 */
+	udelay(200);
+
+	/* Release analog reset */
+	error = i2c_smbus_write_byte_data(client, SYSTEM,
+					  ANALOG_POWER_ON | CPU_POWER_OFF);
+	if (error)
+		return error;
+
+	/* Waiting for the analog warm-up, max. 200usec */
+	udelay(200);
+
+	/* clear all interrupts */
+	error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, EX_WDAT, 0);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, COMMON_SETUP1, 0);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, COMMON_SETUP2, setup2);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, COMMON_SETUP3,
+					  SEL_TBL_DEFAULT | EN_MULTI);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, THRESHOLD_GESTURE,
+					  THRESHOLD_GESTURE_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, INTERVAL_TIME,
+					  INTERVAL_TIME_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, CPU_FREQ, CPU_FREQ_10MHZ);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, PRM_SWOFF_TIME,
+					  PRM_SWOFF_TIME_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, ADC_CTRL, ADC_DIV_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, ADC_WAIT, ADC_WAIT_DEFAULT);
+	if (error)
+		return error;
+
+	/*
+	 * Panel setup, these values change with the panel.
+	 */
+	error = i2c_smbus_write_byte_data(client, STEP_X, STEP_X_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, STEP_Y, STEP_Y_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, OFFSET_X, OFFSET_X_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, OFFSET_Y, OFFSET_Y_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, THRESHOLD_TOUCH,
+					  THRESHOLD_TOUCH_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, EVR_XY, EVR_XY_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, EVR_X, EVR_X_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, EVR_Y, EVR_Y_DEFAULT);
+	if (error)
+		return error;
+
+	/* Fixed value settings */
+	error = i2c_smbus_write_byte_data(client, CALIBRATION_ADJUST,
+					  CALIBRATION_ADJUST_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, SWCONT, SWCONT_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, TEST1,
+					  DUALTOUCH_STABILIZE_ON |
+					  DUALTOUCH_REG_ON);
+	if (error)
+		return error;
+
+	error = rohm_ts_load_firmware(client, BU21023_FIRMWARE_NAME);
+	if (error) {
+		dev_err(dev, "failed to load firmware: %d\n", error);
+		return error;
+	}
+
+	/*
+	 * Manual calibration results are not changed in same environment.
+	 * If the force calibration is performed,
+	 * the controller will not require calibration request interrupt
+	 * when the typical values are set to the calibration registers.
+	 */
+	error = i2c_smbus_write_byte_data(client, CALIBRATION_REG1,
+					  CALIBRATION_REG1_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, CALIBRATION_REG2,
+					  CALIBRATION_REG2_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, CALIBRATION_REG3,
+					  CALIBRATION_REG3_DEFAULT);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+					  FORCE_CALIBRATION_OFF);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, FORCE_CALIBRATION,
+					  FORCE_CALIBRATION_ON);
+	if (error)
+		return error;
+
+	/* Clear all interrupts */
+	error = i2c_smbus_write_byte_data(client, INT_CLEAR, 0xff);
+	if (error)
+		return error;
+
+	/* Enable coordinates update interrupt */
+	error = i2c_smbus_write_byte_data(client, INT_MASK,
+					  CALIBRATION_DONE | SLEEP_OUT |
+					  SLEEP_IN | PROGRAM_LOAD_DONE);
+	if (error)
+		return error;
+
+	error = i2c_smbus_write_byte_data(client, ERR_MASK,
+					  PROGRAM_LOAD_ERR | CPU_TIMEOUT |
+					  ADC_TIMEOUT);
+	if (error)
+		return error;
+
+	/* controller CPU power on */
+	error = i2c_smbus_write_byte_data(client, SYSTEM,
+					  ANALOG_POWER_ON | CPU_POWER_ON);
+
+	enable_irq(client->irq);
+
+	return error;
+}
+
+static int rohm_ts_power_off(struct i2c_client *client)
+{
+	int error;
+
+	error = i2c_smbus_write_byte_data(client, SYSTEM,
+					  ANALOG_POWER_ON | CPU_POWER_OFF);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to power off device CPU: %d\n", error);
+		return error;
+	}
+
+	error = i2c_smbus_write_byte_data(client, SYSTEM,
+					  ANALOG_POWER_OFF | CPU_POWER_OFF);
+	if (error)
+		dev_err(&client->dev,
+			"failed to power off the device: %d\n", error);
+
+	return error;
+}
+
+static int rohm_ts_open(struct input_dev *input_dev)
+{
+	struct rohm_ts_data *ts = input_get_drvdata(input_dev);
+	struct i2c_client *client = ts->client;
+	int error;
+
+	if (!ts->initialized) {
+		error = rohm_ts_device_init(client, ts->setup2);
+		if (error) {
+			dev_err(&client->dev,
+				"device initialization failed: %d\n", error);
+			return error;
+		}
+
+		ts->initialized = true;
+	}
+
+	return 0;
+}
+
+static void rohm_ts_close(struct input_dev *input_dev)
+{
+	struct rohm_ts_data *ts = input_get_drvdata(input_dev);
+
+	rohm_ts_power_off(ts->client);
+
+	ts->initialized = false;
+}
+
+static void rohm_ts_remove_sysfs_group(void *_dev)
+{
+	struct device *dev = _dev;
+
+	sysfs_remove_group(&dev->kobj, &rohm_ts_attr_group);
+}
+
+static int rohm_bu21023_i2c_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct rohm_ts_data *ts;
+	struct input_dev *input;
+	int error;
+
+	if (!client->irq) {
+		dev_err(dev, "IRQ is not assigned\n");
+		return -EINVAL;
+	}
+
+	if (!client->adapter->algo->master_xfer) {
+		dev_err(dev, "I2C level transfers not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	/* Turn off CPU just in case */
+	error = rohm_ts_power_off(client);
+	if (error)
+		return error;
+
+	ts = devm_kzalloc(dev, sizeof(struct rohm_ts_data), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	ts->client = client;
+	ts->setup2 = MAF_1SAMPLE;
+	i2c_set_clientdata(client, ts);
+
+	input = devm_input_allocate_device(dev);
+	if (!input)
+		return -ENOMEM;
+
+	input->name = BU21023_NAME;
+	input->id.bustype = BUS_I2C;
+	input->open = rohm_ts_open;
+	input->close = rohm_ts_close;
+
+	ts->input = input;
+	input_set_drvdata(input, ts);
+
+	input_set_abs_params(input, ABS_MT_POSITION_X,
+			     ROHM_TS_ABS_X_MIN, ROHM_TS_ABS_X_MAX, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y,
+			     ROHM_TS_ABS_Y_MIN, ROHM_TS_ABS_Y_MAX, 0, 0);
+
+	error = input_mt_init_slots(input, MAX_CONTACTS,
+				    INPUT_MT_DIRECT | INPUT_MT_TRACK |
+				    INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(dev, "failed to multi touch slots initialization\n");
+		return error;
+	}
+
+	error = devm_request_threaded_irq(dev, client->irq,
+					  NULL, rohm_ts_soft_irq,
+					  IRQF_ONESHOT, client->name, ts);
+	if (error) {
+		dev_err(dev, "failed to request IRQ: %d\n", error);
+		return error;
+	}
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(dev, "failed to register input device: %d\n", error);
+		return error;
+	}
+
+	error = sysfs_create_group(&dev->kobj, &rohm_ts_attr_group);
+	if (error) {
+		dev_err(dev, "failed to create sysfs group: %d\n", error);
+		return error;
+	}
+
+	error = devm_add_action(dev, rohm_ts_remove_sysfs_group, dev);
+	if (error) {
+		rohm_ts_remove_sysfs_group(dev);
+		dev_err(&client->dev,
+			"Failed to add sysfs cleanup action: %d\n",
+			error);
+		return error;
+	}
+
+	return error;
+}
+
+static const struct i2c_device_id rohm_bu21023_i2c_id[] = {
+	{ BU21023_NAME, 0 },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, rohm_bu21023_i2c_id);
+
+static struct i2c_driver rohm_bu21023_i2c_driver = {
+	.driver = {
+		.name = BU21023_NAME,
+	},
+	.probe = rohm_bu21023_i2c_probe,
+	.id_table = rohm_bu21023_i2c_id,
+};
+module_i2c_driver(rohm_bu21023_i2c_driver);
+
+MODULE_DESCRIPTION("ROHM BU21023/24 Touchscreen driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("ROHM Co., Ltd.");
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
new file mode 100644
index 0000000..a4a103e
--- /dev/null
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -0,0 +1,439 @@
+/*
+ * Samsung S3C24XX touchscreen driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the term 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
+ * Copyright 2008 Ben Dooks <ben-linux@fluff.org>
+ * Copyright 2009 Simtec Electronics <linux@simtec.co.uk>
+ *
+ * Additional work by Herbert Pötzl <herbert@13thfloor.at> and
+ * Harald Welte <laforge@openmoko.org>
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <plat/adc.h>
+#include <plat/regs-adc.h>
+#include <linux/platform_data/touchscreen-s3c2410.h>
+
+#define TSC_SLEEP  (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))
+
+#define INT_DOWN	(0)
+#define INT_UP		(1 << 8)
+
+#define WAIT4INT	(S3C2410_ADCTSC_YM_SEN | \
+			 S3C2410_ADCTSC_YP_SEN | \
+			 S3C2410_ADCTSC_XP_SEN | \
+			 S3C2410_ADCTSC_XY_PST(3))
+
+#define AUTOPST		(S3C2410_ADCTSC_YM_SEN | \
+			 S3C2410_ADCTSC_YP_SEN | \
+			 S3C2410_ADCTSC_XP_SEN | \
+			 S3C2410_ADCTSC_AUTO_PST | \
+			 S3C2410_ADCTSC_XY_PST(0))
+
+#define FEAT_PEN_IRQ	(1 << 0)	/* HAS ADCCLRINTPNDNUP */
+
+/* Per-touchscreen data. */
+
+/**
+ * struct s3c2410ts - driver touchscreen state.
+ * @client: The ADC client we registered with the core driver.
+ * @dev: The device we are bound to.
+ * @input: The input device we registered with the input subsystem.
+ * @clock: The clock for the adc.
+ * @io: Pointer to the IO base.
+ * @xp: The accumulated X position data.
+ * @yp: The accumulated Y position data.
+ * @irq_tc: The interrupt number for pen up/down interrupt
+ * @count: The number of samples collected.
+ * @shift: The log2 of the maximum count to read in one go.
+ * @features: The features supported by the TSADC MOdule.
+ */
+struct s3c2410ts {
+	struct s3c_adc_client *client;
+	struct device *dev;
+	struct input_dev *input;
+	struct clk *clock;
+	void __iomem *io;
+	unsigned long xp;
+	unsigned long yp;
+	int irq_tc;
+	int count;
+	int shift;
+	int features;
+};
+
+static struct s3c2410ts ts;
+
+/**
+ * get_down - return the down state of the pen
+ * @data0: The data read from ADCDAT0 register.
+ * @data1: The data read from ADCDAT1 register.
+ *
+ * Return non-zero if both readings show that the pen is down.
+ */
+static inline bool get_down(unsigned long data0, unsigned long data1)
+{
+	/* returns true if both data values show stylus down */
+	return (!(data0 & S3C2410_ADCDAT0_UPDOWN) &&
+		!(data1 & S3C2410_ADCDAT0_UPDOWN));
+}
+
+static void touch_timer_fire(unsigned long data)
+{
+	unsigned long data0;
+	unsigned long data1;
+	bool down;
+
+	data0 = readl(ts.io + S3C2410_ADCDAT0);
+	data1 = readl(ts.io + S3C2410_ADCDAT1);
+
+	down = get_down(data0, data1);
+
+	if (down) {
+		if (ts.count == (1 << ts.shift)) {
+			ts.xp >>= ts.shift;
+			ts.yp >>= ts.shift;
+
+			dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n",
+				__func__, ts.xp, ts.yp, ts.count);
+
+			input_report_abs(ts.input, ABS_X, ts.xp);
+			input_report_abs(ts.input, ABS_Y, ts.yp);
+
+			input_report_key(ts.input, BTN_TOUCH, 1);
+			input_sync(ts.input);
+
+			ts.xp = 0;
+			ts.yp = 0;
+			ts.count = 0;
+		}
+
+		s3c_adc_start(ts.client, 0, 1 << ts.shift);
+	} else {
+		ts.xp = 0;
+		ts.yp = 0;
+		ts.count = 0;
+
+		input_report_key(ts.input, BTN_TOUCH, 0);
+		input_sync(ts.input);
+
+		writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+	}
+}
+
+static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);
+
+/**
+ * stylus_irq - touchscreen stylus event interrupt
+ * @irq: The interrupt number
+ * @dev_id: The device ID.
+ *
+ * Called when the IRQ_TC is fired for a pen up or down event.
+ */
+static irqreturn_t stylus_irq(int irq, void *dev_id)
+{
+	unsigned long data0;
+	unsigned long data1;
+	bool down;
+
+	data0 = readl(ts.io + S3C2410_ADCDAT0);
+	data1 = readl(ts.io + S3C2410_ADCDAT1);
+
+	down = get_down(data0, data1);
+
+	/* TODO we should never get an interrupt with down set while
+	 * the timer is running, but maybe we ought to verify that the
+	 * timer isn't running anyways. */
+
+	if (down)
+		s3c_adc_start(ts.client, 0, 1 << ts.shift);
+	else
+		dev_dbg(ts.dev, "%s: count=%d\n", __func__, ts.count);
+
+	if (ts.features & FEAT_PEN_IRQ) {
+		/* Clear pen down/up interrupt */
+		writel(0x0, ts.io + S3C64XX_ADCCLRINTPNDNUP);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * s3c24xx_ts_conversion - ADC conversion callback
+ * @client: The client that was registered with the ADC core.
+ * @data0: The reading from ADCDAT0.
+ * @data1: The reading from ADCDAT1.
+ * @left: The number of samples left.
+ *
+ * Called when a conversion has finished.
+ */
+static void s3c24xx_ts_conversion(struct s3c_adc_client *client,
+				  unsigned data0, unsigned data1,
+				  unsigned *left)
+{
+	dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1);
+
+	ts.xp += data0;
+	ts.yp += data1;
+
+	ts.count++;
+
+	/* From tests, it seems that it is unlikely to get a pen-up
+	 * event during the conversion process which means we can
+	 * ignore any pen-up events with less than the requisite
+	 * count done.
+	 *
+	 * In several thousand conversions, no pen-ups where detected
+	 * before count completed.
+	 */
+}
+
+/**
+ * s3c24xx_ts_select - ADC selection callback.
+ * @client: The client that was registered with the ADC core.
+ * @select: The reason for select.
+ *
+ * Called when the ADC core selects (or deslects) us as a client.
+ */
+static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select)
+{
+	if (select) {
+		writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
+		       ts.io + S3C2410_ADCTSC);
+	} else {
+		mod_timer(&touch_timer, jiffies+1);
+		writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC);
+	}
+}
+
+/**
+ * s3c2410ts_probe - device core probe entry point
+ * @pdev: The device we are being bound to.
+ *
+ * Initialise, find and allocate any resources we need to run and then
+ * register with the ADC and input systems.
+ */
+static int s3c2410ts_probe(struct platform_device *pdev)
+{
+	struct s3c2410_ts_mach_info *info;
+	struct device *dev = &pdev->dev;
+	struct input_dev *input_dev;
+	struct resource *res;
+	int ret = -EINVAL;
+
+	/* Initialise input stuff */
+	memset(&ts, 0, sizeof(struct s3c2410ts));
+
+	ts.dev = dev;
+
+	info = dev_get_platdata(&pdev->dev);
+	if (!info) {
+		dev_err(dev, "no platform data, cannot attach\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "initialising touchscreen\n");
+
+	ts.clock = clk_get(dev, "adc");
+	if (IS_ERR(ts.clock)) {
+		dev_err(dev, "cannot get adc clock source\n");
+		return -ENOENT;
+	}
+
+	clk_prepare_enable(ts.clock);
+	dev_dbg(dev, "got and enabled clocks\n");
+
+	ts.irq_tc = ret = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(dev, "no resource for interrupt\n");
+		goto err_clk;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "no resource for registers\n");
+		ret = -ENOENT;
+		goto err_clk;
+	}
+
+	ts.io = ioremap(res->start, resource_size(res));
+	if (ts.io == NULL) {
+		dev_err(dev, "cannot map registers\n");
+		ret = -ENOMEM;
+		goto err_clk;
+	}
+
+	/* inititalise the gpio */
+	if (info->cfg_gpio)
+		info->cfg_gpio(to_platform_device(ts.dev));
+
+	ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,
+				     s3c24xx_ts_conversion, 1);
+	if (IS_ERR(ts.client)) {
+		dev_err(dev, "failed to register adc client\n");
+		ret = PTR_ERR(ts.client);
+		goto err_iomap;
+	}
+
+	/* Initialise registers */
+	if ((info->delay & 0xffff) > 0)
+		writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
+
+	writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(dev, "Unable to allocate the input device !!\n");
+		ret = -ENOMEM;
+		goto err_iomap;
+	}
+
+	ts.input = input_dev;
+	ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
+	input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);
+
+	ts.input->name = "S3C24XX TouchScreen";
+	ts.input->id.bustype = BUS_HOST;
+	ts.input->id.vendor = 0xDEAD;
+	ts.input->id.product = 0xBEEF;
+	ts.input->id.version = 0x0102;
+
+	ts.shift = info->oversampling_shift;
+	ts.features = platform_get_device_id(pdev)->driver_data;
+
+	ret = request_irq(ts.irq_tc, stylus_irq, 0,
+			  "s3c2410_ts_pen", ts.input);
+	if (ret) {
+		dev_err(dev, "cannot get TC interrupt\n");
+		goto err_inputdev;
+	}
+
+	dev_info(dev, "driver attached, registering input device\n");
+
+	/* All went ok, so register to the input system */
+	ret = input_register_device(ts.input);
+	if (ret < 0) {
+		dev_err(dev, "failed to register input device\n");
+		ret = -EIO;
+		goto err_tcirq;
+	}
+
+	return 0;
+
+ err_tcirq:
+	free_irq(ts.irq_tc, ts.input);
+ err_inputdev:
+	input_free_device(ts.input);
+ err_iomap:
+	iounmap(ts.io);
+ err_clk:
+	del_timer_sync(&touch_timer);
+	clk_put(ts.clock);
+	return ret;
+}
+
+/**
+ * s3c2410ts_remove - device core removal entry point
+ * @pdev: The device we are being removed from.
+ *
+ * Free up our state ready to be removed.
+ */
+static int s3c2410ts_remove(struct platform_device *pdev)
+{
+	free_irq(ts.irq_tc, ts.input);
+	del_timer_sync(&touch_timer);
+
+	clk_disable_unprepare(ts.clock);
+	clk_put(ts.clock);
+
+	input_unregister_device(ts.input);
+	iounmap(ts.io);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c2410ts_suspend(struct device *dev)
+{
+	writel(TSC_SLEEP, ts.io + S3C2410_ADCTSC);
+	disable_irq(ts.irq_tc);
+	clk_disable(ts.clock);
+
+	return 0;
+}
+
+static int s3c2410ts_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct s3c2410_ts_mach_info *info = dev_get_platdata(&pdev->dev);
+
+	clk_enable(ts.clock);
+	enable_irq(ts.irq_tc);
+
+	/* Initialise registers */
+	if ((info->delay & 0xffff) > 0)
+		writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
+
+	writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+
+	return 0;
+}
+
+static const struct dev_pm_ops s3c_ts_pmops = {
+	.suspend	= s3c2410ts_suspend,
+	.resume		= s3c2410ts_resume,
+};
+#endif
+
+static const struct platform_device_id s3cts_driver_ids[] = {
+	{ "s3c2410-ts", 0 },
+	{ "s3c2440-ts", 0 },
+	{ "s3c64xx-ts", FEAT_PEN_IRQ },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, s3cts_driver_ids);
+
+static struct platform_driver s3c_ts_driver = {
+	.driver         = {
+		.name   = "samsung-ts",
+#ifdef CONFIG_PM
+		.pm	= &s3c_ts_pmops,
+#endif
+	},
+	.id_table	= s3cts_driver_ids,
+	.probe		= s3c2410ts_probe,
+	.remove		= s3c2410ts_remove,
+};
+module_platform_driver(s3c_ts_driver);
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, "
+	      "Ben Dooks <ben@simtec.co.uk>, "
+	      "Simtec Electronics <linux@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C24XX Touchscreen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c
new file mode 100644
index 0000000..e943678
--- /dev/null
+++ b/drivers/input/touchscreen/st1232.c
@@ -0,0 +1,308 @@
+/*
+ * ST1232 Touchscreen Controller Driver
+ *
+ * Copyright (C) 2010 Renesas Solutions Corp.
+ *	Tony SIM <chinyeow.sim.xt@renesas.com>
+ *
+ * Using code from:
+ *  - android.git.kernel.org: projects/kernel/common.git: synaptics_i2c_rmi.c
+ *	Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_qos.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/platform_data/st1232_pdata.h>
+
+#define ST1232_TS_NAME	"st1232-ts"
+
+#define MIN_X		0x00
+#define MIN_Y		0x00
+#define MAX_X		0x31f	/* (800 - 1) */
+#define MAX_Y		0x1df	/* (480 - 1) */
+#define MAX_AREA	0xff
+#define MAX_FINGERS	2
+
+struct st1232_ts_finger {
+	u16 x;
+	u16 y;
+	u8 t;
+	bool is_valid;
+};
+
+struct st1232_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	struct st1232_ts_finger finger[MAX_FINGERS];
+	struct dev_pm_qos_request low_latency_req;
+	int reset_gpio;
+};
+
+static int st1232_ts_read_data(struct st1232_ts_data *ts)
+{
+	struct st1232_ts_finger *finger = ts->finger;
+	struct i2c_client *client = ts->client;
+	struct i2c_msg msg[2];
+	int error;
+	u8 start_reg;
+	u8 buf[10];
+
+	/* read touchscreen data from ST1232 */
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = &start_reg;
+	start_reg = 0x10;
+
+	msg[1].addr = ts->client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = sizeof(buf);
+	msg[1].buf = buf;
+
+	error = i2c_transfer(client->adapter, msg, 2);
+	if (error < 0)
+		return error;
+
+	/* get "valid" bits */
+	finger[0].is_valid = buf[2] >> 7;
+	finger[1].is_valid = buf[5] >> 7;
+
+	/* get xy coordinate */
+	if (finger[0].is_valid) {
+		finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3];
+		finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4];
+		finger[0].t = buf[8];
+	}
+
+	if (finger[1].is_valid) {
+		finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6];
+		finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7];
+		finger[1].t = buf[9];
+	}
+
+	return 0;
+}
+
+static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
+{
+	struct st1232_ts_data *ts = dev_id;
+	struct st1232_ts_finger *finger = ts->finger;
+	struct input_dev *input_dev = ts->input_dev;
+	int count = 0;
+	int i, ret;
+
+	ret = st1232_ts_read_data(ts);
+	if (ret < 0)
+		goto end;
+
+	/* multi touch protocol */
+	for (i = 0; i < MAX_FINGERS; i++) {
+		if (!finger[i].is_valid)
+			continue;
+
+		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);
+		input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
+		input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
+		input_mt_sync(input_dev);
+		count++;
+	}
+
+	/* SYN_MT_REPORT only if no contact */
+	if (!count) {
+		input_mt_sync(input_dev);
+		if (ts->low_latency_req.dev) {
+			dev_pm_qos_remove_request(&ts->low_latency_req);
+			ts->low_latency_req.dev = NULL;
+		}
+	} else if (!ts->low_latency_req.dev) {
+		/* First contact, request 100 us latency. */
+		dev_pm_qos_add_ancestor_request(&ts->client->dev,
+						&ts->low_latency_req,
+						DEV_PM_QOS_RESUME_LATENCY, 100);
+	}
+
+	/* SYN_REPORT */
+	input_sync(input_dev);
+
+end:
+	return IRQ_HANDLED;
+}
+
+static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron)
+{
+	if (gpio_is_valid(ts->reset_gpio))
+		gpio_direction_output(ts->reset_gpio, poweron);
+}
+
+static int st1232_ts_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	struct st1232_ts_data *ts;
+	struct st1232_pdata *pdata = dev_get_platdata(&client->dev);
+	struct input_dev *input_dev;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "need I2C_FUNC_I2C\n");
+		return -EIO;
+	}
+
+	if (!client->irq) {
+		dev_err(&client->dev, "no IRQ?\n");
+		return -EINVAL;
+	}
+
+	ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	input_dev = devm_input_allocate_device(&client->dev);
+	if (!input_dev)
+		return -ENOMEM;
+
+	ts->client = client;
+	ts->input_dev = input_dev;
+
+	if (pdata)
+		ts->reset_gpio = pdata->reset_gpio;
+	else if (client->dev.of_node)
+		ts->reset_gpio = of_get_gpio(client->dev.of_node, 0);
+	else
+		ts->reset_gpio = -ENODEV;
+
+	if (gpio_is_valid(ts->reset_gpio)) {
+		error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL);
+		if (error) {
+			dev_err(&client->dev,
+				"Unable to request GPIO pin %d.\n",
+				ts->reset_gpio);
+				return error;
+		}
+	}
+
+	st1232_ts_power(ts, true);
+
+	input_dev->name = "st1232-touchscreen";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &client->dev;
+
+	__set_bit(EV_SYN, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(EV_ABS, input_dev->evbit);
+
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0);
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, st1232_ts_irq_handler,
+					  IRQF_ONESHOT,
+					  client->name, ts);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		return error;
+	}
+
+	error = input_register_device(ts->input_dev);
+	if (error) {
+		dev_err(&client->dev, "Unable to register %s input device\n",
+			input_dev->name);
+		return error;
+	}
+
+	i2c_set_clientdata(client, ts);
+	device_init_wakeup(&client->dev, 1);
+
+	return 0;
+}
+
+static int st1232_ts_remove(struct i2c_client *client)
+{
+	struct st1232_ts_data *ts = i2c_get_clientdata(client);
+
+	device_init_wakeup(&client->dev, 0);
+	st1232_ts_power(ts, false);
+
+	return 0;
+}
+
+static int __maybe_unused st1232_ts_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct st1232_ts_data *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(&client->dev)) {
+		enable_irq_wake(client->irq);
+	} else {
+		disable_irq(client->irq);
+		st1232_ts_power(ts, false);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused st1232_ts_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct st1232_ts_data *ts = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(&client->dev)) {
+		disable_irq_wake(client->irq);
+	} else {
+		st1232_ts_power(ts, true);
+		enable_irq(client->irq);
+	}
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops,
+			 st1232_ts_suspend, st1232_ts_resume);
+
+static const struct i2c_device_id st1232_ts_id[] = {
+	{ ST1232_TS_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, st1232_ts_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id st1232_ts_dt_ids[] = {
+	{ .compatible = "sitronix,st1232", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids);
+#endif
+
+static struct i2c_driver st1232_ts_driver = {
+	.probe		= st1232_ts_probe,
+	.remove		= st1232_ts_remove,
+	.id_table	= st1232_ts_id,
+	.driver = {
+		.name	= ST1232_TS_NAME,
+		.of_match_table = of_match_ptr(st1232_ts_dt_ids),
+		.pm	= &st1232_ts_pm_ops,
+	},
+};
+
+module_i2c_driver(st1232_ts_driver);
+
+MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>");
+MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c
new file mode 100644
index 0000000..e414d43
--- /dev/null
+++ b/drivers/input/touchscreen/stmpe-ts.c
@@ -0,0 +1,385 @@
+/*
+ * STMicroelectronics STMPE811 Touchscreen Driver
+ *
+ * (C) 2010 Luotao Fu <l.fu@pengutronix.de>
+ * All rights reserved.
+ *
+ *  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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/stmpe.h>
+
+/* Register layouts and functionalities are identical on all stmpexxx variants
+ * with touchscreen controller
+ */
+#define STMPE_REG_INT_STA		0x0B
+#define STMPE_REG_ADC_CTRL1		0x20
+#define STMPE_REG_ADC_CTRL2		0x21
+#define STMPE_REG_TSC_CTRL		0x40
+#define STMPE_REG_TSC_CFG		0x41
+#define STMPE_REG_FIFO_TH		0x4A
+#define STMPE_REG_FIFO_STA		0x4B
+#define STMPE_REG_FIFO_SIZE		0x4C
+#define STMPE_REG_TSC_DATA_XYZ		0x52
+#define STMPE_REG_TSC_FRACTION_Z	0x56
+#define STMPE_REG_TSC_I_DRIVE		0x58
+
+#define OP_MOD_XYZ			0
+
+#define STMPE_TSC_CTRL_TSC_EN		(1<<0)
+
+#define STMPE_FIFO_STA_RESET		(1<<0)
+
+#define STMPE_IRQ_TOUCH_DET		0
+
+#define SAMPLE_TIME(x)			((x & 0xf) << 4)
+#define MOD_12B(x)			((x & 0x1) << 3)
+#define REF_SEL(x)			((x & 0x1) << 1)
+#define ADC_FREQ(x)			(x & 0x3)
+#define AVE_CTRL(x)			((x & 0x3) << 6)
+#define DET_DELAY(x)			((x & 0x7) << 3)
+#define SETTLING(x)			(x & 0x7)
+#define FRACTION_Z(x)			(x & 0x7)
+#define I_DRIVE(x)			(x & 0x1)
+#define OP_MODE(x)			((x & 0x7) << 1)
+
+#define STMPE_TS_NAME			"stmpe-ts"
+#define XY_MASK				0xfff
+
+struct stmpe_touch {
+	struct stmpe *stmpe;
+	struct input_dev *idev;
+	struct delayed_work work;
+	struct device *dev;
+	u8 sample_time;
+	u8 mod_12b;
+	u8 ref_sel;
+	u8 adc_freq;
+	u8 ave_ctrl;
+	u8 touch_det_delay;
+	u8 settling;
+	u8 fraction_z;
+	u8 i_drive;
+};
+
+static int __stmpe_reset_fifo(struct stmpe *stmpe)
+{
+	int ret;
+
+	ret = stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA,
+			STMPE_FIFO_STA_RESET, STMPE_FIFO_STA_RESET);
+	if (ret)
+		return ret;
+
+	return stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA,
+			STMPE_FIFO_STA_RESET, 0);
+}
+
+static void stmpe_work(struct work_struct *work)
+{
+	int int_sta;
+	u32 timeout = 40;
+
+	struct stmpe_touch *ts =
+	    container_of(work, struct stmpe_touch, work.work);
+
+	int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA);
+
+	/*
+	 * touch_det sometimes get desasserted or just get stuck. This appears
+	 * to be a silicon bug, We still have to clearify this with the
+	 * manufacture. As a workaround We release the key anyway if the
+	 * touch_det keeps coming in after 4ms, while the FIFO contains no value
+	 * during the whole time.
+	 */
+	while ((int_sta & (1 << STMPE_IRQ_TOUCH_DET)) && (timeout > 0)) {
+		timeout--;
+		int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA);
+		udelay(100);
+	}
+
+	/* reset the FIFO before we report release event */
+	__stmpe_reset_fifo(ts->stmpe);
+
+	input_report_abs(ts->idev, ABS_PRESSURE, 0);
+	input_report_key(ts->idev, BTN_TOUCH, 0);
+	input_sync(ts->idev);
+}
+
+static irqreturn_t stmpe_ts_handler(int irq, void *data)
+{
+	u8 data_set[4];
+	int x, y, z;
+	struct stmpe_touch *ts = data;
+
+	/*
+	 * Cancel scheduled polling for release if we have new value
+	 * available. Wait if the polling is already running.
+	 */
+	cancel_delayed_work_sync(&ts->work);
+
+	/*
+	 * The FIFO sometimes just crashes and stops generating interrupts. This
+	 * appears to be a silicon bug. We still have to clearify this with
+	 * the manufacture. As a workaround we disable the TSC while we are
+	 * collecting data and flush the FIFO after reading
+	 */
+	stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+				STMPE_TSC_CTRL_TSC_EN, 0);
+
+	stmpe_block_read(ts->stmpe, STMPE_REG_TSC_DATA_XYZ, 4, data_set);
+
+	x = (data_set[0] << 4) | (data_set[1] >> 4);
+	y = ((data_set[1] & 0xf) << 8) | data_set[2];
+	z = data_set[3];
+
+	input_report_abs(ts->idev, ABS_X, x);
+	input_report_abs(ts->idev, ABS_Y, y);
+	input_report_abs(ts->idev, ABS_PRESSURE, z);
+	input_report_key(ts->idev, BTN_TOUCH, 1);
+	input_sync(ts->idev);
+
+       /* flush the FIFO after we have read out our values. */
+	__stmpe_reset_fifo(ts->stmpe);
+
+	/* reenable the tsc */
+	stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+			STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
+
+	/* start polling for touch_det to detect release */
+	schedule_delayed_work(&ts->work, msecs_to_jiffies(50));
+
+	return IRQ_HANDLED;
+}
+
+static int stmpe_init_hw(struct stmpe_touch *ts)
+{
+	int ret;
+	u8 adc_ctrl1, adc_ctrl1_mask, tsc_cfg, tsc_cfg_mask;
+	struct stmpe *stmpe = ts->stmpe;
+	struct device *dev = ts->dev;
+
+	ret = stmpe_enable(stmpe, STMPE_BLOCK_TOUCHSCREEN | STMPE_BLOCK_ADC);
+	if (ret) {
+		dev_err(dev, "Could not enable clock for ADC and TS\n");
+		return ret;
+	}
+
+	adc_ctrl1 = SAMPLE_TIME(ts->sample_time) | MOD_12B(ts->mod_12b) |
+		REF_SEL(ts->ref_sel);
+	adc_ctrl1_mask = SAMPLE_TIME(0xff) | MOD_12B(0xff) | REF_SEL(0xff);
+
+	ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL1,
+			adc_ctrl1_mask, adc_ctrl1);
+	if (ret) {
+		dev_err(dev, "Could not setup ADC\n");
+		return ret;
+	}
+
+	ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL2,
+			ADC_FREQ(0xff), ADC_FREQ(ts->adc_freq));
+	if (ret) {
+		dev_err(dev, "Could not setup ADC\n");
+		return ret;
+	}
+
+	tsc_cfg = AVE_CTRL(ts->ave_ctrl) | DET_DELAY(ts->touch_det_delay) |
+			SETTLING(ts->settling);
+	tsc_cfg_mask = AVE_CTRL(0xff) | DET_DELAY(0xff) | SETTLING(0xff);
+
+	ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CFG, tsc_cfg_mask, tsc_cfg);
+	if (ret) {
+		dev_err(dev, "Could not config touch\n");
+		return ret;
+	}
+
+	ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_FRACTION_Z,
+			FRACTION_Z(0xff), FRACTION_Z(ts->fraction_z));
+	if (ret) {
+		dev_err(dev, "Could not config touch\n");
+		return ret;
+	}
+
+	ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_I_DRIVE,
+			I_DRIVE(0xff), I_DRIVE(ts->i_drive));
+	if (ret) {
+		dev_err(dev, "Could not config touch\n");
+		return ret;
+	}
+
+	/* set FIFO to 1 for single point reading */
+	ret = stmpe_reg_write(stmpe, STMPE_REG_FIFO_TH, 1);
+	if (ret) {
+		dev_err(dev, "Could not set FIFO\n");
+		return ret;
+	}
+
+	ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CTRL,
+			OP_MODE(0xff), OP_MODE(OP_MOD_XYZ));
+	if (ret) {
+		dev_err(dev, "Could not set mode\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int stmpe_ts_open(struct input_dev *dev)
+{
+	struct stmpe_touch *ts = input_get_drvdata(dev);
+	int ret = 0;
+
+	ret = __stmpe_reset_fifo(ts->stmpe);
+	if (ret)
+		return ret;
+
+	return stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+			STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
+}
+
+static void stmpe_ts_close(struct input_dev *dev)
+{
+	struct stmpe_touch *ts = input_get_drvdata(dev);
+
+	cancel_delayed_work_sync(&ts->work);
+
+	stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+			STMPE_TSC_CTRL_TSC_EN, 0);
+}
+
+static void stmpe_ts_get_platform_info(struct platform_device *pdev,
+					struct stmpe_touch *ts)
+{
+	struct device_node *np = pdev->dev.of_node;
+	u32 val;
+
+	if (np) {
+		if (!of_property_read_u32(np, "st,sample-time", &val))
+			ts->sample_time = val;
+		if (!of_property_read_u32(np, "st,mod-12b", &val))
+			ts->mod_12b = val;
+		if (!of_property_read_u32(np, "st,ref-sel", &val))
+			ts->ref_sel = val;
+		if (!of_property_read_u32(np, "st,adc-freq", &val))
+			ts->adc_freq = val;
+		if (!of_property_read_u32(np, "st,ave-ctrl", &val))
+			ts->ave_ctrl = val;
+		if (!of_property_read_u32(np, "st,touch-det-delay", &val))
+			ts->touch_det_delay = val;
+		if (!of_property_read_u32(np, "st,settling", &val))
+			ts->settling = val;
+		if (!of_property_read_u32(np, "st,fraction-z", &val))
+			ts->fraction_z = val;
+		if (!of_property_read_u32(np, "st,i-drive", &val))
+			ts->i_drive = val;
+	}
+}
+
+static int stmpe_input_probe(struct platform_device *pdev)
+{
+	struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+	struct stmpe_touch *ts;
+	struct input_dev *idev;
+	int error;
+	int ts_irq;
+
+	ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");
+	if (ts_irq < 0)
+		return ts_irq;
+
+	ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	idev = devm_input_allocate_device(&pdev->dev);
+	if (!idev)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, ts);
+	ts->stmpe = stmpe;
+	ts->idev = idev;
+	ts->dev = &pdev->dev;
+
+	stmpe_ts_get_platform_info(pdev, ts);
+
+	INIT_DELAYED_WORK(&ts->work, stmpe_work);
+
+	error = devm_request_threaded_irq(&pdev->dev, ts_irq,
+					  NULL, stmpe_ts_handler,
+					  IRQF_ONESHOT, STMPE_TS_NAME, ts);
+	if (error) {
+		dev_err(&pdev->dev, "Failed to request IRQ %d\n", ts_irq);
+		return error;
+	}
+
+	error = stmpe_init_hw(ts);
+	if (error)
+		return error;
+
+	idev->name = STMPE_TS_NAME;
+	idev->phys = STMPE_TS_NAME"/input0";
+	idev->id.bustype = BUS_I2C;
+
+	idev->open = stmpe_ts_open;
+	idev->close = stmpe_ts_close;
+
+	input_set_drvdata(idev, ts);
+
+	input_set_capability(idev, EV_KEY, BTN_TOUCH);
+	input_set_abs_params(idev, ABS_X, 0, XY_MASK, 0, 0);
+	input_set_abs_params(idev, ABS_Y, 0, XY_MASK, 0, 0);
+	input_set_abs_params(idev, ABS_PRESSURE, 0x0, 0xff, 0, 0);
+
+	error = input_register_device(idev);
+	if (error) {
+		dev_err(&pdev->dev, "Could not register input device\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static int stmpe_ts_remove(struct platform_device *pdev)
+{
+	struct stmpe_touch *ts = platform_get_drvdata(pdev);
+
+	stmpe_disable(ts->stmpe, STMPE_BLOCK_TOUCHSCREEN);
+
+	return 0;
+}
+
+static struct platform_driver stmpe_ts_driver = {
+	.driver = {
+		.name = STMPE_TS_NAME,
+	},
+	.probe = stmpe_input_probe,
+	.remove = stmpe_ts_remove,
+};
+module_platform_driver(stmpe_ts_driver);
+
+static const struct of_device_id stmpe_ts_ids[] = {
+	{ .compatible = "st,stmpe-ts", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, stmpe_ts_ids);
+
+MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
+MODULE_DESCRIPTION("STMPEXXX touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c
new file mode 100644
index 0000000..4857943
--- /dev/null
+++ b/drivers/input/touchscreen/sun4i-ts.c
@@ -0,0 +1,426 @@
+/*
+ * Allwinner sunxi resistive touchscreen controller driver
+ *
+ * Copyright (C) 2013 - 2014 Hans de Goede <hdegoede@redhat.com>
+ *
+ * The hwmon parts are based on work by Corentin LABBE which is:
+ * Copyright (C) 2013 Corentin LABBE <clabbe.montjoie@gmail.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * The sun4i-ts controller is capable of detecting a second touch, but when a
+ * second touch is present then the accuracy becomes so bad the reported touch
+ * location is not useable.
+ *
+ * The original android driver contains some complicated heuristics using the
+ * aprox. distance between the 2 touches to see if the user is making a pinch
+ * open / close movement, and then reports emulated multi-touch events around
+ * the last touch coordinate (as the dual-touch coordinates are worthless).
+ *
+ * These kinds of heuristics are just asking for trouble (and don't belong
+ * in the kernel). So this driver offers straight forward, reliable single
+ * touch functionality only.
+ *
+ * s.a. A20 User Manual "1.15 TP" (Documentation/arm/sunxi/README)
+ * (looks like the description in the A20 User Manual v1.3 is better
+ * than the one in the A10 User Manual v.1.5)
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/thermal.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define TP_CTRL0		0x00
+#define TP_CTRL1		0x04
+#define TP_CTRL2		0x08
+#define TP_CTRL3		0x0c
+#define TP_INT_FIFOC		0x10
+#define TP_INT_FIFOS		0x14
+#define TP_TPR			0x18
+#define TP_CDAT			0x1c
+#define TEMP_DATA		0x20
+#define TP_DATA			0x24
+
+/* TP_CTRL0 bits */
+#define ADC_FIRST_DLY(x)	((x) << 24) /* 8 bits */
+#define ADC_FIRST_DLY_MODE(x)	((x) << 23)
+#define ADC_CLK_SEL(x)		((x) << 22)
+#define ADC_CLK_DIV(x)		((x) << 20) /* 3 bits */
+#define FS_DIV(x)		((x) << 16) /* 4 bits */
+#define T_ACQ(x)		((x) << 0) /* 16 bits */
+
+/* TP_CTRL1 bits */
+#define STYLUS_UP_DEBOUN(x)	((x) << 12) /* 8 bits */
+#define STYLUS_UP_DEBOUN_EN(x)	((x) << 9)
+#define TOUCH_PAN_CALI_EN(x)	((x) << 6)
+#define TP_DUAL_EN(x)		((x) << 5)
+#define TP_MODE_EN(x)		((x) << 4)
+#define TP_ADC_SELECT(x)	((x) << 3)
+#define ADC_CHAN_SELECT(x)	((x) << 0)  /* 3 bits */
+
+/* on sun6i, bits 3~6 are left shifted by 1 to 4~7 */
+#define SUN6I_TP_MODE_EN(x)	((x) << 5)
+
+/* TP_CTRL2 bits */
+#define TP_SENSITIVE_ADJUST(x)	((x) << 28) /* 4 bits */
+#define TP_MODE_SELECT(x)	((x) << 26) /* 2 bits */
+#define PRE_MEA_EN(x)		((x) << 24)
+#define PRE_MEA_THRE_CNT(x)	((x) << 0) /* 24 bits */
+
+/* TP_CTRL3 bits */
+#define FILTER_EN(x)		((x) << 2)
+#define FILTER_TYPE(x)		((x) << 0)  /* 2 bits */
+
+/* TP_INT_FIFOC irq and fifo mask / control bits */
+#define TEMP_IRQ_EN(x)		((x) << 18)
+#define OVERRUN_IRQ_EN(x)	((x) << 17)
+#define DATA_IRQ_EN(x)		((x) << 16)
+#define TP_DATA_XY_CHANGE(x)	((x) << 13)
+#define FIFO_TRIG(x)		((x) << 8)  /* 5 bits */
+#define DATA_DRQ_EN(x)		((x) << 7)
+#define FIFO_FLUSH(x)		((x) << 4)
+#define TP_UP_IRQ_EN(x)		((x) << 1)
+#define TP_DOWN_IRQ_EN(x)	((x) << 0)
+
+/* TP_INT_FIFOS irq and fifo status bits */
+#define TEMP_DATA_PENDING	BIT(18)
+#define FIFO_OVERRUN_PENDING	BIT(17)
+#define FIFO_DATA_PENDING	BIT(16)
+#define TP_IDLE_FLG		BIT(2)
+#define TP_UP_PENDING		BIT(1)
+#define TP_DOWN_PENDING		BIT(0)
+
+/* TP_TPR bits */
+#define TEMP_ENABLE(x)		((x) << 16)
+#define TEMP_PERIOD(x)		((x) << 0)  /* t = x * 256 * 16 / clkin */
+
+struct sun4i_ts_data {
+	struct device *dev;
+	struct input_dev *input;
+	struct thermal_zone_device *tz;
+	void __iomem *base;
+	unsigned int irq;
+	bool ignore_fifo_data;
+	int temp_data;
+	int temp_offset;
+	int temp_step;
+};
+
+static void sun4i_ts_irq_handle_input(struct sun4i_ts_data *ts, u32 reg_val)
+{
+	u32 x, y;
+
+	if (reg_val & FIFO_DATA_PENDING) {
+		x = readl(ts->base + TP_DATA);
+		y = readl(ts->base + TP_DATA);
+		/* The 1st location reported after an up event is unreliable */
+		if (!ts->ignore_fifo_data) {
+			input_report_abs(ts->input, ABS_X, x);
+			input_report_abs(ts->input, ABS_Y, y);
+			/*
+			 * The hardware has a separate down status bit, but
+			 * that gets set before we get the first location,
+			 * resulting in reporting a click on the old location.
+			 */
+			input_report_key(ts->input, BTN_TOUCH, 1);
+			input_sync(ts->input);
+		} else {
+			ts->ignore_fifo_data = false;
+		}
+	}
+
+	if (reg_val & TP_UP_PENDING) {
+		ts->ignore_fifo_data = true;
+		input_report_key(ts->input, BTN_TOUCH, 0);
+		input_sync(ts->input);
+	}
+}
+
+static irqreturn_t sun4i_ts_irq(int irq, void *dev_id)
+{
+	struct sun4i_ts_data *ts = dev_id;
+	u32 reg_val;
+
+	reg_val  = readl(ts->base + TP_INT_FIFOS);
+
+	if (reg_val & TEMP_DATA_PENDING)
+		ts->temp_data = readl(ts->base + TEMP_DATA);
+
+	if (ts->input)
+		sun4i_ts_irq_handle_input(ts, reg_val);
+
+	writel(reg_val, ts->base + TP_INT_FIFOS);
+
+	return IRQ_HANDLED;
+}
+
+static int sun4i_ts_open(struct input_dev *dev)
+{
+	struct sun4i_ts_data *ts = input_get_drvdata(dev);
+
+	/* Flush, set trig level to 1, enable temp, data and up irqs */
+	writel(TEMP_IRQ_EN(1) | DATA_IRQ_EN(1) | FIFO_TRIG(1) | FIFO_FLUSH(1) |
+		TP_UP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
+
+	return 0;
+}
+
+static void sun4i_ts_close(struct input_dev *dev)
+{
+	struct sun4i_ts_data *ts = input_get_drvdata(dev);
+
+	/* Deactivate all input IRQs */
+	writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
+}
+
+static int sun4i_get_temp(const struct sun4i_ts_data *ts, int *temp)
+{
+	/* No temp_data until the first irq */
+	if (ts->temp_data == -1)
+		return -EAGAIN;
+
+	*temp = ts->temp_data * ts->temp_step - ts->temp_offset;
+
+	return 0;
+}
+
+static int sun4i_get_tz_temp(void *data, int *temp)
+{
+	return sun4i_get_temp(data, temp);
+}
+
+static struct thermal_zone_of_device_ops sun4i_ts_tz_ops = {
+	.get_temp = sun4i_get_tz_temp,
+};
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
+			 char *buf)
+{
+	struct sun4i_ts_data *ts = dev_get_drvdata(dev);
+	int temp;
+	int error;
+
+	error = sun4i_get_temp(ts, &temp);
+	if (error)
+		return error;
+
+	return sprintf(buf, "%d\n", temp);
+}
+
+static ssize_t show_temp_label(struct device *dev,
+			      struct device_attribute *devattr, char *buf)
+{
+	return sprintf(buf, "SoC temperature\n");
+}
+
+static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
+static DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL);
+
+static struct attribute *sun4i_ts_attrs[] = {
+	&dev_attr_temp1_input.attr,
+	&dev_attr_temp1_label.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(sun4i_ts);
+
+static int sun4i_ts_probe(struct platform_device *pdev)
+{
+	struct sun4i_ts_data *ts;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct device *hwmon;
+	int error;
+	u32 reg;
+	bool ts_attached;
+	u32 tp_sensitive_adjust = 15;
+	u32 filter_type = 1;
+
+	ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	ts->dev = dev;
+	ts->ignore_fifo_data = true;
+	ts->temp_data = -1;
+	if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) {
+		/* Allwinner SDK has temperature (C) = (value / 6) - 271 */
+		ts->temp_offset = 271000;
+		ts->temp_step = 167;
+	} else if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) {
+		/*
+		 * The A10 temperature sensor has quite a wide spread, these
+		 * parameters are based on the averaging of the calibration
+		 * results of 4 completely different boards, with a spread of
+		 * temp_step from 0.096 - 0.170 and temp_offset from 176 - 331.
+		 */
+		ts->temp_offset = 257000;
+		ts->temp_step = 133;
+	} else {
+		/*
+		 * The user manuals do not contain the formula for calculating
+		 * the temperature. The formula used here is from the AXP209,
+		 * which is designed by X-Powers, an affiliate of Allwinner:
+		 *
+		 *     temperature (C) = (value * 0.1) - 144.7
+		 *
+		 * Allwinner does not have any documentation whatsoever for
+		 * this hardware. Moreover, it is claimed that the sensor
+		 * is inaccurate and cannot work properly.
+		 */
+		ts->temp_offset = 144700;
+		ts->temp_step = 100;
+	}
+
+	ts_attached = of_property_read_bool(np, "allwinner,ts-attached");
+	if (ts_attached) {
+		ts->input = devm_input_allocate_device(dev);
+		if (!ts->input)
+			return -ENOMEM;
+
+		ts->input->name = pdev->name;
+		ts->input->phys = "sun4i_ts/input0";
+		ts->input->open = sun4i_ts_open;
+		ts->input->close = sun4i_ts_close;
+		ts->input->id.bustype = BUS_HOST;
+		ts->input->id.vendor = 0x0001;
+		ts->input->id.product = 0x0001;
+		ts->input->id.version = 0x0100;
+		ts->input->evbit[0] =  BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
+		__set_bit(BTN_TOUCH, ts->input->keybit);
+		input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0);
+		input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0);
+		input_set_drvdata(ts->input, ts);
+	}
+
+	ts->base = devm_ioremap_resource(dev,
+			      platform_get_resource(pdev, IORESOURCE_MEM, 0));
+	if (IS_ERR(ts->base))
+		return PTR_ERR(ts->base);
+
+	ts->irq = platform_get_irq(pdev, 0);
+	error = devm_request_irq(dev, ts->irq, sun4i_ts_irq, 0, "sun4i-ts", ts);
+	if (error)
+		return error;
+
+	/*
+	 * Select HOSC clk, clkin = clk / 6, adc samplefreq = clkin / 8192,
+	 * t_acq = clkin / (16 * 64)
+	 */
+	writel(ADC_CLK_SEL(0) | ADC_CLK_DIV(2) | FS_DIV(7) | T_ACQ(63),
+	       ts->base + TP_CTRL0);
+
+	/*
+	 * tp_sensitive_adjust is an optional property
+	 * tp_mode = 0 : only x and y coordinates, as we don't use dual touch
+	 */
+	of_property_read_u32(np, "allwinner,tp-sensitive-adjust",
+			     &tp_sensitive_adjust);
+	writel(TP_SENSITIVE_ADJUST(tp_sensitive_adjust) | TP_MODE_SELECT(0),
+	       ts->base + TP_CTRL2);
+
+	/*
+	 * Enable median and averaging filter, optional property for
+	 * filter type.
+	 */
+	of_property_read_u32(np, "allwinner,filter-type", &filter_type);
+	writel(FILTER_EN(1) | FILTER_TYPE(filter_type), ts->base + TP_CTRL3);
+
+	/* Enable temperature measurement, period 1953 (2 seconds) */
+	writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR);
+
+	/*
+	 * Set stylus up debounce to aprox 10 ms, enable debounce, and
+	 * finally enable tp mode.
+	 */
+	reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1);
+	if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts"))
+		reg |= SUN6I_TP_MODE_EN(1);
+	else
+		reg |= TP_MODE_EN(1);
+	writel(reg, ts->base + TP_CTRL1);
+
+	/*
+	 * The thermal core does not register hwmon devices for DT-based
+	 * thermal zone sensors, such as this one.
+	 */
+	hwmon = devm_hwmon_device_register_with_groups(ts->dev, "sun4i_ts",
+						       ts, sun4i_ts_groups);
+	if (IS_ERR(hwmon))
+		return PTR_ERR(hwmon);
+
+	ts->tz = thermal_zone_of_sensor_register(ts->dev, 0, ts,
+						 &sun4i_ts_tz_ops);
+	if (IS_ERR(ts->tz))
+		ts->tz = NULL;
+
+	writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
+
+	if (ts_attached) {
+		error = input_register_device(ts->input);
+		if (error) {
+			writel(0, ts->base + TP_INT_FIFOC);
+			thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
+			return error;
+		}
+	}
+
+	platform_set_drvdata(pdev, ts);
+	return 0;
+}
+
+static int sun4i_ts_remove(struct platform_device *pdev)
+{
+	struct sun4i_ts_data *ts = platform_get_drvdata(pdev);
+
+	/* Explicit unregister to avoid open/close changing the imask later */
+	if (ts->input)
+		input_unregister_device(ts->input);
+
+	thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
+
+	/* Deactivate all IRQs */
+	writel(0, ts->base + TP_INT_FIFOC);
+
+	return 0;
+}
+
+static const struct of_device_id sun4i_ts_of_match[] = {
+	{ .compatible = "allwinner,sun4i-a10-ts", },
+	{ .compatible = "allwinner,sun5i-a13-ts", },
+	{ .compatible = "allwinner,sun6i-a31-ts", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sun4i_ts_of_match);
+
+static struct platform_driver sun4i_ts_driver = {
+	.driver = {
+		.name	= "sun4i-ts",
+		.of_match_table = of_match_ptr(sun4i_ts_of_match),
+	},
+	.probe	= sun4i_ts_probe,
+	.remove	= sun4i_ts_remove,
+};
+
+module_platform_driver(sun4i_ts_driver);
+
+MODULE_DESCRIPTION("Allwinner sun4i resistive touchscreen controller driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
new file mode 100644
index 0000000..0146e2c
--- /dev/null
+++ b/drivers/input/touchscreen/sur40.c
@@ -0,0 +1,925 @@
+/*
+ * Surface2.0/SUR40/PixelSense input driver
+ *
+ * Copyright (c) 2014 by Florian 'floe' Echtler <floe@butterbrot.org>
+ *
+ * Derived from the USB Skeleton driver 1.1,
+ * Copyright (c) 2003 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * and from the Apple USB BCM5974 multitouch driver,
+ * Copyright (c) 2008 Henrik Rydberg (rydberg@euromail.se)
+ *
+ * and from the generic hid-multitouch driver,
+ * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr>
+ *
+ * and from the v4l2-pci-skeleton driver,
+ * Copyright (c) Copyright 2014 Cisco Systems, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/printk.h>
+#include <linux/input-polldev.h>
+#include <linux/input/mt.h>
+#include <linux/usb/input.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-sg.h>
+
+/* read 512 bytes from endpoint 0x86 -> get header + blobs */
+struct sur40_header {
+
+	__le16 type;       /* always 0x0001 */
+	__le16 count;      /* count of blobs (if 0: continue prev. packet) */
+
+	__le32 packet_id;  /* unique ID for all packets in one frame */
+
+	__le32 timestamp;  /* milliseconds (inc. by 16 or 17 each frame) */
+	__le32 unknown;    /* "epoch?" always 02/03 00 00 00 */
+
+} __packed;
+
+struct sur40_blob {
+
+	__le16 blob_id;
+
+	u8 action;         /* 0x02 = enter/exit, 0x03 = update (?) */
+	u8 unknown;        /* always 0x01 or 0x02 (no idea what this is?) */
+
+	__le16 bb_pos_x;   /* upper left corner of bounding box */
+	__le16 bb_pos_y;
+
+	__le16 bb_size_x;  /* size of bounding box */
+	__le16 bb_size_y;
+
+	__le16 pos_x;      /* finger tip position */
+	__le16 pos_y;
+
+	__le16 ctr_x;      /* centroid position */
+	__le16 ctr_y;
+
+	__le16 axis_x;     /* somehow related to major/minor axis, mostly: */
+	__le16 axis_y;     /* axis_x == bb_size_y && axis_y == bb_size_x */
+
+	__le32 angle;      /* orientation in radians relative to x axis -
+	                      actually an IEEE754 float, don't use in kernel */
+
+	__le32 area;       /* size in pixels/pressure (?) */
+
+	u8 padding[32];
+
+} __packed;
+
+/* combined header/blob data */
+struct sur40_data {
+	struct sur40_header header;
+	struct sur40_blob   blobs[];
+} __packed;
+
+/* read 512 bytes from endpoint 0x82 -> get header below
+ * continue reading 16k blocks until header.size bytes read */
+struct sur40_image_header {
+	__le32 magic;     /* "SUBF" */
+	__le32 packet_id;
+	__le32 size;      /* always 0x0007e900 = 960x540 */
+	__le32 timestamp; /* milliseconds (increases by 16 or 17 each frame) */
+	__le32 unknown;   /* "epoch?" always 02/03 00 00 00 */
+} __packed;
+
+/* version information */
+#define DRIVER_SHORT   "sur40"
+#define DRIVER_LONG    "Samsung SUR40"
+#define DRIVER_AUTHOR  "Florian 'floe' Echtler <floe@butterbrot.org>"
+#define DRIVER_DESC    "Surface2.0/SUR40/PixelSense input driver"
+
+/* vendor and device IDs */
+#define ID_MICROSOFT 0x045e
+#define ID_SUR40     0x0775
+
+/* sensor resolution */
+#define SENSOR_RES_X 1920
+#define SENSOR_RES_Y 1080
+
+/* touch data endpoint */
+#define TOUCH_ENDPOINT 0x86
+
+/* video data endpoint */
+#define VIDEO_ENDPOINT 0x82
+
+/* video header fields */
+#define VIDEO_HEADER_MAGIC 0x46425553
+#define VIDEO_PACKET_SIZE  16384
+
+/* polling interval (ms) */
+#define POLL_INTERVAL 1
+
+/* maximum number of contacts FIXME: this is a guess? */
+#define MAX_CONTACTS 64
+
+/* control commands */
+#define SUR40_GET_VERSION 0xb0 /* 12 bytes string    */
+#define SUR40_UNKNOWN1    0xb3 /*  5 bytes           */
+#define SUR40_UNKNOWN2    0xc1 /* 24 bytes           */
+
+#define SUR40_GET_STATE   0xc5 /*  4 bytes state (?) */
+#define SUR40_GET_SENSORS 0xb1 /*  8 bytes sensors   */
+
+/* master device state */
+struct sur40_state {
+
+	struct usb_device *usbdev;
+	struct device *dev;
+	struct input_polled_dev *input;
+
+	struct v4l2_device v4l2;
+	struct video_device vdev;
+	struct mutex lock;
+
+	struct vb2_queue queue;
+	struct vb2_alloc_ctx *alloc_ctx;
+	struct list_head buf_list;
+	spinlock_t qlock;
+	int sequence;
+
+	struct sur40_data *bulk_in_buffer;
+	size_t bulk_in_size;
+	u8 bulk_in_epaddr;
+
+	char phys[64];
+};
+
+struct sur40_buffer {
+	struct vb2_v4l2_buffer vb;
+	struct list_head list;
+};
+
+/* forward declarations */
+static const struct video_device sur40_video_device;
+static const struct v4l2_pix_format sur40_video_format;
+static const struct vb2_queue sur40_queue;
+static void sur40_process_video(struct sur40_state *sur40);
+
+/*
+ * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT
+ * here by mistake which is very likely to have corrupted the firmware EEPROM
+ * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug.
+ * Should you ever run into a similar problem, the background story to this
+ * incident and instructions on how to fix the corrupted EEPROM are available
+ * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html
+*/
+
+/* command wrapper */
+static int sur40_command(struct sur40_state *dev,
+			 u8 command, u16 index, void *buffer, u16 size)
+{
+	return usb_control_msg(dev->usbdev, usb_rcvctrlpipe(dev->usbdev, 0),
+			       command,
+			       USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+			       0x00, index, buffer, size, 1000);
+}
+
+/* Initialization routine, called from sur40_open */
+static int sur40_init(struct sur40_state *dev)
+{
+	int result;
+	u8 buffer[24];
+
+	/* stupidly replay the original MS driver init sequence */
+	result = sur40_command(dev, SUR40_GET_VERSION, 0x00, buffer, 12);
+	if (result < 0)
+		return result;
+
+	result = sur40_command(dev, SUR40_GET_VERSION, 0x01, buffer, 12);
+	if (result < 0)
+		return result;
+
+	result = sur40_command(dev, SUR40_GET_VERSION, 0x02, buffer, 12);
+	if (result < 0)
+		return result;
+
+	result = sur40_command(dev, SUR40_UNKNOWN2,    0x00, buffer, 24);
+	if (result < 0)
+		return result;
+
+	result = sur40_command(dev, SUR40_UNKNOWN1,    0x00, buffer,  5);
+	if (result < 0)
+		return result;
+
+	result = sur40_command(dev, SUR40_GET_VERSION, 0x03, buffer, 12);
+
+	/*
+	 * Discard the result buffer - no known data inside except
+	 * some version strings, maybe extract these sometime...
+	 */
+
+	return result;
+}
+
+/*
+ * Callback routines from input_polled_dev
+ */
+
+/* Enable the device, polling will now start. */
+static void sur40_open(struct input_polled_dev *polldev)
+{
+	struct sur40_state *sur40 = polldev->private;
+
+	dev_dbg(sur40->dev, "open\n");
+	sur40_init(sur40);
+}
+
+/* Disable device, polling has stopped. */
+static void sur40_close(struct input_polled_dev *polldev)
+{
+	struct sur40_state *sur40 = polldev->private;
+
+	dev_dbg(sur40->dev, "close\n");
+	/*
+	 * There is no known way to stop the device, so we simply
+	 * stop polling.
+	 */
+}
+
+/*
+ * This function is called when a whole contact has been processed,
+ * so that it can assign it to a slot and store the data there.
+ */
+static void sur40_report_blob(struct sur40_blob *blob, struct input_dev *input)
+{
+	int wide, major, minor;
+
+	int bb_size_x = le16_to_cpu(blob->bb_size_x);
+	int bb_size_y = le16_to_cpu(blob->bb_size_y);
+
+	int pos_x = le16_to_cpu(blob->pos_x);
+	int pos_y = le16_to_cpu(blob->pos_y);
+
+	int ctr_x = le16_to_cpu(blob->ctr_x);
+	int ctr_y = le16_to_cpu(blob->ctr_y);
+
+	int slotnum = input_mt_get_slot_by_key(input, blob->blob_id);
+	if (slotnum < 0 || slotnum >= MAX_CONTACTS)
+		return;
+
+	input_mt_slot(input, slotnum);
+	input_mt_report_slot_state(input, MT_TOOL_FINGER, 1);
+	wide = (bb_size_x > bb_size_y);
+	major = max(bb_size_x, bb_size_y);
+	minor = min(bb_size_x, bb_size_y);
+
+	input_report_abs(input, ABS_MT_POSITION_X, pos_x);
+	input_report_abs(input, ABS_MT_POSITION_Y, pos_y);
+	input_report_abs(input, ABS_MT_TOOL_X, ctr_x);
+	input_report_abs(input, ABS_MT_TOOL_Y, ctr_y);
+
+	/* TODO: use a better orientation measure */
+	input_report_abs(input, ABS_MT_ORIENTATION, wide);
+	input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
+	input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
+}
+
+/* core function: poll for new input data */
+static void sur40_poll(struct input_polled_dev *polldev)
+{
+	struct sur40_state *sur40 = polldev->private;
+	struct input_dev *input = polldev->input;
+	int result, bulk_read, need_blobs, packet_blobs, i;
+	u32 uninitialized_var(packet_id);
+
+	struct sur40_header *header = &sur40->bulk_in_buffer->header;
+	struct sur40_blob *inblob = &sur40->bulk_in_buffer->blobs[0];
+
+	dev_dbg(sur40->dev, "poll\n");
+
+	need_blobs = -1;
+
+	do {
+
+		/* perform a blocking bulk read to get data from the device */
+		result = usb_bulk_msg(sur40->usbdev,
+			usb_rcvbulkpipe(sur40->usbdev, sur40->bulk_in_epaddr),
+			sur40->bulk_in_buffer, sur40->bulk_in_size,
+			&bulk_read, 1000);
+
+		dev_dbg(sur40->dev, "received %d bytes\n", bulk_read);
+
+		if (result < 0) {
+			dev_err(sur40->dev, "error in usb_bulk_read\n");
+			return;
+		}
+
+		result = bulk_read - sizeof(struct sur40_header);
+
+		if (result % sizeof(struct sur40_blob) != 0) {
+			dev_err(sur40->dev, "transfer size mismatch\n");
+			return;
+		}
+
+		/* first packet? */
+		if (need_blobs == -1) {
+			need_blobs = le16_to_cpu(header->count);
+			dev_dbg(sur40->dev, "need %d blobs\n", need_blobs);
+			packet_id = le32_to_cpu(header->packet_id);
+		}
+
+		/*
+		 * Sanity check. when video data is also being retrieved, the
+		 * packet ID will usually increase in the middle of a series
+		 * instead of at the end.
+		 */
+		if (packet_id != header->packet_id)
+			dev_dbg(sur40->dev, "packet ID mismatch\n");
+
+		packet_blobs = result / sizeof(struct sur40_blob);
+		dev_dbg(sur40->dev, "received %d blobs\n", packet_blobs);
+
+		/* packets always contain at least 4 blobs, even if empty */
+		if (packet_blobs > need_blobs)
+			packet_blobs = need_blobs;
+
+		for (i = 0; i < packet_blobs; i++) {
+			need_blobs--;
+			dev_dbg(sur40->dev, "processing blob\n");
+			sur40_report_blob(&(inblob[i]), input);
+		}
+
+	} while (need_blobs > 0);
+
+	input_mt_sync_frame(input);
+	input_sync(input);
+
+	sur40_process_video(sur40);
+}
+
+/* deal with video data */
+static void sur40_process_video(struct sur40_state *sur40)
+{
+
+	struct sur40_image_header *img = (void *)(sur40->bulk_in_buffer);
+	struct sur40_buffer *new_buf;
+	struct usb_sg_request sgr;
+	struct sg_table *sgt;
+	int result, bulk_read;
+
+	if (!vb2_start_streaming_called(&sur40->queue))
+		return;
+
+	/* get a new buffer from the list */
+	spin_lock(&sur40->qlock);
+	if (list_empty(&sur40->buf_list)) {
+		dev_dbg(sur40->dev, "buffer queue empty\n");
+		spin_unlock(&sur40->qlock);
+		return;
+	}
+	new_buf = list_entry(sur40->buf_list.next, struct sur40_buffer, list);
+	list_del(&new_buf->list);
+	spin_unlock(&sur40->qlock);
+
+	dev_dbg(sur40->dev, "buffer acquired\n");
+
+	/* retrieve data via bulk read */
+	result = usb_bulk_msg(sur40->usbdev,
+			usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT),
+			sur40->bulk_in_buffer, sur40->bulk_in_size,
+			&bulk_read, 1000);
+
+	if (result < 0) {
+		dev_err(sur40->dev, "error in usb_bulk_read\n");
+		goto err_poll;
+	}
+
+	if (bulk_read != sizeof(struct sur40_image_header)) {
+		dev_err(sur40->dev, "received %d bytes (%zd expected)\n",
+			bulk_read, sizeof(struct sur40_image_header));
+		goto err_poll;
+	}
+
+	if (le32_to_cpu(img->magic) != VIDEO_HEADER_MAGIC) {
+		dev_err(sur40->dev, "image magic mismatch\n");
+		goto err_poll;
+	}
+
+	if (le32_to_cpu(img->size) != sur40_video_format.sizeimage) {
+		dev_err(sur40->dev, "image size mismatch\n");
+		goto err_poll;
+	}
+
+	dev_dbg(sur40->dev, "header acquired\n");
+
+	sgt = vb2_dma_sg_plane_desc(&new_buf->vb.vb2_buf, 0);
+
+	result = usb_sg_init(&sgr, sur40->usbdev,
+		usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0,
+		sgt->sgl, sgt->nents, sur40_video_format.sizeimage, 0);
+	if (result < 0) {
+		dev_err(sur40->dev, "error %d in usb_sg_init\n", result);
+		goto err_poll;
+	}
+
+	usb_sg_wait(&sgr);
+	if (sgr.status < 0) {
+		dev_err(sur40->dev, "error %d in usb_sg_wait\n", sgr.status);
+		goto err_poll;
+	}
+
+	dev_dbg(sur40->dev, "image acquired\n");
+
+	/* return error if streaming was stopped in the meantime */
+	if (sur40->sequence == -1)
+		return;
+
+	/* mark as finished */
+	v4l2_get_timestamp(&new_buf->vb.timestamp);
+	new_buf->vb.sequence = sur40->sequence++;
+	new_buf->vb.field = V4L2_FIELD_NONE;
+	vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+	dev_dbg(sur40->dev, "buffer marked done\n");
+	return;
+
+err_poll:
+	vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+}
+
+/* Initialize input device parameters. */
+static void sur40_input_setup(struct input_dev *input_dev)
+{
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(EV_ABS, input_dev->evbit);
+
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+			     0, SENSOR_RES_X, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+			     0, SENSOR_RES_Y, 0, 0);
+
+	input_set_abs_params(input_dev, ABS_MT_TOOL_X,
+			     0, SENSOR_RES_X, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOOL_Y,
+			     0, SENSOR_RES_Y, 0, 0);
+
+	/* max value unknown, but major/minor axis
+	 * can never be larger than screen */
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+			     0, SENSOR_RES_X, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
+			     0, SENSOR_RES_Y, 0, 0);
+
+	input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+
+	input_mt_init_slots(input_dev, MAX_CONTACTS,
+			    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+}
+
+/* Check candidate USB interface. */
+static int sur40_probe(struct usb_interface *interface,
+		       const struct usb_device_id *id)
+{
+	struct usb_device *usbdev = interface_to_usbdev(interface);
+	struct sur40_state *sur40;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	struct input_polled_dev *poll_dev;
+	int error;
+
+	/* Check if we really have the right interface. */
+	iface_desc = &interface->altsetting[0];
+	if (iface_desc->desc.bInterfaceClass != 0xFF)
+		return -ENODEV;
+
+	if (iface_desc->desc.bNumEndpoints < 5)
+		return -ENODEV;
+
+	/* Use endpoint #4 (0x86). */
+	endpoint = &iface_desc->endpoint[4].desc;
+	if (endpoint->bEndpointAddress != TOUCH_ENDPOINT)
+		return -ENODEV;
+
+	/* Allocate memory for our device state and initialize it. */
+	sur40 = kzalloc(sizeof(struct sur40_state), GFP_KERNEL);
+	if (!sur40)
+		return -ENOMEM;
+
+	poll_dev = input_allocate_polled_device();
+	if (!poll_dev) {
+		error = -ENOMEM;
+		goto err_free_dev;
+	}
+
+	/* initialize locks/lists */
+	INIT_LIST_HEAD(&sur40->buf_list);
+	spin_lock_init(&sur40->qlock);
+	mutex_init(&sur40->lock);
+
+	/* Set up polled input device control structure */
+	poll_dev->private = sur40;
+	poll_dev->poll_interval = POLL_INTERVAL;
+	poll_dev->open = sur40_open;
+	poll_dev->poll = sur40_poll;
+	poll_dev->close = sur40_close;
+
+	/* Set up regular input device structure */
+	sur40_input_setup(poll_dev->input);
+
+	poll_dev->input->name = DRIVER_LONG;
+	usb_to_input_id(usbdev, &poll_dev->input->id);
+	usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys));
+	strlcat(sur40->phys, "/input0", sizeof(sur40->phys));
+	poll_dev->input->phys = sur40->phys;
+	poll_dev->input->dev.parent = &interface->dev;
+
+	sur40->usbdev = usbdev;
+	sur40->dev = &interface->dev;
+	sur40->input = poll_dev;
+
+	/* use the bulk-in endpoint tested above */
+	sur40->bulk_in_size = usb_endpoint_maxp(endpoint);
+	sur40->bulk_in_epaddr = endpoint->bEndpointAddress;
+	sur40->bulk_in_buffer = kmalloc(sur40->bulk_in_size, GFP_KERNEL);
+	if (!sur40->bulk_in_buffer) {
+		dev_err(&interface->dev, "Unable to allocate input buffer.");
+		error = -ENOMEM;
+		goto err_free_polldev;
+	}
+
+	/* register the polled input device */
+	error = input_register_polled_device(poll_dev);
+	if (error) {
+		dev_err(&interface->dev,
+			"Unable to register polled input device.");
+		goto err_free_buffer;
+	}
+
+	/* register the video master device */
+	snprintf(sur40->v4l2.name, sizeof(sur40->v4l2.name), "%s", DRIVER_LONG);
+	error = v4l2_device_register(sur40->dev, &sur40->v4l2);
+	if (error) {
+		dev_err(&interface->dev,
+			"Unable to register video master device.");
+		goto err_unreg_v4l2;
+	}
+
+	/* initialize the lock and subdevice */
+	sur40->queue = sur40_queue;
+	sur40->queue.drv_priv = sur40;
+	sur40->queue.lock = &sur40->lock;
+
+	/* initialize the queue */
+	error = vb2_queue_init(&sur40->queue);
+	if (error)
+		goto err_unreg_v4l2;
+
+	sur40->alloc_ctx = vb2_dma_sg_init_ctx(sur40->dev);
+	if (IS_ERR(sur40->alloc_ctx)) {
+		dev_err(sur40->dev, "Can't allocate buffer context");
+		error = PTR_ERR(sur40->alloc_ctx);
+		goto err_unreg_v4l2;
+	}
+
+	sur40->vdev = sur40_video_device;
+	sur40->vdev.v4l2_dev = &sur40->v4l2;
+	sur40->vdev.lock = &sur40->lock;
+	sur40->vdev.queue = &sur40->queue;
+	video_set_drvdata(&sur40->vdev, sur40);
+
+	error = video_register_device(&sur40->vdev, VFL_TYPE_GRABBER, -1);
+	if (error) {
+		dev_err(&interface->dev,
+			"Unable to register video subdevice.");
+		goto err_unreg_video;
+	}
+
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata(interface, sur40);
+	dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC);
+
+	return 0;
+
+err_unreg_video:
+	video_unregister_device(&sur40->vdev);
+err_unreg_v4l2:
+	v4l2_device_unregister(&sur40->v4l2);
+err_free_buffer:
+	kfree(sur40->bulk_in_buffer);
+err_free_polldev:
+	input_free_polled_device(sur40->input);
+err_free_dev:
+	kfree(sur40);
+
+	return error;
+}
+
+/* Unregister device & clean up. */
+static void sur40_disconnect(struct usb_interface *interface)
+{
+	struct sur40_state *sur40 = usb_get_intfdata(interface);
+
+	video_unregister_device(&sur40->vdev);
+	v4l2_device_unregister(&sur40->v4l2);
+	vb2_dma_sg_cleanup_ctx(sur40->alloc_ctx);
+
+	input_unregister_polled_device(sur40->input);
+	input_free_polled_device(sur40->input);
+	kfree(sur40->bulk_in_buffer);
+	kfree(sur40);
+
+	usb_set_intfdata(interface, NULL);
+	dev_dbg(&interface->dev, "%s is now disconnected\n", DRIVER_DESC);
+}
+
+/*
+ * Setup the constraints of the queue: besides setting the number of planes
+ * per buffer and the size and allocation context of each plane, it also
+ * checks if sufficient buffers have been allocated. Usually 3 is a good
+ * minimum number: many DMA engines need a minimum of 2 buffers in the
+ * queue and you need to have another available for userspace processing.
+ */
+static int sur40_queue_setup(struct vb2_queue *q, const void *parg,
+		       unsigned int *nbuffers, unsigned int *nplanes,
+		       unsigned int sizes[], void *alloc_ctxs[])
+{
+	const struct v4l2_format *fmt = parg;
+	struct sur40_state *sur40 = vb2_get_drv_priv(q);
+
+	if (q->num_buffers + *nbuffers < 3)
+		*nbuffers = 3 - q->num_buffers;
+
+	if (fmt && fmt->fmt.pix.sizeimage < sur40_video_format.sizeimage)
+		return -EINVAL;
+
+	*nplanes = 1;
+	sizes[0] = fmt ? fmt->fmt.pix.sizeimage : sur40_video_format.sizeimage;
+	alloc_ctxs[0] = sur40->alloc_ctx;
+
+	return 0;
+}
+
+/*
+ * Prepare the buffer for queueing to the DMA engine: check and set the
+ * payload size.
+ */
+static int sur40_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue);
+	unsigned long size = sur40_video_format.sizeimage;
+
+	if (vb2_plane_size(vb, 0) < size) {
+		dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n",
+			 vb2_plane_size(vb, 0), size);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(vb, 0, size);
+	return 0;
+}
+
+/*
+ * Queue this buffer to the DMA engine.
+ */
+static void sur40_buffer_queue(struct vb2_buffer *vb)
+{
+	struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue);
+	struct sur40_buffer *buf = (struct sur40_buffer *)vb;
+
+	spin_lock(&sur40->qlock);
+	list_add_tail(&buf->list, &sur40->buf_list);
+	spin_unlock(&sur40->qlock);
+}
+
+static void return_all_buffers(struct sur40_state *sur40,
+			       enum vb2_buffer_state state)
+{
+	struct sur40_buffer *buf, *node;
+
+	spin_lock(&sur40->qlock);
+	list_for_each_entry_safe(buf, node, &sur40->buf_list, list) {
+		vb2_buffer_done(&buf->vb.vb2_buf, state);
+		list_del(&buf->list);
+	}
+	spin_unlock(&sur40->qlock);
+}
+
+/*
+ * Start streaming. First check if the minimum number of buffers have been
+ * queued. If not, then return -ENOBUFS and the vb2 framework will call
+ * this function again the next time a buffer has been queued until enough
+ * buffers are available to actually start the DMA engine.
+ */
+static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct sur40_state *sur40 = vb2_get_drv_priv(vq);
+
+	sur40->sequence = 0;
+	return 0;
+}
+
+/*
+ * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued
+ * and passed on to the vb2 framework marked as STATE_ERROR.
+ */
+static void sur40_stop_streaming(struct vb2_queue *vq)
+{
+	struct sur40_state *sur40 = vb2_get_drv_priv(vq);
+	vb2_wait_for_all_buffers(vq);
+	sur40->sequence = -1;
+
+	/* Release all active buffers */
+	return_all_buffers(sur40, VB2_BUF_STATE_ERROR);
+}
+
+/* V4L ioctl */
+static int sur40_vidioc_querycap(struct file *file, void *priv,
+				 struct v4l2_capability *cap)
+{
+	struct sur40_state *sur40 = video_drvdata(file);
+
+	strlcpy(cap->driver, DRIVER_SHORT, sizeof(cap->driver));
+	strlcpy(cap->card, DRIVER_LONG, sizeof(cap->card));
+	usb_make_path(sur40->usbdev, cap->bus_info, sizeof(cap->bus_info));
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
+		V4L2_CAP_READWRITE |
+		V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+	return 0;
+}
+
+static int sur40_vidioc_enum_input(struct file *file, void *priv,
+				   struct v4l2_input *i)
+{
+	if (i->index != 0)
+		return -EINVAL;
+	i->type = V4L2_INPUT_TYPE_CAMERA;
+	i->std = V4L2_STD_UNKNOWN;
+	strlcpy(i->name, "In-Cell Sensor", sizeof(i->name));
+	i->capabilities = 0;
+	return 0;
+}
+
+static int sur40_vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	return (i == 0) ? 0 : -EINVAL;
+}
+
+static int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int sur40_vidioc_fmt(struct file *file, void *priv,
+			    struct v4l2_format *f)
+{
+	f->fmt.pix = sur40_video_format;
+	return 0;
+}
+
+static int sur40_vidioc_enum_fmt(struct file *file, void *priv,
+				 struct v4l2_fmtdesc *f)
+{
+	if (f->index != 0)
+		return -EINVAL;
+	strlcpy(f->description, "8-bit greyscale", sizeof(f->description));
+	f->pixelformat = V4L2_PIX_FMT_GREY;
+	f->flags = 0;
+	return 0;
+}
+
+static int sur40_vidioc_enum_framesizes(struct file *file, void *priv,
+					struct v4l2_frmsizeenum *f)
+{
+	if ((f->index != 0) || (f->pixel_format != V4L2_PIX_FMT_GREY))
+		return -EINVAL;
+
+	f->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+	f->discrete.width  = sur40_video_format.width;
+	f->discrete.height = sur40_video_format.height;
+	return 0;
+}
+
+static int sur40_vidioc_enum_frameintervals(struct file *file, void *priv,
+					    struct v4l2_frmivalenum *f)
+{
+	if ((f->index > 1) || (f->pixel_format != V4L2_PIX_FMT_GREY)
+		|| (f->width  != sur40_video_format.width)
+		|| (f->height != sur40_video_format.height))
+			return -EINVAL;
+
+	f->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+	f->discrete.denominator  = 60/(f->index+1);
+	f->discrete.numerator = 1;
+	return 0;
+}
+
+
+static const struct usb_device_id sur40_table[] = {
+	{ USB_DEVICE(ID_MICROSOFT, ID_SUR40) },  /* Samsung SUR40 */
+	{ }                                      /* terminating null entry */
+};
+MODULE_DEVICE_TABLE(usb, sur40_table);
+
+/* V4L2 structures */
+static const struct vb2_ops sur40_queue_ops = {
+	.queue_setup		= sur40_queue_setup,
+	.buf_prepare		= sur40_buffer_prepare,
+	.buf_queue		= sur40_buffer_queue,
+	.start_streaming	= sur40_start_streaming,
+	.stop_streaming		= sur40_stop_streaming,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+static const struct vb2_queue sur40_queue = {
+	.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+	/*
+	 * VB2_USERPTR in currently not enabled: passing a user pointer to
+	 * dma-sg will result in segment sizes that are not a multiple of
+	 * 512 bytes, which is required by the host controller.
+	*/
+	.io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF,
+	.buf_struct_size = sizeof(struct sur40_buffer),
+	.ops = &sur40_queue_ops,
+	.mem_ops = &vb2_dma_sg_memops,
+	.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC,
+	.min_buffers_needed = 3,
+};
+
+static const struct v4l2_file_operations sur40_video_fops = {
+	.owner = THIS_MODULE,
+	.open = v4l2_fh_open,
+	.release = vb2_fop_release,
+	.unlocked_ioctl = video_ioctl2,
+	.read = vb2_fop_read,
+	.mmap = vb2_fop_mmap,
+	.poll = vb2_fop_poll,
+};
+
+static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = {
+
+	.vidioc_querycap	= sur40_vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap = sur40_vidioc_enum_fmt,
+	.vidioc_try_fmt_vid_cap	= sur40_vidioc_fmt,
+	.vidioc_s_fmt_vid_cap	= sur40_vidioc_fmt,
+	.vidioc_g_fmt_vid_cap	= sur40_vidioc_fmt,
+
+	.vidioc_enum_framesizes = sur40_vidioc_enum_framesizes,
+	.vidioc_enum_frameintervals = sur40_vidioc_enum_frameintervals,
+
+	.vidioc_enum_input	= sur40_vidioc_enum_input,
+	.vidioc_g_input		= sur40_vidioc_g_input,
+	.vidioc_s_input		= sur40_vidioc_s_input,
+
+	.vidioc_reqbufs		= vb2_ioctl_reqbufs,
+	.vidioc_create_bufs	= vb2_ioctl_create_bufs,
+	.vidioc_querybuf	= vb2_ioctl_querybuf,
+	.vidioc_qbuf		= vb2_ioctl_qbuf,
+	.vidioc_dqbuf		= vb2_ioctl_dqbuf,
+	.vidioc_expbuf		= vb2_ioctl_expbuf,
+
+	.vidioc_streamon	= vb2_ioctl_streamon,
+	.vidioc_streamoff	= vb2_ioctl_streamoff,
+};
+
+static const struct video_device sur40_video_device = {
+	.name = DRIVER_LONG,
+	.fops = &sur40_video_fops,
+	.ioctl_ops = &sur40_video_ioctl_ops,
+	.release = video_device_release_empty,
+};
+
+static const struct v4l2_pix_format sur40_video_format = {
+	.pixelformat = V4L2_PIX_FMT_GREY,
+	.width  = SENSOR_RES_X / 2,
+	.height = SENSOR_RES_Y / 2,
+	.field = V4L2_FIELD_NONE,
+	.colorspace = V4L2_COLORSPACE_SRGB,
+	.bytesperline = SENSOR_RES_X / 2,
+	.sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),
+};
+
+/* USB-specific object needed to register this driver with the USB subsystem. */
+static struct usb_driver sur40_driver = {
+	.name = DRIVER_SHORT,
+	.probe = sur40_probe,
+	.disconnect = sur40_disconnect,
+	.id_table = sur40_table,
+};
+
+module_usb_driver(sur40_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
new file mode 100644
index 0000000..642f4a5
--- /dev/null
+++ b/drivers/input/touchscreen/sx8654.c
@@ -0,0 +1,286 @@
+/*
+ * Driver for Semtech SX8654 I2C touchscreen controller.
+ *
+ * Copyright (c) 2015 Armadeus Systems
+ *	Sébastien Szymanski <sebastien.szymanski@armadeus.com>
+ *
+ * Using code from:
+ *  - sx865x.c
+ *	Copyright (c) 2013 U-MoBo Srl
+ *	Pierluigi Passaro <p.passaro@u-mobo.com>
+ *  - sx8650.c
+ *      Copyright (c) 2009 Wayne Roberts
+ *  - tsc2007.c
+ *      Copyright (c) 2008 Kwangwoo Lee
+ *  - ads7846.c
+ *      Copyright (c) 2005 David Brownell
+ *      Copyright (c) 2006 Nokia Corporation
+ *  - corgi_ts.c
+ *      Copyright (C) 2004-2005 Richard Purdie
+ *  - omap_ts.[hc], ads7846.h, ts_osk.c
+ *      Copyright (C) 2002 MontaVista Software
+ *      Copyright (C) 2004 Texas Instruments
+ *      Copyright (C) 2005 Dirk Behme
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+/* register addresses */
+#define I2C_REG_TOUCH0			0x00
+#define I2C_REG_TOUCH1			0x01
+#define I2C_REG_CHANMASK		0x04
+#define I2C_REG_IRQMASK			0x22
+#define I2C_REG_IRQSRC			0x23
+#define I2C_REG_SOFTRESET		0x3f
+
+/* commands */
+#define CMD_READ_REGISTER		0x40
+#define CMD_MANUAL			0xc0
+#define CMD_PENTRG			0xe0
+
+/* value for I2C_REG_SOFTRESET */
+#define SOFTRESET_VALUE			0xde
+
+/* bits for I2C_REG_IRQSRC */
+#define IRQ_PENTOUCH_TOUCHCONVDONE	0x08
+#define IRQ_PENRELEASE			0x04
+
+/* bits for RegTouch1 */
+#define CONDIRQ				0x20
+#define FILT_7SA			0x03
+
+/* bits for I2C_REG_CHANMASK */
+#define CONV_X				0x80
+#define CONV_Y				0x40
+
+/* coordinates rate: higher nibble of CTRL0 register */
+#define RATE_MANUAL			0x00
+#define RATE_5000CPS			0xf0
+
+/* power delay: lower nibble of CTRL0 register */
+#define POWDLY_1_1MS			0x0b
+
+#define MAX_12BIT			((1 << 12) - 1)
+
+struct sx8654 {
+	struct input_dev *input;
+	struct i2c_client *client;
+};
+
+static irqreturn_t sx8654_irq(int irq, void *handle)
+{
+	struct sx8654 *sx8654 = handle;
+	int irqsrc;
+	u8 data[4];
+	unsigned int x, y;
+	int retval;
+
+	irqsrc = i2c_smbus_read_byte_data(sx8654->client,
+					  CMD_READ_REGISTER | I2C_REG_IRQSRC);
+	dev_dbg(&sx8654->client->dev, "irqsrc = 0x%x", irqsrc);
+
+	if (irqsrc < 0)
+		goto out;
+
+	if (irqsrc & IRQ_PENRELEASE) {
+		dev_dbg(&sx8654->client->dev, "pen release interrupt");
+
+		input_report_key(sx8654->input, BTN_TOUCH, 0);
+		input_sync(sx8654->input);
+	}
+
+	if (irqsrc & IRQ_PENTOUCH_TOUCHCONVDONE) {
+		dev_dbg(&sx8654->client->dev, "pen touch interrupt");
+
+		retval = i2c_master_recv(sx8654->client, data, sizeof(data));
+		if (retval != sizeof(data))
+			goto out;
+
+		/* invalid data */
+		if (unlikely(data[0] & 0x80 || data[2] & 0x80))
+			goto out;
+
+		x = ((data[0] & 0xf) << 8) | (data[1]);
+		y = ((data[2] & 0xf) << 8) | (data[3]);
+
+		input_report_abs(sx8654->input, ABS_X, x);
+		input_report_abs(sx8654->input, ABS_Y, y);
+		input_report_key(sx8654->input, BTN_TOUCH, 1);
+		input_sync(sx8654->input);
+
+		dev_dbg(&sx8654->client->dev, "point(%4d,%4d)\n", x, y);
+	}
+
+out:
+	return IRQ_HANDLED;
+}
+
+static int sx8654_open(struct input_dev *dev)
+{
+	struct sx8654 *sx8654 = input_get_drvdata(dev);
+	struct i2c_client *client = sx8654->client;
+	int error;
+
+	/* enable pen trigger mode */
+	error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0,
+					  RATE_5000CPS | POWDLY_1_1MS);
+	if (error) {
+		dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
+		return error;
+	}
+
+	error = i2c_smbus_write_byte(client, CMD_PENTRG);
+	if (error) {
+		dev_err(&client->dev, "writing command CMD_PENTRG failed");
+		return error;
+	}
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static void sx8654_close(struct input_dev *dev)
+{
+	struct sx8654 *sx8654 = input_get_drvdata(dev);
+	struct i2c_client *client = sx8654->client;
+	int error;
+
+	disable_irq(client->irq);
+
+	/* enable manual mode mode */
+	error = i2c_smbus_write_byte(client, CMD_MANUAL);
+	if (error) {
+		dev_err(&client->dev, "writing command CMD_MANUAL failed");
+		return;
+	}
+
+	error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0);
+	if (error) {
+		dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
+		return;
+	}
+}
+
+static int sx8654_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct sx8654 *sx8654;
+	struct input_dev *input;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_READ_WORD_DATA))
+		return -ENXIO;
+
+	sx8654 = devm_kzalloc(&client->dev, sizeof(*sx8654), GFP_KERNEL);
+	if (!sx8654)
+		return -ENOMEM;
+
+	input = devm_input_allocate_device(&client->dev);
+	if (!input)
+		return -ENOMEM;
+
+	input->name = "SX8654 I2C Touchscreen";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &client->dev;
+	input->open = sx8654_open;
+	input->close = sx8654_close;
+
+	__set_bit(INPUT_PROP_DIRECT, input->propbit);
+	input_set_capability(input, EV_KEY, BTN_TOUCH);
+	input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0);
+	input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0);
+
+	sx8654->client = client;
+	sx8654->input = input;
+
+	input_set_drvdata(sx8654->input, sx8654);
+
+	error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET,
+					  SOFTRESET_VALUE);
+	if (error) {
+		dev_err(&client->dev, "writing softreset value failed");
+		return error;
+	}
+
+	error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
+					  CONV_X | CONV_Y);
+	if (error) {
+		dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
+		return error;
+	}
+
+	error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
+					  IRQ_PENTOUCH_TOUCHCONVDONE |
+						IRQ_PENRELEASE);
+	if (error) {
+		dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed");
+		return error;
+	}
+
+	error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1,
+					  CONDIRQ | FILT_7SA);
+	if (error) {
+		dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed");
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, sx8654_irq,
+					  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					  client->name, sx8654);
+	if (error) {
+		dev_err(&client->dev,
+			"Failed to enable IRQ %d, error: %d\n",
+			client->irq, error);
+		return error;
+	}
+
+	/* Disable the IRQ, we'll enable it in sx8654_open() */
+	disable_irq(client->irq);
+
+	error = input_register_device(sx8654->input);
+	if (error)
+		return error;
+
+	i2c_set_clientdata(client, sx8654);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sx8654_of_match[] = {
+	{ .compatible = "semtech,sx8654", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, sx8654_of_match);
+#endif
+
+static const struct i2c_device_id sx8654_id_table[] = {
+	{ "semtech_sx8654", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, sx8654_id_table);
+
+static struct i2c_driver sx8654_driver = {
+	.driver = {
+		.name = "sx8654",
+		.of_match_table = of_match_ptr(sx8654_of_match),
+	},
+	.id_table = sx8654_id_table,
+	.probe = sx8654_probe,
+};
+module_i2c_driver(sx8654_driver);
+
+MODULE_AUTHOR("Sébastien Szymanski <sebastien.szymanski@armadeus.com>");
+MODULE_DESCRIPTION("Semtech SX8654 I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
new file mode 100644
index 0000000..191a1b8
--- /dev/null
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -0,0 +1,554 @@
+/*
+ * TI Touch Screen driver
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/sort.h>
+
+#include <linux/mfd/ti_am335x_tscadc.h>
+
+#define ADCFSM_STEPID		0x10
+#define SEQ_SETTLE		275
+#define MAX_12BIT		((1 << 12) - 1)
+
+static const int config_pins[] = {
+	STEPCONFIG_XPP,
+	STEPCONFIG_XNN,
+	STEPCONFIG_YPP,
+	STEPCONFIG_YNN,
+};
+
+struct titsc {
+	struct input_dev	*input;
+	struct ti_tscadc_dev	*mfd_tscadc;
+	unsigned int		irq;
+	unsigned int		wires;
+	unsigned int		x_plate_resistance;
+	bool			pen_down;
+	int			coordinate_readouts;
+	u32			config_inp[4];
+	u32			bit_xp, bit_xn, bit_yp, bit_yn;
+	u32			inp_xp, inp_xn, inp_yp, inp_yn;
+	u32			step_mask;
+	u32			charge_delay;
+};
+
+static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
+{
+	return readl(ts->mfd_tscadc->tscadc_base + reg);
+}
+
+static void titsc_writel(struct titsc *tsc, unsigned int reg,
+					unsigned int val)
+{
+	writel(val, tsc->mfd_tscadc->tscadc_base + reg);
+}
+
+static int titsc_config_wires(struct titsc *ts_dev)
+{
+	u32 analog_line[4];
+	u32 wire_order[4];
+	int i, bit_cfg;
+
+	for (i = 0; i < 4; i++) {
+		/*
+		 * Get the order in which TSC wires are attached
+		 * w.r.t. each of the analog input lines on the EVM.
+		 */
+		analog_line[i] = (ts_dev->config_inp[i] & 0xF0) >> 4;
+		wire_order[i] = ts_dev->config_inp[i] & 0x0F;
+		if (WARN_ON(analog_line[i] > 7))
+			return -EINVAL;
+		if (WARN_ON(wire_order[i] > ARRAY_SIZE(config_pins)))
+			return -EINVAL;
+	}
+
+	for (i = 0; i < 4; i++) {
+		int an_line;
+		int wi_order;
+
+		an_line = analog_line[i];
+		wi_order = wire_order[i];
+		bit_cfg = config_pins[wi_order];
+		if (bit_cfg == 0)
+			return -EINVAL;
+		switch (wi_order) {
+		case 0:
+			ts_dev->bit_xp = bit_cfg;
+			ts_dev->inp_xp = an_line;
+			break;
+
+		case 1:
+			ts_dev->bit_xn = bit_cfg;
+			ts_dev->inp_xn = an_line;
+			break;
+
+		case 2:
+			ts_dev->bit_yp = bit_cfg;
+			ts_dev->inp_yp = an_line;
+			break;
+		case 3:
+			ts_dev->bit_yn = bit_cfg;
+			ts_dev->inp_yn = an_line;
+			break;
+		}
+	}
+	return 0;
+}
+
+static void titsc_step_config(struct titsc *ts_dev)
+{
+	unsigned int	config;
+	int i;
+	int end_step, first_step, tsc_steps;
+	u32 stepenable;
+
+	config = STEPCONFIG_MODE_HWSYNC |
+			STEPCONFIG_AVG_16 | ts_dev->bit_xp;
+	switch (ts_dev->wires) {
+	case 4:
+		config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn;
+		break;
+	case 5:
+		config |= ts_dev->bit_yn |
+				STEPCONFIG_INP_AN4 | ts_dev->bit_xn |
+				ts_dev->bit_yp;
+		break;
+	case 8:
+		config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn;
+		break;
+	}
+
+	tsc_steps = ts_dev->coordinate_readouts * 2 + 2;
+	first_step = TOTAL_STEPS - tsc_steps;
+	/* Steps 16 to 16-coordinate_readouts is for X */
+	end_step = first_step + tsc_steps;
+	for (i = end_step - ts_dev->coordinate_readouts; i < end_step; i++) {
+		titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
+		titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
+	}
+
+	config = 0;
+	config = STEPCONFIG_MODE_HWSYNC |
+			STEPCONFIG_AVG_16 | ts_dev->bit_yn |
+			STEPCONFIG_INM_ADCREFM;
+	switch (ts_dev->wires) {
+	case 4:
+		config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp);
+		break;
+	case 5:
+		config |= ts_dev->bit_xp | STEPCONFIG_INP_AN4 |
+				ts_dev->bit_xn | ts_dev->bit_yp;
+		break;
+	case 8:
+		config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp);
+		break;
+	}
+
+	/* 1 ... coordinate_readouts is for Y */
+	end_step = first_step + ts_dev->coordinate_readouts;
+	for (i = first_step; i < end_step; i++) {
+		titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
+		titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
+	}
+
+	/* Make CHARGECONFIG same as IDLECONFIG */
+
+	config = titsc_readl(ts_dev, REG_IDLECONFIG);
+	titsc_writel(ts_dev, REG_CHARGECONFIG, config);
+	titsc_writel(ts_dev, REG_CHARGEDELAY, ts_dev->charge_delay);
+
+	/* coordinate_readouts + 1 ... coordinate_readouts + 2 is for Z */
+	config = STEPCONFIG_MODE_HWSYNC |
+			STEPCONFIG_AVG_16 | ts_dev->bit_yp |
+			ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM |
+			STEPCONFIG_INP(ts_dev->inp_xp);
+	titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config);
+	titsc_writel(ts_dev, REG_STEPDELAY(end_step),
+			STEPCONFIG_OPENDLY);
+
+	end_step++;
+	config |= STEPCONFIG_INP(ts_dev->inp_yn);
+	titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config);
+	titsc_writel(ts_dev, REG_STEPDELAY(end_step),
+			STEPCONFIG_OPENDLY);
+
+	/* The steps end ... end - readouts * 2 + 2 and bit 0 for TS_Charge */
+	stepenable = 1;
+	for (i = 0; i < tsc_steps; i++)
+		stepenable |= 1 << (first_step + i + 1);
+
+	ts_dev->step_mask = stepenable;
+	am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
+}
+
+static int titsc_cmp_coord(const void *a, const void *b)
+{
+	return *(int *)a - *(int *)b;
+}
+
+static void titsc_read_coordinates(struct titsc *ts_dev,
+		u32 *x, u32 *y, u32 *z1, u32 *z2)
+{
+	unsigned int yvals[7], xvals[7];
+	unsigned int i, xsum = 0, ysum = 0;
+	unsigned int creads = ts_dev->coordinate_readouts;
+
+	for (i = 0; i < creads; i++) {
+		yvals[i] = titsc_readl(ts_dev, REG_FIFO0);
+		yvals[i] &= 0xfff;
+	}
+
+	*z1 = titsc_readl(ts_dev, REG_FIFO0);
+	*z1 &= 0xfff;
+	*z2 = titsc_readl(ts_dev, REG_FIFO0);
+	*z2 &= 0xfff;
+
+	for (i = 0; i < creads; i++) {
+		xvals[i] = titsc_readl(ts_dev, REG_FIFO0);
+		xvals[i] &= 0xfff;
+	}
+
+	/*
+	 * If co-ordinates readouts is less than 4 then
+	 * report the average. In case of 4 or more
+	 * readouts, sort the co-ordinate samples, drop
+	 * min and max values and report the average of
+	 * remaining values.
+	 */
+	if (creads <=  3) {
+		for (i = 0; i < creads; i++) {
+			ysum += yvals[i];
+			xsum += xvals[i];
+		}
+		ysum /= creads;
+		xsum /= creads;
+	} else {
+		sort(yvals, creads, sizeof(unsigned int),
+		     titsc_cmp_coord, NULL);
+		sort(xvals, creads, sizeof(unsigned int),
+		     titsc_cmp_coord, NULL);
+		for (i = 1; i < creads - 1; i++) {
+			ysum += yvals[i];
+			xsum += xvals[i];
+		}
+		ysum /= creads - 2;
+		xsum /= creads - 2;
+	}
+	*y = ysum;
+	*x = xsum;
+}
+
+static irqreturn_t titsc_irq(int irq, void *dev)
+{
+	struct titsc *ts_dev = dev;
+	struct input_dev *input_dev = ts_dev->input;
+	unsigned int fsm, status, irqclr = 0;
+	unsigned int x = 0, y = 0;
+	unsigned int z1, z2, z;
+
+	status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
+	if (status & IRQENB_HW_PEN) {
+		ts_dev->pen_down = true;
+		titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00);
+		titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
+		irqclr |= IRQENB_HW_PEN;
+	}
+
+	if (status & IRQENB_PENUP) {
+		fsm = titsc_readl(ts_dev, REG_ADCFSM);
+		if (fsm == ADCFSM_STEPID) {
+			ts_dev->pen_down = false;
+			input_report_key(input_dev, BTN_TOUCH, 0);
+			input_report_abs(input_dev, ABS_PRESSURE, 0);
+			input_sync(input_dev);
+		} else {
+			ts_dev->pen_down = true;
+		}
+		irqclr |= IRQENB_PENUP;
+	}
+
+	if (status & IRQENB_EOS)
+		irqclr |= IRQENB_EOS;
+
+	/*
+	 * ADC and touchscreen share the IRQ line.
+	 * FIFO1 interrupts are used by ADC. Handle FIFO0 IRQs here only
+	 */
+	if (status & IRQENB_FIFO0THRES) {
+
+		titsc_read_coordinates(ts_dev, &x, &y, &z1, &z2);
+
+		if (ts_dev->pen_down && z1 != 0 && z2 != 0) {
+			/*
+			 * Calculate pressure using formula
+			 * Resistance(touch) = x plate resistance *
+			 * x postion/4096 * ((z2 / z1) - 1)
+			 */
+			z = z1 - z2;
+			z *= x;
+			z *= ts_dev->x_plate_resistance;
+			z /= z2;
+			z = (z + 2047) >> 12;
+
+			if (z <= MAX_12BIT) {
+				input_report_abs(input_dev, ABS_X, x);
+				input_report_abs(input_dev, ABS_Y, y);
+				input_report_abs(input_dev, ABS_PRESSURE, z);
+				input_report_key(input_dev, BTN_TOUCH, 1);
+				input_sync(input_dev);
+			}
+		}
+		irqclr |= IRQENB_FIFO0THRES;
+	}
+	if (irqclr) {
+		titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
+		if (status & IRQENB_EOS)
+			am335x_tsc_se_set_cache(ts_dev->mfd_tscadc,
+						ts_dev->step_mask);
+		return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
+
+static int titsc_parse_dt(struct platform_device *pdev,
+					struct titsc *ts_dev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	int err;
+
+	if (!node)
+		return -EINVAL;
+
+	err = of_property_read_u32(node, "ti,wires", &ts_dev->wires);
+	if (err < 0)
+		return err;
+	switch (ts_dev->wires) {
+	case 4:
+	case 5:
+	case 8:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	err = of_property_read_u32(node, "ti,x-plate-resistance",
+			&ts_dev->x_plate_resistance);
+	if (err < 0)
+		return err;
+
+	/*
+	 * Try with the new binding first. If it fails, try again with
+	 * bogus, miss-spelled version.
+	 */
+	err = of_property_read_u32(node, "ti,coordinate-readouts",
+			&ts_dev->coordinate_readouts);
+	if (err < 0) {
+		dev_warn(&pdev->dev, "please use 'ti,coordinate-readouts' instead\n");
+		err = of_property_read_u32(node, "ti,coordiante-readouts",
+				&ts_dev->coordinate_readouts);
+	}
+
+	if (err < 0)
+		return err;
+
+	if (ts_dev->coordinate_readouts <= 0) {
+		dev_warn(&pdev->dev,
+			 "invalid co-ordinate readouts, resetting it to 5\n");
+		ts_dev->coordinate_readouts = 5;
+	}
+
+	err = of_property_read_u32(node, "ti,charge-delay",
+				   &ts_dev->charge_delay);
+	/*
+	 * If ti,charge-delay value is not specified, then use
+	 * CHARGEDLY_OPENDLY as the default value.
+	 */
+	if (err < 0) {
+		ts_dev->charge_delay = CHARGEDLY_OPENDLY;
+		dev_warn(&pdev->dev, "ti,charge-delay not specified\n");
+	}
+
+	return of_property_read_u32_array(node, "ti,wire-config",
+			ts_dev->config_inp, ARRAY_SIZE(ts_dev->config_inp));
+}
+
+/*
+ * The functions for inserting/removing driver as a module.
+ */
+
+static int titsc_probe(struct platform_device *pdev)
+{
+	struct titsc *ts_dev;
+	struct input_dev *input_dev;
+	struct ti_tscadc_dev *tscadc_dev = ti_tscadc_dev_get(pdev);
+	int err;
+
+	/* Allocate memory for device */
+	ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!ts_dev || !input_dev) {
+		dev_err(&pdev->dev, "failed to allocate memory.\n");
+		err = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	tscadc_dev->tsc = ts_dev;
+	ts_dev->mfd_tscadc = tscadc_dev;
+	ts_dev->input = input_dev;
+	ts_dev->irq = tscadc_dev->irq;
+
+	err = titsc_parse_dt(pdev, ts_dev);
+	if (err) {
+		dev_err(&pdev->dev, "Could not find valid DT data.\n");
+		goto err_free_mem;
+	}
+
+	err = request_irq(ts_dev->irq, titsc_irq,
+			  IRQF_SHARED, pdev->dev.driver->name, ts_dev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to allocate irq.\n");
+		goto err_free_mem;
+	}
+
+	titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
+	titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_EOS);
+	err = titsc_config_wires(ts_dev);
+	if (err) {
+		dev_err(&pdev->dev, "wrong i/p wire configuration\n");
+		goto err_free_irq;
+	}
+	titsc_step_config(ts_dev);
+	titsc_writel(ts_dev, REG_FIFO0THR,
+			ts_dev->coordinate_readouts * 2 + 2 - 1);
+
+	input_dev->name = "ti-tsc";
+	input_dev->dev.parent = &pdev->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
+
+	/* register to the input system */
+	err = input_register_device(input_dev);
+	if (err)
+		goto err_free_irq;
+
+	platform_set_drvdata(pdev, ts_dev);
+	return 0;
+
+err_free_irq:
+	free_irq(ts_dev->irq, ts_dev);
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(ts_dev);
+	return err;
+}
+
+static int titsc_remove(struct platform_device *pdev)
+{
+	struct titsc *ts_dev = platform_get_drvdata(pdev);
+	u32 steps;
+
+	free_irq(ts_dev->irq, ts_dev);
+
+	/* total steps followed by the enable mask */
+	steps = 2 * ts_dev->coordinate_readouts + 2;
+	steps = (1 << steps) - 1;
+	am335x_tsc_se_clr(ts_dev->mfd_tscadc, steps);
+
+	input_unregister_device(ts_dev->input);
+
+	kfree(ts_dev);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int titsc_suspend(struct device *dev)
+{
+	struct titsc *ts_dev = dev_get_drvdata(dev);
+	struct ti_tscadc_dev *tscadc_dev;
+	unsigned int idle;
+
+	tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
+	if (device_may_wakeup(tscadc_dev->dev)) {
+		idle = titsc_readl(ts_dev, REG_IRQENABLE);
+		titsc_writel(ts_dev, REG_IRQENABLE,
+				(idle | IRQENB_HW_PEN));
+		titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB);
+	}
+	return 0;
+}
+
+static int titsc_resume(struct device *dev)
+{
+	struct titsc *ts_dev = dev_get_drvdata(dev);
+	struct ti_tscadc_dev *tscadc_dev;
+
+	tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
+	if (device_may_wakeup(tscadc_dev->dev)) {
+		titsc_writel(ts_dev, REG_IRQWAKEUP,
+				0x00);
+		titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
+	}
+	titsc_step_config(ts_dev);
+	titsc_writel(ts_dev, REG_FIFO0THR,
+			ts_dev->coordinate_readouts * 2 + 2 - 1);
+	return 0;
+}
+
+static const struct dev_pm_ops titsc_pm_ops = {
+	.suspend = titsc_suspend,
+	.resume  = titsc_resume,
+};
+#define TITSC_PM_OPS (&titsc_pm_ops)
+#else
+#define TITSC_PM_OPS NULL
+#endif
+
+static const struct of_device_id ti_tsc_dt_ids[] = {
+	{ .compatible = "ti,am3359-tsc", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ti_tsc_dt_ids);
+
+static struct platform_driver ti_tsc_driver = {
+	.probe	= titsc_probe,
+	.remove	= titsc_remove,
+	.driver	= {
+		.name   = "TI-am335x-tsc",
+		.pm	= TITSC_PM_OPS,
+		.of_match_table = ti_tsc_dt_ids,
+	},
+};
+module_platform_driver(ti_tsc_driver);
+
+MODULE_DESCRIPTION("TI touchscreen controller driver");
+MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/touchit213.c b/drivers/input/touchscreen/touchit213.c
new file mode 100644
index 0000000..c27cf8f
--- /dev/null
+++ b/drivers/input/touchscreen/touchit213.c
@@ -0,0 +1,218 @@
+/*
+ * Sahara TouchIT-213 serial touchscreen driver
+ *
+ * Copyright (c) 2007-2008 Claudio Nieder <private@claudio.ch>
+ *
+ * Based on Touchright driver (drivers/input/touchscreen/touchright.c)
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Sahara TouchIT-213 serial touchscreen driver"
+
+MODULE_AUTHOR("Claudio Nieder <private@claudio.ch>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+/*
+ * Data is received through COM1 at 9600bit/s,8bit,no parity in packets
+ * of 5 byte each.
+ *
+ *   +--------+   +--------+   +--------+   +--------+   +--------+
+ *   |1000000p|   |0xxxxxxx|   |0xxxxxxx|   |0yyyyyyy|   |0yyyyyyy|
+ *   +--------+   +--------+   +--------+   +--------+   +--------+
+ *                    MSB          LSB          MSB          LSB
+ *
+ * The value of p is 1 as long as the screen is touched and 0 when
+ * reporting the location where touching stopped, e.g. where the pen was
+ * lifted from the screen.
+ *
+ * When holding the screen in landscape mode as the BIOS text output is
+ * presented, x is the horizontal axis with values growing from left to
+ * right and y is the vertical axis with values growing from top to
+ * bottom.
+ *
+ * When holding the screen in portrait mode with the Sahara logo in its
+ * correct position, x ist the vertical axis with values growing from
+ * top to bottom and y is the horizontal axis with values growing from
+ * right to left.
+ */
+
+#define T213_FORMAT_TOUCH_BIT	0x01
+#define T213_FORMAT_STATUS_BYTE	0x80
+#define T213_FORMAT_STATUS_MASK	~T213_FORMAT_TOUCH_BIT
+
+/*
+ * On my Sahara Touch-IT 213 I have observed x values from 0 to 0x7f0
+ * and y values from 0x1d to 0x7e9, so the actual measurement is
+ * probably done with an 11 bit precision.
+ */
+#define T213_MIN_XC 0
+#define T213_MAX_XC 0x07ff
+#define T213_MIN_YC 0
+#define T213_MAX_YC 0x07ff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct touchit213 {
+	struct input_dev *dev;
+	struct serio *serio;
+	int idx;
+	unsigned char csum;
+	unsigned char data[5];
+	char phys[32];
+};
+
+static irqreturn_t touchit213_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct touchit213 *touchit213 = serio_get_drvdata(serio);
+	struct input_dev *dev = touchit213->dev;
+
+	touchit213->data[touchit213->idx] = data;
+
+	switch (touchit213->idx++) {
+	case 0:
+		if ((touchit213->data[0] & T213_FORMAT_STATUS_MASK) !=
+				T213_FORMAT_STATUS_BYTE) {
+			pr_debug("unsynchronized data: 0x%02x\n", data);
+			touchit213->idx = 0;
+		}
+		break;
+
+	case 4:
+		touchit213->idx = 0;
+		input_report_abs(dev, ABS_X,
+			(touchit213->data[1] << 7) | touchit213->data[2]);
+		input_report_abs(dev, ABS_Y,
+			(touchit213->data[3] << 7) | touchit213->data[4]);
+		input_report_key(dev, BTN_TOUCH,
+			touchit213->data[0] & T213_FORMAT_TOUCH_BIT);
+		input_sync(dev);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * touchit213_disconnect() is the opposite of touchit213_connect()
+ */
+
+static void touchit213_disconnect(struct serio *serio)
+{
+	struct touchit213 *touchit213 = serio_get_drvdata(serio);
+
+	input_get_device(touchit213->dev);
+	input_unregister_device(touchit213->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_put_device(touchit213->dev);
+	kfree(touchit213);
+}
+
+/*
+ * touchit213_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchright protocol and registers it as
+ * an input device.
+ */
+
+static int touchit213_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct touchit213 *touchit213;
+	struct input_dev *input_dev;
+	int err;
+
+	touchit213 = kzalloc(sizeof(struct touchit213), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!touchit213 || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	touchit213->serio = serio;
+	touchit213->dev = input_dev;
+	snprintf(touchit213->phys, sizeof(touchit213->phys),
+		 "%s/input0", serio->phys);
+
+	input_dev->name = "Sahara Touch-iT213 Serial TouchScreen";
+	input_dev->phys = touchit213->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_TOUCHIT213;
+	input_dev->id.product = 0;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(touchit213->dev, ABS_X,
+			     T213_MIN_XC, T213_MAX_XC, 0, 0);
+	input_set_abs_params(touchit213->dev, ABS_Y,
+			     T213_MIN_YC, T213_MAX_YC, 0, 0);
+
+	serio_set_drvdata(serio, touchit213);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(touchit213->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(touchit213);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id touchit213_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_TOUCHIT213,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, touchit213_serio_ids);
+
+static struct serio_driver touchit213_drv = {
+	.driver		= {
+		.name	= "touchit213",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= touchit213_serio_ids,
+	.interrupt	= touchit213_interrupt,
+	.connect	= touchit213_connect,
+	.disconnect	= touchit213_disconnect,
+};
+
+module_serio_driver(touchit213_drv);
diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c
new file mode 100644
index 0000000..4000e52
--- /dev/null
+++ b/drivers/input/touchscreen/touchright.c
@@ -0,0 +1,178 @@
+/*
+ * Touchright serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Touchright serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define TR_FORMAT_TOUCH_BIT	0x01
+#define TR_FORMAT_STATUS_BYTE	0x40
+#define TR_FORMAT_STATUS_MASK	~TR_FORMAT_TOUCH_BIT
+
+#define TR_LENGTH 5
+
+#define TR_MIN_XC 0
+#define TR_MAX_XC 0x1ff
+#define TR_MIN_YC 0
+#define TR_MAX_YC 0x1ff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct tr {
+	struct input_dev *dev;
+	struct serio *serio;
+	int idx;
+	unsigned char data[TR_LENGTH];
+	char phys[32];
+};
+
+static irqreturn_t tr_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct tr *tr = serio_get_drvdata(serio);
+	struct input_dev *dev = tr->dev;
+
+	tr->data[tr->idx] = data;
+
+	if ((tr->data[0] & TR_FORMAT_STATUS_MASK) == TR_FORMAT_STATUS_BYTE) {
+		if (++tr->idx == TR_LENGTH) {
+			input_report_abs(dev, ABS_X,
+				(tr->data[1] << 5) | (tr->data[2] >> 1));
+			input_report_abs(dev, ABS_Y,
+				(tr->data[3] << 5) | (tr->data[4] >> 1));
+			input_report_key(dev, BTN_TOUCH,
+				tr->data[0] & TR_FORMAT_TOUCH_BIT);
+			input_sync(dev);
+			tr->idx = 0;
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * tr_disconnect() is the opposite of tr_connect()
+ */
+
+static void tr_disconnect(struct serio *serio)
+{
+	struct tr *tr = serio_get_drvdata(serio);
+
+	input_get_device(tr->dev);
+	input_unregister_device(tr->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_put_device(tr->dev);
+	kfree(tr);
+}
+
+/*
+ * tr_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchright protocol and registers it as
+ * an input device.
+ */
+
+static int tr_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct tr *tr;
+	struct input_dev *input_dev;
+	int err;
+
+	tr = kzalloc(sizeof(struct tr), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!tr || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	tr->serio = serio;
+	tr->dev = input_dev;
+	snprintf(tr->phys, sizeof(tr->phys), "%s/input0", serio->phys);
+
+	input_dev->name = "Touchright Serial TouchScreen";
+	input_dev->phys = tr->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_TOUCHRIGHT;
+	input_dev->id.product = 0;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(tr->dev, ABS_X, TR_MIN_XC, TR_MAX_XC, 0, 0);
+	input_set_abs_params(tr->dev, ABS_Y, TR_MIN_YC, TR_MAX_YC, 0, 0);
+
+	serio_set_drvdata(serio, tr);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(tr->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(tr);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id tr_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_TOUCHRIGHT,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, tr_serio_ids);
+
+static struct serio_driver tr_drv = {
+	.driver		= {
+		.name	= "touchright",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= tr_serio_ids,
+	.interrupt	= tr_interrupt,
+	.connect	= tr_connect,
+	.disconnect	= tr_disconnect,
+};
+
+module_serio_driver(tr_drv);
diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c
new file mode 100644
index 0000000..ba90f44
--- /dev/null
+++ b/drivers/input/touchscreen/touchwin.c
@@ -0,0 +1,185 @@
+/*
+ * Touchwindow serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+/*
+ * 2005/02/19 Rick Koch:
+ *   The Touchwindow I used is made by Edmark Corp. and
+ *   constantly outputs a stream of 0's unless it is touched.
+ *   It then outputs 3 bytes: X, Y, and a copy of Y.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"Touchwindow serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define TW_LENGTH 3
+
+#define TW_MIN_XC 0
+#define TW_MAX_XC 0xff
+#define TW_MIN_YC 0
+#define TW_MAX_YC 0xff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct tw {
+	struct input_dev *dev;
+	struct serio *serio;
+	int idx;
+	int touched;
+	unsigned char data[TW_LENGTH];
+	char phys[32];
+};
+
+static irqreturn_t tw_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct tw *tw = serio_get_drvdata(serio);
+	struct input_dev *dev = tw->dev;
+
+	if (data) {		/* touch */
+		tw->touched = 1;
+		tw->data[tw->idx++] = data;
+		/* verify length and that the two Y's are the same */
+		if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) {
+			input_report_abs(dev, ABS_X, tw->data[0]);
+			input_report_abs(dev, ABS_Y, tw->data[1]);
+			input_report_key(dev, BTN_TOUCH, 1);
+			input_sync(dev);
+			tw->idx = 0;
+		}
+	} else if (tw->touched) {	/* untouch */
+		input_report_key(dev, BTN_TOUCH, 0);
+		input_sync(dev);
+		tw->idx = 0;
+		tw->touched = 0;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * tw_disconnect() is the opposite of tw_connect()
+ */
+
+static void tw_disconnect(struct serio *serio)
+{
+	struct tw *tw = serio_get_drvdata(serio);
+
+	input_get_device(tw->dev);
+	input_unregister_device(tw->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	input_put_device(tw->dev);
+	kfree(tw);
+}
+
+/*
+ * tw_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchwin protocol and registers it as
+ * an input device.
+ */
+
+static int tw_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct tw *tw;
+	struct input_dev *input_dev;
+	int err;
+
+	tw = kzalloc(sizeof(struct tw), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!tw || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	tw->serio = serio;
+	tw->dev = input_dev;
+	snprintf(tw->phys, sizeof(tw->phys), "%s/input0", serio->phys);
+
+	input_dev->name = "Touchwindow Serial TouchScreen";
+	input_dev->phys = tw->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_TOUCHWIN;
+	input_dev->id.product = 0;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(tw->dev, ABS_X, TW_MIN_XC, TW_MAX_XC, 0, 0);
+	input_set_abs_params(tw->dev, ABS_Y, TW_MIN_YC, TW_MAX_YC, 0, 0);
+
+	serio_set_drvdata(serio, tw);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = input_register_device(tw->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
+	kfree(tw);
+	return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id tw_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_TOUCHWIN,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, tw_serio_ids);
+
+static struct serio_driver tw_drv = {
+	.driver		= {
+		.name	= "touchwin",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= tw_serio_ids,
+	.interrupt	= tw_interrupt,
+	.connect	= tw_connect,
+	.disconnect	= tw_disconnect,
+};
+
+module_serio_driver(tw_drv);
diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c
new file mode 100644
index 0000000..a340bfc
--- /dev/null
+++ b/drivers/input/touchscreen/tps6507x-ts.c
@@ -0,0 +1,319 @@
+/*
+ * Touchscreen driver for the tps6507x chip.
+ *
+ * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com)
+ *
+ * Credits:
+ *
+ *    Using code from tsc2007, MtekVision Co., Ltd.
+ *
+ * For licencing details see kernel-base/COPYING
+ *
+ * TPS65070, TPS65073, TPS650731, and TPS650732 support
+ * 10 bit touch screen interface.
+ */
+
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/tps6507x.h>
+#include <linux/input/tps6507x-ts.h>
+#include <linux/delay.h>
+
+#define TSC_DEFAULT_POLL_PERIOD 30 /* ms */
+#define TPS_DEFAULT_MIN_PRESSURE 0x30
+#define MAX_10BIT ((1 << 10) - 1)
+
+#define	TPS6507X_ADCONFIG_CONVERT_TS (TPS6507X_ADCONFIG_AD_ENABLE | \
+					 TPS6507X_ADCONFIG_START_CONVERSION | \
+					 TPS6507X_ADCONFIG_INPUT_REAL_TSC)
+#define	TPS6507X_ADCONFIG_POWER_DOWN_TS (TPS6507X_ADCONFIG_INPUT_REAL_TSC)
+
+struct ts_event {
+	u16	x;
+	u16	y;
+	u16	pressure;
+};
+
+struct tps6507x_ts {
+	struct device		*dev;
+	struct input_polled_dev	*poll_dev;
+	struct tps6507x_dev	*mfd;
+	char			phys[32];
+	struct ts_event		tc;
+	u16			min_pressure;
+	bool			pendown;
+};
+
+static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data)
+{
+	return tsc->mfd->read_dev(tsc->mfd, reg, 1, data);
+}
+
+static int tps6507x_write_u8(struct tps6507x_ts *tsc, u8 reg, u8 data)
+{
+	return tsc->mfd->write_dev(tsc->mfd, reg, 1, &data);
+}
+
+static s32 tps6507x_adc_conversion(struct tps6507x_ts *tsc,
+				   u8 tsc_mode, u16 *value)
+{
+	s32 ret;
+	u8 adc_status;
+	u8 result;
+
+	/* Route input signal to A/D converter */
+
+	ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, tsc_mode);
+	if (ret) {
+		dev_err(tsc->dev, "TSC mode read failed\n");
+		goto err;
+	}
+
+	/* Start A/D conversion */
+
+	ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG,
+				TPS6507X_ADCONFIG_CONVERT_TS);
+	if (ret) {
+		dev_err(tsc->dev, "ADC config write failed\n");
+		return ret;
+	}
+
+	do {
+		ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADCONFIG,
+				       &adc_status);
+		if (ret) {
+			dev_err(tsc->dev, "ADC config read failed\n");
+			goto err;
+		}
+	} while (adc_status & TPS6507X_ADCONFIG_START_CONVERSION);
+
+	ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_2, &result);
+	if (ret) {
+		dev_err(tsc->dev, "ADC result 2 read failed\n");
+		goto err;
+	}
+
+	*value = (result & TPS6507X_REG_ADRESULT_2_MASK) << 8;
+
+	ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_1, &result);
+	if (ret) {
+		dev_err(tsc->dev, "ADC result 1 read failed\n");
+		goto err;
+	}
+
+	*value |= result;
+
+	dev_dbg(tsc->dev, "TSC channel %d = 0x%X\n", tsc_mode, *value);
+
+err:
+	return ret;
+}
+
+/* Need to call tps6507x_adc_standby() after using A/D converter for the
+ * touch screen interrupt to work properly.
+ */
+
+static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc)
+{
+	s32 ret;
+	s32 loops = 0;
+	u8 val;
+
+	ret = tps6507x_write_u8(tsc,  TPS6507X_REG_ADCONFIG,
+				TPS6507X_ADCONFIG_INPUT_TSC);
+	if (ret)
+		return ret;
+
+	ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE,
+				TPS6507X_TSCMODE_STANDBY);
+	if (ret)
+		return ret;
+
+	ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
+	if (ret)
+		return ret;
+
+	while (val & TPS6507X_REG_TSC_INT) {
+		mdelay(10);
+		ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
+		if (ret)
+			return ret;
+		loops++;
+	}
+
+	return ret;
+}
+
+static void tps6507x_ts_poll(struct input_polled_dev *poll_dev)
+{
+	struct tps6507x_ts *tsc = poll_dev->private;
+	struct input_dev *input_dev = poll_dev->input;
+	bool pendown;
+	s32 ret;
+
+	ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE,
+				      &tsc->tc.pressure);
+	if (ret)
+		goto done;
+
+	pendown = tsc->tc.pressure > tsc->min_pressure;
+
+	if (unlikely(!pendown && tsc->pendown)) {
+		dev_dbg(tsc->dev, "UP\n");
+		input_report_key(input_dev, BTN_TOUCH, 0);
+		input_report_abs(input_dev, ABS_PRESSURE, 0);
+		input_sync(input_dev);
+		tsc->pendown = false;
+	}
+
+	if (pendown) {
+
+		if (!tsc->pendown) {
+			dev_dbg(tsc->dev, "DOWN\n");
+			input_report_key(input_dev, BTN_TOUCH, 1);
+		} else
+			dev_dbg(tsc->dev, "still down\n");
+
+		ret =  tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_X_POSITION,
+					       &tsc->tc.x);
+		if (ret)
+			goto done;
+
+		ret =  tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_Y_POSITION,
+					       &tsc->tc.y);
+		if (ret)
+			goto done;
+
+		input_report_abs(input_dev, ABS_X, tsc->tc.x);
+		input_report_abs(input_dev, ABS_Y, tsc->tc.y);
+		input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure);
+		input_sync(input_dev);
+		tsc->pendown = true;
+	}
+
+done:
+	tps6507x_adc_standby(tsc);
+}
+
+static int tps6507x_ts_probe(struct platform_device *pdev)
+{
+	struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
+	const struct tps6507x_board *tps_board;
+	const struct touchscreen_init_data *init_data;
+	struct tps6507x_ts *tsc;
+	struct input_polled_dev *poll_dev;
+	struct input_dev *input_dev;
+	int error;
+
+	/*
+	 * tps_board points to pmic related constants
+	 * coming from the board-evm file.
+	 */
+	tps_board = dev_get_platdata(tps6507x_dev->dev);
+	if (!tps_board) {
+		dev_err(tps6507x_dev->dev,
+			"Could not find tps6507x platform data\n");
+		return -ENODEV;
+	}
+
+	/*
+	 * init_data points to array of regulator_init structures
+	 * coming from the board-evm file.
+	 */
+	init_data = tps_board->tps6507x_ts_init_data;
+
+	tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL);
+	if (!tsc) {
+		dev_err(tps6507x_dev->dev, "failed to allocate driver data\n");
+		return -ENOMEM;
+	}
+
+	tsc->mfd = tps6507x_dev;
+	tsc->dev = tps6507x_dev->dev;
+	tsc->min_pressure = init_data ?
+			init_data->min_pressure : TPS_DEFAULT_MIN_PRESSURE;
+
+	snprintf(tsc->phys, sizeof(tsc->phys),
+		 "%s/input0", dev_name(tsc->dev));
+
+	poll_dev = input_allocate_polled_device();
+	if (!poll_dev) {
+		dev_err(tsc->dev, "Failed to allocate polled input device.\n");
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	tsc->poll_dev = poll_dev;
+
+	poll_dev->private = tsc;
+	poll_dev->poll = tps6507x_ts_poll;
+	poll_dev->poll_interval = init_data ?
+			init_data->poll_period : TSC_DEFAULT_POLL_PERIOD;
+
+	input_dev = poll_dev->input;
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0);
+
+	input_dev->name = "TPS6507x Touchscreen";
+	input_dev->phys = tsc->phys;
+	input_dev->dev.parent = tsc->dev;
+	input_dev->id.bustype = BUS_I2C;
+	if (init_data) {
+		input_dev->id.vendor = init_data->vendor;
+		input_dev->id.product = init_data->product;
+		input_dev->id.version = init_data->version;
+	}
+
+	error = tps6507x_adc_standby(tsc);
+	if (error)
+		goto err_free_polled_dev;
+
+	error = input_register_polled_device(poll_dev);
+	if (error)
+		goto err_free_polled_dev;
+
+	platform_set_drvdata(pdev, tsc);
+
+	return 0;
+
+err_free_polled_dev:
+	input_free_polled_device(poll_dev);
+err_free_mem:
+	kfree(tsc);
+	return error;
+}
+
+static int tps6507x_ts_remove(struct platform_device *pdev)
+{
+	struct tps6507x_ts *tsc = platform_get_drvdata(pdev);
+	struct input_polled_dev *poll_dev = tsc->poll_dev;
+
+	input_unregister_polled_device(poll_dev);
+	input_free_polled_device(poll_dev);
+
+	kfree(tsc);
+
+	return 0;
+}
+
+static struct platform_driver tps6507x_ts_driver = {
+	.driver = {
+		.name = "tps6507x-ts",
+	},
+	.probe = tps6507x_ts_probe,
+	.remove = tps6507x_ts_remove,
+};
+module_platform_driver(tps6507x_ts_driver);
+
+MODULE_AUTHOR("Todd Fischer <todd.fischer@ridgerun.com>");
+MODULE_DESCRIPTION("TPS6507x - TouchScreen driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps6507x-ts");
diff --git a/drivers/input/touchscreen/tsc2004.c b/drivers/input/touchscreen/tsc2004.c
new file mode 100644
index 0000000..6fe55d5
--- /dev/null
+++ b/drivers/input/touchscreen/tsc2004.c
@@ -0,0 +1,88 @@
+/*
+ * TSC2004 touchscreen driver
+ *
+ * Copyright (C) 2015 QWERTY Embedded Design
+ * Copyright (C) 2015 EMAC Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include "tsc200x-core.h"
+
+static const struct input_id tsc2004_input_id = {
+	.bustype = BUS_I2C,
+	.product = 2004,
+};
+
+static int tsc2004_cmd(struct device *dev, u8 cmd)
+{
+	u8 tx = TSC200X_CMD | TSC200X_CMD_12BIT | cmd;
+	s32 data;
+	struct i2c_client *i2c = to_i2c_client(dev);
+
+	data = i2c_smbus_write_byte(i2c, tx);
+	if (data < 0) {
+		dev_err(dev, "%s: failed, command: %x i2c error: %d\n",
+			__func__, cmd, data);
+		return data;
+	}
+
+	return 0;
+}
+
+static int tsc2004_probe(struct i2c_client *i2c,
+			 const struct i2c_device_id *id)
+
+{
+	return tsc200x_probe(&i2c->dev, i2c->irq, &tsc2004_input_id,
+			     devm_regmap_init_i2c(i2c, &tsc200x_regmap_config),
+			     tsc2004_cmd);
+}
+
+static int tsc2004_remove(struct i2c_client *i2c)
+{
+	return tsc200x_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id tsc2004_idtable[] = {
+	{ "tsc2004", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tsc2004_idtable);
+
+#ifdef CONFIG_OF
+static const struct of_device_id tsc2004_of_match[] = {
+	{ .compatible = "ti,tsc2004" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tsc2004_of_match);
+#endif
+
+static struct i2c_driver tsc2004_driver = {
+	.driver = {
+		.name   = "tsc2004",
+		.of_match_table = of_match_ptr(tsc2004_of_match),
+		.pm     = &tsc200x_pm_ops,
+	},
+	.id_table       = tsc2004_idtable,
+	.probe          = tsc2004_probe,
+	.remove         = tsc2004_remove,
+};
+module_i2c_driver(tsc2004_driver);
+
+MODULE_AUTHOR("Michael Welling <mwelling@ieee.org>");
+MODULE_DESCRIPTION("TSC2004 Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c
new file mode 100644
index 0000000..f2c5f0e
--- /dev/null
+++ b/drivers/input/touchscreen/tsc2005.c
@@ -0,0 +1,93 @@
+/*
+ * TSC2005 touchscreen driver
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2015 QWERTY Embedded Design
+ * Copyright (C) 2015 EMAC Inc.
+ *
+ * Based on original tsc2005.c by Lauri Leukkunen <lauri.leukkunen@nokia.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include "tsc200x-core.h"
+
+static const struct input_id tsc2005_input_id = {
+	.bustype = BUS_SPI,
+	.product = 2005,
+};
+
+static int tsc2005_cmd(struct device *dev, u8 cmd)
+{
+	u8 tx = TSC200X_CMD | TSC200X_CMD_12BIT | cmd;
+	struct spi_transfer xfer = {
+		.tx_buf         = &tx,
+		.len            = 1,
+		.bits_per_word  = 8,
+	};
+	struct spi_message msg;
+	struct spi_device *spi = to_spi_device(dev);
+	int error;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+
+	error = spi_sync(spi, &msg);
+	if (error) {
+		dev_err(dev, "%s: failed, command: %x, spi error: %d\n",
+			__func__, cmd, error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int tsc2005_probe(struct spi_device *spi)
+{
+	int error;
+
+	spi->mode = SPI_MODE_0;
+	spi->bits_per_word = 8;
+	if (!spi->max_speed_hz)
+		spi->max_speed_hz = TSC2005_SPI_MAX_SPEED_HZ;
+
+	error = spi_setup(spi);
+	if (error)
+		return error;
+
+	return tsc200x_probe(&spi->dev, spi->irq, &tsc2005_input_id,
+			     devm_regmap_init_spi(spi, &tsc200x_regmap_config),
+			     tsc2005_cmd);
+}
+
+static int tsc2005_remove(struct spi_device *spi)
+{
+	return tsc200x_remove(&spi->dev);
+}
+
+static struct spi_driver tsc2005_driver = {
+	.driver	= {
+		.name	= "tsc2005",
+		.pm	= &tsc200x_pm_ops,
+	},
+	.probe	= tsc2005_probe,
+	.remove	= tsc2005_remove,
+};
+module_spi_driver(tsc2005_driver);
+
+MODULE_AUTHOR("Michael Welling <mwelling@ieee.org>");
+MODULE_DESCRIPTION("TSC2005 Touchscreen Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:tsc2005");
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
new file mode 100644
index 0000000..5d0cd51
--- /dev/null
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -0,0 +1,496 @@
+/*
+ * drivers/input/touchscreen/tsc2007.c
+ *
+ * Copyright (c) 2008 MtekVision Co., Ltd.
+ *	Kwangwoo Lee <kwlee@mtekvision.com>
+ *
+ * Using code from:
+ *  - ads7846.c
+ *	Copyright (c) 2005 David Brownell
+ *	Copyright (c) 2006 Nokia Corporation
+ *  - corgi_ts.c
+ *	Copyright (C) 2004-2005 Richard Purdie
+ *  - omap_ts.[hc], ads7846.h, ts_osk.c
+ *	Copyright (C) 2002 MontaVista Software
+ *	Copyright (C) 2004 Texas Instruments
+ *	Copyright (C) 2005 Dirk Behme
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/i2c/tsc2007.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#define TSC2007_MEASURE_TEMP0		(0x0 << 4)
+#define TSC2007_MEASURE_AUX		(0x2 << 4)
+#define TSC2007_MEASURE_TEMP1		(0x4 << 4)
+#define TSC2007_ACTIVATE_XN		(0x8 << 4)
+#define TSC2007_ACTIVATE_YN		(0x9 << 4)
+#define TSC2007_ACTIVATE_YP_XN		(0xa << 4)
+#define TSC2007_SETUP			(0xb << 4)
+#define TSC2007_MEASURE_X		(0xc << 4)
+#define TSC2007_MEASURE_Y		(0xd << 4)
+#define TSC2007_MEASURE_Z1		(0xe << 4)
+#define TSC2007_MEASURE_Z2		(0xf << 4)
+
+#define TSC2007_POWER_OFF_IRQ_EN	(0x0 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS0		(0x1 << 2)
+#define TSC2007_ADC_OFF_IRQ_EN		(0x2 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS1		(0x3 << 2)
+
+#define TSC2007_12BIT			(0x0 << 1)
+#define TSC2007_8BIT			(0x1 << 1)
+
+#define	MAX_12BIT			((1 << 12) - 1)
+
+#define ADC_ON_12BIT	(TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
+
+#define READ_Y		(ADC_ON_12BIT | TSC2007_MEASURE_Y)
+#define READ_Z1		(ADC_ON_12BIT | TSC2007_MEASURE_Z1)
+#define READ_Z2		(ADC_ON_12BIT | TSC2007_MEASURE_Z2)
+#define READ_X		(ADC_ON_12BIT | TSC2007_MEASURE_X)
+#define PWRDOWN		(TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
+
+struct ts_event {
+	u16	x;
+	u16	y;
+	u16	z1, z2;
+};
+
+struct tsc2007 {
+	struct input_dev	*input;
+	char			phys[32];
+
+	struct i2c_client	*client;
+
+	u16			model;
+	u16			x_plate_ohms;
+	u16			max_rt;
+	unsigned long		poll_period; /* in jiffies */
+	int			fuzzx;
+	int			fuzzy;
+	int			fuzzz;
+
+	unsigned		gpio;
+	int			irq;
+
+	wait_queue_head_t	wait;
+	bool			stopped;
+
+	int			(*get_pendown_state)(struct device *);
+	void			(*clear_penirq)(void);
+};
+
+static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
+{
+	s32 data;
+	u16 val;
+
+	data = i2c_smbus_read_word_data(tsc->client, cmd);
+	if (data < 0) {
+		dev_err(&tsc->client->dev, "i2c io error: %d\n", data);
+		return data;
+	}
+
+	/* The protocol and raw data format from i2c interface:
+	 * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
+	 * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
+	 */
+	val = swab16(data) >> 4;
+
+	dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);
+
+	return val;
+}
+
+static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
+{
+	/* y- still on; turn on only y+ (and ADC) */
+	tc->y = tsc2007_xfer(tsc, READ_Y);
+
+	/* turn y- off, x+ on, then leave in lowpower */
+	tc->x = tsc2007_xfer(tsc, READ_X);
+
+	/* turn y+ off, x- on; we'll use formula #1 */
+	tc->z1 = tsc2007_xfer(tsc, READ_Z1);
+	tc->z2 = tsc2007_xfer(tsc, READ_Z2);
+
+	/* Prepare for next touch reading - power down ADC, enable PENIRQ */
+	tsc2007_xfer(tsc, PWRDOWN);
+}
+
+static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
+{
+	u32 rt = 0;
+
+	/* range filtering */
+	if (tc->x == MAX_12BIT)
+		tc->x = 0;
+
+	if (likely(tc->x && tc->z1)) {
+		/* compute touch pressure resistance using equation #1 */
+		rt = tc->z2 - tc->z1;
+		rt *= tc->x;
+		rt *= tsc->x_plate_ohms;
+		rt /= tc->z1;
+		rt = (rt + 2047) >> 12;
+	}
+
+	return rt;
+}
+
+static bool tsc2007_is_pen_down(struct tsc2007 *ts)
+{
+	/*
+	 * NOTE: We can't rely on the pressure to determine the pen down
+	 * state, even though this controller has a pressure sensor.
+	 * The pressure value can fluctuate for quite a while after
+	 * lifting the pen and in some cases may not even settle at the
+	 * expected value.
+	 *
+	 * The only safe way to check for the pen up condition is in the
+	 * work function by reading the pen signal state (it's a GPIO
+	 * and IRQ). Unfortunately such callback is not always available,
+	 * in that case we assume that the pen is down and expect caller
+	 * to fall back on the pressure reading.
+	 */
+
+	if (!ts->get_pendown_state)
+		return true;
+
+	return ts->get_pendown_state(&ts->client->dev);
+}
+
+static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
+{
+	struct tsc2007 *ts = handle;
+	struct input_dev *input = ts->input;
+	struct ts_event tc;
+	u32 rt;
+
+	while (!ts->stopped && tsc2007_is_pen_down(ts)) {
+
+		/* pen is down, continue with the measurement */
+		tsc2007_read_values(ts, &tc);
+
+		rt = tsc2007_calculate_pressure(ts, &tc);
+
+		if (!rt && !ts->get_pendown_state) {
+			/*
+			 * If pressure reported is 0 and we don't have
+			 * callback to check pendown state, we have to
+			 * assume that pen was lifted up.
+			 */
+			break;
+		}
+
+		if (rt <= ts->max_rt) {
+			dev_dbg(&ts->client->dev,
+				"DOWN point(%4d,%4d), pressure (%4u)\n",
+				tc.x, tc.y, rt);
+
+			input_report_key(input, BTN_TOUCH, 1);
+			input_report_abs(input, ABS_X, tc.x);
+			input_report_abs(input, ABS_Y, tc.y);
+			input_report_abs(input, ABS_PRESSURE, rt);
+
+			input_sync(input);
+
+		} else {
+			/*
+			 * Sample found inconsistent by debouncing or pressure is
+			 * beyond the maximum. Don't report it to user space,
+			 * repeat at least once more the measurement.
+			 */
+			dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
+		}
+
+		wait_event_timeout(ts->wait, ts->stopped, ts->poll_period);
+	}
+
+	dev_dbg(&ts->client->dev, "UP\n");
+
+	input_report_key(input, BTN_TOUCH, 0);
+	input_report_abs(input, ABS_PRESSURE, 0);
+	input_sync(input);
+
+	if (ts->clear_penirq)
+		ts->clear_penirq();
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
+{
+	struct tsc2007 *ts = handle;
+
+	if (tsc2007_is_pen_down(ts))
+		return IRQ_WAKE_THREAD;
+
+	if (ts->clear_penirq)
+		ts->clear_penirq();
+
+	return IRQ_HANDLED;
+}
+
+static void tsc2007_stop(struct tsc2007 *ts)
+{
+	ts->stopped = true;
+	mb();
+	wake_up(&ts->wait);
+
+	disable_irq(ts->irq);
+}
+
+static int tsc2007_open(struct input_dev *input_dev)
+{
+	struct tsc2007 *ts = input_get_drvdata(input_dev);
+	int err;
+
+	ts->stopped = false;
+	mb();
+
+	enable_irq(ts->irq);
+
+	/* Prepare for touch readings - power down ADC and enable PENIRQ */
+	err = tsc2007_xfer(ts, PWRDOWN);
+	if (err < 0) {
+		tsc2007_stop(ts);
+		return err;
+	}
+
+	return 0;
+}
+
+static void tsc2007_close(struct input_dev *input_dev)
+{
+	struct tsc2007 *ts = input_get_drvdata(input_dev);
+
+	tsc2007_stop(ts);
+}
+
+#ifdef CONFIG_OF
+static int tsc2007_get_pendown_state_gpio(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct tsc2007 *ts = i2c_get_clientdata(client);
+
+	return !gpio_get_value(ts->gpio);
+}
+
+static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
+{
+	struct device_node *np = client->dev.of_node;
+	u32 val32;
+	u64 val64;
+
+	if (!np) {
+		dev_err(&client->dev, "missing device tree data\n");
+		return -EINVAL;
+	}
+
+	if (!of_property_read_u32(np, "ti,max-rt", &val32))
+		ts->max_rt = val32;
+	else
+		ts->max_rt = MAX_12BIT;
+
+	if (!of_property_read_u32(np, "ti,fuzzx", &val32))
+		ts->fuzzx = val32;
+
+	if (!of_property_read_u32(np, "ti,fuzzy", &val32))
+		ts->fuzzy = val32;
+
+	if (!of_property_read_u32(np, "ti,fuzzz", &val32))
+		ts->fuzzz = val32;
+
+	if (!of_property_read_u64(np, "ti,poll-period", &val64))
+		ts->poll_period = msecs_to_jiffies(val64);
+	else
+		ts->poll_period = msecs_to_jiffies(1);
+
+	if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
+		ts->x_plate_ohms = val32;
+	} else {
+		dev_err(&client->dev, "missing ti,x-plate-ohms devicetree property.");
+		return -EINVAL;
+	}
+
+	ts->gpio = of_get_gpio(np, 0);
+	if (gpio_is_valid(ts->gpio))
+		ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
+	else
+		dev_warn(&client->dev,
+			 "GPIO not specified in DT (of_get_gpio returned %d)\n",
+			 ts->gpio);
+
+	return 0;
+}
+#else
+static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
+{
+	dev_err(&client->dev, "platform data is required!\n");
+	return -EINVAL;
+}
+#endif
+
+static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
+			      const struct tsc2007_platform_data *pdata,
+			      const struct i2c_device_id *id)
+{
+	ts->model             = pdata->model;
+	ts->x_plate_ohms      = pdata->x_plate_ohms;
+	ts->max_rt            = pdata->max_rt ? : MAX_12BIT;
+	ts->poll_period       = msecs_to_jiffies(pdata->poll_period ? : 1);
+	ts->get_pendown_state = pdata->get_pendown_state;
+	ts->clear_penirq      = pdata->clear_penirq;
+	ts->fuzzx             = pdata->fuzzx;
+	ts->fuzzy             = pdata->fuzzy;
+	ts->fuzzz             = pdata->fuzzz;
+
+	if (pdata->x_plate_ohms == 0) {
+		dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void tsc2007_call_exit_platform_hw(void *data)
+{
+	struct device *dev = data;
+	const struct tsc2007_platform_data *pdata = dev_get_platdata(dev);
+
+	pdata->exit_platform_hw();
+}
+
+static int tsc2007_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
+	struct tsc2007 *ts;
+	struct input_dev *input_dev;
+	int err;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_READ_WORD_DATA))
+		return -EIO;
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	if (pdata)
+		err = tsc2007_probe_pdev(client, ts, pdata, id);
+	else
+		err = tsc2007_probe_dt(client, ts);
+	if (err)
+		return err;
+
+	input_dev = devm_input_allocate_device(&client->dev);
+	if (!input_dev)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, ts);
+
+	ts->client = client;
+	ts->irq = client->irq;
+	ts->input = input_dev;
+	init_waitqueue_head(&ts->wait);
+
+	snprintf(ts->phys, sizeof(ts->phys),
+		 "%s/input0", dev_name(&client->dev));
+
+	input_dev->name = "TSC2007 Touchscreen";
+	input_dev->phys = ts->phys;
+	input_dev->id.bustype = BUS_I2C;
+
+	input_dev->open = tsc2007_open;
+	input_dev->close = tsc2007_close;
+
+	input_set_drvdata(input_dev, ts);
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, ts->fuzzx, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
+			     ts->fuzzz, 0);
+
+	if (pdata) {
+		if (pdata->exit_platform_hw) {
+			err = devm_add_action(&client->dev,
+					      tsc2007_call_exit_platform_hw,
+					      &client->dev);
+			if (err) {
+				dev_err(&client->dev,
+					"Failed to register exit_platform_hw action, %d\n",
+					err);
+				return err;
+			}
+		}
+
+		if (pdata->init_platform_hw)
+			pdata->init_platform_hw();
+	}
+
+	err = devm_request_threaded_irq(&client->dev, ts->irq,
+					tsc2007_hard_irq, tsc2007_soft_irq,
+					IRQF_ONESHOT,
+					client->dev.driver->name, ts);
+	if (err) {
+		dev_err(&client->dev, "Failed to request irq %d: %d\n",
+			ts->irq, err);
+		return err;
+	}
+
+	tsc2007_stop(ts);
+
+	err = input_register_device(input_dev);
+	if (err) {
+		dev_err(&client->dev,
+			"Failed to register input device: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static const struct i2c_device_id tsc2007_idtable[] = {
+	{ "tsc2007", 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
+
+#ifdef CONFIG_OF
+static const struct of_device_id tsc2007_of_match[] = {
+	{ .compatible = "ti,tsc2007" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tsc2007_of_match);
+#endif
+
+static struct i2c_driver tsc2007_driver = {
+	.driver = {
+		.name	= "tsc2007",
+		.of_match_table = of_match_ptr(tsc2007_of_match),
+	},
+	.id_table	= tsc2007_idtable,
+	.probe		= tsc2007_probe,
+};
+
+module_i2c_driver(tsc2007_driver);
+
+MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");
+MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tsc200x-core.c b/drivers/input/touchscreen/tsc200x-core.c
new file mode 100644
index 0000000..dfa7f1c
--- /dev/null
+++ b/drivers/input/touchscreen/tsc200x-core.c
@@ -0,0 +1,674 @@
+/*
+ * TSC2004/TSC2005 touchscreen driver core
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2015 QWERTY Embedded Design
+ * Copyright (C) 2015 EMAC Inc.
+ *
+ * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
+ * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/spi/tsc2005.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <linux/gpio/consumer.h>
+#include "tsc200x-core.h"
+
+/*
+ * The touchscreen interface operates as follows:
+ *
+ * 1) Pen is pressed against the touchscreen.
+ * 2) TSC200X performs AD conversion.
+ * 3) After the conversion is done TSC200X drives DAV line down.
+ * 4) GPIO IRQ is received and tsc200x_irq_thread() is scheduled.
+ * 5) tsc200x_irq_thread() queues up a transfer to fetch the x, y, z1, z2
+ *    values.
+ * 6) tsc200x_irq_thread() reports coordinates to input layer and sets up
+ *    tsc200x_penup_timer() to be called after TSC200X_PENUP_TIME_MS (40ms).
+ * 7) When the penup timer expires, there have not been touch or DAV interrupts
+ *    during the last 40ms which means the pen has been lifted.
+ *
+ * ESD recovery via a hardware reset is done if the TSC200X doesn't respond
+ * after a configurable period (in ms) of activity. If esd_timeout is 0, the
+ * watchdog is disabled.
+ */
+
+static const struct regmap_range tsc200x_writable_ranges[] = {
+	regmap_reg_range(TSC200X_REG_AUX_HIGH, TSC200X_REG_CFR2),
+};
+
+static const struct regmap_access_table tsc200x_writable_table = {
+	.yes_ranges = tsc200x_writable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(tsc200x_writable_ranges),
+};
+
+const struct regmap_config tsc200x_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 16,
+	.reg_stride = 0x08,
+	.max_register = 0x78,
+	.read_flag_mask = TSC200X_REG_READ,
+	.write_flag_mask = TSC200X_REG_PND0,
+	.wr_table = &tsc200x_writable_table,
+	.use_single_rw = true,
+};
+EXPORT_SYMBOL_GPL(tsc200x_regmap_config);
+
+struct tsc200x_data {
+	u16 x;
+	u16 y;
+	u16 z1;
+	u16 z2;
+} __packed;
+#define TSC200X_DATA_REGS 4
+
+struct tsc200x {
+	struct device           *dev;
+	struct regmap		*regmap;
+	__u16                   bustype;
+
+	struct input_dev	*idev;
+	char			phys[32];
+
+	struct mutex		mutex;
+
+	/* raw copy of previous x,y,z */
+	int			in_x;
+	int			in_y;
+	int                     in_z1;
+	int			in_z2;
+
+	spinlock_t		lock;
+	struct timer_list	penup_timer;
+
+	unsigned int		esd_timeout;
+	struct delayed_work	esd_work;
+	unsigned long		last_valid_interrupt;
+
+	unsigned int		x_plate_ohm;
+
+	bool			opened;
+	bool			suspended;
+
+	bool			pen_down;
+
+	struct regulator	*vio;
+
+	struct gpio_desc	*reset_gpio;
+	void			(*set_reset)(bool enable);
+	int			(*tsc200x_cmd)(struct device *dev, u8 cmd);
+	int			irq;
+};
+
+static void tsc200x_update_pen_state(struct tsc200x *ts,
+				     int x, int y, int pressure)
+{
+	if (pressure) {
+		input_report_abs(ts->idev, ABS_X, x);
+		input_report_abs(ts->idev, ABS_Y, y);
+		input_report_abs(ts->idev, ABS_PRESSURE, pressure);
+		if (!ts->pen_down) {
+			input_report_key(ts->idev, BTN_TOUCH, !!pressure);
+			ts->pen_down = true;
+		}
+	} else {
+		input_report_abs(ts->idev, ABS_PRESSURE, 0);
+		if (ts->pen_down) {
+			input_report_key(ts->idev, BTN_TOUCH, 0);
+			ts->pen_down = false;
+		}
+	}
+	input_sync(ts->idev);
+	dev_dbg(ts->dev, "point(%4d,%4d), pressure (%4d)\n", x, y,
+		pressure);
+}
+
+static irqreturn_t tsc200x_irq_thread(int irq, void *_ts)
+{
+	struct tsc200x *ts = _ts;
+	unsigned long flags;
+	unsigned int pressure;
+	struct tsc200x_data tsdata;
+	int error;
+
+	/* read the coordinates */
+	error = regmap_bulk_read(ts->regmap, TSC200X_REG_X, &tsdata,
+				 TSC200X_DATA_REGS);
+	if (unlikely(error))
+		goto out;
+
+	/* validate position */
+	if (unlikely(tsdata.x > MAX_12BIT || tsdata.y > MAX_12BIT))
+		goto out;
+
+	/* Skip reading if the pressure components are out of range */
+	if (unlikely(tsdata.z1 == 0 || tsdata.z2 > MAX_12BIT))
+		goto out;
+	if (unlikely(tsdata.z1 >= tsdata.z2))
+		goto out;
+
+       /*
+	* Skip point if this is a pen down with the exact same values as
+	* the value before pen-up - that implies SPI fed us stale data
+	*/
+	if (!ts->pen_down &&
+	    ts->in_x == tsdata.x && ts->in_y == tsdata.y &&
+	    ts->in_z1 == tsdata.z1 && ts->in_z2 == tsdata.z2) {
+		goto out;
+	}
+
+	/*
+	 * At this point we are happy we have a valid and useful reading.
+	 * Remember it for later comparisons. We may now begin downsampling.
+	 */
+	ts->in_x = tsdata.x;
+	ts->in_y = tsdata.y;
+	ts->in_z1 = tsdata.z1;
+	ts->in_z2 = tsdata.z2;
+
+	/* Compute touch pressure resistance using equation #1 */
+	pressure = tsdata.x * (tsdata.z2 - tsdata.z1) / tsdata.z1;
+	pressure = pressure * ts->x_plate_ohm / 4096;
+	if (unlikely(pressure > MAX_12BIT))
+		goto out;
+
+	spin_lock_irqsave(&ts->lock, flags);
+
+	tsc200x_update_pen_state(ts, tsdata.x, tsdata.y, pressure);
+	mod_timer(&ts->penup_timer,
+		  jiffies + msecs_to_jiffies(TSC200X_PENUP_TIME_MS));
+
+	spin_unlock_irqrestore(&ts->lock, flags);
+
+	ts->last_valid_interrupt = jiffies;
+out:
+	return IRQ_HANDLED;
+}
+
+static void tsc200x_penup_timer(unsigned long data)
+{
+	struct tsc200x *ts = (struct tsc200x *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ts->lock, flags);
+	tsc200x_update_pen_state(ts, 0, 0, 0);
+	spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+static void tsc200x_start_scan(struct tsc200x *ts)
+{
+	regmap_write(ts->regmap, TSC200X_REG_CFR0, TSC200X_CFR0_INITVALUE);
+	regmap_write(ts->regmap, TSC200X_REG_CFR1, TSC200X_CFR1_INITVALUE);
+	regmap_write(ts->regmap, TSC200X_REG_CFR2, TSC200X_CFR2_INITVALUE);
+	ts->tsc200x_cmd(ts->dev, TSC200X_CMD_NORMAL);
+}
+
+static void tsc200x_stop_scan(struct tsc200x *ts)
+{
+	ts->tsc200x_cmd(ts->dev, TSC200X_CMD_STOP);
+}
+
+static void tsc200x_set_reset(struct tsc200x *ts, bool enable)
+{
+	if (ts->reset_gpio)
+		gpiod_set_value_cansleep(ts->reset_gpio, enable);
+	else if (ts->set_reset)
+		ts->set_reset(enable);
+}
+
+/* must be called with ts->mutex held */
+static void __tsc200x_disable(struct tsc200x *ts)
+{
+	tsc200x_stop_scan(ts);
+
+	disable_irq(ts->irq);
+	del_timer_sync(&ts->penup_timer);
+
+	cancel_delayed_work_sync(&ts->esd_work);
+
+	enable_irq(ts->irq);
+}
+
+/* must be called with ts->mutex held */
+static void __tsc200x_enable(struct tsc200x *ts)
+{
+	tsc200x_start_scan(ts);
+
+	if (ts->esd_timeout && (ts->set_reset || ts->reset_gpio)) {
+		ts->last_valid_interrupt = jiffies;
+		schedule_delayed_work(&ts->esd_work,
+				round_jiffies_relative(
+					msecs_to_jiffies(ts->esd_timeout)));
+	}
+}
+
+static ssize_t tsc200x_selftest_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct tsc200x *ts = dev_get_drvdata(dev);
+	unsigned int temp_high;
+	unsigned int temp_high_orig;
+	unsigned int temp_high_test;
+	bool success = true;
+	int error;
+
+	mutex_lock(&ts->mutex);
+
+	/*
+	 * Test TSC200X communications via temp high register.
+	 */
+	__tsc200x_disable(ts);
+
+	error = regmap_read(ts->regmap, TSC200X_REG_TEMP_HIGH, &temp_high_orig);
+	if (error) {
+		dev_warn(dev, "selftest failed: read error %d\n", error);
+		success = false;
+		goto out;
+	}
+
+	temp_high_test = (temp_high_orig - 1) & MAX_12BIT;
+
+	error = regmap_write(ts->regmap, TSC200X_REG_TEMP_HIGH, temp_high_test);
+	if (error) {
+		dev_warn(dev, "selftest failed: write error %d\n", error);
+		success = false;
+		goto out;
+	}
+
+	error = regmap_read(ts->regmap, TSC200X_REG_TEMP_HIGH, &temp_high);
+	if (error) {
+		dev_warn(dev, "selftest failed: read error %d after write\n",
+			 error);
+		success = false;
+		goto out;
+	}
+
+	if (temp_high != temp_high_test) {
+		dev_warn(dev, "selftest failed: %d != %d\n",
+			 temp_high, temp_high_test);
+		success = false;
+	}
+
+	/* hardware reset */
+	tsc200x_set_reset(ts, false);
+	usleep_range(100, 500); /* only 10us required */
+	tsc200x_set_reset(ts, true);
+
+	if (!success)
+		goto out;
+
+	/* test that the reset really happened */
+	error = regmap_read(ts->regmap, TSC200X_REG_TEMP_HIGH, &temp_high);
+	if (error) {
+		dev_warn(dev, "selftest failed: read error %d after reset\n",
+			 error);
+		success = false;
+		goto out;
+	}
+
+	if (temp_high != temp_high_orig) {
+		dev_warn(dev, "selftest failed after reset: %d != %d\n",
+			 temp_high, temp_high_orig);
+		success = false;
+	}
+
+out:
+	__tsc200x_enable(ts);
+	mutex_unlock(&ts->mutex);
+
+	return sprintf(buf, "%d\n", success);
+}
+
+static DEVICE_ATTR(selftest, S_IRUGO, tsc200x_selftest_show, NULL);
+
+static struct attribute *tsc200x_attrs[] = {
+	&dev_attr_selftest.attr,
+	NULL
+};
+
+static umode_t tsc200x_attr_is_visible(struct kobject *kobj,
+				      struct attribute *attr, int n)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct tsc200x *ts = dev_get_drvdata(dev);
+	umode_t mode = attr->mode;
+
+	if (attr == &dev_attr_selftest.attr) {
+		if (!ts->set_reset && !ts->reset_gpio)
+			mode = 0;
+	}
+
+	return mode;
+}
+
+static const struct attribute_group tsc200x_attr_group = {
+	.is_visible	= tsc200x_attr_is_visible,
+	.attrs		= tsc200x_attrs,
+};
+
+static void tsc200x_esd_work(struct work_struct *work)
+{
+	struct tsc200x *ts = container_of(work, struct tsc200x, esd_work.work);
+	int error;
+	unsigned int r;
+
+	if (!mutex_trylock(&ts->mutex)) {
+		/*
+		 * If the mutex is taken, it means that disable or enable is in
+		 * progress. In that case just reschedule the work. If the work
+		 * is not needed, it will be canceled by disable.
+		 */
+		goto reschedule;
+	}
+
+	if (time_is_after_jiffies(ts->last_valid_interrupt +
+				  msecs_to_jiffies(ts->esd_timeout)))
+		goto out;
+
+	/* We should be able to read register without disabling interrupts. */
+	error = regmap_read(ts->regmap, TSC200X_REG_CFR0, &r);
+	if (!error &&
+	    !((r ^ TSC200X_CFR0_INITVALUE) & TSC200X_CFR0_RW_MASK)) {
+		goto out;
+	}
+
+	/*
+	 * If we could not read our known value from configuration register 0
+	 * then we should reset the controller as if from power-up and start
+	 * scanning again.
+	 */
+	dev_info(ts->dev, "TSC200X not responding - resetting\n");
+
+	disable_irq(ts->irq);
+	del_timer_sync(&ts->penup_timer);
+
+	tsc200x_update_pen_state(ts, 0, 0, 0);
+
+	tsc200x_set_reset(ts, false);
+	usleep_range(100, 500); /* only 10us required */
+	tsc200x_set_reset(ts, true);
+
+	enable_irq(ts->irq);
+	tsc200x_start_scan(ts);
+
+out:
+	mutex_unlock(&ts->mutex);
+reschedule:
+	/* re-arm the watchdog */
+	schedule_delayed_work(&ts->esd_work,
+			      round_jiffies_relative(
+					msecs_to_jiffies(ts->esd_timeout)));
+}
+
+static int tsc200x_open(struct input_dev *input)
+{
+	struct tsc200x *ts = input_get_drvdata(input);
+
+	mutex_lock(&ts->mutex);
+
+	if (!ts->suspended)
+		__tsc200x_enable(ts);
+
+	ts->opened = true;
+
+	mutex_unlock(&ts->mutex);
+
+	return 0;
+}
+
+static void tsc200x_close(struct input_dev *input)
+{
+	struct tsc200x *ts = input_get_drvdata(input);
+
+	mutex_lock(&ts->mutex);
+
+	if (!ts->suspended)
+		__tsc200x_disable(ts);
+
+	ts->opened = false;
+
+	mutex_unlock(&ts->mutex);
+}
+
+int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
+		  struct regmap *regmap,
+		  int (*tsc200x_cmd)(struct device *dev, u8 cmd))
+{
+	const struct tsc2005_platform_data *pdata = dev_get_platdata(dev);
+	struct device_node *np = dev->of_node;
+
+	struct tsc200x *ts;
+	struct input_dev *input_dev;
+	unsigned int max_x = MAX_12BIT;
+	unsigned int max_y = MAX_12BIT;
+	unsigned int max_p = MAX_12BIT;
+	unsigned int fudge_x = TSC200X_DEF_X_FUZZ;
+	unsigned int fudge_y = TSC200X_DEF_Y_FUZZ;
+	unsigned int fudge_p = TSC200X_DEF_P_FUZZ;
+	unsigned int x_plate_ohm = TSC200X_DEF_RESISTOR;
+	unsigned int esd_timeout;
+	int error;
+
+	if (!np && !pdata) {
+		dev_err(dev, "no platform data\n");
+		return -ENODEV;
+	}
+
+	if (irq <= 0) {
+		dev_err(dev, "no irq\n");
+		return -ENODEV;
+	}
+
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	if (!tsc200x_cmd) {
+		dev_err(dev, "no cmd function\n");
+		return -ENODEV;
+	}
+
+	if (pdata) {
+		fudge_x	= pdata->ts_x_fudge;
+		fudge_y	= pdata->ts_y_fudge;
+		fudge_p	= pdata->ts_pressure_fudge;
+		max_x	= pdata->ts_x_max;
+		max_y	= pdata->ts_y_max;
+		max_p	= pdata->ts_pressure_max;
+		x_plate_ohm = pdata->ts_x_plate_ohm;
+		esd_timeout = pdata->esd_timeout_ms;
+	} else {
+		x_plate_ohm = TSC200X_DEF_RESISTOR;
+		of_property_read_u32(np, "ti,x-plate-ohms", &x_plate_ohm);
+		esd_timeout = 0;
+		of_property_read_u32(np, "ti,esd-recovery-timeout-ms",
+								&esd_timeout);
+	}
+
+	ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	input_dev = devm_input_allocate_device(dev);
+	if (!input_dev)
+		return -ENOMEM;
+
+	ts->irq = irq;
+	ts->dev = dev;
+	ts->idev = input_dev;
+	ts->regmap = regmap;
+	ts->tsc200x_cmd = tsc200x_cmd;
+	ts->x_plate_ohm = x_plate_ohm;
+	ts->esd_timeout = esd_timeout;
+
+	ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(ts->reset_gpio)) {
+		error = PTR_ERR(ts->reset_gpio);
+		dev_err(dev, "error acquiring reset gpio: %d\n", error);
+		return error;
+	}
+
+	ts->vio = devm_regulator_get_optional(dev, "vio");
+	if (IS_ERR(ts->vio)) {
+		error = PTR_ERR(ts->vio);
+		dev_err(dev, "vio regulator missing (%d)", error);
+		return error;
+	}
+
+	if (!ts->reset_gpio && pdata)
+		ts->set_reset = pdata->set_reset;
+
+	mutex_init(&ts->mutex);
+
+	spin_lock_init(&ts->lock);
+	setup_timer(&ts->penup_timer, tsc200x_penup_timer, (unsigned long)ts);
+
+	INIT_DELAYED_WORK(&ts->esd_work, tsc200x_esd_work);
+
+	snprintf(ts->phys, sizeof(ts->phys),
+		 "%s/input-ts", dev_name(dev));
+
+	if (tsc_id->product == 2004) {
+		input_dev->name = "TSC200X touchscreen";
+	} else {
+		input_dev->name = devm_kasprintf(dev, GFP_KERNEL,
+						 "TSC%04d touchscreen",
+						 tsc_id->product);
+		if (!input_dev->name)
+			return -ENOMEM;
+	}
+
+	input_dev->phys = ts->phys;
+	input_dev->id = *tsc_id;
+	input_dev->dev.parent = dev;
+	input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_set_abs_params(input_dev, ABS_X, 0, max_x, fudge_x, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, max_y, fudge_y, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0);
+
+	if (np)
+		touchscreen_parse_properties(input_dev, false);
+
+	input_dev->open = tsc200x_open;
+	input_dev->close = tsc200x_close;
+
+	input_set_drvdata(input_dev, ts);
+
+	/* Ensure the touchscreen is off */
+	tsc200x_stop_scan(ts);
+
+	error = devm_request_threaded_irq(dev, irq, NULL,
+					  tsc200x_irq_thread,
+					  IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					  "tsc200x", ts);
+	if (error) {
+		dev_err(dev, "Failed to request irq, err: %d\n", error);
+		return error;
+	}
+
+	/* enable regulator for DT */
+	if (ts->vio) {
+		error = regulator_enable(ts->vio);
+		if (error)
+			return error;
+	}
+
+	dev_set_drvdata(dev, ts);
+	error = sysfs_create_group(&dev->kobj, &tsc200x_attr_group);
+	if (error) {
+		dev_err(dev,
+			"Failed to create sysfs attributes, err: %d\n", error);
+		goto disable_regulator;
+	}
+
+	error = input_register_device(ts->idev);
+	if (error) {
+		dev_err(dev,
+			"Failed to register input device, err: %d\n", error);
+		goto err_remove_sysfs;
+	}
+
+	irq_set_irq_wake(irq, 1);
+	return 0;
+
+err_remove_sysfs:
+	sysfs_remove_group(&dev->kobj, &tsc200x_attr_group);
+disable_regulator:
+	if (ts->vio)
+		regulator_disable(ts->vio);
+	return error;
+}
+EXPORT_SYMBOL_GPL(tsc200x_probe);
+
+int tsc200x_remove(struct device *dev)
+{
+	struct tsc200x *ts = dev_get_drvdata(dev);
+
+	sysfs_remove_group(&dev->kobj, &tsc200x_attr_group);
+
+	if (ts->vio)
+		regulator_disable(ts->vio);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tsc200x_remove);
+
+static int __maybe_unused tsc200x_suspend(struct device *dev)
+{
+	struct tsc200x *ts = dev_get_drvdata(dev);
+
+	mutex_lock(&ts->mutex);
+
+	if (!ts->suspended && ts->opened)
+		__tsc200x_disable(ts);
+
+	ts->suspended = true;
+
+	mutex_unlock(&ts->mutex);
+
+	return 0;
+}
+
+static int __maybe_unused tsc200x_resume(struct device *dev)
+{
+	struct tsc200x *ts = dev_get_drvdata(dev);
+
+	mutex_lock(&ts->mutex);
+
+	if (ts->suspended && ts->opened)
+		__tsc200x_enable(ts);
+
+	ts->suspended = false;
+
+	mutex_unlock(&ts->mutex);
+
+	return 0;
+}
+
+SIMPLE_DEV_PM_OPS(tsc200x_pm_ops, tsc200x_suspend, tsc200x_resume);
+EXPORT_SYMBOL_GPL(tsc200x_pm_ops);
+
+MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
+MODULE_DESCRIPTION("TSC200x Touchscreen Driver Core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tsc200x-core.h b/drivers/input/touchscreen/tsc200x-core.h
new file mode 100644
index 0000000..49a63a3
--- /dev/null
+++ b/drivers/input/touchscreen/tsc200x-core.h
@@ -0,0 +1,78 @@
+#ifndef _TSC200X_CORE_H
+#define _TSC200X_CORE_H
+
+/* control byte 1 */
+#define TSC200X_CMD			0x80
+#define TSC200X_CMD_NORMAL		0x00
+#define TSC200X_CMD_STOP		0x01
+#define TSC200X_CMD_12BIT		0x04
+
+/* control byte 0 */
+#define TSC200X_REG_READ		0x01 /* R/W access */
+#define TSC200X_REG_PND0		0x02 /* Power Not Down Control */
+#define TSC200X_REG_X			(0x0 << 3)
+#define TSC200X_REG_Y			(0x1 << 3)
+#define TSC200X_REG_Z1			(0x2 << 3)
+#define TSC200X_REG_Z2			(0x3 << 3)
+#define TSC200X_REG_AUX			(0x4 << 3)
+#define TSC200X_REG_TEMP1		(0x5 << 3)
+#define TSC200X_REG_TEMP2		(0x6 << 3)
+#define TSC200X_REG_STATUS		(0x7 << 3)
+#define TSC200X_REG_AUX_HIGH		(0x8 << 3)
+#define TSC200X_REG_AUX_LOW		(0x9 << 3)
+#define TSC200X_REG_TEMP_HIGH		(0xA << 3)
+#define TSC200X_REG_TEMP_LOW		(0xB << 3)
+#define TSC200X_REG_CFR0		(0xC << 3)
+#define TSC200X_REG_CFR1		(0xD << 3)
+#define TSC200X_REG_CFR2		(0xE << 3)
+#define TSC200X_REG_CONV_FUNC		(0xF << 3)
+
+/* configuration register 0 */
+#define TSC200X_CFR0_PRECHARGE_276US	0x0040
+#define TSC200X_CFR0_STABTIME_1MS	0x0300
+#define TSC200X_CFR0_CLOCK_1MHZ		0x1000
+#define TSC200X_CFR0_RESOLUTION12	0x2000
+#define TSC200X_CFR0_PENMODE		0x8000
+#define TSC200X_CFR0_INITVALUE		(TSC200X_CFR0_STABTIME_1MS    | \
+					 TSC200X_CFR0_CLOCK_1MHZ      | \
+					 TSC200X_CFR0_RESOLUTION12    | \
+					 TSC200X_CFR0_PRECHARGE_276US | \
+					 TSC200X_CFR0_PENMODE)
+
+/* bits common to both read and write of configuration register 0 */
+#define	TSC200X_CFR0_RW_MASK		0x3fff
+
+/* configuration register 1 */
+#define TSC200X_CFR1_BATCHDELAY_4MS	0x0003
+#define TSC200X_CFR1_INITVALUE		TSC200X_CFR1_BATCHDELAY_4MS
+
+/* configuration register 2 */
+#define TSC200X_CFR2_MAVE_Z		0x0004
+#define TSC200X_CFR2_MAVE_Y		0x0008
+#define TSC200X_CFR2_MAVE_X		0x0010
+#define TSC200X_CFR2_AVG_7		0x0800
+#define TSC200X_CFR2_MEDIUM_15		0x3000
+#define TSC200X_CFR2_INITVALUE		(TSC200X_CFR2_MAVE_X	| \
+					 TSC200X_CFR2_MAVE_Y	| \
+					 TSC200X_CFR2_MAVE_Z	| \
+					 TSC200X_CFR2_MEDIUM_15	| \
+					 TSC200X_CFR2_AVG_7)
+
+#define MAX_12BIT			0xfff
+#define TSC200X_DEF_X_FUZZ		4
+#define TSC200X_DEF_Y_FUZZ		8
+#define TSC200X_DEF_P_FUZZ		2
+#define TSC200X_DEF_RESISTOR		280
+
+#define TSC2005_SPI_MAX_SPEED_HZ	10000000
+#define TSC200X_PENUP_TIME_MS		40
+
+extern const struct regmap_config tsc200x_regmap_config;
+extern const struct dev_pm_ops tsc200x_pm_ops;
+
+int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
+		  struct regmap *regmap,
+		  int (*tsc200x_cmd)(struct device *dev, u8 cmd));
+int tsc200x_remove(struct device *dev);
+
+#endif
diff --git a/drivers/input/touchscreen/tsc40.c b/drivers/input/touchscreen/tsc40.c
new file mode 100644
index 0000000..2968787
--- /dev/null
+++ b/drivers/input/touchscreen/tsc40.c
@@ -0,0 +1,172 @@
+/*
+ * TSC-40 serial touchscreen driver. It should be compatible with
+ * TSC-10 and 25.
+ *
+ * Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ * License: GPLv2 as published by the FSF.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define PACKET_LENGTH  5
+struct tsc_ser {
+	struct input_dev *dev;
+	struct serio *serio;
+	u32 idx;
+	unsigned char data[PACKET_LENGTH];
+	char phys[32];
+};
+
+static void tsc_process_data(struct tsc_ser *ptsc)
+{
+	struct input_dev *dev = ptsc->dev;
+	u8 *data = ptsc->data;
+	u32 x;
+	u32 y;
+
+	x = ((data[1] & 0x03) << 8) | data[2];
+	y = ((data[3] & 0x03) << 8) | data[4];
+
+	input_report_abs(dev, ABS_X, x);
+	input_report_abs(dev, ABS_Y, y);
+	input_report_key(dev, BTN_TOUCH, 1);
+
+	input_sync(dev);
+}
+
+static irqreturn_t tsc_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags)
+{
+	struct tsc_ser *ptsc = serio_get_drvdata(serio);
+	struct input_dev *dev = ptsc->dev;
+
+	ptsc->data[ptsc->idx] = data;
+	switch (ptsc->idx++) {
+	case 0:
+		if (unlikely((data & 0x3e) != 0x10)) {
+			dev_dbg(&serio->dev,
+				"unsynchronized packet start (0x%02x)\n", data);
+			ptsc->idx = 0;
+		} else if (!(data & 0x01)) {
+			input_report_key(dev, BTN_TOUCH, 0);
+			input_sync(dev);
+			ptsc->idx = 0;
+		}
+		break;
+
+	case 1:
+	case 3:
+		if (unlikely(data & 0xfc)) {
+			dev_dbg(&serio->dev,
+				"unsynchronized data 0x%02x at offset %d\n",
+				data, ptsc->idx - 1);
+			ptsc->idx = 0;
+		}
+		break;
+
+	case 4:
+		tsc_process_data(ptsc);
+		ptsc->idx = 0;
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int tsc_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct tsc_ser *ptsc;
+	struct input_dev *input_dev;
+	int error;
+
+	ptsc = kzalloc(sizeof(struct tsc_ser), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!ptsc || !input_dev) {
+		error = -ENOMEM;
+		goto fail1;
+	}
+
+	ptsc->serio = serio;
+	ptsc->dev = input_dev;
+	snprintf(ptsc->phys, sizeof(ptsc->phys), "%s/input0", serio->phys);
+
+	input_dev->name = "TSC-10/25/40 Serial TouchScreen";
+	input_dev->phys = ptsc->phys;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = SERIO_TSC40;
+	input_dev->id.product = 40;
+	input_dev->id.version = 0x0001;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+	input_set_abs_params(ptsc->dev, ABS_X, 0, 0x3ff, 0, 0);
+	input_set_abs_params(ptsc->dev, ABS_Y, 0, 0x3ff, 0, 0);
+
+	serio_set_drvdata(serio, ptsc);
+
+	error = serio_open(serio, drv);
+	if (error)
+		goto fail2;
+
+	error = input_register_device(ptsc->dev);
+	if (error)
+		goto fail3;
+
+	return 0;
+
+fail3:
+	serio_close(serio);
+fail2:
+	serio_set_drvdata(serio, NULL);
+fail1:
+	input_free_device(input_dev);
+	kfree(ptsc);
+	return error;
+}
+
+static void tsc_disconnect(struct serio *serio)
+{
+	struct tsc_ser *ptsc = serio_get_drvdata(serio);
+
+	serio_close(serio);
+
+	input_unregister_device(ptsc->dev);
+	kfree(ptsc);
+
+	serio_set_drvdata(serio, NULL);
+}
+
+static struct serio_device_id tsc_serio_ids[] = {
+	{
+		.type   = SERIO_RS232,
+		.proto  = SERIO_TSC40,
+		.id     = SERIO_ANY,
+		.extra  = SERIO_ANY,
+	},
+	{ 0 }
+};
+MODULE_DEVICE_TABLE(serio, tsc_serio_ids);
+
+#define DRIVER_DESC    "TSC-10/25/40 serial touchscreen driver"
+
+static struct serio_driver tsc_drv = {
+	.driver	= {
+		.name   = "tsc40",
+	},
+	.description    = DRIVER_DESC,
+	.id_table	= tsc_serio_ids,
+	.interrupt      = tsc_interrupt,
+	.connect	= tsc_connect,
+	.disconnect     = tsc_disconnect,
+};
+
+module_serio_driver(tsc_drv);
+
+MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c
new file mode 100644
index 0000000..c1e23cf
--- /dev/null
+++ b/drivers/input/touchscreen/ucb1400_ts.c
@@ -0,0 +1,463 @@
+/*
+ *  Philips UCB1400 touchscreen driver
+ *
+ *  Author:	Nicolas Pitre
+ *  Created:	September 25, 2006
+ *  Copyright:	MontaVista Software, Inc.
+ *
+ * Spliting done by: Marek Vasut <marek.vasut@gmail.com>
+ * If something doesn't work and it worked before spliting, e-mail me,
+ * dont bother Nicolas please ;-)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This code is heavily based on ucb1x00-*.c copyrighted by Russell King
+ * covering the UCB1100, UCB1200 and UCB1300..  Support for the UCB1400 has
+ * been made separate from ucb1x00-core/ucb1x00-ts on Russell's request.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/ucb1400.h>
+
+#define UCB1400_TS_POLL_PERIOD	10 /* ms */
+
+static bool adcsync;
+static int ts_delay = 55; /* us */
+static int ts_delay_pressure;	/* us */
+
+/* Switch to interrupt mode. */
+static void ucb1400_ts_mode_int(struct ucb1400_ts *ucb)
+{
+	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+			UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+			UCB_TS_CR_MODE_INT);
+}
+
+/*
+ * Switch to pressure mode, and read pressure.  We don't need to wait
+ * here, since both plates are being driven.
+ */
+static unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb)
+{
+	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+			UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+
+	udelay(ts_delay_pressure);
+
+	return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync);
+}
+
+/*
+ * Switch to X position mode and measure Y plate.  We switch the plate
+ * configuration in pressure mode, then switch to position mode.  This
+ * gives a faster response time.  Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb)
+{
+	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+	udelay(ts_delay);
+
+	return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync);
+}
+
+/*
+ * Switch to Y position mode and measure X plate.  We switch the plate
+ * configuration in pressure mode, then switch to position mode.  This
+ * gives a faster response time.  Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb)
+{
+	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+	udelay(ts_delay);
+
+	return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPX, adcsync);
+}
+
+/*
+ * Switch to X plate resistance mode.  Set MX to ground, PX to
+ * supply.  Measure current.
+ */
+static unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb)
+{
+	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	return ucb1400_adc_read(ucb->ac97, 0, adcsync);
+}
+
+/*
+ * Switch to Y plate resistance mode.  Set MY to ground, PY to
+ * supply.  Measure current.
+ */
+static unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb)
+{
+	ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	return ucb1400_adc_read(ucb->ac97, 0, adcsync);
+}
+
+static int ucb1400_ts_pen_up(struct ucb1400_ts *ucb)
+{
+	unsigned short val = ucb1400_reg_read(ucb->ac97, UCB_TS_CR);
+
+	return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW);
+}
+
+static void ucb1400_ts_irq_enable(struct ucb1400_ts *ucb)
+{
+	ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, UCB_IE_TSPX);
+	ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
+	ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_TSPX);
+}
+
+static void ucb1400_ts_irq_disable(struct ucb1400_ts *ucb)
+{
+	ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0);
+}
+
+static void ucb1400_ts_report_event(struct input_dev *idev, u16 pressure, u16 x, u16 y)
+{
+	input_report_abs(idev, ABS_X, x);
+	input_report_abs(idev, ABS_Y, y);
+	input_report_abs(idev, ABS_PRESSURE, pressure);
+	input_report_key(idev, BTN_TOUCH, 1);
+	input_sync(idev);
+}
+
+static void ucb1400_ts_event_release(struct input_dev *idev)
+{
+	input_report_abs(idev, ABS_PRESSURE, 0);
+	input_report_key(idev, BTN_TOUCH, 0);
+	input_sync(idev);
+}
+
+static void ucb1400_clear_pending_irq(struct ucb1400_ts *ucb)
+{
+	unsigned int isr;
+
+	isr = ucb1400_reg_read(ucb->ac97, UCB_IE_STATUS);
+	ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, isr);
+	ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
+
+	if (isr & UCB_IE_TSPX)
+		ucb1400_ts_irq_disable(ucb);
+	else
+		dev_dbg(&ucb->ts_idev->dev,
+			"ucb1400: unexpected IE_STATUS = %#x\n", isr);
+}
+
+/*
+ * A restriction with interrupts exists when using the ucb1400, as
+ * the codec read/write routines may sleep while waiting for codec
+ * access completion and uses semaphores for access control to the
+ * AC97 bus. Therefore the driver is forced to use threaded interrupt
+ * handler.
+ */
+static irqreturn_t ucb1400_irq(int irqnr, void *devid)
+{
+	struct ucb1400_ts *ucb = devid;
+	unsigned int x, y, p;
+	bool penup;
+
+	if (unlikely(irqnr != ucb->irq))
+		return IRQ_NONE;
+
+	ucb1400_clear_pending_irq(ucb);
+
+	/* Start with a small delay before checking pendown state */
+	msleep(UCB1400_TS_POLL_PERIOD);
+
+	while (!ucb->stopped && !(penup = ucb1400_ts_pen_up(ucb))) {
+
+		ucb1400_adc_enable(ucb->ac97);
+		x = ucb1400_ts_read_xpos(ucb);
+		y = ucb1400_ts_read_ypos(ucb);
+		p = ucb1400_ts_read_pressure(ucb);
+		ucb1400_adc_disable(ucb->ac97);
+
+		ucb1400_ts_report_event(ucb->ts_idev, p, x, y);
+
+		wait_event_timeout(ucb->ts_wait, ucb->stopped,
+				   msecs_to_jiffies(UCB1400_TS_POLL_PERIOD));
+	}
+
+	ucb1400_ts_event_release(ucb->ts_idev);
+
+	if (!ucb->stopped) {
+		/* Switch back to interrupt mode. */
+		ucb1400_ts_mode_int(ucb);
+		ucb1400_ts_irq_enable(ucb);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void ucb1400_ts_stop(struct ucb1400_ts *ucb)
+{
+	/* Signal IRQ thread to stop polling and disable the handler. */
+	ucb->stopped = true;
+	mb();
+	wake_up(&ucb->ts_wait);
+	disable_irq(ucb->irq);
+
+	ucb1400_ts_irq_disable(ucb);
+	ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0);
+}
+
+/* Must be called with ts->lock held */
+static void ucb1400_ts_start(struct ucb1400_ts *ucb)
+{
+	/* Tell IRQ thread that it may poll the device. */
+	ucb->stopped = false;
+	mb();
+
+	ucb1400_ts_mode_int(ucb);
+	ucb1400_ts_irq_enable(ucb);
+
+	enable_irq(ucb->irq);
+}
+
+static int ucb1400_ts_open(struct input_dev *idev)
+{
+	struct ucb1400_ts *ucb = input_get_drvdata(idev);
+
+	ucb1400_ts_start(ucb);
+
+	return 0;
+}
+
+static void ucb1400_ts_close(struct input_dev *idev)
+{
+	struct ucb1400_ts *ucb = input_get_drvdata(idev);
+
+	ucb1400_ts_stop(ucb);
+}
+
+#ifndef NO_IRQ
+#define NO_IRQ	0
+#endif
+
+/*
+ * Try to probe our interrupt, rather than relying on lots of
+ * hard-coded machine dependencies.
+ */
+static int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb,
+					   struct platform_device *pdev)
+{
+	unsigned long mask, timeout;
+
+	mask = probe_irq_on();
+
+	/* Enable the ADC interrupt. */
+	ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, UCB_IE_ADC);
+	ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_ADC);
+	ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff);
+	ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
+
+	/* Cause an ADC interrupt. */
+	ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA);
+	ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START);
+
+	/* Wait for the conversion to complete. */
+	timeout = jiffies + HZ/2;
+	while (!(ucb1400_reg_read(ucb->ac97, UCB_ADC_DATA) &
+						UCB_ADC_DAT_VALID)) {
+		cpu_relax();
+		if (time_after(jiffies, timeout)) {
+			dev_err(&pdev->dev, "timed out in IRQ probe\n");
+			probe_irq_off(mask);
+			return -ENODEV;
+		}
+	}
+	ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, 0);
+
+	/* Disable and clear interrupt. */
+	ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, 0);
+	ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0);
+	ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff);
+	ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
+
+	/* Read triggered interrupt. */
+	ucb->irq = probe_irq_off(mask);
+	if (ucb->irq < 0 || ucb->irq == NO_IRQ)
+		return -ENODEV;
+
+	return 0;
+}
+
+static int ucb1400_ts_probe(struct platform_device *pdev)
+{
+	struct ucb1400_ts *ucb = dev_get_platdata(&pdev->dev);
+	int error, x_res, y_res;
+	u16 fcsr;
+
+	ucb->ts_idev = input_allocate_device();
+	if (!ucb->ts_idev) {
+		error = -ENOMEM;
+		goto err;
+	}
+
+	/* Only in case the IRQ line wasn't supplied, try detecting it */
+	if (ucb->irq < 0) {
+		error = ucb1400_ts_detect_irq(ucb, pdev);
+		if (error) {
+			dev_err(&pdev->dev, "IRQ probe failed\n");
+			goto err_free_devs;
+		}
+	}
+	dev_dbg(&pdev->dev, "found IRQ %d\n", ucb->irq);
+
+	init_waitqueue_head(&ucb->ts_wait);
+
+	input_set_drvdata(ucb->ts_idev, ucb);
+
+	ucb->ts_idev->dev.parent	= &pdev->dev;
+	ucb->ts_idev->name		= "UCB1400 touchscreen interface";
+	ucb->ts_idev->id.vendor		= ucb1400_reg_read(ucb->ac97,
+						AC97_VENDOR_ID1);
+	ucb->ts_idev->id.product	= ucb->id;
+	ucb->ts_idev->open		= ucb1400_ts_open;
+	ucb->ts_idev->close		= ucb1400_ts_close;
+	ucb->ts_idev->evbit[0]		= BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
+	ucb->ts_idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	/*
+	 * Enable ADC filter to prevent horrible jitter on Colibri.
+	 * This also further reduces jitter on boards where ADCSYNC
+	 * pin is connected.
+	 */
+	fcsr = ucb1400_reg_read(ucb->ac97, UCB_FCSR);
+	ucb1400_reg_write(ucb->ac97, UCB_FCSR, fcsr | UCB_FCSR_AVE);
+
+	ucb1400_adc_enable(ucb->ac97);
+	x_res = ucb1400_ts_read_xres(ucb);
+	y_res = ucb1400_ts_read_yres(ucb);
+	ucb1400_adc_disable(ucb->ac97);
+	dev_dbg(&pdev->dev, "x/y = %d/%d\n", x_res, y_res);
+
+	input_set_abs_params(ucb->ts_idev, ABS_X, 0, x_res, 0, 0);
+	input_set_abs_params(ucb->ts_idev, ABS_Y, 0, y_res, 0, 0);
+	input_set_abs_params(ucb->ts_idev, ABS_PRESSURE, 0, 0, 0, 0);
+
+	ucb1400_ts_stop(ucb);
+
+	error = request_threaded_irq(ucb->irq, NULL, ucb1400_irq,
+				     IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+				     "UCB1400", ucb);
+	if (error) {
+		dev_err(&pdev->dev,
+			"unable to grab irq%d: %d\n", ucb->irq, error);
+		goto err_free_devs;
+	}
+
+	error = input_register_device(ucb->ts_idev);
+	if (error)
+		goto err_free_irq;
+
+	return 0;
+
+err_free_irq:
+	free_irq(ucb->irq, ucb);
+err_free_devs:
+	input_free_device(ucb->ts_idev);
+err:
+	return error;
+}
+
+static int ucb1400_ts_remove(struct platform_device *pdev)
+{
+	struct ucb1400_ts *ucb = dev_get_platdata(&pdev->dev);
+
+	free_irq(ucb->irq, ucb);
+	input_unregister_device(ucb->ts_idev);
+
+	return 0;
+}
+
+static int __maybe_unused ucb1400_ts_suspend(struct device *dev)
+{
+	struct ucb1400_ts *ucb = dev_get_platdata(dev);
+	struct input_dev *idev = ucb->ts_idev;
+
+	mutex_lock(&idev->mutex);
+
+	if (idev->users)
+		ucb1400_ts_start(ucb);
+
+	mutex_unlock(&idev->mutex);
+	return 0;
+}
+
+static int __maybe_unused ucb1400_ts_resume(struct device *dev)
+{
+	struct ucb1400_ts *ucb = dev_get_platdata(dev);
+	struct input_dev *idev = ucb->ts_idev;
+
+	mutex_lock(&idev->mutex);
+
+	if (idev->users)
+		ucb1400_ts_stop(ucb);
+
+	mutex_unlock(&idev->mutex);
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ucb1400_ts_pm_ops,
+			 ucb1400_ts_suspend, ucb1400_ts_resume);
+
+static struct platform_driver ucb1400_ts_driver = {
+	.probe	= ucb1400_ts_probe,
+	.remove	= ucb1400_ts_remove,
+	.driver	= {
+		.name	= "ucb1400_ts",
+		.pm	= &ucb1400_ts_pm_ops,
+	},
+};
+module_platform_driver(ucb1400_ts_driver);
+
+module_param(adcsync, bool, 0444);
+MODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin.");
+
+module_param(ts_delay, int, 0444);
+MODULE_PARM_DESC(ts_delay, "Delay between panel setup and"
+			    " position read. Default = 55us.");
+
+module_param(ts_delay_pressure, int, 0444);
+MODULE_PARM_DESC(ts_delay_pressure,
+		"delay between panel setup and pressure read."
+		"  Default = 0us.");
+
+MODULE_DESCRIPTION("Philips UCB1400 touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
new file mode 100644
index 0000000..2c41107
--- /dev/null
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -0,0 +1,1772 @@
+/******************************************************************************
+ * usbtouchscreen.c
+ * Driver for USB Touchscreens, supporting those devices:
+ *  - eGalax Touchkit
+ *    includes eTurboTouch CT-410/510/700
+ *  - 3M/Microtouch  EX II series
+ *  - ITM
+ *  - PanJit TouchSet
+ *  - eTurboTouch
+ *  - Gunze AHL61
+ *  - DMC TSC-10/25
+ *  - IRTOUCHSYSTEMS/UNITOP
+ *  - IdealTEK URTC1000
+ *  - General Touch
+ *  - GoTop Super_Q2/GogoPen/PenPower tablets
+ *  - JASTEC USB touch controller/DigiTech DTR-02U
+ *  - Zytronic capacitive touchscreen
+ *  - NEXIO/iNexio
+ *  - Elo TouchSystems 2700 IntelliTouch
+ *  - EasyTouch USB Dual/Multi touch controller from Data Modul
+ *
+ * Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch>
+ * Copyright (C) by Todd E. Johnson (mtouchusb.c)
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Driver is based on touchkitusb.c
+ * - ITM parts are from itmtouch.c
+ * - 3M parts are from mtouchusb.c
+ * - PanJit parts are from an unmerged driver by Lanslott Gish
+ * - DMC TSC 10/25 are from Holger Schurig, with ideas from an unmerged
+ *   driver from Marius Vollmer
+ *
+ *****************************************************************************/
+
+//#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/hid.h>
+
+
+#define DRIVER_VERSION		"v0.6"
+#define DRIVER_AUTHOR		"Daniel Ritz <daniel.ritz@gmx.ch>"
+#define DRIVER_DESC		"USB Touchscreen Driver"
+
+static bool swap_xy;
+module_param(swap_xy, bool, 0644);
+MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped.");
+
+static bool hwcalib_xy;
+module_param(hwcalib_xy, bool, 0644);
+MODULE_PARM_DESC(hwcalib_xy, "If set hw-calibrated X/Y are used if available");
+
+/* device specifc data/functions */
+struct usbtouch_usb;
+struct usbtouch_device_info {
+	int min_xc, max_xc;
+	int min_yc, max_yc;
+	int min_press, max_press;
+	int rept_size;
+
+	/*
+	 * Always service the USB devices irq not just when the input device is
+	 * open. This is useful when devices have a watchdog which prevents us
+	 * from periodically polling the device. Leave this unset unless your
+	 * touchscreen device requires it, as it does consume more of the USB
+	 * bandwidth.
+	 */
+	bool irq_always;
+
+	void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len);
+
+	/*
+	 * used to get the packet len. possible return values:
+	 * > 0: packet len
+	 * = 0: skip one byte
+	 * < 0: -return value more bytes needed
+	 */
+	int  (*get_pkt_len) (unsigned char *pkt, int len);
+
+	int  (*read_data)   (struct usbtouch_usb *usbtouch, unsigned char *pkt);
+	int  (*alloc)       (struct usbtouch_usb *usbtouch);
+	int  (*init)        (struct usbtouch_usb *usbtouch);
+	void (*exit)	    (struct usbtouch_usb *usbtouch);
+};
+
+/* a usbtouch device */
+struct usbtouch_usb {
+	unsigned char *data;
+	dma_addr_t data_dma;
+	int data_size;
+	unsigned char *buffer;
+	int buf_len;
+	struct urb *irq;
+	struct usb_interface *interface;
+	struct input_dev *input;
+	struct usbtouch_device_info *type;
+	char name[128];
+	char phys[64];
+	void *priv;
+
+	int x, y;
+	int touch, press;
+};
+
+
+/* device types */
+enum {
+	DEVTYPE_IGNORE = -1,
+	DEVTYPE_EGALAX,
+	DEVTYPE_PANJIT,
+	DEVTYPE_3M,
+	DEVTYPE_ITM,
+	DEVTYPE_ETURBO,
+	DEVTYPE_GUNZE,
+	DEVTYPE_DMC_TSC10,
+	DEVTYPE_IRTOUCH,
+	DEVTYPE_IRTOUCH_HIRES,
+	DEVTYPE_IDEALTEK,
+	DEVTYPE_GENERAL_TOUCH,
+	DEVTYPE_GOTOP,
+	DEVTYPE_JASTEC,
+	DEVTYPE_E2I,
+	DEVTYPE_ZYTRONIC,
+	DEVTYPE_TC45USB,
+	DEVTYPE_NEXIO,
+	DEVTYPE_ELO,
+	DEVTYPE_ETOUCH,
+};
+
+#define USB_DEVICE_HID_CLASS(vend, prod) \
+	.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS \
+		| USB_DEVICE_ID_MATCH_DEVICE, \
+	.idVendor = (vend), \
+	.idProduct = (prod), \
+	.bInterfaceClass = USB_INTERFACE_CLASS_HID
+
+static const struct usb_device_id usbtouch_devices[] = {
+#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
+	/* ignore the HID capable devices, handled by usbhid */
+	{USB_DEVICE_HID_CLASS(0x0eef, 0x0001), .driver_info = DEVTYPE_IGNORE},
+	{USB_DEVICE_HID_CLASS(0x0eef, 0x0002), .driver_info = DEVTYPE_IGNORE},
+
+	/* normal device IDs */
+	{USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX},
+	{USB_DEVICE(0x3823, 0x0002), .driver_info = DEVTYPE_EGALAX},
+	{USB_DEVICE(0x0123, 0x0001), .driver_info = DEVTYPE_EGALAX},
+	{USB_DEVICE(0x0eef, 0x0001), .driver_info = DEVTYPE_EGALAX},
+	{USB_DEVICE(0x0eef, 0x0002), .driver_info = DEVTYPE_EGALAX},
+	{USB_DEVICE(0x1234, 0x0001), .driver_info = DEVTYPE_EGALAX},
+	{USB_DEVICE(0x1234, 0x0002), .driver_info = DEVTYPE_EGALAX},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_PANJIT
+	{USB_DEVICE(0x134c, 0x0001), .driver_info = DEVTYPE_PANJIT},
+	{USB_DEVICE(0x134c, 0x0002), .driver_info = DEVTYPE_PANJIT},
+	{USB_DEVICE(0x134c, 0x0003), .driver_info = DEVTYPE_PANJIT},
+	{USB_DEVICE(0x134c, 0x0004), .driver_info = DEVTYPE_PANJIT},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_3M
+	{USB_DEVICE(0x0596, 0x0001), .driver_info = DEVTYPE_3M},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ITM
+	{USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM},
+	{USB_DEVICE(0x16e3, 0xf9e9), .driver_info = DEVTYPE_ITM},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO
+	{USB_DEVICE(0x1234, 0x5678), .driver_info = DEVTYPE_ETURBO},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GUNZE
+	{USB_DEVICE(0x0637, 0x0001), .driver_info = DEVTYPE_GUNZE},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_DMC_TSC10
+	{USB_DEVICE(0x0afa, 0x03e8), .driver_info = DEVTYPE_DMC_TSC10},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
+	{USB_DEVICE(0x595a, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
+	{USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
+	{USB_DEVICE(0x6615, 0x0012), .driver_info = DEVTYPE_IRTOUCH_HIRES},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
+	{USB_DEVICE(0x1391, 0x1000), .driver_info = DEVTYPE_IDEALTEK},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
+	{USB_DEVICE(0x0dfc, 0x0001), .driver_info = DEVTYPE_GENERAL_TOUCH},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GOTOP
+	{USB_DEVICE(0x08f2, 0x007f), .driver_info = DEVTYPE_GOTOP},
+	{USB_DEVICE(0x08f2, 0x00ce), .driver_info = DEVTYPE_GOTOP},
+	{USB_DEVICE(0x08f2, 0x00f4), .driver_info = DEVTYPE_GOTOP},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC
+	{USB_DEVICE(0x0f92, 0x0001), .driver_info = DEVTYPE_JASTEC},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_E2I
+	{USB_DEVICE(0x1ac7, 0x0001), .driver_info = DEVTYPE_E2I},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+	{USB_DEVICE(0x14c8, 0x0003), .driver_info = DEVTYPE_ZYTRONIC},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB
+	/* TC5UH */
+	{USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC45USB},
+	/* TC4UM */
+	{USB_DEVICE(0x0664, 0x0306), .driver_info = DEVTYPE_TC45USB},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO
+	/* data interface only */
+	{USB_DEVICE_AND_INTERFACE_INFO(0x10f0, 0x2002, 0x0a, 0x00, 0x00),
+		.driver_info = DEVTYPE_NEXIO},
+	{USB_DEVICE_AND_INTERFACE_INFO(0x1870, 0x0001, 0x0a, 0x00, 0x00),
+		.driver_info = DEVTYPE_NEXIO},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ELO
+	{USB_DEVICE(0x04e7, 0x0020), .driver_info = DEVTYPE_ELO},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH
+	{USB_DEVICE(0x7374, 0x0001), .driver_info = DEVTYPE_ETOUCH},
+#endif
+
+	{}
+};
+
+
+/*****************************************************************************
+ * e2i Part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_E2I
+static int e2i_init(struct usbtouch_usb *usbtouch)
+{
+	int ret;
+	struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+	                      0x01, 0x02, 0x0000, 0x0081,
+	                      NULL, 0, USB_CTRL_SET_TIMEOUT);
+
+	dev_dbg(&usbtouch->interface->dev,
+		"%s - usb_control_msg - E2I_RESET - bytes|err: %d\n",
+		__func__, ret);
+	return ret;
+}
+
+static int e2i_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	int tmp = (pkt[0] << 8) | pkt[1];
+	dev->x  = (pkt[2] << 8) | pkt[3];
+	dev->y  = (pkt[4] << 8) | pkt[5];
+
+	tmp = tmp - 0xA000;
+	dev->touch = (tmp > 0);
+	dev->press = (tmp > 0 ? tmp : 0);
+
+	return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * eGalax part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
+
+#ifndef MULTI_PACKET
+#define MULTI_PACKET
+#endif
+
+#define EGALAX_PKT_TYPE_MASK		0xFE
+#define EGALAX_PKT_TYPE_REPT		0x80
+#define EGALAX_PKT_TYPE_DIAG		0x0A
+
+static int egalax_init(struct usbtouch_usb *usbtouch)
+{
+	int ret, i;
+	unsigned char *buf;
+	struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+
+	/*
+	 * An eGalax diagnostic packet kicks the device into using the right
+	 * protocol.  We send a "check active" packet.  The response will be
+	 * read later and ignored.
+	 */
+
+	buf = kmalloc(3, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = EGALAX_PKT_TYPE_DIAG;
+	buf[1] = 1;	/* length */
+	buf[2] = 'A';	/* command - check active */
+
+	for (i = 0; i < 3; i++) {
+		ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				      0,
+				      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+				      0, 0, buf, 3,
+				      USB_CTRL_SET_TIMEOUT);
+		if (ret >= 0) {
+			ret = 0;
+			break;
+		}
+		if (ret != -EPIPE)
+			break;
+	}
+
+	kfree(buf);
+
+	return ret;
+}
+
+static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT)
+		return 0;
+
+	dev->x = ((pkt[3] & 0x0F) << 7) | (pkt[4] & 0x7F);
+	dev->y = ((pkt[1] & 0x0F) << 7) | (pkt[2] & 0x7F);
+	dev->touch = pkt[0] & 0x01;
+
+	return 1;
+}
+
+static int egalax_get_pkt_len(unsigned char *buf, int len)
+{
+	switch (buf[0] & EGALAX_PKT_TYPE_MASK) {
+	case EGALAX_PKT_TYPE_REPT:
+		return 5;
+
+	case EGALAX_PKT_TYPE_DIAG:
+		if (len < 2)
+			return -1;
+
+		return buf[1] + 2;
+	}
+
+	return 0;
+}
+#endif
+
+/*****************************************************************************
+ * EasyTouch part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH
+
+#ifndef MULTI_PACKET
+#define MULTI_PACKET
+#endif
+
+#define ETOUCH_PKT_TYPE_MASK		0xFE
+#define ETOUCH_PKT_TYPE_REPT		0x80
+#define ETOUCH_PKT_TYPE_REPT2		0xB0
+#define ETOUCH_PKT_TYPE_DIAG		0x0A
+
+static int etouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	if ((pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT &&
+		(pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT2)
+		return 0;
+
+	dev->x = ((pkt[1] & 0x1F) << 7) | (pkt[2] & 0x7F);
+	dev->y = ((pkt[3] & 0x1F) << 7) | (pkt[4] & 0x7F);
+	dev->touch = pkt[0] & 0x01;
+
+	return 1;
+}
+
+static int etouch_get_pkt_len(unsigned char *buf, int len)
+{
+	switch (buf[0] & ETOUCH_PKT_TYPE_MASK) {
+	case ETOUCH_PKT_TYPE_REPT:
+	case ETOUCH_PKT_TYPE_REPT2:
+		return 5;
+
+	case ETOUCH_PKT_TYPE_DIAG:
+		if (len < 2)
+			return -1;
+
+		return buf[1] + 2;
+	}
+
+	return 0;
+}
+#endif
+
+/*****************************************************************************
+ * PanJit Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_PANJIT
+static int panjit_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1];
+	dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3];
+	dev->touch = pkt[0] & 0x01;
+
+	return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * 3M/Microtouch Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_3M
+
+#define MTOUCHUSB_ASYNC_REPORT          1
+#define MTOUCHUSB_RESET                 7
+#define MTOUCHUSB_REQ_CTRLLR_ID         10
+
+static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	if (hwcalib_xy) {
+		dev->x = (pkt[4] << 8) | pkt[3];
+		dev->y = 0xffff - ((pkt[6] << 8) | pkt[5]);
+	} else {
+		dev->x = (pkt[8] << 8) | pkt[7];
+		dev->y = (pkt[10] << 8) | pkt[9];
+	}
+	dev->touch = (pkt[2] & 0x40) ? 1 : 0;
+
+	return 1;
+}
+
+static int mtouch_init(struct usbtouch_usb *usbtouch)
+{
+	int ret, i;
+	struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+	                      MTOUCHUSB_RESET,
+	                      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+	                      1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+	dev_dbg(&usbtouch->interface->dev,
+		"%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d\n",
+		__func__, ret);
+	if (ret < 0)
+		return ret;
+	msleep(150);
+
+	for (i = 0; i < 3; i++) {
+		ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+				      MTOUCHUSB_ASYNC_REPORT,
+				      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+				      1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT);
+		dev_dbg(&usbtouch->interface->dev,
+			"%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d\n",
+			__func__, ret);
+		if (ret >= 0)
+			break;
+		if (ret != -EPIPE)
+			return ret;
+	}
+
+	/* Default min/max xy are the raw values, override if using hw-calib */
+	if (hwcalib_xy) {
+		input_set_abs_params(usbtouch->input, ABS_X, 0, 0xffff, 0, 0);
+		input_set_abs_params(usbtouch->input, ABS_Y, 0, 0xffff, 0, 0);
+	}
+
+	return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * ITM Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ITM
+static int itm_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	int touch;
+	/*
+	 * ITM devices report invalid x/y data if not touched.
+	 * if the screen was touched before but is not touched any more
+	 * report touch as 0 with the last valid x/y data once. then stop
+	 * reporting data until touched again.
+	 */
+	dev->press = ((pkt[2] & 0x01) << 7) | (pkt[5] & 0x7F);
+
+	touch = ~pkt[7] & 0x20;
+	if (!touch) {
+		if (dev->touch) {
+			dev->touch = 0;
+			return 1;
+		}
+
+		return 0;
+	}
+
+	dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[3] & 0x7F);
+	dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[4] & 0x7F);
+	dev->touch = touch;
+
+	return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * eTurboTouch part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO
+#ifndef MULTI_PACKET
+#define MULTI_PACKET
+#endif
+static int eturbo_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	unsigned int shift;
+
+	/* packets should start with sync */
+	if (!(pkt[0] & 0x80))
+		return 0;
+
+	shift = (6 - (pkt[0] & 0x03));
+	dev->x = ((pkt[3] << 7) | pkt[4]) >> shift;
+	dev->y = ((pkt[1] << 7) | pkt[2]) >> shift;
+	dev->touch = (pkt[0] & 0x10) ? 1 : 0;
+
+	return 1;
+}
+
+static int eturbo_get_pkt_len(unsigned char *buf, int len)
+{
+	if (buf[0] & 0x80)
+		return 5;
+	if (buf[0] == 0x01)
+		return 3;
+	return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * Gunze part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_GUNZE
+static int gunze_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	if (!(pkt[0] & 0x80) || ((pkt[1] | pkt[2] | pkt[3]) & 0x80))
+		return 0;
+
+	dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[2] & 0x7F);
+	dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[3] & 0x7F);
+	dev->touch = pkt[0] & 0x20;
+
+	return 1;
+}
+#endif
+
+/*****************************************************************************
+ * DMC TSC-10/25 Part
+ *
+ * Documentation about the controller and it's protocol can be found at
+ *   http://www.dmccoltd.com/files/controler/tsc10usb_pi_e.pdf
+ *   http://www.dmccoltd.com/files/controler/tsc25_usb_e.pdf
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_DMC_TSC10
+
+/* supported data rates. currently using 130 */
+#define TSC10_RATE_POINT	0x50
+#define TSC10_RATE_30		0x40
+#define TSC10_RATE_50		0x41
+#define TSC10_RATE_80		0x42
+#define TSC10_RATE_100		0x43
+#define TSC10_RATE_130		0x44
+#define TSC10_RATE_150		0x45
+
+/* commands */
+#define TSC10_CMD_RESET		0x55
+#define TSC10_CMD_RATE		0x05
+#define TSC10_CMD_DATA1		0x01
+
+static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
+{
+	struct usb_device *dev = interface_to_usbdev(usbtouch->interface);
+	int ret = -ENOMEM;
+	unsigned char *buf;
+
+	buf = kmalloc(2, GFP_NOIO);
+	if (!buf)
+		goto err_nobuf;
+	/* reset */
+	buf[0] = buf[1] = 0xFF;
+	ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
+	                      TSC10_CMD_RESET,
+	                      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+	                      0, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
+	if (ret < 0)
+		goto err_out;
+	if (buf[0] != 0x06) {
+		ret = -ENODEV;
+		goto err_out;
+	}
+
+	/* TSC-25 data sheet specifies a delay after the RESET command */
+	msleep(150);
+
+	/* set coordinate output rate */
+	buf[0] = buf[1] = 0xFF;
+	ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
+	                      TSC10_CMD_RATE,
+	                      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+	                      TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
+	if (ret < 0)
+		goto err_out;
+	if ((buf[0] != 0x06) && (buf[0] != 0x15 || buf[1] != 0x01)) {
+		ret = -ENODEV;
+		goto err_out;
+	}
+
+	/* start sending data */
+	ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
+	                      TSC10_CMD_DATA1,
+	                      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+	                      0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+err_out:
+	kfree(buf);
+err_nobuf:
+	return ret;
+}
+
+
+static int dmc_tsc10_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	dev->x = ((pkt[2] & 0x03) << 8) | pkt[1];
+	dev->y = ((pkt[4] & 0x03) << 8) | pkt[3];
+	dev->touch = pkt[0] & 0x01;
+
+	return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * IRTOUCH Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
+static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	dev->x = (pkt[3] << 8) | pkt[2];
+	dev->y = (pkt[5] << 8) | pkt[4];
+	dev->touch = (pkt[1] & 0x03) ? 1 : 0;
+
+	return 1;
+}
+#endif
+
+/*****************************************************************************
+ * ET&T TC5UH/TC4UM part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB
+static int tc45usb_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1];
+	dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3];
+	dev->touch = pkt[0] & 0x01;
+
+	return 1;
+}
+#endif
+
+/*****************************************************************************
+ * IdealTEK URTC1000 Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
+#ifndef MULTI_PACKET
+#define MULTI_PACKET
+#endif
+static int idealtek_get_pkt_len(unsigned char *buf, int len)
+{
+	if (buf[0] & 0x80)
+		return 5;
+	if (buf[0] == 0x01)
+		return len;
+	return 0;
+}
+
+static int idealtek_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	switch (pkt[0] & 0x98) {
+	case 0x88:
+		/* touch data in IdealTEK mode */
+		dev->x = (pkt[1] << 5) | (pkt[2] >> 2);
+		dev->y = (pkt[3] << 5) | (pkt[4] >> 2);
+		dev->touch = (pkt[0] & 0x40) ? 1 : 0;
+		return 1;
+
+	case 0x98:
+		/* touch data in MT emulation mode */
+		dev->x = (pkt[2] << 5) | (pkt[1] >> 2);
+		dev->y = (pkt[4] << 5) | (pkt[3] >> 2);
+		dev->touch = (pkt[0] & 0x40) ? 1 : 0;
+		return 1;
+
+	default:
+		return 0;
+	}
+}
+#endif
+
+/*****************************************************************************
+ * General Touch Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
+static int general_touch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	dev->x = (pkt[2] << 8) | pkt[1];
+	dev->y = (pkt[4] << 8) | pkt[3];
+	dev->press = pkt[5] & 0xff;
+	dev->touch = pkt[0] & 0x01;
+
+	return 1;
+}
+#endif
+
+/*****************************************************************************
+ * GoTop Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_GOTOP
+static int gotop_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	dev->x = ((pkt[1] & 0x38) << 4) | pkt[2];
+	dev->y = ((pkt[1] & 0x07) << 7) | pkt[3];
+	dev->touch = pkt[0] & 0x01;
+
+	return 1;
+}
+#endif
+
+/*****************************************************************************
+ * JASTEC Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC
+static int jastec_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	dev->x = ((pkt[0] & 0x3f) << 6) | (pkt[2] & 0x3f);
+	dev->y = ((pkt[1] & 0x3f) << 6) | (pkt[3] & 0x3f);
+	dev->touch = (pkt[0] & 0x40) >> 6;
+
+	return 1;
+}
+#endif
+
+/*****************************************************************************
+ * Zytronic Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+static int zytronic_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	struct usb_interface *intf = dev->interface;
+
+	switch (pkt[0]) {
+	case 0x3A: /* command response */
+		dev_dbg(&intf->dev, "%s: Command response %d\n", __func__, pkt[1]);
+		break;
+
+	case 0xC0: /* down */
+		dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7);
+		dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7);
+		dev->touch = 1;
+		dev_dbg(&intf->dev, "%s: down %d,%d\n", __func__, dev->x, dev->y);
+		return 1;
+
+	case 0x80: /* up */
+		dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7);
+		dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7);
+		dev->touch = 0;
+		dev_dbg(&intf->dev, "%s: up %d,%d\n", __func__, dev->x, dev->y);
+		return 1;
+
+	default:
+		dev_dbg(&intf->dev, "%s: Unknown return %d\n", __func__, pkt[0]);
+		break;
+	}
+
+	return 0;
+}
+#endif
+
+/*****************************************************************************
+ * NEXIO Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO
+
+#define NEXIO_TIMEOUT	5000
+#define NEXIO_BUFSIZE	1024
+#define NEXIO_THRESHOLD	50
+
+struct nexio_priv {
+	struct urb *ack;
+	unsigned char *ack_buf;
+};
+
+struct nexio_touch_packet {
+	u8	flags;		/* 0xe1 = touch, 0xe1 = release */
+	__be16	data_len;	/* total bytes of touch data */
+	__be16	x_len;		/* bytes for X axis */
+	__be16	y_len;		/* bytes for Y axis */
+	u8	data[];
+} __attribute__ ((packed));
+
+static unsigned char nexio_ack_pkt[2] = { 0xaa, 0x02 };
+static unsigned char nexio_init_pkt[4] = { 0x82, 0x04, 0x0a, 0x0f };
+
+static void nexio_ack_complete(struct urb *urb)
+{
+}
+
+static int nexio_alloc(struct usbtouch_usb *usbtouch)
+{
+	struct nexio_priv *priv;
+	int ret = -ENOMEM;
+
+	usbtouch->priv = kmalloc(sizeof(struct nexio_priv), GFP_KERNEL);
+	if (!usbtouch->priv)
+		goto out_buf;
+
+	priv = usbtouch->priv;
+
+	priv->ack_buf = kmemdup(nexio_ack_pkt, sizeof(nexio_ack_pkt),
+				GFP_KERNEL);
+	if (!priv->ack_buf)
+		goto err_priv;
+
+	priv->ack = usb_alloc_urb(0, GFP_KERNEL);
+	if (!priv->ack) {
+		dev_dbg(&usbtouch->interface->dev,
+			"%s - usb_alloc_urb failed: usbtouch->ack\n", __func__);
+		goto err_ack_buf;
+	}
+
+	return 0;
+
+err_ack_buf:
+	kfree(priv->ack_buf);
+err_priv:
+	kfree(priv);
+out_buf:
+	return ret;
+}
+
+static int nexio_init(struct usbtouch_usb *usbtouch)
+{
+	struct usb_device *dev = interface_to_usbdev(usbtouch->interface);
+	struct usb_host_interface *interface = usbtouch->interface->cur_altsetting;
+	struct nexio_priv *priv = usbtouch->priv;
+	int ret = -ENOMEM;
+	int actual_len, i;
+	unsigned char *buf;
+	char *firmware_ver = NULL, *device_name = NULL;
+	int input_ep = 0, output_ep = 0;
+
+	/* find first input and output endpoint */
+	for (i = 0; i < interface->desc.bNumEndpoints; i++) {
+		if (!input_ep &&
+		    usb_endpoint_dir_in(&interface->endpoint[i].desc))
+			input_ep = interface->endpoint[i].desc.bEndpointAddress;
+		if (!output_ep &&
+		    usb_endpoint_dir_out(&interface->endpoint[i].desc))
+			output_ep = interface->endpoint[i].desc.bEndpointAddress;
+	}
+	if (!input_ep || !output_ep)
+		return -ENXIO;
+
+	buf = kmalloc(NEXIO_BUFSIZE, GFP_NOIO);
+	if (!buf)
+		goto out_buf;
+
+	/* two empty reads */
+	for (i = 0; i < 2; i++) {
+		ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, input_ep),
+				   buf, NEXIO_BUFSIZE, &actual_len,
+				   NEXIO_TIMEOUT);
+		if (ret < 0)
+			goto out_buf;
+	}
+
+	/* send init command */
+	memcpy(buf, nexio_init_pkt, sizeof(nexio_init_pkt));
+	ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, output_ep),
+			   buf, sizeof(nexio_init_pkt), &actual_len,
+			   NEXIO_TIMEOUT);
+	if (ret < 0)
+		goto out_buf;
+
+	/* read replies */
+	for (i = 0; i < 3; i++) {
+		memset(buf, 0, NEXIO_BUFSIZE);
+		ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, input_ep),
+				   buf, NEXIO_BUFSIZE, &actual_len,
+				   NEXIO_TIMEOUT);
+		if (ret < 0 || actual_len < 1 || buf[1] != actual_len)
+			continue;
+		switch (buf[0]) {
+		case 0x83:	/* firmware version */
+			if (!firmware_ver)
+				firmware_ver = kstrdup(&buf[2], GFP_NOIO);
+			break;
+		case 0x84:	/* device name */
+			if (!device_name)
+				device_name = kstrdup(&buf[2], GFP_NOIO);
+			break;
+		}
+	}
+
+	printk(KERN_INFO "Nexio device: %s, firmware version: %s\n",
+	       device_name, firmware_ver);
+
+	kfree(firmware_ver);
+	kfree(device_name);
+
+	usb_fill_bulk_urb(priv->ack, dev, usb_sndbulkpipe(dev, output_ep),
+			  priv->ack_buf, sizeof(nexio_ack_pkt),
+			  nexio_ack_complete, usbtouch);
+	ret = 0;
+
+out_buf:
+	kfree(buf);
+	return ret;
+}
+
+static void nexio_exit(struct usbtouch_usb *usbtouch)
+{
+	struct nexio_priv *priv = usbtouch->priv;
+
+	usb_kill_urb(priv->ack);
+	usb_free_urb(priv->ack);
+	kfree(priv->ack_buf);
+	kfree(priv);
+}
+
+static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)
+{
+	struct nexio_touch_packet *packet = (void *) pkt;
+	struct nexio_priv *priv = usbtouch->priv;
+	unsigned int data_len = be16_to_cpu(packet->data_len);
+	unsigned int x_len = be16_to_cpu(packet->x_len);
+	unsigned int y_len = be16_to_cpu(packet->y_len);
+	int x, y, begin_x, begin_y, end_x, end_y, w, h, ret;
+
+	/* got touch data? */
+	if ((pkt[0] & 0xe0) != 0xe0)
+		return 0;
+
+	if (data_len > 0xff)
+		data_len -= 0x100;
+	if (x_len > 0xff)
+		x_len -= 0x80;
+
+	/* send ACK */
+	ret = usb_submit_urb(priv->ack, GFP_ATOMIC);
+
+	if (!usbtouch->type->max_xc) {
+		usbtouch->type->max_xc = 2 * x_len;
+		input_set_abs_params(usbtouch->input, ABS_X,
+				     0, usbtouch->type->max_xc, 0, 0);
+		usbtouch->type->max_yc = 2 * y_len;
+		input_set_abs_params(usbtouch->input, ABS_Y,
+				     0, usbtouch->type->max_yc, 0, 0);
+	}
+	/*
+	 * The device reports state of IR sensors on X and Y axes.
+	 * Each byte represents "darkness" percentage (0-100) of one element.
+	 * 17" touchscreen reports only 64 x 52 bytes so the resolution is low.
+	 * This also means that there's a limited multi-touch capability but
+	 * it's disabled (and untested) here as there's no X driver for that.
+	 */
+	begin_x = end_x = begin_y = end_y = -1;
+	for (x = 0; x < x_len; x++) {
+		if (begin_x == -1 && packet->data[x] > NEXIO_THRESHOLD) {
+			begin_x = x;
+			continue;
+		}
+		if (end_x == -1 && begin_x != -1 && packet->data[x] < NEXIO_THRESHOLD) {
+			end_x = x - 1;
+			for (y = x_len; y < data_len; y++) {
+				if (begin_y == -1 && packet->data[y] > NEXIO_THRESHOLD) {
+					begin_y = y - x_len;
+					continue;
+				}
+				if (end_y == -1 &&
+				    begin_y != -1 && packet->data[y] < NEXIO_THRESHOLD) {
+					end_y = y - 1 - x_len;
+					w = end_x - begin_x;
+					h = end_y - begin_y;
+#if 0
+					/* multi-touch */
+					input_report_abs(usbtouch->input,
+						    ABS_MT_TOUCH_MAJOR, max(w,h));
+					input_report_abs(usbtouch->input,
+						    ABS_MT_TOUCH_MINOR, min(x,h));
+					input_report_abs(usbtouch->input,
+						    ABS_MT_POSITION_X, 2*begin_x+w);
+					input_report_abs(usbtouch->input,
+						    ABS_MT_POSITION_Y, 2*begin_y+h);
+					input_report_abs(usbtouch->input,
+						    ABS_MT_ORIENTATION, w > h);
+					input_mt_sync(usbtouch->input);
+#endif
+					/* single touch */
+					usbtouch->x = 2 * begin_x + w;
+					usbtouch->y = 2 * begin_y + h;
+					usbtouch->touch = packet->flags & 0x01;
+					begin_y = end_y = -1;
+					return 1;
+				}
+			}
+			begin_x = end_x = -1;
+		}
+
+	}
+	return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * ELO part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ELO
+
+static int elo_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+	dev->x = (pkt[3] << 8) | pkt[2];
+	dev->y = (pkt[5] << 8) | pkt[4];
+	dev->touch = pkt[6] > 0;
+	dev->press = pkt[6];
+
+	return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * the different device descriptors
+ */
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+				   unsigned char *pkt, int len);
+#endif
+
+static struct usbtouch_device_info usbtouch_dev_info[] = {
+#ifdef CONFIG_TOUCHSCREEN_USB_ELO
+	[DEVTYPE_ELO] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x0fff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x0fff,
+		.max_press	= 0xff,
+		.rept_size	= 8,
+		.read_data	= elo_read_data,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
+	[DEVTYPE_EGALAX] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x07ff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x07ff,
+		.rept_size	= 16,
+		.process_pkt	= usbtouch_process_multi,
+		.get_pkt_len	= egalax_get_pkt_len,
+		.read_data	= egalax_read_data,
+		.init		= egalax_init,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_PANJIT
+	[DEVTYPE_PANJIT] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x0fff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x0fff,
+		.rept_size	= 8,
+		.read_data	= panjit_read_data,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_3M
+	[DEVTYPE_3M] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x4000,
+		.min_yc		= 0x0,
+		.max_yc		= 0x4000,
+		.rept_size	= 11,
+		.read_data	= mtouch_read_data,
+		.init		= mtouch_init,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ITM
+	[DEVTYPE_ITM] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x0fff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x0fff,
+		.max_press	= 0xff,
+		.rept_size	= 8,
+		.read_data	= itm_read_data,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO
+	[DEVTYPE_ETURBO] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x07ff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x07ff,
+		.rept_size	= 8,
+		.process_pkt	= usbtouch_process_multi,
+		.get_pkt_len	= eturbo_get_pkt_len,
+		.read_data	= eturbo_read_data,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GUNZE
+	[DEVTYPE_GUNZE] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x0fff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x0fff,
+		.rept_size	= 4,
+		.read_data	= gunze_read_data,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_DMC_TSC10
+	[DEVTYPE_DMC_TSC10] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x03ff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x03ff,
+		.rept_size	= 5,
+		.init		= dmc_tsc10_init,
+		.read_data	= dmc_tsc10_read_data,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
+	[DEVTYPE_IRTOUCH] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x0fff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x0fff,
+		.rept_size	= 8,
+		.read_data	= irtouch_read_data,
+	},
+
+	[DEVTYPE_IRTOUCH_HIRES] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x7fff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x7fff,
+		.rept_size	= 8,
+		.read_data	= irtouch_read_data,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
+	[DEVTYPE_IDEALTEK] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x0fff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x0fff,
+		.rept_size	= 8,
+		.process_pkt	= usbtouch_process_multi,
+		.get_pkt_len	= idealtek_get_pkt_len,
+		.read_data	= idealtek_read_data,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
+	[DEVTYPE_GENERAL_TOUCH] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x7fff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x7fff,
+		.rept_size	= 7,
+		.read_data	= general_touch_read_data,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_GOTOP
+	[DEVTYPE_GOTOP] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x03ff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x03ff,
+		.rept_size	= 4,
+		.read_data	= gotop_read_data,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC
+	[DEVTYPE_JASTEC] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x0fff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x0fff,
+		.rept_size	= 4,
+		.read_data	= jastec_read_data,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_E2I
+	[DEVTYPE_E2I] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x7fff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x7fff,
+		.rept_size	= 6,
+		.init		= e2i_init,
+		.read_data	= e2i_read_data,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+	[DEVTYPE_ZYTRONIC] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x03ff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x03ff,
+		.rept_size	= 5,
+		.read_data	= zytronic_read_data,
+		.irq_always     = true,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB
+	[DEVTYPE_TC45USB] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x0fff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x0fff,
+		.rept_size	= 5,
+		.read_data	= tc45usb_read_data,
+	},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO
+	[DEVTYPE_NEXIO] = {
+		.rept_size	= 1024,
+		.irq_always	= true,
+		.read_data	= nexio_read_data,
+		.alloc		= nexio_alloc,
+		.init		= nexio_init,
+		.exit		= nexio_exit,
+	},
+#endif
+#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH
+	[DEVTYPE_ETOUCH] = {
+		.min_xc		= 0x0,
+		.max_xc		= 0x07ff,
+		.min_yc		= 0x0,
+		.max_yc		= 0x07ff,
+		.rept_size	= 16,
+		.process_pkt	= usbtouch_process_multi,
+		.get_pkt_len	= etouch_get_pkt_len,
+		.read_data	= etouch_read_data,
+	},
+#endif
+};
+
+
+/*****************************************************************************
+ * Generic Part
+ */
+static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
+                                 unsigned char *pkt, int len)
+{
+	struct usbtouch_device_info *type = usbtouch->type;
+
+	if (!type->read_data(usbtouch, pkt))
+			return;
+
+	input_report_key(usbtouch->input, BTN_TOUCH, usbtouch->touch);
+
+	if (swap_xy) {
+		input_report_abs(usbtouch->input, ABS_X, usbtouch->y);
+		input_report_abs(usbtouch->input, ABS_Y, usbtouch->x);
+	} else {
+		input_report_abs(usbtouch->input, ABS_X, usbtouch->x);
+		input_report_abs(usbtouch->input, ABS_Y, usbtouch->y);
+	}
+	if (type->max_press)
+		input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press);
+	input_sync(usbtouch->input);
+}
+
+
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+                                   unsigned char *pkt, int len)
+{
+	unsigned char *buffer;
+	int pkt_len, pos, buf_len, tmp;
+
+	/* process buffer */
+	if (unlikely(usbtouch->buf_len)) {
+		/* try to get size */
+		pkt_len = usbtouch->type->get_pkt_len(
+				usbtouch->buffer, usbtouch->buf_len);
+
+		/* drop? */
+		if (unlikely(!pkt_len))
+			goto out_flush_buf;
+
+		/* need to append -pkt_len bytes before able to get size */
+		if (unlikely(pkt_len < 0)) {
+			int append = -pkt_len;
+			if (unlikely(append > len))
+			       append = len;
+			if (usbtouch->buf_len + append >= usbtouch->type->rept_size)
+				goto out_flush_buf;
+			memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, append);
+			usbtouch->buf_len += append;
+
+			pkt_len = usbtouch->type->get_pkt_len(
+					usbtouch->buffer, usbtouch->buf_len);
+			if (pkt_len < 0)
+				return;
+		}
+
+		/* append */
+		tmp = pkt_len - usbtouch->buf_len;
+		if (usbtouch->buf_len + tmp >= usbtouch->type->rept_size)
+			goto out_flush_buf;
+		memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp);
+		usbtouch_process_pkt(usbtouch, usbtouch->buffer, pkt_len);
+
+		buffer = pkt + tmp;
+		buf_len = len - tmp;
+	} else {
+		buffer = pkt;
+		buf_len = len;
+	}
+
+	/* loop over the received packet, process */
+	pos = 0;
+	while (pos < buf_len) {
+		/* get packet len */
+		pkt_len = usbtouch->type->get_pkt_len(buffer + pos,
+							buf_len - pos);
+
+		/* unknown packet: skip one byte */
+		if (unlikely(!pkt_len)) {
+			pos++;
+			continue;
+		}
+
+		/* full packet: process */
+		if (likely((pkt_len > 0) && (pkt_len <= buf_len - pos))) {
+			usbtouch_process_pkt(usbtouch, buffer + pos, pkt_len);
+		} else {
+			/* incomplete packet: save in buffer */
+			memcpy(usbtouch->buffer, buffer + pos, buf_len - pos);
+			usbtouch->buf_len = buf_len - pos;
+			return;
+		}
+		pos += pkt_len;
+	}
+
+out_flush_buf:
+	usbtouch->buf_len = 0;
+	return;
+}
+#endif
+
+
+static void usbtouch_irq(struct urb *urb)
+{
+	struct usbtouch_usb *usbtouch = urb->context;
+	struct device *dev = &usbtouch->interface->dev;
+	int retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ETIME:
+		/* this urb is timing out */
+		dev_dbg(dev,
+			"%s - urb timed out - was the device unplugged?\n",
+			__func__);
+		return;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+	case -EPIPE:
+		/* this urb is terminated, clean up */
+		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
+			__func__, urb->status);
+		return;
+	default:
+		dev_dbg(dev, "%s - nonzero urb status received: %d\n",
+			__func__, urb->status);
+		goto exit;
+	}
+
+	usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);
+
+exit:
+	usb_mark_last_busy(interface_to_usbdev(usbtouch->interface));
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(dev, "%s - usb_submit_urb failed with result: %d\n",
+			__func__, retval);
+}
+
+static int usbtouch_open(struct input_dev *input)
+{
+	struct usbtouch_usb *usbtouch = input_get_drvdata(input);
+	int r;
+
+	usbtouch->irq->dev = interface_to_usbdev(usbtouch->interface);
+
+	r = usb_autopm_get_interface(usbtouch->interface) ? -EIO : 0;
+	if (r < 0)
+		goto out;
+
+	if (!usbtouch->type->irq_always) {
+		if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) {
+			r = -EIO;
+			goto out_put;
+		}
+	}
+
+	usbtouch->interface->needs_remote_wakeup = 1;
+out_put:
+	usb_autopm_put_interface(usbtouch->interface);
+out:
+	return r;
+}
+
+static void usbtouch_close(struct input_dev *input)
+{
+	struct usbtouch_usb *usbtouch = input_get_drvdata(input);
+	int r;
+
+	if (!usbtouch->type->irq_always)
+		usb_kill_urb(usbtouch->irq);
+	r = usb_autopm_get_interface(usbtouch->interface);
+	usbtouch->interface->needs_remote_wakeup = 0;
+	if (!r)
+		usb_autopm_put_interface(usbtouch->interface);
+}
+
+static int usbtouch_suspend
+(struct usb_interface *intf, pm_message_t message)
+{
+	struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
+
+	usb_kill_urb(usbtouch->irq);
+
+	return 0;
+}
+
+static int usbtouch_resume(struct usb_interface *intf)
+{
+	struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
+	struct input_dev *input = usbtouch->input;
+	int result = 0;
+
+	mutex_lock(&input->mutex);
+	if (input->users || usbtouch->type->irq_always)
+		result = usb_submit_urb(usbtouch->irq, GFP_NOIO);
+	mutex_unlock(&input->mutex);
+
+	return result;
+}
+
+static int usbtouch_reset_resume(struct usb_interface *intf)
+{
+	struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
+	struct input_dev *input = usbtouch->input;
+	int err = 0;
+
+	/* reinit the device */
+	if (usbtouch->type->init) {
+		err = usbtouch->type->init(usbtouch);
+		if (err) {
+			dev_dbg(&intf->dev,
+				"%s - type->init() failed, err: %d\n",
+				__func__, err);
+			return err;
+		}
+	}
+
+	/* restart IO if needed */
+	mutex_lock(&input->mutex);
+	if (input->users)
+		err = usb_submit_urb(usbtouch->irq, GFP_NOIO);
+	mutex_unlock(&input->mutex);
+
+	return err;
+}
+
+static void usbtouch_free_buffers(struct usb_device *udev,
+				  struct usbtouch_usb *usbtouch)
+{
+	usb_free_coherent(udev, usbtouch->data_size,
+			  usbtouch->data, usbtouch->data_dma);
+	kfree(usbtouch->buffer);
+}
+
+static struct usb_endpoint_descriptor *
+usbtouch_get_input_endpoint(struct usb_host_interface *interface)
+{
+	int i;
+
+	for (i = 0; i < interface->desc.bNumEndpoints; i++)
+		if (usb_endpoint_dir_in(&interface->endpoint[i].desc))
+			return &interface->endpoint[i].desc;
+
+	return NULL;
+}
+
+static int usbtouch_probe(struct usb_interface *intf,
+			  const struct usb_device_id *id)
+{
+	struct usbtouch_usb *usbtouch;
+	struct input_dev *input_dev;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usbtouch_device_info *type;
+	int err = -ENOMEM;
+
+	/* some devices are ignored */
+	if (id->driver_info == DEVTYPE_IGNORE)
+		return -ENODEV;
+
+	endpoint = usbtouch_get_input_endpoint(intf->cur_altsetting);
+	if (!endpoint)
+		return -ENXIO;
+
+	usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!usbtouch || !input_dev)
+		goto out_free;
+
+	type = &usbtouch_dev_info[id->driver_info];
+	usbtouch->type = type;
+	if (!type->process_pkt)
+		type->process_pkt = usbtouch_process_pkt;
+
+	usbtouch->data_size = type->rept_size;
+	if (type->get_pkt_len) {
+		/*
+		 * When dealing with variable-length packets we should
+		 * not request more than wMaxPacketSize bytes at once
+		 * as we do not know if there is more data coming or
+		 * we filled exactly wMaxPacketSize bytes and there is
+		 * nothing else.
+		 */
+		usbtouch->data_size = min(usbtouch->data_size,
+					  usb_endpoint_maxp(endpoint));
+	}
+
+	usbtouch->data = usb_alloc_coherent(udev, usbtouch->data_size,
+					    GFP_KERNEL, &usbtouch->data_dma);
+	if (!usbtouch->data)
+		goto out_free;
+
+	if (type->get_pkt_len) {
+		usbtouch->buffer = kmalloc(type->rept_size, GFP_KERNEL);
+		if (!usbtouch->buffer)
+			goto out_free_buffers;
+	}
+
+	usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!usbtouch->irq) {
+		dev_dbg(&intf->dev,
+			"%s - usb_alloc_urb failed: usbtouch->irq\n", __func__);
+		goto out_free_buffers;
+	}
+
+	usbtouch->interface = intf;
+	usbtouch->input = input_dev;
+
+	if (udev->manufacturer)
+		strlcpy(usbtouch->name, udev->manufacturer, sizeof(usbtouch->name));
+
+	if (udev->product) {
+		if (udev->manufacturer)
+			strlcat(usbtouch->name, " ", sizeof(usbtouch->name));
+		strlcat(usbtouch->name, udev->product, sizeof(usbtouch->name));
+	}
+
+	if (!strlen(usbtouch->name))
+		snprintf(usbtouch->name, sizeof(usbtouch->name),
+			"USB Touchscreen %04x:%04x",
+			 le16_to_cpu(udev->descriptor.idVendor),
+			 le16_to_cpu(udev->descriptor.idProduct));
+
+	usb_make_path(udev, usbtouch->phys, sizeof(usbtouch->phys));
+	strlcat(usbtouch->phys, "/input0", sizeof(usbtouch->phys));
+
+	input_dev->name = usbtouch->name;
+	input_dev->phys = usbtouch->phys;
+	usb_to_input_id(udev, &input_dev->id);
+	input_dev->dev.parent = &intf->dev;
+
+	input_set_drvdata(input_dev, usbtouch);
+
+	input_dev->open = usbtouch_open;
+	input_dev->close = usbtouch_close;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0);
+	if (type->max_press)
+		input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press,
+		                     type->max_press, 0, 0);
+
+	if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT)
+		usb_fill_int_urb(usbtouch->irq, udev,
+			 usb_rcvintpipe(udev, endpoint->bEndpointAddress),
+			 usbtouch->data, usbtouch->data_size,
+			 usbtouch_irq, usbtouch, endpoint->bInterval);
+	else
+		usb_fill_bulk_urb(usbtouch->irq, udev,
+			 usb_rcvbulkpipe(udev, endpoint->bEndpointAddress),
+			 usbtouch->data, usbtouch->data_size,
+			 usbtouch_irq, usbtouch);
+
+	usbtouch->irq->dev = udev;
+	usbtouch->irq->transfer_dma = usbtouch->data_dma;
+	usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* device specific allocations */
+	if (type->alloc) {
+		err = type->alloc(usbtouch);
+		if (err) {
+			dev_dbg(&intf->dev,
+				"%s - type->alloc() failed, err: %d\n",
+				__func__, err);
+			goto out_free_urb;
+		}
+	}
+
+	/* device specific initialisation*/
+	if (type->init) {
+		err = type->init(usbtouch);
+		if (err) {
+			dev_dbg(&intf->dev,
+				"%s - type->init() failed, err: %d\n",
+				__func__, err);
+			goto out_do_exit;
+		}
+	}
+
+	err = input_register_device(usbtouch->input);
+	if (err) {
+		dev_dbg(&intf->dev,
+			"%s - input_register_device failed, err: %d\n",
+			__func__, err);
+		goto out_do_exit;
+	}
+
+	usb_set_intfdata(intf, usbtouch);
+
+	if (usbtouch->type->irq_always) {
+		/* this can't fail */
+		usb_autopm_get_interface(intf);
+		err = usb_submit_urb(usbtouch->irq, GFP_KERNEL);
+		if (err) {
+			usb_autopm_put_interface(intf);
+			dev_err(&intf->dev,
+				"%s - usb_submit_urb failed with result: %d\n",
+				__func__, err);
+			goto out_unregister_input;
+		}
+	}
+
+	return 0;
+
+out_unregister_input:
+	input_unregister_device(input_dev);
+	input_dev = NULL;
+out_do_exit:
+	if (type->exit)
+		type->exit(usbtouch);
+out_free_urb:
+	usb_free_urb(usbtouch->irq);
+out_free_buffers:
+	usbtouch_free_buffers(udev, usbtouch);
+out_free:
+	input_free_device(input_dev);
+	kfree(usbtouch);
+	return err;
+}
+
+static void usbtouch_disconnect(struct usb_interface *intf)
+{
+	struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
+
+	if (!usbtouch)
+		return;
+
+	dev_dbg(&intf->dev,
+		"%s - usbtouch is initialized, cleaning up\n", __func__);
+
+	usb_set_intfdata(intf, NULL);
+	/* this will stop IO via close */
+	input_unregister_device(usbtouch->input);
+	usb_free_urb(usbtouch->irq);
+	if (usbtouch->type->exit)
+		usbtouch->type->exit(usbtouch);
+	usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch);
+	kfree(usbtouch);
+}
+
+MODULE_DEVICE_TABLE(usb, usbtouch_devices);
+
+static struct usb_driver usbtouch_driver = {
+	.name		= "usbtouchscreen",
+	.probe		= usbtouch_probe,
+	.disconnect	= usbtouch_disconnect,
+	.suspend	= usbtouch_suspend,
+	.resume		= usbtouch_resume,
+	.reset_resume	= usbtouch_reset_resume,
+	.id_table	= usbtouch_devices,
+	.supports_autosuspend = 1,
+};
+
+module_usb_driver(usbtouch_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("touchkitusb");
+MODULE_ALIAS("itmtouch");
+MODULE_ALIAS("mtouchusb");
diff --git a/drivers/input/touchscreen/w90p910_ts.c b/drivers/input/touchscreen/w90p910_ts.c
new file mode 100644
index 0000000..da6004e
--- /dev/null
+++ b/drivers/input/touchscreen/w90p910_ts.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2008 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * 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;version 2 of the License.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+/* ADC controller bit defines */
+#define ADC_DELAY	0xf00
+#define ADC_DOWN	0x01
+#define ADC_TSC_Y	(0x01 << 8)
+#define ADC_TSC_X	(0x00 << 8)
+#define TSC_FOURWIRE	(~(0x03 << 1))
+#define ADC_CLK_EN	(0x01 << 28)	/* ADC clock enable */
+#define ADC_READ_CON	(0x01 << 12)
+#define ADC_CONV	(0x01 << 13)
+#define ADC_SEMIAUTO	(0x01 << 14)
+#define ADC_WAITTRIG	(0x03 << 14)
+#define ADC_RST1	(0x01 << 16)
+#define ADC_RST0	(0x00 << 16)
+#define ADC_EN		(0x01 << 17)
+#define ADC_INT		(0x01 << 18)
+#define WT_INT		(0x01 << 20)
+#define ADC_INT_EN	(0x01 << 21)
+#define LVD_INT_EN	(0x01 << 22)
+#define WT_INT_EN	(0x01 << 23)
+#define ADC_DIV		(0x04 << 1)	/* div = 6 */
+
+enum ts_state {
+	TS_WAIT_NEW_PACKET,	/* We are waiting next touch report */
+	TS_WAIT_X_COORD,	/* We are waiting for ADC to report X coord */
+	TS_WAIT_Y_COORD,	/* We are waiting for ADC to report Y coord */
+	TS_IDLE,		/* Input device is closed, don't do anything */
+};
+
+struct w90p910_ts {
+	struct input_dev *input;
+	struct timer_list timer;
+	struct clk *clk;
+	int irq_num;
+	void __iomem *ts_reg;
+	spinlock_t lock;
+	enum ts_state state;
+};
+
+static void w90p910_report_event(struct w90p910_ts *w90p910_ts, bool down)
+{
+	struct input_dev *dev = w90p910_ts->input;
+
+	if (down) {
+		input_report_abs(dev, ABS_X,
+				 __raw_readl(w90p910_ts->ts_reg + 0x0c));
+		input_report_abs(dev, ABS_Y,
+				 __raw_readl(w90p910_ts->ts_reg + 0x10));
+	}
+
+	input_report_key(dev, BTN_TOUCH, down);
+	input_sync(dev);
+}
+
+static void w90p910_prepare_x_reading(struct w90p910_ts *w90p910_ts)
+{
+	unsigned long ctlreg;
+
+	__raw_writel(ADC_TSC_X, w90p910_ts->ts_reg + 0x04);
+	ctlreg = __raw_readl(w90p910_ts->ts_reg);
+	ctlreg &= ~(ADC_WAITTRIG | WT_INT | WT_INT_EN);
+	ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV;
+	__raw_writel(ctlreg, w90p910_ts->ts_reg);
+
+	w90p910_ts->state = TS_WAIT_X_COORD;
+}
+
+static void w90p910_prepare_y_reading(struct w90p910_ts *w90p910_ts)
+{
+	unsigned long ctlreg;
+
+	__raw_writel(ADC_TSC_Y, w90p910_ts->ts_reg + 0x04);
+	ctlreg = __raw_readl(w90p910_ts->ts_reg);
+	ctlreg &= ~(ADC_WAITTRIG | ADC_INT | WT_INT_EN);
+	ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV;
+	__raw_writel(ctlreg, w90p910_ts->ts_reg);
+
+	w90p910_ts->state = TS_WAIT_Y_COORD;
+}
+
+static void w90p910_prepare_next_packet(struct w90p910_ts *w90p910_ts)
+{
+	unsigned long ctlreg;
+
+	ctlreg = __raw_readl(w90p910_ts->ts_reg);
+	ctlreg &= ~(ADC_INT | ADC_INT_EN | ADC_SEMIAUTO | ADC_CONV);
+	ctlreg |= ADC_WAITTRIG | WT_INT_EN;
+	__raw_writel(ctlreg, w90p910_ts->ts_reg);
+
+	w90p910_ts->state = TS_WAIT_NEW_PACKET;
+}
+
+static irqreturn_t w90p910_ts_interrupt(int irq, void *dev_id)
+{
+	struct w90p910_ts *w90p910_ts = dev_id;
+	unsigned long flags;
+
+	spin_lock_irqsave(&w90p910_ts->lock, flags);
+
+	switch (w90p910_ts->state) {
+	case TS_WAIT_NEW_PACKET:
+		/*
+		 * The controller only generates interrupts when pen
+		 * is down.
+		 */
+		del_timer(&w90p910_ts->timer);
+		w90p910_prepare_x_reading(w90p910_ts);
+		break;
+
+
+	case TS_WAIT_X_COORD:
+		w90p910_prepare_y_reading(w90p910_ts);
+		break;
+
+	case TS_WAIT_Y_COORD:
+		w90p910_report_event(w90p910_ts, true);
+		w90p910_prepare_next_packet(w90p910_ts);
+		mod_timer(&w90p910_ts->timer, jiffies + msecs_to_jiffies(100));
+		break;
+
+	case TS_IDLE:
+		break;
+	}
+
+	spin_unlock_irqrestore(&w90p910_ts->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static void w90p910_check_pen_up(unsigned long data)
+{
+	struct w90p910_ts *w90p910_ts = (struct w90p910_ts *) data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&w90p910_ts->lock, flags);
+
+	if (w90p910_ts->state == TS_WAIT_NEW_PACKET &&
+	    !(__raw_readl(w90p910_ts->ts_reg + 0x04) & ADC_DOWN)) {
+
+		w90p910_report_event(w90p910_ts, false);
+	}
+
+	spin_unlock_irqrestore(&w90p910_ts->lock, flags);
+}
+
+static int w90p910_open(struct input_dev *dev)
+{
+	struct w90p910_ts *w90p910_ts = input_get_drvdata(dev);
+	unsigned long val;
+
+	/* enable the ADC clock */
+	clk_enable(w90p910_ts->clk);
+
+	__raw_writel(ADC_RST1, w90p910_ts->ts_reg);
+	msleep(1);
+	__raw_writel(ADC_RST0, w90p910_ts->ts_reg);
+	msleep(1);
+
+	/* set delay and screen type */
+	val = __raw_readl(w90p910_ts->ts_reg + 0x04);
+	__raw_writel(val & TSC_FOURWIRE, w90p910_ts->ts_reg + 0x04);
+	__raw_writel(ADC_DELAY, w90p910_ts->ts_reg + 0x08);
+
+	w90p910_ts->state = TS_WAIT_NEW_PACKET;
+	wmb();
+
+	/* set trigger mode */
+	val = __raw_readl(w90p910_ts->ts_reg);
+	val |= ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN;
+	__raw_writel(val, w90p910_ts->ts_reg);
+
+	return 0;
+}
+
+static void w90p910_close(struct input_dev *dev)
+{
+	struct w90p910_ts *w90p910_ts = input_get_drvdata(dev);
+	unsigned long val;
+
+	/* disable trigger mode */
+
+	spin_lock_irq(&w90p910_ts->lock);
+
+	w90p910_ts->state = TS_IDLE;
+
+	val = __raw_readl(w90p910_ts->ts_reg);
+	val &= ~(ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN | ADC_INT_EN);
+	__raw_writel(val, w90p910_ts->ts_reg);
+
+	spin_unlock_irq(&w90p910_ts->lock);
+
+	/* Now that interrupts are shut off we can safely delete timer */
+	del_timer_sync(&w90p910_ts->timer);
+
+	/* stop the ADC clock */
+	clk_disable(w90p910_ts->clk);
+}
+
+static int w90x900ts_probe(struct platform_device *pdev)
+{
+	struct w90p910_ts *w90p910_ts;
+	struct input_dev *input_dev;
+	struct resource *res;
+	int err;
+
+	w90p910_ts = kzalloc(sizeof(struct w90p910_ts), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!w90p910_ts || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	w90p910_ts->input = input_dev;
+	w90p910_ts->state = TS_IDLE;
+	spin_lock_init(&w90p910_ts->lock);
+	setup_timer(&w90p910_ts->timer, w90p910_check_pen_up,
+		    (unsigned long)w90p910_ts);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		err = -ENXIO;
+		goto fail1;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res),
+				pdev->name)) {
+		err = -EBUSY;
+		goto fail1;
+	}
+
+	w90p910_ts->ts_reg = ioremap(res->start, resource_size(res));
+	if (!w90p910_ts->ts_reg) {
+		err = -ENOMEM;
+		goto fail2;
+	}
+
+	w90p910_ts->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(w90p910_ts->clk)) {
+		err = PTR_ERR(w90p910_ts->clk);
+		goto fail3;
+	}
+
+	input_dev->name = "W90P910 TouchScreen";
+	input_dev->phys = "w90p910ts/event0";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor  = 0x0005;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &pdev->dev;
+	input_dev->open = w90p910_open;
+	input_dev->close = w90p910_close;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_set_abs_params(input_dev, ABS_X, 0, 0x400, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, 0x400, 0, 0);
+
+	input_set_drvdata(input_dev, w90p910_ts);
+
+	w90p910_ts->irq_num = platform_get_irq(pdev, 0);
+	if (request_irq(w90p910_ts->irq_num, w90p910_ts_interrupt,
+			0, "w90p910ts", w90p910_ts)) {
+		err = -EBUSY;
+		goto fail4;
+	}
+
+	err = input_register_device(w90p910_ts->input);
+	if (err)
+		goto fail5;
+
+	platform_set_drvdata(pdev, w90p910_ts);
+
+	return 0;
+
+fail5:	free_irq(w90p910_ts->irq_num, w90p910_ts);
+fail4:	clk_put(w90p910_ts->clk);
+fail3:	iounmap(w90p910_ts->ts_reg);
+fail2:	release_mem_region(res->start, resource_size(res));
+fail1:	input_free_device(input_dev);
+	kfree(w90p910_ts);
+	return err;
+}
+
+static int w90x900ts_remove(struct platform_device *pdev)
+{
+	struct w90p910_ts *w90p910_ts = platform_get_drvdata(pdev);
+	struct resource *res;
+
+	free_irq(w90p910_ts->irq_num, w90p910_ts);
+	del_timer_sync(&w90p910_ts->timer);
+	iounmap(w90p910_ts->ts_reg);
+
+	clk_put(w90p910_ts->clk);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	input_unregister_device(w90p910_ts->input);
+	kfree(w90p910_ts);
+
+	return 0;
+}
+
+static struct platform_driver w90x900ts_driver = {
+	.probe		= w90x900ts_probe,
+	.remove		= w90x900ts_remove,
+	.driver		= {
+		.name	= "nuc900-ts",
+	},
+};
+module_platform_driver(w90x900ts_driver);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("w90p910 touch screen driver!");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nuc900-ts");
diff --git a/drivers/input/touchscreen/wacom_i2c.c b/drivers/input/touchscreen/wacom_i2c.c
new file mode 100644
index 0000000..8d7a285
--- /dev/null
+++ b/drivers/input/touchscreen/wacom_i2c.c
@@ -0,0 +1,285 @@
+/*
+ * Wacom Penabled Driver for I2C
+ *
+ * Copyright (c) 2011 - 2013 Tatsunosuke Tobita, Wacom.
+ * <tobita.tatsunosuke@wacom.co.jp>
+ *
+ * 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 of 2 of the License,
+ * or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <asm/unaligned.h>
+
+#define WACOM_CMD_QUERY0	0x04
+#define WACOM_CMD_QUERY1	0x00
+#define WACOM_CMD_QUERY2	0x33
+#define WACOM_CMD_QUERY3	0x02
+#define WACOM_CMD_THROW0	0x05
+#define WACOM_CMD_THROW1	0x00
+#define WACOM_QUERY_SIZE	19
+
+struct wacom_features {
+	int x_max;
+	int y_max;
+	int pressure_max;
+	char fw_version;
+};
+
+struct wacom_i2c {
+	struct i2c_client *client;
+	struct input_dev *input;
+	u8 data[WACOM_QUERY_SIZE];
+	bool prox;
+	int tool;
+};
+
+static int wacom_query_device(struct i2c_client *client,
+			      struct wacom_features *features)
+{
+	int ret;
+	u8 cmd1[] = { WACOM_CMD_QUERY0, WACOM_CMD_QUERY1,
+			WACOM_CMD_QUERY2, WACOM_CMD_QUERY3 };
+	u8 cmd2[] = { WACOM_CMD_THROW0, WACOM_CMD_THROW1 };
+	u8 data[WACOM_QUERY_SIZE];
+	struct i2c_msg msgs[] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = sizeof(cmd1),
+			.buf = cmd1,
+		},
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = sizeof(cmd2),
+			.buf = cmd2,
+		},
+		{
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = sizeof(data),
+			.buf = data,
+		},
+	};
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret < 0)
+		return ret;
+	if (ret != ARRAY_SIZE(msgs))
+		return -EIO;
+
+	features->x_max = get_unaligned_le16(&data[3]);
+	features->y_max = get_unaligned_le16(&data[5]);
+	features->pressure_max = get_unaligned_le16(&data[11]);
+	features->fw_version = get_unaligned_le16(&data[13]);
+
+	dev_dbg(&client->dev,
+		"x_max:%d, y_max:%d, pressure:%d, fw:%d\n",
+		features->x_max, features->y_max,
+		features->pressure_max, features->fw_version);
+
+	return 0;
+}
+
+static irqreturn_t wacom_i2c_irq(int irq, void *dev_id)
+{
+	struct wacom_i2c *wac_i2c = dev_id;
+	struct input_dev *input = wac_i2c->input;
+	u8 *data = wac_i2c->data;
+	unsigned int x, y, pressure;
+	unsigned char tsw, f1, f2, ers;
+	int error;
+
+	error = i2c_master_recv(wac_i2c->client,
+				wac_i2c->data, sizeof(wac_i2c->data));
+	if (error < 0)
+		goto out;
+
+	tsw = data[3] & 0x01;
+	ers = data[3] & 0x04;
+	f1 = data[3] & 0x02;
+	f2 = data[3] & 0x10;
+	x = le16_to_cpup((__le16 *)&data[4]);
+	y = le16_to_cpup((__le16 *)&data[6]);
+	pressure = le16_to_cpup((__le16 *)&data[8]);
+
+	if (!wac_i2c->prox)
+		wac_i2c->tool = (data[3] & 0x0c) ?
+			BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+
+	wac_i2c->prox = data[3] & 0x20;
+
+	input_report_key(input, BTN_TOUCH, tsw || ers);
+	input_report_key(input, wac_i2c->tool, wac_i2c->prox);
+	input_report_key(input, BTN_STYLUS, f1);
+	input_report_key(input, BTN_STYLUS2, f2);
+	input_report_abs(input, ABS_X, x);
+	input_report_abs(input, ABS_Y, y);
+	input_report_abs(input, ABS_PRESSURE, pressure);
+	input_sync(input);
+
+out:
+	return IRQ_HANDLED;
+}
+
+static int wacom_i2c_open(struct input_dev *dev)
+{
+	struct wacom_i2c *wac_i2c = input_get_drvdata(dev);
+	struct i2c_client *client = wac_i2c->client;
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static void wacom_i2c_close(struct input_dev *dev)
+{
+	struct wacom_i2c *wac_i2c = input_get_drvdata(dev);
+	struct i2c_client *client = wac_i2c->client;
+
+	disable_irq(client->irq);
+}
+
+static int wacom_i2c_probe(struct i2c_client *client,
+				     const struct i2c_device_id *id)
+{
+	struct wacom_i2c *wac_i2c;
+	struct input_dev *input;
+	struct wacom_features features = { 0 };
+	int error;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "i2c_check_functionality error\n");
+		return -EIO;
+	}
+
+	error = wacom_query_device(client, &features);
+	if (error)
+		return error;
+
+	wac_i2c = kzalloc(sizeof(*wac_i2c), GFP_KERNEL);
+	input = input_allocate_device();
+	if (!wac_i2c || !input) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	wac_i2c->client = client;
+	wac_i2c->input = input;
+
+	input->name = "Wacom I2C Digitizer";
+	input->id.bustype = BUS_I2C;
+	input->id.vendor = 0x56a;
+	input->id.version = features.fw_version;
+	input->dev.parent = &client->dev;
+	input->open = wacom_i2c_open;
+	input->close = wacom_i2c_close;
+
+	input->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	__set_bit(BTN_TOOL_PEN, input->keybit);
+	__set_bit(BTN_TOOL_RUBBER, input->keybit);
+	__set_bit(BTN_STYLUS, input->keybit);
+	__set_bit(BTN_STYLUS2, input->keybit);
+	__set_bit(BTN_TOUCH, input->keybit);
+
+	input_set_abs_params(input, ABS_X, 0, features.x_max, 0, 0);
+	input_set_abs_params(input, ABS_Y, 0, features.y_max, 0, 0);
+	input_set_abs_params(input, ABS_PRESSURE,
+			     0, features.pressure_max, 0, 0);
+
+	input_set_drvdata(input, wac_i2c);
+
+	error = request_threaded_irq(client->irq, NULL, wacom_i2c_irq,
+				     IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+				     "wacom_i2c", wac_i2c);
+	if (error) {
+		dev_err(&client->dev,
+			"Failed to enable IRQ, error: %d\n", error);
+		goto err_free_mem;
+	}
+
+	/* Disable the IRQ, we'll enable it in wac_i2c_open() */
+	disable_irq(client->irq);
+
+	error = input_register_device(wac_i2c->input);
+	if (error) {
+		dev_err(&client->dev,
+			"Failed to register input device, error: %d\n", error);
+		goto err_free_irq;
+	}
+
+	i2c_set_clientdata(client, wac_i2c);
+	return 0;
+
+err_free_irq:
+	free_irq(client->irq, wac_i2c);
+err_free_mem:
+	input_free_device(input);
+	kfree(wac_i2c);
+
+	return error;
+}
+
+static int wacom_i2c_remove(struct i2c_client *client)
+{
+	struct wacom_i2c *wac_i2c = i2c_get_clientdata(client);
+
+	free_irq(client->irq, wac_i2c);
+	input_unregister_device(wac_i2c->input);
+	kfree(wac_i2c);
+
+	return 0;
+}
+
+static int __maybe_unused wacom_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	disable_irq(client->irq);
+
+	return 0;
+}
+
+static int __maybe_unused wacom_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(wacom_i2c_pm, wacom_i2c_suspend, wacom_i2c_resume);
+
+static const struct i2c_device_id wacom_i2c_id[] = {
+	{ "WAC_I2C_EMR", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, wacom_i2c_id);
+
+static struct i2c_driver wacom_i2c_driver = {
+	.driver	= {
+		.name	= "wacom_i2c",
+		.pm	= &wacom_i2c_pm,
+	},
+
+	.probe		= wacom_i2c_probe,
+	.remove		= wacom_i2c_remove,
+	.id_table	= wacom_i2c_id,
+};
+module_i2c_driver(wacom_i2c_driver);
+
+MODULE_AUTHOR("Tatsunosuke Tobita <tobita.tatsunosuke@wacom.co.jp>");
+MODULE_DESCRIPTION("WACOM EMR I2C Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
new file mode 100644
index 0000000..3ed0ce1
--- /dev/null
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -0,0 +1,596 @@
+/*
+ * Wacom W8001 penabled serial touchscreen driver
+ *
+ * Copyright (c) 2008 Jaya Kumar
+ * Copyright (c) 2010 Red Hat, Inc.
+ * Copyright (c) 2010 - 2011 Ping Cheng, Wacom. <pingc@wacom.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * Layout based on Elo serial touchscreen driver by Vojtech Pavlik
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input/mt.h>
+#include <linux/serio.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+
+#define DRIVER_DESC	"Wacom W8001 serial touchscreen driver"
+
+MODULE_AUTHOR("Jaya Kumar <jayakumar.lkml@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define W8001_MAX_LENGTH	13
+#define W8001_LEAD_MASK		0x80
+#define W8001_LEAD_BYTE		0x80
+#define W8001_TAB_MASK		0x40
+#define W8001_TAB_BYTE		0x40
+/* set in first byte of touch data packets */
+#define W8001_TOUCH_MASK	(0x10 | W8001_LEAD_MASK)
+#define W8001_TOUCH_BYTE	(0x10 | W8001_LEAD_BYTE)
+
+#define W8001_QUERY_PACKET	0x20
+
+#define W8001_CMD_STOP		'0'
+#define W8001_CMD_START		'1'
+#define W8001_CMD_QUERY		'*'
+#define W8001_CMD_TOUCHQUERY	'%'
+
+/* length of data packets in bytes, depends on device. */
+#define W8001_PKTLEN_TOUCH93	5
+#define W8001_PKTLEN_TOUCH9A	7
+#define W8001_PKTLEN_TPCPEN	9
+#define W8001_PKTLEN_TPCCTL	11	/* control packet */
+#define W8001_PKTLEN_TOUCH2FG	13
+
+/* resolution in points/mm */
+#define W8001_PEN_RESOLUTION    100
+#define W8001_TOUCH_RESOLUTION  10
+
+struct w8001_coord {
+	u8 rdy;
+	u8 tsw;
+	u8 f1;
+	u8 f2;
+	u16 x;
+	u16 y;
+	u16 pen_pressure;
+	u8 tilt_x;
+	u8 tilt_y;
+};
+
+/* touch query reply packet */
+struct w8001_touch_query {
+	u16 x;
+	u16 y;
+	u8 panel_res;
+	u8 capacity_res;
+	u8 sensor_id;
+};
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct w8001 {
+	struct input_dev *dev;
+	struct serio *serio;
+	struct completion cmd_done;
+	int id;
+	int idx;
+	unsigned char response_type;
+	unsigned char response[W8001_MAX_LENGTH];
+	unsigned char data[W8001_MAX_LENGTH];
+	char phys[32];
+	int type;
+	unsigned int pktlen;
+	u16 max_touch_x;
+	u16 max_touch_y;
+	u16 max_pen_x;
+	u16 max_pen_y;
+	char name[64];
+};
+
+static void parse_pen_data(u8 *data, struct w8001_coord *coord)
+{
+	memset(coord, 0, sizeof(*coord));
+
+	coord->rdy = data[0] & 0x20;
+	coord->tsw = data[0] & 0x01;
+	coord->f1 = data[0] & 0x02;
+	coord->f2 = data[0] & 0x04;
+
+	coord->x = (data[1] & 0x7F) << 9;
+	coord->x |= (data[2] & 0x7F) << 2;
+	coord->x |= (data[6] & 0x60) >> 5;
+
+	coord->y = (data[3] & 0x7F) << 9;
+	coord->y |= (data[4] & 0x7F) << 2;
+	coord->y |= (data[6] & 0x18) >> 3;
+
+	coord->pen_pressure = data[5] & 0x7F;
+	coord->pen_pressure |= (data[6] & 0x07) << 7 ;
+
+	coord->tilt_x = data[7] & 0x7F;
+	coord->tilt_y = data[8] & 0x7F;
+}
+
+static void parse_single_touch(u8 *data, struct w8001_coord *coord)
+{
+	coord->x = (data[1] << 7) | data[2];
+	coord->y = (data[3] << 7) | data[4];
+	coord->tsw = data[0] & 0x01;
+}
+
+static void scale_touch_coordinates(struct w8001 *w8001,
+				    unsigned int *x, unsigned int *y)
+{
+	if (w8001->max_pen_x && w8001->max_touch_x)
+		*x = *x * w8001->max_pen_x / w8001->max_touch_x;
+
+	if (w8001->max_pen_y && w8001->max_touch_y)
+		*y = *y * w8001->max_pen_y / w8001->max_touch_y;
+}
+
+static void parse_multi_touch(struct w8001 *w8001)
+{
+	struct input_dev *dev = w8001->dev;
+	unsigned char *data = w8001->data;
+	unsigned int x, y;
+	int i;
+	int count = 0;
+
+	for (i = 0; i < 2; i++) {
+		bool touch = data[0] & (1 << i);
+
+		input_mt_slot(dev, i);
+		input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
+		if (touch) {
+			x = (data[6 * i + 1] << 7) | data[6 * i + 2];
+			y = (data[6 * i + 3] << 7) | data[6 * i + 4];
+			/* data[5,6] and [11,12] is finger capacity */
+
+			/* scale to pen maximum */
+			scale_touch_coordinates(w8001, &x, &y);
+
+			input_report_abs(dev, ABS_MT_POSITION_X, x);
+			input_report_abs(dev, ABS_MT_POSITION_Y, y);
+			count++;
+		}
+	}
+
+	/* emulate single touch events when stylus is out of proximity.
+	 * This is to make single touch backward support consistent
+	 * across all Wacom single touch devices.
+	 */
+	if (w8001->type != BTN_TOOL_PEN &&
+			    w8001->type != BTN_TOOL_RUBBER) {
+		w8001->type = count == 1 ? BTN_TOOL_FINGER : KEY_RESERVED;
+		input_mt_report_pointer_emulation(dev, true);
+	}
+
+	input_sync(dev);
+}
+
+static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
+{
+	memset(query, 0, sizeof(*query));
+
+	query->panel_res = data[1];
+	query->sensor_id = data[2] & 0x7;
+	query->capacity_res = data[7];
+
+	query->x = data[3] << 9;
+	query->x |= data[4] << 2;
+	query->x |= (data[2] >> 5) & 0x3;
+
+	query->y = data[5] << 9;
+	query->y |= data[6] << 2;
+	query->y |= (data[2] >> 3) & 0x3;
+
+	/* Early days' single-finger touch models need the following defaults */
+	if (!query->x && !query->y) {
+		query->x = 1024;
+		query->y = 1024;
+		if (query->panel_res)
+			query->x = query->y = (1 << query->panel_res);
+		query->panel_res = W8001_TOUCH_RESOLUTION;
+	}
+}
+
+static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
+{
+	struct input_dev *dev = w8001->dev;
+
+	/*
+	 * We have 1 bit for proximity (rdy) and 3 bits for tip, side,
+	 * side2/eraser. If rdy && f2 are set, this can be either pen + side2,
+	 * or eraser. Assume:
+	 * - if dev is already in proximity and f2 is toggled → pen + side2
+	 * - if dev comes into proximity with f2 set → eraser
+	 * If f2 disappears after assuming eraser, fake proximity out for
+	 * eraser and in for pen.
+	 */
+
+	switch (w8001->type) {
+	case BTN_TOOL_RUBBER:
+		if (!coord->f2) {
+			input_report_abs(dev, ABS_PRESSURE, 0);
+			input_report_key(dev, BTN_TOUCH, 0);
+			input_report_key(dev, BTN_STYLUS, 0);
+			input_report_key(dev, BTN_STYLUS2, 0);
+			input_report_key(dev, BTN_TOOL_RUBBER, 0);
+			input_sync(dev);
+			w8001->type = BTN_TOOL_PEN;
+		}
+		break;
+
+	case BTN_TOOL_FINGER:
+		input_report_key(dev, BTN_TOUCH, 0);
+		input_report_key(dev, BTN_TOOL_FINGER, 0);
+		input_sync(dev);
+		/* fall through */
+
+	case KEY_RESERVED:
+		w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+		break;
+
+	default:
+		input_report_key(dev, BTN_STYLUS2, coord->f2);
+		break;
+	}
+
+	input_report_abs(dev, ABS_X, coord->x);
+	input_report_abs(dev, ABS_Y, coord->y);
+	input_report_abs(dev, ABS_PRESSURE, coord->pen_pressure);
+	input_report_key(dev, BTN_TOUCH, coord->tsw);
+	input_report_key(dev, BTN_STYLUS, coord->f1);
+	input_report_key(dev, w8001->type, coord->rdy);
+	input_sync(dev);
+
+	if (!coord->rdy)
+		w8001->type = KEY_RESERVED;
+}
+
+static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
+{
+	struct input_dev *dev = w8001->dev;
+	unsigned int x = coord->x;
+	unsigned int y = coord->y;
+
+	/* scale to pen maximum */
+	scale_touch_coordinates(w8001, &x, &y);
+
+	input_report_abs(dev, ABS_X, x);
+	input_report_abs(dev, ABS_Y, y);
+	input_report_key(dev, BTN_TOUCH, coord->tsw);
+	input_report_key(dev, BTN_TOOL_FINGER, coord->tsw);
+
+	input_sync(dev);
+
+	w8001->type = coord->tsw ? BTN_TOOL_FINGER : KEY_RESERVED;
+}
+
+static irqreturn_t w8001_interrupt(struct serio *serio,
+				   unsigned char data, unsigned int flags)
+{
+	struct w8001 *w8001 = serio_get_drvdata(serio);
+	struct w8001_coord coord;
+	unsigned char tmp;
+
+	w8001->data[w8001->idx] = data;
+	switch (w8001->idx++) {
+	case 0:
+		if ((data & W8001_LEAD_MASK) != W8001_LEAD_BYTE) {
+			pr_debug("w8001: unsynchronized data: 0x%02x\n", data);
+			w8001->idx = 0;
+		}
+		break;
+
+	case W8001_PKTLEN_TOUCH93 - 1:
+	case W8001_PKTLEN_TOUCH9A - 1:
+		tmp = w8001->data[0] & W8001_TOUCH_BYTE;
+		if (tmp != W8001_TOUCH_BYTE)
+			break;
+
+		if (w8001->pktlen == w8001->idx) {
+			w8001->idx = 0;
+			if (w8001->type != BTN_TOOL_PEN &&
+			    w8001->type != BTN_TOOL_RUBBER) {
+				parse_single_touch(w8001->data, &coord);
+				report_single_touch(w8001, &coord);
+			}
+		}
+		break;
+
+	/* Pen coordinates packet */
+	case W8001_PKTLEN_TPCPEN - 1:
+		tmp = w8001->data[0] & W8001_TAB_MASK;
+		if (unlikely(tmp == W8001_TAB_BYTE))
+			break;
+
+		tmp = w8001->data[0] & W8001_TOUCH_BYTE;
+		if (tmp == W8001_TOUCH_BYTE)
+			break;
+
+		w8001->idx = 0;
+		parse_pen_data(w8001->data, &coord);
+		report_pen_events(w8001, &coord);
+		break;
+
+	/* control packet */
+	case W8001_PKTLEN_TPCCTL - 1:
+		tmp = w8001->data[0] & W8001_TOUCH_MASK;
+		if (tmp == W8001_TOUCH_BYTE)
+			break;
+
+		w8001->idx = 0;
+		memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH);
+		w8001->response_type = W8001_QUERY_PACKET;
+		complete(&w8001->cmd_done);
+		break;
+
+	/* 2 finger touch packet */
+	case W8001_PKTLEN_TOUCH2FG - 1:
+		w8001->idx = 0;
+		parse_multi_touch(w8001);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int w8001_command(struct w8001 *w8001, unsigned char command,
+			 bool wait_response)
+{
+	int rc;
+
+	w8001->response_type = 0;
+	init_completion(&w8001->cmd_done);
+
+	rc = serio_write(w8001->serio, command);
+	if (rc == 0 && wait_response) {
+
+		wait_for_completion_timeout(&w8001->cmd_done, HZ);
+		if (w8001->response_type != W8001_QUERY_PACKET)
+			rc = -EIO;
+	}
+
+	return rc;
+}
+
+static int w8001_open(struct input_dev *dev)
+{
+	struct w8001 *w8001 = input_get_drvdata(dev);
+
+	return w8001_command(w8001, W8001_CMD_START, false);
+}
+
+static void w8001_close(struct input_dev *dev)
+{
+	struct w8001 *w8001 = input_get_drvdata(dev);
+
+	w8001_command(w8001, W8001_CMD_STOP, false);
+}
+
+static int w8001_setup(struct w8001 *w8001)
+{
+	struct input_dev *dev = w8001->dev;
+	struct w8001_coord coord;
+	struct w8001_touch_query touch;
+	int error;
+
+	error = w8001_command(w8001, W8001_CMD_STOP, false);
+	if (error)
+		return error;
+
+	msleep(250);	/* wait 250ms before querying the device */
+
+	dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name));
+
+	__set_bit(INPUT_PROP_DIRECT, dev->propbit);
+
+	/* penabled? */
+	error = w8001_command(w8001, W8001_CMD_QUERY, true);
+	if (!error) {
+		__set_bit(BTN_TOUCH, dev->keybit);
+		__set_bit(BTN_TOOL_PEN, dev->keybit);
+		__set_bit(BTN_TOOL_RUBBER, dev->keybit);
+		__set_bit(BTN_STYLUS, dev->keybit);
+		__set_bit(BTN_STYLUS2, dev->keybit);
+
+		parse_pen_data(w8001->response, &coord);
+		w8001->max_pen_x = coord.x;
+		w8001->max_pen_y = coord.y;
+
+		input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
+		input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
+		input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION);
+		input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION);
+		input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
+		if (coord.tilt_x && coord.tilt_y) {
+			input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
+			input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
+		}
+		w8001->id = 0x90;
+		strlcat(w8001->name, " Penabled", sizeof(w8001->name));
+	}
+
+	/* Touch enabled? */
+	error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
+
+	/*
+	 * Some non-touch devices may reply to the touch query. But their
+	 * second byte is empty, which indicates touch is not supported.
+	 */
+	if (!error && w8001->response[1]) {
+		__set_bit(BTN_TOUCH, dev->keybit);
+		__set_bit(BTN_TOOL_FINGER, dev->keybit);
+
+		parse_touchquery(w8001->response, &touch);
+		w8001->max_touch_x = touch.x;
+		w8001->max_touch_y = touch.y;
+
+		if (w8001->max_pen_x && w8001->max_pen_y) {
+			/* if pen is supported scale to pen maximum */
+			touch.x = w8001->max_pen_x;
+			touch.y = w8001->max_pen_y;
+			touch.panel_res = W8001_PEN_RESOLUTION;
+		}
+
+		input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
+		input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
+		input_abs_set_res(dev, ABS_X, touch.panel_res);
+		input_abs_set_res(dev, ABS_Y, touch.panel_res);
+
+		switch (touch.sensor_id) {
+		case 0:
+		case 2:
+			w8001->pktlen = W8001_PKTLEN_TOUCH93;
+			w8001->id = 0x93;
+			strlcat(w8001->name, " 1FG", sizeof(w8001->name));
+			break;
+
+		case 1:
+		case 3:
+		case 4:
+			w8001->pktlen = W8001_PKTLEN_TOUCH9A;
+			strlcat(w8001->name, " 1FG", sizeof(w8001->name));
+			w8001->id = 0x9a;
+			break;
+
+		case 5:
+			w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
+
+			input_mt_init_slots(dev, 2, 0);
+			input_set_abs_params(dev, ABS_MT_POSITION_X,
+						0, touch.x, 0, 0);
+			input_set_abs_params(dev, ABS_MT_POSITION_Y,
+						0, touch.y, 0, 0);
+			input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
+						0, MT_TOOL_MAX, 0, 0);
+
+			strlcat(w8001->name, " 2FG", sizeof(w8001->name));
+			if (w8001->max_pen_x && w8001->max_pen_y)
+				w8001->id = 0xE3;
+			else
+				w8001->id = 0xE2;
+			break;
+		}
+	}
+
+	strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
+
+	return 0;
+}
+
+/*
+ * w8001_disconnect() is the opposite of w8001_connect()
+ */
+
+static void w8001_disconnect(struct serio *serio)
+{
+	struct w8001 *w8001 = serio_get_drvdata(serio);
+
+	serio_close(serio);
+
+	input_unregister_device(w8001->dev);
+	kfree(w8001);
+
+	serio_set_drvdata(serio, NULL);
+}
+
+/*
+ * w8001_connect() is the routine that is called when someone adds a
+ * new serio device that supports the w8001 protocol and registers it as
+ * an input device.
+ */
+
+static int w8001_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct w8001 *w8001;
+	struct input_dev *input_dev;
+	int err;
+
+	w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!w8001 || !input_dev) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	w8001->serio = serio;
+	w8001->dev = input_dev;
+	init_completion(&w8001->cmd_done);
+	snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
+
+	serio_set_drvdata(serio, w8001);
+	err = serio_open(serio, drv);
+	if (err)
+		goto fail2;
+
+	err = w8001_setup(w8001);
+	if (err)
+		goto fail3;
+
+	input_dev->name = w8001->name;
+	input_dev->phys = w8001->phys;
+	input_dev->id.product = w8001->id;
+	input_dev->id.bustype = BUS_RS232;
+	input_dev->id.vendor = 0x056a;
+	input_dev->id.version = 0x0100;
+	input_dev->dev.parent = &serio->dev;
+
+	input_dev->open = w8001_open;
+	input_dev->close = w8001_close;
+
+	input_set_drvdata(input_dev, w8001);
+
+	err = input_register_device(w8001->dev);
+	if (err)
+		goto fail3;
+
+	return 0;
+
+fail3:
+	serio_close(serio);
+fail2:
+	serio_set_drvdata(serio, NULL);
+fail1:
+	input_free_device(input_dev);
+	kfree(w8001);
+	return err;
+}
+
+static struct serio_device_id w8001_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_W8001,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, w8001_serio_ids);
+
+static struct serio_driver w8001_drv = {
+	.driver		= {
+		.name	= "w8001",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= w8001_serio_ids,
+	.interrupt	= w8001_interrupt,
+	.connect	= w8001_connect,
+	.disconnect	= w8001_disconnect,
+};
+
+module_serio_driver(w8001_drv);
diff --git a/drivers/input/touchscreen/wdt87xx_i2c.c b/drivers/input/touchscreen/wdt87xx_i2c.c
new file mode 100644
index 0000000..515c20a
--- /dev/null
+++ b/drivers/input/touchscreen/wdt87xx_i2c.c
@@ -0,0 +1,1194 @@
+/*
+ * Weida HiTech WDT87xx TouchScreen I2C driver
+ *
+ * Copyright (c) 2015  Weida Hi-Tech Co., Ltd.
+ * HN Chen <hn.chen@weidahitech.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/input/mt.h>
+#include <linux/acpi.h>
+#include <asm/unaligned.h>
+
+#define WDT87XX_NAME		"wdt87xx_i2c"
+#define WDT87XX_DRV_VER		"0.9.7"
+#define WDT87XX_FW_NAME		"wdt87xx_fw.bin"
+#define WDT87XX_CFG_NAME	"wdt87xx_cfg.bin"
+
+#define MODE_ACTIVE			0x01
+#define MODE_READY			0x02
+#define MODE_IDLE			0x03
+#define MODE_SLEEP			0x04
+#define MODE_STOP			0xFF
+
+#define WDT_MAX_FINGER			10
+#define WDT_RAW_BUF_COUNT		54
+#define WDT_V1_RAW_BUF_COUNT		74
+#define WDT_FIRMWARE_ID			0xa9e368f5
+
+#define PG_SIZE				0x1000
+#define MAX_RETRIES			3
+
+#define MAX_UNIT_AXIS			0x7FFF
+
+#define PKT_READ_SIZE			72
+#define PKT_WRITE_SIZE			80
+
+/* the finger definition of the report event */
+#define FINGER_EV_OFFSET_ID		0
+#define FINGER_EV_OFFSET_X		1
+#define FINGER_EV_OFFSET_Y		3
+#define FINGER_EV_SIZE			5
+
+#define FINGER_EV_V1_OFFSET_ID		0
+#define FINGER_EV_V1_OFFSET_W		1
+#define FINGER_EV_V1_OFFSET_P		2
+#define FINGER_EV_V1_OFFSET_X		3
+#define FINGER_EV_V1_OFFSET_Y		5
+#define FINGER_EV_V1_SIZE		7
+
+/* The definition of a report packet */
+#define TOUCH_PK_OFFSET_REPORT_ID	0
+#define TOUCH_PK_OFFSET_EVENT		1
+#define TOUCH_PK_OFFSET_SCAN_TIME	51
+#define TOUCH_PK_OFFSET_FNGR_NUM	53
+
+#define TOUCH_PK_V1_OFFSET_REPORT_ID	0
+#define TOUCH_PK_V1_OFFSET_EVENT	1
+#define TOUCH_PK_V1_OFFSET_SCAN_TIME	71
+#define TOUCH_PK_V1_OFFSET_FNGR_NUM	73
+
+/* The definition of the controller parameters */
+#define CTL_PARAM_OFFSET_FW_ID		0
+#define CTL_PARAM_OFFSET_PLAT_ID	2
+#define CTL_PARAM_OFFSET_XMLS_ID1	4
+#define CTL_PARAM_OFFSET_XMLS_ID2	6
+#define CTL_PARAM_OFFSET_PHY_CH_X	8
+#define CTL_PARAM_OFFSET_PHY_CH_Y	10
+#define CTL_PARAM_OFFSET_PHY_X0		12
+#define CTL_PARAM_OFFSET_PHY_X1		14
+#define CTL_PARAM_OFFSET_PHY_Y0		16
+#define CTL_PARAM_OFFSET_PHY_Y1		18
+#define CTL_PARAM_OFFSET_PHY_W		22
+#define CTL_PARAM_OFFSET_PHY_H		24
+#define CTL_PARAM_OFFSET_FACTOR		32
+
+/* The definition of the device descriptor */
+#define WDT_GD_DEVICE			1
+#define DEV_DESC_OFFSET_VID		8
+#define DEV_DESC_OFFSET_PID		10
+
+/* Communication commands */
+#define PACKET_SIZE			56
+#define VND_REQ_READ			0x06
+#define VND_READ_DATA			0x07
+#define VND_REQ_WRITE			0x08
+
+#define VND_CMD_START			0x00
+#define VND_CMD_STOP			0x01
+#define VND_CMD_RESET			0x09
+
+#define VND_CMD_ERASE			0x1A
+
+#define VND_GET_CHECKSUM		0x66
+
+#define VND_SET_DATA			0x83
+#define VND_SET_COMMAND_DATA		0x84
+#define VND_SET_CHECKSUM_CALC		0x86
+#define VND_SET_CHECKSUM_LENGTH		0x87
+
+#define VND_CMD_SFLCK			0xFC
+#define VND_CMD_SFUNL			0xFD
+
+#define CMD_SFLCK_KEY			0xC39B
+#define CMD_SFUNL_KEY			0x95DA
+
+#define STRIDX_PLATFORM_ID		0x80
+#define STRIDX_PARAMETERS		0x81
+
+#define CMD_BUF_SIZE			8
+#define PKT_BUF_SIZE			64
+
+/* The definition of the command packet */
+#define CMD_REPORT_ID_OFFSET		0x0
+#define CMD_TYPE_OFFSET			0x1
+#define CMD_INDEX_OFFSET		0x2
+#define CMD_KEY_OFFSET			0x3
+#define CMD_LENGTH_OFFSET		0x4
+#define CMD_DATA_OFFSET			0x8
+
+/* The definition of firmware chunk tags */
+#define FOURCC_ID_RIFF			0x46464952
+#define FOURCC_ID_WHIF			0x46494857
+#define FOURCC_ID_FRMT			0x544D5246
+#define FOURCC_ID_FRWR			0x52575246
+#define FOURCC_ID_CNFG			0x47464E43
+
+#define CHUNK_ID_FRMT			FOURCC_ID_FRMT
+#define CHUNK_ID_FRWR			FOURCC_ID_FRWR
+#define CHUNK_ID_CNFG			FOURCC_ID_CNFG
+
+#define FW_FOURCC1_OFFSET		0
+#define FW_SIZE_OFFSET			4
+#define FW_FOURCC2_OFFSET		8
+#define FW_PAYLOAD_OFFSET		40
+
+#define FW_CHUNK_ID_OFFSET		0
+#define FW_CHUNK_SIZE_OFFSET		4
+#define FW_CHUNK_TGT_START_OFFSET	8
+#define FW_CHUNK_PAYLOAD_LEN_OFFSET	12
+#define FW_CHUNK_SRC_START_OFFSET	16
+#define FW_CHUNK_VERSION_OFFSET		20
+#define FW_CHUNK_ATTR_OFFSET		24
+#define FW_CHUNK_PAYLOAD_OFFSET		32
+
+/* Controller requires minimum 300us between commands */
+#define WDT_COMMAND_DELAY_MS		2
+#define WDT_FLASH_WRITE_DELAY_MS	4
+#define WDT_FW_RESET_TIME		2500
+
+struct wdt87xx_sys_param {
+	u16	fw_id;
+	u16	plat_id;
+	u16	xmls_id1;
+	u16	xmls_id2;
+	u16	phy_ch_x;
+	u16	phy_ch_y;
+	u16	phy_w;
+	u16	phy_h;
+	u16	scaling_factor;
+	u32	max_x;
+	u32	max_y;
+	u16	vendor_id;
+	u16	product_id;
+};
+
+struct wdt87xx_data {
+	struct i2c_client		*client;
+	struct input_dev		*input;
+	/* Mutex for fw update to prevent concurrent access */
+	struct mutex			fw_mutex;
+	struct wdt87xx_sys_param	param;
+	u8				phys[32];
+};
+
+static int wdt87xx_i2c_xfer(struct i2c_client *client,
+			    void *txdata, size_t txlen,
+			    void *rxdata, size_t rxlen)
+{
+	struct i2c_msg msgs[] = {
+		{
+			.addr	= client->addr,
+			.flags	= 0,
+			.len	= txlen,
+			.buf	= txdata,
+		},
+		{
+			.addr	= client->addr,
+			.flags	= I2C_M_RD,
+			.len	= rxlen,
+			.buf	= rxdata,
+		},
+	};
+	int error;
+	int ret;
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs)) {
+		error = ret < 0 ? ret : -EIO;
+		dev_err(&client->dev, "%s: i2c transfer failed: %d\n",
+			__func__, error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int wdt87xx_get_desc(struct i2c_client *client, u8 desc_idx,
+			    u8 *buf, size_t len)
+{
+	u8 tx_buf[] = { 0x22, 0x00, 0x10, 0x0E, 0x23, 0x00 };
+	int error;
+
+	tx_buf[2] |= desc_idx & 0xF;
+
+	error = wdt87xx_i2c_xfer(client, tx_buf, sizeof(tx_buf),
+				 buf, len);
+	if (error) {
+		dev_err(&client->dev, "get desc failed: %d\n", error);
+		return error;
+	}
+
+	if (buf[0] != len) {
+		dev_err(&client->dev, "unexpected response to get desc: %d\n",
+			buf[0]);
+		return -EINVAL;
+	}
+
+	mdelay(WDT_COMMAND_DELAY_MS);
+
+	return 0;
+}
+
+static int wdt87xx_get_string(struct i2c_client *client, u8 str_idx,
+			      u8 *buf, size_t len)
+{
+	u8 tx_buf[] = { 0x22, 0x00, 0x13, 0x0E, str_idx, 0x23, 0x00 };
+	u8 rx_buf[PKT_WRITE_SIZE];
+	size_t rx_len = len + 2;
+	int error;
+
+	if (rx_len > sizeof(rx_buf))
+		return -EINVAL;
+
+	error = wdt87xx_i2c_xfer(client, tx_buf, sizeof(tx_buf),
+				 rx_buf, rx_len);
+	if (error) {
+		dev_err(&client->dev, "get string failed: %d\n", error);
+		return error;
+	}
+
+	if (rx_buf[1] != 0x03) {
+		dev_err(&client->dev, "unexpected response to get string: %d\n",
+			rx_buf[1]);
+		return -EINVAL;
+	}
+
+	rx_len = min_t(size_t, len, rx_buf[0]);
+	memcpy(buf, &rx_buf[2], rx_len);
+
+	mdelay(WDT_COMMAND_DELAY_MS);
+
+	return 0;
+}
+
+static int wdt87xx_get_feature(struct i2c_client *client,
+			       u8 *buf, size_t buf_size)
+{
+	u8 tx_buf[8];
+	u8 rx_buf[PKT_WRITE_SIZE];
+	size_t tx_len = 0;
+	size_t rx_len = buf_size + 2;
+	int error;
+
+	if (rx_len > sizeof(rx_buf))
+		return -EINVAL;
+
+	/* Get feature command packet */
+	tx_buf[tx_len++] = 0x22;
+	tx_buf[tx_len++] = 0x00;
+	if (buf[CMD_REPORT_ID_OFFSET] > 0xF) {
+		tx_buf[tx_len++] = 0x30;
+		tx_buf[tx_len++] = 0x02;
+		tx_buf[tx_len++] = buf[CMD_REPORT_ID_OFFSET];
+	} else {
+		tx_buf[tx_len++] = 0x30 | buf[CMD_REPORT_ID_OFFSET];
+		tx_buf[tx_len++] = 0x02;
+	}
+	tx_buf[tx_len++] = 0x23;
+	tx_buf[tx_len++] = 0x00;
+
+	error = wdt87xx_i2c_xfer(client, tx_buf, tx_len, rx_buf, rx_len);
+	if (error) {
+		dev_err(&client->dev, "get feature failed: %d\n", error);
+		return error;
+	}
+
+	rx_len = min_t(size_t, buf_size, get_unaligned_le16(rx_buf));
+	memcpy(buf, &rx_buf[2], rx_len);
+
+	mdelay(WDT_COMMAND_DELAY_MS);
+
+	return 0;
+}
+
+static int wdt87xx_set_feature(struct i2c_client *client,
+			       const u8 *buf, size_t buf_size)
+{
+	u8 tx_buf[PKT_WRITE_SIZE];
+	int tx_len = 0;
+	int error;
+
+	/* Set feature command packet */
+	tx_buf[tx_len++] = 0x22;
+	tx_buf[tx_len++] = 0x00;
+	if (buf[CMD_REPORT_ID_OFFSET] > 0xF) {
+		tx_buf[tx_len++] = 0x30;
+		tx_buf[tx_len++] = 0x03;
+		tx_buf[tx_len++] = buf[CMD_REPORT_ID_OFFSET];
+	} else {
+		tx_buf[tx_len++] = 0x30 | buf[CMD_REPORT_ID_OFFSET];
+		tx_buf[tx_len++] = 0x03;
+	}
+	tx_buf[tx_len++] = 0x23;
+	tx_buf[tx_len++] = 0x00;
+	tx_buf[tx_len++] = (buf_size & 0xFF);
+	tx_buf[tx_len++] = ((buf_size & 0xFF00) >> 8);
+
+	if (tx_len + buf_size > sizeof(tx_buf))
+		return -EINVAL;
+
+	memcpy(&tx_buf[tx_len], buf, buf_size);
+	tx_len += buf_size;
+
+	error = i2c_master_send(client, tx_buf, tx_len);
+	if (error < 0) {
+		dev_err(&client->dev, "set feature failed: %d\n", error);
+		return error;
+	}
+
+	mdelay(WDT_COMMAND_DELAY_MS);
+
+	return 0;
+}
+
+static int wdt87xx_send_command(struct i2c_client *client, int cmd, int value)
+{
+	u8 cmd_buf[CMD_BUF_SIZE];
+
+	/* Set the command packet */
+	cmd_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_WRITE;
+	cmd_buf[CMD_TYPE_OFFSET] = VND_SET_COMMAND_DATA;
+	put_unaligned_le16((u16)cmd, &cmd_buf[CMD_INDEX_OFFSET]);
+
+	switch (cmd) {
+	case VND_CMD_START:
+	case VND_CMD_STOP:
+	case VND_CMD_RESET:
+		/* Mode selector */
+		put_unaligned_le32((value & 0xFF), &cmd_buf[CMD_LENGTH_OFFSET]);
+		break;
+
+	case VND_CMD_SFLCK:
+		put_unaligned_le16(CMD_SFLCK_KEY, &cmd_buf[CMD_KEY_OFFSET]);
+		break;
+
+	case VND_CMD_SFUNL:
+		put_unaligned_le16(CMD_SFUNL_KEY, &cmd_buf[CMD_KEY_OFFSET]);
+		break;
+
+	case VND_CMD_ERASE:
+	case VND_SET_CHECKSUM_CALC:
+	case VND_SET_CHECKSUM_LENGTH:
+		put_unaligned_le32(value, &cmd_buf[CMD_KEY_OFFSET]);
+		break;
+
+	default:
+		cmd_buf[CMD_REPORT_ID_OFFSET] = 0;
+		dev_err(&client->dev, "Invalid command: %d\n", cmd);
+		return -EINVAL;
+	}
+
+	return wdt87xx_set_feature(client, cmd_buf, sizeof(cmd_buf));
+}
+
+static int wdt87xx_sw_reset(struct i2c_client *client)
+{
+	int error;
+
+	dev_dbg(&client->dev, "resetting device now\n");
+
+	error = wdt87xx_send_command(client, VND_CMD_RESET, 0);
+	if (error) {
+		dev_err(&client->dev, "reset failed\n");
+		return error;
+	}
+
+	/* Wait the device to be ready */
+	msleep(WDT_FW_RESET_TIME);
+
+	return 0;
+}
+
+static const void *wdt87xx_get_fw_chunk(const struct firmware *fw, u32 id)
+{
+	size_t pos = FW_PAYLOAD_OFFSET;
+	u32 chunk_id, chunk_size;
+
+	while (pos < fw->size) {
+		chunk_id = get_unaligned_le32(fw->data +
+					      pos + FW_CHUNK_ID_OFFSET);
+		if (chunk_id == id)
+			return fw->data + pos;
+
+		chunk_size = get_unaligned_le32(fw->data +
+						pos + FW_CHUNK_SIZE_OFFSET);
+		pos += chunk_size + 2 * sizeof(u32); /* chunk ID + size */
+	}
+
+	return NULL;
+}
+
+static int wdt87xx_get_sysparam(struct i2c_client *client,
+				struct wdt87xx_sys_param *param)
+{
+	u8 buf[PKT_READ_SIZE];
+	int error;
+
+	error = wdt87xx_get_desc(client, WDT_GD_DEVICE, buf, 18);
+	if (error) {
+		dev_err(&client->dev, "failed to get device desc\n");
+		return error;
+	}
+
+	param->vendor_id = get_unaligned_le16(buf + DEV_DESC_OFFSET_VID);
+	param->product_id = get_unaligned_le16(buf + DEV_DESC_OFFSET_PID);
+
+	error = wdt87xx_get_string(client, STRIDX_PARAMETERS, buf, 34);
+	if (error) {
+		dev_err(&client->dev, "failed to get parameters\n");
+		return error;
+	}
+
+	param->xmls_id1 = get_unaligned_le16(buf + CTL_PARAM_OFFSET_XMLS_ID1);
+	param->xmls_id2 = get_unaligned_le16(buf + CTL_PARAM_OFFSET_XMLS_ID2);
+	param->phy_ch_x = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_CH_X);
+	param->phy_ch_y = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_CH_Y);
+	param->phy_w = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_W) / 10;
+	param->phy_h = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_H) / 10;
+
+	/* Get the scaling factor of pixel to logical coordinate */
+	param->scaling_factor =
+			get_unaligned_le16(buf + CTL_PARAM_OFFSET_FACTOR);
+
+	param->max_x = MAX_UNIT_AXIS;
+	param->max_y = DIV_ROUND_CLOSEST(MAX_UNIT_AXIS * param->phy_h,
+					 param->phy_w);
+
+	error = wdt87xx_get_string(client, STRIDX_PLATFORM_ID, buf, 8);
+	if (error) {
+		dev_err(&client->dev, "failed to get platform id\n");
+		return error;
+	}
+
+	param->plat_id = buf[1];
+
+	buf[0] = 0xf2;
+	error = wdt87xx_get_feature(client, buf, 16);
+	if (error) {
+		dev_err(&client->dev, "failed to get firmware id\n");
+		return error;
+	}
+
+	if (buf[0] != 0xf2) {
+		dev_err(&client->dev, "wrong id of fw response: 0x%x\n",
+			buf[0]);
+		return -EINVAL;
+	}
+
+	param->fw_id = get_unaligned_le16(&buf[1]);
+
+	dev_info(&client->dev,
+		 "fw_id: 0x%x, plat_id: 0x%x, xml_id1: %04x, xml_id2: %04x\n",
+		 param->fw_id, param->plat_id,
+		 param->xmls_id1, param->xmls_id2);
+
+	return 0;
+}
+
+static int wdt87xx_validate_firmware(struct wdt87xx_data *wdt,
+				     const struct firmware *fw)
+{
+	const void *fw_chunk;
+	u32 data1, data2;
+	u32 size;
+	u8 fw_chip_id;
+	u8 chip_id;
+
+	data1 = get_unaligned_le32(fw->data + FW_FOURCC1_OFFSET);
+	data2 = get_unaligned_le32(fw->data + FW_FOURCC2_OFFSET);
+	if (data1 != FOURCC_ID_RIFF || data2 != FOURCC_ID_WHIF) {
+		dev_err(&wdt->client->dev, "check fw tag failed\n");
+		return -EINVAL;
+	}
+
+	size = get_unaligned_le32(fw->data + FW_SIZE_OFFSET);
+	if (size != fw->size) {
+		dev_err(&wdt->client->dev,
+			"fw size mismatch: expected %d, actual %zu\n",
+			size, fw->size);
+		return -EINVAL;
+	}
+
+	/*
+	 * Get the chip_id from the firmware. Make sure that it is the
+	 * right controller to do the firmware and config update.
+	 */
+	fw_chunk = wdt87xx_get_fw_chunk(fw, CHUNK_ID_FRWR);
+	if (!fw_chunk) {
+		dev_err(&wdt->client->dev,
+			"unable to locate firmware chunk\n");
+		return -EINVAL;
+	}
+
+	fw_chip_id = (get_unaligned_le32(fw_chunk +
+					 FW_CHUNK_VERSION_OFFSET) >> 12) & 0xF;
+	chip_id = (wdt->param.fw_id >> 12) & 0xF;
+
+	if (fw_chip_id != chip_id) {
+		dev_err(&wdt->client->dev,
+			"fw version mismatch: fw %d vs. chip %d\n",
+			fw_chip_id, chip_id);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int wdt87xx_validate_fw_chunk(const void *data, int id)
+{
+	if (id == CHUNK_ID_FRWR) {
+		u32 fw_id;
+
+		fw_id = get_unaligned_le32(data + FW_CHUNK_PAYLOAD_OFFSET);
+		if (fw_id != WDT_FIRMWARE_ID)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int wdt87xx_write_data(struct i2c_client *client, const char *data,
+			      u32 address, int length)
+{
+	u16 packet_size;
+	int count = 0;
+	int error;
+	u8 pkt_buf[PKT_BUF_SIZE];
+
+	/* Address and length should be 4 bytes aligned */
+	if ((address & 0x3) != 0 || (length & 0x3) != 0) {
+		dev_err(&client->dev,
+			"addr & len must be 4 bytes aligned %x, %x\n",
+			address, length);
+		return -EINVAL;
+	}
+
+	while (length) {
+		packet_size = min(length, PACKET_SIZE);
+
+		pkt_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_WRITE;
+		pkt_buf[CMD_TYPE_OFFSET] = VND_SET_DATA;
+		put_unaligned_le16(packet_size, &pkt_buf[CMD_INDEX_OFFSET]);
+		put_unaligned_le32(address, &pkt_buf[CMD_LENGTH_OFFSET]);
+		memcpy(&pkt_buf[CMD_DATA_OFFSET], data, packet_size);
+
+		error = wdt87xx_set_feature(client, pkt_buf, sizeof(pkt_buf));
+		if (error)
+			return error;
+
+		length -= packet_size;
+		data += packet_size;
+		address += packet_size;
+
+		/* Wait for the controller to finish the write */
+		mdelay(WDT_FLASH_WRITE_DELAY_MS);
+
+		if ((++count % 32) == 0) {
+			/* Delay for fw to clear watch dog */
+			msleep(20);
+		}
+	}
+
+	return 0;
+}
+
+static u16 misr(u16 cur_value, u8 new_value)
+{
+	u32 a, b;
+	u32 bit0;
+	u32 y;
+
+	a = cur_value;
+	b = new_value;
+	bit0 = a ^ (b & 1);
+	bit0 ^= a >> 1;
+	bit0 ^= a >> 2;
+	bit0 ^= a >> 4;
+	bit0 ^= a >> 5;
+	bit0 ^= a >> 7;
+	bit0 ^= a >> 11;
+	bit0 ^= a >> 15;
+	y = (a << 1) ^ b;
+	y = (y & ~1) | (bit0 & 1);
+
+	return (u16)y;
+}
+
+static u16 wdt87xx_calculate_checksum(const u8 *data, size_t length)
+{
+	u16 checksum = 0;
+	size_t i;
+
+	for (i = 0; i < length; i++)
+		checksum = misr(checksum, data[i]);
+
+	return checksum;
+}
+
+static int wdt87xx_get_checksum(struct i2c_client *client, u16 *checksum,
+				u32 address, int length)
+{
+	int error;
+	int time_delay;
+	u8 pkt_buf[PKT_BUF_SIZE];
+	u8 cmd_buf[CMD_BUF_SIZE];
+
+	error = wdt87xx_send_command(client, VND_SET_CHECKSUM_LENGTH, length);
+	if (error) {
+		dev_err(&client->dev, "failed to set checksum length\n");
+		return error;
+	}
+
+	error = wdt87xx_send_command(client, VND_SET_CHECKSUM_CALC, address);
+	if (error) {
+		dev_err(&client->dev, "failed to set checksum address\n");
+		return error;
+	}
+
+	/* Wait the operation to complete */
+	time_delay = DIV_ROUND_UP(length, 1024);
+	msleep(time_delay * 30);
+
+	memset(cmd_buf, 0, sizeof(cmd_buf));
+	cmd_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_READ;
+	cmd_buf[CMD_TYPE_OFFSET] = VND_GET_CHECKSUM;
+	error = wdt87xx_set_feature(client, cmd_buf, sizeof(cmd_buf));
+	if (error) {
+		dev_err(&client->dev, "failed to request checksum\n");
+		return error;
+	}
+
+	memset(pkt_buf, 0, sizeof(pkt_buf));
+	pkt_buf[CMD_REPORT_ID_OFFSET] = VND_READ_DATA;
+	error = wdt87xx_get_feature(client, pkt_buf, sizeof(pkt_buf));
+	if (error) {
+		dev_err(&client->dev, "failed to read checksum\n");
+		return error;
+	}
+
+	*checksum = get_unaligned_le16(&pkt_buf[CMD_DATA_OFFSET]);
+	return 0;
+}
+
+static int wdt87xx_write_firmware(struct i2c_client *client, const void *chunk)
+{
+	u32 start_addr = get_unaligned_le32(chunk + FW_CHUNK_TGT_START_OFFSET);
+	u32 size = get_unaligned_le32(chunk + FW_CHUNK_PAYLOAD_LEN_OFFSET);
+	const void *data = chunk + FW_CHUNK_PAYLOAD_OFFSET;
+	int error;
+	int err1;
+	int page_size;
+	int retry = 0;
+	u16 device_checksum, firmware_checksum;
+
+	dev_dbg(&client->dev, "start 4k page program\n");
+
+	error = wdt87xx_send_command(client, VND_CMD_STOP, MODE_STOP);
+	if (error) {
+		dev_err(&client->dev, "stop report mode failed\n");
+		return error;
+	}
+
+	error = wdt87xx_send_command(client, VND_CMD_SFUNL, 0);
+	if (error) {
+		dev_err(&client->dev, "unlock failed\n");
+		goto out_enable_reporting;
+	}
+
+	mdelay(10);
+
+	while (size) {
+		dev_dbg(&client->dev, "%s: %x, %x\n", __func__,
+			start_addr, size);
+
+		page_size = min_t(u32, size, PG_SIZE);
+		size -= page_size;
+
+		for (retry = 0; retry < MAX_RETRIES; retry++) {
+			error = wdt87xx_send_command(client, VND_CMD_ERASE,
+						     start_addr);
+			if (error) {
+				dev_err(&client->dev,
+					"erase failed at %#08x\n", start_addr);
+				break;
+			}
+
+			msleep(50);
+
+			error = wdt87xx_write_data(client, data, start_addr,
+						   page_size);
+			if (error) {
+				dev_err(&client->dev,
+					"write failed at %#08x (%d bytes)\n",
+					start_addr, page_size);
+				break;
+			}
+
+			error = wdt87xx_get_checksum(client, &device_checksum,
+						     start_addr, page_size);
+			if (error) {
+				dev_err(&client->dev,
+					"failed to retrieve checksum for %#08x (len: %d)\n",
+					start_addr, page_size);
+				break;
+			}
+
+			firmware_checksum =
+				wdt87xx_calculate_checksum(data, page_size);
+
+			if (device_checksum == firmware_checksum)
+				break;
+
+			dev_err(&client->dev,
+				"checksum fail: %d vs %d, retry %d\n",
+				device_checksum, firmware_checksum, retry);
+		}
+
+		if (retry == MAX_RETRIES) {
+			dev_err(&client->dev, "page write failed\n");
+			error = -EIO;
+			goto out_lock_device;
+		}
+
+		start_addr = start_addr + page_size;
+		data = data + page_size;
+	}
+
+out_lock_device:
+	err1 = wdt87xx_send_command(client, VND_CMD_SFLCK, 0);
+	if (err1)
+		dev_err(&client->dev, "lock failed\n");
+
+	mdelay(10);
+
+out_enable_reporting:
+	err1 = wdt87xx_send_command(client, VND_CMD_START, 0);
+	if (err1)
+		dev_err(&client->dev, "start to report failed\n");
+
+	return error ? error : err1;
+}
+
+static int wdt87xx_load_chunk(struct i2c_client *client,
+			      const struct firmware *fw, u32 ck_id)
+{
+	const void *chunk;
+	int error;
+
+	chunk = wdt87xx_get_fw_chunk(fw, ck_id);
+	if (!chunk) {
+		dev_err(&client->dev, "unable to locate chunk (type %d)\n",
+			ck_id);
+		return -EINVAL;
+	}
+
+	error = wdt87xx_validate_fw_chunk(chunk, ck_id);
+	if (error) {
+		dev_err(&client->dev, "invalid chunk (type %d): %d\n",
+			ck_id, error);
+		return error;
+	}
+
+	error = wdt87xx_write_firmware(client, chunk);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to write fw chunk (type %d): %d\n",
+			ck_id, error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int wdt87xx_do_update_firmware(struct i2c_client *client,
+				      const struct firmware *fw,
+				      unsigned int chunk_id)
+{
+	struct wdt87xx_data *wdt = i2c_get_clientdata(client);
+	int error;
+
+	error = wdt87xx_validate_firmware(wdt, fw);
+	if (error)
+		return error;
+
+	error = mutex_lock_interruptible(&wdt->fw_mutex);
+	if (error)
+		return error;
+
+	disable_irq(client->irq);
+
+	error = wdt87xx_load_chunk(client, fw, chunk_id);
+	if (error) {
+		dev_err(&client->dev,
+			"firmware load failed (type: %d): %d\n",
+			chunk_id, error);
+		goto out;
+	}
+
+	error = wdt87xx_sw_reset(client);
+	if (error) {
+		dev_err(&client->dev, "soft reset failed: %d\n", error);
+		goto out;
+	}
+
+	/* Refresh the parameters */
+	error = wdt87xx_get_sysparam(client, &wdt->param);
+	if (error)
+		dev_err(&client->dev,
+			"failed to refresh system paramaters: %d\n", error);
+out:
+	enable_irq(client->irq);
+	mutex_unlock(&wdt->fw_mutex);
+
+	return error ? error : 0;
+}
+
+static int wdt87xx_update_firmware(struct device *dev,
+				   const char *fw_name, unsigned int chunk_id)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	const struct firmware *fw;
+	int error;
+
+	error = request_firmware(&fw, fw_name, dev);
+	if (error) {
+		dev_err(&client->dev, "unable to retrieve firmware %s: %d\n",
+			fw_name, error);
+		return error;
+	}
+
+	error = wdt87xx_do_update_firmware(client, fw, chunk_id);
+
+	release_firmware(fw);
+
+	return error ? error : 0;
+}
+
+static ssize_t config_csum_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct wdt87xx_data *wdt = i2c_get_clientdata(client);
+	u32 cfg_csum;
+
+	cfg_csum = wdt->param.xmls_id1;
+	cfg_csum = (cfg_csum << 16) | wdt->param.xmls_id2;
+
+	return scnprintf(buf, PAGE_SIZE, "%x\n", cfg_csum);
+}
+
+static ssize_t fw_version_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct wdt87xx_data *wdt = i2c_get_clientdata(client);
+
+	return scnprintf(buf, PAGE_SIZE, "%x\n", wdt->param.fw_id);
+}
+
+static ssize_t plat_id_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct wdt87xx_data *wdt = i2c_get_clientdata(client);
+
+	return scnprintf(buf, PAGE_SIZE, "%x\n", wdt->param.plat_id);
+}
+
+static ssize_t update_config_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	int error;
+
+	error = wdt87xx_update_firmware(dev, WDT87XX_CFG_NAME, CHUNK_ID_CNFG);
+
+	return error ? error : count;
+}
+
+static ssize_t update_fw_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	int error;
+
+	error = wdt87xx_update_firmware(dev, WDT87XX_FW_NAME, CHUNK_ID_FRWR);
+
+	return error ? error : count;
+}
+
+static DEVICE_ATTR_RO(config_csum);
+static DEVICE_ATTR_RO(fw_version);
+static DEVICE_ATTR_RO(plat_id);
+static DEVICE_ATTR_WO(update_config);
+static DEVICE_ATTR_WO(update_fw);
+
+static struct attribute *wdt87xx_attrs[] = {
+	&dev_attr_config_csum.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_plat_id.attr,
+	&dev_attr_update_config.attr,
+	&dev_attr_update_fw.attr,
+	NULL
+};
+
+static const struct attribute_group wdt87xx_attr_group = {
+	.attrs = wdt87xx_attrs,
+};
+
+static void wdt87xx_report_contact(struct input_dev *input,
+				   struct wdt87xx_sys_param *param,
+				   u8 *buf)
+{
+	int finger_id;
+	u32 x, y, w;
+	u8 p;
+
+	finger_id = (buf[FINGER_EV_V1_OFFSET_ID] >> 3) - 1;
+	if (finger_id < 0)
+		return;
+
+	/* Check if this is an active contact */
+	if (!(buf[FINGER_EV_V1_OFFSET_ID] & 0x1))
+		return;
+
+	w = buf[FINGER_EV_V1_OFFSET_W];
+	w *= param->scaling_factor;
+
+	p = buf[FINGER_EV_V1_OFFSET_P];
+
+	x = get_unaligned_le16(buf + FINGER_EV_V1_OFFSET_X);
+
+	y = get_unaligned_le16(buf + FINGER_EV_V1_OFFSET_Y);
+	y = DIV_ROUND_CLOSEST(y * param->phy_h, param->phy_w);
+
+	/* Refuse incorrect coordinates */
+	if (x > param->max_x || y > param->max_y)
+		return;
+
+	dev_dbg(input->dev.parent, "tip on (%d), x(%d), y(%d)\n",
+		finger_id, x, y);
+
+	input_mt_slot(input, finger_id);
+	input_mt_report_slot_state(input, MT_TOOL_FINGER, 1);
+	input_report_abs(input, ABS_MT_TOUCH_MAJOR, w);
+	input_report_abs(input, ABS_MT_PRESSURE, p);
+	input_report_abs(input, ABS_MT_POSITION_X, x);
+	input_report_abs(input, ABS_MT_POSITION_Y, y);
+}
+
+static irqreturn_t wdt87xx_ts_interrupt(int irq, void *dev_id)
+{
+	struct wdt87xx_data *wdt = dev_id;
+	struct i2c_client *client = wdt->client;
+	int i, fingers;
+	int error;
+	u8 raw_buf[WDT_V1_RAW_BUF_COUNT] = {0};
+
+	error = i2c_master_recv(client, raw_buf, WDT_V1_RAW_BUF_COUNT);
+	if (error < 0) {
+		dev_err(&client->dev, "read v1 raw data failed: %d\n", error);
+		goto irq_exit;
+	}
+
+	fingers = raw_buf[TOUCH_PK_V1_OFFSET_FNGR_NUM];
+	if (!fingers)
+		goto irq_exit;
+
+	for (i = 0; i < WDT_MAX_FINGER; i++)
+		wdt87xx_report_contact(wdt->input,
+				       &wdt->param,
+				       &raw_buf[TOUCH_PK_V1_OFFSET_EVENT +
+						i * FINGER_EV_V1_SIZE]);
+
+	input_mt_sync_frame(wdt->input);
+	input_sync(wdt->input);
+
+irq_exit:
+	return IRQ_HANDLED;
+}
+
+static int wdt87xx_ts_create_input_device(struct wdt87xx_data *wdt)
+{
+	struct device *dev = &wdt->client->dev;
+	struct input_dev *input;
+	unsigned int res = DIV_ROUND_CLOSEST(MAX_UNIT_AXIS, wdt->param.phy_w);
+	int error;
+
+	input = devm_input_allocate_device(dev);
+	if (!input) {
+		dev_err(dev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+	wdt->input = input;
+
+	input->name = "WDT87xx Touchscreen";
+	input->id.bustype = BUS_I2C;
+	input->id.vendor = wdt->param.vendor_id;
+	input->id.product = wdt->param.product_id;
+	input->phys = wdt->phys;
+
+	input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+			     wdt->param.max_x, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+			     wdt->param.max_y, 0, 0);
+	input_abs_set_res(input, ABS_MT_POSITION_X, res);
+	input_abs_set_res(input, ABS_MT_POSITION_Y, res);
+
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
+			     0, wdt->param.max_x, 0, 0);
+	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xFF, 0, 0);
+
+	input_mt_init_slots(input, WDT_MAX_FINGER,
+			    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+
+	error = input_register_device(input);
+	if (error) {
+		dev_err(dev, "failed to register input device: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int wdt87xx_ts_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	struct wdt87xx_data *wdt;
+	int error;
+
+	dev_dbg(&client->dev, "adapter=%d, client irq: %d\n",
+		client->adapter->nr, client->irq);
+
+	/* Check if the I2C function is ok in this adaptor */
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -ENXIO;
+
+	wdt = devm_kzalloc(&client->dev, sizeof(*wdt), GFP_KERNEL);
+	if (!wdt)
+		return -ENOMEM;
+
+	wdt->client = client;
+	mutex_init(&wdt->fw_mutex);
+	i2c_set_clientdata(client, wdt);
+
+	snprintf(wdt->phys, sizeof(wdt->phys), "i2c-%u-%04x/input0",
+		 client->adapter->nr, client->addr);
+
+	error = wdt87xx_get_sysparam(client, &wdt->param);
+	if (error)
+		return error;
+
+	error = wdt87xx_ts_create_input_device(wdt);
+	if (error)
+		return error;
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, wdt87xx_ts_interrupt,
+					  IRQF_ONESHOT,
+					  client->name, wdt);
+	if (error) {
+		dev_err(&client->dev, "request irq failed: %d\n", error);
+		return error;
+	}
+
+	error = sysfs_create_group(&client->dev.kobj, &wdt87xx_attr_group);
+	if (error) {
+		dev_err(&client->dev, "create sysfs failed: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int wdt87xx_ts_remove(struct i2c_client *client)
+{
+	sysfs_remove_group(&client->dev.kobj, &wdt87xx_attr_group);
+
+	return 0;
+}
+
+static int __maybe_unused wdt87xx_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int error;
+
+	disable_irq(client->irq);
+
+	error = wdt87xx_send_command(client, VND_CMD_STOP, MODE_IDLE);
+	if (error) {
+		enable_irq(client->irq);
+		dev_err(&client->dev,
+			"failed to stop device when suspending: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused wdt87xx_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int error;
+
+	/*
+	 * The chip may have been reset while system is resuming,
+	 * give it some time to settle.
+	 */
+	mdelay(100);
+
+	error = wdt87xx_send_command(client, VND_CMD_START, 0);
+	if (error)
+		dev_err(&client->dev,
+			"failed to start device when resuming: %d\n",
+			error);
+
+	enable_irq(client->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(wdt87xx_pm_ops, wdt87xx_suspend, wdt87xx_resume);
+
+static const struct i2c_device_id wdt87xx_dev_id[] = {
+	{ WDT87XX_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wdt87xx_dev_id);
+
+static const struct acpi_device_id wdt87xx_acpi_id[] = {
+	{ "WDHT0001", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, wdt87xx_acpi_id);
+
+static struct i2c_driver wdt87xx_driver = {
+	.probe		= wdt87xx_ts_probe,
+	.remove		= wdt87xx_ts_remove,
+	.id_table	= wdt87xx_dev_id,
+	.driver	= {
+		.name	= WDT87XX_NAME,
+		.pm     = &wdt87xx_pm_ops,
+		.acpi_match_table = ACPI_PTR(wdt87xx_acpi_id),
+	},
+};
+module_i2c_driver(wdt87xx_driver);
+
+MODULE_AUTHOR("HN Chen <hn.chen@weidahitech.com>");
+MODULE_DESCRIPTION("WeidaHiTech WDT87XX Touchscreen driver");
+MODULE_VERSION(WDT87XX_DRV_VER);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c
new file mode 100644
index 0000000..1db0a14
--- /dev/null
+++ b/drivers/input/touchscreen/wm831x-ts.c
@@ -0,0 +1,405 @@
+/*
+ * Touchscreen driver for WM831x PMICs
+ *
+ * Copyright 2011 Wolfson Microelectronics plc.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/pm.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/irq.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+/*
+ * R16424 (0x4028) - Touch Control 1
+ */
+#define WM831X_TCH_ENA                          0x8000  /* TCH_ENA */
+#define WM831X_TCH_CVT_ENA                      0x4000  /* TCH_CVT_ENA */
+#define WM831X_TCH_SLPENA                       0x1000  /* TCH_SLPENA */
+#define WM831X_TCH_Z_ENA                        0x0400  /* TCH_Z_ENA */
+#define WM831X_TCH_Y_ENA                        0x0200  /* TCH_Y_ENA */
+#define WM831X_TCH_X_ENA                        0x0100  /* TCH_X_ENA */
+#define WM831X_TCH_DELAY_MASK                   0x00E0  /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_DELAY_SHIFT                       5  /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_DELAY_WIDTH                       3  /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_RATE_MASK                    0x001F  /* TCH_RATE - [4:0] */
+#define WM831X_TCH_RATE_SHIFT                        0  /* TCH_RATE - [4:0] */
+#define WM831X_TCH_RATE_WIDTH                        5  /* TCH_RATE - [4:0] */
+
+/*
+ * R16425 (0x4029) - Touch Control 2
+ */
+#define WM831X_TCH_PD_WK                        0x2000  /* TCH_PD_WK */
+#define WM831X_TCH_5WIRE                        0x1000  /* TCH_5WIRE */
+#define WM831X_TCH_PDONLY                       0x0800  /* TCH_PDONLY */
+#define WM831X_TCH_ISEL                         0x0100  /* TCH_ISEL */
+#define WM831X_TCH_RPU_MASK                     0x000F  /* TCH_RPU - [3:0] */
+#define WM831X_TCH_RPU_SHIFT                         0  /* TCH_RPU - [3:0] */
+#define WM831X_TCH_RPU_WIDTH                         4  /* TCH_RPU - [3:0] */
+
+/*
+ * R16426-8 (0x402A-C) - Touch Data X/Y/X
+ */
+#define WM831X_TCH_PD                           0x8000  /* TCH_PD1 */
+#define WM831X_TCH_DATA_MASK                    0x0FFF  /* TCH_DATA - [11:0] */
+#define WM831X_TCH_DATA_SHIFT                        0  /* TCH_DATA - [11:0] */
+#define WM831X_TCH_DATA_WIDTH                       12  /* TCH_DATA - [11:0] */
+
+struct wm831x_ts {
+	struct input_dev *input_dev;
+	struct wm831x *wm831x;
+	unsigned int data_irq;
+	unsigned int pd_irq;
+	bool pressure;
+	bool pen_down;
+	struct work_struct pd_data_work;
+};
+
+static void wm831x_pd_data_work(struct work_struct *work)
+{
+	struct wm831x_ts *wm831x_ts =
+		container_of(work, struct wm831x_ts, pd_data_work);
+
+	if (wm831x_ts->pen_down) {
+		enable_irq(wm831x_ts->data_irq);
+		dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n");
+	} else {
+		enable_irq(wm831x_ts->pd_irq);
+		dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n");
+	}
+}
+
+static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data)
+{
+	struct wm831x_ts *wm831x_ts = irq_data;
+	struct wm831x *wm831x = wm831x_ts->wm831x;
+	static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE };
+	u16 data[3];
+	int count;
+	int i, ret;
+
+	if (wm831x_ts->pressure)
+		count = 3;
+	else
+		count = 2;
+
+	wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+			WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
+
+	ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count,
+			       data);
+	if (ret != 0) {
+		dev_err(wm831x->dev, "Failed to read touch data: %d\n",
+			ret);
+		return IRQ_NONE;
+	}
+
+	/*
+	 * We get a pen down reading on every reading, report pen up if any
+	 * individual reading does so.
+	 */
+	wm831x_ts->pen_down = true;
+	for (i = 0; i < count; i++) {
+		if (!(data[i] & WM831X_TCH_PD)) {
+			wm831x_ts->pen_down = false;
+			continue;
+		}
+		input_report_abs(wm831x_ts->input_dev, data_types[i],
+				 data[i] & WM831X_TCH_DATA_MASK);
+	}
+
+	if (!wm831x_ts->pen_down) {
+		/* Switch from data to pen down */
+		dev_dbg(wm831x->dev, "IRQ DATA->PD\n");
+
+		disable_irq_nosync(wm831x_ts->data_irq);
+
+		/* Don't need data any more */
+		wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+				WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
+				WM831X_TCH_Z_ENA, 0);
+
+		/* Flush any final samples that arrived while reading */
+		wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+				WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
+
+		wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data);
+
+		if (wm831x_ts->pressure)
+			input_report_abs(wm831x_ts->input_dev,
+					 ABS_PRESSURE, 0);
+
+		input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0);
+
+		schedule_work(&wm831x_ts->pd_data_work);
+	} else {
+		input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1);
+	}
+
+	input_sync(wm831x_ts->input_dev);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data)
+{
+	struct wm831x_ts *wm831x_ts = irq_data;
+	struct wm831x *wm831x = wm831x_ts->wm831x;
+	int ena = 0;
+
+	if (wm831x_ts->pen_down)
+		return IRQ_HANDLED;
+
+	disable_irq_nosync(wm831x_ts->pd_irq);
+
+	/* Start collecting data */
+	if (wm831x_ts->pressure)
+		ena |= WM831X_TCH_Z_ENA;
+
+	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+			WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA,
+			WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena);
+
+	wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+			WM831X_TCHPD_EINT, WM831X_TCHPD_EINT);
+
+	wm831x_ts->pen_down = true;
+
+	/* Switch from pen down to data */
+	dev_dbg(wm831x->dev, "IRQ PD->DATA\n");
+	schedule_work(&wm831x_ts->pd_data_work);
+
+	return IRQ_HANDLED;
+}
+
+static int wm831x_ts_input_open(struct input_dev *idev)
+{
+	struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
+	struct wm831x *wm831x = wm831x_ts->wm831x;
+
+	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+			WM831X_TCH_ENA | WM831X_TCH_CVT_ENA |
+			WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
+			WM831X_TCH_Z_ENA, WM831X_TCH_ENA);
+
+	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+			WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA);
+
+	return 0;
+}
+
+static void wm831x_ts_input_close(struct input_dev *idev)
+{
+	struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
+	struct wm831x *wm831x = wm831x_ts->wm831x;
+
+	/* Shut the controller down, disabling all other functionality too */
+	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+			WM831X_TCH_ENA | WM831X_TCH_X_ENA |
+			WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0);
+
+	/* Make sure any pending IRQs are done, the above will prevent
+	 * new ones firing.
+	 */
+	synchronize_irq(wm831x_ts->data_irq);
+	synchronize_irq(wm831x_ts->pd_irq);
+
+	/* Make sure the IRQ completion work is quiesced */
+	flush_work(&wm831x_ts->pd_data_work);
+
+	/* If we ended up with the pen down then make sure we revert back
+	 * to pen detection state for the next time we start up.
+	 */
+	if (wm831x_ts->pen_down) {
+		disable_irq(wm831x_ts->data_irq);
+		enable_irq(wm831x_ts->pd_irq);
+		wm831x_ts->pen_down = false;
+	}
+}
+
+static int wm831x_ts_probe(struct platform_device *pdev)
+{
+	struct wm831x_ts *wm831x_ts;
+	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+	struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent);
+	struct wm831x_touch_pdata *pdata = NULL;
+	struct input_dev *input_dev;
+	int error, irqf;
+
+	if (core_pdata)
+		pdata = core_pdata->touch;
+
+	wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts),
+				 GFP_KERNEL);
+	input_dev = devm_input_allocate_device(&pdev->dev);
+	if (!wm831x_ts || !input_dev) {
+		error = -ENOMEM;
+		goto err_alloc;
+	}
+
+	wm831x_ts->wm831x = wm831x;
+	wm831x_ts->input_dev = input_dev;
+	INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work);
+
+	/*
+	 * If we have a direct IRQ use it, otherwise use the interrupt
+	 * from the WM831x IRQ controller.
+	 */
+	wm831x_ts->data_irq = wm831x_irq(wm831x,
+					 platform_get_irq_byname(pdev,
+								 "TCHDATA"));
+	if (pdata && pdata->data_irq)
+		wm831x_ts->data_irq = pdata->data_irq;
+
+	wm831x_ts->pd_irq = wm831x_irq(wm831x,
+				       platform_get_irq_byname(pdev, "TCHPD"));
+	if (pdata && pdata->pd_irq)
+		wm831x_ts->pd_irq = pdata->pd_irq;
+
+	if (pdata)
+		wm831x_ts->pressure = pdata->pressure;
+	else
+		wm831x_ts->pressure = true;
+
+	/* Five wire touchscreens can't report pressure */
+	if (pdata && pdata->fivewire) {
+		wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+				WM831X_TCH_5WIRE, WM831X_TCH_5WIRE);
+
+		/* Pressure measurements are not possible for five wire mode */
+		WARN_ON(pdata->pressure && pdata->fivewire);
+		wm831x_ts->pressure = false;
+	} else {
+		wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+				WM831X_TCH_5WIRE, 0);
+	}
+
+	if (pdata) {
+		switch (pdata->isel) {
+		default:
+			dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n",
+				pdata->isel);
+			/* Fall through */
+		case 200:
+		case 0:
+			wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+					WM831X_TCH_ISEL, 0);
+			break;
+		case 400:
+			wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+					WM831X_TCH_ISEL, WM831X_TCH_ISEL);
+			break;
+		}
+	}
+
+	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+			WM831X_TCH_PDONLY, 0);
+
+	/* Default to 96 samples/sec */
+	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+			WM831X_TCH_RATE_MASK, 6);
+
+	if (pdata && pdata->data_irqf)
+		irqf = pdata->data_irqf;
+	else
+		irqf = IRQF_TRIGGER_HIGH;
+
+	error = request_threaded_irq(wm831x_ts->data_irq,
+				     NULL, wm831x_ts_data_irq,
+				     irqf | IRQF_ONESHOT,
+				     "Touchscreen data", wm831x_ts);
+	if (error) {
+		dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n",
+			wm831x_ts->data_irq, error);
+		goto err_alloc;
+	}
+	disable_irq(wm831x_ts->data_irq);
+
+	if (pdata && pdata->pd_irqf)
+		irqf = pdata->pd_irqf;
+	else
+		irqf = IRQF_TRIGGER_HIGH;
+
+	error = request_threaded_irq(wm831x_ts->pd_irq,
+				     NULL, wm831x_ts_pen_down_irq,
+				     irqf | IRQF_ONESHOT,
+				     "Touchscreen pen down", wm831x_ts);
+	if (error) {
+		dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n",
+			wm831x_ts->pd_irq, error);
+		goto err_data_irq;
+	}
+
+	/* set up touch configuration */
+	input_dev->name = "WM831x touchscreen";
+	input_dev->phys = "wm831x";
+	input_dev->open = wm831x_ts_input_open;
+	input_dev->close = wm831x_ts_input_close;
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+
+	input_set_abs_params(input_dev, ABS_X, 0, 4095, 5, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, 4095, 5, 0);
+	if (wm831x_ts->pressure)
+		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 4095, 5, 0);
+
+	input_set_drvdata(input_dev, wm831x_ts);
+	input_dev->dev.parent = &pdev->dev;
+
+	error = input_register_device(input_dev);
+	if (error)
+		goto err_pd_irq;
+
+	platform_set_drvdata(pdev, wm831x_ts);
+	return 0;
+
+err_pd_irq:
+	free_irq(wm831x_ts->pd_irq, wm831x_ts);
+err_data_irq:
+	free_irq(wm831x_ts->data_irq, wm831x_ts);
+err_alloc:
+
+	return error;
+}
+
+static int wm831x_ts_remove(struct platform_device *pdev)
+{
+	struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev);
+
+	free_irq(wm831x_ts->pd_irq, wm831x_ts);
+	free_irq(wm831x_ts->data_irq, wm831x_ts);
+
+	return 0;
+}
+
+static struct platform_driver wm831x_ts_driver = {
+	.driver = {
+		.name = "wm831x-touch",
+	},
+	.probe = wm831x_ts_probe,
+	.remove = wm831x_ts_remove,
+};
+module_platform_driver(wm831x_ts_driver);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM831x PMIC touchscreen driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-touch");
diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c
new file mode 100644
index 0000000..adc13a5
--- /dev/null
+++ b/drivers/input/touchscreen/wm9705.c
@@ -0,0 +1,350 @@
+/*
+ * wm9705.c  --  Codec driver for Wolfson WM9705 AC97 Codec.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *                   Russell King <rmk@arm.linux.org.uk>
+ *
+ *  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME			"wm97xx"
+#define WM9705_VERSION		"1.00"
+#define DEFAULT_PRESSURE	0xb0c0
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ *     pil = 1 to use 200uA and
+ *     pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 4;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Pen detect comparator threshold.
+ *
+ * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold
+ * i.e. 1 =  Vmid/15 threshold
+ *      15 =  Vmid/1 threshold
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down events.
+ */
+static int pdd = 8;
+module_param(pdd, int, 0);
+MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+	21,    /* 1 AC97 Link frames */
+	42,    /* 2                  */
+	84,    /* 4                  */
+	167,   /* 8                  */
+	333,   /* 16                 */
+	667,   /* 32                 */
+	1000,  /* 48                 */
+	1333,  /* 64                 */
+	2000,  /* 96                 */
+	2667,  /* 128                */
+	3333,  /* 160                */
+	4000,  /* 192                */
+	4667,  /* 224                */
+	5333,  /* 256                */
+	6000,  /* 288                */
+	0      /* No delay, switch matrix always on */
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+	udelay(3 * AC97_LINK_FRAME + delay_table[d]);
+}
+
+/*
+ * set up the physical settings of the WM9705
+ */
+static void wm9705_phy_init(struct wm97xx *wm)
+{
+	u16 dig1 = 0, dig2 = WM97XX_RPR;
+
+	/*
+	* mute VIDEO and AUX as they share X and Y touchscreen
+	* inputs on the WM9705
+	*/
+	wm97xx_reg_write(wm, AC97_AUX, 0x8000);
+	wm97xx_reg_write(wm, AC97_VIDEO, 0x8000);
+
+	/* touchpanel pressure current*/
+	if (pil == 2) {
+		dig2 |= WM9705_PIL;
+		dev_dbg(wm->dev,
+			"setting pressure measurement current to 400uA.");
+	} else if (pil)
+		dev_dbg(wm->dev,
+			"setting pressure measurement current to 200uA.");
+	if (!pil)
+		pressure = 0;
+
+	/* polling mode sample settling delay */
+	if (delay != 4) {
+		if (delay < 0 || delay > 15) {
+			dev_dbg(wm->dev, "supplied delay out of range.");
+			delay = 4;
+		}
+	}
+	dig1 &= 0xff0f;
+	dig1 |= WM97XX_DELAY(delay);
+	dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.",
+		delay_table[delay]);
+
+	/* WM9705 pdd */
+	dig2 |= (pdd & 0x000f);
+	dev_dbg(wm->dev, "setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
+
+	/* mask */
+	dig2 |= ((mask & 0x3) << 4);
+
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+}
+
+static void wm9705_dig_enable(struct wm97xx *wm, int enable)
+{
+	if (enable) {
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+				 wm->dig[2] | WM97XX_PRP_DET_DIG);
+		wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+	} else
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+				 wm->dig[2] & ~WM97XX_PRP_DET_DIG);
+}
+
+static void wm9705_aux_prepare(struct wm97xx *wm)
+{
+	memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
+}
+
+static void wm9705_dig_restore(struct wm97xx *wm)
+{
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
+}
+
+static inline int is_pden(struct wm97xx *wm)
+{
+	return wm->dig[2] & WM9705_PDEN;
+}
+
+/*
+ * Read a sample from the WM9705 adc in polling mode.
+ */
+static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
+{
+	int timeout = 5 * delay;
+	bool wants_pen = adcsel & WM97XX_PEN_DOWN;
+
+	if (wants_pen && !wm->pen_probably_down) {
+		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+		if (!(data & WM97XX_PEN_DOWN))
+			return RC_PENUP;
+		wm->pen_probably_down = 1;
+	}
+
+	/* set up digitiser */
+	if (wm->mach_ops && wm->mach_ops->pre_sample)
+		wm->mach_ops->pre_sample(adcsel);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK)
+				| WM97XX_POLL | WM97XX_DELAY(delay));
+
+	/* wait 3 AC97 time slots + delay for conversion */
+	poll_delay(delay);
+
+	/* wait for POLL to go low */
+	while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
+	       && timeout) {
+		udelay(AC97_LINK_FRAME);
+		timeout--;
+	}
+
+	if (timeout == 0) {
+		/* If PDEN is set, we can get a timeout when pen goes up */
+		if (is_pden(wm))
+			wm->pen_probably_down = 0;
+		else
+			dev_dbg(wm->dev, "adc sample timeout");
+		return RC_PENUP;
+	}
+
+	*sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	if (wm->mach_ops && wm->mach_ops->post_sample)
+		wm->mach_ops->post_sample(adcsel);
+
+	/* check we have correct sample */
+	if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+		dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+			adcsel & WM97XX_ADCSEL_MASK,
+			*sample & WM97XX_ADCSEL_MASK);
+		return RC_PENUP;
+	}
+
+	if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
+		wm->pen_probably_down = 0;
+		return RC_PENUP;
+	}
+
+	return RC_VALID;
+}
+
+/*
+ * Sample the WM9705 touchscreen in polling mode
+ */
+static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
+{
+	int rc;
+
+	rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);
+	if (rc != RC_VALID)
+		return rc;
+	rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);
+	if (rc != RC_VALID)
+		return rc;
+	if (pil) {
+		rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN, &data->p);
+		if (rc != RC_VALID)
+			return rc;
+	} else
+		data->p = DEFAULT_PRESSURE;
+
+	return RC_VALID;
+}
+
+/*
+ * Enable WM9705 continuous mode, i.e. touch data is streamed across
+ * an AC97 slot
+ */
+static int wm9705_acc_enable(struct wm97xx *wm, int enable)
+{
+	u16 dig1, dig2;
+	int ret = 0;
+
+	dig1 = wm->dig[1];
+	dig2 = wm->dig[2];
+
+	if (enable) {
+		/* continuous mode */
+		if (wm->mach_ops->acc_startup &&
+		    (ret = wm->mach_ops->acc_startup(wm)) < 0)
+			return ret;
+		dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
+			  WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
+		dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
+			WM97XX_DELAY(delay) |
+			WM97XX_SLT(wm->acc_slot) |
+			WM97XX_RATE(wm->acc_rate);
+		if (pil)
+			dig1 |= WM97XX_ADCSEL_PRES;
+		dig2 |= WM9705_PDEN;
+	} else {
+		dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
+		dig2 &= ~WM9705_PDEN;
+		if (wm->mach_ops->acc_shutdown)
+			wm->mach_ops->acc_shutdown(wm);
+	}
+
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+
+	return ret;
+}
+
+struct wm97xx_codec_drv wm9705_codec = {
+	.id = WM9705_ID2,
+	.name = "wm9705",
+	.poll_sample = wm9705_poll_sample,
+	.poll_touch = wm9705_poll_touch,
+	.acc_enable = wm9705_acc_enable,
+	.phy_init = wm9705_phy_init,
+	.dig_enable = wm9705_dig_enable,
+	.dig_restore = wm9705_dig_restore,
+	.aux_prepare = wm9705_aux_prepare,
+};
+EXPORT_SYMBOL_GPL(wm9705_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c
new file mode 100644
index 0000000..705ffa1
--- /dev/null
+++ b/drivers/input/touchscreen/wm9712.c
@@ -0,0 +1,471 @@
+/*
+ * wm9712.c  --  Codec driver for Wolfson WM9712 AC97 Codecs.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *                   Russell King <rmk@arm.linux.org.uk>
+ *
+ *  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME			"wm97xx"
+#define WM9712_VERSION		"1.00"
+#define DEFAULT_PRESSURE	0xb0c0
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set internal pull up for pen detect.
+ *
+ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
+ * i.e. pull up resistance = 64k Ohms / rpu.
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down event.
+ */
+static int rpu = 8;
+module_param(rpu, int, 0);
+MODULE_PARM_DESC(rpu, "Set internal pull up resistor for pen detect.");
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ *     pil = 1 to use 200uA and
+ *     pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 3;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Set five_wire = 1 to use a 5 wire touchscreen.
+ *
+ * NOTE: Five wire mode does not allow for readback of pressure.
+ */
+static int five_wire;
+module_param(five_wire, int, 0);
+MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen.");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * Coordinate Polling Enable.
+ *
+ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
+ * for every poll.
+ */
+static int coord;
+module_param(coord, int, 0);
+MODULE_PARM_DESC(coord, "Polling coordinate mode");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+	21,    /* 1 AC97 Link frames */
+	42,    /* 2 */
+	84,    /* 4 */
+	167,   /* 8 */
+	333,   /* 16 */
+	667,   /* 32 */
+	1000,  /* 48 */
+	1333,  /* 64 */
+	2000,  /* 96 */
+	2667,  /* 128 */
+	3333,  /* 160 */
+	4000,  /* 192 */
+	4667,  /* 224 */
+	5333,  /* 256 */
+	6000,  /* 288 */
+	0      /* No delay, switch matrix always on */
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+	udelay(3 * AC97_LINK_FRAME + delay_table[d]);
+}
+
+/*
+ * set up the physical settings of the WM9712
+ */
+static void wm9712_phy_init(struct wm97xx *wm)
+{
+	u16 dig1 = 0;
+	u16 dig2 = WM97XX_RPR | WM9712_RPU(1);
+
+	/* WM9712 rpu */
+	if (rpu) {
+		dig2 &= 0xffc0;
+		dig2 |= WM9712_RPU(rpu);
+		dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms\n",
+			64000 / rpu);
+	}
+
+	/* WM9712 five wire */
+	if (five_wire) {
+		dig2 |= WM9712_45W;
+		dev_dbg(wm->dev, "setting 5-wire touchscreen mode.\n");
+
+		if (pil) {
+			dev_warn(wm->dev, "pressure measurement is not "
+				 "supported in 5-wire mode\n");
+			pil = 0;
+		}
+	}
+
+	/* touchpanel pressure current*/
+	if (pil == 2) {
+		dig2 |= WM9712_PIL;
+		dev_dbg(wm->dev,
+			"setting pressure measurement current to 400uA.\n");
+	} else if (pil)
+		dev_dbg(wm->dev,
+			"setting pressure measurement current to 200uA.\n");
+	if (!pil)
+		pressure = 0;
+
+	/* polling mode sample settling delay */
+	if (delay < 0 || delay > 15) {
+		dev_dbg(wm->dev, "supplied delay out of range.\n");
+		delay = 4;
+	}
+	dig1 &= 0xff0f;
+	dig1 |= WM97XX_DELAY(delay);
+	dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.\n",
+		delay_table[delay]);
+
+	/* mask */
+	dig2 |= ((mask & 0x3) << 6);
+	if (mask) {
+		u16 reg;
+		/* Set GPIO4 as Mask Pin*/
+		reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+		wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4);
+		reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+		wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4);
+	}
+
+	/* wait - coord mode */
+	if (coord)
+		dig2 |= WM9712_WAIT;
+
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+}
+
+static void wm9712_dig_enable(struct wm97xx *wm, int enable)
+{
+	u16 dig2 = wm->dig[2];
+
+	if (enable) {
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+				 dig2 | WM97XX_PRP_DET_DIG);
+		wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+	} else
+		wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+				 dig2 & ~WM97XX_PRP_DET_DIG);
+}
+
+static void wm9712_aux_prepare(struct wm97xx *wm)
+{
+	memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
+}
+
+static void wm9712_dig_restore(struct wm97xx *wm)
+{
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
+}
+
+static inline int is_pden(struct wm97xx *wm)
+{
+	return wm->dig[2] & WM9712_PDEN;
+}
+
+/*
+ * Read a sample from the WM9712 adc in polling mode.
+ */
+static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
+{
+	int timeout = 5 * delay;
+	bool wants_pen = adcsel & WM97XX_PEN_DOWN;
+
+	if (wants_pen && !wm->pen_probably_down) {
+		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+		if (!(data & WM97XX_PEN_DOWN))
+			return RC_PENUP;
+		wm->pen_probably_down = 1;
+	}
+
+	/* set up digitiser */
+	if (wm->mach_ops && wm->mach_ops->pre_sample)
+		wm->mach_ops->pre_sample(adcsel);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, (adcsel & WM97XX_ADCSEL_MASK)
+				| WM97XX_POLL | WM97XX_DELAY(delay));
+
+	/* wait 3 AC97 time slots + delay for conversion */
+	poll_delay(delay);
+
+	/* wait for POLL to go low */
+	while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
+	       && timeout) {
+		udelay(AC97_LINK_FRAME);
+		timeout--;
+	}
+
+	if (timeout <= 0) {
+		/* If PDEN is set, we can get a timeout when pen goes up */
+		if (is_pden(wm))
+			wm->pen_probably_down = 0;
+		else
+			dev_dbg(wm->dev, "adc sample timeout\n");
+		return RC_PENUP;
+	}
+
+	*sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	if (wm->mach_ops && wm->mach_ops->post_sample)
+		wm->mach_ops->post_sample(adcsel);
+
+	/* check we have correct sample */
+	if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+		dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x\n",
+			adcsel & WM97XX_ADCSEL_MASK,
+			*sample & WM97XX_ADCSEL_MASK);
+		return RC_AGAIN;
+	}
+
+	if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
+		/* Sometimes it reads a wrong value the first time. */
+		*sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+		if (!(*sample & WM97XX_PEN_DOWN)) {
+			wm->pen_probably_down = 0;
+			return RC_PENUP;
+		}
+	}
+
+	return RC_VALID;
+}
+
+/*
+ * Read a coord from the WM9712 adc in polling mode.
+ */
+static int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data)
+{
+	int timeout = 5 * delay;
+
+	if (!wm->pen_probably_down) {
+		u16 data_rd = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+		if (!(data_rd & WM97XX_PEN_DOWN))
+			return RC_PENUP;
+		wm->pen_probably_down = 1;
+	}
+
+	/* set up digitiser */
+	if (wm->mach_ops && wm->mach_ops->pre_sample)
+		wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
+		WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay));
+
+	/* wait 3 AC97 time slots + delay for conversion and read x */
+	poll_delay(delay);
+	data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	/* wait for POLL to go low */
+	while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
+	       && timeout) {
+		udelay(AC97_LINK_FRAME);
+		timeout--;
+	}
+
+	if (timeout <= 0) {
+		/* If PDEN is set, we can get a timeout when pen goes up */
+		if (is_pden(wm))
+			wm->pen_probably_down = 0;
+		else
+			dev_dbg(wm->dev, "adc sample timeout\n");
+		return RC_PENUP;
+	}
+
+	/* read back y data */
+	data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	if (pil)
+		data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	else
+		data->p = DEFAULT_PRESSURE;
+
+	if (wm->mach_ops && wm->mach_ops->post_sample)
+		wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+	/* check we have correct sample */
+	if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
+		goto err;
+	if (pil && !(data->p & WM97XX_ADCSEL_PRES))
+		goto err;
+
+	if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) {
+		wm->pen_probably_down = 0;
+		return RC_PENUP;
+	}
+	return RC_VALID;
+err:
+	return 0;
+}
+
+/*
+ * Sample the WM9712 touchscreen in polling mode
+ */
+static int wm9712_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
+{
+	int rc;
+
+	if (coord) {
+		rc = wm9712_poll_coord(wm, data);
+		if (rc != RC_VALID)
+			return rc;
+	} else {
+		rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN,
+					&data->x);
+		if (rc != RC_VALID)
+			return rc;
+
+		rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN,
+					&data->y);
+		if (rc != RC_VALID)
+			return rc;
+
+		if (pil && !five_wire) {
+			rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN,
+						&data->p);
+			if (rc != RC_VALID)
+				return rc;
+		} else
+			data->p = DEFAULT_PRESSURE;
+	}
+	return RC_VALID;
+}
+
+/*
+ * Enable WM9712 continuous mode, i.e. touch data is streamed across
+ * an AC97 slot
+ */
+static int wm9712_acc_enable(struct wm97xx *wm, int enable)
+{
+	u16 dig1, dig2;
+	int ret = 0;
+
+	dig1 = wm->dig[1];
+	dig2 = wm->dig[2];
+
+	if (enable) {
+		/* continuous mode */
+		if (wm->mach_ops->acc_startup) {
+			ret = wm->mach_ops->acc_startup(wm);
+			if (ret < 0)
+				return ret;
+		}
+		dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
+			WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
+		dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
+			WM97XX_DELAY(delay) |
+			WM97XX_SLT(wm->acc_slot) |
+			WM97XX_RATE(wm->acc_rate);
+		if (pil)
+			dig1 |= WM97XX_ADCSEL_PRES;
+		dig2 |= WM9712_PDEN;
+	} else {
+		dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
+		dig2 &= ~WM9712_PDEN;
+		if (wm->mach_ops->acc_shutdown)
+			wm->mach_ops->acc_shutdown(wm);
+	}
+
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+	wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+
+	return 0;
+}
+
+struct wm97xx_codec_drv wm9712_codec = {
+	.id = WM9712_ID2,
+	.name = "wm9712",
+	.poll_sample = wm9712_poll_sample,
+	.poll_touch = wm9712_poll_touch,
+	.acc_enable = wm9712_acc_enable,
+	.phy_init = wm9712_phy_init,
+	.dig_enable = wm9712_dig_enable,
+	.dig_restore = wm9712_dig_restore,
+	.aux_prepare = wm9712_aux_prepare,
+};
+EXPORT_SYMBOL_GPL(wm9712_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("WM9712 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c
new file mode 100644
index 0000000..572a5a6
--- /dev/null
+++ b/drivers/input/touchscreen/wm9713.c
@@ -0,0 +1,481 @@
+/*
+ * wm9713.c  --  Codec touch driver for Wolfson WM9713 AC97 Codec.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *                   Russell King <rmk@arm.linux.org.uk>
+ *
+ *  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME			"wm97xx"
+#define WM9713_VERSION		"1.00"
+#define DEFAULT_PRESSURE	0xb0c0
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set internal pull up for pen detect.
+ *
+ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
+ * i.e. pull up resistance = 64k Ohms / rpu.
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down event.
+ */
+static int rpu = 8;
+module_param(rpu, int, 0);
+MODULE_PARM_DESC(rpu, "Set internal pull up resistor for pen detect.");
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ *     pil = 1 to use 200uA and
+ *     pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 4;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Set five_wire = 1 to use a 5 wire touchscreen.
+ *
+ * NOTE: Five wire mode does not allow for readback of pressure.
+ */
+static int five_wire;
+module_param(five_wire, int, 0);
+MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen.");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * Coordinate Polling Enable.
+ *
+ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
+ * for every poll.
+ */
+static int coord;
+module_param(coord, int, 0);
+MODULE_PARM_DESC(coord, "Polling coordinate mode");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+	21,    /* 1 AC97 Link frames */
+	42,    /* 2 */
+	84,    /* 4 */
+	167,   /* 8 */
+	333,   /* 16 */
+	667,   /* 32 */
+	1000,  /* 48 */
+	1333,  /* 64 */
+	2000,  /* 96 */
+	2667,  /* 128 */
+	3333,  /* 160 */
+	4000,  /* 192 */
+	4667,  /* 224 */
+	5333,  /* 256 */
+	6000,  /* 288 */
+	0      /* No delay, switch matrix always on */
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+	udelay(3 * AC97_LINK_FRAME + delay_table[d]);
+}
+
+/*
+ * set up the physical settings of the WM9713
+ */
+static void wm9713_phy_init(struct wm97xx *wm)
+{
+	u16 dig1 = 0, dig2, dig3;
+
+	/* default values */
+	dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5);
+	dig3 = WM9712_RPU(1);
+
+	/* rpu */
+	if (rpu) {
+		dig3 &= 0xffc0;
+		dig3 |= WM9712_RPU(rpu);
+		dev_info(wm->dev, "setting pen detect pull-up to %d Ohms\n",
+			 64000 / rpu);
+	}
+
+	/* Five wire panel? */
+	if (five_wire) {
+		dig3 |= WM9713_45W;
+		dev_info(wm->dev, "setting 5-wire touchscreen mode.");
+
+		if (pil) {
+			dev_warn(wm->dev,
+				 "Pressure measurement not supported in 5 "
+				 "wire mode, disabling\n");
+			pil = 0;
+		}
+	}
+
+	/* touchpanel pressure */
+	if (pil == 2) {
+		dig3 |= WM9712_PIL;
+		dev_info(wm->dev,
+			 "setting pressure measurement current to 400uA.");
+	} else if (pil)
+		dev_info(wm->dev,
+			 "setting pressure measurement current to 200uA.");
+	if (!pil)
+		pressure = 0;
+
+	/* sample settling delay */
+	if (delay < 0 || delay > 15) {
+		dev_info(wm->dev, "supplied delay out of range.");
+		delay = 4;
+		dev_info(wm->dev, "setting adc sample delay to %d u Secs.",
+			 delay_table[delay]);
+	}
+	dig2 &= 0xff0f;
+	dig2 |= WM97XX_DELAY(delay);
+
+	/* mask */
+	dig3 |= ((mask & 0x3) << 4);
+	if (coord)
+		dig3 |= WM9713_WAIT;
+
+	wm->misc = wm97xx_reg_read(wm, 0x5a);
+
+	wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
+	wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0);
+}
+
+static void wm9713_dig_enable(struct wm97xx *wm, int enable)
+{
+	u16 val;
+
+	if (enable) {
+		val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
+		wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff);
+		wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] |
+				 WM97XX_PRP_DET_DIG);
+		wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+	} else {
+		wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] &
+					~WM97XX_PRP_DET_DIG);
+		val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
+		wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000);
+	}
+}
+
+static void wm9713_dig_restore(struct wm97xx *wm)
+{
+	wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]);
+}
+
+static void wm9713_aux_prepare(struct wm97xx *wm)
+{
+	memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+	wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG);
+}
+
+static inline int is_pden(struct wm97xx *wm)
+{
+	return wm->dig[2] & WM9713_PDEN;
+}
+
+/*
+ * Read a sample from the WM9713 adc in polling mode.
+ */
+static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
+{
+	u16 dig1;
+	int timeout = 5 * delay;
+	bool wants_pen = adcsel & WM97XX_PEN_DOWN;
+
+	if (wants_pen && !wm->pen_probably_down) {
+		u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+		if (!(data & WM97XX_PEN_DOWN))
+			return RC_PENUP;
+		wm->pen_probably_down = 1;
+	}
+
+	/* set up digitiser */
+	dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
+	dig1 &= ~WM9713_ADCSEL_MASK;
+	/* WM97XX_ADCSEL_* channels need to be converted to WM9713 format */
+	dig1 |= 1 << ((adcsel & WM97XX_ADCSEL_MASK) >> 12);
+
+	if (wm->mach_ops && wm->mach_ops->pre_sample)
+		wm->mach_ops->pre_sample(adcsel);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL);
+
+	/* wait 3 AC97 time slots + delay for conversion */
+	poll_delay(delay);
+
+	/* wait for POLL to go low */
+	while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) &&
+		timeout) {
+		udelay(AC97_LINK_FRAME);
+		timeout--;
+	}
+
+	if (timeout <= 0) {
+		/* If PDEN is set, we can get a timeout when pen goes up */
+		if (is_pden(wm))
+			wm->pen_probably_down = 0;
+		else
+			dev_dbg(wm->dev, "adc sample timeout");
+		return RC_PENUP;
+	}
+
+	*sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	if (wm->mach_ops && wm->mach_ops->post_sample)
+		wm->mach_ops->post_sample(adcsel);
+
+	/* check we have correct sample */
+	if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) {
+		dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x",
+			adcsel & WM97XX_ADCSEL_MASK,
+			*sample & WM97XX_ADCSEL_MASK);
+		return RC_PENUP;
+	}
+
+	if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) {
+		wm->pen_probably_down = 0;
+		return RC_PENUP;
+	}
+
+	return RC_VALID;
+}
+
+/*
+ * Read a coordinate from the WM9713 adc in polling mode.
+ */
+static int wm9713_poll_coord(struct wm97xx *wm, struct wm97xx_data *data)
+{
+	u16 dig1;
+	int timeout = 5 * delay;
+
+	if (!wm->pen_probably_down) {
+		u16 val = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+		if (!(val & WM97XX_PEN_DOWN))
+			return RC_PENUP;
+		wm->pen_probably_down = 1;
+	}
+
+	/* set up digitiser */
+	dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
+	dig1 &= ~WM9713_ADCSEL_MASK;
+	if (pil)
+		dig1 |= WM9713_ADCSEL_PRES;
+
+	if (wm->mach_ops && wm->mach_ops->pre_sample)
+		wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG1,
+			 dig1 | WM9713_POLL | WM9713_COO);
+
+	/* wait 3 AC97 time slots + delay for conversion */
+	poll_delay(delay);
+	data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	/* wait for POLL to go low */
+	while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL)
+	       && timeout) {
+		udelay(AC97_LINK_FRAME);
+		timeout--;
+	}
+
+	if (timeout <= 0) {
+		/* If PDEN is set, we can get a timeout when pen goes up */
+		if (is_pden(wm))
+			wm->pen_probably_down = 0;
+		else
+			dev_dbg(wm->dev, "adc sample timeout");
+		return RC_PENUP;
+	}
+
+	/* read back data */
+	data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	if (pil)
+		data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+	else
+		data->p = DEFAULT_PRESSURE;
+
+	if (wm->mach_ops && wm->mach_ops->post_sample)
+		wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+	/* check we have correct sample */
+	if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
+		goto err;
+	if (pil && !(data->p & WM97XX_ADCSEL_PRES))
+		goto err;
+
+	if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) {
+		wm->pen_probably_down = 0;
+		return RC_PENUP;
+	}
+	return RC_VALID;
+err:
+	return 0;
+}
+
+/*
+ * Sample the WM9713 touchscreen in polling mode
+ */
+static int wm9713_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
+{
+	int rc;
+
+	if (coord) {
+		rc = wm9713_poll_coord(wm, data);
+		if (rc != RC_VALID)
+			return rc;
+	} else {
+		rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_X | WM97XX_PEN_DOWN, &data->x);
+		if (rc != RC_VALID)
+			return rc;
+		rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_Y | WM97XX_PEN_DOWN, &data->y);
+		if (rc != RC_VALID)
+			return rc;
+		if (pil) {
+			rc = wm9713_poll_sample(wm, WM97XX_ADCSEL_PRES | WM97XX_PEN_DOWN,
+						&data->p);
+			if (rc != RC_VALID)
+				return rc;
+		} else
+			data->p = DEFAULT_PRESSURE;
+	}
+	return RC_VALID;
+}
+
+/*
+ * Enable WM9713 continuous mode, i.e. touch data is streamed across
+ * an AC97 slot
+ */
+static int wm9713_acc_enable(struct wm97xx *wm, int enable)
+{
+	u16 dig1, dig2, dig3;
+	int ret = 0;
+
+	dig1 = wm->dig[0];
+	dig2 = wm->dig[1];
+	dig3 = wm->dig[2];
+
+	if (enable) {
+		/* continuous mode */
+		if (wm->mach_ops->acc_startup &&
+			(ret = wm->mach_ops->acc_startup(wm)) < 0)
+			return ret;
+
+		dig1 &= ~WM9713_ADCSEL_MASK;
+		dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X |
+			WM9713_ADCSEL_Y;
+		if (pil)
+			dig1 |= WM9713_ADCSEL_PRES;
+		dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK  |
+			WM97XX_CM_RATE_MASK);
+		dig2 |= WM97XX_SLEN | WM97XX_DELAY(delay) |
+		WM97XX_SLT(wm->acc_slot) | WM97XX_RATE(wm->acc_rate);
+		dig3 |= WM9713_PDEN;
+	} else {
+		dig1 &= ~(WM9713_CTC | WM9713_COO);
+		dig2 &= ~WM97XX_SLEN;
+		dig3 &= ~WM9713_PDEN;
+		if (wm->mach_ops->acc_shutdown)
+			wm->mach_ops->acc_shutdown(wm);
+	}
+
+	wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
+
+	return ret;
+}
+
+struct wm97xx_codec_drv wm9713_codec = {
+	.id = WM9713_ID2,
+	.name = "wm9713",
+	.poll_sample = wm9713_poll_sample,
+	.poll_touch = wm9713_poll_touch,
+	.acc_enable = wm9713_acc_enable,
+	.phy_init = wm9713_phy_init,
+	.dig_enable = wm9713_dig_enable,
+	.dig_restore = wm9713_dig_restore,
+	.aux_prepare = wm9713_aux_prepare,
+};
+EXPORT_SYMBOL_GPL(wm9713_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("WM9713 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
new file mode 100644
index 0000000..1534e9b
--- /dev/null
+++ b/drivers/input/touchscreen/wm97xx-core.c
@@ -0,0 +1,854 @@
+/*
+ * wm97xx-core.c  --  Touch screen driver core for Wolfson WM9705, WM9712
+ *                    and WM9713 AC97 Codecs.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *                   Russell King <rmk@arm.linux.org.uk>
+ *
+ *  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.
+ *
+ * Notes:
+ *
+ *  Features:
+ *       - supports WM9705, WM9712, WM9713
+ *       - polling mode
+ *       - continuous mode (arch-dependent)
+ *       - adjustable rpu/dpp settings
+ *       - adjustable pressure current
+ *       - adjustable sample settle delay
+ *       - 4 and 5 wire touchscreens (5 wire is WM9712 only)
+ *       - pen down detection
+ *       - battery monitor
+ *       - sample AUX adcs
+ *       - power management
+ *       - codec GPIO
+ *       - codec event notification
+ * Todo
+ *       - Support for async sampling control for noisy LCDs.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/pm.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/wm97xx.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#define TS_NAME			"wm97xx"
+#define WM_CORE_VERSION		"1.00"
+#define DEFAULT_PRESSURE	0xb0c0
+
+
+/*
+ * Touchscreen absolute values
+ *
+ * These parameters are used to help the input layer discard out of
+ * range readings and reduce jitter etc.
+ *
+ *   o min, max:- indicate the min and max values your touch screen returns
+ *   o fuzz:- use a higher number to reduce jitter
+ *
+ * The default values correspond to Mainstone II in QVGA mode
+ *
+ * Please read
+ * Documentation/input/input-programming.txt for more details.
+ */
+
+static int abs_x[3] = {150, 4000, 5};
+module_param_array(abs_x, int, NULL, 0);
+MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz");
+
+static int abs_y[3] = {200, 4000, 40};
+module_param_array(abs_y, int, NULL, 0);
+MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz");
+
+static int abs_p[3] = {0, 150, 4};
+module_param_array(abs_p, int, NULL, 0);
+MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz");
+
+/*
+ * wm97xx IO access, all IO locking done by AC97 layer
+ */
+int wm97xx_reg_read(struct wm97xx *wm, u16 reg)
+{
+	if (wm->ac97)
+		return wm->ac97->bus->ops->read(wm->ac97, reg);
+	else
+		return -1;
+}
+EXPORT_SYMBOL_GPL(wm97xx_reg_read);
+
+void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val)
+{
+	/* cache digitiser registers */
+	if (reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3)
+		wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val;
+
+	/* cache gpio regs */
+	if (reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE)
+		wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val;
+
+	/* wm9713 irq reg */
+	if (reg == 0x5a)
+		wm->misc = val;
+
+	if (wm->ac97)
+		wm->ac97->bus->ops->write(wm->ac97, reg, val);
+}
+EXPORT_SYMBOL_GPL(wm97xx_reg_write);
+
+/**
+ * wm97xx_read_aux_adc - Read the aux adc.
+ * @wm: wm97xx device.
+ * @adcsel: codec ADC to be read
+ *
+ * Reads the selected AUX ADC.
+ */
+
+int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
+{
+	int power_adc = 0, auxval;
+	u16 power = 0;
+	int rc = 0;
+	int timeout = 0;
+
+	/* get codec */
+	mutex_lock(&wm->codec_mutex);
+
+	/* When the touchscreen is not in use, we may have to power up
+	 * the AUX ADC before we can use sample the AUX inputs->
+	 */
+	if (wm->id == WM9713_ID2 &&
+	    (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) {
+		power_adc = 1;
+		wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff);
+	}
+
+	/* Prepare the codec for AUX reading */
+	wm->codec->aux_prepare(wm);
+
+	/* Turn polling mode on to read AUX ADC */
+	wm->pen_probably_down = 1;
+
+	while (rc != RC_VALID && timeout++ < 5)
+		rc = wm->codec->poll_sample(wm, adcsel, &auxval);
+
+	if (power_adc)
+		wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000);
+
+	wm->codec->dig_restore(wm);
+
+	wm->pen_probably_down = 0;
+
+	if (timeout >= 5) {
+		dev_err(wm->dev,
+			"timeout reading auxadc %d, disabling digitiser\n",
+			adcsel);
+		wm->codec->dig_enable(wm, false);
+	}
+
+	mutex_unlock(&wm->codec_mutex);
+	return (rc == RC_VALID ? auxval & 0xfff : -EBUSY);
+}
+EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc);
+
+/**
+ * wm97xx_get_gpio - Get the status of a codec GPIO.
+ * @wm: wm97xx device.
+ * @gpio: gpio
+ *
+ * Get the status of a codec GPIO pin
+ */
+
+enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio)
+{
+	u16 status;
+	enum wm97xx_gpio_status ret;
+
+	mutex_lock(&wm->codec_mutex);
+	status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+
+	if (status & gpio)
+		ret = WM97XX_GPIO_HIGH;
+	else
+		ret = WM97XX_GPIO_LOW;
+
+	mutex_unlock(&wm->codec_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm97xx_get_gpio);
+
+/**
+ * wm97xx_set_gpio - Set the status of a codec GPIO.
+ * @wm: wm97xx device.
+ * @gpio: gpio
+ *
+ *
+ * Set the status of a codec GPIO pin
+ */
+
+void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
+				enum wm97xx_gpio_status status)
+{
+	u16 reg;
+
+	mutex_lock(&wm->codec_mutex);
+	reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+
+	if (status == WM97XX_GPIO_HIGH)
+		reg |= gpio;
+	else
+		reg &= ~gpio;
+
+	if (wm->id == WM9712_ID2 && wm->variant != WM97xx_WM1613)
+		wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1);
+	else
+		wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg);
+	mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_set_gpio);
+
+/*
+ * Codec GPIO pin configuration, this sets pin direction, polarity,
+ * stickyness and wake up.
+ */
+void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir,
+		   enum wm97xx_gpio_pol pol, enum wm97xx_gpio_sticky sticky,
+		   enum wm97xx_gpio_wake wake)
+{
+	u16 reg;
+
+	mutex_lock(&wm->codec_mutex);
+	reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+
+	if (pol == WM97XX_GPIO_POL_HIGH)
+		reg |= gpio;
+	else
+		reg &= ~gpio;
+
+	wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg);
+	reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
+
+	if (sticky == WM97XX_GPIO_STICKY)
+		reg |= gpio;
+	else
+		reg &= ~gpio;
+
+	wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg);
+	reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
+
+	if (wake == WM97XX_GPIO_WAKE)
+		reg |= gpio;
+	else
+		reg &= ~gpio;
+
+	wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg);
+	reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+
+	if (dir == WM97XX_GPIO_IN)
+		reg |= gpio;
+	else
+		reg &= ~gpio;
+
+	wm97xx_reg_write(wm, AC97_GPIO_CFG, reg);
+	mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_config_gpio);
+
+/*
+ * Configure the WM97XX_PRP value to use while system is suspended.
+ * If a value other than 0 is set then WM97xx pen detection will be
+ * left enabled in the configured mode while the system is in suspend,
+ * the device has users and suspend has not been disabled via the
+ * wakeup sysfs entries.
+ *
+ * @wm:   WM97xx device to configure
+ * @mode: WM97XX_PRP value to configure while suspended
+ */
+void wm97xx_set_suspend_mode(struct wm97xx *wm, u16 mode)
+{
+	wm->suspend_mode = mode;
+	device_init_wakeup(&wm->input_dev->dev, mode != 0);
+}
+EXPORT_SYMBOL_GPL(wm97xx_set_suspend_mode);
+
+/*
+ * Handle a pen down interrupt.
+ */
+static void wm97xx_pen_irq_worker(struct work_struct *work)
+{
+	struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work);
+	int pen_was_down = wm->pen_is_down;
+
+	/* do we need to enable the touch panel reader */
+	if (wm->id == WM9705_ID2) {
+		if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) &
+					WM97XX_PEN_DOWN)
+			wm->pen_is_down = 1;
+		else
+			wm->pen_is_down = 0;
+	} else {
+		u16 status, pol;
+		mutex_lock(&wm->codec_mutex);
+		status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+		pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+
+		if (WM97XX_GPIO_13 & pol & status) {
+			wm->pen_is_down = 1;
+			wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol &
+						~WM97XX_GPIO_13);
+		} else {
+			wm->pen_is_down = 0;
+			wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol |
+					 WM97XX_GPIO_13);
+		}
+
+		if (wm->id == WM9712_ID2 && wm->variant != WM97xx_WM1613)
+			wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status &
+						~WM97XX_GPIO_13) << 1);
+		else
+			wm97xx_reg_write(wm, AC97_GPIO_STATUS, status &
+						~WM97XX_GPIO_13);
+		mutex_unlock(&wm->codec_mutex);
+	}
+
+	/* If the system is not using continuous mode or it provides a
+	 * pen down operation then we need to schedule polls while the
+	 * pen is down.  Otherwise the machine driver is responsible
+	 * for scheduling reads.
+	 */
+	if (!wm->mach_ops->acc_enabled || wm->mach_ops->acc_pen_down) {
+		if (wm->pen_is_down && !pen_was_down) {
+			/* Data is not available immediately on pen down */
+			queue_delayed_work(wm->ts_workq, &wm->ts_reader, 1);
+		}
+
+		/* Let ts_reader report the pen up for debounce. */
+		if (!wm->pen_is_down && pen_was_down)
+			wm->pen_is_down = 1;
+	}
+
+	if (!wm->pen_is_down && wm->mach_ops->acc_enabled)
+		wm->mach_ops->acc_pen_up(wm);
+
+	wm->mach_ops->irq_enable(wm, 1);
+}
+
+/*
+ * Codec PENDOWN irq handler
+ *
+ * We have to disable the codec interrupt in the handler because it
+ * can take up to 1ms to clear the interrupt source. We schedule a task
+ * in a work queue to do the actual interaction with the chip.  The
+ * interrupt is then enabled again in the slow handler when the source
+ * has been cleared.
+ */
+static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id)
+{
+	struct wm97xx *wm = dev_id;
+
+	if (!work_pending(&wm->pen_event_work)) {
+		wm->mach_ops->irq_enable(wm, 0);
+		queue_work(wm->ts_workq, &wm->pen_event_work);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * initialise pen IRQ handler and workqueue
+ */
+static int wm97xx_init_pen_irq(struct wm97xx *wm)
+{
+	u16 reg;
+
+	/* If an interrupt is supplied an IRQ enable operation must also be
+	 * provided. */
+	BUG_ON(!wm->mach_ops->irq_enable);
+
+	if (request_irq(wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED,
+			"wm97xx-pen", wm)) {
+		dev_err(wm->dev,
+			"Failed to register pen down interrupt, polling");
+		wm->pen_irq = 0;
+		return -EINVAL;
+	}
+
+	/* Configure GPIO as interrupt source on WM971x */
+	if (wm->id != WM9705_ID2) {
+		BUG_ON(!wm->mach_ops->irq_gpio);
+		reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+		wm97xx_reg_write(wm, AC97_MISC_AFE,
+				reg & ~(wm->mach_ops->irq_gpio));
+		reg = wm97xx_reg_read(wm, 0x5a);
+		wm97xx_reg_write(wm, 0x5a, reg & ~0x0001);
+	}
+
+	return 0;
+}
+
+static int wm97xx_read_samples(struct wm97xx *wm)
+{
+	struct wm97xx_data data;
+	int rc;
+
+	mutex_lock(&wm->codec_mutex);
+
+	if (wm->mach_ops && wm->mach_ops->acc_enabled)
+		rc = wm->mach_ops->acc_pen_down(wm);
+	else
+		rc = wm->codec->poll_touch(wm, &data);
+
+	if (rc & RC_PENUP) {
+		if (wm->pen_is_down) {
+			wm->pen_is_down = 0;
+			dev_dbg(wm->dev, "pen up\n");
+			input_report_abs(wm->input_dev, ABS_PRESSURE, 0);
+			input_report_key(wm->input_dev, BTN_TOUCH, 0);
+			input_sync(wm->input_dev);
+		} else if (!(rc & RC_AGAIN)) {
+			/* We need high frequency updates only while
+			* pen is down, the user never will be able to
+			* touch screen faster than a few times per
+			* second... On the other hand, when the user
+			* is actively working with the touchscreen we
+			* don't want to lose the quick response. So we
+			* will slowly increase sleep time after the
+			* pen is up and quicky restore it to ~one task
+			* switch when pen is down again.
+			*/
+			if (wm->ts_reader_interval < HZ / 10)
+				wm->ts_reader_interval++;
+		}
+
+	} else if (rc & RC_VALID) {
+		dev_dbg(wm->dev,
+			"pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n",
+			data.x >> 12, data.x & 0xfff, data.y >> 12,
+			data.y & 0xfff, data.p >> 12, data.p & 0xfff);
+
+		if (abs_x[0] > (data.x & 0xfff) ||
+		    abs_x[1] < (data.x & 0xfff) ||
+		    abs_y[0] > (data.y & 0xfff) ||
+		    abs_y[1] < (data.y & 0xfff)) {
+			dev_dbg(wm->dev, "Measurement out of range, dropping it\n");
+			rc = RC_AGAIN;
+			goto out;
+		}
+
+		input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff);
+		input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff);
+		input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff);
+		input_report_key(wm->input_dev, BTN_TOUCH, 1);
+		input_sync(wm->input_dev);
+		wm->pen_is_down = 1;
+		wm->ts_reader_interval = wm->ts_reader_min_interval;
+	} else if (rc & RC_PENDOWN) {
+		dev_dbg(wm->dev, "pen down\n");
+		wm->pen_is_down = 1;
+		wm->ts_reader_interval = wm->ts_reader_min_interval;
+	}
+
+out:
+	mutex_unlock(&wm->codec_mutex);
+	return rc;
+}
+
+/*
+* The touchscreen sample reader.
+*/
+static void wm97xx_ts_reader(struct work_struct *work)
+{
+	int rc;
+	struct wm97xx *wm = container_of(work, struct wm97xx, ts_reader.work);
+
+	BUG_ON(!wm->codec);
+
+	do {
+		rc = wm97xx_read_samples(wm);
+	} while (rc & RC_AGAIN);
+
+	if (wm->pen_is_down || !wm->pen_irq)
+		queue_delayed_work(wm->ts_workq, &wm->ts_reader,
+				   wm->ts_reader_interval);
+}
+
+/**
+ * wm97xx_ts_input_open - Open the touch screen input device.
+ * @idev:	Input device to be opened.
+ *
+ * Called by the input sub system to open a wm97xx touchscreen device.
+ * Starts the touchscreen thread and touch digitiser.
+ */
+static int wm97xx_ts_input_open(struct input_dev *idev)
+{
+	struct wm97xx *wm = input_get_drvdata(idev);
+
+	wm->ts_workq = create_singlethread_workqueue("kwm97xx");
+	if (wm->ts_workq == NULL) {
+		dev_err(wm->dev,
+			"Failed to create workqueue\n");
+		return -EINVAL;
+	}
+
+	/* start digitiser */
+	if (wm->mach_ops && wm->mach_ops->acc_enabled)
+		wm->codec->acc_enable(wm, 1);
+	wm->codec->dig_enable(wm, 1);
+
+	INIT_DELAYED_WORK(&wm->ts_reader, wm97xx_ts_reader);
+	INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker);
+
+	wm->ts_reader_min_interval = HZ >= 100 ? HZ / 100 : 1;
+	if (wm->ts_reader_min_interval < 1)
+		wm->ts_reader_min_interval = 1;
+	wm->ts_reader_interval = wm->ts_reader_min_interval;
+
+	wm->pen_is_down = 0;
+	if (wm->pen_irq)
+		wm97xx_init_pen_irq(wm);
+	else
+		dev_err(wm->dev, "No IRQ specified\n");
+
+	/* If we either don't have an interrupt for pen down events or
+	 * failed to acquire it then we need to poll.
+	 */
+	if (wm->pen_irq == 0)
+		queue_delayed_work(wm->ts_workq, &wm->ts_reader,
+				   wm->ts_reader_interval);
+
+	return 0;
+}
+
+/**
+ * wm97xx_ts_input_close - Close the touch screen input device.
+ * @idev:	Input device to be closed.
+ *
+ * Called by the input sub system to close a wm97xx touchscreen
+ * device.  Kills the touchscreen thread and stops the touch
+ * digitiser.
+ */
+
+static void wm97xx_ts_input_close(struct input_dev *idev)
+{
+	struct wm97xx *wm = input_get_drvdata(idev);
+	u16 reg;
+
+	if (wm->pen_irq) {
+		/* Return the interrupt to GPIO usage (disabling it) */
+		if (wm->id != WM9705_ID2) {
+			BUG_ON(!wm->mach_ops->irq_gpio);
+			reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+			wm97xx_reg_write(wm, AC97_MISC_AFE,
+					 reg | wm->mach_ops->irq_gpio);
+		}
+
+		free_irq(wm->pen_irq, wm);
+	}
+
+	wm->pen_is_down = 0;
+
+	/* Balance out interrupt disables/enables */
+	if (cancel_work_sync(&wm->pen_event_work))
+		wm->mach_ops->irq_enable(wm, 1);
+
+	/* ts_reader rearms itself so we need to explicitly stop it
+	 * before we destroy the workqueue.
+	 */
+	cancel_delayed_work_sync(&wm->ts_reader);
+
+	destroy_workqueue(wm->ts_workq);
+
+	/* stop digitiser */
+	wm->codec->dig_enable(wm, 0);
+	if (wm->mach_ops && wm->mach_ops->acc_enabled)
+		wm->codec->acc_enable(wm, 0);
+}
+
+static int wm97xx_probe(struct device *dev)
+{
+	struct wm97xx *wm;
+	struct wm97xx_pdata *pdata = dev_get_platdata(dev);
+	int ret = 0, id = 0;
+
+	wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL);
+	if (!wm)
+		return -ENOMEM;
+	mutex_init(&wm->codec_mutex);
+
+	wm->dev = dev;
+	dev_set_drvdata(dev, wm);
+	wm->ac97 = to_ac97_t(dev);
+
+	/* check that we have a supported codec */
+	id = wm97xx_reg_read(wm, AC97_VENDOR_ID1);
+	if (id != WM97XX_ID1) {
+		dev_err(dev, "Device with vendor %04x is not a wm97xx\n", id);
+		ret = -ENODEV;
+		goto alloc_err;
+	}
+
+	wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2);
+
+	wm->variant = WM97xx_GENERIC;
+
+	dev_info(wm->dev, "detected a wm97%02x codec\n", wm->id & 0xff);
+
+	switch (wm->id & 0xff) {
+#ifdef CONFIG_TOUCHSCREEN_WM9705
+	case 0x05:
+		wm->codec = &wm9705_codec;
+		break;
+#endif
+#ifdef CONFIG_TOUCHSCREEN_WM9712
+	case 0x12:
+		wm->codec = &wm9712_codec;
+		break;
+#endif
+#ifdef CONFIG_TOUCHSCREEN_WM9713
+	case 0x13:
+		wm->codec = &wm9713_codec;
+		break;
+#endif
+	default:
+		dev_err(wm->dev, "Support for wm97%02x not compiled in.\n",
+			wm->id & 0xff);
+		ret = -ENODEV;
+		goto alloc_err;
+	}
+
+	/* set up physical characteristics */
+	wm->codec->phy_init(wm);
+
+	/* load gpio cache */
+	wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+	wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+	wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
+	wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
+	wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+	wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE);
+
+	wm->input_dev = input_allocate_device();
+	if (wm->input_dev == NULL) {
+		ret = -ENOMEM;
+		goto alloc_err;
+	}
+
+	/* set up touch configuration */
+	wm->input_dev->name = "wm97xx touchscreen";
+	wm->input_dev->phys = "wm97xx";
+	wm->input_dev->open = wm97xx_ts_input_open;
+	wm->input_dev->close = wm97xx_ts_input_close;
+
+	__set_bit(EV_ABS, wm->input_dev->evbit);
+	__set_bit(EV_KEY, wm->input_dev->evbit);
+	__set_bit(BTN_TOUCH, wm->input_dev->keybit);
+
+	input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1],
+			     abs_x[2], 0);
+	input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1],
+			     abs_y[2], 0);
+	input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1],
+			     abs_p[2], 0);
+
+	input_set_drvdata(wm->input_dev, wm);
+	wm->input_dev->dev.parent = dev;
+
+	ret = input_register_device(wm->input_dev);
+	if (ret < 0)
+		goto dev_alloc_err;
+
+	/* register our battery device */
+	wm->battery_dev = platform_device_alloc("wm97xx-battery", -1);
+	if (!wm->battery_dev) {
+		ret = -ENOMEM;
+		goto batt_err;
+	}
+	platform_set_drvdata(wm->battery_dev, wm);
+	wm->battery_dev->dev.parent = dev;
+	wm->battery_dev->dev.platform_data = pdata;
+	ret = platform_device_add(wm->battery_dev);
+	if (ret < 0)
+		goto batt_reg_err;
+
+	/* register our extended touch device (for machine specific
+	 * extensions) */
+	wm->touch_dev = platform_device_alloc("wm97xx-touch", -1);
+	if (!wm->touch_dev) {
+		ret = -ENOMEM;
+		goto touch_err;
+	}
+	platform_set_drvdata(wm->touch_dev, wm);
+	wm->touch_dev->dev.parent = dev;
+	wm->touch_dev->dev.platform_data = pdata;
+	ret = platform_device_add(wm->touch_dev);
+	if (ret < 0)
+		goto touch_reg_err;
+
+	return ret;
+
+ touch_reg_err:
+	platform_device_put(wm->touch_dev);
+ touch_err:
+	platform_device_del(wm->battery_dev);
+ batt_reg_err:
+	platform_device_put(wm->battery_dev);
+ batt_err:
+	input_unregister_device(wm->input_dev);
+	wm->input_dev = NULL;
+ dev_alloc_err:
+	input_free_device(wm->input_dev);
+ alloc_err:
+	kfree(wm);
+
+	return ret;
+}
+
+static int wm97xx_remove(struct device *dev)
+{
+	struct wm97xx *wm = dev_get_drvdata(dev);
+
+	platform_device_unregister(wm->battery_dev);
+	platform_device_unregister(wm->touch_dev);
+	input_unregister_device(wm->input_dev);
+	kfree(wm);
+
+	return 0;
+}
+
+static int __maybe_unused wm97xx_suspend(struct device *dev)
+{
+	struct wm97xx *wm = dev_get_drvdata(dev);
+	u16 reg;
+	int suspend_mode;
+
+	if (device_may_wakeup(&wm->input_dev->dev))
+		suspend_mode = wm->suspend_mode;
+	else
+		suspend_mode = 0;
+
+	if (wm->input_dev->users)
+		cancel_delayed_work_sync(&wm->ts_reader);
+
+	/* Power down the digitiser (bypassing the cache for resume) */
+	reg = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER2);
+	reg &= ~WM97XX_PRP_DET_DIG;
+	if (wm->input_dev->users)
+		reg |= suspend_mode;
+	wm->ac97->bus->ops->write(wm->ac97, AC97_WM97XX_DIGITISER2, reg);
+
+	/* WM9713 has an additional power bit - turn it off if there
+	 * are no users or if suspend mode is zero. */
+	if (wm->id == WM9713_ID2 &&
+	    (!wm->input_dev->users || !suspend_mode)) {
+		reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) | 0x8000;
+		wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused wm97xx_resume(struct device *dev)
+{
+	struct wm97xx *wm = dev_get_drvdata(dev);
+
+	/* restore digitiser and gpios */
+	if (wm->id == WM9713_ID2) {
+		wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]);
+		wm97xx_reg_write(wm, 0x5a, wm->misc);
+		if (wm->input_dev->users) {
+			u16 reg;
+			reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff;
+			wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg);
+		}
+	}
+
+	wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]);
+	wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]);
+
+	wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]);
+	wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]);
+	wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]);
+	wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]);
+	wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]);
+	wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]);
+
+	if (wm->input_dev->users && !wm->pen_irq) {
+		wm->ts_reader_interval = wm->ts_reader_min_interval;
+		queue_delayed_work(wm->ts_workq, &wm->ts_reader,
+				   wm->ts_reader_interval);
+	}
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(wm97xx_pm_ops, wm97xx_suspend, wm97xx_resume);
+
+/*
+ * Machine specific operations
+ */
+int wm97xx_register_mach_ops(struct wm97xx *wm,
+			     struct wm97xx_mach_ops *mach_ops)
+{
+	mutex_lock(&wm->codec_mutex);
+	if (wm->mach_ops) {
+		mutex_unlock(&wm->codec_mutex);
+		return -EINVAL;
+	}
+	wm->mach_ops = mach_ops;
+	mutex_unlock(&wm->codec_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops);
+
+void wm97xx_unregister_mach_ops(struct wm97xx *wm)
+{
+	mutex_lock(&wm->codec_mutex);
+	wm->mach_ops = NULL;
+	mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops);
+
+static struct device_driver wm97xx_driver = {
+	.name =		"wm97xx-ts",
+	.bus =		&ac97_bus_type,
+	.owner =	THIS_MODULE,
+	.probe =	wm97xx_probe,
+	.remove =	wm97xx_remove,
+	.pm =		&wm97xx_pm_ops,
+};
+
+static int __init wm97xx_init(void)
+{
+	return driver_register(&wm97xx_driver);
+}
+
+static void __exit wm97xx_exit(void)
+{
+	driver_unregister(&wm97xx_driver);
+}
+
+module_init(wm97xx_init);
+module_exit(wm97xx_exit);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c
new file mode 100644
index 0000000..7b3845a
--- /dev/null
+++ b/drivers/input/touchscreen/zforce_ts.c
@@ -0,0 +1,964 @@
+/*
+ * Copyright (C) 2012-2013 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * based in parts on Nook zforce driver
+ *
+ * Copyright (C) 2010 Barnes & Noble, Inc.
+ * Author: Pieter Truter<ptruter@intrinsyc.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/input/mt.h>
+#include <linux/platform_data/zforce_ts.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
+
+#define WAIT_TIMEOUT		msecs_to_jiffies(1000)
+
+#define FRAME_START		0xee
+#define FRAME_MAXSIZE		257
+
+/* Offsets of the different parts of the payload the controller sends */
+#define PAYLOAD_HEADER		0
+#define PAYLOAD_LENGTH		1
+#define PAYLOAD_BODY		2
+
+/* Response offsets */
+#define RESPONSE_ID		0
+#define RESPONSE_DATA		1
+
+/* Commands */
+#define COMMAND_DEACTIVATE	0x00
+#define COMMAND_INITIALIZE	0x01
+#define COMMAND_RESOLUTION	0x02
+#define COMMAND_SETCONFIG	0x03
+#define COMMAND_DATAREQUEST	0x04
+#define COMMAND_SCANFREQ	0x08
+#define COMMAND_STATUS		0X1e
+
+/*
+ * Responses the controller sends as a result of
+ * command requests
+ */
+#define RESPONSE_DEACTIVATE	0x00
+#define RESPONSE_INITIALIZE	0x01
+#define RESPONSE_RESOLUTION	0x02
+#define RESPONSE_SETCONFIG	0x03
+#define RESPONSE_SCANFREQ	0x08
+#define RESPONSE_STATUS		0X1e
+
+/*
+ * Notifications are sent by the touch controller without
+ * being requested by the driver and include for example
+ * touch indications
+ */
+#define NOTIFICATION_TOUCH		0x04
+#define NOTIFICATION_BOOTCOMPLETE	0x07
+#define NOTIFICATION_OVERRUN		0x25
+#define NOTIFICATION_PROXIMITY		0x26
+#define NOTIFICATION_INVALID_COMMAND	0xfe
+
+#define ZFORCE_REPORT_POINTS		2
+#define ZFORCE_MAX_AREA			0xff
+
+#define STATE_DOWN			0
+#define STATE_MOVE			1
+#define STATE_UP			2
+
+#define SETCONFIG_DUALTOUCH		(1 << 0)
+
+struct zforce_point {
+	int coord_x;
+	int coord_y;
+	int state;
+	int id;
+	int area_major;
+	int area_minor;
+	int orientation;
+	int pressure;
+	int prblty;
+};
+
+/*
+ * @client		the i2c_client
+ * @input		the input device
+ * @suspending		in the process of going to suspend (don't emit wakeup
+ *			events for commands executed to suspend the device)
+ * @suspended		device suspended
+ * @access_mutex	serialize i2c-access, to keep multipart reads together
+ * @command_done	completion to wait for the command result
+ * @command_mutex	serialize commands sent to the ic
+ * @command_waiting	the id of the command that is currently waiting
+ *			for a result
+ * @command_result	returned result of the command
+ */
+struct zforce_ts {
+	struct i2c_client	*client;
+	struct input_dev	*input;
+	const struct zforce_ts_platdata *pdata;
+	char			phys[32];
+
+	struct regulator	*reg_vdd;
+
+	struct gpio_desc	*gpio_int;
+	struct gpio_desc	*gpio_rst;
+
+	bool			suspending;
+	bool			suspended;
+	bool			boot_complete;
+
+	/* Firmware version information */
+	u16			version_major;
+	u16			version_minor;
+	u16			version_build;
+	u16			version_rev;
+
+	struct mutex		access_mutex;
+
+	struct completion	command_done;
+	struct mutex		command_mutex;
+	int			command_waiting;
+	int			command_result;
+};
+
+static int zforce_command(struct zforce_ts *ts, u8 cmd)
+{
+	struct i2c_client *client = ts->client;
+	char buf[3];
+	int ret;
+
+	dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd);
+
+	buf[0] = FRAME_START;
+	buf[1] = 1; /* data size, command only */
+	buf[2] = cmd;
+
+	mutex_lock(&ts->access_mutex);
+	ret = i2c_master_send(client, &buf[0], ARRAY_SIZE(buf));
+	mutex_unlock(&ts->access_mutex);
+	if (ret < 0) {
+		dev_err(&client->dev, "i2c send data request error: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void zforce_reset_assert(struct zforce_ts *ts)
+{
+	gpiod_set_value_cansleep(ts->gpio_rst, 1);
+}
+
+static void zforce_reset_deassert(struct zforce_ts *ts)
+{
+	gpiod_set_value_cansleep(ts->gpio_rst, 0);
+}
+
+static int zforce_send_wait(struct zforce_ts *ts, const char *buf, int len)
+{
+	struct i2c_client *client = ts->client;
+	int ret;
+
+	ret = mutex_trylock(&ts->command_mutex);
+	if (!ret) {
+		dev_err(&client->dev, "already waiting for a command\n");
+		return -EBUSY;
+	}
+
+	dev_dbg(&client->dev, "sending %d bytes for command 0x%x\n",
+		buf[1], buf[2]);
+
+	ts->command_waiting = buf[2];
+
+	mutex_lock(&ts->access_mutex);
+	ret = i2c_master_send(client, buf, len);
+	mutex_unlock(&ts->access_mutex);
+	if (ret < 0) {
+		dev_err(&client->dev, "i2c send data request error: %d\n", ret);
+		goto unlock;
+	}
+
+	dev_dbg(&client->dev, "waiting for result for command 0x%x\n", buf[2]);
+
+	if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0) {
+		ret = -ETIME;
+		goto unlock;
+	}
+
+	ret = ts->command_result;
+
+unlock:
+	mutex_unlock(&ts->command_mutex);
+	return ret;
+}
+
+static int zforce_command_wait(struct zforce_ts *ts, u8 cmd)
+{
+	struct i2c_client *client = ts->client;
+	char buf[3];
+	int ret;
+
+	dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd);
+
+	buf[0] = FRAME_START;
+	buf[1] = 1; /* data size, command only */
+	buf[2] = cmd;
+
+	ret = zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+	if (ret < 0) {
+		dev_err(&client->dev, "i2c send data request error: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int zforce_resolution(struct zforce_ts *ts, u16 x, u16 y)
+{
+	struct i2c_client *client = ts->client;
+	char buf[7] = { FRAME_START, 5, COMMAND_RESOLUTION,
+			(x & 0xff), ((x >> 8) & 0xff),
+			(y & 0xff), ((y >> 8) & 0xff) };
+
+	dev_dbg(&client->dev, "set resolution to (%d,%d)\n", x, y);
+
+	return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+}
+
+static int zforce_scan_frequency(struct zforce_ts *ts, u16 idle, u16 finger,
+				 u16 stylus)
+{
+	struct i2c_client *client = ts->client;
+	char buf[9] = { FRAME_START, 7, COMMAND_SCANFREQ,
+			(idle & 0xff), ((idle >> 8) & 0xff),
+			(finger & 0xff), ((finger >> 8) & 0xff),
+			(stylus & 0xff), ((stylus >> 8) & 0xff) };
+
+	dev_dbg(&client->dev,
+		"set scan frequency to (idle: %d, finger: %d, stylus: %d)\n",
+		idle, finger, stylus);
+
+	return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+}
+
+static int zforce_setconfig(struct zforce_ts *ts, char b1)
+{
+	struct i2c_client *client = ts->client;
+	char buf[7] = { FRAME_START, 5, COMMAND_SETCONFIG,
+			b1, 0, 0, 0 };
+
+	dev_dbg(&client->dev, "set config to (%d)\n", b1);
+
+	return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
+}
+
+static int zforce_start(struct zforce_ts *ts)
+{
+	struct i2c_client *client = ts->client;
+	const struct zforce_ts_platdata *pdata = ts->pdata;
+	int ret;
+
+	dev_dbg(&client->dev, "starting device\n");
+
+	ret = zforce_command_wait(ts, COMMAND_INITIALIZE);
+	if (ret) {
+		dev_err(&client->dev, "Unable to initialize, %d\n", ret);
+		return ret;
+	}
+
+	ret = zforce_resolution(ts, pdata->x_max, pdata->y_max);
+	if (ret) {
+		dev_err(&client->dev, "Unable to set resolution, %d\n", ret);
+		goto error;
+	}
+
+	ret = zforce_scan_frequency(ts, 10, 50, 50);
+	if (ret) {
+		dev_err(&client->dev, "Unable to set scan frequency, %d\n",
+			ret);
+		goto error;
+	}
+
+	ret = zforce_setconfig(ts, SETCONFIG_DUALTOUCH);
+	if (ret) {
+		dev_err(&client->dev, "Unable to set config\n");
+		goto error;
+	}
+
+	/* start sending touch events */
+	ret = zforce_command(ts, COMMAND_DATAREQUEST);
+	if (ret) {
+		dev_err(&client->dev, "Unable to request data\n");
+		goto error;
+	}
+
+	/*
+	 * Per NN, initial cal. take max. of 200msec.
+	 * Allow time to complete this calibration
+	 */
+	msleep(200);
+
+	return 0;
+
+error:
+	zforce_command_wait(ts, COMMAND_DEACTIVATE);
+	return ret;
+}
+
+static int zforce_stop(struct zforce_ts *ts)
+{
+	struct i2c_client *client = ts->client;
+	int ret;
+
+	dev_dbg(&client->dev, "stopping device\n");
+
+	/* Deactivates touch sensing and puts the device into sleep. */
+	ret = zforce_command_wait(ts, COMMAND_DEACTIVATE);
+	if (ret != 0) {
+		dev_err(&client->dev, "could not deactivate device, %d\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int zforce_touch_event(struct zforce_ts *ts, u8 *payload)
+{
+	struct i2c_client *client = ts->client;
+	const struct zforce_ts_platdata *pdata = ts->pdata;
+	struct zforce_point point;
+	int count, i, num = 0;
+
+	count = payload[0];
+	if (count > ZFORCE_REPORT_POINTS) {
+		dev_warn(&client->dev,
+			 "too many coordinates %d, expected max %d\n",
+			 count, ZFORCE_REPORT_POINTS);
+		count = ZFORCE_REPORT_POINTS;
+	}
+
+	for (i = 0; i < count; i++) {
+		point.coord_x =
+			payload[9 * i + 2] << 8 | payload[9 * i + 1];
+		point.coord_y =
+			payload[9 * i + 4] << 8 | payload[9 * i + 3];
+
+		if (point.coord_x > pdata->x_max ||
+		    point.coord_y > pdata->y_max) {
+			dev_warn(&client->dev, "coordinates (%d,%d) invalid\n",
+				point.coord_x, point.coord_y);
+			point.coord_x = point.coord_y = 0;
+		}
+
+		point.state = payload[9 * i + 5] & 0x0f;
+		point.id = (payload[9 * i + 5] & 0xf0) >> 4;
+
+		/* determine touch major, minor and orientation */
+		point.area_major = max(payload[9 * i + 6],
+					  payload[9 * i + 7]);
+		point.area_minor = min(payload[9 * i + 6],
+					  payload[9 * i + 7]);
+		point.orientation = payload[9 * i + 6] > payload[9 * i + 7];
+
+		point.pressure = payload[9 * i + 8];
+		point.prblty = payload[9 * i + 9];
+
+		dev_dbg(&client->dev,
+			"point %d/%d: state %d, id %d, pressure %d, prblty %d, x %d, y %d, amajor %d, aminor %d, ori %d\n",
+			i, count, point.state, point.id,
+			point.pressure, point.prblty,
+			point.coord_x, point.coord_y,
+			point.area_major, point.area_minor,
+			point.orientation);
+
+		/* the zforce id starts with "1", so needs to be decreased */
+		input_mt_slot(ts->input, point.id - 1);
+
+		input_mt_report_slot_state(ts->input, MT_TOOL_FINGER,
+						point.state != STATE_UP);
+
+		if (point.state != STATE_UP) {
+			input_report_abs(ts->input, ABS_MT_POSITION_X,
+					 point.coord_x);
+			input_report_abs(ts->input, ABS_MT_POSITION_Y,
+					 point.coord_y);
+			input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+					 point.area_major);
+			input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+					 point.area_minor);
+			input_report_abs(ts->input, ABS_MT_ORIENTATION,
+					 point.orientation);
+			num++;
+		}
+	}
+
+	input_mt_sync_frame(ts->input);
+
+	input_mt_report_finger_count(ts->input, num);
+
+	input_sync(ts->input);
+
+	return 0;
+}
+
+static int zforce_read_packet(struct zforce_ts *ts, u8 *buf)
+{
+	struct i2c_client *client = ts->client;
+	int ret;
+
+	mutex_lock(&ts->access_mutex);
+
+	/* read 2 byte message header */
+	ret = i2c_master_recv(client, buf, 2);
+	if (ret < 0) {
+		dev_err(&client->dev, "error reading header: %d\n", ret);
+		goto unlock;
+	}
+
+	if (buf[PAYLOAD_HEADER] != FRAME_START) {
+		dev_err(&client->dev, "invalid frame start: %d\n", buf[0]);
+		ret = -EIO;
+		goto unlock;
+	}
+
+	if (buf[PAYLOAD_LENGTH] == 0) {
+		dev_err(&client->dev, "invalid payload length: %d\n",
+			buf[PAYLOAD_LENGTH]);
+		ret = -EIO;
+		goto unlock;
+	}
+
+	/* read the message */
+	ret = i2c_master_recv(client, &buf[PAYLOAD_BODY], buf[PAYLOAD_LENGTH]);
+	if (ret < 0) {
+		dev_err(&client->dev, "error reading payload: %d\n", ret);
+		goto unlock;
+	}
+
+	dev_dbg(&client->dev, "read %d bytes for response command 0x%x\n",
+		buf[PAYLOAD_LENGTH], buf[PAYLOAD_BODY]);
+
+unlock:
+	mutex_unlock(&ts->access_mutex);
+	return ret;
+}
+
+static void zforce_complete(struct zforce_ts *ts, int cmd, int result)
+{
+	struct i2c_client *client = ts->client;
+
+	if (ts->command_waiting == cmd) {
+		dev_dbg(&client->dev, "completing command 0x%x\n", cmd);
+		ts->command_result = result;
+		complete(&ts->command_done);
+	} else {
+		dev_dbg(&client->dev, "command %d not for us\n", cmd);
+	}
+}
+
+static irqreturn_t zforce_irq(int irq, void *dev_id)
+{
+	struct zforce_ts *ts = dev_id;
+	struct i2c_client *client = ts->client;
+
+	if (ts->suspended && device_may_wakeup(&client->dev))
+		pm_wakeup_event(&client->dev, 500);
+
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
+{
+	struct zforce_ts *ts = dev_id;
+	struct i2c_client *client = ts->client;
+	int ret;
+	u8 payload_buffer[FRAME_MAXSIZE];
+	u8 *payload;
+
+	/*
+	 * When still suspended, return.
+	 * Due to the level-interrupt we will get re-triggered later.
+	 */
+	if (ts->suspended) {
+		msleep(20);
+		return IRQ_HANDLED;
+	}
+
+	dev_dbg(&client->dev, "handling interrupt\n");
+
+	/* Don't emit wakeup events from commands run by zforce_suspend */
+	if (!ts->suspending && device_may_wakeup(&client->dev))
+		pm_stay_awake(&client->dev);
+
+	/*
+	 * Run at least once and exit the loop if
+	 * - the optional interrupt GPIO isn't specified
+	 *   (there is only one packet read per ISR invocation, then)
+	 * or
+	 * - the GPIO isn't active any more
+	 *   (packet read until the level GPIO indicates that there is
+	 *    no IRQ any more)
+	 */
+	do {
+		ret = zforce_read_packet(ts, payload_buffer);
+		if (ret < 0) {
+			dev_err(&client->dev,
+				"could not read packet, ret: %d\n", ret);
+			break;
+		}
+
+		payload =  &payload_buffer[PAYLOAD_BODY];
+
+		switch (payload[RESPONSE_ID]) {
+		case NOTIFICATION_TOUCH:
+			/*
+			 * Always report touch-events received while
+			 * suspending, when being a wakeup source
+			 */
+			if (ts->suspending && device_may_wakeup(&client->dev))
+				pm_wakeup_event(&client->dev, 500);
+			zforce_touch_event(ts, &payload[RESPONSE_DATA]);
+			break;
+
+		case NOTIFICATION_BOOTCOMPLETE:
+			ts->boot_complete = payload[RESPONSE_DATA];
+			zforce_complete(ts, payload[RESPONSE_ID], 0);
+			break;
+
+		case RESPONSE_INITIALIZE:
+		case RESPONSE_DEACTIVATE:
+		case RESPONSE_SETCONFIG:
+		case RESPONSE_RESOLUTION:
+		case RESPONSE_SCANFREQ:
+			zforce_complete(ts, payload[RESPONSE_ID],
+					payload[RESPONSE_DATA]);
+			break;
+
+		case RESPONSE_STATUS:
+			/*
+			 * Version Payload Results
+			 * [2:major] [2:minor] [2:build] [2:rev]
+			 */
+			ts->version_major = (payload[RESPONSE_DATA + 1] << 8) |
+						payload[RESPONSE_DATA];
+			ts->version_minor = (payload[RESPONSE_DATA + 3] << 8) |
+						payload[RESPONSE_DATA + 2];
+			ts->version_build = (payload[RESPONSE_DATA + 5] << 8) |
+						payload[RESPONSE_DATA + 4];
+			ts->version_rev   = (payload[RESPONSE_DATA + 7] << 8) |
+						payload[RESPONSE_DATA + 6];
+			dev_dbg(&ts->client->dev,
+				"Firmware Version %04x:%04x %04x:%04x\n",
+				ts->version_major, ts->version_minor,
+				ts->version_build, ts->version_rev);
+
+			zforce_complete(ts, payload[RESPONSE_ID], 0);
+			break;
+
+		case NOTIFICATION_INVALID_COMMAND:
+			dev_err(&ts->client->dev, "invalid command: 0x%x\n",
+				payload[RESPONSE_DATA]);
+			break;
+
+		default:
+			dev_err(&ts->client->dev,
+				"unrecognized response id: 0x%x\n",
+				payload[RESPONSE_ID]);
+			break;
+		}
+	} while (gpiod_get_value_cansleep(ts->gpio_int));
+
+	if (!ts->suspending && device_may_wakeup(&client->dev))
+		pm_relax(&client->dev);
+
+	dev_dbg(&client->dev, "finished interrupt\n");
+
+	return IRQ_HANDLED;
+}
+
+static int zforce_input_open(struct input_dev *dev)
+{
+	struct zforce_ts *ts = input_get_drvdata(dev);
+
+	return zforce_start(ts);
+}
+
+static void zforce_input_close(struct input_dev *dev)
+{
+	struct zforce_ts *ts = input_get_drvdata(dev);
+	struct i2c_client *client = ts->client;
+	int ret;
+
+	ret = zforce_stop(ts);
+	if (ret)
+		dev_warn(&client->dev, "stopping zforce failed\n");
+
+	return;
+}
+
+static int __maybe_unused zforce_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct zforce_ts *ts = i2c_get_clientdata(client);
+	struct input_dev *input = ts->input;
+	int ret = 0;
+
+	mutex_lock(&input->mutex);
+	ts->suspending = true;
+
+	/*
+	 * When configured as a wakeup source device should always wake
+	 * the system, therefore start device if necessary.
+	 */
+	if (device_may_wakeup(&client->dev)) {
+		dev_dbg(&client->dev, "suspend while being a wakeup source\n");
+
+		/* Need to start device, if not open, to be a wakeup source. */
+		if (!input->users) {
+			ret = zforce_start(ts);
+			if (ret)
+				goto unlock;
+		}
+
+		enable_irq_wake(client->irq);
+	} else if (input->users) {
+		dev_dbg(&client->dev,
+			"suspend without being a wakeup source\n");
+
+		ret = zforce_stop(ts);
+		if (ret)
+			goto unlock;
+
+		disable_irq(client->irq);
+	}
+
+	ts->suspended = true;
+
+unlock:
+	ts->suspending = false;
+	mutex_unlock(&input->mutex);
+
+	return ret;
+}
+
+static int __maybe_unused zforce_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct zforce_ts *ts = i2c_get_clientdata(client);
+	struct input_dev *input = ts->input;
+	int ret = 0;
+
+	mutex_lock(&input->mutex);
+
+	ts->suspended = false;
+
+	if (device_may_wakeup(&client->dev)) {
+		dev_dbg(&client->dev, "resume from being a wakeup source\n");
+
+		disable_irq_wake(client->irq);
+
+		/* need to stop device if it was not open on suspend */
+		if (!input->users) {
+			ret = zforce_stop(ts);
+			if (ret)
+				goto unlock;
+		}
+	} else if (input->users) {
+		dev_dbg(&client->dev, "resume without being a wakeup source\n");
+
+		enable_irq(client->irq);
+
+		ret = zforce_start(ts);
+		if (ret < 0)
+			goto unlock;
+	}
+
+unlock:
+	mutex_unlock(&input->mutex);
+
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(zforce_pm_ops, zforce_suspend, zforce_resume);
+
+static void zforce_reset(void *data)
+{
+	struct zforce_ts *ts = data;
+
+	zforce_reset_assert(ts);
+
+	udelay(10);
+
+	if (!IS_ERR(ts->reg_vdd))
+		regulator_disable(ts->reg_vdd);
+}
+
+static struct zforce_ts_platdata *zforce_parse_dt(struct device *dev)
+{
+	struct zforce_ts_platdata *pdata;
+	struct device_node *np = dev->of_node;
+
+	if (!np)
+		return ERR_PTR(-ENOENT);
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		dev_err(dev, "failed to allocate platform data\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	if (of_property_read_u32(np, "x-size", &pdata->x_max)) {
+		dev_err(dev, "failed to get x-size property\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (of_property_read_u32(np, "y-size", &pdata->y_max)) {
+		dev_err(dev, "failed to get y-size property\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	return pdata;
+}
+
+static int zforce_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
+	struct zforce_ts *ts;
+	struct input_dev *input_dev;
+	int ret;
+
+	if (!pdata) {
+		pdata = zforce_parse_dt(&client->dev);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+	}
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct zforce_ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	ts->gpio_rst = devm_gpiod_get_optional(&client->dev, "reset",
+					       GPIOD_OUT_HIGH);
+	if (IS_ERR(ts->gpio_rst)) {
+		ret = PTR_ERR(ts->gpio_rst);
+		dev_err(&client->dev,
+			"failed to request reset GPIO: %d\n", ret);
+		return ret;
+	}
+
+	if (ts->gpio_rst) {
+		ts->gpio_int = devm_gpiod_get_optional(&client->dev, "irq",
+						       GPIOD_IN);
+		if (IS_ERR(ts->gpio_int)) {
+			ret = PTR_ERR(ts->gpio_int);
+			dev_err(&client->dev,
+				"failed to request interrupt GPIO: %d\n", ret);
+			return ret;
+		}
+	} else {
+		/*
+		 * Deprecated GPIO handling for compatibility
+		 * with legacy binding.
+		 */
+
+		/* INT GPIO */
+		ts->gpio_int = devm_gpiod_get_index(&client->dev, NULL, 0,
+						    GPIOD_IN);
+		if (IS_ERR(ts->gpio_int)) {
+			ret = PTR_ERR(ts->gpio_int);
+			dev_err(&client->dev,
+				"failed to request interrupt GPIO: %d\n", ret);
+			return ret;
+		}
+
+		/* RST GPIO */
+		ts->gpio_rst = devm_gpiod_get_index(&client->dev, NULL, 1,
+					    GPIOD_OUT_HIGH);
+		if (IS_ERR(ts->gpio_rst)) {
+			ret = PTR_ERR(ts->gpio_rst);
+			dev_err(&client->dev,
+				"failed to request reset GPIO: %d\n", ret);
+			return ret;
+		}
+	}
+
+	ts->reg_vdd = devm_regulator_get_optional(&client->dev, "vdd");
+	if (IS_ERR(ts->reg_vdd)) {
+		ret = PTR_ERR(ts->reg_vdd);
+		if (ret == -EPROBE_DEFER)
+			return ret;
+	} else {
+		ret = regulator_enable(ts->reg_vdd);
+		if (ret)
+			return ret;
+
+		/*
+		 * according to datasheet add 100us grace time after regular
+		 * regulator enable delay.
+		 */
+		udelay(100);
+	}
+
+	ret = devm_add_action(&client->dev, zforce_reset, ts);
+	if (ret) {
+		dev_err(&client->dev, "failed to register reset action, %d\n",
+			ret);
+
+		/* hereafter the regulator will be disabled by the action */
+		if (!IS_ERR(ts->reg_vdd))
+			regulator_disable(ts->reg_vdd);
+
+		return ret;
+	}
+
+	snprintf(ts->phys, sizeof(ts->phys),
+		 "%s/input0", dev_name(&client->dev));
+
+	input_dev = devm_input_allocate_device(&client->dev);
+	if (!input_dev) {
+		dev_err(&client->dev, "could not allocate input device\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&ts->access_mutex);
+	mutex_init(&ts->command_mutex);
+
+	ts->pdata = pdata;
+	ts->client = client;
+	ts->input = input_dev;
+
+	input_dev->name = "Neonode zForce touchscreen";
+	input_dev->phys = ts->phys;
+	input_dev->id.bustype = BUS_I2C;
+
+	input_dev->open = zforce_input_open;
+	input_dev->close = zforce_input_close;
+
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(EV_SYN, input_dev->evbit);
+	__set_bit(EV_ABS, input_dev->evbit);
+
+	/* For multi touch */
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+			     pdata->x_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+			     pdata->y_max, 0, 0);
+
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
+			     ZFORCE_MAX_AREA, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0,
+			     ZFORCE_MAX_AREA, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+	input_mt_init_slots(input_dev, ZFORCE_REPORT_POINTS, INPUT_MT_DIRECT);
+
+	input_set_drvdata(ts->input, ts);
+
+	init_completion(&ts->command_done);
+
+	/*
+	 * The zforce pulls the interrupt low when it has data ready.
+	 * After it is triggered the isr thread runs until all the available
+	 * packets have been read and the interrupt is high again.
+	 * Therefore we can trigger the interrupt anytime it is low and do
+	 * not need to limit it to the interrupt edge.
+	 */
+	ret = devm_request_threaded_irq(&client->dev, client->irq,
+					zforce_irq, zforce_irq_thread,
+					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					input_dev->name, ts);
+	if (ret) {
+		dev_err(&client->dev, "irq %d request failed\n", client->irq);
+		return ret;
+	}
+
+	i2c_set_clientdata(client, ts);
+
+	/* let the controller boot */
+	zforce_reset_deassert(ts);
+
+	ts->command_waiting = NOTIFICATION_BOOTCOMPLETE;
+	if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0)
+		dev_warn(&client->dev, "bootcomplete timed out\n");
+
+	/* need to start device to get version information */
+	ret = zforce_command_wait(ts, COMMAND_INITIALIZE);
+	if (ret) {
+		dev_err(&client->dev, "unable to initialize, %d\n", ret);
+		return ret;
+	}
+
+	/* this gets the firmware version among other information */
+	ret = zforce_command_wait(ts, COMMAND_STATUS);
+	if (ret < 0) {
+		dev_err(&client->dev, "couldn't get status, %d\n", ret);
+		zforce_stop(ts);
+		return ret;
+	}
+
+	/* stop device and put it into sleep until it is opened */
+	ret = zforce_stop(ts);
+	if (ret < 0)
+		return ret;
+
+	device_set_wakeup_capable(&client->dev, true);
+
+	ret = input_register_device(input_dev);
+	if (ret) {
+		dev_err(&client->dev, "could not register input device, %d\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct i2c_device_id zforce_idtable[] = {
+	{ "zforce-ts", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, zforce_idtable);
+
+#ifdef CONFIG_OF
+static const struct of_device_id zforce_dt_idtable[] = {
+	{ .compatible = "neonode,zforce" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, zforce_dt_idtable);
+#endif
+
+static struct i2c_driver zforce_driver = {
+	.driver = {
+		.name	= "zforce-ts",
+		.pm	= &zforce_pm_ops,
+		.of_match_table	= of_match_ptr(zforce_dt_idtable),
+	},
+	.probe		= zforce_probe,
+	.id_table	= zforce_idtable,
+};
+
+module_i2c_driver(zforce_driver);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("zForce TouchScreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/zylonite-wm97xx.c b/drivers/input/touchscreen/zylonite-wm97xx.c
new file mode 100644
index 0000000..e2ccd68
--- /dev/null
+++ b/drivers/input/touchscreen/zylonite-wm97xx.c
@@ -0,0 +1,231 @@
+/*
+ * zylonite-wm97xx.c  --  Zylonite Continuous Touch screen driver
+ *
+ * Copyright 2004, 2007, 2008 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *
+ *  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.
+ *
+ * Notes:
+ *     This is a wm97xx extended touch driver supporting interrupt driven
+ *     and continuous operation on Marvell Zylonite development systems
+ *     (which have a WM9713 on board).
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/wm97xx.h>
+
+#include <mach/hardware.h>
+#include <mach/mfp.h>
+#include <mach/regs-ac97.h>
+
+struct continuous {
+	u16 id;    /* codec id */
+	u8 code;   /* continuous code */
+	u8 reads;  /* number of coord reads per read cycle */
+	u32 speed; /* number of coords per second */
+};
+
+#define WM_READS(sp) ((sp / HZ) + 1)
+
+static const struct continuous cinfo[] = {
+	{ WM9713_ID2, 0, WM_READS(94),  94  },
+	{ WM9713_ID2, 1, WM_READS(120), 120 },
+	{ WM9713_ID2, 2, WM_READS(154), 154 },
+	{ WM9713_ID2, 3, WM_READS(188), 188 },
+};
+
+/* continuous speed index */
+static int sp_idx;
+
+/*
+ * Pen sampling frequency (Hz) in continuous mode.
+ */
+static int cont_rate = 200;
+module_param(cont_rate, int, 0);
+MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
+
+/*
+ * Pressure readback.
+ *
+ * Set to 1 to read back pen down pressure
+ */
+static int pressure;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
+
+/*
+ * AC97 touch data slot.
+ *
+ * Touch screen readback data ac97 slot
+ */
+static int ac97_touch_slot = 5;
+module_param(ac97_touch_slot, int, 0);
+MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
+
+
+/* flush AC97 slot 5 FIFO machines */
+static void wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+	int i;
+
+	msleep(1);
+
+	for (i = 0; i < 16; i++)
+		MODR;
+}
+
+static int wm97xx_acc_pen_down(struct wm97xx *wm)
+{
+	u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES;
+	int reads = 0;
+	static u16 last, tries;
+
+	/* When the AC97 queue has been drained we need to allow time
+	 * to buffer up samples otherwise we end up spinning polling
+	 * for samples.  The controller can't have a suitably low
+	 * threshold set to use the notifications it gives.
+	 */
+	msleep(1);
+
+	if (tries > 5) {
+		tries = 0;
+		return RC_PENUP;
+	}
+
+	x = MODR;
+	if (x == last) {
+		tries++;
+		return RC_AGAIN;
+	}
+	last = x;
+	do {
+		if (reads)
+			x = MODR;
+		y = MODR;
+		if (pressure)
+			p = MODR;
+
+		dev_dbg(wm->dev, "Raw coordinates: x=%x, y=%x, p=%x\n",
+			x, y, p);
+
+		/* are samples valid */
+		if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X ||
+		    (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y ||
+		    (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES)
+			goto up;
+
+		/* coordinate is good */
+		tries = 0;
+		input_report_abs(wm->input_dev, ABS_X, x & 0xfff);
+		input_report_abs(wm->input_dev, ABS_Y, y & 0xfff);
+		input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff);
+		input_report_key(wm->input_dev, BTN_TOUCH, (p != 0));
+		input_sync(wm->input_dev);
+		reads++;
+	} while (reads < cinfo[sp_idx].reads);
+up:
+	return RC_PENDOWN | RC_AGAIN;
+}
+
+static int wm97xx_acc_startup(struct wm97xx *wm)
+{
+	int idx;
+
+	/* check we have a codec */
+	if (wm->ac97 == NULL)
+		return -ENODEV;
+
+	/* Go you big red fire engine */
+	for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
+		if (wm->id != cinfo[idx].id)
+			continue;
+		sp_idx = idx;
+		if (cont_rate <= cinfo[idx].speed)
+			break;
+	}
+	wm->acc_rate = cinfo[sp_idx].code;
+	wm->acc_slot = ac97_touch_slot;
+	dev_info(wm->dev,
+		 "zylonite accelerated touchscreen driver, %d samples/sec\n",
+		 cinfo[sp_idx].speed);
+
+	return 0;
+}
+
+static void wm97xx_irq_enable(struct wm97xx *wm, int enable)
+{
+	if (enable)
+		enable_irq(wm->pen_irq);
+	else
+		disable_irq_nosync(wm->pen_irq);
+}
+
+static struct wm97xx_mach_ops zylonite_mach_ops = {
+	.acc_enabled	= 1,
+	.acc_pen_up	= wm97xx_acc_pen_up,
+	.acc_pen_down	= wm97xx_acc_pen_down,
+	.acc_startup	= wm97xx_acc_startup,
+	.irq_enable	= wm97xx_irq_enable,
+	.irq_gpio	= WM97XX_GPIO_2,
+};
+
+static int zylonite_wm97xx_probe(struct platform_device *pdev)
+{
+	struct wm97xx *wm = platform_get_drvdata(pdev);
+	int gpio_touch_irq;
+
+	if (cpu_is_pxa320())
+		gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO15);
+	else
+		gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO26);
+
+	wm->pen_irq = gpio_to_irq(gpio_touch_irq);
+	irq_set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH);
+
+	wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
+			   WM97XX_GPIO_POL_HIGH,
+			   WM97XX_GPIO_STICKY,
+			   WM97XX_GPIO_WAKE);
+	wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT,
+			   WM97XX_GPIO_POL_HIGH,
+			   WM97XX_GPIO_NOTSTICKY,
+			   WM97XX_GPIO_NOWAKE);
+
+	return wm97xx_register_mach_ops(wm, &zylonite_mach_ops);
+}
+
+static int zylonite_wm97xx_remove(struct platform_device *pdev)
+{
+	struct wm97xx *wm = platform_get_drvdata(pdev);
+
+	wm97xx_unregister_mach_ops(wm);
+
+	return 0;
+}
+
+static struct platform_driver zylonite_wm97xx_driver = {
+	.probe	= zylonite_wm97xx_probe,
+	.remove	= zylonite_wm97xx_remove,
+	.driver	= {
+		.name	= "wm97xx-touch",
+	},
+};
+module_platform_driver(zylonite_wm97xx_driver);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("wm97xx continuous touch driver for Zylonite");
+MODULE_LICENSE("GPL");