diff mbox

[RFC,05/06] input/rmi4: F01 - device control

Message ID 1349496603-20775-6-git-send-email-cheiny@synaptics.com (mailing list archive)
State New, archived
Headers show

Commit Message

Christopher Heiny Oct. 6, 2012, 4:10 a.m. UTC
RMI Function 01 implements basic device control and power management
behaviors for the RMI4 sensor.  Since the last patch, we've decoupled rmi_f01.c
implementation from rmi_driver.c, so rmi_f01.c acts as a standard driver
module to handle F01 devices on the RMI bus.

Like other modules, a number of attributes have been moved from sysfs to
debugfs, depending on their expected use.


rmi_f01.h exports definitions that we expect to be used by other functionality
in the future (such as firmware reflash).


Signed-off-by: Christopher Heiny <cheiny@synaptics.com>

Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Linus Walleij <linus.walleij@stericsson.com>
Cc: Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
Cc: Joeri de Gram <j.de.gram@gmail.com>

---

 drivers/input/rmi4/rmi_f01.c | 1463 ++++++++++++++++++++++++++++++++++++++++++
 drivers/input/rmi4/rmi_f01.h |  136 ++++
 2 files changed, 1599 insertions(+), 0 deletions(-)

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Linus Walleij Oct. 9, 2012, 9:31 a.m. UTC | #1
On Sat, Oct 6, 2012 at 6:10 AM, Christopher Heiny <cheiny@synaptics.com> wrote:

> RMI Function 01 implements basic device control and power management
> behaviors for the RMI4 sensor.  Since the last patch, we've decoupled rmi_f01.c
> implementation from rmi_driver.c, so rmi_f01.c acts as a standard driver
> module to handle F01 devices on the RMI bus.
>
> Like other modules, a number of attributes have been moved from sysfs to
> debugfs, depending on their expected use.
>
>
> rmi_f01.h exports definitions that we expect to be used by other functionality
> in the future (such as firmware reflash).
>
>
> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Linus Walleij <linus.walleij@stericsson.com>
> Cc: Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
> Cc: Joeri de Gram <j.de.gram@gmail.com>
>
> ---

There is liberal whitespacing above. (No big deal, but anyway.)

(...)
> +/**
> + * @reset - set this bit to force a firmware reset of the sensor.
> + */
> +union f01_device_commands {
> +       struct {
> +               bool reset:1;
> +               u8 reserved:7;
> +       } __attribute__((__packed__));
> +       u8 reg;
> +};

I'm still scared by these unions. I see what you're doing but my
preferred style of driver writing is to use a simple u8 if you just treat
it the right way with some |= and &= ...

#include <linux/bitops.h>

#define F01_RESET BIT(0)

u8 my_command = F01_RESET;

send(&my_command);

I will not insist on this because it's a bit about programming style.
For memory-mapped devices we usually do it my way, but this
is more like some protocol and I know protocols like to do things
with structs and stuff so no big deal.

> +#ifdef CONFIG_RMI4_DEBUG
> +struct f01_debugfs_data {
> +       bool done;
> +       struct rmi_function_container *fc;
> +};
> +
> +static int f01_debug_open(struct inode *inodep, struct file *filp)
> +{
> +       struct f01_debugfs_data *data;
> +       struct rmi_function_container *fc = inodep->i_private;
> +
> +       data = devm_kzalloc(&fc->dev, sizeof(struct f01_debugfs_data),
> +               GFP_KERNEL);

Wait, you probably did this because I requested it, but I was maybe
wrong?

Will this not re-allocate a chunk every time you look at a debugfs
file? So it leaks memory?

In that case common kzalloc() and kfree() is the way to go, as it
is for dynamic buffers. Sorry for screwing things up for you. :-(

> +       for (i = 0; i < f01->irq_count && *local_buf != 0;
> +            i++, local_buf += 2) {
> +               int irq_shift;
> +               int interrupt_enable;
> +               int result;
> +
> +               irq_reg = i / 8;
> +               irq_shift = i % 8;

Please stop doing these arithmetics-turned-maths things.

irq_reg = i >> 8;
irq_shift = i & 0xFF;

(...)
> +static ssize_t rmi_fn_01_interrupt_enable_show(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       struct rmi_function_container *fc;
> +       struct f01_data *data;
> +       int i, len, total_len = 0;
> +       char *current_buf = buf;
> +
> +       fc = to_rmi_function_container(dev);
> +       data = fc->data;
> +       /* loop through each irq value and copy its
> +        * string representation into buf */
> +       for (i = 0; i < data->irq_count; i++) {
> +               int irq_reg;
> +               int irq_shift;
> +               int interrupt_enable;
> +
> +               irq_reg = i / 8;
> +               irq_shift = i % 8;

Dito.

(...)
> +static int f01_probe(struct device *dev);

Do you really need to forward-declare this?

(...)
> +static struct rmi_function_handler function_handler = {
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "rmi_f01",
> +               .bus = &rmi_bus_type,
> +               .probe = f01_probe,
> +               .remove = f01_remove_device,
> +       },
> +       .func = 0x01,
> +       .config = rmi_f01_config,
> +       .attention = rmi_f01_attention,
> +
> +#ifdef CONFIG_PM
> +       .suspend = rmi_f01_suspend,
> +       .resume = rmi_f01_resume,
> +#endif  /* CONFIG_PM */
> +};

Just move this block of struct *below* the probe function...

> +static __devinit int f01_probe(struct device *dev)

Header file looks OK (if these unions is the way to go...)

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christopher Heiny Oct. 11, 2012, 4:34 a.m. UTC | #2
Linus Walleij wrote:
> On Sat, Oct 6, 2012 at 6:10 AM, Christopher Heiny <cheiny@synaptics.com> wrote:
> > RMI Function 01 implements basic device control and power management
> > behaviors for the RMI4 sensor.  Since the last patch, we've decoupled
> > rmi_f01.c implementation from rmi_driver.c, so rmi_f01.c acts as a
> > standard driver module to handle F01 devices on the RMI bus.
> > 
> > Like other modules, a number of attributes have been moved from sysfs to
> > debugfs, depending on their expected use.
> > 
> > 
> > rmi_f01.h exports definitions that we expect to be used by other
> > functionality in the future (such as firmware reflash).
> > 
> > 
> > Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
> > 
> > Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> > Cc: Linus Walleij <linus.walleij@stericsson.com>
> > Cc: Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
> > Cc: Joeri de Gram <j.de.gram@gmail.com>
> > 
> > ---
> 
> There is liberal whitespacing above. (No big deal, but anyway.)
> 
> (...)
> 
> > +/**
> > + * @reset - set this bit to force a firmware reset of the sensor.
> > + */
> > +union f01_device_commands {
> > +       struct {
> > +               bool reset:1;
> > +               u8 reserved:7;
> > +       } __attribute__((__packed__));
> > +       u8 reg;
> > +};
> 
> I'm still scared by these unions. I see what you're doing but my
> preferred style of driver writing is to use a simple u8 if you just treat
> it the right way with some |= and &= ...
> 
> #include <linux/bitops.h>
> 
> #define F01_RESET BIT(0)
> 
> u8 my_command = F01_RESET;
> 
> send(&my_command);
> 
> I will not insist on this because it's a bit about programming style.
> For memory-mapped devices we usually do it my way, but this
> is more like some protocol and I know protocols like to do things
> with structs and stuff so no big deal.

That's a good summary of what we're trying to do.  Our original version did more of the traditional mask+shift approach to manipulating the fields in the various registers, but in the case of complicated functions such as F11 this rapidly became unreadable.  We found the unions worked a lot better - the code was more readable and less error prone.  For consistency we decided to apply them throughout the code.

> 
> > +#ifdef CONFIG_RMI4_DEBUG
> > +struct f01_debugfs_data {
> > +       bool done;
> > +       struct rmi_function_container *fc;
> > +};
> > +
> > +static int f01_debug_open(struct inode *inodep, struct file *filp)
> > +{
> > +       struct f01_debugfs_data *data;
> > +       struct rmi_function_container *fc = inodep->i_private;
> > +
> > +       data = devm_kzalloc(&fc->dev, sizeof(struct f01_debugfs_data),
> > +               GFP_KERNEL);
> 
> Wait, you probably did this because I requested it, but I was maybe
> wrong?
> 
> Will this not re-allocate a chunk every time you look at a debugfs
> file? So it leaks memory?
> 
> In that case common kzalloc() and kfree() is the way to go, as it
> is for dynamic buffers. Sorry for screwing things up for you.

No problem - we'll fix it.  Or unfix it.  Or something like that. :-)


> 
> > +       for (i = 0; i < f01->irq_count && *local_buf != 0;
> > +            i++, local_buf += 2) {
> > +               int irq_shift;
> > +               int interrupt_enable;
> > +               int result;
> > +
> > +               irq_reg = i / 8;
> > +               irq_shift = i % 8;
> 
> Please stop doing these arithmetics-turned-maths things.
> 
> irq_reg = i >> 8;
> irq_shift = i & 0xFF;

See note on this in a previous email.

> 
> (...)
> 
> > +static ssize_t rmi_fn_01_interrupt_enable_show(struct device *dev,
> > +                               struct device_attribute *attr, char *buf)
> > +{
> > +       struct rmi_function_container *fc;
> > +       struct f01_data *data;
> > +       int i, len, total_len = 0;
> > +       char *current_buf = buf;
> > +
> > +       fc = to_rmi_function_container(dev);
> > +       data = fc->data;
> > +       /* loop through each irq value and copy its
> > +        * string representation into buf */
> > +       for (i = 0; i < data->irq_count; i++) {
> > +               int irq_reg;
> > +               int irq_shift;
> > +               int interrupt_enable;
> > +
> > +               irq_reg = i / 8;
> > +               irq_shift = i % 8;
> 
> Dito.
> 
> (...)
> 
> > +static int f01_probe(struct device *dev);
> 
> Do you really need to forward-declare this?

It's a leftover from the process of eliminating roll-your-own bus implementation, and move the other code around as well.  (same applies for similar code in rmi_f11.c).

> 
> (...)
> 
> > +static struct rmi_function_handler function_handler = {
> > +       .driver = {
> > +               .owner = THIS_MODULE,
> > +               .name = "rmi_f01",
> > +               .bus = &rmi_bus_type,
> > +               .probe = f01_probe,
> > +               .remove = f01_remove_device,
> > +       },
> > +       .func = 0x01,
> > +       .config = rmi_f01_config,
> > +       .attention = rmi_f01_attention,
> > +
> > +#ifdef CONFIG_PM
> > +       .suspend = rmi_f01_suspend,
> > +       .resume = rmi_f01_resume,
> > +#endif  /* CONFIG_PM */
> > +};
> 
> Just move this block of struct below the probe function...
> 
> > +static __devinit int f01_probe(struct device *dev)
> 
> Header file looks OK (if these unions is the way to go...)
> 
> Yours,
> Linus Walleij--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
new file mode 100644
index 0000000..d734f46
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -0,0 +1,1463 @@ 
+/*
+ * Copyright (c) 2011-2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * 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/debugfs.h>
+#include <linux/kconfig.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include "rmi_driver.h"
+#include "rmi_f01.h"
+
+/**
+ * @reset - set this bit to force a firmware reset of the sensor.
+ */
+union f01_device_commands {
+	struct {
+		bool reset:1;
+		u8 reserved:7;
+	} __attribute__((__packed__));
+	u8 reg;
+};
+
+/**
+ * @ctrl0 - see documentation in rmi_f01.h.
+ * @interrupt_enable - A mask of per-function interrupts on the touch sensor.
+ * @doze_interval - controls the interval between checks for finger presence
+ * when the touch sensor is in doze mode, in units of 10ms.
+ * @wakeup_threshold - controls the capacitance threshold at which the touch
+ * sensor will decide to wake up from that low power state.
+ * @doze_holdoff - controls how long the touch sensor waits after the last
+ * finger lifts before entering the doze state, in units of 100ms.
+ */
+struct f01_device_control {
+	union f01_device_control_0 ctrl0;
+	u8 *interrupt_enable;
+	u8 doze_interval;
+	u8 wakeup_threshold;
+	u8 doze_holdoff;
+};
+
+/**
+ * @has_ds4_queries - if true, the query registers relating to Design Studio 4
+ * features are present.
+ * @has_multi_phy - if true, multiple physical communications interfaces are
+ * supported.
+ * @has_guest - if true, a "guest" device is supported.
+ */
+union f01_query_42 {
+	struct {
+		bool has_ds4_queries:1;
+		bool has_multi_phy:1;
+		bool has_guest:1;
+		u8 reserved:5;
+	} __attribute__((__packed__));
+	u8 regs[1];
+};
+
+/**
+ * @length - the length of the remaining Query43.* register block, not
+ * including the first register.
+ * @has_package_id_query -  the package ID query data will be accessible from
+ * inside the ProductID query registers.
+ * @has_packrat_query -  the packrat query data will be accessible from inside
+ * the ProductID query registers.
+ * @has_reset_query - the reset pin related registers are valid.
+ * @has_maskrev_query - the silicon mask revision number will be reported.
+ * @has_i2c_control - the register F01_RMI_Ctrl6 will exist.
+ * @has_spi_control - the register F01_RMI_Ctrl7 will exist.
+ * @has_attn_control - the register F01_RMI_Ctrl8 will exist.
+ * @reset_enabled - the hardware reset pin functionality has been enabled
+ * for this device.
+ * @reset_polarity - If this bit reports as ‘0’, it means that the reset state
+ * is active low. A ‘1’ means that the reset state is active high.
+ * @pullup_enabled - If set, it indicates that a built-in weak pull up has
+ * been enabled on the Reset pin; clear means that no pull-up is present.
+ * @reset_pin_number - This field represents which GPIO pin number has been
+ * assigned the reset functionality.
+ */
+union f01_ds4_queries {
+	struct {
+		u8 length:4;
+		u8 reserved_1:4;
+
+		bool has_package_id_query:1;
+		bool has_packrat_query:1;
+		bool has_reset_query:1;
+		bool has_maskrev_query:1;
+		u8 reserved_2:4;
+
+		bool has_i2c_control:1;
+		bool has_spi_control:1;
+		bool has_attn_control:1;
+		u8 reserved_3:5;
+
+		bool reset_enabled:1;
+		bool reset_polarity:1;
+		bool pullup_enabled:1;
+		u8 reserved_4:1;
+		u8 reset_pin_number:4;
+	} __attribute__((__packed__));
+	u8 regs[4];
+};
+
+struct f01_data {
+	struct f01_device_control device_control;
+	union f01_basic_queries basic_queries;
+	union f01_device_status device_status;
+	u8 product_id[RMI_PRODUCT_ID_LENGTH+1];
+
+	u16 interrupt_enable_addr;
+	u16 doze_interval_addr;
+	u16 wakeup_threshold_addr;
+	u16 doze_holdoff_addr;
+
+	int irq_count;
+	int num_of_irq_regs;
+
+#ifdef	CONFIG_PM
+	bool suspended;
+	bool old_nosleep;
+#endif
+
+#ifdef	CONFIG_RMI4_DEBUG
+	struct dentry *debugfs_interrupt_enable;
+#endif
+};
+
+#ifdef	CONFIG_RMI4_DEBUG
+struct f01_debugfs_data {
+	bool done;
+	struct rmi_function_container *fc;
+};
+
+static int f01_debug_open(struct inode *inodep, struct file *filp)
+{
+	struct f01_debugfs_data *data;
+	struct rmi_function_container *fc = inodep->i_private;
+
+	data = devm_kzalloc(&fc->dev, sizeof(struct f01_debugfs_data),
+		GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->fc = fc;
+	filp->private_data = data;
+	return 0;
+}
+
+static ssize_t interrupt_enable_read(struct file *filp, char __user *buffer,
+				     size_t size, loff_t *offset) {
+	int i;
+	int len;
+	int total_len = 0;
+	char local_buf[size];
+	char *current_buf = local_buf;
+	struct f01_debugfs_data *data = filp->private_data;
+	struct f01_data *f01 = data->fc->data;
+
+	if (data->done)
+		return 0;
+
+	data->done = 1;
+
+	/* loop through each irq value and copy its
+	 * string representation into buf */
+	for (i = 0; i < f01->irq_count; i++) {
+		int irq_reg;
+		int irq_shift;
+		int interrupt_enable;
+
+		irq_reg = i / 8;
+		irq_shift = i % 8;
+		interrupt_enable =
+		    ((f01->device_control.interrupt_enable[irq_reg]
+			>> irq_shift) & 0x01);
+
+		/* get next irq value and write it to buf */
+		len = snprintf(current_buf, size - total_len,
+			"%u ", interrupt_enable);
+		/* bump up ptr to next location in buf if the
+		 * snprintf was valid.  Otherwise issue an error
+		 * and return. */
+		if (len > 0) {
+			current_buf += len;
+			total_len += len;
+		} else {
+			dev_err(&data->fc->dev, "Failed to build interrupt_enable buffer, code = %d.\n",
+						len);
+			return snprintf(local_buf, size, "unknown\n");
+		}
+	}
+	len = snprintf(current_buf, size - total_len, "\n");
+	if (len > 0)
+		total_len += len;
+	else
+		dev_warn(&data->fc->dev, "%s: Failed to append carriage return.\n",
+			 __func__);
+
+	if (copy_to_user(buffer, local_buf, total_len))
+		return -EFAULT;
+
+	return total_len;
+}
+
+static ssize_t interrupt_enable_write(struct file *filp,
+		const char __user *buffer, size_t size, loff_t *offset) {
+	int retval;
+	char buf[size];
+	char *local_buf = buf;
+	int i;
+	int irq_count = 0;
+	int irq_reg = 0;
+	struct f01_debugfs_data *data = filp->private_data;
+	struct f01_data *f01 = data->fc->data;
+
+	retval = copy_from_user(buf, buffer, size);
+	if (retval)
+		return -EFAULT;
+
+	for (i = 0; i < f01->irq_count && *local_buf != 0;
+	     i++, local_buf += 2) {
+		int irq_shift;
+		int interrupt_enable;
+		int result;
+
+		irq_reg = i / 8;
+		irq_shift = i % 8;
+
+		/* get next interrupt mapping value and store and bump up to
+		 * point to next item in local_buf */
+		result = sscanf(local_buf, "%u", &interrupt_enable);
+		if ((result != 1) ||
+			(interrupt_enable != 0 && interrupt_enable != 1)) {
+			dev_err(&data->fc->dev, "Interrupt enable[%d] is not a valid value 0x%x.\n",
+				i, interrupt_enable);
+			return -EINVAL;
+		}
+		if (interrupt_enable == 0) {
+			f01->device_control.interrupt_enable[irq_reg] &=
+				(1 << irq_shift) ^ 0xFF;
+		} else
+			f01->device_control.interrupt_enable[irq_reg] |=
+				(1 << irq_shift);
+		irq_count++;
+	}
+
+	/* Make sure the irq count matches */
+	if (irq_count != f01->irq_count) {
+		dev_err(&data->fc->dev, "Interrupt enable count of %d doesn't match device count of %d.\n",
+			 irq_count, f01->irq_count);
+		return -EINVAL;
+	}
+
+	/* write back to the control register */
+	retval = rmi_write_block(data->fc->rmi_dev, f01->interrupt_enable_addr,
+			f01->device_control.interrupt_enable,
+			f01->num_of_irq_regs);
+	if (retval < 0) {
+		dev_err(&data->fc->dev, "Could not write interrupt_enable mask to %#06x\n",
+			f01->interrupt_enable_addr);
+		return retval;
+	}
+
+	return size;
+}
+
+static const struct file_operations interrupt_enable_fops = {
+	.owner = THIS_MODULE,
+	.open = f01_debug_open,
+	.read = interrupt_enable_read,
+	.write = interrupt_enable_write,
+};
+
+static int setup_debugfs(struct rmi_function_container *fc)
+{
+	struct f01_data *data = fc->data;
+
+	if (!fc->debugfs_root)
+		return -ENODEV;
+
+	data->debugfs_interrupt_enable = debugfs_create_file("interrupt_enable",
+		RMI_RW_ATTR, fc->debugfs_root, fc, &interrupt_enable_fops);
+	if (!data->debugfs_interrupt_enable)
+		dev_warn(&fc->dev,
+			 "Failed to create debugfs interrupt_enable.\n");
+
+	return 0;
+}
+
+static void teardown_debugfs(struct f01_data *f01)
+{
+	if (f01->debugfs_interrupt_enable)
+		debugfs_remove(f01->debugfs_interrupt_enable);
+}
+#endif
+
+
+static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf);
+
+static ssize_t rmi_fn_01_productid_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+
+static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf);
+
+static ssize_t rmi_fn_01_datecode_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf);
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_interrupt_enable_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+
+static ssize_t rmi_fn_01_doze_interval_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+
+static ssize_t rmi_fn_01_doze_interval_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_wakeup_threshold_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+
+static ssize_t rmi_fn_01_wakeup_threshold_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_doze_holdoff_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+
+static ssize_t rmi_fn_01_doze_holdoff_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_reset_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_sleepmode_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+
+static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_nosleep_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_chargerinput_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_chargerinput_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_configured_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_unconfigured_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_flashprog_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_statuscode_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static int rmi_f01_alloc_memory(struct rmi_function_container *fc,
+					int num_of_irq_regs);
+
+static int rmi_f01_initialize(struct rmi_function_container *fc);
+
+static int rmi_f01_create_sysfs(struct rmi_function_container *fc);
+
+static int rmi_f01_config(struct rmi_function_container *fc);
+
+
+static struct device_attribute fn_01_attrs[] = {
+	__ATTR(productinfo, RMI_RO_ATTR,
+	       rmi_fn_01_productinfo_show, rmi_store_error),
+	__ATTR(productid, RMI_RO_ATTR,
+	       rmi_fn_01_productid_show, rmi_store_error),
+	__ATTR(manufacturer, RMI_RO_ATTR,
+	       rmi_fn_01_manufacturer_show, rmi_store_error),
+	__ATTR(datecode, RMI_RO_ATTR,
+	       rmi_fn_01_datecode_show, rmi_store_error),
+
+	/* control register access */
+	__ATTR(sleepmode, RMI_RW_ATTR,
+	       rmi_fn_01_sleepmode_show, rmi_fn_01_sleepmode_store),
+	__ATTR(nosleep, RMI_RW_ATTR,
+	       rmi_fn_01_nosleep_show, rmi_fn_01_nosleep_store),
+	__ATTR(chargerinput, RMI_RW_ATTR,
+	       rmi_fn_01_chargerinput_show, rmi_fn_01_chargerinput_store),
+	__ATTR(reportrate, RMI_RW_ATTR,
+	       rmi_fn_01_reportrate_show, rmi_fn_01_reportrate_store),
+	/* We don't want arbitrary callers changing the interrupt enable mask,
+	 * so it's read only.
+	 */
+	__ATTR(interrupt_enable, RMI_RO_ATTR,
+	       rmi_fn_01_interrupt_enable_show, rmi_store_error),
+	__ATTR(doze_interval, RMI_RW_ATTR,
+	       rmi_fn_01_doze_interval_show, rmi_fn_01_doze_interval_store),
+	__ATTR(wakeup_threshold, RMI_RW_ATTR,
+	       rmi_fn_01_wakeup_threshold_show,
+		rmi_fn_01_wakeup_threshold_store),
+	__ATTR(doze_holdoff, RMI_RW_ATTR,
+	       rmi_fn_01_doze_holdoff_show, rmi_fn_01_doze_holdoff_store),
+
+	/* We make report rate RO, since the driver uses that to look for
+	 * resets.  We don't want someone faking us out by changing that
+	 * bit.
+	 */
+	__ATTR(configured, RMI_RO_ATTR,
+	       rmi_fn_01_configured_show, rmi_store_error),
+
+	/* Command register access. */
+	__ATTR(reset, RMI_WO_ATTR,
+	       rmi_show_error, rmi_fn_01_reset_store),
+
+	/* STatus register access. */
+	__ATTR(unconfigured, RMI_RO_ATTR,
+	       rmi_fn_01_unconfigured_show, rmi_store_error),
+	__ATTR(flashprog, RMI_RO_ATTR,
+	       rmi_fn_01_flashprog_show, rmi_store_error),
+	__ATTR(statuscode, RMI_RO_ATTR,
+	       rmi_fn_01_statuscode_show, rmi_store_error),
+};
+
+/* Utility routine to set the value of a bit field in a register. */
+int rmi_mask_and_set(struct rmi_device *rmi_dev,
+		      u16 address,
+		      u8 mask,
+		      u8 set)
+{
+	u8 reg_contents;
+	int retval;
+
+	retval = rmi_read(rmi_dev, address, &reg_contents);
+	if (retval < 0)
+		return retval;
+	reg_contents = (reg_contents & ~mask) | set;
+	retval = rmi_write(rmi_dev, address, reg_contents);
+	if (retval == 1)
+		return 0;
+	else if (retval == 0)
+		return -EIO;
+	return retval;
+}
+
+static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n",
+			data->basic_queries.productinfo_1,
+			data->basic_queries.productinfo_2);
+}
+
+static ssize_t rmi_fn_01_productid_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", data->product_id);
+}
+
+static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "0x%02x\n",
+			data->basic_queries.manufacturer_id);
+}
+
+static ssize_t rmi_fn_01_datecode_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "20%02u-%02u-%02u\n",
+			data->basic_queries.year,
+			data->basic_queries.month,
+			data->basic_queries.day);
+}
+
+static ssize_t rmi_fn_01_reset_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct rmi_function_container *fc = NULL;
+	unsigned int reset;
+	int retval = 0;
+	/* Command register always reads as 0, so we can just use a local. */
+	union f01_device_commands commands = {};
+
+	fc = to_rmi_function_container(dev);
+
+	if (sscanf(buf, "%u", &reset) != 1)
+		return -EINVAL;
+	if (reset < 0 || reset > 1)
+		return -EINVAL;
+
+	/* Per spec, 0 has no effect, so we skip it entirely. */
+	if (reset) {
+		commands.reset = 1;
+		retval = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+				&commands.reg, sizeof(commands.reg));
+		if (retval < 0) {
+			dev_err(dev, "Failed to issue reset command, code = %d.",
+						retval);
+			return retval;
+		}
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_01_sleepmode_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE,
+			"%d\n", data->device_control.ctrl0.sleep_mode);
+}
+
+static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || !RMI_IS_VALID_SLEEPMODE(new_value)) {
+		dev_err(dev, "%s: Invalid sleep mode %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "Setting sleep mode to %ld.", new_value);
+	data->device_control.ctrl0.sleep_mode = new_value;
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write sleep mode, code %d.\n", retval);
+	return retval;
+}
+
+static ssize_t rmi_fn_01_nosleep_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+		data->device_control.ctrl0.nosleep);
+}
+
+static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 1) {
+		dev_err(dev, "%s: Invalid nosleep bit %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.ctrl0.nosleep = new_value;
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write nosleep bit.\n");
+	return retval;
+}
+
+static ssize_t rmi_fn_01_chargerinput_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.ctrl0.charger_input);
+}
+
+static ssize_t rmi_fn_01_chargerinput_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 1) {
+		dev_err(dev, "%s: Invalid chargerinput bit %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.ctrl0.charger_input = new_value;
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write chargerinput bit.\n");
+	return retval;
+}
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.ctrl0.report_rate);
+}
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 1) {
+		dev_err(dev, "%s: Invalid reportrate bit %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.ctrl0.report_rate = new_value;
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write reportrate bit.\n");
+	return retval;
+}
+
+static ssize_t rmi_fn_01_interrupt_enable_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f01_data *data;
+	int i, len, total_len = 0;
+	char *current_buf = buf;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	/* loop through each irq value and copy its
+	 * string representation into buf */
+	for (i = 0; i < data->irq_count; i++) {
+		int irq_reg;
+		int irq_shift;
+		int interrupt_enable;
+
+		irq_reg = i / 8;
+		irq_shift = i % 8;
+		interrupt_enable =
+		    ((data->device_control.interrupt_enable[irq_reg]
+			>> irq_shift) & 0x01);
+
+		/* get next irq value and write it to buf */
+		len = snprintf(current_buf, PAGE_SIZE - total_len,
+			"%u ", interrupt_enable);
+		/* bump up ptr to next location in buf if the
+		 * snprintf was valid.  Otherwise issue an error
+		 * and return. */
+		if (len > 0) {
+			current_buf += len;
+			total_len += len;
+		} else {
+			dev_err(dev, "Failed to build interrupt_enable buffer, code = %d.\n",
+						len);
+			return snprintf(buf, PAGE_SIZE, "unknown\n");
+		}
+	}
+	len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
+	if (len > 0)
+		total_len += len;
+	else
+		dev_warn(dev, "%s: Failed to append carriage return.\n",
+			 __func__);
+	return total_len;
+
+}
+
+static ssize_t rmi_fn_01_doze_interval_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.doze_interval);
+
+}
+
+static ssize_t rmi_fn_01_doze_interval_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	u16 ctrl_base_addr;
+
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 255) {
+		dev_err(dev, "%s: Invalid doze interval %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.doze_interval = new_value;
+	ctrl_base_addr = fc->fd.control_base_addr + sizeof(u8) +
+			(sizeof(u8)*(data->num_of_irq_regs));
+	dev_dbg(dev, "doze_interval store address %x, value %d",
+		ctrl_base_addr, data->device_control.doze_interval);
+
+	retval = rmi_write_block(fc->rmi_dev, data->doze_interval_addr,
+			&data->device_control.doze_interval,
+			sizeof(u8));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write doze interval.\n");
+	return retval;
+
+}
+
+static ssize_t rmi_fn_01_wakeup_threshold_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.wakeup_threshold);
+}
+
+static ssize_t rmi_fn_01_wakeup_threshold_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 255) {
+		dev_err(dev, "%s: Invalid wakeup threshold %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.doze_interval = new_value;
+	retval = rmi_write_block(fc->rmi_dev, data->wakeup_threshold_addr,
+			&data->device_control.wakeup_threshold,
+			sizeof(u8));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write wakeup threshold.\n");
+	return retval;
+
+}
+
+static ssize_t rmi_fn_01_doze_holdoff_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.doze_holdoff);
+
+}
+
+
+static ssize_t rmi_fn_01_doze_holdoff_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 255) {
+		dev_err(dev, "%s: Invalid doze holdoff %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.doze_interval = new_value;
+	retval = rmi_write_block(fc->rmi_dev, data->doze_holdoff_addr,
+			&data->device_control.doze_holdoff,
+			sizeof(u8));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write doze holdoff.\n");
+	return retval;
+
+}
+
+static ssize_t rmi_fn_01_configured_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.ctrl0.configured);
+}
+
+static ssize_t rmi_fn_01_unconfigured_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_status.unconfigured);
+}
+
+static ssize_t rmi_fn_01_flashprog_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_status.flash_prog);
+}
+
+static ssize_t rmi_fn_01_statuscode_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "0x%02x\n",
+			data->device_status.status_code);
+}
+
+static int f01_device_init(struct rmi_function_container *fc)
+{
+	struct rmi_driver_data *driver_data =
+			dev_get_drvdata(&fc->rmi_dev->dev);
+	int error;
+
+	error = rmi_f01_alloc_memory(fc, driver_data->num_of_irq_regs);
+	if (error < 0)
+		return error;
+
+	error = rmi_f01_initialize(fc);
+	if (error < 0)
+		return error;
+
+	error = rmi_f01_create_sysfs(fc);
+	if (error < 0)
+		return error;
+
+	return 0;
+}
+
+static int rmi_f01_alloc_memory(struct rmi_function_container *fc,
+	int num_of_irq_regs)
+{
+	struct f01_data *f01;
+
+	f01 = devm_kzalloc(&fc->dev, sizeof(struct f01_data), GFP_KERNEL);
+	if (!f01) {
+		dev_err(&fc->dev, "Failed to allocate fn_01_data.\n");
+		return -ENOMEM;
+	}
+
+	f01->device_control.interrupt_enable = devm_kzalloc(&fc->dev,
+			sizeof(u8)*(num_of_irq_regs),
+			GFP_KERNEL);
+	if (!f01->device_control.interrupt_enable) {
+		dev_err(&fc->dev, "Failed to allocate interrupt enable.\n");
+		return -ENOMEM;
+	}
+	fc->data = f01;
+
+	return 0;
+}
+
+static int rmi_f01_initialize(struct rmi_function_container *fc)
+{
+	u8 temp;
+	int retval;
+	u16 ctrl_base_addr;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
+	struct f01_data *data = fc->data;
+	struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
+
+	/* Set the configured bit and (optionally) other important stuff
+	 * in the device control register. */
+	ctrl_base_addr = fc->fd.control_base_addr;
+	retval = rmi_read_block(rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read F01 control.\n");
+		return retval;
+	}
+	switch (pdata->power_management.nosleep) {
+	case RMI_F01_NOSLEEP_DEFAULT:
+		break;
+	case RMI_F01_NOSLEEP_OFF:
+		data->device_control.ctrl0.nosleep = 0;
+		break;
+	case RMI_F01_NOSLEEP_ON:
+		data->device_control.ctrl0.nosleep = 1;
+		break;
+	}
+	/* Sleep mode might be set as a hangover from a system crash or
+	 * reboot without power cycle.  If so, clear it so the sensor
+	 * is certain to function.
+	 */
+	if (data->device_control.ctrl0.sleep_mode != RMI_SLEEP_MODE_NORMAL) {
+		dev_warn(&fc->dev,
+			 "WARNING: Non-zero sleep mode found. Clearing...\n");
+		data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+	}
+
+	data->device_control.ctrl0.configured = 1;
+	retval = rmi_write_block(rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to write F01 control.\n");
+		return retval;
+	}
+
+	data->irq_count = driver_data->irq_count;
+	data->num_of_irq_regs = driver_data->num_of_irq_regs;
+	ctrl_base_addr += sizeof(union f01_device_control_0);
+
+	data->interrupt_enable_addr = ctrl_base_addr;
+	retval = rmi_read_block(rmi_dev, ctrl_base_addr,
+			data->device_control.interrupt_enable,
+			sizeof(u8)*(driver_data->num_of_irq_regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read F01 control interrupt enable register.\n");
+		goto error_exit;
+	}
+	ctrl_base_addr += driver_data->num_of_irq_regs;
+
+	/* dummy read in order to clear irqs */
+	retval = rmi_read(rmi_dev, fc->fd.data_base_addr + 1, &temp);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read Interrupt Status.\n");
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, fc->fd.query_base_addr,
+				data->basic_queries.regs,
+				sizeof(data->basic_queries.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read device query registers.\n");
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev,
+		fc->fd.query_base_addr + sizeof(data->basic_queries.regs),
+		data->product_id, RMI_PRODUCT_ID_LENGTH);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read product ID.\n");
+		return retval;
+	}
+	data->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
+	dev_info(&fc->dev, "found RMI device, manufacturer: %s, product: %s\n",
+		 data->basic_queries.manufacturer_id == 1 ?
+							"synaptics" : "unknown",
+		 data->product_id);
+
+	/* read control register */
+	if (data->basic_queries.has_adjustable_doze) {
+		data->doze_interval_addr = ctrl_base_addr;
+		ctrl_base_addr++;
+
+		if (pdata->power_management.doze_interval) {
+			data->device_control.doze_interval =
+				pdata->power_management.doze_interval;
+			retval = rmi_write(rmi_dev, data->doze_interval_addr,
+					data->device_control.doze_interval);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to configure F01 doze interval register.\n");
+				goto error_exit;
+			}
+		} else {
+			retval = rmi_read(rmi_dev, data->doze_interval_addr,
+					&data->device_control.doze_interval);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to read F01 doze interval register.\n");
+				goto error_exit;
+			}
+		}
+
+		data->wakeup_threshold_addr = ctrl_base_addr;
+		ctrl_base_addr++;
+
+		if (pdata->power_management.wakeup_threshold) {
+			data->device_control.wakeup_threshold =
+				pdata->power_management.wakeup_threshold;
+			retval = rmi_write(rmi_dev, data->wakeup_threshold_addr,
+					data->device_control.wakeup_threshold);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to configure F01 wakeup threshold register.\n");
+				goto error_exit;
+			}
+		} else {
+			retval = rmi_read(rmi_dev, data->wakeup_threshold_addr,
+					&data->device_control.wakeup_threshold);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to read F01 wakeup threshold register.\n");
+				goto error_exit;
+			}
+		}
+	}
+
+	if (data->basic_queries.has_adjustable_doze_holdoff) {
+		data->doze_holdoff_addr = ctrl_base_addr;
+		ctrl_base_addr++;
+
+		if (pdata->power_management.doze_holdoff) {
+			data->device_control.doze_holdoff =
+				pdata->power_management.doze_holdoff;
+			retval = rmi_write(rmi_dev, data->doze_holdoff_addr,
+					data->device_control.doze_holdoff);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to configure F01 doze holdoff register.\n");
+				goto error_exit;
+			}
+		} else {
+			retval = rmi_read(rmi_dev, data->doze_holdoff_addr,
+					&data->device_control.doze_holdoff);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to read F01 doze holdoff register.\n");
+				goto error_exit;
+			}
+		}
+	}
+
+	retval = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
+		data->device_status.regs, ARRAY_SIZE(data->device_status.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read device status.\n");
+		goto error_exit;
+	}
+
+	if (data->device_status.unconfigured) {
+		dev_err(&fc->dev, "Device reset during configuration process, status: %#02x!\n",
+				data->device_status.status_code);
+		retval = -EINVAL;
+		goto error_exit;
+	}
+
+	if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+		retval = setup_debugfs(fc);
+		if (retval < 0)
+			dev_warn(&fc->dev, "Failed to setup debugfs. Code: %d.\n",
+				retval);
+	}
+
+	return retval;
+
+ error_exit:
+	kfree(data);
+	return retval;
+}
+
+static int rmi_f01_create_sysfs(struct rmi_function_container *fc)
+{
+	int attr_count = 0;
+	int retval = 0;
+	struct f01_data *data = fc->data;
+
+	dev_dbg(&fc->dev, "Creating sysfs files.");
+	for (attr_count = 0; attr_count < ARRAY_SIZE(fn_01_attrs);
+			attr_count++) {
+		if (!strcmp("doze_interval", fn_01_attrs[attr_count].attr.name)
+			&& !data->basic_queries.has_lts) {
+			continue;
+		}
+		if (!strcmp("wakeup_threshold",
+			fn_01_attrs[attr_count].attr.name)
+			&& !data->basic_queries.has_adjustable_doze) {
+			continue;
+		}
+		if (!strcmp("doze_holdoff", fn_01_attrs[attr_count].attr.name)
+			&& !data->basic_queries.has_adjustable_doze_holdoff) {
+			continue;
+		}
+		retval = sysfs_create_file(&fc->dev.kobj,
+				      &fn_01_attrs[attr_count].attr);
+		if (retval < 0) {
+			dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+			       fn_01_attrs[attr_count].attr.name);
+			goto err_remove_sysfs;
+		}
+	}
+
+	return 0;
+
+err_remove_sysfs:
+	for (attr_count--; attr_count >= 0; attr_count--)
+		sysfs_remove_file(&fc->dev.kobj,
+				  &fn_01_attrs[attr_count].attr);
+
+	return retval;
+}
+
+static int rmi_f01_config(struct rmi_function_container *fc)
+{
+	struct f01_data *data = fc->data;
+	int retval;
+
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to write device_control.reg.\n");
+		return retval;
+	}
+
+	retval = rmi_write_block(fc->rmi_dev, data->interrupt_enable_addr,
+			data->device_control.interrupt_enable,
+			sizeof(u8)*(data->num_of_irq_regs));
+
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to write interrupt enable.\n");
+		return retval;
+	}
+	if (data->basic_queries.has_lts) {
+		retval = rmi_write_block(fc->rmi_dev, data->doze_interval_addr,
+				&data->device_control.doze_interval,
+				sizeof(u8));
+		if (retval < 0) {
+			dev_err(&fc->dev, "Failed to write doze interval.\n");
+			return retval;
+		}
+	}
+
+	if (data->basic_queries.has_adjustable_doze) {
+		retval = rmi_write_block(
+				fc->rmi_dev, data->wakeup_threshold_addr,
+				&data->device_control.wakeup_threshold,
+				sizeof(u8));
+		if (retval < 0) {
+			dev_err(&fc->dev, "Failed to write wakeup threshold.\n");
+			return retval;
+		}
+	}
+
+	if (data->basic_queries.has_adjustable_doze_holdoff) {
+		retval = rmi_write_block(fc->rmi_dev, data->doze_holdoff_addr,
+				&data->device_control.doze_holdoff,
+				sizeof(u8));
+		if (retval < 0) {
+			dev_err(&fc->dev, "Failed to write doze holdoff.\n");
+			return retval;
+		}
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int rmi_f01_suspend(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
+	struct f01_data *data = driver_data->f01_container->data;
+	int retval = 0;
+
+	if (data->suspended)
+		return 0;
+
+	data->old_nosleep = data->device_control.ctrl0.nosleep;
+	data->device_control.ctrl0.nosleep = 0;
+	data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_SENSOR_SLEEP;
+
+	retval = rmi_write_block(rmi_dev,
+			driver_data->f01_container->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to write sleep mode. Code: %d.\n",
+			retval);
+		data->device_control.ctrl0.nosleep = data->old_nosleep;
+		data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+	} else {
+		data->suspended = true;
+		retval = 0;
+	}
+
+	return retval;
+}
+
+static int rmi_f01_resume(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
+	struct f01_data *data = driver_data->f01_container->data;
+	int retval = 0;
+
+	if (!data->suspended)
+		return 0;
+
+	data->device_control.ctrl0.nosleep = data->old_nosleep;
+	data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+
+	retval = rmi_write_block(rmi_dev,
+			driver_data->f01_container->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval < 0)
+		dev_err(&fc->dev,
+			"Failed to restore normal operation. Code: %d.\n",
+			retval);
+	else {
+		data->suspended = false;
+		retval = 0;
+	}
+
+	return retval;
+}
+#endif /* CONFIG_PM */
+
+static int f01_remove_device(struct device *dev)
+{
+	int attr_count;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	if (IS_ENABLED(CONFIG_RMI4_DEBUG))
+		teardown_debugfs(fc->data);
+
+	for (attr_count = 0; attr_count < ARRAY_SIZE(fn_01_attrs);
+			attr_count++) {
+		sysfs_remove_file(&fc->dev.kobj, &fn_01_attrs[attr_count].attr);
+	}
+	return 0;
+}
+
+static int rmi_f01_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct f01_data *data = fc->data;
+	int retval;
+
+	retval = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
+		data->device_status.regs, ARRAY_SIZE(data->device_status.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read device status, code: %d.\n",
+			retval);
+		return retval;
+	}
+	if (data->device_status.unconfigured) {
+		dev_warn(&fc->dev, "Device reset detected.\n");
+		retval = rmi_dev->driver->reset_handler(rmi_dev);
+		if (retval < 0)
+			return retval;
+	}
+	return 0;
+}
+
+static int f01_probe(struct device *dev);
+
+static struct rmi_function_handler function_handler = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "rmi_f01",
+		.bus = &rmi_bus_type,
+		.probe = f01_probe,
+		.remove = f01_remove_device,
+	},
+	.func = 0x01,
+	.config = rmi_f01_config,
+	.attention = rmi_f01_attention,
+
+#ifdef	CONFIG_PM
+	.suspend = rmi_f01_suspend,
+	.resume = rmi_f01_resume,
+#endif  /* CONFIG_PM */
+};
+
+static __devinit int f01_probe(struct device *dev)
+{
+	struct rmi_function_container *fc;
+
+	if (dev->type != &rmi_function_type) {
+		dev_dbg(dev, "Not a function device.\n");
+		return 1;
+	}
+	fc = to_rmi_function_container(dev);
+	if (fc->fd.function_number != function_handler.func) {
+		dev_dbg(dev, "Device is F%02X, not F%02X.\n",
+			fc->fd.function_number, function_handler.func);
+		return 1;
+	}
+
+	dev_dbg(dev, "Yay! It is F01!\n");
+	return f01_device_init(fc);
+}
+
+static int __init rmi_f01_module_init(void)
+{
+	int error;
+
+	error = driver_register(&function_handler.driver);
+	if (error < 0) {
+		pr_err("%s: register failed!\n", __func__);
+		return error;
+	}
+
+	return 0;
+}
+
+static void __exit rmi_f01_module_exit(void)
+{
+	driver_unregister(&function_handler.driver);
+}
+
+module_init(rmi_f01_module_init);
+module_exit(rmi_f01_module_exit);
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
+MODULE_DESCRIPTION("RMI F01 module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
diff --git a/drivers/input/rmi4/rmi_f01.h b/drivers/input/rmi4/rmi_f01.h
new file mode 100644
index 0000000..34db09f
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f01.h
@@ -0,0 +1,136 @@ 
+/*
+ * 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.
+ */
+
+
+#ifndef _RMI_F01_H
+#define _RMI_F01_H
+
+#define RMI_PRODUCT_ID_LENGTH    10
+
+/**
+ * @manufacturer_id - reports the identity of the manufacturer of the RMI
+ * device. Synaptics RMI devices report a Manufacturer ID of $01.
+ * @custom_map - at least one custom, non
+ * RMI-compatible register exists in the register address map for this device.
+ * @non-compliant - the device implements a register map that is not compliant
+ * with the RMI specification.
+ * @has_lts - the device uses Synaptics' LTS hardware architecture.
+ * @has_sensor_id - the SensorID query register (F01_RMI_Query22) exists.
+ * @has_charger_input - the ChargerConnected bit (F01_RMI_Ctrl0, bit 5) is
+ * meaningful.
+ * @has_adjustable_doze - the doze (power management) control registers exist.
+ * @has_adjustable_doze_holdoff - the doze holdoff register exists.
+ * @has_product_properties - indicates the presence of F01_RMI_Query42,
+ * ProductProperties2.
+ * @productinfo_1 - meaning varies from product to product, consult your
+ * product spec sheet.
+ * @productinfo_2 - meaning varies from product to product, consult your
+ * product spec sheet.
+ * @year - year of manufacture MOD 2000.
+ * @month - month of manufacture
+ * @day - day of manufacture
+ * @wafer_id1_lsb - The wafer-lot ID registers record the lot number of the
+ * wafer from which the module’s touch controller was produced.
+ * @wafer_id1_msb - The wafer-lot ID registers record the lot number of the
+ * wafer from which the module’s touch controller was produced.
+ * @wafer_id2_lsb - The wafer-lot ID registers record the lot number of the
+ * wafer from which the module’s touch controller was produced.
+ * @wafer_id2_msb - The wafer-lot ID registers record the lot number of the
+ * wafer from which the module’s touch controller was produced.
+ * @wafer_id3_lsb - The wafer-lot ID registers record the lot number of the
+ * wafer from which the module’s touch controller was produced.
+ */
+union f01_basic_queries {
+	struct {
+		u8 manufacturer_id:8;
+
+		bool custom_map:1;
+		bool non_compliant:1;
+		bool has_lts:1;
+		bool has_sensor_id:1;
+		bool has_charger_input:1;
+		bool has_adjustable_doze:1;
+		bool has_adjustable_doze_holdoff:1;
+		bool has_product_properties_2:1;
+
+		u8 productinfo_1:7;
+		bool q2_bit_7:1;
+		u8 productinfo_2:7;
+		bool q3_bit_7:1;
+
+		u8 year:5;
+		u8 month:4;
+		u8 day:5;
+		bool cp1:1;
+		bool cp2:1;
+		u8 wafer_id1_lsb:8;
+		u8 wafer_id1_msb:8;
+		u8 wafer_id2_lsb:8;
+		u8 wafer_id2_msb:8;
+		u8 wafer_id3_lsb:8;
+	} __attribute__((__packed__));
+	u8 regs[11];
+};
+
+union f01_device_status {
+	struct {
+		u8 status_code:4;
+		u8 reserved:2;
+		bool flash_prog:1;
+		bool unconfigured:1;
+	} __attribute__((__packed__));
+	u8 regs[1];
+};
+
+/* control register bits */
+#define RMI_SLEEP_MODE_NORMAL (0x00)
+#define RMI_SLEEP_MODE_SENSOR_SLEEP (0x01)
+#define RMI_SLEEP_MODE_RESERVED0 (0x02)
+#define RMI_SLEEP_MODE_RESERVED1 (0x03)
+
+#define RMI_IS_VALID_SLEEPMODE(mode) \
+	(mode >= RMI_SLEEP_MODE_NORMAL && mode <= RMI_SLEEP_MODE_RESERVED1)
+
+/**
+ * @sleep_mode - This field controls power management on the device. This
+ * field affects all functions of the device together.
+ * @nosleep - When set to ‘1’, this bit disables whatever sleep mode may be
+ * selected by the sleep_mode field,and forces the device to run at full power
+ * without sleeping.
+ * @charger_input - When this bit is set to ‘1’, the touch controller employs
+ * a noise-filtering algorithm designed for use with a connected battery
+ * charger.
+ * @report_rate - sets the report rate for the device.  The effect of this
+ * setting is highly product dependent.  Check the spec sheet for your
+ * particular touch sensor.
+ * @configured - written by the host as an indicator that the device has been
+ * successfuly configured.
+ */
+union f01_device_control_0 {
+	struct {
+		u8 sleep_mode:2;
+		bool nosleep:1;
+		u8 reserved:2;
+		bool charger_input:1;
+		bool report_rate:1;
+		bool configured:1;
+	} __attribute__((__packed__));
+	u8 regs[1];
+};
+
+#endif