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