diff --git a/drivers/input/rmi4/rmi_f17.c b/drivers/input/rmi4/rmi_f17.c
new file mode 100644
index 0000000..006a716
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f17.c
@@ -0,0 +1,713 @@
+/*
+ * Copyright (c) 2012 Synaptics Incorporated
+ *
+ * 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/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+#define QUERY_BASE_INDEX 1
+#define MAX_NAME_LENGTH 256
+
+union f17_device_query {
+	struct {
+		u8 number_of_sticks:3;
+	};
+	u8 regs[1];
+};
+
+#define F17_MANUFACTURER_SYNAPTICS 0
+#define F17_MANUFACTURER_NMB 1
+#define F17_MANUFACTURER_ALPS 2
+
+struct f17_stick_query {
+	union {
+		struct {
+			u8 manufacturer:4;
+			u8 resistive:1;
+			u8 ballistics:1;
+			u8 reserved1:2;
+			u8 has_relative:1;
+			u8 has_absolute:1;
+			u8 has_gestures:1;
+			u8 has_dribble:1;
+			u8 reserved2:4;
+		};
+		u8 regs[2];
+	} general;
+
+	union {
+		struct {
+			u8 has_single_tap:1;
+			u8 has_tap_and_hold:1;
+			u8 has_double_tap:1;
+			u8 has_early_tap:1;
+			u8 has_press:1;
+		};
+		u8 regs[1];
+	} gestures;
+};
+
+union f17_device_controls {
+	struct {
+		u8 reporting_mode:3;
+		u8 dribble:1;
+	};
+	u8 regs[1];
+};
+
+struct f17_stick_controls {
+	union {
+		struct {
+			u8 z_force_threshold;
+			u8 radial_force_threshold;
+		};
+		u8 regs[3];
+	} general;
+
+	union {
+		struct {
+			u8 motion_sensitivity:4;
+			u8 antijitter:1;
+		};
+		u8 regs[1];
+	} relative;
+
+	union {
+		struct {
+			u8 single_tap:1;
+			u8 tap_and_hold:1;
+
+			u8 double_tap:1;
+			u8 early_tap:1;
+			u8 press:1;
+		};
+		u8 regs[1];
+	} enable;
+
+	u8 maximum_tap_time;
+	u8 minimum_press_time;
+	u8 maximum_radial_force;
+};
+
+
+union f17_device_commands {
+	struct {
+		u8 rezero:1;
+	};
+	u8 regs[1];
+};
+
+struct f17_stick_data {
+	union {
+		struct {
+			u8 x_force_high:8;
+			u8 y_force_high:8;
+			u8 y_force_low:4;
+			u8 x_force_low:4;
+			u8 z_force:8;
+		} __attribute__((__packed__));
+		struct {
+			u8 regs[4];
+			u16 address;
+		};
+	} abs;
+	union {
+		struct {
+			s8 x_delta:8;
+			s8 y_delta:8;
+		} __attribute__((__packed__));
+		struct {
+			u8 regs[2];
+			u16 address;
+		};
+	} rel;
+	union {
+		struct {
+			u8 single_tap:1;
+			u8 tap_and_hold:1;
+			u8 double_tap:1;
+			u8 early_tap:1;
+			u8 press:1;
+			u8 reserved:3;
+		} __attribute__((__packed__));
+		struct {
+			u8 regs[1];
+			u16 address;
+		};
+	} gestures;
+};
+
+
+/* data specific to f17 that needs to be kept around */
+
+struct rmi_f17_stick_data {
+	struct f17_stick_query query;
+	struct f17_stick_controls controls;
+	struct f17_stick_data data;
+
+	u16 control_address;
+
+	int index;
+
+	char input_name[MAX_NAME_LENGTH];
+	char input_phys[MAX_NAME_LENGTH];
+	struct input_dev *input;
+	char mouse_name[MAX_NAME_LENGTH];
+	char mouse_phys[MAX_NAME_LENGTH];
+	struct input_dev *mouse;
+};
+
+struct rmi_f17_device_data {
+	u16 control_address;
+
+	union f17_device_query query;
+	union f17_device_commands commands;
+	union f17_device_controls controls;
+
+	struct rmi_f17_stick_data *sticks;
+
+};
+
+static ssize_t f17_rezero_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+static ssize_t f17_rezero_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+
+
+
+static int f17_alloc_memory(struct rmi_function_container *fc);
+
+static void f17_free_memory(struct rmi_function_container *fc);
+
+static int f17_initialize(struct rmi_function_container *fc);
+
+static int f17_register_devices(struct rmi_function_container *fc);
+
+static int f17_create_sysfs(struct rmi_function_container *fc);
+
+static int f17_config(struct rmi_function_container *fc);
+
+
+static struct device_attribute attrs[] = {
+	__ATTR(rezero, RMI_RW_ATTR,
+		f17_rezero_show, f17_rezero_store),
+};
+
+
+int f17_read_control_parameters(struct rmi_device *rmi_dev,
+	struct rmi_f17_device_data *f17)
+{
+	int retval = 0;
+
+	/* TODO: read this or delete the function */
+
+	return retval;
+}
+
+
+static int f17_init(struct rmi_function_container *fc)
+{
+	int retval;
+
+	retval = f17_alloc_memory(fc);
+	if (retval < 0)
+		goto err_free_data;
+
+	retval = f17_initialize(fc);
+	if (retval < 0)
+		goto err_free_data;
+
+	retval = f17_register_devices(fc);
+	if (retval < 0)
+		goto err_free_data;
+
+	retval = f17_create_sysfs(fc);
+	if (retval < 0)
+		goto err_free_data;
+
+	return 0;
+
+err_free_data:
+	f17_free_memory(fc);
+
+	return retval;
+}
+
+static int f17_alloc_memory(struct rmi_function_container *fc)
+{
+	struct rmi_f17_device_data *f17;
+	int retval;
+
+	f17 = kzalloc(sizeof(struct rmi_f17_device_data), GFP_KERNEL);
+	if (!f17) {
+		dev_err(&fc->dev, "Failed to allocate function data.\n");
+		return -ENOMEM;
+	}
+	fc->data = f17;
+
+	retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+				f17->query.regs, sizeof(f17->query.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read query register.\n");
+		goto error_exit;
+	}
+
+	f17->sticks = kcalloc(f17->query.number_of_sticks + 1,
+			sizeof(struct rmi_f17_stick_data), GFP_KERNEL);
+	if (!f17->sticks) {
+		dev_err(&fc->dev, "Failed to allocate per stick data.\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+
+error_exit:
+	kfree(f17);
+	fc->data = NULL;
+	return retval;
+}
+
+static void f17_free_memory(struct rmi_function_container *fc)
+{
+	struct rmi_f17_device_data *f17 = fc->data;
+
+	if (f17) {
+		kfree(f17->sticks);
+		f17->sticks = NULL;
+	}
+	kfree(f17);
+	fc->data = NULL;
+}
+
+static int f17_init_stick(struct rmi_device *rmi_dev,
+			  struct rmi_f17_stick_data *stick,
+			  u16 *next_query_reg, u16 *next_data_reg,
+			  u16 *next_control_reg) {
+	int retval = 0;
+
+	retval = rmi_read_block(rmi_dev, *next_query_reg,
+		stick->query.general.regs,
+		sizeof(stick->query.general.regs));
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev, "Failed to read stick general query.\n");
+		return retval;
+	}
+	*next_query_reg += sizeof(stick->query.general.regs);
+
+	dev_dbg(&rmi_dev->dev, "Stick %d found\n", stick->index);
+	dev_dbg(&rmi_dev->dev, "    Manufacturer: %d.\n",
+				stick->query.general.manufacturer);
+	dev_dbg(&rmi_dev->dev, "    Resistive:    %d.\n",
+				stick->query.general.resistive);
+	dev_dbg(&rmi_dev->dev, "    Ballistics:   %d.\n",
+				stick->query.general.ballistics);
+	dev_dbg(&rmi_dev->dev, "    Manufacturer: %d.\n",
+				stick->query.general.ballistics);
+	dev_dbg(&rmi_dev->dev, "    Has relative: %d.\n",
+				stick->query.general.has_relative);
+	dev_dbg(&rmi_dev->dev, "    Has absolute: %d.\n",
+				stick->query.general.has_absolute);
+	dev_dbg(&rmi_dev->dev, "    Had dribble:  %d.\n",
+				stick->query.general.has_dribble);
+	dev_dbg(&rmi_dev->dev, "    Has gestures: %d.\n",
+				stick->query.general.has_gestures);
+
+	if (stick->query.general.has_gestures) {
+		retval = rmi_read_block(rmi_dev, *next_query_reg,
+			stick->query.gestures.regs,
+			sizeof(stick->query.gestures.regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read F17 gestures query, code %d.\n",
+				retval);
+			return retval;
+		}
+		*next_query_reg += sizeof(stick->query.gestures.regs);
+		dev_dbg(&rmi_dev->dev, "        single tap: %d.\n",
+					stick->query.gestures.has_single_tap);
+		dev_dbg(&rmi_dev->dev, "        tap & hold: %d.\n",
+					stick->query.gestures.has_tap_and_hold);
+		dev_dbg(&rmi_dev->dev, "        double tap: %d.\n",
+					stick->query.gestures.has_double_tap);
+		dev_dbg(&rmi_dev->dev, "        early tap:  %d.\n",
+					stick->query.gestures.has_early_tap);
+		dev_dbg(&rmi_dev->dev, "        press:      %d.\n",
+					stick->query.gestures.has_press);
+	}
+	if (stick->query.general.has_absolute) {
+		stick->data.abs.address = *next_data_reg;
+		*next_data_reg += sizeof(stick->data.abs.regs);
+	}
+	if (stick->query.general.has_relative) {
+		stick->data.rel.address = *next_data_reg;
+		*next_data_reg += sizeof(stick->data.rel.regs);
+	}
+	if (stick->query.general.has_gestures) {
+		stick->data.gestures.address = *next_data_reg;
+		*next_data_reg += sizeof(stick->data.gestures.regs);
+	}
+
+	return retval;
+}
+
+static int f17_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_f17_device_data *f17 = fc->data;
+	int i;
+	int retval;
+	u16 next_query_reg = fc->fd.query_base_addr;
+	u16 next_data_reg = fc->fd.data_base_addr;
+	u16 next_control_reg = fc->fd.control_base_addr;
+
+	retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+				f17->query.regs, sizeof(f17->query.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read query register.\n");
+		return retval;
+	}
+	dev_info(&fc->dev, "Found F17 with %d sticks.\n",
+		 f17->query.number_of_sticks + 1);
+	next_query_reg += sizeof(f17->query.regs);
+
+	retval = rmi_read_block(rmi_dev, fc->fd.command_base_addr,
+		f17->commands.regs, sizeof(f17->commands.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read command register.\n");
+		return retval;
+	}
+
+	f17->control_address = fc->fd.control_base_addr;
+	retval = f17_read_control_parameters(rmi_dev, f17);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to initialize F17 control params.\n");
+		return retval;
+	}
+
+	for (i = 0; i < f17->query.number_of_sticks + 1; i++) {
+		f17->sticks[i].index = i;
+		retval = f17_init_stick(rmi_dev, &f17->sticks[i],
+					&next_query_reg, &next_data_reg,
+					&next_control_reg);
+		if (!retval) {
+			dev_err(&fc->dev, "Failed to init stick %d.\n", i);
+			return retval;
+		}
+	}
+
+	return retval;
+}
+
+static int f17_register_stick(struct rmi_function_container *fc,
+			      struct rmi_f17_stick_data *stick) {
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	int retval = 0;
+
+	if (stick->query.general.has_absolute) {
+		struct input_dev *input_dev;
+		input_dev = input_allocate_device();
+		if (!input_dev) {
+			dev_err(&rmi_dev->dev, "Failed to allocate stick device %d.\n",
+				stick->index);
+			return -ENOMEM;
+		}
+
+		snprintf(stick->input_name, sizeof(stick->input_name),
+			"RMI F%02x Stick %d", 0x17, stick->index);
+		snprintf(stick->input_phys, sizeof(stick->input_phys),
+			 "sensor00fn%02x/stick%d", 0x17, stick->index);
+		input_dev->name = stick->input_name;
+		input_dev->phys = stick->input_phys;
+		input_dev->dev.parent = &fc->dev;
+		input_set_drvdata(input_dev, stick);
+
+		retval = input_register_device(input_dev);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Failed to register stick device %d.\n",
+				stick->index);
+			goto error_free_device;
+		}
+		stick->input = input_dev;
+	}
+
+	if (stick->query.general.has_relative) {
+		struct input_dev *input_dev_mouse;
+		/*create input device for mouse events  */
+		input_dev_mouse = input_allocate_device();
+		if (!input_dev_mouse) {
+			retval = -ENOMEM;
+			goto error_free_device;
+		}
+
+		snprintf(stick->mouse_name, sizeof(stick->mouse_name),
+			"RMI F%02x Mouse %d", 0x17, stick->index);
+		snprintf(stick->mouse_phys, sizeof(stick->mouse_name),
+			 "sensor00fn%02x/mouse%d", 0x17, stick->index);
+		input_dev_mouse->name = stick->mouse_name;
+		input_dev_mouse->phys = stick->mouse_phys;
+		input_dev_mouse->dev.parent = &fc->dev;
+
+		input_dev_mouse->id.vendor  = 0x18d1;
+		input_dev_mouse->id.product = 0x0210;
+		input_dev_mouse->id.version = 0x0100;
+
+		set_bit(EV_REL, input_dev_mouse->evbit);
+		set_bit(REL_X, input_dev_mouse->relbit);
+		set_bit(REL_Y, input_dev_mouse->relbit);
+
+		set_bit(BTN_MOUSE, input_dev_mouse->evbit);
+		/* Register device's buttons and keys */
+		set_bit(EV_KEY, input_dev_mouse->evbit);
+		set_bit(BTN_LEFT, input_dev_mouse->keybit);
+		set_bit(BTN_MIDDLE, input_dev_mouse->keybit);
+		set_bit(BTN_RIGHT, input_dev_mouse->keybit);
+
+		retval = input_register_device(input_dev_mouse);
+		if (retval < 0)
+			goto error_free_device;
+		stick->mouse = input_dev_mouse;
+	}
+
+	return 0;
+
+error_free_device:
+	if (stick->input) {
+		input_free_device(stick->input);
+		stick->input = NULL;
+	}
+	if (stick->mouse) {
+		input_free_device(stick->mouse);
+		stick->mouse = NULL;
+	}
+	return retval;
+}
+
+static int f17_register_devices(struct rmi_function_container *fc)
+{
+	struct rmi_f17_device_data *f17 = fc->data;
+	int i;
+	int retval = 0;
+
+	for (i = 0; i < f17->query.number_of_sticks + 1 && !retval; i++)
+		retval = f17_register_stick(fc, &f17->sticks[i]);
+
+	return retval;
+}
+
+static int f17_create_sysfs(struct rmi_function_container *fc)
+{
+	int attr_count = 0;
+	int rc;
+
+	dev_dbg(&fc->dev, "Creating sysfs files.\n");
+	/* Set up sysfs device attributes. */
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+		if (sysfs_create_file
+		    (&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
+			dev_err(&fc->dev,
+				"Failed to create sysfs file for %s.",
+				attrs[attr_count].attr.name);
+			rc = -ENODEV;
+			goto err_remove_sysfs;
+		}
+	}
+
+	return 0;
+
+err_remove_sysfs:
+	for (attr_count--; attr_count >= 0; attr_count--)
+		sysfs_remove_file(&fc->dev.kobj, &attrs[attr_count].attr);
+	return rc;
+
+}
+
+static int f17_config(struct rmi_function_container *fc)
+{
+	struct rmi_f17_device_data *f17 = fc->data;
+	int retval;
+
+	retval = rmi_write_block(fc->rmi_dev, f17->control_address,
+		f17->controls.regs, sizeof(f17->controls.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not write stick controls to 0x%04x\n",
+				f17->control_address);
+		return retval;
+	}
+
+	return retval;
+}
+
+static void f17_remove(struct rmi_function_container *fc)
+{
+	struct rmi_f17_device_data *f17 = fc->data;
+	int i = 0;
+
+	for (i = 0; i < ARRAY_SIZE(attrs); i++)
+		sysfs_remove_file(&fc->dev.kobj, &attrs[i].attr);
+
+	for (i = 0; i < f17->query.number_of_sticks + 1; i++)
+		input_unregister_device(f17->sticks[i].input);
+
+	f17_free_memory(fc);
+}
+
+static int f17_process_stick(struct rmi_device *rmi_dev,
+			     struct rmi_f17_stick_data *stick) {
+	int retval = 0;
+
+	if (stick->query.general.has_absolute) {
+		retval = rmi_read_block(rmi_dev, stick->data.abs.address,
+			stick->data.abs.regs, sizeof(stick->data.abs.regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read abs data for stick %d, code %d.\n",
+				stick->index, retval);
+			goto error_exit;
+		}
+	}
+	if (stick->query.general.has_relative) {
+		retval = rmi_read_block(rmi_dev, stick->data.rel.address,
+			stick->data.rel.regs, sizeof(stick->data.rel.regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read rel data for stick %d, code %d.\n",
+				stick->index, retval);
+			goto error_exit;
+		}
+		dev_dbg(&rmi_dev->dev, "Reporting dX: %d, dy: %d\n",
+			stick->data.rel.x_delta, stick->data.rel.y_delta);
+		input_report_rel(stick->mouse, REL_X, stick->data.rel.x_delta);
+		input_report_rel(stick->mouse, REL_Y, stick->data.rel.y_delta);
+	}
+	if (stick->query.general.has_gestures) {
+		retval = rmi_read_block(rmi_dev, stick->data.gestures.address,
+			stick->data.gestures.regs,
+			sizeof(stick->data.gestures.regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read gestures for stick %d, code %d.\n",
+				stick->index, retval);
+			goto error_exit;
+		}
+	}
+	retval = 0;
+
+error_exit:
+	if (stick->input)
+		input_sync(stick->input);
+	if (stick->mouse)
+		input_sync(stick->mouse);
+	return retval;
+}
+
+static int f17_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_f17_device_data *f17 = fc->data;
+	int i;
+	int retval = 0;
+
+	for (i = 0; i < f17->query.number_of_sticks + 1 && !retval; i++)
+		retval = f17_process_stick(rmi_dev, &f17->sticks[i]);
+
+	return retval;
+}
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x17,
+	.init = f17_init,
+	.config = f17_config,
+	.attention = f17_attention,
+	.remove = f17_remove
+};
+
+static int __init f17_module_init(void)
+{
+	int error;
+
+	error = rmi_register_function_driver(&function_handler);
+	if (error < 0) {
+		pr_err("%s: register failed!\n", __func__);
+		return error;
+	}
+
+	return 0;
+}
+
+static void f17_module_exit(void)
+{
+	rmi_unregister_function_driver(&function_handler);
+}
+
+
+static ssize_t f17_rezero_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rmi_function_container *fc;
+	struct rmi_f17_device_data *f17;
+
+	fc = to_rmi_function_container(dev);
+	f17 = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			f17->commands.rezero);
+
+}
+
+static ssize_t f17_rezero_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t count)
+{
+	struct rmi_function_container *fc;
+	struct rmi_f17_device_data *data;
+	unsigned int new_value;
+	int len;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	len = sscanf(buf, "%u", &new_value);
+	if (new_value != 0 && new_value != 1) {
+		dev_err(dev,
+			"%s: Error - rezero is not a valid value 0x%x.\n",
+			__func__, new_value);
+		return -EINVAL;
+	}
+	data->commands.rezero = new_value;
+	len = rmi_write(fc->rmi_dev, fc->fd.command_base_addr,
+		data->commands.rezero);
+
+	if (len < 0) {
+		dev_err(dev, "%s : Could not write rezero to 0x%x\n",
+				__func__, fc->fd.command_base_addr);
+		return -EINVAL;
+	}
+	return count;
+}
+
+module_init(f17_module_init);
+module_exit(f17_module_exit);
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
+MODULE_DESCRIPTION("RMI F17 module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
