summaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authorAli Ekici <aekici@nvidia.com>2012-01-11 14:10:13 -0800
committerVarun Colbert <vcolbert@nvidia.com>2012-01-30 13:54:13 -0800
commit2780d644bd2373de349e01631ed8af37dbdc7091 (patch)
tree5914f0c71f153c4f248d940e18c7e28c3dfcd1d9 /drivers/input
parentf3c3d72daf4a33e45bf586aac77079bda8b9fa96 (diff)
input: touchscreen: DirectTouch Synaptics driver
This is Synaptics' open source driver except one line change to accomodate an SPI kernel driver feature which does not accomodate optional byte-delay. It will be fully original driver when we add byte-delay into Tegra SPI kernel drivers. Bug 912775 Reviewed-on: http://git-master/r/74642 Change-Id: I0f39928c48bc0e633de0d2c1f595bcef47268d52 Signed-off-by: Peter Zu <pzu@nvidia.com> Signed-off-by: Varun Wadekar <vwadekar@nvidia.com> Reviewed-on: http://git-master/r/77774 Reviewed-by: Automatic_Commit_Validation_User
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/touchscreen/Kconfig12
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/rmi4/Makefile18
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_bus.c315
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_dev.c428
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_driver.c1354
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_driver.h109
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_f01.c775
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_f09.c298
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_f11.c1513
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_f19.c1419
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_f34.c821
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_f54.c1347
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_i2c.c421
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_spi.c869
15 files changed, 9700 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index a4523838cacf..a8fc74f730dd 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -758,4 +758,16 @@ config TOUCHSCREEN_RM31080A
To compile this driver as a module, choose M here: the
module will be called RAYDIUM_31080A.
+config TOUCHSCREEN_SYN_RMI4_SPI
+ tristate "RMI4 SPI Support"
+ depends on SPI_MASTER
+ help
+ Say Y here if you want to support RMI4 devices connect
+ to an SPI bus.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rmi-spi.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 8a38afe36425..9c42d2100b5c 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -63,3 +63,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
obj-$(CONFIG_TOUCHSCREEN_RM31080A) += rm31080a_ts.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi4/
diff --git a/drivers/input/touchscreen/rmi4/Makefile b/drivers/input/touchscreen/rmi4/Makefile
new file mode 100644
index 000000000000..a7375ed07a39
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for Synaptics Touchscreen (RMI4/SPI)
+#
+GCOV_PROFILE := y
+
+ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
+
+#Synaptics SPI Sensor (2002)
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_bus.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_spi.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_driver.o rmi_f01.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f09.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f11.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f19.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f34.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f54.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_dev.o
diff --git a/drivers/input/touchscreen/rmi4/rmi_bus.c b/drivers/input/touchscreen/rmi4/rmi_bus.c
new file mode 100644
index 000000000000..6a269df4ff35
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_bus.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2011 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/device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/rmi.h>
+
+static struct rmi_function_list {
+ struct list_head list;
+ struct rmi_function_handler *fh;
+} rmi_supported_functions;
+
+static int rmi_bus_match(struct device *dev, struct device_driver *driver)
+{
+ struct rmi_driver *rmi_driver;
+ struct rmi_device *rmi_dev;
+ struct rmi_device_platform_data *pdata;
+
+ pr_info("in function ____%s____ \n", __func__);
+ rmi_driver = to_rmi_driver(driver);
+ rmi_dev = to_rmi_device(dev);
+ pdata = to_rmi_platform_data(rmi_dev);
+
+ pr_info(" rmi_driver->driver.name = %s\n", rmi_driver->driver.name);
+ pr_info(" device:rmi_device = 0x%x \n", rmi_dev);
+ pr_info(" device:rmi_device:rmi_device_platform_data:driver_name = %s \n", pdata->driver_name);
+ pr_info(" rmi_device:driver = 0x%x \n", rmi_dev->driver);
+
+ if (!strcmp(pdata->driver_name, rmi_driver->driver.name)) {
+ rmi_dev->driver = rmi_driver;
+ pr_info(" names match, so now rmi_device:driver = 0x%x \n",rmi_dev->driver);
+ return 1;
+ }
+ pr_info(" names DO NOT match, so return nothing \n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rmi_bus_suspend(struct device *dev)
+{
+#ifdef GENERIC_SUBSYS_PM_OPS
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (pm && pm->suspend)
+ return pm->suspend(dev);
+#endif
+
+ return 0;
+}
+
+static int rmi_bus_resume(struct device *dev)
+{
+#ifdef GENERIC_SUBSYS_PM_OPS
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ pr_info("in function ____%s____ \n", __func__);
+
+ if (pm && pm->resume)
+ return pm->resume(dev);
+#endif
+
+ return 0;
+}
+#endif
+
+static int rmi_bus_probe(struct device *dev)
+{
+ struct rmi_driver *driver;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ pr_info("in function ____%s____ \n", __func__);
+ driver = rmi_dev->driver;
+ if (driver && driver->probe)
+ return driver->probe(rmi_dev);
+
+ return 0;
+}
+
+static int rmi_bus_remove(struct device *dev)
+{
+ struct rmi_driver *driver;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ pr_info("in function ____%s____ \n", __func__);
+ driver = rmi_dev->driver;
+ if (driver && driver->remove)
+ return driver->remove(rmi_dev);
+
+ return 0;
+}
+
+static void rmi_bus_shutdown(struct device *dev)
+{
+ struct rmi_driver *driver;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ driver = rmi_dev->driver;
+ if (driver && driver->shutdown)
+ driver->shutdown(rmi_dev);
+}
+
+static SIMPLE_DEV_PM_OPS(rmi_bus_pm_ops,
+ rmi_bus_suspend, rmi_bus_resume);
+
+struct bus_type rmi_bus_type = {
+ .name = "rmi",
+ .match = rmi_bus_match,
+ .probe = rmi_bus_probe,
+ .remove = rmi_bus_remove,
+ .shutdown = rmi_bus_shutdown,
+ .pm = &rmi_bus_pm_ops
+};
+
+int rmi_register_phys_device(struct rmi_phys_device *phys)
+{
+ static int phys_device_num;
+ struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+ struct rmi_device *rmi_dev;
+
+ pr_info("in function ____%s____ \n", __func__);
+
+ if (!pdata) {
+ dev_err(phys->dev, "no platform data!\n");
+ return -EINVAL;
+ }
+
+ rmi_dev = kzalloc(sizeof(struct rmi_device), GFP_KERNEL);
+ if (!rmi_dev)
+ return -ENOMEM;
+
+ rmi_dev->phys = phys;
+ rmi_dev->dev.bus = &rmi_bus_type;
+ dev_set_name(&rmi_dev->dev, "sensor%02d", phys_device_num++);
+
+ phys->rmi_dev = rmi_dev;
+ pr_info(" registering physical device:\n");
+ pr_info(" dev.init_name = \n", rmi_dev->dev.init_name);
+ pr_info(" dev.bus->name = \n", rmi_dev->dev.bus->name);
+ return device_register(&rmi_dev->dev);
+}
+EXPORT_SYMBOL(rmi_register_phys_device);
+
+void rmi_unregister_phys_device(struct rmi_phys_device *phys)
+{
+ struct rmi_device *rmi_dev = phys->rmi_dev;
+ pr_info("in function ____%s____ \n", __func__);
+
+ device_unregister(&rmi_dev->dev);
+ kfree(rmi_dev);
+}
+EXPORT_SYMBOL(rmi_unregister_phys_device);
+
+int rmi_register_driver(struct rmi_driver *driver)
+{
+ pr_info("in function ____%s____ \n", __func__);
+ driver->driver.bus = &rmi_bus_type;
+ return driver_register(&driver->driver);
+}
+EXPORT_SYMBOL(rmi_register_driver);
+
+static int __rmi_driver_remove(struct device *dev, void *data)
+{
+ struct rmi_driver *driver = data;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ if (rmi_dev->driver == driver)
+ rmi_dev->driver = NULL;
+
+ return 0;
+}
+
+void rmi_unregister_driver(struct rmi_driver *driver)
+{
+ bus_for_each_dev(&rmi_bus_type, NULL, driver, __rmi_driver_remove);
+ driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL(rmi_unregister_driver);
+
+static int __rmi_bus_fh_add(struct device *dev, void *data)
+{
+ struct rmi_driver *driver;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ pr_info("in function ____%s____ \n", __func__);
+
+ driver = rmi_dev->driver;
+ if (driver && driver->fh_add)
+ driver->fh_add(rmi_dev, data);
+
+ return 0;
+}
+
+int rmi_register_function_driver(struct rmi_function_handler *fh)
+{
+ struct rmi_function_list *entry;
+ struct rmi_function_handler *fh_dup;
+
+ fh_dup = rmi_get_function_handler(fh->func);
+ if (fh_dup) {
+ pr_err("%s: function f%.2x already registered!\n", __func__,
+ fh->func);
+ return -EINVAL;
+ }
+
+ entry = kzalloc(sizeof(struct rmi_function_list), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->fh = fh;
+ list_add_tail(&entry->list, &rmi_supported_functions.list);
+
+ /* notify devices of the new function handler */
+ bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_add);
+
+ return 0;
+}
+EXPORT_SYMBOL(rmi_register_function_driver);
+
+static int __rmi_bus_fh_remove(struct device *dev, void *data)
+{
+ struct rmi_driver *driver;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ pr_info("in function ____%s____ \n", __func__);
+ driver = rmi_dev->driver;
+ if (driver && driver->fh_remove)
+ driver->fh_remove(rmi_dev, data);
+
+ return 0;
+}
+
+void rmi_unregister_function_driver(struct rmi_function_handler *fh)
+{
+ struct rmi_function_list *entry, *n;
+ pr_info("in function ____%s____ \n", __func__);
+
+ /* notify devices of the removal of the function handler */
+ bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_remove);
+
+ list_for_each_entry_safe(entry, n, &rmi_supported_functions.list, list)
+ if (entry->fh->func == fh->func) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+}
+EXPORT_SYMBOL(rmi_unregister_function_driver);
+
+struct rmi_function_handler *rmi_get_function_handler(int id)
+{
+ struct rmi_function_list *entry;
+ pr_info("in function ____%s____ \n", __func__);
+
+ list_for_each_entry(entry, &rmi_supported_functions.list, list)
+ if (entry->fh->func == id)
+ return entry->fh;
+
+ return NULL;
+}
+EXPORT_SYMBOL(rmi_get_function_handler);
+
+static int __init rmi_bus_init(void)
+{
+ int error;
+
+ pr_info("in function ____%s____ \n", __func__);
+ INIT_LIST_HEAD(&rmi_supported_functions.list);
+
+ error = bus_register(&rmi_bus_type);
+ if (error < 0) {
+ pr_err("%s: error registering the RMI bus: %d\n", __func__,
+ error);
+ return error;
+ }
+ pr_info("%s: successfully registered RMI bus.\n", __func__);
+
+ return 0;
+}
+
+static void __exit rmi_bus_exit(void)
+{
+ struct rmi_function_list *entry, *n;
+ pr_info("in function ____%s____ \n", __func__);
+
+ list_for_each_entry_safe(entry, n, &rmi_supported_functions.list,
+ list) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+
+ bus_unregister(&rmi_bus_type);
+}
+
+module_init(rmi_bus_init);
+module_exit(rmi_bus_exit);
+
+MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
+MODULE_DESCRIPTION("RMI bus");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_dev.c b/drivers/input/touchscreen/rmi4/rmi_dev.c
new file mode 100644
index 000000000000..fa21ef9b20fa
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_dev.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2011 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/fs.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/syscalls.h>
+
+#include <linux/rmi.h>
+#include "rmi_driver.h"
+
+#define CHAR_DEVICE_NAME "rmi"
+
+#define REG_ADDR_LIMIT 0xFFFF
+
+/*store dynamically allocated major number of char device*/
+static int rmi_char_dev_major_num;
+
+
+/* file operations for RMI char device */
+
+/*
+ * rmi_char_dev_llseek: - use to setup register address
+ *
+ * @filp: file structure for seek
+ * @off: offset
+ * if whence == SEEK_SET,
+ * high 16 bits: page address
+ * low 16 bits: register address
+ *
+ * if whence == SEEK_CUR,
+ * offset from current position
+ *
+ * if whence == SEEK_END,
+ * offset from END(0xFFFF)
+ *
+ * @whence: SEEK_SET , SEEK_CUR or SEEK_END
+ */
+static loff_t rmi_char_dev_llseek(struct file *filp, loff_t off, int whence)
+{
+ loff_t newpos;
+ struct rmi_char_dev *my_char_dev = filp->private_data;
+
+ if (IS_ERR(my_char_dev)) {
+ pr_err("%s: pointer of char device is invalid", __func__);
+ return -EBADF;
+ }
+
+ mutex_lock(&(my_char_dev->mutex_file_op));
+
+ switch (whence) {
+ case SEEK_SET:
+ newpos = off;
+ break;
+
+ case SEEK_CUR:
+ newpos = filp->f_pos + off;
+ break;
+
+ case SEEK_END:
+ newpos = REG_ADDR_LIMIT + off;
+ break;
+
+ default: /* can't happen */
+ newpos = -EINVAL;
+ goto clean_up;
+ }
+
+ if (newpos < 0 || newpos > REG_ADDR_LIMIT) {
+ dev_err(my_char_dev->phys->dev, "newpos 0x%04x is invalid.\n",
+ (unsigned int)newpos);
+ newpos = -EINVAL;
+ goto clean_up;
+ }
+
+ filp->f_pos = newpos;
+
+clean_up:
+ mutex_unlock(&(my_char_dev->mutex_file_op));
+ return newpos;
+}
+
+/*
+ * rmi_char_dev_read: - use to read data from RMI stream
+ *
+ * @filp: file structure for read
+ * @buf: user-level buffer pointer
+ *
+ * @count: number of byte read
+ * @f_pos: offset (starting register address)
+ *
+ * @return number of bytes read into user buffer (buf) if succeeds
+ * negative number if error occurs.
+ */
+static ssize_t rmi_char_dev_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct rmi_char_dev *my_char_dev = filp->private_data;
+ ssize_t ret_value = 0;
+ unsigned char tmpbuf[count+1];
+ struct rmi_phys_device *phys;
+
+ /* limit offset to REG_ADDR_LIMIT-1 */
+ if (count > (REG_ADDR_LIMIT - *f_pos))
+ count = REG_ADDR_LIMIT - *f_pos;
+
+ if (count == 0)
+ return 0;
+
+ if (IS_ERR(my_char_dev)) {
+ pr_err("%s: pointer of char device is invalid", __func__);
+ ret_value = -EBADF;
+ return ret_value;
+ }
+
+ mutex_lock(&(my_char_dev->mutex_file_op));
+
+ phys = my_char_dev->phys;
+ /*
+ * just let it go through , because we do not know the register is FIFO
+ * register or not
+ */
+
+ ret_value = phys->read_block(phys, *f_pos, tmpbuf, count);
+
+ if (ret_value < 0)
+ goto clean_up;
+ else
+ *f_pos += ret_value;
+
+ if (copy_to_user(buf, tmpbuf, count))
+ ret_value = -EFAULT;
+
+clean_up:
+
+ mutex_unlock(&(my_char_dev->mutex_file_op));
+
+ return ret_value;
+}
+
+/*
+ * rmi_char_dev_write: - use to write data into RMI stream
+ *
+ * @filep : file structure for write
+ * @buf: user-level buffer pointer contains data to be written
+ * @count: number of byte be be written
+ * @f_pos: offset (starting register address)
+ *
+ * @return number of bytes written from user buffer (buf) if succeeds
+ * negative number if error occurs.
+ */
+static ssize_t rmi_char_dev_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct rmi_char_dev *my_char_dev = filp->private_data;
+ ssize_t ret_value = 0;
+ unsigned char tmpbuf[count+1];
+ struct rmi_phys_device *phys;
+
+ /* limit offset to REG_ADDR_LIMIT-1 */
+ if (count > (REG_ADDR_LIMIT - *f_pos))
+ count = REG_ADDR_LIMIT - *f_pos;
+
+ if (count == 0)
+ return 0;
+
+ if (IS_ERR(my_char_dev)) {
+ pr_err("%s: pointer of char device is invalid", __func__);
+ ret_value = -EBADF;
+ return ret_value;
+ }
+
+ if (copy_from_user(tmpbuf, buf, count)) {
+ ret_value = -EFAULT;
+ return ret_value;
+ }
+
+ mutex_lock(&(my_char_dev->mutex_file_op));
+
+ phys = my_char_dev->phys;
+ /*
+ * just let it go through , because we do not know the register is FIFO
+ * register or not
+ */
+
+ ret_value = phys->write_block(phys, *f_pos, tmpbuf, count);
+
+ if (ret_value >= 0)
+ *f_pos += count;
+
+ mutex_unlock(&(my_char_dev->mutex_file_op));
+
+ return ret_value;
+}
+
+/*
+ * rmi_char_dev_open: - get a new handle for from RMI stream
+ * @inp : inode struture
+ * @filp: file structure for read/write
+ *
+ * @return 0 if succeeds
+ */
+static int rmi_char_dev_open(struct inode *inp, struct file *filp)
+{
+ /* store the device pointer to file structure */
+ struct rmi_char_dev *my_dev = container_of(inp->i_cdev,
+ struct rmi_char_dev, main_dev);
+ struct rmi_phys_device *phys = my_dev->phys;
+ int ret_value = 0;
+
+ filp->private_data = my_dev;
+
+ if (!phys)
+ return -EACCES;
+
+ mutex_lock(&(my_dev->mutex_file_op));
+ if (my_dev->ref_count < 1)
+ my_dev->ref_count++;
+ else
+ ret_value = -EACCES;
+
+ mutex_unlock(&(my_dev->mutex_file_op));
+
+ return ret_value;
+}
+
+/*
+ * rmi_char_dev_release: - release an existing handle
+ * @inp: inode structure
+ * @filp: file structure for read/write
+ *
+ * @return 0 if succeeds
+ */
+static int rmi_char_dev_release(struct inode *inp, struct file *filp)
+{
+ struct rmi_char_dev *my_dev = container_of(inp->i_cdev,
+ struct rmi_char_dev, main_dev);
+ struct rmi_phys_device *phys = my_dev->phys;
+
+ if (!phys)
+ return -EACCES;
+
+ mutex_lock(&(my_dev->mutex_file_op));
+
+ my_dev->ref_count--;
+ if (my_dev->ref_count < 0)
+ my_dev->ref_count = 0;
+
+ mutex_unlock(&(my_dev->mutex_file_op));
+
+ return 0;
+}
+
+static const struct file_operations rmi_char_dev_fops = {
+ .owner = THIS_MODULE,
+ .llseek = rmi_char_dev_llseek,
+ .read = rmi_char_dev_read,
+ .write = rmi_char_dev_write,
+ .open = rmi_char_dev_open,
+ .release = rmi_char_dev_release,
+};
+
+/*
+ * rmi_char_dev_clean_up - release memory or unregister driver
+ * @rmi_char_dev: rmi_char_dev structure
+ *
+ */
+static void rmi_char_dev_clean_up(struct rmi_char_dev *char_dev,
+ struct class *char_device_class)
+{
+ dev_t devno;
+
+ /* Get rid of our char dev entries */
+ if (char_dev) {
+ devno = char_dev->main_dev.dev;
+
+ cdev_del(&char_dev->main_dev);
+ kfree(char_dev);
+
+ if (char_device_class) {
+ device_destroy(char_device_class, devno);
+ class_unregister(char_device_class);
+ class_destroy(char_device_class);
+ }
+
+ /* cleanup_module is never called if registering failed */
+ unregister_chrdev_region(devno, 1);
+ pr_debug("%s: rmi_char_dev is removed\n", __func__);
+ }
+}
+
+/*
+ * rmi_char_devnode - return device permission
+ *
+ * @dev: char device structure
+ * @mode: file permission
+ *
+ */
+static char *rmi_char_devnode(struct device *dev, mode_t *mode)
+{
+ if (!mode)
+ return NULL;
+ /* rmi** */
+ /**mode = 0666*/
+ *mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+
+ return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev));
+}
+
+/*
+ * rmi_char_dev_register - register char device (called from up-level)
+ *
+ * @phy: a pointer to an rmi_phys_devices structure
+ *
+ * @return: zero if suceeds
+ */
+int rmi_char_dev_register(struct rmi_phys_device *phys)
+{
+ struct rmi_char_dev *char_dev;
+ dev_t dev_no;
+ int err;
+ int result;
+ struct device *device_ptr;
+
+ if (rmi_char_dev_major_num) {
+ dev_no = MKDEV(rmi_char_dev_major_num, 0);
+ result = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
+ } else {
+ result = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
+ /* let kernel allocate a major for us */
+ rmi_char_dev_major_num = MAJOR(dev_no);
+ dev_info(phys->dev, "Major number of rmi_char_dev: %d\n",
+ rmi_char_dev_major_num);
+ }
+ if (result < 0)
+ return result;
+
+ char_dev = kzalloc(sizeof(struct rmi_char_dev), GFP_KERNEL);
+ if (!char_dev) {
+ dev_err(phys->dev, "Failed to allocate rmi_char_dev.\n");
+ /* unregister the char device region */
+ __unregister_chrdev(rmi_char_dev_major_num, MINOR(dev_no), 1,
+ CHAR_DEVICE_NAME);
+ return -ENOMEM;
+ }
+
+ mutex_init(&char_dev->mutex_file_op);
+
+ phys->char_dev = char_dev;
+ char_dev->phys = phys;
+
+ cdev_init(&char_dev->main_dev, &rmi_char_dev_fops);
+
+ err = cdev_add(&char_dev->main_dev, dev_no, 1);
+ if (err) {
+ dev_err(phys->dev, "Error %d adding rmi_char_dev.\n", err);
+ rmi_char_dev_clean_up(phys->char_dev,
+ phys->rmi_char_device_class);
+ return err;
+ }
+
+ /* create device node */
+ phys->rmi_char_device_class =
+ class_create(THIS_MODULE, CHAR_DEVICE_NAME);
+
+ if (IS_ERR(phys->rmi_char_device_class)) {
+ dev_err(phys->dev, "Failed to create /dev/%s.\n",
+ CHAR_DEVICE_NAME);
+ rmi_char_dev_clean_up(phys->char_dev,
+ phys->rmi_char_device_class);
+ return -ENODEV;
+ }
+ /* setup permission */
+ phys->rmi_char_device_class->devnode = rmi_char_devnode;
+
+ /* class creation */
+ device_ptr = device_create(
+ phys->rmi_char_device_class,
+ NULL, dev_no, NULL,
+ CHAR_DEVICE_NAME"%d",
+ MINOR(dev_no));
+
+ if (IS_ERR(device_ptr)) {
+ dev_err(phys->dev, "Failed to create rmi device.\n");
+ rmi_char_dev_clean_up(phys->char_dev,
+ phys->rmi_char_device_class);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(rmi_char_dev_register);
+
+/* rmi_char_dev_unregister - unregister char device (called from up-level)
+ *
+ * @phys: pointer to an rmi_phys_device structure
+ */
+
+void rmi_char_dev_unregister(struct rmi_phys_device *phys)
+{
+ /* clean up */
+ if (phys)
+ rmi_char_dev_clean_up(phys->char_dev,
+ phys->rmi_char_device_class);
+}
+EXPORT_SYMBOL(rmi_char_dev_unregister);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("RMI4 Char Device");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_driver.c b/drivers/input/touchscreen/rmi4/rmi_driver.c
new file mode 100644
index 000000000000..6aeb3b93b2c5
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_driver.c
@@ -0,0 +1,1354 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This driver adds support for generic RMI4 devices from Synpatics. It
+ * implements the mandatory f01 RMI register and depends on the presence of
+ * other required RMI functions.
+ *
+ * The RMI4 specification can be found here (URL split after files/ for
+ * style reasons):
+ * http://www.synaptics.com/sites/default/files/
+ * 511-000136-01-Rev-E-RMI4%20Intrfacing%20Guide.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/rmi.h>
+#include "rmi_driver.h"
+
+#define DELAY_DEBUG 0
+#define REGISTER_DEBUG 0
+
+#define PDT_END_SCAN_LOCATION 0x0005
+#define PDT_PROPERTIES_LOCATION 0x00EF
+#define BSR_LOCATION 0x00FE
+#define HAS_BSR_MASK 0x20
+#define HAS_NONSTANDARD_PDT_MASK 0x40
+#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
+#define RMI4_MAX_PAGE 0xff
+#define RMI4_PAGE_SIZE 0x100
+
+#define RMI_DEVICE_RESET_CMD 0x01
+#define INITIAL_RESET_WAIT_MS 20
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void rmi_driver_early_suspend(struct early_suspend *h);
+static void rmi_driver_late_resume(struct early_suspend *h);
+#endif
+
+
+/* sysfs files for attributes for driver values. */
+static ssize_t rmi_driver_hasbsr_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_driver_bsr_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_driver_bsr_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_driver_enabled_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_driver_enabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_driver_phys_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_driver_version_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+#if REGISTER_DEBUG
+static ssize_t rmi_driver_reg_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+#endif
+
+#if DELAY_DEBUG
+static ssize_t rmi_delay_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_delay_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+#endif
+
+static struct device_attribute attrs[] = {
+ __ATTR(hasbsr, RMI_RO_ATTR,
+ rmi_driver_hasbsr_show, rmi_store_error),
+ __ATTR(bsr, RMI_RW_ATTR,
+ rmi_driver_bsr_show, rmi_driver_bsr_store),
+ __ATTR(enabled, RMI_RW_ATTR,
+ rmi_driver_enabled_show, rmi_driver_enabled_store),
+ __ATTR(phys, RMI_RO_ATTR,
+ rmi_driver_phys_show, rmi_store_error),
+#if REGISTER_DEBUG
+ __ATTR(reg, RMI_WO_ATTR,
+ rmi_show_error, rmi_driver_reg_store),
+#endif
+#if DELAY_DEBUG
+ __ATTR(delay, RMI_RW_ATTR,
+ rmi_delay_show, rmi_delay_store),
+#endif
+ __ATTR(version, RMI_RO_ATTR,
+ rmi_driver_version_show, rmi_store_error),
+};
+
+
+/*
+** ONLY needed for POLLING mode of the driver
+*/
+struct rmi_device *polled_synaptics_rmi_device = NULL;
+EXPORT_SYMBOL(polled_synaptics_rmi_device);
+
+/* Useful helper functions for u8* */
+
+void u8_set_bit(u8 *target, int pos)
+{
+ target[pos/8] |= 1<<pos%8;
+}
+
+void u8_clear_bit(u8 *target, int pos)
+{
+ target[pos/8] &= ~(1<<pos%8);
+}
+
+bool u8_is_set(u8 *target, int pos)
+{
+ return target[pos/8] & 1<<pos%8;
+}
+
+bool u8_is_any_set(u8 *target, int size)
+{
+ int i;
+ for (i = 0; i < size; i++) {
+ if (target[i])
+ return true;
+ }
+ return false;
+}
+
+void u8_or(u8 *dest, u8 *target1, u8 *target2, int size)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ dest[i] = target1[i] | target2[i];
+}
+
+void u8_and(u8 *dest, u8 *target1, u8 *target2, int size)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ dest[i] = target1[i] & target2[i];
+}
+
+/* Helper fn to convert a byte array representing a short in the RMI
+ * endian-ness to a short in the native processor's specific endianness.
+ * We don't use ntohs/htons here because, well, we're not dealing with
+ * a pair of shorts. And casting dest to short* wouldn't work, because
+ * that would imply knowing the byte order of short in the first place.
+ */
+void batohs(unsigned short *dest, unsigned char *src)
+{
+ *dest = src[1] * 0x100 + src[0];
+}
+
+/* Helper function to convert a short (in host processor endianess) to
+ * a byte array in the RMI endianess for shorts. See above comment for
+ * why we dont us htons or something like that.
+ */
+void hstoba(unsigned char *dest, unsigned short src)
+{
+ dest[0] = src % 0x100;
+ dest[1] = src / 0x100;
+}
+
+static bool has_bsr(struct rmi_driver_data *data)
+{
+ return (data->pdt_props & HAS_BSR_MASK) != 0;
+}
+
+/* Utility routine to set bits in a register. */
+int rmi_set_bits(struct rmi_device *rmi_dev, unsigned short address,
+ unsigned char bits)
+{
+ unsigned char reg_contents;
+ int retval;
+
+ retval = rmi_read_block(rmi_dev, address, &reg_contents, 1);
+ if (retval)
+ return retval;
+ reg_contents = reg_contents | bits;
+ retval = rmi_write_block(rmi_dev, address, &reg_contents, 1);
+ if (retval == 1)
+ return 0;
+ else if (retval == 0)
+ return -EIO;
+ return retval;
+}
+EXPORT_SYMBOL(rmi_set_bits);
+
+/* Utility routine to clear bits in a register. */
+int rmi_clear_bits(struct rmi_device *rmi_dev, unsigned short address,
+ unsigned char bits)
+{
+ unsigned char reg_contents;
+ int retval;
+
+ retval = rmi_read_block(rmi_dev, address, &reg_contents, 1);
+ if (retval)
+ return retval;
+ reg_contents = reg_contents & ~bits;
+ retval = rmi_write_block(rmi_dev, address, &reg_contents, 1);
+ if (retval == 1)
+ return 0;
+ else if (retval == 0)
+ return -EIO;
+ return retval;
+}
+EXPORT_SYMBOL(rmi_clear_bits);
+
+static void rmi_free_function_list(struct rmi_device *rmi_dev)
+{
+ struct rmi_function_container *entry, *n;
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+ list_for_each_entry_safe(entry, n, &data->rmi_functions.list, list) {
+ kfree(entry->irq_mask);
+ list_del(&entry->list);
+ }
+}
+
+static int init_one_function(struct rmi_device *rmi_dev,
+ struct rmi_function_container *fc)
+{
+ int retval;
+
+ dev_info(&rmi_dev->dev, "Initializing F%02X.\n",
+ fc->fd.function_number);
+ dev_dbg(&rmi_dev->dev, "Initializing F%02X.\n",
+ fc->fd.function_number);
+
+ if (!fc->fh) {
+ struct rmi_function_handler *fh =
+ rmi_get_function_handler(fc->fd.function_number);
+ if (!fh) {
+ dev_dbg(&rmi_dev->dev, "No handler for F%02X.\n",
+ fc->fd.function_number);
+ return 0;
+ }
+ fc->fh = fh;
+ }
+
+ if (!fc->fh->init)
+ return 0;
+
+ dev_set_name(&(fc->dev), "fn%02x", fc->fd.function_number);
+
+ fc->dev.parent = &rmi_dev->dev;
+
+ retval = device_register(&fc->dev);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "Failed device_register for F%02X.\n",
+ fc->fd.function_number);
+ return retval;
+ }
+
+ retval = fc->fh->init(fc);
+ if (retval < 0) {
+ dev_err(&rmi_dev->dev, "Failed to initialize function F%02x\n",
+ fc->fd.function_number);
+ goto error_exit;
+ }
+
+ return 0;
+
+error_exit:
+ device_unregister(&fc->dev);
+ return retval;
+}
+
+static void rmi_driver_fh_add(struct rmi_device *rmi_dev,
+ struct rmi_function_handler *fh)
+{
+ struct rmi_function_container *entry;
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+ if (fh->func == 0x01)
+ data->f01_container->fh = fh;
+ else {
+ list_for_each_entry(entry, &data->rmi_functions.list, list)
+ if (entry->fd.function_number == fh->func) {
+ entry->fh = fh;
+ init_one_function(rmi_dev, entry);
+ }
+ }
+
+}
+
+static void rmi_driver_fh_remove(struct rmi_device *rmi_dev,
+ struct rmi_function_handler *fh)
+{
+ struct rmi_function_container *entry;
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list)
+ if (entry->fd.function_number == fh->func) {
+ if (fh->remove)
+ fh->remove(entry);
+
+ entry->fh = NULL;
+ }
+}
+
+static void construct_mask(u8 *mask, int num, int pos)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ u8_set_bit(mask, pos+i);
+}
+
+static int process_interrupt_requests(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+ struct device *dev = &rmi_dev->dev;
+ struct rmi_function_container *entry;
+ u8 irq_status[data->num_of_irq_regs];
+ u8 irq_bits[data->num_of_irq_regs];
+ int error;
+
+ error = rmi_read_block(rmi_dev,
+ data->f01_container->fd.data_base_addr + 1,
+ irq_status, data->num_of_irq_regs);
+ if (error < 0) {
+ dev_err(dev, "%s: failed to read irqs.", __func__);
+ return error;
+ }
+ /* Device control (F01) is handled before anything else. */
+ u8_and(irq_bits, irq_status, data->f01_container->irq_mask,
+ data->num_of_irq_regs);
+ if (u8_is_any_set(irq_bits, data->num_of_irq_regs))
+ data->f01_container->fh->attention(
+ data->f01_container, irq_bits);
+
+ //dev_info(dev, " irq_status = 0x%2x data->current_irq_mask = 0x%2x data->num_of_irq_regs = %d\n",
+ // irq_status[0], data->current_irq_mask[0], data->num_of_irq_regs );
+
+
+ u8_and(irq_status, irq_status, data->current_irq_mask,
+ data->num_of_irq_regs);
+
+ /* At this point, irq_status has all bits that are set in the
+ * interrupt status register and are enabled.
+ */
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list){
+ if (entry->irq_mask && entry->fh && entry->fh->attention) {
+
+ u8_and(irq_bits, irq_status, entry->irq_mask,
+ data->num_of_irq_regs);
+ if (u8_is_any_set(irq_bits, data->num_of_irq_regs)) {
+ error = entry->fh->attention(entry, irq_bits);
+ if (error < 0)
+ dev_err(dev, "%s: f%.2x"
+ " attention handler failed:"
+ " %d\n", __func__,
+ entry->fh->func, error);
+ }
+ }
+ }
+ return 0;
+}
+
+
+static int rmi_driver_irq_handler(struct rmi_device *rmi_dev, int irq)
+{
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+ /* Can get called before the driver is fully ready to deal with
+ * interrupts.
+ */
+ if (!data || !data->f01_container || !data->f01_container->fh) {
+ dev_warn(&rmi_dev->dev,
+ "Not ready to handle interrupts yet!\n");
+ return 0;
+ }
+
+ return process_interrupt_requests(rmi_dev);
+}
+
+/*
+ * Construct a function's IRQ mask. This should
+ * be called once and stored.
+ */
+static u8 *rmi_driver_irq_get_mask(struct rmi_device *rmi_dev,
+ struct rmi_function_container *fc) {
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+ /* TODO: Where should this be freed? */
+ u8 *irq_mask = kzalloc(sizeof(u8) * data->num_of_irq_regs, GFP_KERNEL);
+ if (irq_mask)
+ construct_mask(irq_mask, fc->num_of_irqs, fc->irq_pos);
+
+ return irq_mask;
+}
+
+/*
+ * This pair of functions allows functions like function 54 to request to have
+ * other interupts disabled until the restore function is called. Only one store
+ * happens at a time.
+ */
+static int rmi_driver_irq_save(struct rmi_device *rmi_dev, u8 * new_ints)
+{
+ int retval = 0;
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+ struct device *dev = &rmi_dev->dev;
+
+ mutex_lock(&data->irq_mutex);
+ if (!data->irq_stored) {
+ /* Save current enabled interupts */
+ retval = rmi_read_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ data->irq_mask_store, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to read enabled interrupts!",
+ __func__);
+ goto error_unlock;
+ }
+ /*
+ * Disable every interupt except for function 54
+ * TODO:Will also want to not disable function 1-like functions.
+ * No need to take care of this now, since there's no good way
+ * to identify them.
+ */
+ retval = rmi_write_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ new_ints, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to change enabled interrupts!",
+ __func__);
+ goto error_unlock;
+ }
+ memcpy(data->current_irq_mask, new_ints,
+ data->num_of_irq_regs * sizeof(u8));
+ data->irq_stored = true;
+ } else {
+ retval = -ENOSPC; /* No space to store IRQs.*/
+ dev_err(dev, "%s: Attempted to save values when"
+ " already stored!", __func__);
+ }
+
+error_unlock:
+ mutex_unlock(&data->irq_mutex);
+ return retval;
+}
+
+static int rmi_driver_irq_restore(struct rmi_device *rmi_dev)
+{
+ int retval = 0;
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+ struct device *dev = &rmi_dev->dev;
+ mutex_lock(&data->irq_mutex);
+
+ if (data->irq_stored) {
+ retval = rmi_write_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ data->irq_mask_store, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to write enabled interupts!",
+ __func__);
+ goto error_unlock;
+ }
+ memcpy(data->current_irq_mask, data->irq_mask_store,
+ data->num_of_irq_regs * sizeof(u8));
+ data->irq_stored = false;
+ } else {
+ retval = -EINVAL;
+ dev_err(dev, "%s: Attempted to restore values when not stored!",
+ __func__);
+ }
+
+error_unlock:
+ mutex_unlock(&data->irq_mutex);
+ return retval;
+}
+
+static int rmi_driver_fn_01_specific(struct rmi_device *rmi_dev,
+ struct pdt_entry *pdt_ptr,
+ int *current_irq_count,
+ u16 page_start)
+{
+ struct rmi_driver_data *data = NULL;
+ struct rmi_function_container *fc = NULL;
+ int retval = 0;
+ struct device *dev = &rmi_dev->dev;
+ struct rmi_function_handler *fh =
+ rmi_get_function_handler(0x01);
+
+ data = rmi_get_driverdata(rmi_dev);
+
+ dev_info(dev, "%s: Found F01, initializing.\n", __func__);
+ if (!fh)
+ dev_dbg(dev, "%s: No function handler for F01?!", __func__);
+
+ fc = kzalloc(sizeof(struct rmi_function_container), GFP_KERNEL);
+ if (!fc) {
+ retval = -ENOMEM;
+ return retval;
+ }
+
+ copy_pdt_entry_to_fd(pdt_ptr, &fc->fd, page_start);
+ fc->num_of_irqs = pdt_ptr->interrupt_source_count;
+ fc->irq_pos = *current_irq_count;
+
+ *current_irq_count += fc->num_of_irqs;
+
+ fc->rmi_dev = rmi_dev;
+ fc->dev.parent = &fc->rmi_dev->dev;
+ fc->fh = fh;
+
+ dev_set_name(&(fc->dev), "fn%02x", fc->fd.function_number);
+
+ retval = device_register(&fc->dev);
+ if (retval) {
+ dev_err(dev, "%s: Failed device_register for F01.\n", __func__);
+ goto error_free_data;
+ }
+
+ data->f01_container = fc;
+
+ return retval;
+
+error_free_data:
+ kfree(fc);
+ return retval;
+}
+
+/*
+ * Scan the PDT for F01 so we can force a reset before anything else
+ * is done. This forces the sensor into a known state, and also
+ * forces application of any pending updates from reflashing the
+ * firmware or configuration. We have to do this before actually
+ * building the PDT because the reflash might cause various registers
+ * to move around.
+ */
+static int do_initial_reset(struct rmi_device* rmi_dev)
+{
+ struct pdt_entry pdt_entry;
+ int page;
+ struct device *dev = &rmi_dev->dev;
+ bool done = false;
+ int i;
+ int retval;
+
+ pr_info("in function ____%s____ \n", __func__);
+
+ polled_synaptics_rmi_device = rmi_dev;
+
+ for (page = 0; (page <= RMI4_MAX_PAGE) && !done; page++) {
+
+ u16 page_start = RMI4_PAGE_SIZE * page;
+ u16 pdt_start = page_start + PDT_START_SCAN_LOCATION;
+ u16 pdt_end = page_start + PDT_END_SCAN_LOCATION;
+ pr_info(" reading page = %d\n", page );
+ done = true;
+ for (i = pdt_start; i >= pdt_end; i -= sizeof(pdt_entry)) {
+
+ pr_info(" reading PDT entry %3d (block %3d)\n",
+ i%sizeof(pdt_entry), i);
+
+ retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry,
+ sizeof(pdt_entry));
+ if (retval != sizeof(pdt_entry)) {
+ dev_err(dev, "Read PDT entry at 0x%04x"
+ "failed, code = %d.\n", i, retval);
+ return retval;
+ }
+
+ if (RMI4_END_OF_PDT(pdt_entry.function_number))
+ break;
+ done = false;
+
+ if (pdt_entry.function_number == 0x01) {
+ u16 cmd_addr = page_start +
+ pdt_entry.command_base_addr;
+ u8 cmd_buf = RMI_DEVICE_RESET_CMD;
+ retval = rmi_write_block(rmi_dev, cmd_addr,
+ &cmd_buf, 1);
+ if (retval < 0) {
+ dev_err(dev, "Initial reset failed. "
+ "Code = %d.\n", retval);
+ return retval;
+ }
+ mdelay(INITIAL_RESET_WAIT_MS);
+ return 0;
+ }
+ }
+ }
+
+ dev_warn(dev, "WARNING: Failed to find F01 for initial reset.\n");
+ return -ENODEV;
+}
+
+
+static int rmi_scan_pdt(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data;
+ struct rmi_function_container *fc;
+ struct pdt_entry pdt_entry;
+ int page;
+ struct device *dev = &rmi_dev->dev;
+ int irq_count = 0;
+ bool done = false;
+ int i;
+ int retval;
+ pr_info("in function ____%s____ \n", __func__);
+ pr_info(" doing initial reset \n");
+
+ retval = do_initial_reset(rmi_dev);
+ pr_info(" back in %s \n", __func__);
+
+ if (retval)
+ dev_err(dev, "WARNING: Initial reset failed! Soldiering on.\n");
+
+ data = rmi_get_driverdata(rmi_dev);
+
+ INIT_LIST_HEAD(&data->rmi_functions.list);
+
+ /* parse the PDT */
+ for (page = 0; (page <= RMI4_MAX_PAGE) && !done; page++) {
+ u16 page_start = RMI4_PAGE_SIZE * page;
+ u16 pdt_start = page_start + PDT_START_SCAN_LOCATION;
+ u16 pdt_end = page_start + PDT_END_SCAN_LOCATION;
+
+ done = true;
+ for (i = pdt_start; i >= pdt_end; i -= sizeof(pdt_entry)) {
+ retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry,
+ sizeof(pdt_entry));
+ if (retval != sizeof(pdt_entry)) {
+ dev_err(dev, "Read PDT entry at 0x%04x"
+ "failed.\n", i);
+ goto error_exit;
+ }
+
+ if (RMI4_END_OF_PDT(pdt_entry.function_number))
+ break;
+
+ dev_dbg(dev, "%s: Found F%.2X on page 0x%02X\n",
+ __func__, pdt_entry.function_number, page);
+ done = false;
+
+ /*
+ * F01 is handled by rmi_driver. Hopefully we will get
+ * rid of the special treatment of f01 at some point
+ * in time.
+ */
+ if (pdt_entry.function_number == 0x01) {
+ retval = rmi_driver_fn_01_specific(rmi_dev,
+ &pdt_entry, &irq_count,
+ page_start);
+ if (retval)
+ goto error_exit;
+ continue;
+ }
+
+ fc = kzalloc(sizeof(struct rmi_function_container),
+ GFP_KERNEL);
+ if (!fc) {
+ dev_err(dev, "Failed to allocate function "
+ "container for F%02X.\n",
+ pdt_entry.function_number);
+ retval = -ENOMEM;
+ goto error_exit;
+ }
+
+ copy_pdt_entry_to_fd(&pdt_entry, &fc->fd, page_start);
+
+ fc->rmi_dev = rmi_dev;
+ fc->num_of_irqs = pdt_entry.interrupt_source_count;
+ fc->irq_pos = irq_count;
+ irq_count += fc->num_of_irqs;
+
+ retval = init_one_function(rmi_dev, fc);
+ if (retval < 0) {
+ dev_err(dev, "Failed to initialize F%.2x\n",
+ pdt_entry.function_number);
+ kfree(fc);
+ goto error_exit;
+ }
+
+ list_add_tail(&fc->list, &data->rmi_functions.list);
+ }
+ }
+ data->num_of_irq_regs = (irq_count + 7) / 8;
+ dev_dbg(dev, "%s: Done with PDT scan.\n", __func__);
+ return 0;
+
+error_exit:
+ return retval;
+}
+
+
+#ifdef SYNAPTICS_SENSOR_POLL
+void synaptics_sensor_poller(unsigned long data){
+ pr_info("in function ____%s____ , rmi_device= 0x%8x \n", __func__, polled_synaptics_rmi_device);
+ // msleep(10000);
+ for (;;) {
+ msleep(100);
+ rmi_driver_irq_handler(polled_synaptics_rmi_device, 0);
+ }
+
+ return;
+}
+
+struct workqueue_struct *synaptics_rmi_polling_queue = NULL;
+struct delayed_work synaptics_rmi_polling_work;
+
+#endif
+
+
+static int rmi_driver_probe(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data;
+ struct rmi_function_container *fc;
+ struct rmi_device_platform_data *pdata;
+ int error = 0;
+ struct device *dev = &rmi_dev->dev;
+ int attr_count = 0;
+
+ dev_dbg(dev, "%s: Starting probe.\n", __func__);
+
+ pdata = to_rmi_platform_data(rmi_dev);
+
+ data = kzalloc(sizeof(struct rmi_driver_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(dev, "%s: Failed to allocate driver data.\n", __func__);
+ return -ENOMEM;
+ }
+
+ rmi_set_driverdata(rmi_dev, data);
+
+ error = rmi_scan_pdt(rmi_dev);
+ if (error) {
+ dev_err(dev, "PDT scan failed with code %d.\n", error);
+ goto err_free_data;
+ }
+
+ if (!data->f01_container) {
+ dev_err(dev, "missing f01 function!\n");
+ error = -EINVAL;
+ goto err_free_data;
+ }
+
+ data->f01_container->irq_mask = kzalloc(
+ sizeof(u8) * data->num_of_irq_regs, GFP_KERNEL);
+ if (!data->f01_container->irq_mask) {
+ dev_err(dev, "Failed to allocate F01 IRQ mask.\n");
+ error = -ENOMEM;
+ goto err_free_data;
+ }
+ construct_mask(data->f01_container->irq_mask,
+ data->f01_container->num_of_irqs,
+ data->f01_container->irq_pos);
+ list_for_each_entry(fc, &data->rmi_functions.list, list)
+ fc->irq_mask = rmi_driver_irq_get_mask(rmi_dev, fc);
+
+ error = rmi_driver_f01_init(rmi_dev);
+ if (error < 0) {
+ dev_err(dev, "Failed to initialize F01.\n");
+ goto err_free_data;
+ }
+
+ error = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION,
+ (char *) &data->pdt_props);
+ if (error < 0) {
+ /* we'll print out a warning and continue since
+ * failure to get the PDT properties is not a cause to fail
+ */
+ dev_warn(dev, "Could not read PDT properties from 0x%04x. "
+ "Assuming 0x00.\n", PDT_PROPERTIES_LOCATION);
+ }
+
+ dev_dbg(dev, "%s: Creating sysfs files.", __func__);
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ error = device_create_file(dev, &attrs[attr_count]);
+ if (error < 0) {
+ dev_err(dev, "%s: Failed to create sysfs file %s.\n",
+ __func__, attrs[attr_count].attr.name);
+ goto err_free_data;
+ }
+ }
+
+ __mutex_init(&data->irq_mutex, "irq_mutex", &data->irq_key);
+ data->current_irq_mask = kzalloc(sizeof(u8) * data->num_of_irq_regs,
+ GFP_KERNEL);
+ if (!data->current_irq_mask) {
+ dev_err(dev, "Failed to allocate current_irq_mask.\n");
+ error = -ENOMEM;
+ goto err_free_data;
+ }
+ error = rmi_read_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ data->current_irq_mask, data->num_of_irq_regs);
+ if (error < 0) {
+ dev_err(dev, "%s: Failed to read current IRQ mask.\n",
+ __func__);
+ goto err_free_data;
+ }
+ data->irq_mask_store = kzalloc(sizeof(u8) * data->num_of_irq_regs,
+ GFP_KERNEL);
+ if (!data->irq_mask_store) {
+ dev_err(dev, "Failed to allocate mask store.\n");
+ error = -ENOMEM;
+ goto err_free_data;
+ }
+
+#if defined(CONFIG_RMI4_DEV)
+ if (rmi_char_dev_register(rmi_dev->phys))
+ pr_err("%s: error register char device", __func__);
+#endif /*CONFIG_RMI4_DEV*/
+
+#ifdef CONFIG_PM
+ data->pm_data = pdata->pm_data;
+ data->pre_suspend = pdata->pre_suspend;
+ data->post_resume = pdata->post_resume;
+
+ mutex_init(&data->suspend_mutex);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ rmi_dev->early_suspend_handler.level =
+ EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ rmi_dev->early_suspend_handler.suspend = rmi_driver_early_suspend;
+ rmi_dev->early_suspend_handler.resume = rmi_driver_late_resume;
+ register_early_suspend(&rmi_dev->early_suspend_handler);
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+#endif /* CONFIG_PM */
+ data->enabled = true;
+
+ dev_info(dev, "connected RMI device manufacturer: %s product: %s\n",
+ data->manufacturer_id == 1 ? "synaptics" : "unknown",
+ data->product_id);
+
+#ifdef SYNAPTICS_SENSOR_POLL
+ synaptics_rmi_polling_queue = create_singlethread_workqueue("rmi_poll_work");
+ INIT_DELAYED_WORK_DEFERRABLE(&synaptics_rmi_polling_work, synaptics_sensor_poller);
+ pr_info("%s: setting up POLLING mode, rmi_device= 0x%8x \n", __func__, polled_synaptics_rmi_device);
+ queue_delayed_work(synaptics_rmi_polling_queue, &synaptics_rmi_polling_work, 1000);
+#endif
+ return 0;
+
+ err_free_data:
+ rmi_free_function_list(rmi_dev);
+ for (attr_count--; attr_count >= 0; attr_count--)
+ device_remove_file(dev, &attrs[attr_count]);
+ kfree(data->f01_container->irq_mask);
+ kfree(data->irq_mask_store);
+ kfree(data->current_irq_mask);
+ kfree(data);
+ return error;
+}
+
+#ifdef CONFIG_PM
+static int rmi_driver_suspend(struct device *dev)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+ struct rmi_function_container *entry;
+ int retval = 0;
+
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ mutex_lock(&data->suspend_mutex);
+ if (data->suspended)
+ goto exit;
+
+ if (data->pre_suspend) {
+ retval = data->pre_suspend(data->pm_data);
+ if (retval)
+ goto exit;
+ }
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list)
+ if (entry->fh && entry->fh->suspend) {
+ retval = entry->fh->suspend(entry);
+ if (retval < 0)
+ goto exit;
+ }
+
+ if (data->f01_container && data->f01_container->fh
+ && data->f01_container->fh->suspend) {
+ retval = data->f01_container->fh->suspend(data->f01_container);
+ if (retval < 0)
+ goto exit;
+ }
+ data->suspended = true;
+
+exit:
+ mutex_unlock(&data->suspend_mutex);
+ return retval;
+}
+
+static int rmi_driver_resume(struct device *dev)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+ struct rmi_function_container *entry;
+ int retval = 0;
+
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ mutex_lock(&data->suspend_mutex);
+ if (!data->suspended)
+ goto exit;
+
+ if (data->f01_container && data->f01_container->fh
+ && data->f01_container->fh->resume) {
+ retval = data->f01_container->fh->resume(data->f01_container);
+ if (retval < 0)
+ goto exit;
+ }
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list)
+ if (entry->fh && entry->fh->resume) {
+ retval = entry->fh->resume(entry);
+ if (retval < 0)
+ goto exit;
+ }
+
+ if (data->post_resume) {
+ retval = data->post_resume(data->pm_data);
+ if (retval)
+ goto exit;
+ }
+
+ data->suspended = false;
+
+exit:
+ mutex_unlock(&data->suspend_mutex);
+ return retval;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void rmi_driver_early_suspend(struct early_suspend *h)
+{
+ struct rmi_device *rmi_dev =
+ container_of(h, struct rmi_device, early_suspend_handler);
+
+ dev_dbg(&rmi_dev->dev, "Early suspend.\n");
+ rmi_driver_suspend(&rmi_dev->dev);
+}
+
+static void rmi_driver_late_resume(struct early_suspend *h)
+{
+ struct rmi_device *rmi_dev =
+ container_of(h, struct rmi_device, early_suspend_handler);
+
+ dev_dbg(&rmi_dev->dev, "Late resume.\n");
+ rmi_driver_resume(&rmi_dev->dev);
+}
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+#endif /* CONFIG_PM */
+
+static int __devexit rmi_driver_remove(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+ struct rmi_function_container *entry;
+ int i;
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list)
+ if (entry->fh && entry->fh->remove)
+ entry->fh->remove(entry);
+
+ rmi_free_function_list(rmi_dev);
+ for (i = 0; i < ARRAY_SIZE(attrs); i++)
+ device_remove_file(&rmi_dev->dev, &attrs[i]);
+ kfree(data->f01_container->irq_mask);
+ kfree(data->irq_mask_store);
+ kfree(data->current_irq_mask);
+ kfree(data);
+
+ return 0;
+}
+
+#ifdef UNIVERSAL_DEV_PM_OPS
+static UNIVERSAL_DEV_PM_OPS(rmi_driver_pm, rmi_driver_suspend,
+ rmi_driver_resume, NULL);
+#endif
+
+static struct rmi_driver sensor_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ // .name = "rmi-generic",
+ .name = "rmi_generic",
+#ifdef UNIVERSAL_DEV_PM_OPS
+ .pm = &rmi_driver_pm,
+#endif
+ },
+ .probe = rmi_driver_probe,
+ .irq_handler = rmi_driver_irq_handler,
+ .fh_add = rmi_driver_fh_add,
+ .fh_remove = rmi_driver_fh_remove,
+ .get_func_irq_mask = rmi_driver_irq_get_mask,
+ .store_irq_mask = rmi_driver_irq_save,
+ .restore_irq_mask = rmi_driver_irq_restore,
+ .remove = __devexit_p(rmi_driver_remove)
+};
+
+/* Utility routine to handle writes to read-only attributes. Hopefully
+ * this will never happen, but if the user does something stupid, we don't
+ * want to accept it quietly (which is what can happen if you just put NULL
+ * for the attribute's store function).
+ */
+ssize_t rmi_store_error(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ dev_warn(dev,
+ "RMI4 WARNING: Attempt to write %d characters to read-only "
+ "attribute %s.", count, attr->attr.name);
+ return -EPERM;
+}
+
+/* Utility routine to handle reads of write-only attributes. Hopefully
+ * this will never happen, but if the user does something stupid, we don't
+ * want to accept it quietly (which is what can happen if you just put NULL
+ * for the attribute's show function).
+ */
+ssize_t rmi_show_error(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ dev_warn(dev,
+ "RMI4 WARNING: Attempt to read from write-only attribute %s.",
+ attr->attr.name);
+ return -EPERM;
+}
+
+/* sysfs show and store fns for driver attributes */
+static ssize_t rmi_driver_hasbsr_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", has_bsr(data));
+}
+
+static ssize_t rmi_driver_bsr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->bsr);
+}
+
+static ssize_t rmi_driver_bsr_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ unsigned long val;
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ /* need to convert the string data to an actual value */
+ retval = strict_strtoul(buf, 10, &val);
+ if (retval < 0) {
+ dev_err(dev, "Invalid value '%s' written to BSR.\n", buf);
+ return -EINVAL;
+ }
+
+ retval = rmi_write(rmi_dev, BSR_LOCATION, (unsigned char)val);
+ if (retval) {
+ dev_err(dev, "%s : failed to write bsr %u to 0x%x\n",
+ __func__, (unsigned int)val, BSR_LOCATION);
+ return retval;
+ }
+
+ data->bsr = val;
+
+ return count;
+}
+
+static void disable_sensor(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+ rmi_dev->phys->disable_device(rmi_dev->phys);
+
+ data->enabled = false;
+}
+
+static int enable_sensor(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+ int retval = 0;
+ pr_info("in function ____%s____ \n", __func__);
+ retval = rmi_dev->phys->enable_device(rmi_dev->phys);
+ /* non-zero means error occurred */
+ if (retval)
+ return retval;
+
+ data->enabled = true;
+
+ return 0;
+}
+
+static ssize_t rmi_driver_enabled_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->enabled);
+}
+
+static ssize_t rmi_driver_enabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ int new_value;
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ if (sysfs_streq(buf, "0"))
+ new_value = false;
+ else if (sysfs_streq(buf, "1"))
+ new_value = true;
+ else
+ return -EINVAL;
+
+ if (new_value) {
+ retval = enable_sensor(rmi_dev);
+ if (retval) {
+ dev_err(dev, "Failed to enable sensor, code=%d.\n",
+ retval);
+ return -EIO;
+ }
+ } else {
+ disable_sensor(rmi_dev);
+ }
+
+ return count;
+}
+
+#if REGISTER_DEBUG
+static ssize_t rmi_driver_reg_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ unsigned int address;
+ unsigned int bytes;
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+ u8 readbuf[128];
+ unsigned char outbuf[512];
+ unsigned char *bufptr = outbuf;
+ int i;
+
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ retval = sscanf(buf, "%x %u", &address, &bytes);
+ if (retval != 2) {
+ dev_err(dev, "Invalid input (code %d) for reg store: %s",
+ retval, buf);
+ return -EINVAL;
+ }
+ if (address < 0 || address > 0xFFFF) {
+ dev_err(dev, "Invalid address for reg store '%#06x'.\n",
+ address);
+ return -EINVAL;
+ }
+ if (bytes < 0 || bytes >= sizeof(readbuf) || address+bytes > 65535) {
+ dev_err(dev, "Invalid byte count for reg store '%d'.\n",
+ bytes);
+ return -EINVAL;
+ }
+
+ retval = rmi_read_block(rmi_dev, address, readbuf, bytes);
+ if (retval != bytes) {
+ dev_err(dev, "Failed to read %d registers at %#06x, code %d.\n",
+ bytes, address, retval);
+ return retval;
+ }
+
+ dev_info(dev, "Reading %d bytes from %#06x.\n", bytes, address);
+ for (i = 0; i < bytes; i++) {
+ retval = snprintf(bufptr, 4, "%02X ", readbuf[i]);
+ if (retval < 0) {
+ dev_err(dev, "Failed to format string. Code: %d",
+ retval);
+ return retval;
+ }
+ bufptr += retval;
+ }
+ dev_info(dev, "%s\n", outbuf);
+
+ return count;
+}
+#endif
+
+#if DELAY_DEBUG
+static ssize_t rmi_delay_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ struct rmi_device *rmi_dev;
+ struct rmi_device_platform_data *pdata;
+ unsigned int new_read_delay;
+ unsigned int new_write_delay;
+ unsigned int new_block_delay;
+ unsigned int new_pre_delay;
+ unsigned int new_post_delay;
+
+ retval = sscanf(buf, "%u %u %u %u %u", &new_read_delay,
+ &new_write_delay, &new_block_delay,
+ &new_pre_delay, &new_post_delay);
+ if (retval != 5) {
+ dev_err(dev, "Incorrect number of values provided for delay.");
+ return -EINVAL;
+ }
+ if (new_read_delay < 0) {
+ dev_err(dev, "Byte delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+ if (new_write_delay < 0) {
+ dev_err(dev, "Write delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+ if (new_block_delay < 0) {
+ dev_err(dev, "Block delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+ if (new_pre_delay < 0) {
+ dev_err(dev,
+ "Pre-transfer delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+ if (new_post_delay < 0) {
+ dev_err(dev,
+ "Post-transfer delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+
+ rmi_dev = to_rmi_device(dev);
+ pdata = rmi_dev->phys->dev->platform_data;
+
+ dev_info(dev, "Setting delays to %u %u %u %u %u.\n", new_read_delay,
+ new_write_delay, new_block_delay, new_pre_delay,
+ new_post_delay);
+ pdata->spi_data.read_delay_us = new_read_delay;
+ pdata->spi_data.write_delay_us = new_write_delay;
+ pdata->spi_data.block_delay_us = new_block_delay;
+ pdata->spi_data.pre_delay_us = new_pre_delay;
+ pdata->spi_data.post_delay_us = new_post_delay;
+
+ return count;
+}
+
+static ssize_t rmi_delay_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_device_platform_data *pdata;
+
+ rmi_dev = to_rmi_device(dev);
+ pdata = rmi_dev->phys->dev->platform_data;
+
+ return snprintf(buf, PAGE_SIZE, "%d %d %d %d %d\n",
+ pdata->spi_data.read_delay_us, pdata->spi_data.write_delay_us,
+ pdata->spi_data.block_delay_us,
+ pdata->spi_data.pre_delay_us, pdata->spi_data.post_delay_us);
+}
+#endif
+
+static ssize_t rmi_driver_phys_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_phys_info *info;
+
+ rmi_dev = to_rmi_device(dev);
+ info = &rmi_dev->phys->info;
+
+ return snprintf(buf, PAGE_SIZE, "%-5s %ld %ld %ld %ld %ld %ld %ld\n",
+ info->proto ? info->proto : "unk",
+ info->tx_count, info->tx_bytes, info->tx_errs,
+ info->rx_count, info->rx_bytes, info->rx_errs,
+ info->attn_count);
+}
+
+static ssize_t rmi_driver_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ RMI_DRIVER_VERSION_STRING);
+}
+
+static int __init rmi_driver_init(void)
+{
+ return rmi_register_driver(&sensor_driver);
+}
+
+static void __exit rmi_driver_exit(void)
+{
+ rmi_unregister_driver(&sensor_driver);
+}
+
+module_init(rmi_driver_init);
+module_exit(rmi_driver_exit);
+
+MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
+MODULE_DESCRIPTION("RMI generic driver");
diff --git a/drivers/input/touchscreen/rmi4/rmi_driver.h b/drivers/input/touchscreen/rmi4/rmi_driver.h
new file mode 100644
index 000000000000..9e9f2c098225
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_driver.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2011 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.
+ */
+#ifndef _RMI_DRIVER_H
+#define _RMI_DRIVER_H
+
+#define RMI_DRIVER_MAJOR_VERSION 1
+#define RMI_DRIVER_MINOR_VERSION 3
+#define RMI_DRIVER_SUB_MINOR_VERSION 0
+
+/* TODO: Figure out some way to construct this string in the define macro
+ * using the values defined above.
+ */
+#define RMI_DRIVER_VERSION_STRING "1.3.0"
+
+
+#define RMI_PRODUCT_ID_LENGTH 10
+#define RMI_PRODUCT_INFO_LENGTH 2
+#define RMI_DATE_CODE_LENGTH 3
+
+struct rmi_driver_data {
+ struct rmi_function_container rmi_functions;
+
+ struct rmi_function_container *f01_container;
+
+ int num_of_irq_regs;
+ u8 *current_irq_mask;
+ u8 *irq_mask_store;
+ bool irq_stored;
+ struct mutex irq_mutex;
+ struct lock_class_key irq_key;
+
+ unsigned char pdt_props;
+ unsigned char bsr;
+ bool enabled;
+
+ u8 manufacturer_id;
+ /* product id + null termination */
+ u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
+
+#ifdef CONFIG_PM
+ bool suspended;
+ struct mutex suspend_mutex;
+
+ void *pm_data;
+ int (*pre_suspend) (const void *pm_data);
+ int (*post_resume) (const void *pm_data);
+#endif
+
+ void *data;
+};
+
+struct pdt_entry {
+ u8 query_base_addr:8;
+ u8 command_base_addr:8;
+ u8 control_base_addr:8;
+ u8 data_base_addr:8;
+ u8 interrupt_source_count:3;
+ u8 bits3and4:2;
+ u8 function_version:2;
+ u8 bit7:1;
+ u8 function_number:8;
+};
+
+int rmi_driver_f01_init(struct rmi_device *rmi_dev);
+
+static inline void copy_pdt_entry_to_fd(struct pdt_entry *pdt,
+ struct rmi_function_descriptor *fd,
+ u16 page_start)
+{
+ fd->query_base_addr = pdt->query_base_addr + page_start;
+ fd->command_base_addr = pdt->command_base_addr + page_start;
+ fd->control_base_addr = pdt->control_base_addr + page_start;
+ fd->data_base_addr = pdt->data_base_addr + page_start;
+ fd->function_number = pdt->function_number;
+ fd->interrupt_source_count = pdt->interrupt_source_count;
+ fd->function_version = pdt->function_version;
+}
+
+/* Helper function to convert a short (in host processor endianess) to
+ * a byte array in the RMI endianess for shorts. See above comment for
+ * why we dont us htons or something like that.
+ */
+void hstoba(u8 *dest, u16 src);
+
+/* Helper fn to convert a byte array representing a short in the RMI
+ * endian-ness to a short in the native processor's specific endianness.
+ * We don't use ntohs/htons here because, well, we're not dealing with
+ * a pair of shorts. And casting dest to short* wouldn't work, because
+ * that would imply knowing the byte order of short in the first place.
+ */
+void batohs(u16 *dest, u8 *src);
+
+#endif
diff --git a/drivers/input/touchscreen/rmi4/rmi_f01.c b/drivers/input/touchscreen/rmi4/rmi_f01.c
new file mode 100644
index 000000000000..ef9adb0586b9
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_f01.c
@@ -0,0 +1,775 @@
+/*
+ * Copyright (c) 2011 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.
+ */
+
+#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+/* 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)
+
+union f01_device_commands {
+ struct {
+ u8 reset:1;
+ u8 reserved:1;
+ };
+ u8 reg;
+};
+
+union f01_device_control {
+ struct {
+ u8 sleep_mode:2;
+ u8 nosleep:1;
+ u8 reserved:2;
+ u8 charger_input:1;
+ u8 report_rate:1;
+ u8 configured:1;
+ };
+ u8 reg;
+};
+
+union f01_device_status {
+ struct {
+ u8 status_code:4;
+ u8 reserved:2;
+ u8 flash_prog:1;
+ u8 unconfigured:1;
+ };
+ u8 reg;
+};
+
+union f01_basic_queries {
+ struct {
+ u8 manufacturer_id:8;
+
+ u8 custom_map:1;
+ u8 non_compliant:1;
+ u8 q1_bit_2:1;
+ u8 has_sensor_id:1;
+ u8 has_charger_input:1;
+ u8 has_adjustable_doze:1;
+ u8 has_adjustable_doze_holdoff:1;
+ u8 q1_bit_7:1;
+
+ u8 productinfo_1:7;
+ u8 q2_bit_7:1;
+ u8 productinfo_2:7;
+ u8 q3_bit_7:1;
+
+ u8 year:5;
+ u8 month:4;
+ u8 day:5;
+ u8 cp1:1;
+ u8 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;
+ };
+ u8 regs[11];
+};
+
+struct f01_data {
+ union 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];
+
+#ifdef CONFIG_PM
+ bool suspended;
+ bool old_nosleep;
+#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_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 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 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_set_bit_field(struct rmi_device *rmi_dev,
+ unsigned short address,
+ unsigned char field_mask,
+ unsigned char bits)
+{
+ unsigned char reg_contents;
+ int retval;
+
+ retval = rmi_read(rmi_dev, address, &reg_contents);
+ if (retval)
+ return retval;
+ reg_contents = (reg_contents & ~field_mask) | bits;
+ 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, "%s: failed to issue reset command, "
+ "error = %d.", __func__, 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.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.sleep_mode = new_value;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ 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.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.nosleep = new_value;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ 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.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.charger_input = new_value;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ 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.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.report_rate = new_value;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ if (retval >= 0)
+ retval = count;
+ else
+ dev_err(dev, "Failed to write reportrate bit.\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.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);
+}
+
+int rmi_driver_f01_init(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *driver_data = rmi_get_driverdata(rmi_dev);
+ struct rmi_function_container *fc = driver_data->f01_container;
+ struct f01_data *data;
+ int error;
+ u8 temp;
+ int attr_count;
+
+ data = kzalloc(sizeof(struct f01_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F01 data.\n");
+ return -ENOMEM;
+ }
+ fc->data = data;
+
+ /* Set the configured bit. */
+ error = rmi_read_block(rmi_dev, fc->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to read F01 control.\n");
+ goto error_exit;
+ }
+
+ /* 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.sleep_mode != RMI_SLEEP_MODE_NORMAL) {
+ dev_warn(&fc->dev,
+ "WARNING: Non-zero sleep mode found. Clearing...\n");
+ data->device_control.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+ }
+
+ data->device_control.configured = 1;
+ error = rmi_write_block(rmi_dev, fc->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to write F01 control.\n");
+ goto error_exit;
+ }
+
+ /* dummy read in order to clear irqs */
+ error = rmi_read(rmi_dev, fc->fd.data_base_addr + 1, &temp);
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to read Interrupt Status.\n");
+ goto error_exit;
+ }
+
+ error = rmi_read_block(rmi_dev, fc->fd.query_base_addr,
+ data->basic_queries.regs,
+ sizeof(data->basic_queries.regs));
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to read device query registers.\n");
+ goto error_exit;
+ }
+ driver_data->manufacturer_id = data->basic_queries.manufacturer_id;
+
+ error = rmi_read_block(rmi_dev,
+ fc->fd.query_base_addr + sizeof(data->basic_queries.regs),
+ data->product_id, RMI_PRODUCT_ID_LENGTH);
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to read product ID.\n");
+ goto error_exit;
+ }
+ data->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
+ memcpy(driver_data->product_id, data->product_id,
+ sizeof(data->product_id));
+
+ error = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
+ &data->device_status.reg,
+ sizeof(data->device_status.reg));
+ if (error < 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);
+ error = -EINVAL;
+ goto error_exit;
+ }
+ /*
+ ** attach the routines that handle sysfs interaction
+ ** Meaning: Set up sysfs device attributes.
+ */
+ for (attr_count = 0; attr_count < ARRAY_SIZE(fn_01_attrs);
+ attr_count++) {
+ if (sysfs_create_file(&fc->dev.kobj,
+ &fn_01_attrs[attr_count].attr) < 0) {
+ dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+ fn_01_attrs[attr_count].attr.name);
+ error = -ENODEV;
+ goto error_exit;
+ }
+ }
+
+ return error;
+
+ error_exit:
+ kfree(data);
+ return error;
+}
+
+#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 = rmi_get_driverdata(rmi_dev);
+ struct f01_data *data = driver_data->f01_container->data;
+ int retval = 0;
+
+ dev_dbg(&fc->dev, "Suspending...\n");
+ if (data->suspended)
+ return 0;
+
+ data->old_nosleep = data->device_control.nosleep;
+ data->device_control.nosleep = 0;
+ data->device_control.sleep_mode = RMI_SLEEP_MODE_SENSOR_SLEEP;
+
+ retval = rmi_write_block(rmi_dev,
+ driver_data->f01_container->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to write sleep mode. "
+ "Code: %d.\n", retval);
+ data->device_control.nosleep = data->old_nosleep;
+ data->device_control.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 = rmi_get_driverdata(rmi_dev);
+ struct f01_data *data = driver_data->f01_container->data;
+ int retval = 0;
+
+ dev_dbg(&fc->dev, "Resuming...\n");
+ if (!data->suspended)
+ return 0;
+
+ data->device_control.nosleep = data->old_nosleep;
+ data->device_control.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+
+ retval = rmi_write_block(rmi_dev,
+ driver_data->f01_container->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ 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 rmi_f01_init(struct rmi_function_container *fc)
+{
+ 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 error;
+
+ error = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
+ &data->device_status.reg,
+ sizeof(data->device_status.reg));
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to read device status.\n");
+ return error;
+ }
+
+ /* TODO: Do we handle reset here or elsewhere? */
+ if (data->device_status.unconfigured)
+ dev_warn(&rmi_dev->dev, "Reset detected! Status code: %#04x.\n",
+ data->device_status.status_code);
+ return 0;
+}
+
+static struct rmi_function_handler function_handler = {
+ .func = 0x01,
+ .init = rmi_f01_init,
+ .attention = rmi_f01_attention,
+#ifdef CONFIG_PM
+ .suspend = rmi_f01_suspend,
+ .resume = rmi_f01_resume,
+#endif
+};
+
+static int __init rmi_f01_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 __exit rmi_f01_module_exit(void)
+{
+ rmi_unregister_function_driver(&function_handler);
+}
+
+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");
diff --git a/drivers/input/touchscreen/rmi4/rmi_f09.c b/drivers/input/touchscreen/rmi4/rmi_f09.c
new file mode 100644
index 000000000000..0ec980d7db07
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_f09.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2011 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/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#define QUERY_BASE_INDEX 1
+#define MAX_LEN 256
+
+/* data specific to fn $09 that needs to be kept around */
+struct f09_query {
+ u8 Limit_Register_Count;
+ union {
+ struct {
+ u8 Result_Register_Count:3;
+ u8 Reserved:3;
+ u8 InternalLimits:1;
+ u8 HostTestEn:1;
+ };
+ u8 f09_bist_query1;
+ };
+};
+
+struct f09_control {
+ /* test1 */
+ u8 Test1LimitLo;
+ u8 Test1LimitHi;
+ u8 Test1LimitDiff;
+ /* test2 */
+ u8 Test2LimitLo;
+ u8 Test2LimitHi;
+ u8 Test2LimitDiff;
+};
+
+struct f09_data {
+ u8 TestNumberControl;
+ u8 Overall_BIST_Result;
+ u8 TestResult1;
+ u8 TestResult2;
+ u8 Transmitter_Number;
+
+ union {
+ struct {
+ u8 Receiver_Number:6;
+ u8 Limit_Failure_Code:2;
+ };
+ u8 f09_bist_data2;
+ };
+};
+
+struct f09_cmd {
+ union {
+ struct {
+ u8 RunBIST:1;
+ };
+ u8 f09_bist_cmd0;
+ };
+};
+
+struct rmi_fn_09_data {
+ struct f09_query query;
+};
+
+static ssize_t rmi_f09_Limit_Register_Count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_HostTestEn_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_HostTestEn_store(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, size_t count);
+
+static ssize_t rmi_f09_InternalLimits_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_Result_Register_Count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_Overall_BIST_Result_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_Overall_BIST_Result_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static struct device_attribute attrs[] = {
+ __ATTR(Limit_Register_Count, RMI_RO_ATTR,
+ rmi_f09_Limit_Register_Count_show, rmi_store_error),
+ __ATTR(HostTestEn, RMI_RW_ATTR,
+ rmi_f09_HostTestEn_show, rmi_f09_HostTestEn_store),
+ __ATTR(InternalLimits, RMI_RO_ATTR,
+ rmi_f09_Limit_Register_Count_show, rmi_store_error),
+ __ATTR(Result_Register_Count, RMI_RO_ATTR,
+ rmi_f09_Result_Register_Count_show, rmi_store_error),
+};
+
+static int rmi_f09_init(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct rmi_device_platform_data *pdata;
+ struct rmi_fn_09_data *f09;
+ u8 query_base_addr;
+ int rc;
+ int i;
+ int attr_count = 0;
+ int retval = 0;
+
+ dev_info(&fc->dev, "Intializing F09 values.");
+
+ f09 = kzalloc(sizeof(struct rmi_fn_09_data), GFP_KERNEL);
+ if (!f09) {
+ dev_err(&fc->dev, "Failed to allocate rmi_fn_09_data.\n");
+ retval = -ENOMEM;
+ goto error_exit;
+ }
+ fc->data = f09;
+
+ pdata = to_rmi_platform_data(rmi_dev);
+ query_base_addr = fc->fd.query_base_addr;
+
+ /* initial all default values for f09 query here */
+ rc = rmi_read_block(rmi_dev, query_base_addr,
+ (u8 *)&f09->query, sizeof(f09->query));
+ if (rc < 0) {
+ dev_err(&fc->dev, "Failed to read query register."
+ " from 0x%04x\n", query_base_addr);
+ goto error_exit;
+ }
+
+ dev_dbg(&fc->dev, "Creating sysfs files.");
+ /* 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);
+ retval = -ENODEV;
+ goto error_exit;
+ }
+ }
+ return 0;
+
+error_exit:
+ dev_err(&fc->dev, "An error occured in F09 init!\n");
+ for (attr_count--; attr_count >= 0; attr_count--)
+ sysfs_remove_file(&fc->dev.kobj,
+ &attrs[attr_count].attr);
+ kfree(f09);
+ return retval;
+}
+
+static void rmi_f09_remove(struct rmi_function_container *fc)
+{
+ struct rmi_fn_09_data *data = fc->data;
+ if (data) {
+ kfree(data->query.Limit_Register_Count);
+ kfree(data->query.f09_bist_query1);
+ }
+ kfree(fc->data);
+}
+
+static struct rmi_function_handler function_handler = {
+ .func = 0x09,
+ .init = rmi_f09_init,
+ .remove = rmi_f09_remove
+};
+
+static int __init rmi_f09_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 rmi_f09_module_exit(void)
+{
+ rmi_unregister_function_driver(&function_handler);
+}
+
+
+static ssize_t rmi_f09_Limit_Register_Count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_09_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.Limit_Register_Count);
+}
+
+static ssize_t rmi_f09_HostTestEn_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_09_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.HostTestEn);
+}
+
+static ssize_t rmi_f09_HostTestEn_store(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_09_data *data;
+ unsigned int new_value;
+ int result;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ if (sscanf(buf, "%u", &new_value) != 1) {
+ dev_err(dev,
+ "%s: Error - HostTestEn_store has an "
+ "invalid len.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (new_value < 0 || new_value > 1) {
+ dev_err(dev, "%s: Invalid HostTestEn bit %s.", __func__, buf);
+ return -EINVAL;
+ }
+ data->query.HostTestEn = new_value;
+ result = rmi_write(fc->rmi_dev, fc->fd.query_base_addr,
+ data->query.HostTestEn);
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write HostTestEn_store to 0x%x\n",
+ __func__, fc->fd.query_base_addr);
+ return result;
+ }
+
+ return count;
+
+}
+
+static ssize_t rmi_f09_InternalLimits_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_09_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.InternalLimits);
+}
+
+static ssize_t rmi_f09_Result_Register_Count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_09_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.Result_Register_Count);
+}
+
+module_init(rmi_f09_module_init);
+module_exit(rmi_f09_module_exit);
+
+MODULE_AUTHOR("Allie Xiong <axiong@Synaptics.com>");
+MODULE_DESCRIPTION("RMI F09 module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_f11.c b/drivers/input/touchscreen/rmi4/rmi_f11.c
new file mode 100644
index 000000000000..35bb945143de
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_f11.c
@@ -0,0 +1,1513 @@
+/*
+ * Copyright (c) 2011 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/slab.h>
+#include <linux/input.h>
+#include <linux/rmi.h>
+
+#define F11_MAX_NUM_OF_SENSORS 8
+#define F11_MAX_NUM_OF_FINGERS 10
+#define F11_MAX_NUM_OF_TOUCH_SHAPES 16
+
+#define F11_REL_POS_MIN -128
+#define F11_REL_POS_MAX 127
+
+#define F11_FINGER_STATE_MASK 0x03
+#define F11_FINGER_STATE_SIZE 0x02
+#define F11_FINGER_STATE_MASK_N(i) \
+ (F11_FINGER_STATE_MASK << (i%4 * F11_FINGER_STATE_SIZE))
+
+#define F11_FINGER_STATE_VAL_N(f_state, i) \
+ (f_state >> (i%4 * F11_FINGER_STATE_SIZE))
+
+#define F11_CTRL_SENSOR_MAX_X_POS_OFFSET 6
+#define F11_CTRL_SENSOR_MAX_Y_POS_OFFSET 8
+
+#define F11_CEIL(x, y) (((x) + ((y)-1)) / (y))
+
+/* By default, we'll support two fingers if we can't figure out how many we
+ * really need to handle.
+ */
+#define DEFAULT_NR_OF_FINGERS 2
+#define DEFAULT_XY_MAX 9999
+#define DEFAULT_MAX_ABS_MT_PRESSURE 255
+#define DEFAULT_MAX_ABS_MT_TOUCH 15
+#define DEFAULT_MAX_ABS_MT_ORIENTATION 1
+#define DEFAULT_MIN_ABS_MT_TRACKING_ID 1
+#define DEFAULT_MAX_ABS_MT_TRACKING_ID 10
+#define MAX_LEN 256
+
+static ssize_t rmi_fn_11_flip_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_flip_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_clip_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_clip_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_offset_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_offset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_swap_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_swap_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_relreport_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_11_relreport_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_maxPos_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f11_rezero_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+
+static struct device_attribute attrs[] = {
+ __ATTR(flip, RMI_RW_ATTR, rmi_fn_11_flip_show, rmi_fn_11_flip_store),
+ __ATTR(clip, RMI_RW_ATTR, rmi_fn_11_clip_show, rmi_fn_11_clip_store),
+ __ATTR(offset, RMI_RW_ATTR,
+ rmi_fn_11_offset_show, rmi_fn_11_offset_store),
+ __ATTR(swap, RMI_RW_ATTR, rmi_fn_11_swap_show, rmi_fn_11_swap_store),
+ __ATTR(relreport, RMI_RW_ATTR,
+ rmi_fn_11_relreport_show, rmi_fn_11_relreport_store),
+ __ATTR(maxPos, RMI_RO_ATTR, rmi_fn_11_maxPos_show, rmi_store_error),
+ __ATTR(rezero, RMI_WO_ATTR, rmi_show_error, rmi_f11_rezero_store)
+};
+
+
+union f11_2d_commands {
+ struct {
+ u8 rezero:1;
+ };
+ u8 reg;
+};
+
+
+struct f11_2d_device_query {
+ union {
+ struct {
+ u8 nbr_of_sensors:3;
+ u8 has_query9:1;
+ u8 has_query11:1;
+ };
+ u8 f11_2d_query0;
+ };
+
+ u8 f11_2d_query9;
+
+ union {
+ struct {
+ u8 has_z_tuning:1;
+ u8 has_pos_interpolation_tuning:1;
+ u8 has_w_tuning:1;
+ u8 has_pitch_info:1;
+ u8 has_default_finger_width:1;
+ u8 has_segmentation_aggressiveness:1;
+ u8 has_tx_rw_clip:1;
+ u8 has_drumming_correction:1;
+ };
+ u8 f11_2d_query11;
+ };
+};
+
+struct f11_2d_sensor_query {
+ union {
+ struct {
+ /* query1 */
+ u8 number_of_fingers:3;
+ u8 has_rel:1;
+ u8 has_abs:1;
+ u8 has_gestures:1;
+ u8 has_sensitivity_adjust:1;
+ u8 configurable:1;
+ /* query2 */
+ u8 num_of_x_electrodes:7;
+ /* query3 */
+ u8 num_of_y_electrodes:7;
+ /* query4 */
+ u8 max_electrodes:7;
+ };
+ u8 f11_2d_query1__4[4];
+ };
+
+ union {
+ struct {
+ u8 abs_data_size:3;
+ u8 has_anchored_finger:1;
+ u8 has_adj_hyst:1;
+ u8 has_dribble:1;
+ };
+ u8 f11_2d_query5;
+ };
+
+ u8 f11_2d_query6;
+
+ union {
+ struct {
+ u8 has_single_tap:1;
+ u8 has_tap_n_hold:1;
+ u8 has_double_tap:1;
+ u8 has_early_tap:1;
+ u8 has_flick:1;
+ u8 has_press:1;
+ u8 has_pinch:1;
+ u8 padding:1;
+
+ u8 has_palm_det:1;
+ u8 has_rotate:1;
+ u8 has_touch_shapes:1;
+ u8 has_scroll_zones:1;
+ u8 has_individual_scroll_zones:1;
+ u8 has_multi_finger_scroll:1;
+ };
+ u8 f11_2d_query7__8[2];
+ };
+
+ /* Empty */
+ u8 f11_2d_query9;
+
+ union {
+ struct {
+ u8 nbr_touch_shapes:5;
+ };
+ u8 f11_2d_query10;
+ };
+};
+
+struct f11_2d_data_0 {
+ u8 finger_n;
+};
+
+struct f11_2d_data_1_5 {
+ u8 x_msb;
+ u8 y_msb;
+ u8 x_lsb:4;
+ u8 y_lsb:4;
+ u8 w_y:4;
+ u8 w_x:4;
+ u8 z;
+};
+
+struct f11_2d_data_6_7 {
+ s8 delta_x;
+ s8 delta_y;
+};
+
+struct f11_2d_data_8 {
+ u8 single_tap:1;
+ u8 tap_and_hold:1;
+ u8 double_tap:1;
+ u8 early_tap:1;
+ u8 flick:1;
+ u8 press:1;
+ u8 pinch:1;
+};
+
+struct f11_2d_data_9 {
+ u8 palm_detect:1;
+ u8 rotate:1;
+ u8 shape:1;
+ u8 scrollzone:1;
+ u8 finger_count:3;
+};
+
+struct f11_2d_data_10 {
+ u8 pinch_motion;
+};
+
+struct f11_2d_data_10_12 {
+ u8 x_flick_dist;
+ u8 y_flick_dist;
+ u8 flick_time;
+};
+
+struct f11_2d_data_11_12 {
+ u8 motion;
+ u8 finger_separation;
+};
+
+struct f11_2d_data_13 {
+ u8 shape_n;
+};
+
+struct f11_2d_data_14_15 {
+ u8 horizontal;
+ u8 vertical;
+};
+
+struct f11_2d_data_14_17 {
+ u8 x_low;
+ u8 y_right;
+ u8 x_upper;
+ u8 y_left;
+};
+
+struct f11_2d_data {
+ const struct f11_2d_data_0 *f_state;
+ const struct f11_2d_data_1_5 *abs_pos;
+ const struct f11_2d_data_6_7 *rel_pos;
+ const struct f11_2d_data_8 *gest_1;
+ const struct f11_2d_data_9 *gest_2;
+ const struct f11_2d_data_10 *pinch;
+ const struct f11_2d_data_10_12 *flick;
+ const struct f11_2d_data_11_12 *rotate;
+ const struct f11_2d_data_13 *shapes;
+ const struct f11_2d_data_14_15 *multi_scroll;
+ const struct f11_2d_data_14_17 *scroll_zones;
+};
+
+struct f11_2d_sensor {
+ struct rmi_f11_2d_axis_alignment axis_align;
+ struct f11_2d_sensor_query sens_query;
+ struct f11_2d_data data;
+ u16 max_x;
+ u16 max_y;
+ u8 nbr_fingers;
+ u8 finger_tracker[F11_MAX_NUM_OF_FINGERS];
+ u8 *data_pkt;
+ int pkt_size;
+ u8 sensor_index;
+ char input_name[MAX_LEN];
+ char input_phys[MAX_LEN];
+
+ struct input_dev *input;
+ struct input_dev *mouse_input;
+};
+
+struct f11_data {
+ struct f11_2d_device_query dev_query;
+ struct rmi_f11_2d_ctrl dev_controls;
+ struct f11_2d_sensor sensors[F11_MAX_NUM_OF_SENSORS];
+};
+
+enum finger_state_values {
+ F11_NO_FINGER = 0x00,
+ F11_PRESENT = 0x01,
+ F11_INACCURATE = 0x02,
+ F11_RESERVED = 0x03
+};
+
+static void rmi_f11_rel_pos_report(struct f11_2d_sensor *sensor, u8 n_finger)
+{
+ struct f11_2d_data *data = &sensor->data;
+ struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align;
+ s8 x, y;
+ s8 temp;
+
+ x = data->rel_pos[n_finger].delta_x;
+ y = data->rel_pos[n_finger].delta_y;
+
+ x = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)x));
+ y = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)y));
+
+ if (axis_align->swap_axes) {
+ temp = x;
+ x = y;
+ y = temp;
+ }
+ if (axis_align->flip_x)
+ x = min(F11_REL_POS_MAX, -x);
+ if (axis_align->flip_y)
+ y = min(F11_REL_POS_MAX, -y);
+
+ if (x || y) {
+ input_report_rel(sensor->input, REL_X, x);
+ input_report_rel(sensor->input, REL_Y, y);
+ input_report_rel(sensor->mouse_input, REL_X, x);
+ input_report_rel(sensor->mouse_input, REL_Y, y);
+ }
+ input_sync(sensor->mouse_input);
+}
+
+static void rmi_f11_abs_pos_report(struct f11_2d_sensor *sensor,
+ u8 finger_state, u8 n_finger)
+{
+ struct f11_2d_data *data = &sensor->data;
+ struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align;
+ int prev_state = sensor->finger_tracker[n_finger];
+ int x, y, z;
+ int w_x, w_y, w_max, w_min, orient;
+ int temp;
+ if (prev_state && !finger_state) {
+ /* this is a release */
+ x = y = z = w_max = w_min = orient = 0;
+ } else if (!prev_state && !finger_state) {
+ /* nothing to report */
+ return;
+ } else {
+ x = ((data->abs_pos[n_finger].x_msb << 4) |
+ data->abs_pos[n_finger].x_lsb);
+ y = ((data->abs_pos[n_finger].y_msb << 4) |
+ data->abs_pos[n_finger].y_lsb);
+ z = data->abs_pos[n_finger].z;
+ w_x = data->abs_pos[n_finger].w_x;
+ w_y = data->abs_pos[n_finger].w_y;
+ w_max = max(w_x, w_y);
+ w_min = min(w_x, w_y);
+
+ if (axis_align->swap_axes) {
+ temp = x;
+ x = y;
+ y = temp;
+ temp = w_x;
+ w_x = w_y;
+ w_y = temp;
+ }
+
+ orient = w_x > w_y ? 1 : 0;
+
+ if (axis_align->flip_x)
+ x = max(sensor->max_x - x, 0);
+
+ if (axis_align->flip_y)
+ y = max(sensor->max_y - y, 0);
+
+ /*
+ ** here checking if X offset or y offset are specified is
+ ** redundant. We just add the offsets or, clip the values
+ **
+ ** note: offsets need to be done before clipping occurs,
+ ** or we could get funny values that are outside
+ ** clipping boundaries.
+ */
+ x += axis_align->offset_X;
+ y += axis_align->offset_Y;
+ x = max(axis_align->clip_X_low, x);
+ y = max(axis_align->clip_Y_low, y);
+ if (axis_align->clip_X_high)
+ x = min(axis_align->clip_X_high, x);
+ if (axis_align->clip_Y_high)
+ y = min(axis_align->clip_Y_high, y);
+
+ }
+
+ pr_debug("%s: f_state[%d]:%d - x:%d y:%d z:%d w_max:%d w_min:%d\n",
+ __func__, n_finger, finger_state, x, y, z, w_max, w_min);
+
+
+#ifdef ABS_MT_PRESSURE
+ input_report_abs(sensor->input, ABS_MT_PRESSURE, z);
+#endif
+ input_report_abs(sensor->input, ABS_MT_TOUCH_MAJOR, w_max);
+ input_report_abs(sensor->input, ABS_MT_TOUCH_MINOR, w_min);
+ input_report_abs(sensor->input, ABS_MT_ORIENTATION, orient);
+ input_report_abs(sensor->input, ABS_MT_POSITION_X, x);
+ input_report_abs(sensor->input, ABS_MT_POSITION_Y, y);
+ input_report_abs(sensor->input, ABS_MT_TRACKING_ID, n_finger);
+
+ /* MT sync between fingers */
+ input_mt_sync(sensor->input);
+ sensor->finger_tracker[n_finger] = finger_state;
+}
+
+static void rmi_f11_finger_handler(struct f11_2d_sensor *sensor)
+{
+ const struct f11_2d_data_0 *f_state = sensor->data.f_state;
+ u8 finger_state;
+ u8 finger_pressed_count;
+ u8 i;
+
+ for (i = 0, finger_pressed_count = 0; i < sensor->nbr_fingers; i++) {
+ /* Possible of having 4 fingers per f_statet register */
+ finger_state = (f_state[i >> 2].finger_n &
+ F11_FINGER_STATE_MASK_N(i));
+ finger_state = F11_FINGER_STATE_VAL_N(finger_state, i);
+
+ if (finger_state == F11_RESERVED) {
+ pr_err("%s: Invalid finger state[%d]:0x%02x.", __func__,
+ i, finger_state);
+ continue;
+ } else if ((finger_state == F11_PRESENT) ||
+ (finger_state == F11_INACCURATE)) {
+ finger_pressed_count++;
+ }
+
+ if (sensor->data.abs_pos)
+ rmi_f11_abs_pos_report(sensor, finger_state, i);
+
+ if (sensor->data.rel_pos)
+ rmi_f11_rel_pos_report(sensor, i);
+ }
+ input_report_key(sensor->input, BTN_TOUCH, finger_pressed_count);
+ input_sync(sensor->input);
+}
+
+static inline int rmi_f11_2d_construct_data(struct f11_2d_sensor *sensor)
+{
+ struct f11_2d_sensor_query *query = &sensor->sens_query;
+ struct f11_2d_data *data = &sensor->data;
+ int i;
+
+ sensor->nbr_fingers = (query->number_of_fingers == 5 ? 10 :
+ query->number_of_fingers + 1);
+
+ sensor->pkt_size = F11_CEIL(sensor->nbr_fingers, 4);
+
+ if (query->has_abs)
+ sensor->pkt_size += (sensor->nbr_fingers * 5);
+
+ if (query->has_rel)
+ sensor->pkt_size += (sensor->nbr_fingers * 2);
+
+ /* Check if F11_2D_Query7 is non-zero */
+ if (query->f11_2d_query7__8[0])
+ sensor->pkt_size += sizeof(u8);
+
+ /* Check if F11_2D_Query7 or F11_2D_Query8 is non-zero */
+ if (query->f11_2d_query7__8[0] || query->f11_2d_query7__8[1])
+ sensor->pkt_size += sizeof(u8);
+
+ if (query->has_pinch || query->has_flick || query->has_rotate) {
+ sensor->pkt_size += 3;
+ if (!query->has_flick)
+ sensor->pkt_size--;
+ if (!query->has_rotate)
+ sensor->pkt_size--;
+ }
+
+ if (query->has_touch_shapes)
+ sensor->pkt_size += F11_CEIL(query->nbr_touch_shapes + 1, 8);
+
+ sensor->data_pkt = kzalloc(sensor->pkt_size, GFP_KERNEL);
+ if (!sensor->data_pkt)
+ return -ENOMEM;
+
+ data->f_state = (struct f11_2d_data_0 *)sensor->data_pkt;
+ i = F11_CEIL(sensor->nbr_fingers, 4);
+
+ if (query->has_abs) {
+ data->abs_pos = (struct f11_2d_data_1_5 *)
+ &sensor->data_pkt[i];
+ i += (sensor->nbr_fingers * 5);
+ }
+
+ if (query->has_rel) {
+ data->rel_pos = (struct f11_2d_data_6_7 *)
+ &sensor->data_pkt[i];
+ i += (sensor->nbr_fingers * 2);
+ }
+
+ if (query->f11_2d_query7__8[0]) {
+ data->gest_1 = (struct f11_2d_data_8 *)&sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->f11_2d_query7__8[0] || query->f11_2d_query7__8[1]) {
+ data->gest_2 = (struct f11_2d_data_9 *)&sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->has_pinch) {
+ data->pinch = (struct f11_2d_data_10 *)&sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->has_flick) {
+ if (query->has_pinch) {
+ data->flick = (struct f11_2d_data_10_12 *)data->pinch;
+ i += 2;
+ } else {
+ data->flick = (struct f11_2d_data_10_12 *)
+ &sensor->data_pkt[i];
+ i += 3;
+ }
+ }
+
+ if (query->has_rotate) {
+ if (query->has_flick) {
+ data->rotate = (struct f11_2d_data_11_12 *)
+ (data->flick + 1);
+ } else {
+ data->rotate = (struct f11_2d_data_11_12 *)
+ &sensor->data_pkt[i];
+ i += 2;
+ }
+ }
+
+ if (query->has_touch_shapes)
+ data->shapes = (struct f11_2d_data_13 *)&sensor->data_pkt[i];
+
+ return 0;
+}
+
+static int rmi_f11_read_control_parameters(struct rmi_device *rmi_dev,
+ struct f11_2d_device_query *query,
+ struct rmi_f11_2d_ctrl *ctrl,
+ int ctrl_base_addr) {
+ int read_address = ctrl_base_addr;
+ int error = 0;
+
+ if (ctrl->ctrl0) {
+ error = rmi_read_block(rmi_dev, read_address, &ctrl->ctrl0->reg,
+ sizeof(union rmi_f11_2d_ctrl0));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl0, code: %d.\n", error);
+ return error;
+ }
+ read_address = read_address + sizeof(union rmi_f11_2d_ctrl0);
+ }
+
+ if (ctrl->ctrl1) {
+ error = rmi_read_block(rmi_dev, read_address, &ctrl->ctrl1->reg,
+ sizeof(union rmi_f11_2d_ctrl1));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl1, code: %d.\n", error);
+ return error;
+ }
+ read_address = read_address + sizeof(union rmi_f11_2d_ctrl1);
+ }
+
+ if (ctrl->ctrl2__3) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl2__3->regs,
+ sizeof(ctrl->ctrl2__3->regs));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl2__3, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(ctrl->ctrl2__3->regs);
+ }
+
+ if (ctrl->ctrl4) {
+ error = rmi_read_block(rmi_dev, read_address, &ctrl->ctrl4->reg,
+ sizeof(ctrl->ctrl4->reg));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl4, code: %d.\n", error);
+ return error;
+ }
+ read_address = read_address + sizeof(ctrl->ctrl4->reg);
+ }
+
+ if (ctrl->ctrl5) {
+ error = rmi_read_block(rmi_dev, read_address, &ctrl->ctrl5->reg,
+ sizeof(ctrl->ctrl5->reg));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl5, code: %d.\n", error);
+ return error;
+ }
+ read_address = read_address + sizeof(ctrl->ctrl5->reg);
+ }
+
+ if (ctrl->ctrl6__7) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl6__7->regs,
+ sizeof(ctrl->ctrl6__7->regs));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl6__7, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(ctrl->ctrl6__7->regs);
+ }
+
+ if (ctrl->ctrl8__9) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl8__9->regs,
+ sizeof(ctrl->ctrl8__9->regs));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl8__9, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(ctrl->ctrl8__9->regs);
+ }
+
+ return 0;
+}
+
+static int rmi_f11_initialize_control_parameters(struct rmi_device *rmi_dev,
+ struct f11_2d_device_query *query,
+ struct rmi_f11_2d_ctrl *ctrl,
+ int ctrl_base_addr) {
+ int error = 0;
+
+ ctrl->ctrl0 = kzalloc(sizeof(union rmi_f11_2d_ctrl0), GFP_KERNEL);
+ if (!ctrl->ctrl0) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl0.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ ctrl->ctrl1 = kzalloc(sizeof(union rmi_f11_2d_ctrl1), GFP_KERNEL);
+ if (!ctrl->ctrl1) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl1.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ ctrl->ctrl2__3 = kzalloc(sizeof(union rmi_f11_2d_ctrl2__3), GFP_KERNEL);
+ if (!ctrl->ctrl2__3) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl2__3.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ ctrl->ctrl4 = kzalloc(sizeof(union rmi_f11_2d_ctrl4), GFP_KERNEL);
+ if (!ctrl->ctrl4) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl4.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ ctrl->ctrl5 = kzalloc(sizeof(union rmi_f11_2d_ctrl5), GFP_KERNEL);
+ if (!ctrl->ctrl5) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl5.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ ctrl->ctrl6__7 = kzalloc(sizeof(union rmi_f11_2d_ctrl6__7), GFP_KERNEL);
+ if (!ctrl->ctrl6__7) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl6__7.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ ctrl->ctrl8__9 = kzalloc(sizeof(union rmi_f11_2d_ctrl8__9), GFP_KERNEL);
+ if (!ctrl->ctrl8__9) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl8__9.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ return rmi_f11_read_control_parameters(rmi_dev, query,
+ ctrl, ctrl_base_addr);
+
+error_exit:
+ kfree(ctrl->ctrl0);
+ kfree(ctrl->ctrl1);
+ kfree(ctrl->ctrl2__3);
+ kfree(ctrl->ctrl4);
+ kfree(ctrl->ctrl5);
+ kfree(ctrl->ctrl6__7);
+ kfree(ctrl->ctrl8__9);
+
+ return error;
+}
+
+static inline int rmi_f11_set_control_parameters(struct rmi_device *rmi_dev,
+ struct f11_2d_sensor_query *query,
+ struct rmi_f11_2d_ctrl *ctrl,
+ int ctrl_base_addr)
+{
+ int write_address = ctrl_base_addr;
+ int error;
+
+ if (ctrl->ctrl0) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl0->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl1) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl1->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl2__3) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl2__3->regs,
+ sizeof(ctrl->ctrl2__3->regs));
+ if (error < 0)
+ return error;
+ write_address += sizeof(ctrl->ctrl2__3->regs);
+ }
+
+ if (ctrl->ctrl4) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl4->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl5) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl5->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl6__7) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl6__7->regs[0],
+ sizeof(ctrl->ctrl6__7->regs));
+ if (error < 0)
+ return error;
+ write_address += sizeof(ctrl->ctrl6__7->regs);
+ }
+
+ if (ctrl->ctrl8__9) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl8__9->regs[0],
+ sizeof(ctrl->ctrl8__9->regs));
+ if (error < 0)
+ return error;
+ write_address += sizeof(ctrl->ctrl8__9->regs);
+ }
+
+ if (ctrl->ctrl10) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl10->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl11) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl11->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl12 && ctrl->ctrl12_size && query->configurable) {
+ if (ctrl->ctrl12_size > query->max_electrodes) {
+ dev_err(&rmi_dev->dev,
+ "%s: invalid cfg size:%d, should be < %d.\n",
+ __func__, ctrl->ctrl12_size,
+ query->max_electrodes);
+ return -EINVAL;
+ }
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl12->reg,
+ ctrl->ctrl12_size);
+ if (error < 0)
+ return error;
+ write_address += ctrl->ctrl12_size;
+ }
+
+ if (ctrl->ctrl14) {
+ error = rmi_write_block(rmi_dev,
+ write_address,
+ &ctrl->ctrl0->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl15) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl15,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl16) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl16,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl17) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl17,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl18) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl18,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl19) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl19,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ return 0;
+}
+
+static inline int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev,
+ struct f11_2d_sensor_query *query, u8 query_base_addr)
+{
+ int query_size;
+ int rc;
+
+ rc = rmi_read_block(rmi_dev, query_base_addr, query->f11_2d_query1__4,
+ sizeof(query->f11_2d_query1__4));
+ if (rc < 0)
+ return rc;
+ query_size = rc;
+
+ if (query->has_abs) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ &query->f11_2d_query5);
+ if (rc < 0)
+ return rc;
+ query_size++;
+ }
+
+ if (query->has_rel) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ &query->f11_2d_query6);
+ if (rc < 0)
+ return rc;
+ query_size++;
+ }
+
+ if (query->has_gestures) {
+ rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
+ query->f11_2d_query7__8,
+ sizeof(query->f11_2d_query7__8));
+ if (rc < 0)
+ return rc;
+ query_size += sizeof(query->f11_2d_query7__8);
+ }
+
+ if (query->has_touch_shapes) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ &query->f11_2d_query10);
+ if (rc < 0)
+ return rc;
+ query_size++;
+ }
+
+ return query_size;
+}
+
+/* This operation is done in a number of places, so we have a handy routine
+ * for it.
+ */
+static void f11_set_abs_params(struct rmi_function_container *fc, int index)
+{
+ struct f11_data *instance_data = fc->data;
+ struct input_dev *input = instance_data->sensors[index].input;
+ int device_x_max =
+ instance_data->dev_controls.ctrl6__7->sensor_max_x_pos;
+ int device_y_max =
+ instance_data->dev_controls.ctrl8__9->sensor_max_y_pos;
+ int x_min, x_max, y_min, y_max;
+
+ if (instance_data->sensors[index].axis_align.swap_axes) {
+ int temp = device_x_max;
+ device_x_max = device_y_max;
+ device_y_max = temp;
+ }
+
+ /* Use the max X and max Y read from the device, or the clip values,
+ * whichever is stricter.
+ */
+ x_min = instance_data->sensors[index].axis_align.clip_X_low;
+ if (instance_data->sensors[index].axis_align.clip_X_high)
+ x_max = min((int) device_x_max,
+ instance_data->sensors[index].axis_align.clip_X_high);
+ else
+ x_max = device_x_max;
+
+ y_min = instance_data->sensors[index].axis_align.clip_Y_low;
+ if (instance_data->sensors[index].axis_align.clip_Y_high)
+ y_max = min((int) device_y_max,
+ instance_data->sensors[index].axis_align.clip_Y_high);
+ else
+ y_max = device_y_max;
+
+ dev_dbg(&fc->dev, "Set ranges X=[%d..%d] Y=[%d..%d].",
+ x_min, x_max, y_min, y_max);
+
+#ifdef ABS_MT_PRESSURE
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0,
+ DEFAULT_MAX_ABS_MT_PRESSURE, 0, 0);
+#endif
+ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
+ 0, DEFAULT_MAX_ABS_MT_TOUCH, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MINOR,
+ 0, DEFAULT_MAX_ABS_MT_TOUCH, 0, 0);
+ input_set_abs_params(input, ABS_MT_ORIENTATION,
+ 0, DEFAULT_MAX_ABS_MT_ORIENTATION, 0, 0);
+ input_set_abs_params(input, ABS_MT_TRACKING_ID,
+ DEFAULT_MIN_ABS_MT_TRACKING_ID,
+ DEFAULT_MAX_ABS_MT_TRACKING_ID, 0, 0);
+ /* TODO get max_x_pos (and y) from control registers. */
+ input_set_abs_params(input, ABS_MT_POSITION_X,
+ x_min, x_max, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y,
+ y_min, y_max, 0, 0);
+}
+
+static int rmi_f11_init(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct rmi_device_platform_data *pdata;
+ struct f11_data *f11;
+ struct input_dev *input_dev;
+ struct input_dev *input_dev_mouse;
+ u8 query_offset;
+ u8 query_base_addr;
+ u8 control_base_addr;
+ u16 max_x_pos, max_y_pos, temp;
+ int rc;
+ int i;
+ int retval = 0;
+ int attr_count = 0;
+
+ dev_info(&fc->dev, "Intializing F11 values.");
+
+ /*
+ ** init instance data, fill in values and create any sysfs files
+ */
+ f11 = kzalloc(sizeof(struct f11_data), GFP_KERNEL);
+ if (!f11)
+ return -ENOMEM;
+ fc->data = f11;
+
+ query_base_addr = fc->fd.query_base_addr;
+ control_base_addr = fc->fd.control_base_addr;
+
+ rc = rmi_read(rmi_dev, query_base_addr, &f11->dev_query.f11_2d_query0);
+ if (rc < 0)
+ goto err_free_data;
+
+ rc = rmi_f11_initialize_control_parameters(rmi_dev, &f11->dev_query,
+ &f11->dev_controls, control_base_addr);
+ if (rc < 0) {
+ dev_err(&fc->dev,
+ "Failed to initialize F11 control params.\n");
+ goto err_free_data;
+ }
+
+ query_offset = (query_base_addr + 1);
+ /* Increase with one since number of sensors is zero based */
+ for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) {
+ f11->sensors[i].sensor_index = i;
+
+ rc = rmi_f11_get_query_parameters(rmi_dev,
+ &f11->sensors[i].sens_query,
+ query_offset);
+ if (rc < 0)
+ goto err_free_data;
+
+ query_offset += rc;
+
+ pdata = to_rmi_platform_data(rmi_dev);
+ if (pdata)
+ f11->sensors[i].axis_align = pdata->axis_align;
+
+ if (pdata && pdata->f11_ctrl) {
+ rc = rmi_f11_set_control_parameters(rmi_dev,
+ &f11->sensors[i].sens_query,
+ pdata->f11_ctrl,
+ control_base_addr);
+ if (rc < 0)
+ goto err_free_data;
+ }
+
+ if (pdata && pdata->f11_ctrl &&
+ pdata->f11_ctrl->ctrl6__7 &&
+ pdata->f11_ctrl->ctrl8__9) {
+ max_x_pos = pdata->f11_ctrl->ctrl6__7->sensor_max_x_pos;
+ max_y_pos = pdata->f11_ctrl->ctrl8__9->sensor_max_y_pos;
+
+ } else {
+ rc = rmi_read_block(rmi_dev,
+ control_base_addr + F11_CTRL_SENSOR_MAX_X_POS_OFFSET,
+ (u8 *)&max_x_pos, sizeof(max_x_pos));
+ if (rc < 0)
+ goto err_free_data;
+
+ rc = rmi_read_block(rmi_dev,
+ control_base_addr + F11_CTRL_SENSOR_MAX_Y_POS_OFFSET,
+ (u8 *)&max_y_pos, sizeof(max_y_pos));
+ if (rc < 0)
+ goto err_free_data;
+ }
+
+ if (pdata->axis_align.swap_axes) {
+ temp = max_x_pos;
+ max_x_pos = max_y_pos;
+ max_y_pos = temp;
+ }
+ f11->sensors[i].max_x = max_x_pos;
+ f11->sensors[i].max_y = max_y_pos;
+
+ rc = rmi_f11_2d_construct_data(&f11->sensors[i]);
+ if (rc < 0)
+ goto err_free_data;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ rc = -ENOMEM;
+ goto err_free_data;
+ }
+
+ f11->sensors[i].input = input_dev;
+ /* TODO how to modify the dev name and
+ * phys name for input device */
+ sprintf(f11->sensors[i].input_name, "%sfn%02x",
+ dev_name(&rmi_dev->dev), fc->fd.function_number);
+ input_dev->name = f11->sensors[i].input_name;
+ sprintf(f11->sensors[i].input_phys, "%s/input0",
+ input_dev->name);
+ input_dev->phys = f11->sensors[i].input_phys;
+ input_dev->dev.parent = &rmi_dev->dev;
+ input_set_drvdata(input_dev, f11);
+
+ set_bit(EV_SYN, input_dev->evbit);
+ set_bit(EV_KEY, input_dev->evbit);
+ set_bit(EV_ABS, input_dev->evbit);
+
+ f11_set_abs_params(fc, i);
+
+ dev_dbg(&fc->dev, "%s: Sensor %d hasRel %d.\n",
+ __func__, i, f11->sensors[i].sens_query.has_rel);
+ if (f11->sensors[i].sens_query.has_rel) {
+ set_bit(EV_REL, input_dev->evbit);
+ set_bit(REL_X, input_dev->relbit);
+ set_bit(REL_Y, input_dev->relbit);
+ }
+ rc = input_register_device(input_dev);
+ if (rc < 0)
+ goto err_free_input;
+
+ if (f11->sensors[i].sens_query.has_rel) {
+ /*create input device for mouse events */
+ input_dev_mouse = input_allocate_device();
+ if (!input_dev_mouse) {
+ rc = -ENOMEM;
+ goto err_free_data;
+ }
+
+ f11->sensors[i].mouse_input = input_dev_mouse;
+ input_dev_mouse->name = "rmi_mouse";
+ input_dev_mouse->phys = "rmi_f11/input0";
+
+ 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);
+
+ rc = input_register_device(input_dev_mouse);
+ if (rc < 0)
+ goto err_free_input;
+ set_bit(BTN_RIGHT, input_dev_mouse->keybit);
+ }
+
+ }
+
+ dev_info(&fc->dev, "Creating sysfs files.");
+ dev_dbg(&fc->dev, "Creating fn11 sysfs files.");
+
+ /* 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);
+ retval = -ENODEV;
+ goto err_free_input;
+ }
+ }
+
+ dev_info(&fc->dev, "Done Creating fn11 sysfs files.");
+ return 0;
+
+err_free_input:
+ for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) {
+ if (f11->sensors[i].input)
+ input_free_device(f11->sensors[i].input);
+ if (f11->sensors[i].sens_query.has_rel &&
+ f11->sensors[i].mouse_input)
+ input_free_device(f11->sensors[i].mouse_input);
+ }
+err_free_data:
+ for (attr_count--; attr_count >= 0; attr_count--)
+ device_remove_file(&fc->rmi_dev->dev, &attrs[attr_count]);
+
+ kfree(f11);
+ return rc;
+}
+
+int rmi_f11_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct f11_data *f11 = fc->data;
+ u8 data_base_addr = fc->fd.data_base_addr;
+ int data_base_addr_offset = 0;
+ int error;
+ int i;
+
+ for (i = 0; i < f11->dev_query.nbr_of_sensors + 1; i++) {
+ error = rmi_read_block(rmi_dev,
+ data_base_addr + data_base_addr_offset,
+ f11->sensors[i].data_pkt,
+ f11->sensors[i].pkt_size);
+ if (error < 0)
+ return error;
+
+ rmi_f11_finger_handler(&f11->sensors[i]);
+ data_base_addr_offset += f11->sensors[i].pkt_size;
+ }
+ return 0;
+}
+
+static void rmi_f11_remove(struct rmi_function_container *fc)
+{
+ struct f11_data *data = fc->data;
+ int i;
+
+ for (i = 0; i < (data->dev_query.nbr_of_sensors + 1); i++) {
+ input_unregister_device(data->sensors[i].input);
+ if (data->sensors[i].sens_query.has_rel)
+ input_unregister_device(data->sensors[i].mouse_input);
+ }
+ kfree(fc->data);
+}
+
+static struct rmi_function_handler function_handler = {
+ .func = 0x11,
+ .init = rmi_f11_init,
+ .attention = rmi_f11_attention,
+ .remove = rmi_f11_remove
+};
+
+static int __init rmi_f11_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 __exit rmi_f11_module_exit(void)
+{
+ rmi_unregister_function_driver(&function_handler);
+}
+
+static ssize_t rmi_fn_11_maxPos_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u %u\n",
+ data->sensors[0].max_x, data->sensors[0].max_y);
+}
+
+static ssize_t rmi_fn_11_flip_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u %u\n",
+ data->sensors[0].axis_align.flip_x,
+ data->sensors[0].axis_align.flip_y);
+}
+
+static ssize_t rmi_fn_11_flip_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+ unsigned int new_X, new_Y;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+
+ if (sscanf(buf, "%u %u", &new_X, &new_Y) != 2)
+ return -EINVAL;
+ if (new_X < 0 || new_X > 1 || new_Y < 0 || new_Y > 1)
+ return -EINVAL;
+ instance_data->sensors[0].axis_align.flip_x = new_X;
+ instance_data->sensors[0].axis_align.flip_y = new_Y;
+
+ return count;
+}
+
+static ssize_t rmi_fn_11_swap_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->sensors[0].axis_align.swap_axes);
+}
+
+static ssize_t rmi_fn_11_swap_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+ unsigned int newSwap;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+
+ if (sscanf(buf, "%u", &newSwap) != 1)
+ return -EINVAL;
+ if (newSwap < 0 || newSwap > 1)
+ return -EINVAL;
+ instance_data->sensors[0].axis_align.swap_axes = newSwap;
+
+ f11_set_abs_params(fc, 0);
+
+ return count;
+}
+
+static ssize_t rmi_fn_11_relreport_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->
+ sensors[0].axis_align.rel_report_enabled);
+}
+
+static ssize_t rmi_fn_11_relreport_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+ unsigned int new_value;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+
+ if (sscanf(buf, "%u", &new_value) != 1)
+ return -EINVAL;
+ if (new_value < 0 || new_value > 1)
+ return -EINVAL;
+ instance_data->sensors[0].axis_align.rel_report_enabled = new_value;
+
+ return count;
+}
+
+static ssize_t rmi_fn_11_offset_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d %d\n",
+ instance_data->sensors[0].axis_align.offset_X,
+ instance_data->sensors[0].axis_align.offset_Y);
+}
+
+static ssize_t rmi_fn_11_offset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+ int new_X, new_Y;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+
+ if (sscanf(buf, "%d %d", &new_X, &new_Y) != 2)
+ return -EINVAL;
+ instance_data->sensors[0].axis_align.offset_X = new_X;
+ instance_data->sensors[0].axis_align.offset_Y = new_Y;
+
+ return count;
+}
+
+static ssize_t rmi_fn_11_clip_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u %u %u %u\n",
+ instance_data->sensors[0].axis_align.clip_X_low,
+ instance_data->sensors[0].axis_align.clip_X_high,
+ instance_data->sensors[0].axis_align.clip_Y_low,
+ instance_data->sensors[0].axis_align.clip_Y_high);
+}
+
+static ssize_t rmi_fn_11_clip_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+ unsigned int new_X_low, new_X_high, new_Y_low, new_Y_high;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ if (sscanf(buf, "%u %u %u %u",
+ &new_X_low, &new_X_high, &new_Y_low, &new_Y_high) != 4)
+ return -EINVAL;
+ if (new_X_low < 0 || new_X_low >= new_X_high || new_Y_low < 0
+ || new_Y_low >= new_Y_high)
+ return -EINVAL;
+ instance_data->sensors[0].axis_align.clip_X_low = new_X_low;
+ instance_data->sensors[0].axis_align.clip_X_high = new_X_high;
+ instance_data->sensors[0].axis_align.clip_Y_low = new_Y_low;
+ instance_data->sensors[0].axis_align.clip_Y_high = new_Y_high;
+
+ /*
+ ** for now, we assume this is sensor index 0
+ */
+ f11_set_abs_params(fc, 0);
+
+ return count;
+}
+
+static ssize_t rmi_f11_rezero_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc = NULL;
+ unsigned int rezero;
+ int retval = 0;
+ /* Command register always reads as 0, so we can just use a local. */
+ union f11_2d_commands commands = {};
+
+ fc = to_rmi_function_container(dev);
+
+ if (sscanf(buf, "%u", &rezero) != 1)
+ return -EINVAL;
+ if (rezero < 0 || rezero > 1)
+ return -EINVAL;
+
+ /* Per spec, 0 has no effect, so we skip it entirely. */
+ if (rezero) {
+ commands.rezero = 1;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+ &commands.reg, sizeof(commands.reg));
+ if (retval < 0) {
+ dev_err(dev, "%s: failed to issue rezero command, "
+ "error = %d.", __func__, retval);
+ return retval;
+ }
+ }
+
+ return count;
+}
+
+
+module_init(rmi_f11_module_init);
+module_exit(rmi_f11_module_exit);
+
+MODULE_AUTHOR("Stefan Nilsson <stefan.nilsson@unixphere.com>");
+MODULE_DESCRIPTION("RMI F11 module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_f19.c b/drivers/input/touchscreen/rmi4/rmi_f19.c
new file mode 100644
index 000000000000..9ff9ff99f91f
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_f19.c
@@ -0,0 +1,1419 @@
+/*
+ * Copyright (c) 2011 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/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#define QUERY_BASE_INDEX 1
+#define MAX_LEN 256
+
+struct f19_0d_query {
+ union {
+ struct {
+ u8 configurable:1;
+ u8 has_sensitivity_adjust:1;
+ u8 has_hysteresis_threshold:1;
+ };
+ u8 f19_0d_query0;
+ };
+ u8 f19_0d_query1:5;
+};
+
+struct f19_0d_control_0 {
+ union {
+ struct {
+ u8 button_usage:2;
+ u8 filter_mode:2;
+ };
+ u8 f19_0d_control0;
+ };
+};
+
+struct f19_0d_control_1 {
+ u8 int_enabled_button;
+};
+
+struct f19_0d_control_2 {
+ u8 single_button;
+};
+
+struct f19_0d_control_3_4 {
+ u8 sensor_map_button:7;
+ /*u8 sensitivity_button;*/
+};
+
+struct f19_0d_control_5 {
+ u8 sensitivity_adj;
+};
+struct f19_0d_control_6 {
+ u8 hysteresis_threshold;
+};
+
+struct f19_0d_control {
+ struct f19_0d_control_0 *general_control;
+ struct f19_0d_control_1 *button_int_enable;
+ struct f19_0d_control_2 *single_button_participation;
+ struct f19_0d_control_3_4 *sensor_map;
+ struct f19_0d_control_5 *all_button_sensitivity_adj;
+ struct f19_0d_control_6 *all_button_hysteresis_threshold;
+};
+/* data specific to fn $19 that needs to be kept around */
+struct f19_data {
+ struct f19_0d_control *button_control;
+ struct f19_0d_query button_query;
+ u8 button_rezero;
+ bool *button_down;
+ unsigned char button_count;
+ unsigned char button_data_buffer_size;
+ unsigned char *button_data_buffer;
+ unsigned char *button_map;
+ char input_name[MAX_LEN];
+ char input_phys[MAX_LEN];
+ struct input_dev *input;
+};
+
+static ssize_t rmi_f19_button_count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_f19_button_map_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f19_button_map_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_rezero_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_rezero_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_has_hysteresis_threshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t rmi_f19_has_sensitivity_adjust_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t rmi_f19_configurable_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t rmi_f19_filter_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_filter_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_button_usage_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_button_usage_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_interrupt_enable_button_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_interrupt_enable_button_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_single_button_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_single_button_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_sensor_map_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_sensor_map_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_sensitivity_adjust_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_sensitivity_adjust_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_hysteresis_threshold_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_hysteresis_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+
+static struct device_attribute attrs[] = {
+ __ATTR(button_count, RMI_RO_ATTR,
+ rmi_f19_button_count_show, rmi_store_error),
+ __ATTR(button_map, RMI_RW_ATTR,
+ rmi_f19_button_map_show, rmi_f19_button_map_store),
+ __ATTR(rezero, RMI_RW_ATTR,
+ rmi_f19_rezero_show, rmi_f19_rezero_store),
+ __ATTR(has_hysteresis_threshold, RMI_RO_ATTR,
+ rmi_f19_has_hysteresis_threshold_show, rmi_store_error),
+ __ATTR(has_sensitivity_adjust, RMI_RO_ATTR,
+ rmi_f19_has_sensitivity_adjust_show, rmi_store_error),
+ __ATTR(configurable, RMI_RO_ATTR,
+ rmi_f19_configurable_show, rmi_store_error),
+ __ATTR(filter_mode, RMI_RW_ATTR,
+ rmi_f19_filter_mode_show, rmi_f19_filter_mode_store),
+ __ATTR(button_usage, RMI_RW_ATTR,
+ rmi_f19_button_usage_show, rmi_f19_button_usage_store),
+ __ATTR(interrupt_enable_button, RMI_RW_ATTR,
+ rmi_f19_interrupt_enable_button_show,
+ rmi_f19_interrupt_enable_button_store),
+ __ATTR(single_button, RMI_RW_ATTR,
+ rmi_f19_single_button_show, rmi_f19_single_button_store),
+ __ATTR(sensor_map, RMI_RW_ATTR,
+ rmi_f19_sensor_map_show, rmi_f19_sensor_map_store),
+ __ATTR(sensitivity_adjust, RMI_RW_ATTR,
+ rmi_f19_sensitivity_adjust_show,
+ rmi_f19_sensitivity_adjust_store),
+ __ATTR(hysteresis_threshold, RMI_RW_ATTR,
+ rmi_f19_hysteresis_threshold_show,
+ rmi_f19_hysteresis_threshold_store)
+};
+
+
+int rmi_f19_read_control_parameters(struct rmi_device *rmi_dev,
+ struct f19_0d_control *button_control,
+ unsigned char button_count,
+ unsigned char int_button_enabled_count,
+ u8 ctrl_base_addr)
+{
+ int error = 0;
+ int i;
+
+ if (button_control->general_control) {
+ error = rmi_read_block(rmi_dev, ctrl_base_addr,
+ (u8 *)button_control->general_control,
+ sizeof(struct f19_0d_control_0));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read f19_0d_control_0, code:"
+ " %d.\n", error);
+ return error;
+ }
+ ctrl_base_addr = ctrl_base_addr +
+ sizeof(struct f19_0d_control_0);
+ }
+
+ if (button_control->button_int_enable) {
+ for (i = 0; i < int_button_enabled_count; i++) {
+ error = rmi_read_block(rmi_dev, ctrl_base_addr,
+ (u8 *)&button_control->button_int_enable[i],
+ sizeof(struct f19_0d_control_1));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read f19_0d_control_2,"
+ " code: %d.\n", error);
+ return error;
+ }
+ ctrl_base_addr = ctrl_base_addr +
+ sizeof(struct f19_0d_control_1);
+ }
+ }
+
+ if (button_control->single_button_participation) {
+ for (i = 0; i < int_button_enabled_count; i++) {
+ error = rmi_read_block(rmi_dev, ctrl_base_addr,
+ (u8 *)&button_control->
+ single_button_participation[i],
+ sizeof(struct f19_0d_control_2));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read f19_0d_control_2,"
+ " code: %d.\n", error);
+ return error;
+ }
+ ctrl_base_addr = ctrl_base_addr +
+ sizeof(struct f19_0d_control_2);
+ }
+ }
+
+ if (button_control->sensor_map) {
+ for (i = 0; i < button_count; i++) {
+ error = rmi_read_block(rmi_dev, ctrl_base_addr,
+ (u8 *)&button_control->sensor_map[i],
+ sizeof(struct f19_0d_control_3_4));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read f19_0d_control_3_4,"
+ " code: %d.\n", error);
+ return error;
+ }
+ ctrl_base_addr = ctrl_base_addr +
+ sizeof(struct f19_0d_control_3_4);
+ }
+ }
+
+ if (button_control->all_button_sensitivity_adj) {
+ error = rmi_read_block(rmi_dev, ctrl_base_addr,
+ (u8 *)button_control->
+ all_button_sensitivity_adj,
+ sizeof(struct f19_0d_control_5));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read f19_0d_control_5,"
+ " code: %d.\n", error);
+ return error;
+ }
+ ctrl_base_addr = ctrl_base_addr +
+ sizeof(struct f19_0d_control_5);
+ }
+
+ if (button_control->all_button_hysteresis_threshold) {
+ error = rmi_read_block(rmi_dev, ctrl_base_addr,
+ (u8 *)button_control->
+ all_button_hysteresis_threshold,
+ sizeof(struct f19_0d_control_6));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read f19_0d_control_6,"
+ " code: %d.\n", error);
+ return error;
+ }
+ ctrl_base_addr = ctrl_base_addr +
+ sizeof(struct f19_0d_control_6);
+ }
+ return 0;
+}
+
+
+int rmi_f19_initialize_control_parameters(struct rmi_device *rmi_dev,
+ struct f19_0d_control *button_control,
+ unsigned char button_count,
+ unsigned char int_button_enabled_count,
+ int control_base_addr)
+{
+ int error = 0;
+
+ button_control->general_control =
+ kzalloc(sizeof(struct f19_0d_control_0), GFP_KERNEL);
+ if (!button_control->general_control) {
+ dev_err(&rmi_dev->dev, "Failed to allocate"
+ " f19_0d_control_0.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ button_control->button_int_enable =
+ kzalloc(int_button_enabled_count *
+ sizeof(struct f19_0d_control_2), GFP_KERNEL);
+ if (!button_control->button_int_enable) {
+ dev_err(&rmi_dev->dev, "Failed to allocate f19_0d_control_1.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ button_control->single_button_participation =
+ kzalloc(int_button_enabled_count *
+ sizeof(struct f19_0d_control_2), GFP_KERNEL);
+ if (!button_control->single_button_participation) {
+ dev_err(&rmi_dev->dev, "Failed to allocate"
+ " f19_0d_control_2.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ button_control->sensor_map =
+ kzalloc(button_count *
+ sizeof(struct f19_0d_control_3_4), GFP_KERNEL);
+ if (!button_control->sensor_map) {
+ dev_err(&rmi_dev->dev, "Failed to allocate"
+ " f19_0d_control_3_4.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ button_control->all_button_sensitivity_adj =
+ kzalloc(sizeof(struct f19_0d_control_5), GFP_KERNEL);
+ if (!button_control->all_button_sensitivity_adj) {
+ dev_err(&rmi_dev->dev, "Failed to allocate"
+ " f19_0d_control_5.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ button_control->all_button_hysteresis_threshold =
+ kzalloc(sizeof(struct f19_0d_control_6), GFP_KERNEL);
+ if (!button_control->all_button_hysteresis_threshold) {
+ dev_err(&rmi_dev->dev, "Failed to allocate"
+ " f19_0d_control_6.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+ return rmi_f19_read_control_parameters(rmi_dev, button_control,
+ button_count, int_button_enabled_count, control_base_addr);
+
+error_exit:
+ kfree(button_control->general_control);
+ kfree(button_control->button_int_enable);
+ kfree(button_control->single_button_participation);
+ kfree(button_control->sensor_map);
+ kfree(button_control->all_button_sensitivity_adj);
+ kfree(button_control->all_button_hysteresis_threshold);
+ return error;
+}
+
+static int rmi_f19_init(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct rmi_device_platform_data *pdata;
+ struct f19_data *f19;
+ struct input_dev *input_dev;
+ u8 query_base_addr;
+ int rc;
+ int i;
+ int attr_count = 0;
+
+ dev_info(&fc->dev, "Intializing F19 values.");
+
+ f19 = kzalloc(sizeof(struct f19_data), GFP_KERNEL);
+ if (!f19) {
+ dev_err(&fc->dev, "Failed to allocate function data.\n");
+ return -ENOMEM;
+ }
+ pdata = to_rmi_platform_data(rmi_dev);
+ query_base_addr = fc->fd.query_base_addr;
+
+ /* initial all default values for f19 data here */
+ rc = rmi_read(rmi_dev, fc->fd.command_base_addr,
+ (u8 *)&f19->button_rezero);
+ if (rc < 0) {
+ dev_err(&fc->dev, "Failed to read command register.\n");
+ goto err_free_data;
+ }
+
+ f19->button_rezero = f19->button_rezero & 1;
+
+ rc = rmi_read_block(rmi_dev, query_base_addr, (u8 *)&f19->button_query,
+ sizeof(struct f19_0d_query));
+ f19->button_count = f19->button_query.f19_0d_query1;
+
+ if (rc < 0) {
+ dev_err(&fc->dev, "Failed to read query register.\n");
+ goto err_free_data;
+ }
+
+
+ /* Figure out just how much data we'll need to read. */
+ f19->button_down = kcalloc(f19->button_count,
+ sizeof(bool), GFP_KERNEL);
+ if (!f19->button_down) {
+ dev_err(&fc->dev, "Failed to allocate button state buffer.\n");
+ rc = -ENOMEM;
+ goto err_free_data;
+ }
+
+ f19->button_data_buffer_size = (f19->button_count + 7) / 8;
+ f19->button_data_buffer =
+ kcalloc(f19->button_data_buffer_size,
+ sizeof(unsigned char), GFP_KERNEL);
+ if (!f19->button_data_buffer) {
+ dev_err(&fc->dev, "Failed to allocate button data buffer.\n");
+ rc = -ENOMEM;
+ goto err_free_data;
+ }
+
+ f19->button_map = kcalloc(f19->button_count,
+ sizeof(unsigned char), GFP_KERNEL);
+ if (!f19->button_map) {
+ dev_err(&fc->dev, "Failed to allocate button map.\n");
+ rc = -ENOMEM;
+ goto err_free_data;
+ }
+
+ if (pdata) {
+ if (pdata->button_map->nbuttons != f19->button_count) {
+ dev_warn(&fc->dev,
+ "Platformdata button map size (%d) != number "
+ "of buttons on device (%d) - ignored.\n",
+ pdata->button_map->nbuttons,
+ f19->button_count);
+ } else if (!pdata->button_map->map) {
+ dev_warn(&fc->dev,
+ "Platformdata button map is missing!\n");
+ } else {
+ for (i = 0; i < pdata->button_map->nbuttons; i++)
+ f19->button_map[i] = pdata->button_map->map[i];
+ }
+ }
+
+ f19->button_control = kzalloc(sizeof(struct f19_0d_control),
+ GFP_KERNEL);
+
+ rc = rmi_f19_initialize_control_parameters(fc->rmi_dev,
+ f19->button_control, f19->button_count,
+ f19->button_data_buffer_size, fc->fd.control_base_addr);
+ if (rc < 0) {
+ dev_err(&fc->dev,
+ "Failed to initialize F19 control params.\n");
+ goto err_free_data;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&fc->dev, "Failed to allocate input device.\n");
+ rc = -ENOMEM;
+ goto err_free_data;
+ }
+
+ f19->input = input_dev;
+ snprintf(f19->input_name, MAX_LEN, "%sfn%02x", dev_name(&rmi_dev->dev),
+ fc->fd.function_number);
+ input_dev->name = f19->input_name;
+ snprintf(f19->input_phys, MAX_LEN, "%s/input0", input_dev->name);
+ input_dev->phys = f19->input_phys;
+ input_dev->dev.parent = &rmi_dev->dev;
+ input_set_drvdata(input_dev, f19);
+
+ /* Set up any input events. */
+ set_bit(EV_SYN, input_dev->evbit);
+ set_bit(EV_KEY, input_dev->evbit);
+ /* set bits for each button... */
+ for (i = 0; i < f19->button_count; i++)
+ set_bit(f19->button_map[i], input_dev->keybit);
+ rc = input_register_device(input_dev);
+ if (rc < 0) {
+ dev_err(&fc->dev, "Failed to register input device.\n");
+ goto err_free_input;
+ }
+
+ 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_free_data;
+ }
+ }
+ fc->data = f19;
+ return 0;
+
+err_free_input:
+ input_free_device(f19->input);
+
+err_free_data:
+ if (f19) {
+ kfree(f19->button_down);
+ kfree(f19->button_data_buffer);
+ kfree(f19->button_map);
+ }
+ kfree(f19);
+ for (attr_count--; attr_count >= 0; attr_count--)
+ sysfs_remove_file(&fc->dev.kobj,
+ &attrs[attr_count].attr);
+ return rc;
+}
+
+int rmi_f19_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct f19_data *f19 = fc->data;
+ u8 data_base_addr = fc->fd.data_base_addr;
+ int error;
+ int button;
+
+ /* Read the button data. */
+
+ error = rmi_read_block(rmi_dev, data_base_addr, f19->button_data_buffer,
+ f19->button_data_buffer_size);
+ if (error < 0) {
+ dev_err(&fc->dev, "%s: Failed to read button data registers.\n",
+ __func__);
+ return error;
+ }
+
+ /* Generate events for buttons that change state. */
+ for (button = 0; button < f19->button_count;
+ button++) {
+ int button_reg;
+ int button_shift;
+ bool button_status;
+
+ /* determine which data byte the button status is in */
+ button_reg = button / 7;
+ /* bit shift to get button's status */
+ button_shift = button % 8;
+ button_status =
+ ((f19->button_data_buffer[button_reg] >> button_shift)
+ & 0x01) != 0;
+
+ /* if the button state changed from the last time report it
+ * and store the new state */
+ if (button_status != f19->button_down[button]) {
+ dev_dbg(&fc->dev, "%s: Button %d (code %d) -> %d.\n",
+ __func__, button, f19->button_map[button],
+ button_status);
+ /* Generate an event here. */
+ input_report_key(f19->input, f19->button_map[button],
+ button_status);
+ f19->button_down[button] = button_status;
+ }
+ }
+
+ input_sync(f19->input); /* sync after groups of events */
+ return 0;
+}
+
+static void rmi_f19_remove(struct rmi_function_container *fc)
+{
+ struct f19_data *data = fc->data;
+ if (data) {
+ kfree(data->button_down);
+ kfree(data->button_data_buffer);
+ kfree(data->button_map);
+ input_unregister_device(data->input);
+ if (data->button_control) {
+ kfree(data->button_control->general_control);
+ kfree(data->button_control->button_int_enable);
+ kfree(data->button_control->
+ single_button_participation);
+ kfree(data->button_control->sensor_map);
+ kfree(data->button_control->
+ all_button_sensitivity_adj);
+ kfree(data->button_control->
+ all_button_hysteresis_threshold);
+ }
+ kfree(data->button_control);
+ }
+ kfree(fc->data);
+}
+
+static struct rmi_function_handler function_handler = {
+ .func = 0x19,
+ .init = rmi_f19_init,
+ .attention = rmi_f19_attention,
+ .remove = rmi_f19_remove
+};
+
+static int __init rmi_f19_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 rmi_f19_module_exit(void)
+{
+ rmi_unregister_function_driver(&function_handler);
+}
+
+static ssize_t rmi_f19_filter_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_control->general_control->filter_mode);
+
+}
+
+static ssize_t rmi_f19_filter_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ unsigned int new_value;
+ int result;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ if (sscanf(buf, "%u", &new_value) != 1) {
+ dev_err(dev,
+ "%s: Error - filter_mode_store has an "
+ "invalid len.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (new_value < 0 || new_value > 4) {
+ dev_err(dev, "%s: Error - filter_mode_store has an "
+ "invalid value %d.\n",
+ __func__, new_value);
+ return -EINVAL;
+ }
+ data->button_control->general_control->filter_mode = new_value;
+ result = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ (u8 *)data->button_control->general_control,
+ sizeof(struct f19_0d_control_0));
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write filter_mode_store to 0x%x\n",
+ __func__, fc->fd.control_base_addr);
+ return result;
+ }
+
+ return count;
+}
+
+static ssize_t rmi_f19_button_usage_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_control->general_control->button_usage);
+
+}
+
+static ssize_t rmi_f19_button_usage_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ unsigned int new_value;
+ int result;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ if (sscanf(buf, "%u", &new_value) != 1) {
+ dev_err(dev,
+ "%s: Error - button_usage_store has an "
+ "invalid len.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (new_value < 0 || new_value > 4) {
+ dev_err(dev, "%s: Error - button_usage_store has an "
+ "invalid value %d.\n",
+ __func__, new_value);
+ return -EINVAL;
+ }
+ data->button_control->general_control->button_usage = new_value;
+ result = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ (u8 *)data->button_control->general_control,
+ sizeof(struct f19_0d_control_0));
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write button_usage_store to 0x%x\n",
+ __func__, fc->fd.control_base_addr);
+ return result;
+ }
+
+ return count;
+
+}
+
+static ssize_t rmi_f19_interrupt_enable_button_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int i, len, total_len = 0;
+ char *current_buf = buf;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ /* loop through each button map value and copy its
+ * string representation into buf */
+ for (i = 0; i < data->button_count; i++) {
+ int button_reg;
+ int button_shift;
+ int interrupt_button;
+
+ button_reg = i / 7;
+ button_shift = i % 8;
+ interrupt_button =
+ ((data->button_control->
+ button_int_enable[button_reg].int_enabled_button >>
+ button_shift) & 0x01);
+
+ /* get next button mapping value and write it to buf */
+ len = snprintf(current_buf, PAGE_SIZE - total_len,
+ "%u ", interrupt_button);
+ /* 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, "%s: Failed to build interrupt button"
+ " buffer, code = %d.\n", __func__, 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_f19_interrupt_enable_button_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int i;
+ int button_count = 0;
+ int retval = count;
+ int button_reg = 0;
+ int ctrl_bass_addr;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ for (i = 0; i < data->button_count && *buf != 0;
+ i++) {
+ int button_shift;
+ int button;
+
+ button_reg = i / 7;
+ button_shift = i % 8;
+ /* get next button mapping value and store and bump up to
+ * point to next item in buf */
+ sscanf(buf, "%u", &button);
+
+ if (button != 0 && button != 1) {
+ dev_err(dev,
+ "%s: Error - interrupt enable button for"
+ " button %d is not a valid value 0x%x.\n",
+ __func__, i, button);
+ return -EINVAL;
+ }
+
+ if (button_shift == 0)
+ data->button_control->button_int_enable[button_reg].
+ int_enabled_button = 0;
+ data->button_control->button_int_enable[button_reg].
+ int_enabled_button |= (button << button_shift);
+ button_count++;
+ /* bump up buf to point to next item to read */
+ while (*buf != 0) {
+ buf++;
+ if (*(buf - 1) == ' ')
+ break;
+ }
+ }
+
+ /* Make sure the button count matches */
+ if (button_count != data->button_count) {
+ dev_err(dev,
+ "%s: Error - interrupt enable button count of %d"
+ " doesn't match device button count of %d.\n",
+ __func__, button_count, data->button_count);
+ return -EINVAL;
+ }
+
+ /* write back to the control register */
+ ctrl_bass_addr = fc->fd.control_base_addr +
+ sizeof(struct f19_0d_control_0);
+ retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+ (u8 *)data->button_control->button_int_enable,
+ sizeof(struct f19_0d_control_1)*(button_reg + 1));
+ if (retval < 0) {
+ dev_err(dev, "%s : Could not write interrupt_enable_store"
+ " to 0x%x\n", __func__, ctrl_bass_addr);
+ return retval;
+ }
+
+ return count;
+}
+
+static ssize_t rmi_f19_single_button_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int i, len, total_len = 0;
+ char *current_buf = buf;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ /* loop through each button map value and copy its
+ * string representation into buf */
+ for (i = 0; i < data->button_count; i++) {
+ int button_reg;
+ int button_shift;
+ int single_button;
+
+ button_reg = i / 7;
+ button_shift = i % 8;
+ single_button = ((data->button_control->
+ single_button_participation[button_reg].single_button
+ >> button_shift) & 0x01);
+
+ /* get next button mapping value and write it to buf */
+ len = snprintf(current_buf, PAGE_SIZE - total_len,
+ "%u ", single_button);
+ /* 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, "%s: Failed to build signle button buffer"
+ ", code = %d.\n", __func__, 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_f19_single_button_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int i;
+ int button_count = 0;
+ int retval = count;
+ int ctrl_bass_addr;
+ int button_reg = 0;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ for (i = 0; i < data->button_count && *buf != 0;
+ i++) {
+ int button_shift;
+ int button;
+
+ button_reg = i / 7;
+ button_shift = i % 8;
+ /* get next button mapping value and store and bump up to
+ * point to next item in buf */
+ sscanf(buf, "%u", &button);
+
+ if (button != 0 && button != 1) {
+ dev_err(dev,
+ "%s: Error - single button for button %d"
+ " is not a valid value 0x%x.\n",
+ __func__, i, button);
+ return -EINVAL;
+ }
+ if (button_shift == 0)
+ data->button_control->
+ single_button_participation[button_reg].
+ single_button = 0;
+ data->button_control->single_button_participation[button_reg].
+ single_button |= (button << button_shift);
+ button_count++;
+ /* bump up buf to point to next item to read */
+ while (*buf != 0) {
+ buf++;
+ if (*(buf - 1) == ' ')
+ break;
+ }
+ }
+
+ /* Make sure the button count matches */
+ if (button_count != data->button_count) {
+ dev_err(dev,
+ "%s: Error - single button count of %d doesn't match"
+ " device button count of %d.\n", __func__, button_count,
+ data->button_count);
+ return -EINVAL;
+ }
+ /* write back to the control register */
+ ctrl_bass_addr = fc->fd.control_base_addr +
+ sizeof(struct f19_0d_control_0) +
+ sizeof(struct f19_0d_control_2)*(button_reg + 1);
+ retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+ (u8 *)data->button_control->single_button_participation,
+ sizeof(struct f19_0d_control_2)*(button_reg + 1));
+ if (retval < 0) {
+ dev_err(dev, "%s : Could not write interrupt_enable_store to"
+ " 0x%x\n", __func__, ctrl_bass_addr);
+ return -EINVAL;
+ }
+ return count;
+}
+
+static ssize_t rmi_f19_sensor_map_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int i, len, total_len = 0;
+ char *current_buf = buf;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ for (i = 0; i < data->button_count; i++) {
+ len = snprintf(current_buf, PAGE_SIZE - total_len,
+ "%u ", data->button_control->sensor_map[i].
+ sensor_map_button);
+ /* 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, "%s: Failed to build sensor map buffer, "
+ "code = %d.\n", __func__, 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_f19_sensor_map_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int sensor_map;
+ int i;
+ int retval = count;
+ int button_count = 0;
+ int ctrl_bass_addr;
+ int button_reg;
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ if (data->button_query.configurable == 0) {
+ dev_err(dev,
+ "%s: Error - sensor map is not configuralbe at"
+ " run-time", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < data->button_count && *buf != 0; i++) {
+ /* get next button mapping value and store and bump up to
+ * point to next item in buf */
+ sscanf(buf, "%u", &sensor_map);
+
+ /* Make sure the key is a valid key */
+ if (sensor_map < 0 || sensor_map > 127) {
+ dev_err(dev,
+ "%s: Error - sensor map for button %d is"
+ " not a valid value 0x%x.\n",
+ __func__, i, sensor_map);
+ return -EINVAL;
+ }
+
+ data->button_control->sensor_map[i].sensor_map_button =
+ sensor_map;
+ button_count++;
+
+ /* bump up buf to point to next item to read */
+ while (*buf != 0) {
+ buf++;
+ if (*(buf - 1) == ' ')
+ break;
+ }
+ }
+
+ if (button_count != data->button_count) {
+ dev_err(dev,
+ "%s: Error - button map count of %d doesn't match device "
+ "button count of %d.\n", __func__, button_count,
+ data->button_count);
+ return -EINVAL;
+ }
+
+ /* write back to the control register */
+ button_reg = (button_count / 7) + 1;
+ ctrl_bass_addr = fc->fd.control_base_addr +
+ sizeof(struct f19_0d_control_0) +
+ sizeof(struct f19_0d_control_1)*button_reg +
+ sizeof(struct f19_0d_control_2)*button_reg;
+ retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+ (u8 *)data->button_control->sensor_map,
+ sizeof(struct f19_0d_control_3_4)*button_count);
+ if (retval < 0) {
+ dev_err(dev, "%s : Could not sensor_map_store to 0x%x\n",
+ __func__, ctrl_bass_addr);
+ return -EINVAL;
+ }
+ return count;
+}
+
+static ssize_t rmi_f19_sensitivity_adjust_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->button_control->
+ all_button_sensitivity_adj->sensitivity_adj);
+
+}
+
+static ssize_t rmi_f19_sensitivity_adjust_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ unsigned int new_value;
+ int len;
+ int ctrl_bass_addr;
+ int button_reg;
+
+ fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ if (data->button_query.configurable == 0) {
+ dev_err(dev,
+ "%s: Error - sensitivity_adjust is not"
+ " configuralbe at run-time", __func__);
+ return -EINVAL;
+ }
+
+ len = sscanf(buf, "%u", &new_value);
+ if (new_value < 0 || new_value > 31)
+ return -EINVAL;
+
+ data->button_control->all_button_sensitivity_adj->sensitivity_adj =
+ new_value;
+ /* write back to the control register */
+ button_reg = (data->button_count / 7) + 1;
+ ctrl_bass_addr = fc->fd.control_base_addr +
+ sizeof(struct f19_0d_control_0) +
+ sizeof(struct f19_0d_control_1)*button_reg +
+ sizeof(struct f19_0d_control_2)*button_reg +
+ sizeof(struct f19_0d_control_3_4)*data->button_count;
+ len = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+ (u8 *)data->button_control->all_button_sensitivity_adj,
+ sizeof(struct f19_0d_control_5));
+ if (len < 0) {
+ dev_err(dev, "%s : Could not sensitivity_adjust_store to"
+ " 0x%x\n", __func__, ctrl_bass_addr);
+ return len;
+ }
+
+ return len;
+}
+
+static ssize_t rmi_f19_hysteresis_threshold_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->button_control->
+ all_button_hysteresis_threshold->hysteresis_threshold);
+
+}
+static ssize_t rmi_f19_hysteresis_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ unsigned int new_value;
+ int len;
+ int ctrl_bass_addr;
+ int button_reg;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ len = sscanf(buf, "%u", &new_value);
+ if (new_value < 0 || new_value > 15) {
+ dev_err(dev, "%s: Error - hysteresis_threshold_store has an "
+ "invalid value %d.\n",
+ __func__, new_value);
+ return -EINVAL;
+ }
+ data->button_control->all_button_hysteresis_threshold->
+ hysteresis_threshold = new_value;
+ /* write back to the control register */
+ button_reg = (data->button_count / 7) + 1;
+ ctrl_bass_addr = fc->fd.control_base_addr +
+ sizeof(struct f19_0d_control_0) +
+ sizeof(struct f19_0d_control_1)*button_reg +
+ sizeof(struct f19_0d_control_2)*button_reg +
+ sizeof(struct f19_0d_control_3_4)*data->button_count+
+ sizeof(struct f19_0d_control_5);
+ len = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+ (u8 *)data->button_control->all_button_sensitivity_adj,
+ sizeof(struct f19_0d_control_6));
+ if (len < 0) {
+ dev_err(dev, "%s : Could not write all_button hysteresis "
+ "threshold to 0x%x\n", __func__, ctrl_bass_addr);
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t rmi_f19_has_hysteresis_threshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_query.has_hysteresis_threshold);
+}
+
+static ssize_t rmi_f19_has_sensitivity_adjust_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_query.has_sensitivity_adjust);
+}
+
+static ssize_t rmi_f19_configurable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_query.configurable);
+}
+
+static ssize_t rmi_f19_rezero_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_rezero);
+
+}
+
+static ssize_t rmi_f19_rezero_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_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->button_rezero = new_value & 1;
+ len = rmi_write(fc->rmi_dev, fc->fd.command_base_addr,
+ data->button_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;
+}
+
+static ssize_t rmi_f19_button_count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_count);
+}
+
+static ssize_t rmi_f19_button_map_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int i, len, total_len = 0;
+ char *current_buf = buf;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ /* loop through each button map value and copy its
+ * string representation into buf */
+ for (i = 0; i < data->button_count; i++) {
+ /* get next button mapping value and write it to buf */
+ len = snprintf(current_buf, PAGE_SIZE - total_len,
+ "%u ", data->button_map[i]);
+ /* 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, "%s: Failed to build button map buffer, "
+ "code = %d.\n", __func__, 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_f19_button_map_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ unsigned int button;
+ int i;
+ int retval = count;
+ int button_count = 0;
+ unsigned char temp_button_map[KEY_MAX];
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ /* Do validation on the button map data passed in. Store button
+ * mappings into a temp buffer and then verify button count and
+ * data prior to clearing out old button mappings and storing the
+ * new ones. */
+ for (i = 0; i < data->button_count && *buf != 0;
+ i++) {
+ /* get next button mapping value and store and bump up to
+ * point to next item in buf */
+ sscanf(buf, "%u", &button);
+
+ /* Make sure the key is a valid key */
+ if (button > KEY_MAX) {
+ dev_err(dev,
+ "%s: Error - button map for button %d is not a"
+ " valid value 0x%x.\n", __func__, i, button);
+ retval = -EINVAL;
+ goto err_ret;
+ }
+
+ temp_button_map[i] = button;
+ button_count++;
+
+ /* bump up buf to point to next item to read */
+ while (*buf != 0) {
+ buf++;
+ if (*(buf - 1) == ' ')
+ break;
+ }
+ }
+
+ /* Make sure the button count matches */
+ if (button_count != data->button_count) {
+ dev_err(dev,
+ "%s: Error - button map count of %d doesn't match device "
+ "button count of %d.\n", __func__, button_count,
+ data->button_count);
+ retval = -EINVAL;
+ goto err_ret;
+ }
+
+ /* Clear the key bits for the old button map. */
+ for (i = 0; i < button_count; i++)
+ clear_bit(data->button_map[i], data->input->keybit);
+
+ /* Switch to the new map. */
+ memcpy(data->button_map, temp_button_map,
+ data->button_count);
+
+ /* Loop through the key map and set the key bit for the new mapping. */
+ for (i = 0; i < button_count; i++)
+ set_bit(data->button_map[i], data->input->keybit);
+
+err_ret:
+ return retval;
+}
+
+module_init(rmi_f19_module_init);
+module_exit(rmi_f19_module_exit);
+
+MODULE_AUTHOR("Vivian Ly <vly@synaptics.com>");
+MODULE_DESCRIPTION("RMI F19 module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_f34.c b/drivers/input/touchscreen/rmi4/rmi_f34.c
new file mode 100644
index 000000000000..33e84d2cfb24
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_f34.c
@@ -0,0 +1,821 @@
+/*
+ * Copyright (c) 2011 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/rmi.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include "rmi_driver.h"
+
+/* define fn $34 commands */
+#define WRITE_FW_BLOCK 0x2
+#define ERASE_ALL 0x3
+#define READ_CONFIG_BLOCK 0x5
+#define WRITE_CONFIG_BLOCK 0x6
+#define ERASE_CONFIG 0x7
+#define ENABLE_FLASH_PROG 0xf
+
+#define STATUS_IN_PROGRESS 0xff
+#define STATUS_IDLE 0x80
+
+#define PDT_START_SCAN_LOCATION 0x00e9
+#define PDT_END_SCAN_LOCATION 0x0005
+
+#define BLK_SZ_OFF 3
+#define IMG_BLK_CNT_OFF 5
+#define CFG_BLK_CNT_OFF 7
+
+#define BLK_NUM_OFF 2
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+#define KERNEL_VERSION_ABOVE_2_6_32 1
+#endif
+
+/* data specific to fn $34 that needs to be kept around */
+struct rmi_fn_34_data {
+ unsigned char status;
+ unsigned char cmd;
+ unsigned short bootloaderid;
+ unsigned short blocksize;
+ unsigned short imageblockcount;
+ unsigned short configblockcount;
+ unsigned short blocknum;
+ bool inflashprogmode;
+};
+
+static ssize_t rmi_fn_34_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_34_cmd_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_34_cmd_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_34_data_read(struct file *data_file, struct kobject *kobj,
+ struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_34_data_write(struct file *data_file,
+ struct kobject *kobj,
+ struct bin_attribute *attributes, char *buf,
+ loff_t pos, size_t count);
+#else
+static ssize_t rmi_fn_34_data_read(struct kobject *kobj,
+ struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_34_data_write(struct kobject *kobj,
+ struct bin_attribute *attributes, char *buf,
+ loff_t pos, size_t count);
+#endif
+
+static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_34_bootloaderid_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_34_blocksize_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_34_imageblockcount_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_34_configblockcount_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_34_blocknum_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_34_blocknum_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_34_rescanPDT_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static struct device_attribute attrs[] = {
+ __ATTR(status, RMI_RO_ATTR,
+ rmi_fn_34_status_show, rmi_store_error),
+ /* Also, sysfs will need to have a file set up to distinguish
+ * between commands - like Config write/read, Image write/verify. */
+ __ATTR(cmd, RMI_RW_ATTR,
+ rmi_fn_34_cmd_show, rmi_fn_34_cmd_store),
+ __ATTR(bootloaderid, RMI_RW_ATTR,
+ rmi_fn_34_bootloaderid_show, rmi_fn_34_bootloaderid_store),
+ __ATTR(blocksize, RMI_RO_ATTR,
+ rmi_fn_34_blocksize_show, rmi_store_error),
+ __ATTR(imageblockcount, RMI_RO_ATTR,
+ rmi_fn_34_imageblockcount_show, rmi_store_error),
+ __ATTR(configblockcount, RMI_RO_ATTR,
+ rmi_fn_34_configblockcount_show, rmi_store_error),
+ __ATTR(blocknum, RMI_RW_ATTR,
+ rmi_fn_34_blocknum_show, rmi_fn_34_blocknum_store),
+ __ATTR(rescanPDT, RMI_WO_ATTR,
+ rmi_show_error, rmi_fn_34_rescanPDT_store)
+};
+
+struct bin_attribute dev_attr_data = {
+ .attr = {
+ .name = "data",
+ .mode = 0666},
+ .size = 0,
+ .read = rmi_fn_34_data_read,
+ .write = rmi_fn_34_data_write,
+};
+
+static int rmi_f34_init(struct rmi_function_container *fc)
+{
+ int retval = 0;
+ int attr_count = 0;
+ struct rmi_fn_34_data *f34;
+ u16 query_base_addr;
+ u16 control_base_addr;
+ unsigned char buf[2];
+
+ dev_info(&fc->dev, "Intializing f34 values.");
+
+ /* init instance data, fill in values and create any sysfs files */
+ f34 = kzalloc(sizeof(struct rmi_fn_34_data), GFP_KERNEL);
+ if (!f34) {
+ dev_err(&fc->dev, "Failed to allocate rmi_fn_34_data.\n");
+ return -ENOMEM;
+ }
+
+ fc->data = f34;
+
+ /* get the Bootloader ID and Block Size. */
+ query_base_addr = fc->fd.query_base_addr;
+ control_base_addr = fc->fd.control_base_addr;
+
+ retval = rmi_read_block(fc->rmi_dev, query_base_addr, buf,
+ ARRAY_SIZE(buf));
+
+ if (retval < 0) {
+ dev_err(&fc->dev, "Could not read bootloaderid from 0x%04x.\n",
+ query_base_addr);
+ goto exit_free_data;
+ }
+ batohs(&f34->bootloaderid, buf);
+
+ retval = rmi_read_block(fc->rmi_dev, query_base_addr + BLK_SZ_OFF, buf,
+ ARRAY_SIZE(buf));
+
+ if (retval < 0) {
+ dev_err(&fc->dev, "Could not read block size from 0x%04x, "
+ "error=%d.\n", query_base_addr + BLK_SZ_OFF, retval);
+ goto exit_free_data;
+ }
+ batohs(&f34->blocksize, buf);
+
+ /* Get firmware image block count and store it in the instance data */
+ retval = rmi_read_block(fc->rmi_dev, query_base_addr + IMG_BLK_CNT_OFF,
+ buf, ARRAY_SIZE(buf));
+
+ if (retval < 0) {
+ dev_err(&fc->dev, "Couldn't read image block count from 0x%x, "
+ "error=%d.\n", query_base_addr + IMG_BLK_CNT_OFF,
+ retval);
+ goto exit_free_data;
+ }
+ batohs(&f34->imageblockcount, buf);
+
+ /* Get config block count and store it in the instance data */
+ retval = rmi_read_block(fc->rmi_dev, query_base_addr + 7, buf,
+ ARRAY_SIZE(buf));
+
+ if (retval < 0) {
+ dev_err(&fc->dev, "Couldn't read config block count from 0x%x, "
+ "error=%d.\n", query_base_addr + CFG_BLK_CNT_OFF,
+ retval);
+ goto exit_free_data;
+ }
+ batohs(&f34->configblockcount, buf);
+
+ /* We need a sysfs file for the image/config block to write or read.
+ * Set up sysfs bin file for binary data block. Since the image is
+ * already in our format there is no need to convert the data for
+ * endianess. */
+ retval = sysfs_create_bin_file(&fc->dev.kobj,
+ &dev_attr_data);
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to create sysfs file for F34 data "
+ "(error = %d).\n", retval);
+ retval = -ENODEV;
+ goto exit_free_data;
+ }
+
+ dev_dbg(&fc->dev, "Creating sysfs files.\n");
+ 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);
+ retval = -ENODEV;
+ goto exit_free_attrs;
+ }
+ }
+
+ return retval;
+
+exit_free_attrs:
+ for (attr_count--; attr_count >= 0; attr_count--)
+ sysfs_remove_file(&fc->dev.kobj,
+ &attrs[attr_count].attr);
+exit_free_data:
+ kfree(f34);
+ return retval;
+}
+
+static int f34_read_status(struct rmi_function_container *fc)
+{
+ struct rmi_fn_34_data *instance_data = fc->data;
+ u16 data_base_addr = fc->fd.data_base_addr;
+ u8 status;
+ int retval;
+
+ /* Read the Fn $34 status from F34_Flash_Data3 to see the previous
+ * commands status. F34_Flash_Data3 will be the address after the
+ * 2 block number registers plus blocksize Data registers.
+ * inform user space - through a sysfs param. */
+ retval = rmi_read(fc->rmi_dev,
+ data_base_addr + instance_data->blocksize +
+ BLK_NUM_OFF, &status);
+
+ if (retval < 0) {
+ dev_err(&fc->dev, "Could not read status from 0x%x\n",
+ data_base_addr + instance_data->blocksize + BLK_NUM_OFF);
+ status = 0xff; /* failure */
+ }
+
+ /* set a sysfs value that the user mode can read - only
+ * upper 4 bits are the status. successful is $80, anything
+ * else is failure */
+ instance_data->status = status & 0xf0;
+
+ /* put mode into Flash Prog Mode when we successfully do
+ * an Enable Flash Prog cmd. */
+ if ((instance_data->status == STATUS_IDLE) &&
+ (instance_data->cmd == ENABLE_FLASH_PROG))
+ instance_data->inflashprogmode = true;
+
+ return retval;
+}
+
+int rmi_f34_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+ return f34_read_status(fc);
+}
+
+static struct rmi_function_handler function_handler = {
+ .func = 0x34,
+ .init = rmi_f34_init,
+ .attention = rmi_f34_attention
+};
+
+static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->bootloaderid);
+}
+
+static ssize_t rmi_fn_34_bootloaderid_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int error;
+ unsigned long val;
+ unsigned char data[2];
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ u16 data_base_addr;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ /* need to convert the string data to an actual value */
+ error = strict_strtoul(buf, 10, &val);
+
+ if (error)
+ return error;
+
+ instance_data->bootloaderid = val;
+
+ /* Write the Bootloader ID key data back to the first two Block
+ * Data registers (F34_Flash_Data2.0 and F34_Flash_Data2.1). */
+ hstoba(data, (unsigned short)val);
+ data_base_addr = fc->fd.data_base_addr;
+
+ error = rmi_write_block(fc->rmi_dev,
+ data_base_addr + BLK_NUM_OFF,
+ data,
+ ARRAY_SIZE(data));
+
+ if (error < 0) {
+ dev_err(dev, "%s : Could not write bootloader id to 0x%x\n",
+ __func__, data_base_addr + BLK_NUM_OFF);
+ return error;
+ }
+
+ return count;
+}
+
+static ssize_t rmi_fn_34_blocksize_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->blocksize);
+}
+
+static ssize_t rmi_fn_34_imageblockcount_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->imageblockcount);
+}
+
+static ssize_t rmi_fn_34_configblockcount_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->configblockcount);
+}
+
+static ssize_t rmi_fn_34_status_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ int retval;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ retval = f34_read_status(fc);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->status);
+}
+
+static ssize_t rmi_fn_34_cmd_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->cmd);
+}
+
+static ssize_t rmi_fn_34_cmd_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ unsigned long val;
+ u16 data_base_addr;
+ int error;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ data_base_addr = fc->fd.data_base_addr;
+
+ /* need to convert the string data to an actual value */
+ error = strict_strtoul(buf, 10, &val);
+ if (error)
+ return error;
+
+ /* make sure we are in Flash Prog mode for all cmds except the
+ * Enable Flash Programming cmd - otherwise we are in error */
+ if ((val != ENABLE_FLASH_PROG) && !instance_data->inflashprogmode) {
+ dev_err(dev, "%s: CANNOT SEND CMD %d TO SENSOR - "
+ "NOT IN FLASH PROG MODE\n"
+ , __func__, data_base_addr);
+ return -EINVAL;
+ }
+
+ instance_data->cmd = val;
+
+ /* Validate command value and (if necessary) write it to the command
+ * register.
+ */
+ switch (instance_data->cmd) {
+ case ENABLE_FLASH_PROG:
+ case ERASE_ALL:
+ case ERASE_CONFIG:
+ case WRITE_FW_BLOCK:
+ case READ_CONFIG_BLOCK:
+ case WRITE_CONFIG_BLOCK:
+ /* Reset the status to indicate we are in progress on a cmd. */
+ /* The status will change when the ATTN interrupt happens
+ and the status of the cmd that was issued is read from
+ the F34_Flash_Data3 register - result should be 0x80 for
+ success - any other value indicates an error */
+
+ /* Issue the command to the device. */
+ error = rmi_write(fc->rmi_dev,
+ data_base_addr + instance_data->blocksize +
+ BLK_NUM_OFF, instance_data->cmd);
+
+ if (error < 0) {
+ dev_err(dev, "%s: Could not write command 0x%02x "
+ "to 0x%04x\n", __func__, instance_data->cmd,
+ data_base_addr + instance_data->blocksize +
+ BLK_NUM_OFF);
+ return error;
+ }
+
+ if (instance_data->cmd == ENABLE_FLASH_PROG)
+ instance_data->inflashprogmode = true;
+
+ /* set status to indicate we are in progress */
+ instance_data->status = STATUS_IN_PROGRESS;
+ break;
+ default:
+ dev_dbg(dev, "%s: RMI4 function $34 - "
+ "unknown command 0x%02lx.\n", __func__, val);
+ count = -EINVAL;
+ break;
+ }
+
+ return count;
+}
+
+static ssize_t rmi_fn_34_blocknum_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->blocknum);
+}
+
+static ssize_t rmi_fn_34_blocknum_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int error;
+ unsigned long val;
+ unsigned char data[2];
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ u16 data_base_addr;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ data_base_addr = fc->fd.data_base_addr;
+
+ /* need to convert the string data to an actual value */
+ error = strict_strtoul(buf, 10, &val);
+
+ if (error)
+ return error;
+
+ instance_data->blocknum = val;
+
+ /* Write the Block Number data back to the first two Block
+ * Data registers (F34_Flash_Data_0 and F34_Flash_Data_1). */
+ hstoba(data, (unsigned short)val);
+
+ error = rmi_write_block(fc->rmi_dev,
+ data_base_addr,
+ data,
+ ARRAY_SIZE(data));
+
+ if (error < 0) {
+ dev_err(dev, "%s : Could not write block number %u to 0x%x\n",
+ __func__, instance_data->blocknum, data_base_addr);
+ return error;
+ }
+
+ return count;
+}
+
+static ssize_t rmi_fn_34_rescanPDT_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *driver_data;
+ struct pdt_entry pdt_entry;
+ bool fn01found = false;
+ bool fn34found = false;
+ unsigned int rescan;
+ int irq_count = 0;
+ int retval = 0;
+ int i;
+
+ /* Rescan of the PDT is needed since issuing the Flash Enable cmd
+ * the device registers for Fn$01 and Fn$34 moving around because
+ * of the change from Bootloader mode to Flash Programming mode
+ * may change to a different PDT with only Fn$01 and Fn$34 that
+ * could have addresses for query, control, data, command registers
+ * that differ from the PDT scan done at device initialization. */
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ rmi_dev = fc->rmi_dev;
+ driver_data = rmi_get_driverdata(rmi_dev);
+
+ /* Make sure we are only in Flash Programming mode - DON'T
+ * ALLOW THIS IN UI MODE. */
+ if (instance_data->cmd != ENABLE_FLASH_PROG) {
+ dev_err(dev, "%s: NOT IN FLASH PROG MODE - CAN'T RESCAN PDT.\n"
+ , __func__);
+ return -EINVAL;
+ }
+
+ /* The only good value to write to this is 1, we allow 0, but with
+ * no effect (this is consistent with the way the command bit works. */
+ if (sscanf(buf, "%u", &rescan) != 1)
+ return -EINVAL;
+ if (rescan < 0 || rescan > 1)
+ return -EINVAL;
+
+ /* 0 has no effect, so we skip it entirely. */
+ if (rescan) {
+ /* rescan the PDT - filling in Fn01 and Fn34 addresses -
+ * this is only temporary - the device will need to be reset
+ * to return the PDT to the normal values. */
+
+ /* mini-parse the PDT - we only have to get Fn$01 and Fn$34 and
+ since we are Flash Programming mode we only have page 0. */
+ for (i = PDT_START_SCAN_LOCATION; i >= PDT_END_SCAN_LOCATION;
+ i -= sizeof(pdt_entry)) {
+ retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry,
+ sizeof(pdt_entry));
+ if (retval != sizeof(pdt_entry)) {
+ dev_err(dev, "%s: err frm rmi_read_block pdt "
+ "entry data from PDT, "
+ "error = %d.", __func__, retval);
+ return retval;
+ }
+
+ if ((pdt_entry.function_number == 0x00) ||
+ (pdt_entry.function_number == 0xff))
+ break;
+
+ dev_dbg(dev, "%s: Found F%.2X\n",
+ __func__, pdt_entry.function_number);
+
+ /* f01 found - just fill in the new addresses in
+ * the existing fc. */
+ if (pdt_entry.function_number == 0x01) {
+ struct rmi_function_container *f01_fc =
+ driver_data->f01_container;
+ fn01found = true;
+ f01_fc->fd.query_base_addr =
+ pdt_entry.query_base_addr;
+ f01_fc->fd.command_base_addr =
+ pdt_entry.command_base_addr;
+ f01_fc->fd.control_base_addr =
+ pdt_entry.control_base_addr;
+ f01_fc->fd.data_base_addr =
+ pdt_entry.data_base_addr;
+ f01_fc->fd.function_number =
+ pdt_entry.function_number;
+ f01_fc->fd.interrupt_source_count =
+ pdt_entry.interrupt_source_count;
+ f01_fc->num_of_irqs =
+ pdt_entry.interrupt_source_count;
+ f01_fc->irq_pos = irq_count;
+
+ irq_count += f01_fc->num_of_irqs;
+
+ if (fn34found)
+ break;
+ }
+
+ /* f34 found - just fill in the new addresses in
+ * the existing fc. */
+ if (pdt_entry.function_number == 0x34) {
+ fn34found = true;
+ fc->fd.query_base_addr =
+ pdt_entry.query_base_addr;
+ fc->fd.command_base_addr =
+ pdt_entry.command_base_addr;
+ fc->fd.control_base_addr =
+ pdt_entry.control_base_addr;
+ fc->fd.data_base_addr =
+ pdt_entry.data_base_addr;
+ fc->fd.function_number =
+ pdt_entry.function_number;
+ fc->fd.interrupt_source_count =
+ pdt_entry.interrupt_source_count;
+ fc->num_of_irqs =
+ pdt_entry.interrupt_source_count;
+ fc->irq_pos = irq_count;
+
+ irq_count += fc->num_of_irqs;
+
+ if (fn01found)
+ break;
+ }
+
+ }
+
+ if (!fn01found || !fn34found) {
+ dev_err(dev, "%s: failed to find fn$01 or fn$34 trying "
+ "to do rescan PDT.\n"
+ , __func__);
+ return -EINVAL;
+ }
+ }
+
+ return count;
+}
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_34_data_read(struct file *data_file,
+ struct kobject *kobj,
+ struct bin_attribute *attributes,
+ char *buf,
+ loff_t pos,
+ size_t count)
+#else
+static ssize_t rmi_fn_34_data_read(struct kobject *kobj,
+ struct bin_attribute *attributes,
+ char *buf,
+ loff_t pos,
+ size_t count)
+#endif
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ u16 data_base_addr;
+ int error;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ data_base_addr = fc->fd.data_base_addr;
+
+ if (count != instance_data->blocksize) {
+ dev_err(dev,
+ "%s : Incorrect F34 block size %d. "
+ "Expected size %d.\n",
+ __func__, count, instance_data->blocksize);
+ return -EINVAL;
+ }
+
+ /* Read the data from flash into buf. The app layer will be blocked
+ * at reading from the sysfs file. When we return the count (or
+ * error if we fail) the app will resume. */
+ error = rmi_read_block(fc->rmi_dev, data_base_addr + BLK_NUM_OFF,
+ (unsigned char *)buf, count);
+
+ if (error < 0) {
+ dev_err(dev, "%s : Could not read data from 0x%04x\n",
+ __func__, data_base_addr + BLK_NUM_OFF);
+ return error;
+ }
+
+ return count;
+}
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_34_data_write(struct file *data_file,
+ struct kobject *kobj,
+ struct bin_attribute *attributes,
+ char *buf,
+ loff_t pos,
+ size_t count)
+#else
+static ssize_t rmi_fn_34_data_write(struct kobject *kobj,
+ struct bin_attribute *attributes,
+ char *buf,
+ loff_t pos,
+ size_t count)
+#endif
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ u16 data_base_addr;
+ int error;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ data_base_addr = fc->fd.data_base_addr;
+
+ /* Write the data from buf to flash. The app layer will be
+ * blocked at writing to the sysfs file. When we return the
+ * count (or error if we fail) the app will resume. */
+
+ if (count != instance_data->blocksize) {
+ dev_err(dev,
+ "%s : Incorrect F34 block size %d. "
+ "Expected size %d.\n",
+ __func__, count, instance_data->blocksize);
+ return -EINVAL;
+ }
+
+ /* Write the data block - only if the count is non-zero */
+ if (count) {
+ error = rmi_write_block(fc->rmi_dev,
+ data_base_addr + BLK_NUM_OFF,
+ (unsigned char *)buf,
+ count);
+
+ if (error < 0) {
+ dev_err(dev, "%s : Could not write block data "
+ "to 0x%x\n", __func__,
+ data_base_addr + BLK_NUM_OFF);
+ return error;
+ }
+ }
+
+ return count;
+}
+
+static int __init rmi_f34_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 rmi_f34_module_exit(void)
+{
+ rmi_unregister_function_driver(&function_handler);
+}
+
+module_init(rmi_f34_module_init);
+module_exit(rmi_f34_module_exit);
+
+MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
+MODULE_DESCRIPTION("RMI f34 module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_f54.c b/drivers/input/touchscreen/rmi4/rmi_f54.c
new file mode 100644
index 000000000000..11bb0b934bef
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_f54.c
@@ -0,0 +1,1347 @@
+
+/*
+ * Copyright (c) 2011 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/hrtimer.h>
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include "rmi_driver.h"
+
+/* Set this to 1 for raw hex dump of returned data. */
+#define RAW_HEX 0
+/* Set this to 1 for human readable dump of returned data. */
+#define HUMAN_READABLE 0
+/* The watchdog timer can be useful when debugging certain firmware related
+ * issues.
+ */
+#define F54_WATCHDOG 1
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+#define KERNEL_VERSION_ABOVE_2_6_32 1
+#endif
+
+/* define fn $54 commands */
+#define GET_REPORT 1
+#define FORCE_CAL 2
+
+/* status */
+#define BUSY 1
+#define IDLE 0
+
+/* Offsets for data */
+#define RMI_F54_REPORT_DATA_OFFSET 3
+#define RMI_F54_FIFO_OFFSET 1
+#define RMI_F54_NUM_TX_OFFSET 1
+#define RMI_F54_NUM_RX_OFFSET 0
+
+/* Fixed sizes of reports */
+#define RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE 4
+#define RMI_54_HIGH_RESISTANCE_SIZE 6
+
+/* definitions for F54 Query Registers in ultra-portable unionstruct form */
+struct f54_ad_query {
+ /* query 0 */
+ u8 number_of_receiver_electrodes;
+
+ /* query 1 */
+ u8 number_of_transmitter_electrodes;
+
+ union {
+ struct {
+ /* query2 */
+ u8 f54_ad_query2_b0__1:2;
+ u8 has_baseline:1;
+ u8 has_image8:1;
+ u8 f54_ad_query2_b4__5:2;
+ u8 has_image16:1;
+ u8 f54_ad_query2_b7:1;
+ };
+ u8 f54_ad_query2;
+ };
+
+ /* query 3.0 and 3.1 */
+ u16 clock_rate;
+
+ /* query 4 */
+ u8 touch_controller_family;
+
+ /* query 5 */
+ union {
+ struct {
+ u8 has_pixel_touch_threshold_adjustment:1;
+ u8 f54_ad_query5_b1__7:7;
+ };
+ u8 f54_ad_query5;
+ };
+
+ /* query 6 */
+ union {
+ struct {
+ u8 has_sensor_assignment:1;
+ u8 has_interference_metric:1;
+ u8 has_sense_frequency_control:1;
+ u8 has_firmware_noise_mitigation:1;
+ u8 f54_ad_query6_b4:1;
+ u8 has_two_byte_report_rate:1;
+ u8 has_one_byte_report_rate:1;
+ u8 has_relaxation_control:1;
+ };
+ u8 f54_ad_query6;
+ };
+
+ /* query 7 */
+ union {
+ struct {
+ u8 curve_compensation_mode:2;
+ u8 f54_ad_query7_b2__7:6;
+ };
+ u8 f54_ad_query7;
+ };
+
+ /* query 8 */
+ union {
+ struct {
+ u8 f54_ad_query2_b0:1;
+ u8 has_iir_filter:1;
+ u8 has_cmn_removal:1;
+ u8 has_cmn_maximum:1;
+ u8 has_pixel_threshold_hysteresis:1;
+ u8 has_edge_compensation:1;
+ u8 has_perf_frequency_noisecontrol:1;
+ u8 f54_ad_query8_b7:1;
+ };
+ u8 f54_ad_query8;
+ };
+
+ u8 f54_ad_query9;
+ u8 f54_ad_query10;
+ u8 f54_ad_query11;
+
+ /* query 12 */
+ union {
+ struct {
+ u8 number_of_sensing_frequencies:4;
+ u8 f54_ad_query12_b4__7:4;
+ };
+ u8 f54_ad_query12;
+ };
+};
+
+/* define report types */
+enum f54_report_types {
+ /* The numbering should follow automatically, here for clarity */
+ F54_8BIT_IMAGE = 1,
+ F54_16BIT_IMAGE = 2,
+ F54_RAW_16BIT_IMAGE = 3,
+ F54_HIGH_RESISTANCE = 4,
+ F54_TX_TO_TX_SHORT = 5,
+ F54_RX_TO_RX1 = 7,
+ F54_TRUE_BASELINE = 9,
+ F54_FULL_RAW_CAP_MIN_MAX = 13,
+ F54_RX_OPENS1 = 14,
+ F54_TX_OPEN = 15,
+ F54_TX_TO_GROUND = 16,
+ F54_RX_TO_RX2 = 17,
+ F54_RX_OPENS2 = 18,
+ F54_FULL_RAW_CAP = 19,
+ F54_FULL_RAW_CAP_RX_COUPLING_COMP = 20
+};
+
+/* data specific to fn $54 that needs to be kept around */
+struct rmi_fn_54_data {
+ struct f54_ad_query query;
+ u8 cmd;
+ enum f54_report_types report_type;
+ u16 fifoindex;
+ signed char status;
+ bool no_auto_cal;
+ /*
+ * May need to do something to make sure this reflects what is currently
+ * in data.
+ */
+ unsigned int report_size;
+ unsigned char *report_data;
+ unsigned int bufsize;
+ struct mutex data_mutex;
+ struct lock_class_key data_key;
+ struct mutex status_mutex;
+ struct lock_class_key status_key;
+#if F54_WATCHDOG
+ struct hrtimer watchdog;
+#endif
+ struct rmi_function_container *fc;
+ struct work_struct work;
+};
+
+/* sysfs functions */
+static ssize_t rmi_fn_54_report_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_report_type_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_get_report_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_force_cal_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+#else
+static ssize_t rmi_fn_54_data_read(struct kobject *kobj,
+#endif
+ struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_54_num_rx_electrodes_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_num_tx_electrodes_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_image16_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_image8_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_baseline_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_clock_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_54_touch_controller_family_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_54_has_pixel_touch_threshold_adjustment_show(
+ struct device *dev, struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_sensor_assignment_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_interference_metric_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_sense_frequency_control_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_firmware_noise_mitigation_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_two_byte_report_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_one_byte_report_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_relaxation_control_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_curve_compensation_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_iir_filter_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_cmn_removal_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_cmn_maximum_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_pixel_threshold_hysteresis_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_edge_compensation_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_perf_frequency_noisecontrol_show(
+ struct device *dev, struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_number_of_sensing_frequencies_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_no_auto_cal_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_no_auto_cal_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_fifoindex_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_fifoindex_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static struct device_attribute attrs[] = {
+ __ATTR(report_type, RMI_RW_ATTR,
+ rmi_fn_54_report_type_show, rmi_fn_54_report_type_store),
+ __ATTR(get_report, RMI_WO_ATTR,
+ rmi_show_error, rmi_fn_54_get_report_store),
+ __ATTR(force_cal, RMI_WO_ATTR,
+ rmi_show_error, rmi_fn_54_force_cal_store),
+ __ATTR(status, RMI_RO_ATTR,
+ rmi_fn_54_status_show, rmi_store_error),
+ __ATTR(num_rx_electrodes, RMI_RO_ATTR,
+ rmi_fn_54_num_rx_electrodes_show, rmi_store_error),
+ __ATTR(num_tx_electrodes, RMI_RO_ATTR,
+ rmi_fn_54_num_tx_electrodes_show, rmi_store_error),
+ __ATTR(has_image16, RMI_RO_ATTR,
+ rmi_fn_54_has_image16_show, rmi_store_error),
+ __ATTR(has_image8, RMI_RO_ATTR,
+ rmi_fn_54_has_image8_show, rmi_store_error),
+ __ATTR(has_baseline, RMI_RO_ATTR,
+ rmi_fn_54_has_baseline_show, rmi_store_error),
+ __ATTR(clock_rate, RMI_RO_ATTR,
+ rmi_fn_54_clock_rate_show, rmi_store_error),
+ __ATTR(touch_controller_family, RMI_RO_ATTR,
+ rmi_fn_54_touch_controller_family_show, rmi_store_error),
+ __ATTR(has_pixel_touch_threshold_adjustment, RMI_RO_ATTR,
+ rmi_fn_54_has_pixel_touch_threshold_adjustment_show
+ , rmi_store_error),
+ __ATTR(has_sensor_assignment, RMI_RO_ATTR,
+ rmi_fn_54_has_sensor_assignment_show, rmi_store_error),
+ __ATTR(has_interference_metric, RMI_RO_ATTR,
+ rmi_fn_54_has_interference_metric_show, rmi_store_error),
+ __ATTR(has_sense_frequency_control, RMI_RO_ATTR,
+ rmi_fn_54_has_sense_frequency_control_show, rmi_store_error),
+ __ATTR(has_firmware_noise_mitigation, RMI_RO_ATTR,
+ rmi_fn_54_has_firmware_noise_mitigation_show, rmi_store_error),
+ __ATTR(has_two_byte_report_rate, RMI_RO_ATTR,
+ rmi_fn_54_has_two_byte_report_rate_show, rmi_store_error),
+ __ATTR(has_one_byte_report_rate, RMI_RO_ATTR,
+ rmi_fn_54_has_one_byte_report_rate_show, rmi_store_error),
+ __ATTR(has_relaxation_control, RMI_RO_ATTR,
+ rmi_fn_54_has_relaxation_control_show, rmi_store_error),
+ __ATTR(curve_compensation_mode, RMI_RO_ATTR,
+ rmi_fn_54_curve_compensation_mode_show, rmi_store_error),
+ __ATTR(has_iir_filter, RMI_RO_ATTR,
+ rmi_fn_54_has_iir_filter_show, rmi_store_error),
+ __ATTR(has_cmn_removal, RMI_RO_ATTR,
+ rmi_fn_54_has_cmn_removal_show, rmi_store_error),
+ __ATTR(has_cmn_maximum, RMI_RO_ATTR,
+ rmi_fn_54_has_cmn_maximum_show, rmi_store_error),
+ __ATTR(has_pixel_threshold_hysteresis, RMI_RO_ATTR,
+ rmi_fn_54_has_pixel_threshold_hysteresis_show, rmi_store_error),
+ __ATTR(has_edge_compensation, RMI_RO_ATTR,
+ rmi_fn_54_has_edge_compensation_show, rmi_store_error),
+ __ATTR(has_perf_frequency_noisecontrol, RMI_RO_ATTR,
+ rmi_fn_54_has_perf_frequency_noisecontrol_show, rmi_store_error),
+ __ATTR(number_of_sensing_frequencies, RMI_RO_ATTR,
+ rmi_fn_54_number_of_sensing_frequencies_show, rmi_store_error),
+ __ATTR(no_auto_cal, RMI_RW_ATTR,
+ rmi_fn_54_no_auto_cal_show, rmi_fn_54_no_auto_cal_store),
+ __ATTR(fifoindex, RMI_RW_ATTR,
+ rmi_fn_54_fifoindex_show, rmi_fn_54_fifoindex_store),
+};
+
+struct bin_attribute dev_rep_data = {
+ .attr = {
+ .name = "rep_data",
+ .mode = RMI_RO_ATTR},
+ .size = 0,
+ .read = rmi_fn_54_data_read,
+};
+
+#if F54_WATCHDOG
+static enum hrtimer_restart clear_status(struct hrtimer *timer);
+
+static void clear_status_worker(struct work_struct *work);
+#endif
+
+static int rmi_f54_init(struct rmi_function_container *fc)
+{
+ struct rmi_fn_54_data *instance_data;
+ int retval = 0;
+ int attr_count = 0;
+
+ dev_info(&fc->dev, "Intializing F54.");
+
+ instance_data = kzalloc(sizeof(struct rmi_fn_54_data), GFP_KERNEL);
+ if (!instance_data) {
+ dev_err(&fc->dev, "Failed to allocate rmi_fn_54_data.\n");
+ retval = -ENOMEM;
+ goto error_exit;
+ }
+ fc->data = instance_data;
+ instance_data->fc = fc;
+
+#if F54_WATCHDOG
+ /* Set up watchdog timer to catch unanswered get_report commands */
+ hrtimer_init(&instance_data->watchdog, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ instance_data->watchdog.function = clear_status;
+
+ /* work function to do unlocking */
+ INIT_WORK(&instance_data->work, clear_status_worker);
+#endif
+
+ /* Read F54 Query Data */
+ retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+ (u8 *)&instance_data->query, sizeof(instance_data->query));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Could not read query registers"
+ " from 0x%04x\n", fc->fd.query_base_addr);
+ goto error_exit;
+ }
+
+ __mutex_init(&instance_data->data_mutex, "data_mutex",
+ &instance_data->data_key);
+
+ __mutex_init(&instance_data->status_mutex, "status_mutex",
+ &instance_data->status_key);
+
+ dev_dbg(&fc->dev, "Creating sysfs files.");
+ /* 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);
+ retval = -ENODEV;
+ goto error_exit;
+ }
+ }
+ /* Binary sysfs file to report the data back */
+ retval = sysfs_create_bin_file(&fc->dev.kobj, &dev_rep_data);
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to create sysfs file for F54 data "
+ "(error = %d).\n", retval);
+ retval = -ENODEV;
+ goto error_exit;
+ }
+ instance_data->status = IDLE;
+ return retval;
+
+error_exit:
+ dev_err(&fc->dev, "An error occured in F54 init!\n");
+ for (attr_count--; attr_count >= 0; attr_count--)
+ sysfs_remove_file(&fc->dev.kobj,
+ &attrs[attr_count].attr);
+ kfree(instance_data);
+ return retval;
+}
+
+static void set_report_size(struct rmi_fn_54_data *data)
+{
+ u8 rx = data->query.number_of_receiver_electrodes;
+ u8 tx = data->query.number_of_transmitter_electrodes;
+ switch (data->report_type) {
+ case F54_8BIT_IMAGE:
+ data->report_size = rx * tx;
+ break;
+ case F54_16BIT_IMAGE:
+ case F54_RAW_16BIT_IMAGE:
+ case F54_TRUE_BASELINE:
+ case F54_FULL_RAW_CAP:
+ case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+ data->report_size = 2 * rx * tx;
+ break;
+ case F54_HIGH_RESISTANCE:
+ data->report_size = RMI_54_HIGH_RESISTANCE_SIZE;
+ break;
+ case F54_FULL_RAW_CAP_MIN_MAX:
+ data->report_size = RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE;
+ break;
+ case F54_TX_TO_TX_SHORT:
+ case F54_TX_OPEN:
+ case F54_TX_TO_GROUND:
+ data->report_size = (tx + 7) / 8;
+ break;
+ case F54_RX_TO_RX1:
+ case F54_RX_OPENS1:
+ if (rx < tx)
+ data->report_size = 2 * rx * rx;
+ else
+ data->report_size = 2 * rx * tx;
+ break;
+ case F54_RX_TO_RX2:
+ case F54_RX_OPENS2:
+ if (rx <= tx)
+ data->report_size = 0;
+ else
+ data->report_size = 2 * rx * (rx - tx);
+ break;
+ default:
+ data->report_size = 0;
+ }
+}
+
+int rmi_f54_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+ struct rmi_driver *driver = fc->rmi_dev->driver;
+ char fifo[2];
+ struct rmi_fn_54_data *data = fc->data;
+ int error = 0;
+
+ set_report_size(data);
+ if (data->report_size == 0) {
+ dev_err(&fc->dev, "Invalid report type set in %s. "
+ "This should never happen.\n", __func__);
+ error = -EINVAL;
+ goto error_exit;
+ }
+ /*
+ * We need to ensure the buffer is big enough. A Buffer size of 0 means
+ * that the buffer has not been allocated.
+ */
+ if (data->bufsize < data->report_size) {
+ mutex_lock(&data->data_mutex);
+ if (data->bufsize > 0)
+ kfree(data->report_data);
+ data->report_data = kzalloc(data->report_size, GFP_KERNEL);
+ if (!data->report_data) {
+ dev_err(&fc->dev, "Failed to allocate report_data.\n");
+ error = -ENOMEM;
+ data->bufsize = 0;
+ mutex_unlock(&data->data_mutex);
+ goto error_exit;
+ }
+ data->bufsize = data->report_size;
+ mutex_unlock(&data->data_mutex);
+ }
+ dev_vdbg(&fc->dev, "F54 Interrupt handler is running.\nSize: %d\n",
+ data->report_size);
+ /*
+ * Read report type, fifo high, and fifo low
+ * error = rmi_read_multiple(rmifninfo->sensor,
+ * rmifninfo->function_descriptor.data_base_addr ,
+ * repfifo,3);
+ */
+ /* Write 0 to fifohi and fifolo. */
+ fifo[0] = 0;
+ fifo[1] = 0;
+ error = rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr
+ + RMI_F54_FIFO_OFFSET, fifo, sizeof(fifo));
+ if (error < 0)
+ dev_err(&fc->dev, "Failed to write fifo to zero!\n");
+ else
+ error = rmi_read_block(fc->rmi_dev,
+ fc->fd.data_base_addr + RMI_F54_REPORT_DATA_OFFSET,
+ data->report_data, data->report_size);
+ if (error < 0)
+ dev_err(&fc->dev, "F54 data read failed. Code: %d.\n", error);
+ else if (error != data->report_size) {
+ error = -EINVAL;
+ goto error_exit;
+ }
+#if RAW_HEX
+ int l;
+ /* Debugging: Print out the file in hex. */
+ pr_info("Report data (raw hex):\n");
+ for (l = 0; l < data->report_size; l += 2) {
+ pr_info("%03d: 0x%02x%02x\n", l/2,
+ data->report_data[l+1], data->report_data[l]);
+ }
+#endif
+#if HUMAN_READABLE
+ /* Debugging: Print out file in human understandable image */
+ switch (data->report_type) {
+ case F54_16BIT_IMAGE:
+ case F54_RAW_16BIT_IMAGE:
+ case F54_TRUE_BASELINE:
+ case F54_FULL_RAW_CAP:
+ case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+ pr_info("Report data (Image):\n");
+ int i, j, k;
+ char c[2];
+ short s;
+ k = 0;
+ for (i = 0; i < data->query.number_of_transmitter_electrodes;
+ i++) {
+ for (j = 0; j <
+ data->query.number_of_receiver_electrodes; j++) {
+ c[0] = data->report_data[k];
+ c[1] = data->report_data[k+1];
+ memcpy(&s, &c, 2);
+ if (s < -64)
+ printk(".");
+ else if (s < 0)
+ printk("-");
+ else if (s > 64)
+ printk("*");
+ else if (s > 0)
+ printk("+");
+ else
+ printk("0");
+ k += 2;
+ }
+ pr_info("\n");
+ }
+ pr_info("EOF\n");
+ break;
+ default:
+ pr_info("Report type %d debug image not supported",
+ data->report_type);
+ }
+#endif
+ error = IDLE;
+error_exit:
+ mutex_lock(&data->status_mutex);
+ /* Turn back on other interupts, if it
+ * appears that we turned them off. */
+ if (driver->restore_irq_mask) {
+ dev_dbg(&fc->dev, "Restoring interupts!\n");
+ driver->restore_irq_mask(fc->rmi_dev);
+ } else {
+ dev_err(&fc->dev, "No way to restore interrupts!\n");
+ }
+ data->status = error;
+ mutex_unlock(&data->status_mutex);
+ return data->status;
+}
+
+
+#if F54_WATCHDOG
+static void clear_status_worker(struct work_struct *work)
+{
+ struct rmi_fn_54_data *data = container_of(work,
+ struct rmi_fn_54_data, work);
+ struct rmi_function_container *fc = data->fc;
+ struct rmi_driver *driver = fc->rmi_dev->driver;
+ char command;
+ int result;
+
+ mutex_lock(&data->status_mutex);
+ if (data->status == BUSY) {
+ pr_info("F54 Timout Occured: Determining status.\n");
+ result = rmi_read_block(fc->rmi_dev, fc->fd.command_base_addr,
+ &command, 1);
+ if (result < 0) {
+ dev_err(&fc->dev, "Could not read get_report register "
+ "from 0x%04x\n", fc->fd.command_base_addr);
+ data->status = -ETIMEDOUT;
+ } else {
+ if (command & GET_REPORT) {
+ dev_warn(&fc->dev, "Report type unsupported!");
+ data->status = -EINVAL;
+ } else {
+ data->status = -ETIMEDOUT;
+ }
+ }
+ if (driver->restore_irq_mask) {
+ dev_dbg(&fc->dev, "Restoring interupts!\n");
+ driver->restore_irq_mask(fc->rmi_dev);
+ } else {
+ dev_err(&fc->dev, "No way to restore interrupts!\n");
+ }
+ }
+ mutex_unlock(&data->status_mutex);
+}
+
+static enum hrtimer_restart clear_status(struct hrtimer *timer)
+{
+ struct rmi_fn_54_data *data = container_of(timer,
+ struct rmi_fn_54_data, watchdog);
+ schedule_work(&(data->work));
+ return HRTIMER_NORESTART;
+}
+#endif
+
+/* Check if report_type is valid */
+static bool is_report_type_valid(enum f54_report_types reptype)
+{
+ /* Basic checks on report_type to ensure we write a valid type
+ * to the sensor.
+ * TODO: Check Query3 to see if some specific reports are
+ * available. This is currently listed as a reserved register.
+ */
+ switch (reptype) {
+ case F54_8BIT_IMAGE:
+ case F54_16BIT_IMAGE:
+ case F54_RAW_16BIT_IMAGE:
+ case F54_HIGH_RESISTANCE:
+ case F54_TX_TO_TX_SHORT:
+ case F54_RX_TO_RX1:
+ case F54_TRUE_BASELINE:
+ case F54_FULL_RAW_CAP_MIN_MAX:
+ case F54_RX_OPENS1:
+ case F54_TX_OPEN:
+ case F54_TX_TO_GROUND:
+ case F54_RX_TO_RX2:
+ case F54_RX_OPENS2:
+ case F54_FULL_RAW_CAP:
+ case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+ return true;
+ break;
+ default:
+ return false;
+ }
+}
+
+/* SYSFS file show/store functions */
+static ssize_t rmi_fn_54_report_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->report_type);
+}
+
+static ssize_t rmi_fn_54_report_type_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count) {
+ int result;
+ unsigned long val;
+ unsigned char data;
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ /* need to convert the string data to an actual value */
+ result = strict_strtoul(buf, 10, &val);
+ if (result)
+ return result;
+ if (!is_report_type_valid(val)) {
+ dev_err(dev, "%s : Report type %d is invalid.\n",
+ __func__, (u8) val);
+ return -EINVAL;
+ }
+ mutex_lock(&instance_data->status_mutex);
+ if (instance_data->status != BUSY) {
+ instance_data->report_type = (enum f54_report_types)val;
+ data = (char)val;
+ /* Write the Report Type back to the first Block
+ * Data registers (F54_AD_Data0). */
+ result =
+ rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr,
+ &data, 1);
+ mutex_unlock(&instance_data->status_mutex);
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write report type to"
+ " 0x%x\n", __func__, fc->fd.data_base_addr);
+ return result;
+ }
+ return count;
+ } else {
+ dev_err(dev, "%s : Report type cannot be changed in the middle"
+ " of command.\n", __func__);
+ mutex_unlock(&instance_data->status_mutex);
+ return -EINVAL;
+ }
+}
+
+static ssize_t rmi_fn_54_get_report_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count) {
+ unsigned long val;
+ int error, result;
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+ struct rmi_driver *driver;
+ u8 command;
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ driver = fc->rmi_dev->driver;
+
+ /* need to convert the string data to an actual value */
+ error = strict_strtoul(buf, 10, &val);
+ if (error)
+ return error;
+ /* Do nothing if not set to 1. This prevents accidental commands. */
+ if (val != 1)
+ return count;
+ command = (unsigned char)GET_REPORT;
+ /* Basic checks on report_type to ensure we write a valid type
+ * to the sensor.
+ * TODO: Check Query3 to see if some specific reports are
+ * available. This is currently listed as a reserved register.
+ */
+ if (!is_report_type_valid(instance_data->report_type)) {
+ dev_err(dev, "%s : Report type %d is invalid.\n",
+ __func__, instance_data->report_type);
+ return -EINVAL;
+ }
+ mutex_lock(&instance_data->status_mutex);
+ if (instance_data->status != IDLE) {
+ if (instance_data->status != BUSY) {
+ dev_err(dev, "F54 status is in an abnormal state: 0x%x",
+ instance_data->status);
+ }
+ mutex_unlock(&instance_data->status_mutex);
+ return count;
+ }
+ /* Store interrupts */
+ /* Do not exit if we fail to turn off interupts. We are likely
+ * to still get useful data. The report data can, however, be
+ * corrupted, and there may be unexpected behavior.
+ */
+ dev_dbg(dev, "Storing and overriding interupts\n");
+ if (driver->store_irq_mask)
+ driver->store_irq_mask(fc->rmi_dev,
+ fc->irq_mask);
+ else
+ dev_err(dev, "No way to store interupts!\n");
+ instance_data->status = BUSY;
+
+ /* small delay to avoid race condition in firmare. This value is a bit
+ * higher than absolutely necessary. Should be removed once issue is
+ * resolved in firmware. */
+
+ mdelay(2);
+
+ /* Write the command to the command register */
+ result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+ &command, 1);
+ mutex_unlock(&instance_data->status_mutex);
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write command to 0x%x\n",
+ __func__, fc->fd.command_base_addr);
+ return result;
+ }
+#if F54_WATCHDOG
+ /* start watchdog timer */
+ hrtimer_start(&instance_data->watchdog, ktime_set(1, 0),
+ HRTIMER_MODE_REL);
+#endif
+ return count;
+}
+
+static ssize_t rmi_fn_54_force_cal_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count) {
+ unsigned long val;
+ int error, result;
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+ struct rmi_driver *driver;
+ u8 command;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ driver = fc->rmi_dev->driver;
+
+ /* need to convert the string data to an actual value */
+ error = strict_strtoul(buf, 10, &val);
+ if (error)
+ return error;
+ /* Do nothing if not set to 1. This prevents accidental commands. */
+ if (val != 1)
+ return count;
+
+ command = (unsigned char)FORCE_CAL;
+
+ if (instance_data->status == BUSY)
+ return -EBUSY;
+ /* Write the command to the command register */
+ result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+ &command, 1);
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write command to 0x%x\n",
+ __func__, fc->fd.command_base_addr);
+ return result;
+ }
+ return count;
+}
+
+static ssize_t rmi_fn_54_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", instance_data->status);
+}
+
+static ssize_t rmi_fn_54_num_rx_electrodes_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.number_of_receiver_electrodes);
+}
+
+static ssize_t rmi_fn_54_num_tx_electrodes_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.number_of_transmitter_electrodes);
+}
+
+static ssize_t rmi_fn_54_has_image16_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_image16);
+}
+
+static ssize_t rmi_fn_54_has_image8_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_image8);
+}
+
+static ssize_t rmi_fn_54_has_baseline_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_baseline);
+}
+
+static ssize_t rmi_fn_54_clock_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.clock_rate);
+}
+
+
+static ssize_t rmi_fn_54_touch_controller_family_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.touch_controller_family);
+}
+
+
+static ssize_t rmi_fn_54_has_pixel_touch_threshold_adjustment_show(
+ struct device *dev, struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_pixel_touch_threshold_adjustment);
+}
+
+static ssize_t rmi_fn_54_has_sensor_assignment_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_sensor_assignment);
+}
+
+static ssize_t rmi_fn_54_has_interference_metric_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_interference_metric);
+}
+
+static ssize_t rmi_fn_54_has_sense_frequency_control_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_sense_frequency_control);
+}
+
+static ssize_t rmi_fn_54_has_firmware_noise_mitigation_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_firmware_noise_mitigation);
+}
+
+static ssize_t rmi_fn_54_has_two_byte_report_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_two_byte_report_rate);
+}
+
+static ssize_t rmi_fn_54_has_one_byte_report_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_one_byte_report_rate);
+}
+
+static ssize_t rmi_fn_54_has_relaxation_control_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_relaxation_control);
+}
+
+static ssize_t rmi_fn_54_curve_compensation_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.curve_compensation_mode);
+}
+
+static ssize_t rmi_fn_54_has_iir_filter_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_iir_filter);
+}
+
+static ssize_t rmi_fn_54_has_cmn_removal_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_cmn_removal);
+}
+
+static ssize_t rmi_fn_54_has_cmn_maximum_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_cmn_maximum);
+}
+
+static ssize_t rmi_fn_54_has_pixel_threshold_hysteresis_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_pixel_threshold_hysteresis);
+}
+
+static ssize_t rmi_fn_54_has_edge_compensation_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_edge_compensation);
+}
+
+static ssize_t rmi_fn_54_has_perf_frequency_noisecontrol_show(
+ struct device *dev, struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_perf_frequency_noisecontrol);
+}
+
+static ssize_t rmi_fn_54_number_of_sensing_frequencies_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.number_of_sensing_frequencies);
+}
+
+
+static ssize_t rmi_fn_54_no_auto_cal_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->no_auto_cal ? 1 : 0);
+}
+
+static ssize_t rmi_fn_54_no_auto_cal_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count) {
+ int result;
+ unsigned long val;
+ unsigned char data;
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ /* need to convert the string data to an actual value */
+ result = strict_strtoul(buf, 10, &val);
+
+ /* if an error occured, return it */
+ if (result)
+ return result;
+ /* Do nothing if not 0 or 1. This prevents accidental commands. */
+ if (val > 1)
+ return count;
+ /* Read current control values */
+ result =
+ rmi_read_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1);
+
+ /* if the current control registers are already set as we want them, do
+ * nothing to them */
+ if ((data & 1) == val)
+ return count;
+ /* Write the control back to the control register (F54_AD_Ctrl0)
+ * Ignores everything but bit 0 */
+ data = (data & ~1) | (val & 0x01); /* bit mask for lowest bit */
+ result =
+ rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1);
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write control to 0x%x\n",
+ __func__, fc->fd.control_base_addr);
+ return result;
+ }
+ /* update our internal representation iff the write succeeds */
+ instance_data->no_auto_cal = (val == 1);
+ return count;
+}
+
+static ssize_t rmi_fn_54_fifoindex_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+ struct rmi_driver *driver;
+ unsigned char temp_buf[2];
+ int retval;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ driver = fc->rmi_dev->driver;
+
+ /* Read fifoindex from device */
+ retval = rmi_read_block(fc->rmi_dev,
+ fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET,
+ temp_buf, ARRAY_SIZE(temp_buf));
+
+ if (retval < 0) {
+ dev_err(dev, "Could not read fifoindex from 0x%04x\n",
+ fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET);
+ return retval;
+ }
+ batohs(&instance_data->fifoindex, temp_buf);
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->fifoindex);
+}
+static ssize_t rmi_fn_54_fifoindex_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int error;
+ unsigned long val;
+ unsigned char data[2];
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ /* need to convert the string data to an actual value */
+ error = strict_strtoul(buf, 10, &val);
+
+ if (error)
+ return error;
+
+ instance_data->fifoindex = val;
+
+ /* Write the FifoIndex back to the first data registers. */
+ hstoba(data, (unsigned short)val);
+
+ error = rmi_write_block(fc->rmi_dev,
+ fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET,
+ data,
+ ARRAY_SIZE(data));
+
+ if (error < 0) {
+ dev_err(dev, "%s : Could not write fifoindex to 0x%x\n",
+ __func__, fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET);
+ return error;
+ }
+ return count;
+}
+
+/* Provide access to last report */
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+#else
+static ssize_t rmi_fn_54_data_read(struct kobject *kobj,
+#endif
+ struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count)
+{
+ struct device *dev;
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+
+ dev = container_of(kobj, struct device, kobj);
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ mutex_lock(&instance_data->data_mutex);
+ if (count < instance_data->report_size) {
+ dev_err(dev,
+ "%s: F54 report size too large for buffer: %d."
+ " Need at least: %d for Report type: %d.\n",
+ __func__, count, instance_data->report_size,
+ instance_data->report_type);
+ mutex_unlock(&instance_data->data_mutex);
+ return -EINVAL;
+ }
+ if (instance_data->report_data) {
+ /* Copy data from instance_data to buffer */
+ memcpy(buf, instance_data->report_data,
+ instance_data->report_size);
+ mutex_unlock(&instance_data->data_mutex);
+ dev_dbg(dev, "%s: Presumably successful.", __func__);
+ return instance_data->report_size;
+ } else {
+ dev_err(dev, "%s: F54 report_data does not exist!\n", __func__);
+ mutex_unlock(&instance_data->data_mutex);
+ return -EINVAL;
+ }
+}
+
+static struct rmi_function_handler function_handler = {
+ .func = 0x54,
+ .init = rmi_f54_init,
+ .attention = rmi_f54_attention
+};
+
+static int __init rmi_f54_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 rmi_f54_module_exit(void)
+{
+ rmi_unregister_function_driver(&function_handler);
+}
+
+module_init(rmi_f54_module_init);
+module_exit(rmi_f54_module_exit);
+
+MODULE_AUTHOR("Daniel Rosenberg <daniel.rosenberg@synaptics.com>");
+MODULE_DESCRIPTION("RMI F54 module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_i2c.c b/drivers/input/touchscreen/rmi4/rmi_i2c.c
new file mode 100644
index 000000000000..15624f9cf54b
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_i2c.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2011 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/lockdep.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/rmi.h>
+
+#define COMMS_DEBUG 0
+
+#define IRQ_DEBUG 0
+
+#define RMI_PAGE_SELECT_REGISTER 0xff
+#define RMI_I2C_PAGE(addr) (((addr) >> 8) & 0xff)
+
+static char *phys_proto_name = "i2c";
+
+struct rmi_i2c_data {
+ struct mutex page_mutex;
+ int page;
+ int enabled;
+ int irq;
+ int irq_flags;
+ struct rmi_phys_device *phys;
+};
+
+static irqreturn_t rmi_i2c_irq_thread(int irq, void *p)
+{
+ struct rmi_phys_device *phys = p;
+ struct rmi_device *rmi_dev = phys->rmi_dev;
+ struct rmi_driver *driver = rmi_dev->driver;
+ struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+
+#if IRQ_DEBUG
+ dev_dbg(phys->dev, "ATTN gpio, value: %d.\n",
+ gpio_get_value(irq_to_gpio(irq)));
+#endif
+ if (gpio_get_value(irq_to_gpio(irq)) == pdata->irq_polarity) {
+ phys->info.attn_count++;
+ if (driver && driver->irq_handler && rmi_dev)
+ driver->irq_handler(rmi_dev, irq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * rmi_set_page - Set RMI page
+ * @phys: The pointer to the rmi_phys_device struct
+ * @page: The new page address.
+ *
+ * RMI devices have 16-bit addressing, but some of the physical
+ * implementations (like SMBus) only have 8-bit addressing. So RMI implements
+ * a page address at 0xff of every page so we can reliable page addresses
+ * every 256 registers.
+ *
+ * The page_mutex lock must be held when this function is entered.
+ *
+ * Returns zero on success, non-zero on failure.
+ */
+static int rmi_set_page(struct rmi_phys_device *phys, unsigned int page)
+{
+ struct i2c_client *client = to_i2c_client(phys->dev);
+ struct rmi_i2c_data *data = phys->data;
+ char txbuf[2] = {RMI_PAGE_SELECT_REGISTER, page};
+ int retval;
+
+#if COMMS_DEBUG
+ dev_dbg(&client->dev, "RMI4 I2C writes 3 bytes: %02x %02x\n",
+ txbuf[0], txbuf[1]);
+#endif
+ phys->info.tx_count++;
+ phys->info.tx_bytes += sizeof(txbuf);
+ retval = i2c_master_send(client, txbuf, sizeof(txbuf));
+ if (retval != sizeof(txbuf)) {
+ phys->info.tx_errs++;
+ dev_err(&client->dev,
+ "%s: set page failed: %d.", __func__, retval);
+ return (retval < 0) ? retval : -EIO;
+ }
+ data->page = page;
+ return 0;
+}
+
+static int rmi_i2c_write_block(struct rmi_phys_device *phys, u16 addr, u8 *buf,
+ int len)
+{
+ struct i2c_client *client = to_i2c_client(phys->dev);
+ struct rmi_i2c_data *data = phys->data;
+ u8 txbuf[len + 1];
+ int retval;
+#if COMMS_DEBUG
+ int i;
+#endif
+
+ txbuf[0] = addr & 0xff;
+ memcpy(txbuf + 1, buf, len);
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_I2C_PAGE(addr) != data->page) {
+ retval = rmi_set_page(phys, RMI_I2C_PAGE(addr));
+ if (retval < 0)
+ goto exit;
+ }
+
+#if COMMS_DEBUG
+ dev_dbg(&client->dev, "RMI4 I2C writes %d bytes: ", sizeof(txbuf));
+ for (i = 0; i < sizeof(txbuf); i++)
+ dev_dbg(&client->dev, "%02x ", txbuf[i]);
+ dev_dbg(&client->dev, "\n");
+#endif
+
+ phys->info.tx_count++;
+ phys->info.tx_bytes += sizeof(txbuf);
+ retval = i2c_master_send(client, txbuf, sizeof(txbuf));
+ if (retval < 0)
+ phys->info.tx_errs++;
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return retval;
+}
+
+static int rmi_i2c_write(struct rmi_phys_device *phys, u16 addr, u8 data)
+{
+ int retval = rmi_i2c_write_block(phys, addr, &data, 1);
+ return (retval < 0) ? retval : 0;
+}
+
+static int rmi_i2c_read_block(struct rmi_phys_device *phys, u16 addr, u8 *buf,
+ int len)
+{
+ struct i2c_client *client = to_i2c_client(phys->dev);
+ struct rmi_i2c_data *data = phys->data;
+ u8 txbuf[1] = {addr & 0xff};
+ int retval;
+#if COMMS_DEBUG
+ int i;
+#endif
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_I2C_PAGE(addr) != data->page) {
+ retval = rmi_set_page(phys, RMI_I2C_PAGE(addr));
+ if (retval < 0)
+ goto exit;
+ }
+
+#if COMMS_DEBUG
+ dev_dbg(&client->dev, "RMI4 I2C writes 1 bytes: %02x\n", txbuf[0]);
+#endif
+ phys->info.tx_count++;
+ phys->info.tx_bytes += sizeof(txbuf);
+ retval = i2c_master_send(client, txbuf, sizeof(txbuf));
+ if (retval != sizeof(txbuf)) {
+ phys->info.tx_errs++;
+ retval = (retval < 0) ? retval : -EIO;
+ goto exit;
+ }
+
+ retval = i2c_master_recv(client, buf, len);
+
+ phys->info.rx_count++;
+ phys->info.rx_bytes += len;
+ if (retval < 0)
+ phys->info.rx_errs++;
+#if COMMS_DEBUG
+ else {
+ dev_dbg(&client->dev, "RMI4 I2C received %d bytes: ", len);
+ for (i = 0; i < len; i++)
+ dev_dbg(&client->dev, "%02x ", buf[i]);
+ dev_dbg(&client->dev, "\n");
+ }
+#endif
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return retval;
+}
+
+static int rmi_i2c_read(struct rmi_phys_device *phys, u16 addr, u8 *buf)
+{
+ int retval = rmi_i2c_read_block(phys, addr, buf, 1);
+ return (retval < 0) ? retval : 0;
+}
+
+
+static int acquire_attn_irq(struct rmi_i2c_data *data)
+{
+ return request_threaded_irq(data->irq, NULL, rmi_i2c_irq_thread,
+ data->irq_flags, dev_name(data->phys->dev), data->phys);
+}
+
+static int enable_device(struct rmi_phys_device *phys)
+{
+ int retval = 0;
+
+ struct rmi_i2c_data *data = phys->data;
+
+ if (data->enabled)
+ return 0;
+
+ retval = acquire_attn_irq(data);
+ if (retval)
+ goto error_exit;
+
+ data->enabled = true;
+ dev_dbg(phys->dev, "Physical device enabled.\n");
+ return 0;
+
+error_exit:
+ dev_err(phys->dev, "Failed to enable physical device. Code=%d.\n",
+ retval);
+ return retval;
+}
+
+
+static void disable_device(struct rmi_phys_device *phys)
+{
+ struct rmi_i2c_data *data = phys->data;
+
+ if (!data->enabled)
+ return;
+
+ disable_irq(data->irq);
+ free_irq(data->irq, data->phys);
+
+ dev_dbg(phys->dev, "Physical device disabled.\n");
+ data->enabled = false;
+}
+
+
+static int __devinit rmi_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct rmi_phys_device *rmi_phys;
+ struct rmi_i2c_data *data;
+ struct rmi_device_platform_data *pdata = client->dev.platform_data;
+ int error;
+
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "i2c_check_functionality error\n");
+ return -EIO;
+ }
+
+ rmi_phys = kzalloc(sizeof(struct rmi_phys_device), GFP_KERNEL);
+ if (!rmi_phys)
+ return -ENOMEM;
+
+ data = kzalloc(sizeof(struct rmi_i2c_data), GFP_KERNEL);
+ if (!data) {
+ error = -ENOMEM;
+ goto err_phys;
+ }
+
+ data->enabled = true; /* We plan to come up enabled. */
+ data->irq = gpio_to_irq(pdata->irq);
+ data->irq_flags = (pdata->irq_polarity == RMI_IRQ_ACTIVE_HIGH) ?
+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+ data->phys = rmi_phys;
+
+ rmi_phys->data = data;
+ rmi_phys->dev = &client->dev;
+
+ rmi_phys->write = rmi_i2c_write;
+ rmi_phys->write_block = rmi_i2c_write_block;
+ rmi_phys->read = rmi_i2c_read;
+ rmi_phys->read_block = rmi_i2c_read_block;
+ rmi_phys->enable_device = enable_device;
+ rmi_phys->disable_device = disable_device;
+
+ rmi_phys->info.proto = phys_proto_name;
+
+ mutex_init(&data->page_mutex);
+
+ /* Setting the page to zero will (a) make sure the PSR is in a
+ * known state, and (b) make sure we can talk to the device.
+ */
+ error = rmi_set_page(rmi_phys, 0);
+ if (error) {
+ dev_err(&client->dev, "Failed to set page select to 0.\n");
+ goto err_data;
+ }
+
+ if (pdata->gpio_config) {
+ error = pdata->gpio_config(&client->dev, true);
+ if (error < 0) {
+ dev_err(&client->dev, "failed to setup irq %d\n",
+ pdata->irq);
+ goto err_data;
+ }
+ }
+
+ error = rmi_register_phys_device(rmi_phys);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to register physical driver at 0x%.2X.\n",
+ client->addr);
+ goto err_data;
+ }
+ i2c_set_clientdata(client, rmi_phys);
+
+ if (pdata->irq > 0) {
+ error = acquire_attn_irq(data);
+ if (error < 0) {
+ dev_err(&client->dev,
+ "request_threaded_irq failed %d\n",
+ pdata->irq);
+ goto err_unregister;
+ }
+ }
+
+#if defined(CONFIG_RMI4_DEV)
+ error = gpio_export(pdata->irq, false);
+ if (error) {
+ dev_warn(&client->dev, "%s: WARNING: Failed to "
+ "export ATTN gpio!\n", __func__);
+ error = 0;
+ } else {
+ error = gpio_export_link(&(rmi_phys->rmi_dev->dev), "attn",
+ pdata->irq);
+ if (error) {
+ dev_warn(&(rmi_phys->rmi_dev->dev), "%s: WARNING: "
+ "Failed to symlink ATTN gpio!\n", __func__);
+ error = 0;
+ } else {
+ dev_info(&(rmi_phys->rmi_dev->dev),
+ "%s: Exported GPIO %d.", __func__, pdata->irq);
+ }
+ }
+#endif /* CONFIG_RMI4_DEV */
+
+ dev_info(&client->dev, "registered rmi i2c driver at 0x%.2X.\n",
+ client->addr);
+ return 0;
+
+err_unregister:
+ rmi_unregister_phys_device(rmi_phys);
+err_data:
+ kfree(data);
+err_phys:
+ kfree(rmi_phys);
+ return error;
+}
+
+static int __devexit rmi_i2c_remove(struct i2c_client *client)
+{
+ struct rmi_phys_device *phys = i2c_get_clientdata(client);
+ struct rmi_device_platform_data *pd = client->dev.platform_data;
+
+ rmi_unregister_phys_device(phys);
+ kfree(phys->data);
+ kfree(phys);
+
+ if (pd->gpio_config)
+ pd->gpio_config(&client->dev, false);
+
+ return 0;
+}
+
+static const struct i2c_device_id rmi_id[] = {
+ { "rmi", 0 },
+ { "rmi-i2c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rmi_id);
+
+static struct i2c_driver rmi_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rmi-i2c"
+ },
+ .id_table = rmi_id,
+ .probe = rmi_i2c_probe,
+ .remove = __devexit_p(rmi_i2c_remove),
+};
+
+static int __init rmi_i2c_init(void)
+{
+ return i2c_add_driver(&rmi_i2c_driver);
+}
+
+static void __exit rmi_i2c_exit(void)
+{
+ i2c_del_driver(&rmi_i2c_driver);
+}
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
+MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
+MODULE_DESCRIPTION("RMI i2c driver");
+MODULE_LICENSE("GPL");
+
+module_init(rmi_i2c_init);
+module_exit(rmi_i2c_exit);
diff --git a/drivers/input/touchscreen/rmi4/rmi_spi.c b/drivers/input/touchscreen/rmi4/rmi_spi.c
new file mode 100644
index 000000000000..41f72657f829
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_spi.c
@@ -0,0 +1,869 @@
+/*
+ * Copyright (c) 2011 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/module.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/gpio.h>
+#include <linux/rmi.h>
+
+#define COMMS_DEBUG 0
+
+#define RMI_PROTOCOL_VERSION_ADDRESS 0xa0fd
+#define SPI_V2_UNIFIED_READ 0xc0
+#define SPI_V2_WRITE 0x40
+#define SPI_V2_PREPARE_SPLIT_READ 0xc8
+#define SPI_V2_EXECUTE_SPLIT_READ 0xca
+
+#define RMI_SPI_BLOCK_DELAY_US 65
+#define RMI_SPI_BYTE_DELAY_US 65
+#define RMI_SPI_WRITE_DELAY_US 0
+
+#define RMI_V1_READ_FLAG 0x80
+
+#define RMI_PAGE_SELECT_REGISTER 0x00FF
+#define RMI_SPI_PAGE(addr) (((addr) >> 8) & 0x80)
+
+static char *spi_v1_proto_name = "spi";
+static char *spi_v2_proto_name = "spiv2";
+
+struct rmi_spi_data {
+ struct mutex page_mutex;
+ int page;
+ int (*set_page) (struct rmi_phys_device *phys, u8 page);
+ bool split_read_pending;
+ int enabled;
+ int irq;
+ int irq_flags;
+ struct rmi_phys_device *phys;
+ struct completion irq_comp;
+};
+
+static irqreturn_t rmi_spi_hard_irq(int irq, void *p)
+{
+ struct rmi_phys_device *phys = p;
+ struct rmi_spi_data *data = phys->data;
+ struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+
+ if (data->split_read_pending &&
+ gpio_get_value(irq_to_gpio(irq)) == pdata->irq_polarity) {
+ phys->info.attn_count++;
+ complete(&data->irq_comp);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t rmi_spi_irq_thread(int irq, void *p)
+{
+ struct rmi_phys_device *phys = p;
+ struct rmi_device *rmi_dev = phys->rmi_dev;
+ struct rmi_driver *driver = rmi_dev->driver;
+ struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+
+ if (gpio_get_value(irq_to_gpio(irq)) == pdata->irq_polarity) {
+ phys->info.attn_count++;
+ if (driver && driver->irq_handler)
+ driver->irq_handler(rmi_dev, irq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int rmi_spi_xfer(struct rmi_phys_device *phys,
+ const u8 *txbuf, unsigned n_tx, u8 *rxbuf, unsigned n_rx)
+{
+ struct spi_device *client = to_spi_device(phys->dev);
+ struct rmi_spi_data *v2_data = phys->data;
+ struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+ int status;
+ struct spi_message message;
+ struct spi_transfer *xfers;
+ int total_bytes = n_tx + n_rx;
+ u8 local_buf[total_bytes];
+ int xfer_count = 0;
+ int xfer_index = 0;
+ int block_delay = n_rx > 0 ? pdata->spi_data.block_delay_us : 0;
+ int byte_delay = n_rx > 1 ? pdata->spi_data.read_delay_us : 0;
+ int write_delay = n_tx > 1 ? pdata->spi_data.write_delay_us : 0;
+#if COMMS_DEBUG
+ int i;
+#endif
+ // pr_info("in function ____%s____ \n", __func__);
+
+ if (v2_data->split_read_pending) {
+ block_delay =
+ n_rx > 0 ? pdata->spi_data.split_read_block_delay_us : 0;
+ byte_delay =
+ n_tx > 1 ? pdata->spi_data.split_read_byte_delay_us : 0;
+ write_delay = 0;
+ }
+
+ if (n_tx) {
+ phys->info.tx_count++;
+ phys->info.tx_bytes += n_tx;
+ if (write_delay)
+ xfer_count += n_tx;
+ else
+ xfer_count += 1;
+ }
+
+ if (n_rx) {
+ phys->info.rx_count++;
+ phys->info.rx_bytes += n_rx;
+ if (byte_delay)
+ xfer_count += n_rx;
+ else
+ xfer_count += 1;
+ }
+
+ xfers = kcalloc(xfer_count,
+ sizeof(struct spi_transfer), GFP_KERNEL);
+ if (!xfers)
+ return -ENOMEM;
+
+ spi_message_init(&message);
+
+ if (n_tx) {
+ if (write_delay) {
+ for (xfer_index = 0; xfer_index < n_tx;
+ xfer_index++) {
+ memset(&xfers[xfer_index], 0,
+ sizeof(struct spi_transfer));
+ xfers[xfer_index].len = 1;
+ xfers[xfer_index].delay_usecs = write_delay;
+ xfers[xfer_index].cs_change = 1;
+ xfers[xfer_index].tx_buf = txbuf + xfer_index;
+ spi_message_add_tail(&xfers[xfer_index],
+ &message);
+ }
+ } else {
+ memset(&xfers[0], 0, sizeof(struct spi_transfer));
+ xfers[0].len = n_tx;
+ spi_message_add_tail(&xfers[0], &message);
+ memcpy(local_buf, txbuf, n_tx);
+ xfers[0].tx_buf = local_buf;
+ xfer_index++;
+ }
+ if (block_delay){
+ xfers[xfer_index-1].delay_usecs = block_delay;
+ xfers[xfer_index].cs_change = 1;
+ }
+ }
+ if (n_rx) {
+ if (byte_delay) {
+ int buffer_offset = n_tx;
+ for (; xfer_index < xfer_count; xfer_index++) {
+ memset(&xfers[xfer_index], 0,
+ sizeof(struct spi_transfer));
+ xfers[xfer_index].len = 1;
+ xfers[xfer_index].delay_usecs = byte_delay;
+ xfers[xfer_index].cs_change = 1;
+ xfers[xfer_index].rx_buf =
+ local_buf + buffer_offset;
+ buffer_offset++;
+ spi_message_add_tail(&xfers[xfer_index],
+ &message);
+ }
+ } else {
+ memset(&xfers[xfer_index], 0,
+ sizeof(struct spi_transfer));
+ xfers[xfer_index].len = n_rx;
+ xfers[xfer_index].rx_buf = local_buf + n_tx;
+ spi_message_add_tail(&xfers[xfer_index], &message);
+ xfer_index++;
+ }
+ }
+
+#if COMMS_DEBUG
+ if (n_tx) {
+ dev_info(&client->dev, "SPI sends %d bytes: ", n_tx);
+ for (i = 0; i < n_tx; i++)
+ // dev_info(&client->dev, "%02X ", txbuf[i]);
+ pr_info(": %02X ", txbuf[i]);
+ // dev_info(&client->dev, "\n");
+ pr_info("\n");
+ }
+#endif
+
+ /* do the i/o */
+ if (pdata->spi_data.cs_assert) {
+ status = pdata->spi_data.cs_assert(
+ pdata->spi_data.cs_assert_data, true);
+ if (!status) {
+ dev_err(phys->dev, "Failed to assert CS.");
+ /* nonzero means error */
+ status = -1;
+ goto error_exit;
+ } else
+ status = 0;
+ }
+
+ if (pdata->spi_data.pre_delay_us)
+ udelay(pdata->spi_data.pre_delay_us);
+
+ status = spi_sync(client, &message);
+
+ if (pdata->spi_data.post_delay_us)
+ udelay(pdata->spi_data.post_delay_us);
+
+ if (pdata->spi_data.cs_assert) {
+ status = pdata->spi_data.cs_assert(
+ pdata->spi_data.cs_assert_data, false);
+ if (!status) {
+ dev_err(phys->dev, "Failed to deassert CS.");
+ /* nonzero means error */
+ status = -1;
+ goto error_exit;
+ } else
+ status = 0;
+ }
+
+ if (status == 0) {
+ memcpy(rxbuf, local_buf + n_tx, n_rx);
+ status = message.status;
+ } else {
+ phys->info.tx_errs++;
+ phys->info.rx_errs++;
+ dev_err(phys->dev, "spi_sync failed with error code %d.",
+ status);
+ }
+
+#if COMMS_DEBUG
+ if (n_rx) {
+ dev_info(&client->dev, "SPI received %d bytes: ", n_rx);
+ for (i = 0; i < n_rx; i++)
+ // dev_info(&client->dev, "%02X ", rxbuf[i]);
+ pr_info(": %02X ", rxbuf[i]);
+ // dev_info(&client->dev, "\n");
+ pr_info("\n");
+ }
+#endif
+
+error_exit:
+ kfree(xfers);
+ return status;
+}
+
+static int rmi_spi_v2_write_block(struct rmi_phys_device *phys, u16 addr,
+ u8 *buf, int len)
+{
+ struct rmi_spi_data *data = phys->data;
+ u8 txbuf[len + 4];
+ int error;
+ // pr_info("in function ____%s____ \n", __func__);
+
+ txbuf[0] = SPI_V2_WRITE;
+ txbuf[1] = (addr >> 8) & 0x00FF;
+ txbuf[2] = addr & 0x00FF;
+ txbuf[3] = len;
+
+ memcpy(&txbuf[4], buf, len);
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_SPI_PAGE(addr) != data->page) {
+ error = data->set_page(phys, RMI_SPI_PAGE(addr));
+ if (error < 0)
+ goto exit;
+ }
+
+ error = rmi_spi_xfer(phys, buf, len + 4, NULL, 0);
+ if (error < 0)
+ goto exit;
+ error = len;
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return error;
+}
+
+static int rmi_spi_v2_write(struct rmi_phys_device *phys, u16 addr, u8 data)
+{
+ int error = rmi_spi_v2_write_block(phys, addr, &data, 1);
+ // pr_info("in function ____%s____ \n", __func__);
+
+ return (error == 1) ? 0 : error;
+}
+
+static int rmi_spi_v1_write_block(struct rmi_phys_device *phys, u16 addr,
+ u8 *buf, int len)
+{
+ struct rmi_spi_data *data = phys->data;
+ unsigned char txbuf[len + 2];
+ int error;
+ // pr_info("in function ____%s____ \n", __func__);
+
+ txbuf[0] = addr >> 8;
+ txbuf[1] = addr;
+ memcpy(txbuf+2, buf, len);
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_SPI_PAGE(addr) != data->page) {
+ error = data->set_page(phys, RMI_SPI_PAGE(addr));
+ if (error < 0)
+ goto exit;
+ }
+
+ error = rmi_spi_xfer(phys, txbuf, len + 2, NULL, 0);
+ if (error < 0)
+ goto exit;
+ error = len;
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return error;
+}
+
+static int rmi_spi_v1_write(struct rmi_phys_device *phys, u16 addr, u8 data)
+{
+ int error = rmi_spi_v1_write_block(phys, addr, &data, 1);
+ // pr_info("in function ____%s____ \n", __func__);
+
+ return (error == 1) ? 0 : error;
+}
+
+static int rmi_spi_v2_split_read_block(struct rmi_phys_device *phys, u16 addr,
+ u8 *buf, int len)
+{
+ struct rmi_spi_data *data = phys->data;
+ u8 txbuf[4];
+ u8 rxbuf[len + 1]; /* one extra byte for read length */
+ int error;
+ // pr_info("in function ____%s____ \n", __func__);
+
+ txbuf[0] = SPI_V2_PREPARE_SPLIT_READ;
+ txbuf[1] = (addr >> 8) & 0x00FF;
+ txbuf[2] = addr & 0x00ff;
+ txbuf[3] = len;
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_SPI_PAGE(addr) != data->page) {
+ error = data->set_page(phys, RMI_SPI_PAGE(addr));
+ if (error < 0)
+ goto exit;
+ }
+
+ data->split_read_pending = true;
+
+ error = rmi_spi_xfer(phys, txbuf, 4, NULL, 0);
+ if (error < 0) {
+ data->split_read_pending = false;
+ goto exit;
+ }
+
+ wait_for_completion(&data->irq_comp);
+
+ txbuf[0] = SPI_V2_EXECUTE_SPLIT_READ;
+ txbuf[1] = 0;
+
+ error = rmi_spi_xfer(phys, txbuf, 2, rxbuf, len + 1);
+ data->split_read_pending = false;
+ if (error < 0)
+ goto exit;
+
+ /* first byte is length */
+ if (rxbuf[0] != len) {
+ error = -EIO;
+ goto exit;
+ }
+
+ memcpy(buf, rxbuf + 1, len);
+ error = len;
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return error;
+}
+
+static int rmi_spi_v2_read_block(struct rmi_phys_device *phys, u16 addr,
+ u8 *buf, int len)
+{
+ struct rmi_spi_data *data = phys->data;
+ u8 txbuf[4];
+ int error;
+ // pr_info("in function ____%s____ \n", __func__);
+
+ txbuf[0] = SPI_V2_UNIFIED_READ;
+ txbuf[1] = (addr >> 8) & 0x00FF;
+ txbuf[2] = addr & 0x00ff;
+ txbuf[3] = len;
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_SPI_PAGE(addr) != data->page) {
+ error = data->set_page(phys, RMI_SPI_PAGE(addr));
+ if (error < 0)
+ goto exit;
+ }
+
+ error = rmi_spi_xfer(phys, txbuf, 4, buf, len);
+ if (error < 0)
+ goto exit;
+ error = len;
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return error;
+}
+
+static int rmi_spi_v2_read(struct rmi_phys_device *phys, u16 addr, u8 *buf)
+{
+ int error = rmi_spi_v2_read_block(phys, addr, buf, 1);
+
+ return (error == 1) ? 0 : error;
+}
+
+static int rmi_spi_v1_read_block(struct rmi_phys_device *phys, u16 addr,
+ u8 *buf, int len)
+{
+ struct rmi_spi_data *data = phys->data;
+ u8 txbuf[2];
+ int error;
+ // pr_info("in function ____%s____ \n", __func__);
+
+ txbuf[0] = (addr >> 8) | RMI_V1_READ_FLAG;
+ txbuf[1] = addr;
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_SPI_PAGE(addr) != data->page) {
+ error = data->set_page(phys, RMI_SPI_PAGE(addr));
+ if (error < 0)
+ goto exit;
+ }
+
+ error = rmi_spi_xfer(phys, txbuf, 2, buf, len);
+ // pr_info(" back in function %s, rmi_spi_xfer returned %d\n", __func__, error);
+
+ if (error < 0)
+ goto exit;
+ error = len;
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return error;
+}
+
+static int rmi_spi_v1_read(struct rmi_phys_device *phys, u16 addr, u8 *buf)
+{
+ int error = rmi_spi_v1_read_block(phys, addr, buf, 1);
+ pr_info("in function ____%s____ \n", __func__);
+
+ return (error == 1) ? 0 : error;
+}
+
+#define RMI_SPI_PAGE_SELECT_WRITE_LENGTH 1
+
+static int rmi_spi_v1_set_page(struct rmi_phys_device *phys, u8 page)
+{
+ struct rmi_spi_data *data = phys->data;
+ u8 txbuf[] = {RMI_PAGE_SELECT_REGISTER >> 8,
+ RMI_PAGE_SELECT_REGISTER & 0xFF, page};
+ int error;
+ pr_info("in function ____%s____ \n", __func__);
+
+ error = rmi_spi_xfer(phys, txbuf, sizeof(txbuf), NULL, 0);
+ if (error < 0) {
+ dev_err(phys->dev, "Failed to set page select, code: %d.\n",
+ error);
+ return error;
+ }
+
+ data->page = page;
+
+ return RMI_SPI_PAGE_SELECT_WRITE_LENGTH;
+}
+
+static int rmi_spi_v2_set_page(struct rmi_phys_device *phys, u8 page)
+{
+ struct rmi_spi_data *data = phys->data;
+
+ u8 txbuf[] = {SPI_V2_WRITE, RMI_PAGE_SELECT_REGISTER >> 8,
+ RMI_PAGE_SELECT_REGISTER & 0xFF,
+ RMI_SPI_PAGE_SELECT_WRITE_LENGTH, page};
+ int error;
+ pr_info("in function ____%s____ \n", __func__);
+
+ error = rmi_spi_xfer(phys, txbuf, sizeof(txbuf), NULL, 0);
+ if (error < 0) {
+ dev_err(phys->dev, "Failed to set page select, code: %d.\n",
+ error);
+ return error;
+ }
+
+ data->page = page;
+
+ return RMI_SPI_PAGE_SELECT_WRITE_LENGTH;
+}
+
+
+static int acquire_attn_irq(struct rmi_spi_data *data)
+{
+ int retval = 0;
+ pr_info("in function ____%s____ \n", __func__);
+ pr_info(" irq = %d\n", data->irq);
+ pr_info(" rmi_spi_hard_irq = 0x%8x\n", rmi_spi_hard_irq);
+ pr_info(" rmi_spi_irq_thread = 0x%8x\n", rmi_spi_irq_thread);
+ pr_info(" data->irq_flags = 0x%8x\n", data->irq_flags);
+ pr_info(" dev_name(data->phys->dev) = %s\n", dev_name(data->phys->dev));
+ pr_info(" data->phys = 0x%8x\n", data->phys);
+
+ retval = request_threaded_irq(data->irq, rmi_spi_hard_irq,
+ rmi_spi_irq_thread, data->irq_flags,
+ dev_name(data->phys->dev), data->phys);
+
+ pr_info(" retval = = %d\n", retval);
+ return retval;
+}
+
+static int enable_device(struct rmi_phys_device *phys)
+{
+ int retval = 0;
+
+ struct rmi_spi_data *data = phys->data;
+
+ if (data->enabled) {
+ dev_info(phys->dev, "Physical device already enabled.\n");
+ return 0;
+ }
+
+ retval = acquire_attn_irq(data);
+ if (retval)
+ goto error_exit;
+
+ data->enabled = true;
+ dev_info(phys->dev, "Physical device enabled.\n");
+ return 0;
+
+error_exit:
+ dev_err(phys->dev, "Failed to enable physical device. Code=%d.\n",
+ retval);
+ return retval;
+}
+
+
+static void disable_device(struct rmi_phys_device *phys)
+{
+ struct rmi_spi_data *data = phys->data;
+
+ pr_info("in function ____%s____ \n", __func__);
+ if (!data->enabled) {
+ dev_warn(phys->dev, "Physical device already disabled.\n");
+ return;
+ }
+ disable_irq(data->irq);
+ free_irq(data->irq, data->phys);
+
+ dev_info(phys->dev, "Physical device disabled.\n");
+ data->enabled = false;
+}
+
+
+
+#define DUMMY_READ_SLEEP_US 10
+
+static int rmi_spi_check_device(struct rmi_phys_device *rmi_phys)
+{
+ u8 buf[6];
+ int error;
+ int i;
+
+ pr_info("in function ____%s____ \n", __func__);
+
+ /* Some SPI subsystems return 0 for the very first read you do. So
+ * we use this dummy read to get that out of the way.
+ */
+ error = rmi_spi_v1_read_block(rmi_phys, PDT_START_SCAN_LOCATION,
+ buf, sizeof(buf));
+ if (error < 0) {
+ dev_err(rmi_phys->dev, "dummy read failed with %d.\n", error);
+ return error;
+ }
+ udelay(DUMMY_READ_SLEEP_US);
+
+ /* Force page select to 0.
+ */
+ error = rmi_spi_v1_set_page(rmi_phys, 0x00);
+ if (error < 0)
+ return error;
+
+ /* Now read the first PDT entry. We know where this is, and if the
+ * RMI4 device is out there, these 6 bytes will be something other
+ * than all 0x00 or 0xFF. We need to check for 0x00 and 0xFF,
+ * because many (maybe all) SPI implementations will return all 0x00
+ * or all 0xFF on read if the device is not connected.
+ */
+ error = rmi_spi_v1_read_block(rmi_phys, PDT_START_SCAN_LOCATION,
+ buf, sizeof(buf));
+ if (error < 0) {
+ dev_err(rmi_phys->dev, "probe read failed with %d.\n", error);
+ return error;
+ }
+
+ dev_info(rmi_phys->dev, "probe read succeeded with %d.\n", error);
+ for (i = 0; i < sizeof(buf); i++) {
+ if (buf[i] != 0x00 && buf[i] != 0xFF)
+ return error;
+ }
+
+ dev_err(rmi_phys->dev, "probe read returned invalid block.\n");
+ return -ENODEV;
+}
+
+static int __devinit rmi_spi_probe(struct spi_device *spi)
+{
+ struct rmi_phys_device *rmi_phys;
+ struct rmi_spi_data *data;
+ struct rmi_device_platform_data *pdata = spi->dev.platform_data;
+ u8 buf[2];
+ int error;
+
+ pr_info("%s: probe for rmi_spi device\n", __func__);
+
+ if (!pdata) {
+ dev_err(&spi->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ if (spi->master->flags & SPI_MASTER_HALF_DUPLEX)
+ return -EINVAL;
+
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_3;
+ error = spi_setup(spi);
+ if (error < 0) {
+ dev_err(&spi->dev, "spi_setup failed!\n");
+ return error;
+ }
+
+ rmi_phys = kzalloc(sizeof(struct rmi_phys_device), GFP_KERNEL);
+ if (!rmi_phys)
+ return -ENOMEM;
+
+ data = kzalloc(sizeof(struct rmi_spi_data), GFP_KERNEL);
+ if (!data) {
+ error = -ENOMEM;
+ goto err_phys;
+ }
+ data->enabled = true; /* We plan to come up enabled. */
+ data->irq = gpio_to_irq(pdata->irq);
+ data->irq_flags = (pdata->irq_polarity == RMI_IRQ_ACTIVE_HIGH) ?
+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+ data->phys = rmi_phys;
+
+ rmi_phys->data = data;
+ rmi_phys->dev = &spi->dev;
+
+ rmi_phys->write = rmi_spi_v1_write;
+ rmi_phys->write_block = rmi_spi_v1_write_block;
+ rmi_phys->read = rmi_spi_v1_read;
+ rmi_phys->read_block = rmi_spi_v1_read_block;
+ rmi_phys->enable_device = enable_device;
+ rmi_phys->disable_device = disable_device;
+ data->set_page = rmi_spi_v1_set_page;
+
+ rmi_phys->info.proto = spi_v1_proto_name;
+
+ mutex_init(&data->page_mutex);
+
+ pr_info("%s: setting the driverdata on the device\n", __func__);
+
+ dev_set_drvdata(&spi->dev, rmi_phys);
+
+ pr_info("%s: done setting driverdata %s\n", __func__, dev_name(&spi->dev));
+
+
+ pdata->spi_data.block_delay_us = pdata->spi_data.block_delay_us ?
+ pdata->spi_data.block_delay_us : RMI_SPI_BLOCK_DELAY_US;
+ pdata->spi_data.read_delay_us = pdata->spi_data.read_delay_us ?
+ pdata->spi_data.read_delay_us : RMI_SPI_BYTE_DELAY_US;
+ pdata->spi_data.write_delay_us = pdata->spi_data.write_delay_us ?
+ pdata->spi_data.write_delay_us : RMI_SPI_BYTE_DELAY_US;
+ pdata->spi_data.split_read_block_delay_us =
+ pdata->spi_data.split_read_block_delay_us ?
+ pdata->spi_data.split_read_block_delay_us :
+ RMI_SPI_BLOCK_DELAY_US;
+ pdata->spi_data.split_read_byte_delay_us =
+ pdata->spi_data.split_read_byte_delay_us ?
+ pdata->spi_data.split_read_byte_delay_us :
+ RMI_SPI_BYTE_DELAY_US;
+
+ pr_info("%s configuring GPIOs\n", __func__);
+
+ if (pdata->gpio_config) {
+ error = pdata->gpio_config(&spi->dev, true);
+ if (error < 0) {
+ dev_err(&spi->dev, "Failed to setup GPIOs, code: %d.\n",
+ error);
+ goto err_data;
+ }
+ }
+
+ error = rmi_spi_check_device(rmi_phys);
+ if (error < 0)
+ goto err_data;
+
+ /* check if this is an SPI v2 device */
+ dev_info(&spi->dev, "%s: checking SPI version on RMI device\n", __func__);
+ error = rmi_spi_v1_read_block(rmi_phys, RMI_PROTOCOL_VERSION_ADDRESS,
+ buf, 2);
+
+ if (error < 0) {
+ dev_info(&spi->dev, "failed to get SPI version number!\n");
+ dev_err(&spi->dev, "failed to get SPI version number!\n");
+ goto err_data;
+ }
+
+ dev_info(&spi->dev, "SPI version is %d", buf[0]);
+
+ if (buf[0] == 1) {
+ /* SPIv2 */
+ rmi_phys->write = rmi_spi_v2_write;
+ rmi_phys->write_block = rmi_spi_v2_write_block;
+ rmi_phys->read = rmi_spi_v2_read;
+ data->set_page = rmi_spi_v2_set_page;
+
+ rmi_phys->info.proto = spi_v2_proto_name;
+
+ if (pdata->irq > 0) {
+ init_completion(&data->irq_comp);
+ rmi_phys->read_block = rmi_spi_v2_split_read_block;
+ } else {
+ rmi_phys->read_block = rmi_spi_v2_read_block;
+ }
+ } else if (buf[0] != 0) {
+ dev_err(&spi->dev, "Unrecognized SPI version %d.\n", buf[0]);
+ error = -ENODEV;
+ goto err_data;
+ }
+
+ error = rmi_register_phys_device(rmi_phys);
+ if (error) {
+ dev_err(&spi->dev, "failed to register physical driver\n");
+ goto err_data;
+ }
+
+ if (pdata->irq > 0) {
+ error = acquire_attn_irq(data);
+ if (error < 0) {
+ dev_err(&spi->dev, "request_threaded_irq failed %d\n",
+ pdata->irq);
+ goto err_unregister;
+ }
+ }
+
+#if defined(CONFIG_RMI4_DEV)
+ pr_info(" CONFIG_RMI4_DEV is defined\n");
+
+ error = gpio_export(pdata->irq, false);
+ if (error) {
+ dev_warn(&spi->dev, "WARNING: Failed to export ATTN gpio!\n");
+ error = 0;
+ } else {
+ error = gpio_export_link(&(rmi_phys->rmi_dev->dev), "attn",
+ pdata->irq);
+ if (error) {
+ dev_warn(&(rmi_phys->rmi_dev->dev), "WARNING: "
+ "Failed to symlink ATTN gpio!\n");
+ error = 0;
+ } else {
+ dev_info(&(rmi_phys->rmi_dev->dev),
+ "%s: Exported GPIO %d.", __func__, pdata->irq);
+ }
+ }
+#endif /* CONFIG_RMI4_DEV */
+
+ dev_info(&spi->dev, "registered RMI SPI driver\n");
+ return 0;
+
+err_unregister:
+ rmi_unregister_phys_device(rmi_phys);
+err_data:
+ kfree(data);
+err_phys:
+ kfree(rmi_phys);
+ return error;
+}
+
+static int __devexit rmi_spi_remove(struct spi_device *spi)
+{
+ struct rmi_phys_device *phys = dev_get_drvdata(&spi->dev);
+ struct rmi_device_platform_data *pd = spi->dev.platform_data;
+ pr_info("in function ____%s____ \n", __func__);
+
+ rmi_unregister_phys_device(phys);
+ kfree(phys->data);
+ kfree(phys);
+
+ if (pd->gpio_config)
+ pd->gpio_config(&spi->dev, false);
+
+ return 0;
+}
+
+static const struct spi_device_id rmi_id[] = {
+ { "rmi", 0 },
+ { "rmi_spi", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, rmi_id);
+
+static struct spi_driver rmi_spi_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rmi_spi",
+ //.mod_name = "rmi_spi",
+ //.bus = &spi_bus_type,
+ },
+ .id_table = rmi_id,
+ .probe = rmi_spi_probe,
+ .remove = __devexit_p(rmi_spi_remove),
+};
+
+static int __init rmi_spi_init(void)
+{
+ pr_info("%s: registering synaptics spi driver (ref=124)\n", __func__);
+ pr_info(" driver.owner = 0x%x\n", (unsigned int)rmi_spi_driver.driver.owner);
+ pr_info(" driver.name = %s\n", rmi_spi_driver.driver.name);
+ pr_info(" id_table[0].name = %s\n", rmi_spi_driver.id_table[0].name );
+ pr_info(" id_table[1].name = %s\n", rmi_spi_driver.id_table[1].name );
+ pr_info(" probe function ptr = 0x%x\n", (unsigned int)rmi_spi_driver.probe );
+
+
+ return spi_register_driver(&rmi_spi_driver);
+}
+
+static void __exit rmi_spi_exit(void)
+{
+ spi_unregister_driver(&rmi_spi_driver);
+}
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
+MODULE_DESCRIPTION("RMI SPI driver");
+MODULE_LICENSE("GPL");
+
+module_init(rmi_spi_init);
+module_exit(rmi_spi_exit);