linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
@ 2012-08-17 22:17 Christopher Heiny
  2012-08-17 22:17 ` [RFC PATCH 1/17] input: RMI4 public header file and documentation Christopher Heiny
                   ` (18 more replies)
  0 siblings, 19 replies; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

This patch implements a driver supporting Synaptics ClearPad and other
touchscreen sensors that use the RMI4 protocol, as defined here:

    http://www.synaptics.com/sites/default/files/511-000136-01-Rev-E-RMI4%20Intrfacing%20Guide.pdf

as well as successor documents that haven't made their way through to
publication yet.

This code supersedes the patch submitted on 2011-12-21.  We believe that all
outstanding issues arising from the previous submissions have been addressed,
except certain issues relating to the use of device_type and the binding of 
the individual function drivers (see below).


This patch is against the v2.6.38 tag of Linus' kernel tree, commit
521cb40b0c44418a4fd36dc633f575813d59a43d.  This will be our last patch against
such an old kernel version, future patches will be against 3.x kernels.



Included in this patch are:
    - full support for an RMI virtual bus as a standard kernel bus

    - physical layer implementations for I2C and SPI

    - function implementations for the following RMI4 functions:
        * F01 device control
        * F09 self test (aka BIST)
        * F11 multifinger pointing
        * F17 pointing sticks
        * F19 capacitive buttons
        * F1A simple capacitive buttons
        * F21 force sensing
        * F30 GPIO and LED controls
        * F34 device reflash
        * F41 active pen pointing
        * F54 analog data reporting

    - character device access to the RMI4 register map via rmidev
    
    - firmware reflash using the request_firmware mechanism

The driver supports a system having one or more RMI sensors attached to it.
Most devices have just a single touch sensor, but some have more than one.
An example is the Fuse concept phone, which has 4 RMI sensors in it.

Each sensor is presented as a device on the RMI logical bus (/sys/bus/rmi).
Devices are named/numbered in the order they are discovered on the bus,
starting with /sys/bus/rmi/devices/sensor00 for the first once, .../sensor01
for the second one, and so on.

Individual RMI functions are presented as child devices of the sensor device.
For example, sensor00/fn01, sensor00/fn11, and so on.  Control of an RMI
function's operating parameters is implemented via sysfs.

For diagnostic purposes, the rmidev interface is provided.  This implements
a character device (/dev/rmi0, and so on) that provides flat file access to
the RMI4 register map.  For more information, see the file
Documentation/input/rmidev.txt.



Note regarding device_type and functional driver matching.

Feedback on our patch submission from 2011-12 requested that we use device_type
to help match drivers that worked directly with the sensor (such as rmi_dev.c)
and drivers that work with individual functions (such as rmi_f11.c).  We spent
some effort on this request, but in the meantime the original customer
requirements that led to the differentiation between the two types of driver
no longer exist.  As a result, we have rolled back that work, and plan to merge
the rmi_dev functionality into the core of the driver, eliminating the need
to support two device_types and their drivers.  This will be included in our
next patch submission, along with changes to how function drivers are matched
functions.

We considered postponing this patch until that was done, but it's been a 
very long time since the last one, and we wanted to make the other work
available.


Due to the large amount of code, we've broken this patch into 17 parts, as
follows:
    01 - public header files and documentation
    02 - core sensor and bus implementation
    03 - physical layer drivers
    04 - Kconfigs and Makefiles
    05 - rmidev
    06 - firmware update
    07..17 - drivers for individual RMI functions


Comments and other feedback on this driver are welcomed.

Christopher Heiny and the Synaptics RMI4 driver team

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

Cc: Jean Delvare <khali@linux-fr.org>
Cc: Linus Walleij <linus.walleij@stericsson.com>
Cc: Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
Cc: Joeri de Gram <j.de.gram@gmail.com>

---

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [RFC PATCH 1/17] input: RMI4 public header file and documentation.
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-22 19:08   ` Linus Walleij
  2012-08-17 22:17 ` [RFC PATCH 2/17] input: RMI4 core bus and sensor drivers Christopher Heiny
                   ` (17 subsequent siblings)
  18 siblings, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 Documentation/input/rmi4.txt     |   25 ++
 Documentation/input/rmidev.txt   |  144 +++++++++
 Documentation/input/rmisysfs.txt |  200 ++++++++++++
 include/linux/rmi.h              |  652 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 1021 insertions(+), 0 deletions(-)

diff --git a/Documentation/input/rmi4.txt b/Documentation/input/rmi4.txt
new file mode 100644
index 0000000..4e033ff
--- /dev/null
+++ b/Documentation/input/rmi4.txt
@@ -0,0 +1,25 @@
+RMI4 Touchscreen Driver
+=======================
+
+TBD
+
+Firmware Update Function
+========================
+
+The RMI4 driver uses the kernel's request_firmware() feature to obtain
+firmware for the touch sensor.  The firmware is expected to live in
+the file firmware/rmi4/<productid>.img, where <productid> is the Product ID
+found in the F01 query registers (F01_RMI_QUERY11 through F01_RMI_QUERY20).
+
+To prepare Synaptics provided .img file for reflashing, convert it to .ihex
+format using the following command:
+
+    objcopy -I binary -O ihex <productid>.img firmware/rmi4/<productid>.img.ihex
+
+Then make sure to add the image file name to the CONFIG_RMI4_FWLIB entry in
+firmware/Makefile.  If you don't do this, the image file won't be included, and
+the firmware loader class will delay for 60 seconds waiting for a non-existent
+userspace response to the firmware load request.
+
+Firmware updates for multichip solutions (aka LTS) are not supported.
+
diff --git a/Documentation/input/rmidev.txt b/Documentation/input/rmidev.txt
new file mode 100644
index 0000000..73473ee
--- /dev/null
+++ b/Documentation/input/rmidev.txt
@@ -0,0 +1,144 @@
+RMI4 devices are typically handled by kernel drivers attached to devices on
+/sys/bus/rmi.  However, user space programs can access the RMI4 devices
+through a /dev interface.
+
+Each registered RMI4 device is assigned a number, starting from 0.  The rmidev
+module (if present and loaded) creates a corresponding /dev/rmiX device.  This
+device is a flat binary character file that allows you to read, write, and seek
+within the register space of the corresponding RMI4 device.
+
+Opening the rmidev file is done just like opening any other file.  For example,
+in C you might do the following to open the device file for the first RMI4
+device:
+    int file;
+
+    file = open("/dev/rmi0", O_RDWR);
+    if (file < 0) {
+        /* ERROR HANDLING; you can check errno to see what went wrong */
+        exit(1);
+    }
+
+Once the file is open, you can use read() and write() to access registers in
+the RMI4 register map.  Use lseek() to specify the address at which the
+read() or write() operation will begin.
+
+
+NOTES ON ADDRESSING
+-------------------
+
+The RMI4 register space is 16 bits wide, supporting 65536 available 8-bit
+registers.  This means the range of valid positions within the file is 0
+through 65535 (0xFFFF).
+
+Attempting to lseek() to an address of 65536 or higher will return EINVAL.
+read() and write() operations that begin at 65536 or higher will return EINVAL.
+The result of read() and write() operations that begine at 65535 or below, and
+then extend beyond that boundary are undefined.
+
+Although the RMI4 register space supports 65536 registers, not all registers
+are defined or mapped on all products.  Undefined or unmapped registers will
+usually return zero, but you should not rely on this behavior.  The register
+map for a given device may contain 'holes' of unimplemented registers.
+
+If you're not sure what the register map for a given RMI4 device looks like,
+you can use the self describing features of RMI4 to determine the register
+map (this is what the RMI4 touchscreen driver does), or consult the product
+spec and documentation for your product.
+
+
+BUFFERED REGISTERS
+------------------
+
+The RMI4 specification also defines certain special registers, sometimes
+referred to as "buffered registers".  Buffered registers allow large amounts
+of data to be read or written at a single address in a single read or write
+operation.  Reads from (or writes to) buffered registers DO NOT increment the
+RMI4 device's internal register pointer.  When reading/writing a buffered
+register, you MUST follow these steps:
+    (1) lseek() to the buffered register's address
+    (2) read/write all data in a single read() or write() call
+    (3) lseek() to the address of the next register you wish to read/write
+
+The result of a read or write that extends across a buffered register is
+undefined.  It probably won't do what you expect.
+
+The result of trying to use multiple read() or write() calls to access the
+data in a buffered register is undefined.  It most certainly won't do what
+you expect.
+
+The result of failing to lseek() after reading or writing a buffered register
+is undefined.  It probably won't do what you expect.
+
+For more information on buffered registers, please refer to the RMI4
+specification.
+
+
+OPERATIONAL NOTE
+----------------
+
+It's important to remember that reading or writing the registers of an RMI4
+device using rmidev can change the state of the device.  For example, reading
+the F01 interrupt status register has the side effect of clearing the contents
+of that register.  If there was a pending interrupt (for example, a finger
+position report), information about that interrupt would be lost to the RMI4
+touchscreen driver.
+
+For this reason, it is required you disable the RMI4 device before accessing
+the sensor via rmidev.  To do this, write 0 to
+/sys/bus/rmi/devices/sensor00/enabled before you open the /dev/rmi0 file.
+After you close that file, you can write 1 to
+/sys/bus/rmi/devices/sensor00/enabled to return the RMI4 device to its enabled
+state.
+
+The results of using rmidev to access an RMI4 sensor via rmidev while the
+touchscreen driver is enabled are not defined, but it probably won't be pretty.
+
+
+HANDLING ATTENTION
+------------------
+
+RMI4 devices use the gpiolib framework to export their ATTN GPIO to userspace
+via sysfs (see Documentation/gpio.txt for more information about gpiolib).
+For a given RMI4 device, the gpiolib interfaces can be found in
+/sys/bus/rmi/devices/sensorNN/attn/ where NN is the number of the RMI4 sensor.
+
+Disabling the RMI4 device will also cause it to release the ATTN IRQ.  This
+will make it possible for a user space program to write to .../attn/edge file
+and then use poll() monitor the .../attn/value for changes in the state of
+the ATTN pin.
+
+Once you are done with ATTN, you MUST write 'none' to the .../attn/edge file
+in order to release the IRQ.  This MUST be done before you re-enable the RMI4
+device, otherwise the touchscreen driver will not be able to re-acquire the
+ATTN IRQ, and your device will be a brick.
+
+
+PUTTING IT ALL TOGETHER
+-----------------------
+
+Suppose you want to build a user space program to use rmidev to access an
+RMI4 device, and use changes in ATTN to determine when to read the F01
+interrupt status register.  Follow the steps below to disable the device
+and obtain access.
+
+    1) write 0 to /sys/bus/rmi/devices/sensor00/enabled
+    2) open /dev/rmi0
+    3) open /sys/bus/rmi/devices/sensor00/attn/value
+    4) write both to /sys/bus/rmi/devices/sensor00/attn/edge
+
+Then enter an operating mode like this.
+    1) poll() the /sys/bus/rmi/devices/sensor00/attn/value
+    2) lseek() to 0 and read() /sys/bus/rmi/devices/sensor00/attn/value
+       whenever gpiolib indicates there is a change in state
+    3) if ATTN is asserted (usually this will be value == 0) read the
+       interrupt status register(s)
+    4) perform appropriate actions
+    5) go back to (1)
+
+When you're all done, perform the following steps IN ORDER:
+
+    1) close /sys/bus/rmi/devices/sensor00/attn/value
+    2) close /dev/rmi0
+    3) write none to /sys/bus/rmi/devices/sensor00/attn/edge
+    4) write 1 to /sys/bus/rmi/devices/sensor00/enabled
+
diff --git a/Documentation/input/rmisysfs.txt b/Documentation/input/rmisysfs.txt
new file mode 100644
index 0000000..a40f417
--- /dev/null
+++ b/Documentation/input/rmisysfs.txt
@@ -0,0 +1,200 @@
+The RMI4 driver exposes an extensive set of informational and control
+parameters via sysfs.
+
+General parameters for a particular RMI4 device are found in
+/sys/bus/rmi/devices/sensorXX/, where XX is a the device's ID as a two digit
+number (padded with leading zeros).  Function specific parameters associated
+with a particular RMI sensor are found in /sys/bus/rmi/devices/sensorXX/fnYY/,
+where XX is a the device's ID as a two digit number (padded with leading zeros)
+and YY is the hexdecimal function number (for example, fn11 for RMI function
+F11).
+
+Although you could use this sysfs interface to configure an RMI4 device on
+system startup, that is not recommended.  Use the existing platform data and
+board file structures to specify the initial operating parameters, instead.
+
+
+General Parameters
+==================
+
+hasbsr (RO) - Reads back as 1 if the device supports the bus select register
+(see below), 0 otherwise.
+
+bsr (RW) - This contains the current value of the device's Bus Select Register
+(BSR), if it is present.  The Bus Select Register is of interest only when
+reflashing the device.  See the RMI4 Specification for details.
+
+enabled (RW) - Writing 1 to this enables the RMI4 driver, writing 0 to this
+disables it.  When disabled, the RMI4 driver completes any pending activities,
+suspends any current threads, stops polling (if applicable) and releases the
+ATTN IRQ (if applicable).  This can be useful when another driver, such as
+rmidev, wants to interact with the device.  The RMI4 driver is enabled by
+default at start up.
+
+phy (RO) - Presents information about the physical connection of this device.
+It has one line, with the format
+    prot tx_count tx_bytes tx_errors rx_count rx_bytes rx_errors attn
+Where
+    prot is one of i2c, spi1, or spi2
+    tx_count is the number of write operations
+    tx_bytes is the number of bytes written
+    tx_errors is the number of write operations that encountered errors
+    rx_count is the number of read operations
+    rx_bytes is the total number of bytes read
+    rx_errors is the number of read operations that encountered errors
+    attn is the number of times the ATTN line interrupt has been seen
+All counts are 64-bit unsigned values, and are set to zero when the physical
+layer driver is initialized.
+
+version(RO) - Displays RMI4 driver version info.
+
+
+
+F01 Device Control Parameters
+=============================
+
+chargerinput (RW) - User space programs can use this to tell the sensor that
+the system is plugged into an external power source (as opposed to running on
+batteries).  This allows the sensor firmware to make necessary adjustments
+for the current capacitence regime.  Write 1 to this when the system is using
+external power, write 0 to this when the system is running on batteries.
+
+configured (RO) - Shows the current state of the configured bit.  This will be
+1 most of the time (indicating the device has been appropriately configured),
+but will switch to 0 briefly if the sensor experiences a firmware or ASIC
+reset event.
+
+datecode (RO) - the date on which the module was manufactured.
+
+doze_holdoff (RW) - Controls how long the sensor will wait before entering the
+doze state when no fingers are present on the device. The time is in terms of
+10 milliseconds - a doze_holdoff value of 3 corresponds to a time period of 30
+milliseconds. 
+
+doze_interval (RW) - controls the time period that the device sleeps between
+finger-activity checks (F01_RMI_Ctrl3).  The time is in terms of 10 milli-
+seconds - a doze_interval value of 3 corresponds to a time period of 30
+milliseconds.  See also wakeup_threshold, below.
+
+flashprog (RO) - Defines the current device operating mode. The flashprog flag
+is set if the normal operation of the device is suspended because the device is
+in a flash programming enabled state.
+
+interruptEnable (RW) - This represents the current RMI4 interrupt mask
+(F01_RMI_Ctrl1 registers) as a space separated string of 0s and 1s.  Low order
+bits of the mask are presented first, highest order bits are at the end of
+the string.
+
+manufacturer (RO) - This is the identity of the manufacturer of the device,
+as obtained from F01_RMI_Query0.
+
+nosleep (RW) - Writing 1 to this parameter disables all normal firmware
+powersaving behaviors and forces the device to run at full power without
+sleeping.  The default state for this bit is 0.
+
+productinfo (RO) - the product info bytes, as determined from F01_RMI_Query2
+and F01_RMI_Query3 registers.
+
+productID (RO) - A string of up to 10 characters, identifying the product.
+
+reportrate (RW) - This is the current value of the RMI4 ReportRate bit
+(F01_RMI_Ctrl0, bit 6).  The meaning of this bit is very much device-dependent.
+Please see the RMI4 specification for details.
+
+reset (WO) - Writing a 1 to this write only register forces the device to reset.
+
+sleepmode (RW) - Controls power management on the device.  Writing 0 to this
+parameter puts the device into its normal operating mode.  Writing 1 to this
+parameter fully disables touch sensors and similar inputs - no touch data will
+be reported from the device in this mode.  Writing 2 or 3 to this device may
+or may not have an effect, depending on the particular device - see the product
+specification for your sensor for details.
+
+statuscode (RO) - Reports the most recent device status, such as invalid
+configuration, device reset, CRC failure, and so on.  Please se the RMI4
+specification for details.
+
+unconfigured (RO) - This is the opposite of the configured bit, described above.
+
+wakeup_threshold (RW) - This controls the change in capacitive signal needed
+to wake the device from the doze state.  Please see the RMI4 specification for
+the F01_RMI_Ctrl3 register for more details.
+
+
+F11 2D Sensing Parameters
+=========================
+
+swap (RW) - Writing 1 to this parameter swaps the X and Y axis as reported by
+the device, rotating the reported coordinates by 90 degrees.  This can be
+useful when installing a landscape sensor over a portrait display, for example.
+The default state for this parameter is 0.  If enabled, swap is applied before
+any other transformation.
+
+flip (RW) - This parameter is a pair of single binary digits (for example,
+"0 0" or "1 0"), corresponding to the X and Y axis.  Writing a 1 for a
+particular axis will invert the coordinates reported by the device along
+that axis.  For example writing "0 1" to this parameter will flip the Y axis
+top to bottom, but leave the X axis unchanged.  If enabled, flip is applied
+after swap and before offset.
+
+offset (RW) - This is a pair of values that will be SUBTRACTED from the X and
+Y coordinates, respectively.  If non-zero, offset will be applied after flip
+and before clip.  The default value for offset is 0 for both X and Y.
+
+clip (RW) - This is a set of four unsigned values in the range [0..65535],
+representing the lower bounds on X, the upper bounds on X, the lower bounds on
+Y, and the upper bounds on Y.  Coordinates will be clipped to these ranges.  If
+enabled, clip is the final transformation to be applied to the coordinates.
+The default upper and lower bounds for clip are 0 and 65535 respectively for
+both axes.
+
+maxPos (RW) - Contains two values specifying the the maximum reported
+coordinates for X and Y axes, respectively.
+
+relreport (RW) - Writing 1 to this parameter enables relative motion reporting.
+The default state for this parameter is 0.
+
+
+F19 Capacitive Button Parameters
+================================
+
+button_count (RO) - Tells you how many buttons this device supports.  Buttons
+are numbered from 0 through button_count-1.
+
+buttonMap (RW) - This consists of button_count entries, showing which keys each
+button is mapped to.  Writing to this parameter changes the button mappings.
+When writing, you must provide a mapping for each capacitive button.
+
+
+F34 Flash Memory Management Parameters
+======================================
+
+blocksize (RO) - The number of bytes in one data block. When programming the
+firmware, the data should be broken into blocks of this size and each block
+programmed individually.
+
+bootloaderid (RW) - Two ASCII characters identifying the particular bootloader
+version.
+
+cmd (RW) - Write to this parameter to send a flash control operation command.
+See rmi_f34.h for #defines of available commands, and the RMI4 specification
+for details on their meaning.
+
+configblockcount (RO) -  the number of blocks in a configuration image.
+blocksize * configblockcount == total number of bytes in a configuration image.
+
+data (RW) - Write blocks of image or configuration data to this during the
+reflash process.  You can also use this to read the configuration blocks.
+
+imageblockcount (RO) - The number of blocks in a firmware image. blocksize *
+imageblockcount == total number of bytes in a firmware image area.
+
+status (RO) - This parameter, at the completion of a flash operation, indicates
+the success or failure of the
+operation.
+
+
+F54 Diagnostic Reporting Parameters
+===================================
+
+TBD
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
new file mode 100644
index 0000000..64054be
--- /dev/null
+++ b/include/linux/rmi.h
@@ -0,0 +1,652 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _RMI_H
+#define _RMI_H
+#include <linux/kernel.h>
+#include <linux/lockdep.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include <linux/stat.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+#ifdef CONFIG_RMI_DEBUG
+#include <linux/debugfs.h>
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+
+/* Permissions for sysfs attributes.  Since the permissions policy will change
+ * on a global basis in the future, rather than edit all sysfs attrs everywhere
+ * in the driver (and risk screwing that up in the process), we use this handy
+ * set of #defines.  That way when we change the policy for sysfs permissions,
+ * we only need to change them here.
+ */
+#define RMI_RO_ATTR S_IRUGO
+#define RMI_RW_ATTR (S_IRUGO | S_IWUGO)
+#define RMI_WO_ATTR S_IWUGO
+
+enum rmi_attn_polarity {
+	RMI_ATTN_ACTIVE_LOW = 0,
+	RMI_ATTN_ACTIVE_HIGH = 1
+};
+
+/**
+ * struct rmi_f11_axis_alignment - target axis alignment
+ * @swap_axes: set to TRUE if desired to swap x- and y-axis
+ * @flip_x: set to TRUE if desired to flip direction on x-axis
+ * @flip_y: set to TRUE if desired to flip direction on y-axis
+ */
+struct rmi_f11_2d_axis_alignment {
+	bool swap_axes;
+	bool flip_x;
+	bool flip_y;
+	int clip_X_low;
+	int clip_Y_low;
+	int clip_X_high;
+	int clip_Y_high;
+	int offset_X;
+	int offset_Y;
+	int rel_report_enabled;
+
+#ifdef CONFIG_RMI4_DEBUG
+	struct dentry *debugfs_flip;
+	struct dentry *debugfs_clip;
+	struct dentry *debugfs_offset;
+	struct dentry *debugfs_swap;
+	u16 reg_debug_addr;
+	u8 reg_debug_size;
+#endif
+};
+
+/**
+ * struct rmi_f01_power - override default power management settings.
+ *
+ */
+enum rmi_f01_nosleep {
+	RMI_F01_NOSLEEP_DEFAULT = 0,
+	RMI_F01_NOSLEEP_OFF = 1,
+	RMI_F01_NOSLEEP_ON = 2
+};
+
+struct rmi_f01_power_management {
+	enum rmi_f01_nosleep nosleep;
+	u8 wakeup_threshold;
+	u8 doze_holdoff;
+	u8 doze_interval;
+};
+
+struct rmi_button_map {
+	u8 nbuttons;
+	unsigned char *map;
+};
+
+struct rmi_f30_gpioled_map {
+	unsigned char ngpioleds;
+	unsigned char *map;
+};
+
+struct virtualbutton_map {
+	u16 x;
+	u16 y;
+	u16 width;
+	u16 height;
+	u16 code;
+};
+
+struct rmi_f11_virtualbutton_map {
+	u8 buttons;
+	struct virtualbutton_map *map;
+};
+struct rmi_device_platform_data_spi {
+	int block_delay_us;
+	int split_read_block_delay_us;
+	int read_delay_us;
+	int write_delay_us;
+	int split_read_byte_delay_us;
+	int pre_delay_us;
+	int post_delay_us;
+
+	void *cs_assert_data;
+	int (*cs_assert) (const void *cs_assert_data, const bool assert);
+};
+
+/**
+ * struct rmi_device_platform_data - system specific configuration info.
+ *
+ * @firmware_name - if specified will override default firmware name,
+ * for reflashing.
+ *
+ * @f11_type_b - Force F11 to treat the sensor as a multitouch Type B sensor.
+ * This is useful for older Type B RMI4 sensors that can't be queried for this
+ * information, but will cause problems if you force a Type A sensor to be
+ * treated as type_b.
+ *
+ * @pre_suspend - this will be called before any other suspend operations are
+ * done.
+ * @post_suspend - this will be called after all suspend operations are
+ * completed.  This is the ONLY safe place to power off an RMI sensor
+ * during the suspend process.
+ * @pre_resume - this is called before any other resume operations.  If you
+ * powered off the RMI4 sensor in post_suspend(), then you MUST power it back
+ * here, and you MUST wait an appropriate time for the ASIC to come up
+ * (100ms to 200ms, depending on the sensor) before returning.
+ * @pm_data - this will be passed to the various (pre|post)_(suspend/resume)
+ * functions.
+ */
+struct rmi_device_platform_data {
+	char *driver_name;
+	char *sensor_name;	/* Used for diagnostics. */
+
+	int attn_gpio;
+	enum rmi_attn_polarity attn_polarity;
+	bool level_triggered;
+	void *gpio_data;
+	int (*gpio_config)(void *gpio_data, bool configure);
+
+	int reset_delay_ms;
+
+	struct rmi_device_platform_data_spi spi_data;
+
+	/* function handler pdata */
+	struct rmi_f01_power_management power_management;
+	struct rmi_f11_2d_axis_alignment axis_align;
+	struct rmi_button_map *f19_button_map;
+	struct rmi_button_map *f1a_button_map;
+	struct rmi_f30_gpioled_map *gpioled_map;
+	struct rmi_f11_virtualbutton_map *f11_button_map;
+	struct rmi_button_map *f41_button_map;
+
+#ifdef CONFIG_RMI4_FWLIB
+	char *firmware_name;
+#endif
+#ifdef CONFIG_RMI4_F11_TYPEB
+	bool f11_type_b;
+#endif
+
+#ifdef	CONFIG_PM
+	void *pm_data;
+	int (*pre_suspend) (const void *pm_data);
+	int (*post_suspend) (const void *pm_data);
+	int (*pre_resume) (const void *pm_data);
+	int (*post_resume) (const void *pm_data);
+#endif
+};
+
+/**
+ * struct rmi_function_descriptor - RMI function base addresses
+ * @query_base_addr: The RMI Query base address
+ * @command_base_addr: The RMI Command base address
+ * @control_base_addr: The RMI Control base address
+ * @data_base_addr: The RMI Data base address
+ * @interrupt_source_count: The number of irqs this RMI function needs
+ * @function_number: The RMI function number
+ *
+ * This struct is used when iterating the Page Description Table. The addresses
+ * are 16-bit values to include the current page address.
+ *
+ */
+struct rmi_function_descriptor {
+	u16 query_base_addr;
+	u16 command_base_addr;
+	u16 control_base_addr;
+	u16 data_base_addr;
+	u8 interrupt_source_count;
+	u8 function_number;
+	u8 function_version;
+};
+
+struct rmi_function_container;
+struct rmi_device;
+
+/**
+ * struct rmi_function_handler - an RMI function handler
+ * @func: The RMI function number
+ * @init: Callback for RMI function init
+ * @attention: Callback for RMI function attention
+ * @suspend: Callback for function suspend, returns 0 for success.
+ * @resume: Callback for RMI function resume, returns 0 for success.
+ * @remove: Callback for RMI function removal
+ *
+ * This struct describes the interface of an RMI function. These are
+ * registered to the bus using the rmi_register_function_driver() call.
+ *
+ */
+struct rmi_function_handler {
+	int func;
+	int (*init)(struct rmi_function_container *fc);
+	int (*config)(struct rmi_function_container *fc);
+	int (*reset)(struct rmi_function_container *fc);
+	int (*attention)(struct rmi_function_container *fc, u8 *irq_bits);
+#ifdef CONFIG_PM
+	int (*suspend)(struct rmi_function_container *fc);
+	int (*resume)(struct rmi_function_container *fc);
+#if defined(CONFIG_HAS_EARLYSUSPEND) && \
+		!defined(CONFIG_RMI4_SPECIAL_EARLYSUSPEND)
+	int (*early_suspend)(struct rmi_function_container *fc);
+	int (*late_resume)(struct rmi_function_container *fc);
+#endif
+#endif
+	void (*remove)(struct rmi_function_container *fc);
+};
+
+/**
+ * struct rmi_function_container - an element in a function handler list
+ * @list: The list
+ * @fd: The function descriptor of the RMI function
+ * @rmi_dev: Pointer to the RMI device associated with this function container
+ * @fh: The callbacks connected to this function
+ * @num_of_irqs: The number of irqs needed by this function
+ * @irq_pos: The position in the irq bitfield this function holds
+ * @data: Private data pointer
+ *
+ */
+struct rmi_function_container {
+	struct list_head list;
+
+	struct rmi_function_descriptor fd;
+	struct rmi_device *rmi_dev;
+	struct rmi_function_handler *fh;
+	struct device dev;
+
+#ifdef CONFIG_RMI4_DEBUG
+	struct dentry *debugfs_root;
+#endif
+
+	int num_of_irqs;
+	int irq_pos;
+	u8 *irq_mask;
+
+	void *data;
+};
+#define to_rmi_function_container(d) \
+		container_of(d, struct rmi_function_container, dev);
+
+
+/**
+ * struct rmi_driver - represents an RMI driver
+ * @driver: Device driver model driver
+ * @probe: Callback for device probe
+ * @remove: Callback for device removal
+ * @shutdown: Callback for device shutdown
+ * @irq_handler: Callback for handling irqs
+ * @fh_add: Callback for function handler add
+ * @fh_remove: Callback for function handler remove
+ * @get_func_irq_mask: Callback for calculating interrupt mask
+ * @store_irq_mask: Callback for storing and replacing interrupt mask
+ * @restore_irq_mask: Callback for restoring previously stored interrupt mask
+ * @data: Private data pointer
+ *
+ * The RMI driver implements a driver on the RMI bus.
+ *
+ */
+struct rmi_driver {
+	struct device_driver driver;
+
+	int (*probe)(struct rmi_device *rmi_dev);
+	int (*remove)(struct rmi_device *rmi_dev);
+	void (*shutdown)(struct rmi_device *rmi_dev);
+	int (*irq_handler)(struct rmi_device *rmi_dev, int irq);
+	int (*reset_handler)(struct rmi_device *rmi_dev);
+	void (*fh_add)(struct rmi_device *rmi_dev,
+		       struct rmi_function_handler *fh);
+	void (*fh_remove)(struct rmi_device *rmi_dev,
+			  struct rmi_function_handler *fh);
+	u8* (*get_func_irq_mask)(struct rmi_device *rmi_dev,
+			    struct rmi_function_container *fc);
+	int (*store_irq_mask)(struct rmi_device *rmi_dev, u8* new_interupts);
+	int (*restore_irq_mask)(struct rmi_device *rmi_dev);
+	void *data;
+};
+#define to_rmi_driver(d) \
+	container_of(d, struct rmi_driver, driver);
+
+/** struct rmi_phys_info - diagnostic information about the RMI physical
+ * device, used in the phys sysfs file.
+ * @proto String indicating the protocol being used.
+ * @tx_count Number of transmit operations.
+ * @tx_bytes Number of bytes transmitted.
+ * @tx_errs  Number of errors encountered during transmit operations.
+ * @rx_count Number of receive operations.
+ * @rx_bytes Number of bytes received.
+ * @rx_errs  Number of errors encountered during receive operations.
+ * @att_count Number of times ATTN assertions have been handled.
+ */
+struct rmi_phys_info {
+	char *proto;
+	long tx_count;
+	long tx_bytes;
+	long tx_errs;
+	long rx_count;
+	long rx_bytes;
+	long rx_errs;
+	long attn_count;
+};
+
+/**
+ * struct rmi_phys_device - represent an RMI physical device
+ * @dev: Pointer to the communication device, e.g. i2c or spi
+ * @rmi_dev: Pointer to the RMI device
+ * @write: Callback for write
+ * @write_block: Callback for writing a block of data
+ * @read: Callback for read
+ * @read_block: Callback for reading a block of data
+ * @data: Private data pointer
+ *
+ * The RMI physical device implements the glue between different communication
+ * buses such as I2C and SPI.
+ *
+ */
+struct rmi_phys_device {
+	struct device *dev;
+	struct rmi_device *rmi_dev;
+
+	int (*write)(struct rmi_phys_device *phys, u16 addr, u8 data);
+	int (*write_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
+			   int len);
+	int (*read)(struct rmi_phys_device *phys, u16 addr, u8 *buf);
+	int (*read_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
+			  int len);
+
+	int (*enable_device) (struct rmi_phys_device *phys);
+	void (*disable_device) (struct rmi_phys_device *phys);
+
+	void *data;
+
+	struct rmi_phys_info info;
+};
+
+/**
+ * struct rmi_device - represents an RMI device
+ * @dev: The device created for the RMI bus
+ * @number: Unique number for the device on the bus.
+ * @driver: Pointer to associated driver
+ * @phys: Pointer to the physical interface
+ * @early_suspend_handler: Pointers to early_suspend and late_resume, if
+ * configured.
+ *
+ * This structs represent an RMI device.
+ *
+ */
+struct rmi_device {
+	struct device dev;
+	int number;
+
+	struct rmi_driver *driver;
+	struct rmi_phys_device *phys;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct early_suspend early_suspend_handler;
+#endif
+#ifdef CONFIG_RMI4_DEBUG
+	struct dentry *debugfs_root;
+#endif
+};
+#define to_rmi_device(d) container_of(d, struct rmi_device, dev);
+#define to_rmi_platform_data(d) ((d)->phys->dev->platform_data);
+
+static inline void rmi_set_driverdata(struct rmi_device *d, void *data)
+{
+	dev_set_drvdata(&d->dev, data);
+}
+
+static inline void *rmi_get_driverdata(struct rmi_device *d)
+{
+	return dev_get_drvdata(&d->dev);
+}
+
+/**
+ * rmi_read - RMI read byte
+ * @d: Pointer to an RMI device
+ * @addr: The address to read from
+ * @buf: The read buffer
+ *
+ * Reads a byte of data using the underlaying physical protocol in to buf. It
+ * returns zero or a negative error code.
+ */
+static inline int rmi_read(struct rmi_device *d, u16 addr, u8 *buf)
+{
+	return d->phys->read(d->phys, addr, buf);
+}
+
+/**
+ * rmi_read_block - RMI read block
+ * @d: Pointer to an RMI device
+ * @addr: The start address to read from
+ * @buf: The read buffer
+ * @len: Length of the read buffer
+ *
+ * Reads a block of byte data using the underlaying physical protocol in to buf.
+ * It returns the amount of bytes read or a negative error code.
+ */
+static inline int rmi_read_block(struct rmi_device *d, u16 addr, u8 *buf,
+				 int len)
+{
+	return d->phys->read_block(d->phys, addr, buf, len);
+}
+
+/**
+ * rmi_write - RMI write byte
+ * @d: Pointer to an RMI device
+ * @addr: The address to write to
+ * @data: The data to write
+ *
+ * Writes a byte from buf using the underlaying physical protocol. It
+ * returns zero or a negative error code.
+ */
+static inline int rmi_write(struct rmi_device *d, u16 addr, u8 data)
+{
+	return d->phys->write(d->phys, addr, data);
+}
+
+/**
+ * rmi_write_block - RMI write block
+ * @d: Pointer to an RMI device
+ * @addr: The start address to write to
+ * @buf: The write buffer
+ * @len: Length of the write buffer
+ *
+ * Writes a block of byte data from buf using the underlaying physical protocol.
+ * It returns the amount of bytes written or a negative error code.
+ */
+static inline int rmi_write_block(struct rmi_device *d, u16 addr, u8 *buf,
+				  int len)
+{
+	return d->phys->write_block(d->phys, addr, buf, len);
+}
+
+/**
+ * rmi_register_driver - register rmi driver
+ * @driver: the driver to register
+ *
+ * This function registers an RMI driver to the RMI bus.
+ */
+int rmi_register_driver(struct rmi_driver *driver);
+
+/**
+ * rmi_unregister_driver - unregister rmi driver
+ * @driver: the driver to unregister
+ *
+ * This function unregisters an RMI driver to the RMI bus.
+ */
+void rmi_unregister_driver(struct rmi_driver *driver);
+
+/**
+ * rmi_register_phys_device - register a physical device connection
+ * @phys: the physical driver to register
+ *
+ * This function registers a physical driver to the RMI bus. These drivers
+ * provide a communication layer for the drivers connected to the bus, e.g.
+ * I2C, SPI and so on.
+ */
+int rmi_register_phys_device(struct rmi_phys_device *phys);
+
+/**
+ * rmi_unregister_phys_device - unregister a physical device connection
+ * @phys: the physical driver to unregister
+ *
+ * This function unregisters a physical driver from the RMI bus.
+ */
+void rmi_unregister_phys_device(struct rmi_phys_device *phys);
+
+/**
+ * rmi_register_function_driver - register an RMI function driver
+ * @fh: the function handler to register
+ *
+ * This function registers support for a new RMI function to the bus. All
+ * drivers on the bus will be notified of the presence of the new function
+ * driver.
+ */
+int rmi_register_function_driver(struct rmi_function_handler *fh);
+
+/**
+ * rmi_unregister_function_driver - unregister an RMI function driver
+ * @fh: the function handler to unregister
+ *
+ * This function unregisters a RMI function from the RMI bus. All drivers on
+ * the bus will be notified of the removal of a function driver.
+ */
+void rmi_unregister_function_driver(struct rmi_function_handler *fh);
+
+/**
+ * rmi_get_function_handler - get a pointer to specified RMI function
+ * @id: the RMI function id
+ *
+ * This function gets the specified RMI function handler from the list of
+ * supported functions.
+ */
+struct rmi_function_handler *rmi_get_function_handler(int id);
+
+
+struct rmi_char_device;
+
+/**
+ * rmi_char_driver - a general driver that doesn't handle specific functions,
+ * operating outside the bus::sensor::functions
+ * @match: returns 1 if the driver wants to talk to the specified rmi_dev.
+ *
+ * All of the above are optional except driver and init which are required.
+ *
+ */
+struct rmi_char_driver {
+	struct device_driver driver;
+
+	int (*match)(struct rmi_device *rmi_dev);
+	int (*init)(struct rmi_char_device *cd);
+	int (*attention)(struct rmi_char_device *cd, u8 *irq_bits);
+#ifdef CONFIG_PM
+	int (*suspend)(struct rmi_char_device *cd);
+	int (*resume)(struct rmi_char_device *cd);
+#if defined(CONFIG_HAS_EARLYSUSPEND) && \
+			!defined(CONFIG_RMI4_SPECIAL_EARLYSUSPEND)
+	int (*early_suspend)(struct rmi_char_device *cd);
+	int (*late_resume)(struct rmi_char_device *cd);
+#endif
+#endif
+	void (*remove)(struct rmi_char_device *cd);
+
+	struct list_head devices;
+};
+
+struct rmi_char_device {
+	struct list_head list;
+
+	struct rmi_device *rmi_dev;
+	struct rmi_char_driver *driver;
+	struct device dev;
+
+#ifdef CONFIG_RMI4_DEBUG
+	struct dentry *debugfs_root;
+#endif
+
+	void *data;
+};
+#define to_rmi_char_device(d) \
+		container_of(d, struct rmi_char_device, dev)
+
+int rmi_register_character_driver(struct rmi_char_driver *char_driver);
+int rmi_unregister_character_driver(struct rmi_char_driver *char_driver);
+
+
+/* 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.
+ */
+static inline 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.
+ */
+static inline void hstoba(unsigned char *dest, unsigned short src)
+{
+	dest[0] = src % 0x100;
+	dest[1] = src / 0x100;
+}
+
+/* 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).
+ */
+static inline 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).
+ */
+static inline 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;
+}
+
+/* utility function for bit access of u8*'s */
+void u8_set_bit(u8 *target, int pos);
+void u8_clear_bit(u8 *target, int pos);
+bool u8_is_set(u8 *target, int pos);
+bool u8_is_any_set(u8 *target, int size);
+void u8_or(u8 *dest, u8* target1, u8* target2, int size);
+void u8_and(u8 *dest, u8* target1, u8* target2, int size);
+#endif

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 2/17] input: RMI4 core bus and sensor drivers.
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
  2012-08-17 22:17 ` [RFC PATCH 1/17] input: RMI4 public header file and documentation Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-23  8:55   ` Linus Walleij
  2012-08-17 22:17 ` [RFC PATCH 3/17] input: RMI4 physical layer drivers for I2C and SPI Christopher Heiny
                   ` (16 subsequent siblings)
  18 siblings, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

Driver for Synaptics touchscreens using RMI4 protocol.

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_bus.c    |  504 ++++++++++++
 drivers/input/rmi4/rmi_driver.c | 1716 +++++++++++++++++++++++++++++++++++++++
 drivers/input/rmi4/rmi_driver.h |  440 ++++++++++
 3 files changed, 2660 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
new file mode 100644
index 0000000..0f29abb
--- /dev/null
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/rmi.h>
+#include <linux/types.h>
+#ifdef CONFIG_RMI4_DEBUG
+#include <linux/debugfs.h>
+#endif
+#include "rmi_driver.h"
+
+DEFINE_MUTEX(rmi_bus_mutex);
+
+static struct rmi_function_list {
+	struct list_head list;
+	struct rmi_function_handler *fh;
+} rmi_supported_functions;
+
+static struct rmi_character_driver_list {
+	struct list_head list;
+	struct rmi_char_driver *cd;
+} rmi_character_drivers;
+
+static atomic_t physical_device_count/* = ATOMIC_INIT(0) ?*/;
+
+#ifdef CONFIG_RMI4_DEBUG
+static struct dentry *rmi_debugfs_root;
+#endif
+
+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;
+
+	rmi_driver = to_rmi_driver(driver);
+	rmi_dev = to_rmi_device(dev);
+	pdata = to_rmi_platform_data(rmi_dev);
+	dev_dbg(dev, "%s: Matching %s.\n", __func__, pdata->sensor_name);
+
+	if (!strcmp(pdata->driver_name, rmi_driver->driver.name)) {
+		rmi_dev->driver = rmi_driver;
+		dev_dbg(dev, "%s: Match %s to %s succeeded.\n", __func__,
+			pdata->driver_name, rmi_driver->driver.name);
+		return 1;
+	}
+
+	dev_vdbg(dev, "%s: Match %s to %s failed.\n", __func__,
+		pdata->driver_name, rmi_driver->driver.name);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int rmi_bus_suspend(struct device *dev)
+{
+	struct device_driver *driver = dev->driver;
+#ifdef GENERIC_SUBSYS_PM_OPS
+	const struct dev_pm_ops *pm;
+#endif
+
+	if (!driver)
+		return 0;
+
+#ifdef GENERIC_SUBSYS_PM_OPS
+	pm = driver->pm;
+	if (pm && pm->suspend)
+		return pm->suspend(dev);
+#endif
+	if (driver->suspend)
+		return driver->suspend(dev, PMSG_SUSPEND);
+
+	return 0;
+}
+
+static int rmi_bus_resume(struct device *dev)
+{
+	struct device_driver *driver = dev->driver;
+#ifdef GENERIC_SUBSYS_PM_OPS
+	const struct dev_pm_ops *pm;
+#endif
+
+	if (!driver)
+		return 0;
+
+#ifdef GENERIC_SUBSYS_PM_OPS
+	pm = driver->pm;
+	if (pm && pm->resume)
+		return pm->resume(dev);
+#endif
+	if (driver->resume)
+		return driver->resume(dev);
+
+	return 0;
+}
+#endif
+
+static int rmi_bus_probe(struct device *dev)
+{
+	struct rmi_driver *driver;
+	struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+	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);
+
+	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
+};
+
+static void release_rmidev_device(struct device *dev)
+{
+	device_unregister(dev);
+}
+
+int rmi_register_phys_device(struct rmi_phys_device *phys)
+{
+	struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+	struct rmi_device *rmi_dev;
+
+	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;
+
+	rmi_dev->number = atomic_inc_return(&physical_device_count) - 1;
+	rmi_dev->dev.release = release_rmidev_device;
+
+	dev_set_name(&rmi_dev->dev, "sensor%02d", rmi_dev->number);
+	dev_dbg(phys->dev, "%s: Registered %s as %s.\n", __func__,
+		pdata->sensor_name, dev_name(&rmi_dev->dev));
+
+#ifdef CONFIG_RMI4_DEBUG
+	if (rmi_debugfs_root) {
+		rmi_dev->debugfs_root = debugfs_create_dir(
+			dev_name(&rmi_dev->dev), rmi_debugfs_root);
+		if (!rmi_dev->debugfs_root)
+			dev_err(&rmi_dev->dev, "Failed to create debugfs root.\n");
+	}
+#endif
+	phys->rmi_dev = rmi_dev;
+	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;
+
+#ifdef CONFIG_RMI4_DEBUG
+	if (rmi_dev->debugfs_root)
+		debugfs_remove(rmi_dev->debugfs_root);
+#endif
+
+	kfree(rmi_dev);
+}
+EXPORT_SYMBOL(rmi_unregister_phys_device);
+
+int rmi_register_driver(struct rmi_driver *driver)
+{
+	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_add_function_handler(struct device *dev, void *data)
+{
+	struct rmi_driver *driver;
+	struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+	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;
+	int retval = 0;
+
+	mutex_lock(&rmi_bus_mutex);
+	fh_dup = rmi_get_function_handler(fh->func);
+	if (fh_dup) {
+		pr_err("%s: function F%02x already registered!\n", __func__,
+			fh->func);
+		retval = -EINVAL;
+		goto error_exit;
+	}
+
+	entry = kzalloc(sizeof(struct rmi_function_list), GFP_KERNEL);
+	if (!entry) {
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+
+	entry->fh = fh;
+	INIT_LIST_HEAD(&entry->list);
+	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_add_function_handler);
+
+error_exit:
+	mutex_unlock(&rmi_bus_mutex);
+	return retval;
+}
+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);
+
+	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;
+
+	/* notify devices of the removal of the function handler */
+	bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_remove);
+
+	if (list_empty(&rmi_supported_functions.list))
+		return;
+
+	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;
+
+	if (list_empty(&rmi_supported_functions.list))
+		return NULL;
+
+	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 void rmi_release_character_device(struct device *dev)
+{
+	dev_dbg(dev, "%s: Called.\n", __func__);
+	return;
+}
+
+static int rmi_register_character_device(struct device *dev, void *data)
+{
+	struct rmi_device *rmi_dev;
+	struct rmi_char_driver *char_driver = data;
+	struct rmi_char_device *char_dev;
+	int retval;
+
+	dev_dbg(dev, "Attaching character device.\n");
+	rmi_dev = to_rmi_device(dev);
+	if (char_driver->match && !char_driver->match(rmi_dev))
+		return 0;
+
+	if (!char_driver->init) {
+		dev_err(dev, "ERROR: No init() function in %s.\n", __func__);
+		return -EINVAL;
+	}
+
+	char_dev = kzalloc(sizeof(struct rmi_char_device), GFP_KERNEL);
+	if (!char_dev)
+		return -ENOMEM;
+
+	char_dev->rmi_dev = rmi_dev;
+	char_dev->driver = char_driver;
+
+	char_dev->dev.parent = dev;
+	char_dev->dev.release = rmi_release_character_device;
+	char_dev->dev.driver = &char_driver->driver;
+	retval = device_register(&char_dev->dev);
+	if (!retval) {
+		dev_err(dev, "Failed to register character device.\n");
+		goto error_exit;
+	}
+
+	retval = char_driver->init(char_dev);
+	if (retval) {
+		dev_err(dev, "Failed to initialize character device.\n");
+		goto error_exit;
+	}
+
+	mutex_lock(&rmi_bus_mutex);
+	list_add_tail(&char_dev->list, &char_driver->devices);
+	mutex_unlock(&rmi_bus_mutex);
+	dev_info(&char_dev->dev, "Registered a device.\n");
+	return retval;
+
+error_exit:
+	kfree(char_dev);
+	return retval;
+}
+
+int rmi_register_character_driver(struct rmi_char_driver *char_driver)
+{
+	struct rmi_character_driver_list *entry;
+	int retval;
+
+	pr_debug("%s: Registering character driver %s.\n", __func__,
+		char_driver->driver.name);
+
+	char_driver->driver.bus = &rmi_bus_type;
+	INIT_LIST_HEAD(&char_driver->devices);
+	retval = driver_register(&char_driver->driver);
+	if (retval) {
+		pr_err("%s: Failed to register %s, code: %d.\n", __func__,
+		       char_driver->driver.name, retval);
+		return retval;
+	}
+
+	entry = kzalloc(sizeof(struct rmi_character_driver_list), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+	entry->cd = char_driver;
+
+	mutex_lock(&rmi_bus_mutex);
+	list_add_tail(&entry->list, &rmi_character_drivers.list);
+	mutex_unlock(&rmi_bus_mutex);
+
+	/* notify devices of the removal of the function handler */
+	bus_for_each_dev(&rmi_bus_type, NULL, char_driver,
+			 rmi_register_character_device);
+
+	return 0;
+}
+EXPORT_SYMBOL(rmi_register_character_driver);
+
+
+int rmi_unregister_character_driver(struct rmi_char_driver *char_driver)
+{
+	struct rmi_character_driver_list *entry, *n;
+	struct rmi_char_device *char_dev, *m;
+	pr_debug("%s: Unregistering character driver %s.\n", __func__,
+		char_driver->driver.name);
+
+	mutex_lock(&rmi_bus_mutex);
+	list_for_each_entry_safe(char_dev, m, &char_driver->devices,
+				 list) {
+		list_del(&char_dev->list);
+		char_dev->driver->remove(char_dev);
+	}
+	list_for_each_entry_safe(entry, n, &rmi_character_drivers.list,
+				 list) {
+		if (entry->cd == char_driver) {
+			list_del(&entry->list);
+			kfree(entry);
+		}
+	}
+	mutex_unlock(&rmi_bus_mutex);
+
+	driver_unregister(&char_driver->driver);
+
+	return 0;
+}
+EXPORT_SYMBOL(rmi_unregister_character_driver);
+
+static int __init rmi_bus_init(void)
+{
+	int error;
+
+	mutex_init(&rmi_bus_mutex);
+	INIT_LIST_HEAD(&rmi_supported_functions.list);
+	INIT_LIST_HEAD(&rmi_character_drivers.list);
+
+#ifdef CONFIG_RMI4_DEBUG
+	rmi_debugfs_root = debugfs_create_dir(rmi_bus_type.name, NULL);
+	if (!rmi_debugfs_root)
+		pr_err("%s: Failed to create debugfs root.\n", __func__);
+	else if (IS_ERR(rmi_debugfs_root)) {
+		pr_err("%s: Kernel may not contain debugfs support, code=%ld\n",
+		       __func__, PTR_ERR(rmi_debugfs_root));
+		rmi_debugfs_root = NULL;
+	}
+#endif
+
+	error = bus_register(&rmi_bus_type);
+	if (error < 0) {
+		pr_err("%s: error registering the RMI bus: %d\n", __func__,
+		       error);
+		return error;
+	}
+	pr_debug("%s: successfully registered RMI bus.\n", __func__);
+
+	return 0;
+}
+
+static void __exit rmi_bus_exit(void)
+{
+	/* We should only ever get here if all drivers are unloaded, so
+	 * all we have to do at this point is unregister ourselves.
+	 */
+#ifdef CONFIG_RMI4_DEBUG
+	if (rmi_debugfs_root)
+		debugfs_remove(rmi_debugfs_root);
+#endif
+	bus_unregister(&rmi_bus_type);
+}
+
+module_init(rmi_bus_init);
+module_exit(rmi_bus_exit);
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com");
+MODULE_DESCRIPTION("RMI bus");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
new file mode 100644
index 0000000..5aea9ed
--- /dev/null
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -0,0 +1,1716 @@
+/*
+ * Copyright (c) 2011, 2012 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/rmi.h>
+#include "rmi_driver.h"
+#include "rmi_f01.h"
+
+#ifdef CONFIG_RMI4_DEBUG
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#endif
+
+#define REGISTER_DEBUG 0
+
+#define HAS_NONSTANDARD_PDT_MASK 0x40
+#define RMI4_MAX_PAGE 0xff
+#define RMI4_PAGE_SIZE 0x100
+
+#define RMI_DEVICE_RESET_CMD	0x01
+#define DEFAULT_RESET_DELAY_MS	100
+
+#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_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);
+
+#if REGISTER_DEBUG
+static ssize_t rmi_driver_reg_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count);
+#endif
+
+#ifdef CONFIG_RMI4_DEBUG
+
+struct driver_debugfs_data {
+	bool done;
+	struct rmi_device *rmi_dev;
+};
+
+static int debug_open(struct inode *inodep, struct file *filp)
+{
+	struct driver_debugfs_data *data;
+
+	data = kzalloc(sizeof(struct driver_debugfs_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->rmi_dev = inodep->i_private;
+	filp->private_data = data;
+	return 0;
+}
+
+static int debug_release(struct inode *inodep, struct file *filp)
+{
+	kfree(filp->private_data);
+	return 0;
+}
+
+#ifdef CONFIG_RMI4_SPI
+#define DELAY_NAME "delay"
+
+static ssize_t delay_read(struct file *filp, char __user *buffer, size_t size,
+		    loff_t *offset) {
+	struct driver_debugfs_data *data = filp->private_data;
+	struct rmi_device_platform_data *pdata =
+			data->rmi_dev->phys->dev->platform_data;
+	int retval;
+	char local_buf[size];
+
+	if (data->done)
+		return 0;
+
+	data->done = 1;
+
+	retval = snprintf(local_buf, 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);
+
+	if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+		return -EFAULT;
+
+	return retval;
+}
+
+static ssize_t delay_write(struct file *filp, const char __user *buffer,
+			   size_t size, loff_t *offset) {
+	struct driver_debugfs_data *data = filp->private_data;
+	struct rmi_device_platform_data *pdata =
+			data->rmi_dev->phys->dev->platform_data;
+	int retval;
+	char local_buf[size];
+	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 = copy_from_user(local_buf, buffer, size);
+	if (retval)
+		return -EFAULT;
+
+	retval = sscanf(local_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(&data->rmi_dev->dev,
+			"Incorrect number of values provided for delay.");
+		return -EINVAL;
+	}
+	if (new_read_delay < 0) {
+		dev_err(&data->rmi_dev->dev,
+			"Byte delay must be positive microseconds.\n");
+		return -EINVAL;
+	}
+	if (new_write_delay < 0) {
+		dev_err(&data->rmi_dev->dev,
+			"Write delay must be positive microseconds.\n");
+		return -EINVAL;
+	}
+	if (new_block_delay < 0) {
+		dev_err(&data->rmi_dev->dev,
+			"Block delay must be positive microseconds.\n");
+		return -EINVAL;
+	}
+	if (new_pre_delay < 0) {
+		dev_err(&data->rmi_dev->dev,
+			"Pre-transfer delay must be positive microseconds.\n");
+		return -EINVAL;
+	}
+	if (new_post_delay < 0) {
+		dev_err(&data->rmi_dev->dev,
+			"Post-transfer delay must be positive microseconds.\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(&data->rmi_dev->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 size;
+}
+
+static const struct file_operations delay_fops = {
+	.owner = THIS_MODULE,
+	.open = debug_open,
+	.release = debug_release,
+	.read = delay_read,
+	.write = delay_write,
+};
+#endif /* CONFIG_RMI4_SPI */
+
+#define PHYS_NAME "phys"
+
+static ssize_t phys_read(struct file *filp, char __user *buffer, size_t size,
+		    loff_t *offset) {
+	struct driver_debugfs_data *data = filp->private_data;
+	struct rmi_phys_info *info = &data->rmi_dev->phys->info;
+	int retval;
+	char local_buf[size];
+
+	if (data->done)
+		return 0;
+
+	data->done = 1;
+
+	retval = snprintf(local_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);
+	if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+		return -EFAULT;
+
+	return retval;
+}
+
+static const struct file_operations phys_fops = {
+	.owner = THIS_MODULE,
+	.open = debug_open,
+	.release = debug_release,
+	.read = phys_read,
+};
+
+static int setup_debugfs(struct rmi_device *rmi_dev)
+{
+	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+#ifdef CONFIG_RMI4_SPI
+	struct rmi_phys_info *info = &rmi_dev->phys->info;
+#endif
+	int retval = 0;
+
+	if (!rmi_dev->debugfs_root)
+		return -ENODEV;
+
+#ifdef CONFIG_RMI4_SPI
+	if (!strncmp("spi", info->proto, 3)) {
+		data->debugfs_delay = debugfs_create_file(DELAY_NAME,
+				RMI_RW_ATTR, rmi_dev->debugfs_root, rmi_dev,
+				&delay_fops);
+		if (!data->debugfs_delay || IS_ERR(data->debugfs_delay)) {
+			dev_warn(&rmi_dev->dev, "Failed to create debugfs delay.\n");
+			data->debugfs_delay = NULL;
+		}
+	}
+#endif
+
+	data->debugfs_phys = debugfs_create_file(PHYS_NAME, RMI_RO_ATTR,
+				rmi_dev->debugfs_root, rmi_dev, &phys_fops);
+	if (!data->debugfs_phys || IS_ERR(data->debugfs_phys)) {
+		dev_warn(&rmi_dev->dev, "Failed to create debugfs phys.\n");
+		data->debugfs_phys = NULL;
+	}
+
+	return retval;
+}
+
+static void teardown_debugfs(struct rmi_device *rmi_dev)
+{
+	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+#ifdef CONFIG_RMI4_SPI
+	if (!data->debugfs_delay)
+		debugfs_remove(data->debugfs_delay);
+#endif
+	if (!data->debugfs_phys)
+		debugfs_remove(data->debugfs_phys);
+}
+#endif
+
+static int rmi_driver_process_reset_requests(struct rmi_device *rmi_dev);
+
+static int rmi_driver_process_config_requests(struct rmi_device *rmi_dev);
+
+static int rmi_driver_irq_restore(struct rmi_device *rmi_dev);
+
+static struct device_attribute attrs[] = {
+	__ATTR(enabled, RMI_RW_ATTR,
+	       rmi_driver_enabled_show, rmi_driver_enabled_store),
+#if REGISTER_DEBUG
+	__ATTR(reg, RMI_WO_ATTR,
+	       rmi_show_error, rmi_driver_reg_store),
+#endif
+};
+
+static struct device_attribute bsr_attribute = __ATTR(bsr, RMI_RW_ATTR,
+	       rmi_driver_bsr_show, rmi_driver_bsr_store);
+
+/* 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];
+}
+
+/* Utility routine to set bits in a register. */
+int rmi_set_bits(struct rmi_device *rmi_dev, u16 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, u16 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);
+
+	if (!data) {
+		dev_err(&rmi_dev->dev, "WTF: No driver data in %s\n", __func__);
+		return;
+	}
+
+	if (data->f01_container) {
+		if (data->f01_container->fh &&
+				data->f01_container->fh->remove)
+			data->f01_container->fh->remove(data->f01_container);
+		device_unregister(&data->f01_container->dev);
+		kfree(data->f01_container->irq_mask);
+		kfree(data->f01_container);
+		data->f01_container = NULL;
+	}
+
+	if (list_empty(&data->rmi_functions.list))
+		return;
+
+	list_for_each_entry_safe(entry, n, &data->rmi_functions.list, list) {
+		if (entry->fh) {
+			if (entry->fh->remove)
+				entry->fh->remove(entry);
+			device_unregister(&entry->dev);
+		}
+		kfree(entry->irq_mask);
+		list_del(&entry->list);
+		kfree(entry);
+	}
+}
+
+static void no_op(struct device *dev)
+{
+	dev_dbg(dev, "REMOVING KOBJ!");
+	kobject_put(&dev->kobj);
+}
+
+static int init_one_function(struct rmi_device *rmi_dev,
+			     struct rmi_function_container *fc)
+{
+	int retval;
+
+	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;
+	/* This memset might not be what we want to do... */
+	memset(&(fc->dev), 0, sizeof(struct device));
+	dev_set_name(&(fc->dev), "fn%02x", fc->fd.function_number);
+	fc->dev.release = no_op;
+
+	fc->dev.parent = &rmi_dev->dev;
+	dev_dbg(&rmi_dev->dev, "Register F%02X.\n", fc->fd.function_number);
+	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;
+	}
+
+	dev_dbg(&rmi_dev->dev, "Init F%02X.\n", fc->fd.function_number);
+	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 (!data)
+		return;
+	if (fh->func == 0x01) {
+		if (data->f01_container)
+			data->f01_container->fh = fh;
+	} else if (!list_empty(&data->rmi_functions.list)) {
+		mutex_lock(&data->pdt_mutex);
+		list_for_each_entry(entry, &data->rmi_functions.list, list)
+			if (entry->fd.function_number == fh->func) {
+				entry->fh = fh;
+				if (init_one_function(rmi_dev, entry) < 0)
+					entry->fh = NULL;
+			}
+		mutex_unlock(&data->pdt_mutex);
+	}
+
+}
+
+static void rmi_driver_fh_remove(struct rmi_device *rmi_dev,
+				 struct rmi_function_handler *fh)
+{
+	struct rmi_function_container *entry, *temp;
+	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+	if (fh->func == 0x01) {
+		/* don't call remove here since
+		 * rmi_f01_initialize just get call one time */
+		if (data->f01_container)
+			data->f01_container->fh = NULL;
+		return;
+	}
+
+	list_for_each_entry_safe(entry, temp, &data->rmi_functions.list,
+									list) {
+		if (entry->fh && entry->fd.function_number == fh->func) {
+			if (fh->remove)
+				fh->remove(entry);
+
+			entry->fh = NULL;
+			device_unregister(&entry->dev);
+		}
+	}
+}
+
+static int rmi_driver_process_reset_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;
+	int retval;
+
+	/* Device control (F01) is handled before anything else. */
+
+	if (data->f01_container && data->f01_container->fh &&
+			data->f01_container->fh->reset) {
+		retval = data->f01_container->fh->reset(data->f01_container);
+		if (retval < 0) {
+			dev_err(dev, "F%02x reset handler failed: %d.\n",
+				data->f01_container->fh->func, retval);
+			return retval;
+		}
+	}
+
+	if (list_empty(&data->rmi_functions.list))
+		return 0;
+
+	list_for_each_entry(entry, &data->rmi_functions.list, list) {
+		if (entry->fh && entry->fh->reset) {
+			retval = entry->fh->reset(entry);
+			if (retval < 0) {
+				dev_err(dev, "F%02x reset handler failed: %d\n",
+					entry->fh->func, retval);
+				return retval;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int rmi_driver_process_config_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;
+	int retval;
+
+	/* Device control (F01) is handled before anything else. */
+
+	if (data->f01_container && data->f01_container->fh &&
+			data->f01_container->fh->config) {
+		retval = data->f01_container->fh->config(data->f01_container);
+		if (retval < 0) {
+			dev_err(dev, "F%02x config handler failed: %d.\n",
+					data->f01_container->fh->func, retval);
+			return retval;
+		}
+	}
+
+	if (list_empty(&data->rmi_functions.list))
+		return 0;
+
+	list_for_each_entry(entry, &data->rmi_functions.list, list) {
+		if (entry->fh && entry->fh->config) {
+			retval = entry->fh->config(entry);
+			if (retval < 0) {
+				dev_err(dev, "F%02x config handler failed: %d.\n",
+					entry->fh->func, retval);
+				return retval;
+			}
+		}
+	}
+
+	return 0;
+}
+
+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, "Failed to read irqs, code=%d\n", error);
+		return error;
+	}
+	/* Device control (F01) is handled before anything else. */
+	if (data->f01_container->irq_mask &&
+				data->f01_container->fh->attention) {
+		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);
+	}
+
+	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_dbg(&rmi_dev->dev,
+			 "Not ready to handle interrupts yet!\n");
+		return 0;
+	}
+
+	return process_interrupt_requests(rmi_dev);
+}
+
+static int rmi_driver_reset_handler(struct rmi_device *rmi_dev)
+{
+	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+	int error;
+
+	/* 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 reset yet!\n");
+		return 0;
+	}
+
+	error = rmi_driver_process_reset_requests(rmi_dev);
+	if (error < 0)
+		return error;
+
+
+	error = rmi_driver_process_config_requests(rmi_dev);
+	if (error < 0)
+		return error;
+
+	if (data->irq_stored) {
+		error = rmi_driver_irq_restore(rmi_dev);
+		if (error < 0)
+			return error;
+	}
+
+	return 0;
+}
+
+
+
+/*
+ * 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);
+
+	u8 *irq_mask = kcalloc(data->num_of_irq_regs, sizeof(u8), 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 interrupts */
+		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 interrupt 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_generic(struct rmi_device *rmi_dev,
+				     struct pdt_entry *pdt_ptr,
+				     int *current_irq_count,
+				     u16 page_start)
+{
+	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+	struct rmi_function_container *fc = NULL;
+	int retval = 0;
+	struct device *dev = &rmi_dev->dev;
+	struct rmi_device_platform_data *pdata;
+
+	pdata = to_rmi_platform_data(rmi_dev);
+
+	dev_dbg(dev, "Initializing F%02X for %s.\n", pdt_ptr->function_number,
+		pdata->sensor_name);
+
+	fc = kzalloc(sizeof(struct rmi_function_container),
+			GFP_KERNEL);
+	if (!fc) {
+		dev_err(dev, "Failed to allocate container for F%02X.\n",
+			pdt_ptr->function_number);
+		retval = -ENOMEM;
+		goto error_free_data;
+	}
+
+	copy_pdt_entry_to_fd(pdt_ptr, &fc->fd, page_start);
+
+	fc->rmi_dev = rmi_dev;
+	fc->num_of_irqs = pdt_ptr->interrupt_source_count;
+	fc->irq_pos = *current_irq_count;
+	*current_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_ptr->function_number);
+		goto error_free_data;
+	}
+
+	INIT_LIST_HEAD(&fc->list);
+	list_add_tail(&fc->list, &data->rmi_functions.list);
+	return 0;
+
+error_free_data:
+	kfree(fc);
+	return retval;
+}
+
+/*
+ * F01 was once handled very differently from all other functions.  It is
+ * now only slightly special, and as the driver is refined we expect this
+ * function to go away.
+ */
+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;
+	union f01_device_status device_status;
+	int retval = 0;
+	struct device *dev = &rmi_dev->dev;
+	struct rmi_function_handler *fh =
+		rmi_get_function_handler(0x01);
+	struct rmi_device_platform_data *pdata;
+
+	pdata = to_rmi_platform_data(rmi_dev);
+	data = rmi_get_driverdata(rmi_dev);
+
+	retval = rmi_read(rmi_dev, pdt_ptr->data_base_addr,
+			  device_status.regs);
+	if (retval) {
+		dev_err(dev, "Failed to read device status.\n");
+		return retval;
+	}
+
+	dev_dbg(dev, "Initializing F01 for %s.\n", pdata->sensor_name);
+	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);
+	fc->dev.release = no_op;
+
+	dev_dbg(dev, "Register F01.\n");
+	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;
+	data->f01_bootloader_mode = device_status.flash_prog;
+	if (device_status.flash_prog)
+		dev_warn(dev, "WARNING: RMI4 device is in bootloader mode!\n");
+
+	INIT_LIST_HEAD(&fc->list);
+
+	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;
+	bool has_f01 = false;
+#ifdef	CONFIG_RMI4_FWLIB
+	bool has_f34 = false;
+	struct pdt_entry f34_pdt, f01_pdt;
+#endif
+	int i;
+	int retval;
+	struct rmi_device_platform_data *pdata;
+
+	dev_dbg(dev, "Initial reset.\n");
+	pdata = to_rmi_platform_data(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;
+
+		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, 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(pdata->reset_delay_ms);
+#ifndef CONFIG_RMI4_FWLIB
+				done = true;
+#else
+				memcpy(&f01_pdt, &pdt_entry, sizeof(pdt_entry));
+#endif
+				has_f01 = true;
+				break;
+			}
+#ifdef	CONFIG_RMI4_FWLIB
+			else if (pdt_entry.function_number == 0x34) {
+				memcpy(&f34_pdt, &pdt_entry, sizeof(pdt_entry));
+				has_f34 = true;
+			}
+#endif
+		}
+	}
+
+	if (!has_f01) {
+		dev_warn(dev, "WARNING: Failed to find F01 for initial reset.\n");
+		return -ENODEV;
+	}
+
+#ifdef CONFIG_RMI4_FWLIB
+	if (has_f34)
+		rmi4_fw_update(rmi_dev, &f01_pdt, &f34_pdt);
+	else
+		dev_warn(dev, "WARNING: No F34, firmware update will not be done.\n");
+#endif
+
+	return 0;
+}
+
+static int rmi_scan_pdt(struct rmi_device *rmi_dev)
+{
+	struct rmi_driver_data *data;
+	struct pdt_entry pdt_entry;
+	int page;
+	struct device *dev = &rmi_dev->dev;
+	int irq_count = 0;
+	bool done = false;
+	int i;
+	int retval;
+
+	dev_dbg(dev, "Scanning PDT...\n");
+
+	data = rmi_get_driverdata(rmi_dev);
+	mutex_lock(&data->pdt_mutex);
+
+	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;
+
+			if (pdt_entry.function_number == 0x01)
+				retval = rmi_driver_fn_01_specific(rmi_dev,
+						&pdt_entry, &irq_count,
+						page_start);
+			else
+				retval = rmi_driver_fn_generic(rmi_dev,
+						&pdt_entry, &irq_count,
+						page_start);
+
+			if (retval)
+				goto error_exit;
+		}
+		done = done || data->f01_bootloader_mode;
+	}
+	data->irq_count = irq_count;
+	data->num_of_irq_regs = (irq_count + 7) / 8;
+	dev_dbg(dev, "%s: Done with PDT scan.\n", __func__);
+	retval = 0;
+
+error_exit:
+	mutex_unlock(&data->pdt_mutex);
+	return retval;
+}
+
+static int rmi_driver_probe(struct rmi_device *rmi_dev)
+{
+	struct rmi_driver_data *data = NULL;
+	struct rmi_function_container *fc;
+	struct rmi_device_platform_data *pdata;
+	int retval = 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;
+	}
+	INIT_LIST_HEAD(&data->rmi_functions.list);
+	rmi_set_driverdata(rmi_dev, data);
+	mutex_init(&data->pdt_mutex);
+
+	if (!pdata->reset_delay_ms)
+		pdata->reset_delay_ms = DEFAULT_RESET_DELAY_MS;
+	retval = do_initial_reset(rmi_dev);
+	if (retval)
+		dev_warn(dev, "RMI initial reset failed! Soldiering on.\n");
+
+
+	retval = rmi_scan_pdt(rmi_dev);
+	if (retval) {
+		dev_err(dev, "PDT scan for %s failed with code %d.\n",
+			pdata->sensor_name, retval);
+		goto err_free_data;
+	}
+
+	if (!data->f01_container) {
+		dev_err(dev, "missing F01 container!\n");
+		retval = -EINVAL;
+		goto err_free_data;
+	}
+
+	data->f01_container->irq_mask = kcalloc(data->num_of_irq_regs,
+			sizeof(u8), GFP_KERNEL);
+	if (!data->f01_container->irq_mask) {
+		dev_err(dev, "Failed to allocate F01 IRQ mask.\n");
+		retval = -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);
+
+	retval = rmi_driver_f01_init(rmi_dev);
+	if (retval < 0) {
+		dev_err(dev, "Failed to initialize F01.\n");
+		goto err_free_data;
+	}
+
+	retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION,
+			 data->pdt_props.regs);
+	if (retval < 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++) {
+		retval = device_create_file(dev, &attrs[attr_count]);
+		if (retval < 0) {
+			dev_err(dev, "%s: Failed to create sysfs file %s.\n",
+				__func__, attrs[attr_count].attr.name);
+			goto err_free_data;
+		}
+	}
+	if (data->pdt_props.has_bsr) {
+		retval = device_create_file(dev, &bsr_attribute);
+		if (retval < 0) {
+			dev_err(dev, "%s: Failed to create sysfs file bsr.\n",
+				__func__);
+			goto err_free_data;
+		}
+	}
+
+	mutex_init(&data->irq_mutex);
+	data->current_irq_mask = kcalloc(data->num_of_irq_regs, sizeof(u8),
+					 GFP_KERNEL);
+	if (!data->current_irq_mask) {
+		dev_err(dev, "Failed to allocate current_irq_mask.\n");
+		retval = -ENOMEM;
+		goto err_free_data;
+	}
+	retval = rmi_read_block(rmi_dev,
+				data->f01_container->fd.control_base_addr+1,
+				data->current_irq_mask, data->num_of_irq_regs);
+	if (retval < 0) {
+		dev_err(dev, "%s: Failed to read current IRQ mask.\n",
+			__func__);
+		goto err_free_data;
+	}
+	data->irq_mask_store = kcalloc(data->num_of_irq_regs, sizeof(u8),
+				       GFP_KERNEL);
+	if (!data->irq_mask_store) {
+		dev_err(dev, "Failed to allocate mask store.\n");
+		retval = -ENOMEM;
+		goto err_free_data;
+	}
+
+#ifdef	CONFIG_PM
+	data->pm_data = pdata->pm_data;
+	data->pre_suspend = pdata->pre_suspend;
+	data->post_suspend = pdata->post_suspend;
+	data->pre_resume = pdata->pre_resume;
+	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;
+
+#ifdef CONFIG_RMI4_DEBUG
+	retval = setup_debugfs(rmi_dev);
+	if (retval < 0)
+		dev_warn(&fc->dev, "Failed to setup debugfs. Code: %d.\n",
+			 retval);
+#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]);
+	if (data->pdt_props.has_bsr)
+		device_remove_file(dev, &bsr_attribute);
+	if (data->f01_container)
+		kfree(data->f01_container->irq_mask);
+	kfree(data->irq_mask_store);
+	kfree(data->current_irq_mask);
+	kfree(data);
+	rmi_set_driverdata(rmi_dev, NULL);
+	return retval;
+}
+
+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;
+
+	retval = rmi_dev->phys->enable_device(rmi_dev->phys);
+	if (retval)
+		return retval;
+
+	data->enabled = true;
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int standard_suspend(struct rmi_device *rmi_dev)
+{
+	struct rmi_driver_data *data;
+	struct rmi_function_container *entry;
+	int retval = 0;
+
+	data = rmi_get_driverdata(rmi_dev);
+
+	mutex_lock(&data->suspend_mutex);
+	if (data->suspended)
+		goto exit;
+
+#if !defined(CONFIG_HAS_EARLYSUSPEND) || \
+			defined(CONFIG_RMI4_SPECIAL_EARLYSUSPEND)
+	if (data->pre_suspend) {
+		retval = data->pre_suspend(data->pm_data);
+		if (retval)
+			goto exit;
+	}
+#endif  /* !CONFIG_HAS_EARLYSUSPEND */
+
+	disable_sensor(rmi_dev);
+
+	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;
+
+	if (data->post_suspend)
+		retval = data->post_suspend(data->pm_data);
+
+exit:
+	mutex_unlock(&data->suspend_mutex);
+	return retval;
+}
+
+static int standard_resume(struct rmi_device *rmi_dev)
+{
+	struct rmi_driver_data *data;
+	struct rmi_function_container *entry;
+	int retval = 0;
+	data = rmi_get_driverdata(rmi_dev);
+
+	mutex_lock(&data->suspend_mutex);
+	if (!data->suspended)
+		goto exit;
+
+	if (data->pre_resume) {
+		retval = data->pre_resume(data->pm_data);
+		if (retval)
+			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;
+		}
+
+	retval = enable_sensor(rmi_dev);
+	if (retval)
+		goto exit;
+
+#if !defined(CONFIG_HAS_EARLYSUSPEND) || \
+			defined(CONFIG_RMI4_SPECIAL_EARLYSUSPEND)
+	if (data->post_resume) {
+		retval = data->post_resume(data->pm_data);
+		if (retval)
+			goto exit;
+	}
+#endif
+
+	data->suspended = false;
+exit:
+	mutex_unlock(&data->suspend_mutex);
+	return retval;
+}
+
+#if defined(CONFIG_HAS_EARLYSUSPEND) && \
+			!defined(CONFIG_RMI4_SPECIAL_EARLYSUSPEND)
+static void standard_early_suspend(struct early_suspend *h)
+{
+	struct rmi_device *rmi_dev =
+	    container_of(h, struct rmi_device, early_suspend_handler);
+	struct rmi_driver_data *data;
+	struct rmi_function_container *entry;
+	int retval = 0;
+
+	data = rmi_get_driverdata(rmi_dev);
+
+	mutex_lock(&data->suspend_mutex);
+	if (data->early_suspended)
+		goto exit;
+
+	if (data->pre_suspend) {
+		retval = data->pre_suspend(data->pm_data);
+		if (retval) {
+			dev_err(&rmi_dev->dev, "Presuspend failed with %d.\n",
+				retval);
+			goto exit;
+		}
+	}
+
+	list_for_each_entry(entry, &data->rmi_functions.list, list)
+		if (entry->fh && entry->fh->early_suspend) {
+			retval = entry->fh->early_suspend(entry);
+			if (retval < 0) {
+				dev_err(&rmi_dev->dev, "F%02x early suspend failed with %d.\n",
+					entry->fd.function_number, retval);
+				goto exit;
+			}
+		}
+
+	if (data->f01_container && data->f01_container->fh
+				&& data->f01_container->fh->early_suspend) {
+		retval = data->f01_container->fh->early_suspend(
+				data->f01_container);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "F01 early suspend failed with %d.\n",
+				retval);
+			goto exit;
+		}
+	}
+
+	data->early_suspended = true;
+exit:
+	mutex_unlock(&data->suspend_mutex);
+}
+
+static void standard_late_resume(struct early_suspend *h)
+{
+	struct rmi_device *rmi_dev =
+	    container_of(h, struct rmi_device, early_suspend_handler);
+	struct rmi_driver_data *data;
+	struct rmi_function_container *entry;
+	int retval = 0;
+
+	data = rmi_get_driverdata(rmi_dev);
+
+	mutex_lock(&data->suspend_mutex);
+	if (!data->early_suspended)
+		goto exit;
+
+	if (data->f01_container && data->f01_container->fh
+				&& data->f01_container->fh->late_resume) {
+		retval = data->f01_container->fh->late_resume(
+				data->f01_container);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "F01 late resume failed with %d.\n",
+				retval);
+			goto exit;
+		}
+	}
+
+	list_for_each_entry(entry, &data->rmi_functions.list, list)
+		if (entry->fh && entry->fh->late_resume) {
+			retval = entry->fh->late_resume(entry);
+			if (retval < 0) {
+				dev_err(&rmi_dev->dev, "F%02X late resume failed with %d.\n",
+					entry->fd.function_number, retval);
+				goto exit;
+			}
+		}
+
+	if (data->post_resume) {
+		retval = data->post_resume(data->pm_data);
+		if (retval) {
+			dev_err(&rmi_dev->dev, "Post resume failed with %d.\n",
+				retval);
+			goto exit;
+		}
+	}
+
+	data->early_suspended = false;
+
+exit:
+	mutex_unlock(&data->suspend_mutex);
+}
+#endif /* defined(CONFIG_HAS_EARLYSUSPEND) && !de... */
+
+#ifdef CONFIG_RMI4_SPECIAL_EARLYSUSPEND
+static int rmi_driver_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int rmi_driver_resume(struct device *dev)
+{
+	return 0;
+}
+
+static void rmi_driver_early_suspend(struct early_suspend *h)
+{
+	struct rmi_device *rmi_dev =
+	    container_of(h, struct rmi_device, early_suspend_handler);
+	standard_suspend(rmi_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);
+	standard_resume(rmi_dev);
+}
+#else
+static int rmi_driver_suspend(struct device *dev)
+{
+	struct rmi_device *rmi_dev = to_rmi_device(dev);
+	return standard_suspend(rmi_dev);
+}
+
+static int rmi_driver_resume(struct device *dev)
+{
+	struct rmi_device *rmi_dev = to_rmi_device(dev);
+	return standard_resume(rmi_dev);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void rmi_driver_early_suspend(struct early_suspend *h)
+{
+	return standard_early_suspend(h);
+}
+
+static void rmi_driver_late_resume(struct early_suspend *h)
+{
+	return standard_late_resume(h);
+}
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+#endif /* CONFIG_RMI4_SPECIAL_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;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&rmi_dev->early_suspend_handler);
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+#ifdef	CONFIG_RMI4_DEBUG
+	teardown_debugfs(rmi_dev);
+#endif
+
+	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]);
+	if (data->pdt_props.has_bsr)
+		device_remove_file(&rmi_dev->dev, &bsr_attribute);
+	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",
+#ifdef UNIVERSAL_DEV_PM_OPS
+		.pm = &rmi_driver_pm,
+#endif
+	},
+	.probe = rmi_driver_probe,
+	.irq_handler = rmi_driver_irq_handler,
+	.reset_handler = rmi_driver_reset_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)
+};
+
+/* sysfs show and store fns for driver attributes */
+
+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 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;
+	u32 address;	/* use large addr here so we can catch overflow */
+	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
+
+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("Christopher Heiny <cheiny@synaptics.com");
+MODULE_DESCRIPTION("RMI generic driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
new file mode 100644
index 0000000..0a350ce
--- /dev/null
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _RMI_DRIVER_H
+#define _RMI_DRIVER_H
+
+#define RMI_DRIVER_VERSION "1.4"
+
+#define RMI_PRODUCT_ID_LENGTH    10
+#define RMI_PRODUCT_INFO_LENGTH   2
+#define RMI_DATE_CODE_LENGTH      3
+
+#include <linux/ctype.h>
+/* Sysfs related macros */
+
+/* You must define FUNCTION_DATA and FNUM to use these functions. */
+#define RMI4_SYSFS_DEBUG (defined(CONFIG_RMI4_DEBUG) || defined(CONFIG_ANDROID))
+
+#if defined(FNUM) && defined(FUNCTION_DATA)
+
+#define tricat(x, y, z) tricat_(x, y, z)
+
+#define tricat_(x, y, z) x##y##z
+
+#define show_union_struct_prototype(propname)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(\
+					struct device *dev, \
+					struct device_attribute *attr, \
+					char *buf);\
+\
+static DEVICE_ATTR(propname, RMI_RO_ATTR,\
+		tricat(rmi_fn_, FNUM, _##propname##_show), \
+		rmi_store_error);
+
+#define store_union_struct_prototype(propname)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(\
+					struct device *dev,\
+					struct device_attribute *attr,\
+					const char *buf, size_t count);\
+\
+static DEVICE_ATTR(propname, RMI_WO_ATTR,\
+		rmi_show_error,\
+		tricat(rmi_fn_, FNUM, _##propname##_store));
+
+#define show_store_union_struct_prototype(propname)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(\
+					struct device *dev,\
+					struct device_attribute *attr,\
+					char *buf);\
+\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(\
+					struct device *dev,\
+					struct device_attribute *attr,\
+					const char *buf, size_t count);\
+\
+static DEVICE_ATTR(propname, RMI_RW_ATTR,\
+		tricat(rmi_fn_, FNUM, _##propname##_show),\
+		tricat(rmi_fn_, FNUM, _##propname##_store));
+
+#define simple_show_union_struct(regtype, propname, fmt)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
+				struct device_attribute *attr, char *buf) {\
+	struct rmi_function_container *fc;\
+	struct FUNCTION_DATA *data;\
+\
+	fc = to_rmi_function_container(dev);\
+	data = fc->data;\
+\
+	return snprintf(buf, PAGE_SIZE, fmt,\
+			data->regtype.propname);\
+}
+
+#define simple_show_union_struct2(regtype, reg_group, propname, fmt)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
+				struct device_attribute *attr, char *buf) {\
+	struct rmi_function_container *fc;\
+	struct FUNCTION_DATA *data;\
+\
+	fc = to_rmi_function_container(dev);\
+	data = fc->data;\
+\
+	return snprintf(buf, PAGE_SIZE, fmt,\
+			data->regtype.reg_group->propname);\
+}
+
+#define show_union_struct(regtype, reg_group, propname, fmt)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(\
+					struct device *dev,\
+					struct device_attribute *attr,\
+					char *buf) {\
+	struct rmi_function_container *fc;\
+	struct FUNCTION_DATA *data;\
+	int result;\
+\
+	fc = to_rmi_function_container(dev);\
+	data = fc->data;\
+\
+	mutex_lock(&data->regtype##_mutex);\
+	/* Read current regtype values */\
+	result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
+				(u8 *)data->regtype.reg_group,\
+				sizeof(data->regtype.reg_group->regs));\
+	mutex_unlock(&data->regtype##_mutex);\
+	if (result < 0) {\
+		dev_dbg(dev, "%s : Could not read regtype at 0x%x\\n",\
+					__func__,\
+					data->regtype.reg_group->address);\
+		return result;\
+	} \
+	return snprintf(buf, PAGE_SIZE, fmt,\
+			data->regtype.reg_group->propname);\
+}
+
+#define show_store_union_struct(regtype, reg_group, propname, fmt)\
+show_union_struct(regtype, reg_group, propname, fmt)\
+\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(\
+					struct device *dev,\
+					struct device_attribute *attr,\
+					const char *buf, size_t count) {\
+	int result;\
+	unsigned long val;\
+	unsigned long old_val;\
+	struct rmi_function_container *fc;\
+	struct FUNCTION_DATA *data;\
+\
+	fc = to_rmi_function_container(dev);\
+	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;\
+	/* Check value maybe */\
+\
+	/* Read current regtype values */\
+	mutex_lock(&data->regtype##_mutex);\
+	result =\
+	    rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
+				(u8 *)data->regtype.reg_group,\
+				sizeof(data->regtype.reg_group->regs));\
+\
+	if (result < 0) {\
+		mutex_unlock(&data->regtype##_mutex);\
+		dev_dbg(dev, "%s : Could not read regtype at 0x%x\\n",\
+					 __func__,\
+					data->regtype.reg_group->address);\
+		return result;\
+	} \
+	/* if the current regtype registers are already set as we want them,\
+	 * do nothing to them */\
+	if (data->regtype.reg_group->propname == val) {\
+		mutex_unlock(&data->regtype##_mutex);\
+		return count;\
+	} \
+	/* Write the regtype back to the regtype register */\
+	old_val = data->regtype.reg_group->propname;\
+	data->regtype.reg_group->propname = val;\
+	result =\
+	    rmi_write_block(fc->rmi_dev, data->regtype.reg_group->address,\
+				(u8 *)data->regtype.reg_group,\
+				sizeof(data->regtype.reg_group->regs));\
+	if (result < 0) {\
+		dev_dbg(dev, "%s : Could not write regtype to 0x%x\\n",\
+					__func__,\
+					data->regtype.reg_group->address);\
+		/* revert change to local value if value not written */\
+		data->regtype.reg_group->propname = old_val;\
+		mutex_unlock(&data->regtype##_mutex);\
+		return result;\
+	} \
+	mutex_unlock(&data->regtype##_mutex);\
+	return count;\
+}
+
+
+#define show_repeated_union_struct(regtype, reg_group, propname, fmt)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
+					struct device_attribute *attr,\
+					char *buf) {\
+	struct rmi_function_container *fc;\
+	struct FUNCTION_DATA *data;\
+	int reg_length;\
+	int result, size = 0;\
+	char *temp;\
+	int i;\
+\
+	fc = to_rmi_function_container(dev);\
+	data = fc->data;\
+	mutex_lock(&data->regtype##_mutex);\
+\
+	/* Read current regtype values */\
+	reg_length = data->regtype.reg_group->length;\
+	result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
+			(u8 *) data->regtype.reg_group->regs,\
+			reg_length * sizeof(u8));\
+	mutex_unlock(&data->regtype##_mutex);\
+	if (result < 0) {\
+		dev_dbg(dev, "%s : Could not read regtype at 0x%x\n"\
+					"Data may be outdated.", __func__,\
+					data->regtype.reg_group->address);\
+	} \
+	temp = buf;\
+	for (i = 0; i < reg_length; i++) {\
+		result = snprintf(temp, PAGE_SIZE - size, fmt " ",\
+				data->regtype.reg_group->regs[i].propname);\
+		if (result < 0) {\
+			dev_err(dev, "%s : Could not write output.", __func__);\
+			return result;\
+		} \
+		size += result;\
+		temp += result;\
+	} \
+	result = snprintf(temp, PAGE_SIZE - size, "\n");\
+	if (result < 0) {\
+			dev_err(dev, "%s : Could not write output.", __func__);\
+			return result;\
+	} \
+	return size + result;\
+}
+
+#define show_store_repeated_union_struct(regtype, reg_group, propname, fmt)\
+show_repeated_union_struct(regtype, reg_group, propname, fmt)\
+\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(struct device *dev,\
+				   struct device_attribute *attr,\
+				   const char *buf, size_t count) {\
+	struct rmi_function_container *fc;\
+	struct FUNCTION_DATA *data;\
+	int reg_length;\
+	int result;\
+	const char *temp;\
+	int i;\
+	unsigned int newval;\
+\
+	fc = to_rmi_function_container(dev);\
+	data = fc->data;\
+	mutex_lock(&data->regtype##_mutex);\
+\
+	/* Read current regtype values */\
+\
+	reg_length = data->regtype.reg_group->length;\
+	result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
+			(u8 *) data->regtype.reg_group->regs,\
+			reg_length * sizeof(u8));\
+\
+	if (result < 0) {\
+		dev_dbg(dev, "%s: Could not read regtype at %#06x. "\
+					"Data may be outdated.", __func__,\
+					data->regtype.reg_group->address);\
+	} \
+	\
+	/* parse input */\
+	temp = buf;\
+	for (i = 0; i < reg_length; i++) {\
+		if (sscanf(temp, fmt, &newval) == 1) {\
+			data->regtype.reg_group->regs[i].propname = newval;\
+		} else {\
+			/* If we don't read a value for each position, abort, \
+			 * restore previous values locally by rereading */\
+			result = rmi_read_block(fc->rmi_dev, \
+					data->regtype.reg_group->address,\
+					(u8 *) data->regtype.reg_group->regs,\
+					reg_length * sizeof(u8));\
+\
+			if (result < 0) {\
+				dev_dbg(dev, "%s: Couldn't read regtype at "\
+					"%#06x. Local data may be inaccurate", \
+					__func__,\
+					data->regtype.reg_group->address);\
+			} \
+			return -EINVAL;\
+		} \
+		/* move to next number */\
+		while (*temp != 0) {\
+			temp++;\
+			if (isspace(*(temp - 1)) && !isspace(*temp))\
+				break;\
+		} \
+	} \
+	result = rmi_write_block(fc->rmi_dev, data->regtype.reg_group->address,\
+			(u8 *) data->regtype.reg_group->regs,\
+			reg_length * sizeof(u8));\
+	mutex_unlock(&data->regtype##_mutex);\
+	if (result < 0) {\
+		dev_dbg(dev, "%s: Could not write new values to %#06x\n", \
+				__func__, data->regtype.reg_group->address);\
+		return result;\
+	} \
+	return count;\
+}
+
+/* Create templates for given types */
+#define simple_show_union_struct_unsigned(regtype, propname)\
+simple_show_union_struct(regtype, propname, "%u\n")
+
+#define simple_show_union_struct_unsigned2(regtype, reg_group, propname)\
+simple_show_union_struct2(regtype, reg_group, propname, "%u\n")
+
+#define show_union_struct_unsigned(regtype, reg_group, propname)\
+show_union_struct(regtype, reg_group, propname, "%u\n")
+
+#define show_store_union_struct_unsigned(regtype, reg_group, propname)\
+show_store_union_struct(regtype, reg_group, propname, "%u\n")
+
+#define show_repeated_union_struct_unsigned(regtype, reg_group, propname)\
+show_repeated_union_struct(regtype, reg_group, propname, "%u")
+
+#define show_store_repeated_union_struct_unsigned(regtype, reg_group, propname)\
+show_store_repeated_union_struct(regtype, reg_group, propname, "%u")
+
+/* Remove access to raw format string versions */
+/*#undef simple_show_union_struct
+#undef show_union_struct_unsigned
+#undef show_store_union_struct
+#undef show_repeated_union_struct
+#undef show_store_repeated_union_struct*/
+
+#endif
+
+#define GROUP(_attrs) { \
+	.attrs = _attrs,  \
+}
+
+#define attrify(nm) (&dev_attr_##nm.attr)
+
+#define PDT_PROPERTIES_LOCATION 0x00EF
+#define BSR_LOCATION 0x00FE
+
+union pdt_properties {
+	struct {
+		u8 reserved_1:6;
+		u8 has_bsr:1;
+		u8 reserved_2:1;
+	};
+	u8 regs[1];
+};
+
+struct rmi_driver_data {
+	struct rmi_function_container rmi_functions;
+
+	struct rmi_function_container *f01_container;
+	bool f01_bootloader_mode;
+
+	int num_of_irq_regs;
+	int irq_count;
+	u8 *current_irq_mask;
+	u8 *irq_mask_store;
+	bool irq_stored;
+	struct mutex irq_mutex;
+	struct mutex pdt_mutex;
+
+	union pdt_properties pdt_props;
+	unsigned char bsr;
+	bool enabled;
+
+#ifdef CONFIG_PM
+	bool suspended;
+#if defined(CONFIG_HAS_EARLYSUSPEND) && \
+			!defined(CONFIG_RMI4_SPECIAL_EARLYSUSPEND)
+	bool early_suspended;
+#endif
+	struct mutex suspend_mutex;
+
+	void *pm_data;
+	int (*pre_suspend) (const void *pm_data);
+	int (*post_suspend) (const void *pm_data);
+	int (*pre_resume) (const void *pm_data);
+	int (*post_resume) (const void *pm_data);
+#endif
+
+#ifdef CONFIG_RMI4_DEBUG
+#ifdef CONFIG_RMI4_SPI
+	struct dentry *debugfs_delay;
+#endif
+	struct dentry *debugfs_phys;
+	struct dentry *debugfs_reg_ctl;
+	struct dentry *debugfs_reg;
+	u16 reg_debug_addr;
+	u8 reg_debug_size;
+#endif
+
+	void *data;
+};
+
+int rmi_driver_f01_init(struct rmi_device *rmi_dev);
+
+
+#define PDT_START_SCAN_LOCATION 0x00e9
+#define PDT_END_SCAN_LOCATION	0x0005
+#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
+
+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;
+};
+
+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;
+}
+
+#ifdef	CONFIG_RMI4_FWLIB
+extern void rmi4_fw_update(struct rmi_device *rmi_dev,
+		struct pdt_entry *f01_pdt, struct pdt_entry *f34_pdt);
+#endif
+
+#endif

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 3/17] input: RMI4 physical layer drivers for I2C and SPI
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
  2012-08-17 22:17 ` [RFC PATCH 1/17] input: RMI4 public header file and documentation Christopher Heiny
  2012-08-17 22:17 ` [RFC PATCH 2/17] input: RMI4 core bus and sensor drivers Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-23 13:21   ` Linus Walleij
  2012-08-17 22:17 ` [RFC PATCH 4/17] input: RMI4 configs and makefiles Christopher Heiny
                   ` (15 subsequent siblings)
  18 siblings, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_i2c.c |  452 +++++++++++++++++++++
 drivers/input/rmi4/rmi_spi.c |  911 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1363 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c
new file mode 100644
index 0000000..1da8d96
--- /dev/null
+++ b/drivers/input/rmi4/rmi_i2c.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define COMMS_DEBUG 0
+
+#define IRQ_DEBUG 0
+
+#if COMMS_DEBUG || IRQ_DEBUG
+#define DEBUG
+#endif
+
+#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>
+#include "rmi_driver.h"
+
+#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(pdata->attn_gpio));
+#endif
+	if (gpio_get_value(pdata->attn_gpio) == pdata->attn_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
+	char debug_buf[len*3 + 1];
+	int i, n;
+#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
+	n = 0;
+	for (i = 0; i < len; i++)
+		n = snprintf(debug_buf+n, 4, "%02x ", buf[i]);
+	dev_dbg(&client->dev, "RMI4 I2C writes %d bytes at %#06x: %s\n",
+		len, addr, debug_buf);
+#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++;
+	else
+		retval--; /* don't count the address byte */
+
+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
+	char debug_buf[len*3 + 1];
+	char *temp = debug_buf;
+	int i, n;
+#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 {
+		n = 0;
+		for (i = 0; i < len; i++) {
+			n = sprintf(temp, " %02x", buf[i]);
+			temp += n;
+		}
+		dev_dbg(&client->dev, "RMI4 I2C read %d bytes at %#06x:%s\n",
+			len, addr, debug_buf);
+	}
+#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;
+	}
+	dev_info(&client->dev, "Probing %s at %#02x (IRQ %d).\n",
+		pdata->sensor_name ? pdata->sensor_name : "-no name-",
+		client->addr, pdata->attn_gpio);
+
+	if (pdata->gpio_config) {
+		dev_info(&client->dev, "Configuring GPIOs.\n");
+		error = pdata->gpio_config(pdata->gpio_data, true);
+		if (error < 0) {
+			dev_err(&client->dev, "Failed to configure GPIOs, code: %d.\n",
+				error);
+			return error;
+		}
+		dev_info(&client->dev, "Done with GPIO configuration.\n");
+	}
+
+	error = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
+	if (!error) {
+		dev_err(&client->dev, "i2c_check_functionality error %d.\n",
+			error);
+		return error;
+	}
+
+	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->attn_gpio);
+	if (pdata->level_triggered) {
+		data->irq_flags = IRQF_ONESHOT |
+			((pdata->attn_polarity == RMI_ATTN_ACTIVE_HIGH) ?
+			IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW);
+	} else {
+		data->irq_flags =
+			(pdata->attn_polarity == RMI_ATTN_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;
+	}
+
+	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_gpio;
+	}
+	i2c_set_clientdata(client, rmi_phys);
+
+	if (pdata->attn_gpio > 0) {
+		error = acquire_attn_irq(data);
+		if (error < 0) {
+			dev_err(&client->dev,
+				"request_threaded_irq failed %d\n",
+				pdata->attn_gpio);
+			goto err_unregister;
+		}
+	}
+
+#if defined(CONFIG_RMI4_DEV)
+	error = gpio_export(pdata->attn_gpio, false);
+	if (error) {
+		dev_warn(&client->dev,
+			 "WARNING: Failed to export ATTN gpio!\n");
+		error = 0;
+	} else {
+		error = gpio_export_link(&(rmi_phys->rmi_dev->dev), "attn",
+					pdata->attn_gpio);
+		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 ATTN GPIO %d.", __func__,
+				pdata->attn_gpio);
+		}
+	}
+#endif /* CONFIG_RMI4_DEV */
+
+	dev_info(&client->dev, "registered rmi i2c driver at %#04x.\n",
+			client->addr);
+	return 0;
+
+err_unregister:
+	rmi_unregister_phys_device(rmi_phys);
+err_gpio:
+	if (pdata->gpio_config)
+		pdata->gpio_config(pdata->gpio_data, false);
+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;
+
+	disable_device(phys);
+	rmi_unregister_phys_device(phys);
+	kfree(phys->data);
+	kfree(phys);
+
+	if (pd->gpio_config)
+		pd->gpio_config(&pd->gpio_data, 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_init(rmi_i2c_init);
+module_exit(rmi_i2c_exit);
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
+MODULE_DESCRIPTION("RMI I2C driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
diff --git a/drivers/input/rmi4/rmi_spi.c b/drivers/input/rmi4/rmi_spi.c
new file mode 100644
index 0000000..4f446af
--- /dev/null
+++ b/drivers/input/rmi4/rmi_spi.c
@@ -0,0 +1,911 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/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 FF_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)
+
+#define DEFAULT_POLL_INTERVAL_MS	13
+
+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;
+
+	/* Following are used when polling. */
+	struct hrtimer poll_timer;
+	struct work_struct poll_work;
+	int poll_interval;
+
+};
+
+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(pdata->attn_gpio) ==
+		      pdata->attn_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(pdata->attn_gpio) == pdata->attn_polarity) {
+		phys->info.attn_count++;
+		if (driver && driver->irq_handler)
+			driver->irq_handler(rmi_dev, irq);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void spi_poll_work(struct work_struct *work)
+{
+	struct rmi_spi_data *data =
+			container_of(work, struct rmi_spi_data, poll_work);
+	struct rmi_device *rmi_dev = data->phys->rmi_dev;
+	struct rmi_driver *driver = rmi_dev->driver;
+
+	if (driver && driver->irq_handler)
+		driver->irq_handler(rmi_dev, 0);
+}
+
+/* This is the timer function for polling - it simply has to schedule work
+ * and restart the timer. */
+static enum hrtimer_restart spi_poll_timer(struct hrtimer *timer)
+{
+	struct rmi_spi_data *data =
+			container_of(timer, struct rmi_spi_data, poll_timer);
+
+	if (!work_pending(&data->poll_work))
+		schedule_work(&data->poll_work);
+	hrtimer_start(&data->poll_timer, ktime_set(0, data->poll_interval),
+		      HRTIMER_MODE_REL);
+	return HRTIMER_NORESTART;
+}
+
+
+
+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 FF_DEBUG
+	bool bad_data = true;
+#endif
+#if COMMS_DEBUG || FF_DEBUG
+	int i;
+#endif
+
+	if (v2_data->split_read_pending) {
+		block_delay =
+		    n_rx > 0 ? pdata->spi_data.split_read_block_delay_us : 0;
+		byte_delay =
+		    n_rx > 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].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;
+	}
+	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].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_dbg(&client->dev, "SPI sends %d bytes: ", n_tx);
+		for (i = 0; i < n_tx; i++)
+			pr_info("%02X ", txbuf[i]);
+		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, code %d.\n",
+				status);
+			/* 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. code %d.\n",
+				status);
+			/* 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 {
+		if (n_tx)
+			phys->info.tx_errs++;
+		if (n_rx)
+			phys->info.rx_errs++;
+		dev_err(phys->dev, "spi_sync failed with error code %d.",
+		       status);
+		goto error_exit;
+	}
+
+#if COMMS_DEBUG
+	if (n_rx) {
+		dev_dbg(&client->dev, "SPI received %d bytes: ", n_rx);
+		for (i = 0; i < n_rx; i++)
+			pr_info("%02X ", rxbuf[i]);
+		pr_info("\n");
+	}
+#endif
+#if FF_DEBUG
+	if (n_rx) {
+		for (i = 0; i < n_rx; i++) {
+			if (rxbuf[i] != 0xFF) {
+				bad_data = false;
+				break;
+			}
+		}
+		if (bad_data) {
+			phys->info.rx_errs++;
+			dev_err(phys->dev, "BAD READ %lu out of %lu.\n",
+				phys->info.rx_errs, phys->info.rx_count);
+		}
+	}
+#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;
+
+	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);
+
+	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;
+
+	txbuf[0] = (addr >> 8) & ~RMI_V1_READ_FLAG;
+	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);
+
+	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;
+
+	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;
+
+	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;
+
+	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);
+	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);
+
+	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;
+
+	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;
+
+	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;
+	struct rmi_phys_device *rmi_phys = data->phys;
+
+	retval = request_threaded_irq(data->irq, rmi_spi_hard_irq,
+				rmi_spi_irq_thread, data->irq_flags,
+				dev_name(rmi_phys->dev), rmi_phys);
+	if (retval < 0) {
+		dev_err(&(rmi_phys->rmi_dev->dev), "request_threaded_irq "
+			"failed, code: %d.\n", retval);
+	}
+	return retval;
+}
+
+static int setup_attn(struct rmi_spi_data *data)
+{
+	int retval;
+	struct rmi_phys_device *rmi_phys = data->phys;
+	struct rmi_device_platform_data *pdata = rmi_phys->dev->platform_data;
+
+	retval = acquire_attn_irq(data);
+	if (retval < 0)
+		return retval;
+
+#if defined(CONFIG_RMI4_DEV)
+	retval = gpio_export(pdata->attn_gpio, false);
+	if (retval) {
+		dev_warn(&(rmi_phys->rmi_dev->dev),
+			 "WARNING: Failed to export ATTN gpio!\n");
+		retval = 0;
+	} else {
+		retval = gpio_export_link(&(rmi_phys->rmi_dev->dev), "attn",
+					pdata->attn_gpio);
+		if (retval) {
+			dev_warn(&(rmi_phys->rmi_dev->dev), "WARNING: "
+				"Failed to symlink ATTN gpio!\n");
+			retval = 0;
+		} else {
+			dev_info(&(rmi_phys->rmi_dev->dev),
+				"%s: Exported GPIO %d.", __func__,
+				pdata->attn_gpio);
+		}
+	}
+#endif /* CONFIG_RMI4_DEV */
+
+	return retval;
+}
+
+static int setup_polling(struct rmi_spi_data *data)
+{
+	INIT_WORK(&data->poll_work, spi_poll_work);
+	hrtimer_init(&data->poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	data->poll_timer.function = spi_poll_timer;
+	hrtimer_start(&data->poll_timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+
+	return 0;
+}
+
+static int enable_device(struct rmi_phys_device *phys)
+{
+	int retval = 0;
+
+	struct rmi_spi_data *data = phys->data;
+
+	if (data->enabled) {
+		dev_dbg(phys->dev, "Physical device already enabled.\n");
+		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_spi_data *data = phys->data;
+
+	if (!data->enabled) {
+		dev_warn(phys->dev, "Physical device already disabled.\n");
+		return;
+	}
+	disable_irq(data->irq);
+	free_irq(data->irq, data->phys);
+
+	dev_dbg(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;
+
+	/* 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;
+	}
+	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 retval;
+
+	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;
+	retval = spi_setup(spi);
+	if (retval < 0) {
+		dev_err(&spi->dev, "spi_setup failed!\n");
+		return retval;
+	}
+
+	if (pdata->gpio_config) {
+		retval = pdata->gpio_config(pdata->gpio_data, true);
+		if (retval < 0) {
+			dev_err(&spi->dev, "Failed to setup GPIOs, code: %d.\n",
+				retval);
+			return retval;
+		}
+	}
+
+	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) {
+		retval = -ENOMEM;
+		goto err_phys;
+	}
+	data->enabled = true;	/* We plan to come up enabled. */
+	data->irq = gpio_to_irq(pdata->attn_gpio);
+	if (pdata->level_triggered) {
+		data->irq_flags = IRQF_ONESHOT |
+			((pdata->attn_polarity == RMI_ATTN_ACTIVE_HIGH) ?
+			IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW);
+	} else {
+		data->irq_flags =
+			(pdata->attn_polarity == RMI_ATTN_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);
+
+	dev_set_drvdata(&spi->dev, rmi_phys);
+
+	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;
+
+	retval = rmi_spi_check_device(rmi_phys);
+	if (retval < 0)
+		goto err_gpio;
+
+	/* check if this is an SPI v2 device */
+	retval = rmi_spi_v1_read_block(rmi_phys, RMI_PROTOCOL_VERSION_ADDRESS,
+				      buf, 2);
+	if (retval < 0) {
+		dev_err(&spi->dev, "failed to get SPI version number!\n");
+		goto err_gpio;
+	}
+	dev_dbg(&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->attn_gpio > 0) {
+			init_completion(&data->irq_comp);
+			rmi_phys->read_block = rmi_spi_v2_split_read_block;
+		} else {
+			dev_warn(&spi->dev, "WARNING: SPI V2 detected, but no "
+				"attention GPIO was specified. This is unlikely"
+				" to work well.\n");
+			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]);
+		retval = -ENODEV;
+		goto err_gpio;
+	}
+
+	retval = rmi_register_phys_device(rmi_phys);
+	if (retval) {
+		dev_err(&spi->dev, "failed to register physical driver\n");
+		goto err_gpio;
+	}
+
+	if (pdata->attn_gpio > 0) {
+		retval = setup_attn(data);
+		if (retval < 0)
+			goto err_unregister;
+	} else {
+		retval = setup_polling(data);
+		if (retval < 0)
+			goto err_unregister;
+	}
+
+	dev_info(&spi->dev, "registered RMI SPI driver\n");
+	return 0;
+
+err_unregister:
+	rmi_unregister_phys_device(rmi_phys);
+err_gpio:
+	if (pdata->gpio_config)
+		pdata->gpio_config(pdata->gpio_data, false);
+err_data:
+	kfree(data);
+err_phys:
+	kfree(rmi_phys);
+	return retval;
+}
+
+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;
+
+	disable_device(phys);
+	rmi_unregister_phys_device(phys);
+	kfree(phys->data);
+	kfree(phys);
+
+	if (pd->gpio_config)
+		pd->gpio_config(pdata->gpio_data, 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",
+	},
+	.id_table	= rmi_id,
+	.probe		= rmi_spi_probe,
+	.remove		= __devexit_p(rmi_spi_remove),
+};
+
+static int __init rmi_spi_init(void)
+{
+	return spi_register_driver(&rmi_spi_driver);
+}
+
+static void __exit rmi_spi_exit(void)
+{
+	spi_unregister_driver(&rmi_spi_driver);
+}
+
+module_init(rmi_spi_init);
+module_exit(rmi_spi_exit);
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
+MODULE_DESCRIPTION("RMI SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 4/17] input: RMI4 configs and makefiles
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (2 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 3/17] input: RMI4 physical layer drivers for I2C and SPI Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-27 18:39   ` Linus Walleij
  2012-08-17 22:17 ` [RFC PATCH 5/17] input: rmidev character driver for RMI4 sensors Christopher Heiny
                   ` (14 subsequent siblings)
  18 siblings, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/Kconfig       |    2 +
 drivers/input/Makefile      |    4 +-
 drivers/input/rmi4/Kconfig  |  262 +++++++++++++++++++++++++++++++++++++++++++
 drivers/input/rmi4/Makefile |   18 +++
 4 files changed, 284 insertions(+), 2 deletions(-)

diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 1903c0f..0cdd8a5 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -185,6 +185,8 @@ source "drivers/input/touchscreen/Kconfig"
 
 source "drivers/input/misc/Kconfig"
 
+source "drivers/input/rmi4/Kconfig"
+
 endif
 
 menu "Hardware I/O ports"
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 09614ce..cfb383e 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -23,6 +23,6 @@ obj-$(CONFIG_INPUT_TABLET)	+= tablet/
 obj-$(CONFIG_INPUT_TOUCHSCREEN)	+= touchscreen/
 obj-$(CONFIG_INPUT_MISC)	+= misc/
 
-obj-$(CONFIG_INPUT_APMPOWER)	+= apm-power.o
+obj-$(CONFIG_INPUT_RMI4) += rmi4/
 
-obj-$(CONFIG_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
+obj-$(CONFIG_INPUT_APMPOWER)	+= apm-power.o
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
new file mode 100644
index 0000000..3cedf8b
--- /dev/null
+++ b/drivers/input/rmi4/Kconfig
@@ -0,0 +1,262 @@
+#
+# RMI4 configuration
+#
+config RMI4_BUS
+	bool "Synaptics RMI4 bus support"
+	help
+	  Say Y here if you want to support the Synaptics RMI4 bus.  This is
+	  required for all RMI4 device support.
+
+	  If unsure, say Y.
+
+	  This feature is not currently available as a loadable module.
+
+config RMI4_DEBUG
+	bool "RMI4 Debugging"
+	depends on RMI4_BUS
+	help
+	  Say Y here to enable debug feature in the RMI4 driver.
+
+	  Note that the RMI4 driver debug features can generate a lot of
+	  output (potentially clogging up your dmesg output) and generally
+	  slow down driver operation.  It's recommended to enable them only
+	  if you are actively developing/debugging RMI4 features.
+
+	  If unsure, say N.
+
+config RMI4_SPECIAL_EARLYSUSPEND
+	bool "Synaptics RMI4 special early suspend"
+	depends on RMI4_BUS && HAS_EARLYSUSPEND
+	help
+	  Say Y here to enable special suspend handling in the RMI4 driver.
+
+	  This feature causes the RMI4 sensors to be placed in full suspend
+	  state during the early suspend process.  The standard early suspend
+	  and late resume handlers will be replaced by the suspend and resume
+	  handling routines.  The result will be a small savings in power
+	  consumption, at the expense of decreased UI responsiveness at late
+	  resume.  Enable this feature only if you have extremely limited
+	  power resources in your system.
+
+	  If unsure, say N.
+
+config RMI4_FWLIB
+	bool "RMI4 Firmware Update"
+	depends on RMI4_BUS
+	help
+	  Say Y here to enable in-kernel firmware update capability.
+
+	  The RMI4 driver will check for firmware updates during the device
+	  initialization process, and apply the updates if appropriate.
+
+	  This feature is not available as a loadable module.
+
+
+config RMI4_I2C
+	bool "RMI4 I2C Support"
+	depends on RMI4_BUS && I2C
+	help
+	  Say Y here if you want to support RMI4 devices connected to an I2C
+	  bus.
+
+	  If unsure, say Y.
+
+	  This feature is not currently available as a loadable module.
+
+config RMI4_SPI
+	bool "RMI4 SPI Support"
+	depends on RMI4_BUS && SPI
+	help
+	  Say Y here if you want to support RMI4 devices connected to an SPI
+	  bus.
+
+	  If unsure, say Y.
+
+	  This feature is not currently available as a loadable module.
+
+config RMI4_SMB
+	bool "RMI4 SMB Support"
+	depends on RMI4_BUS
+	help
+	  Say Y here if you want to support RMI4 devices connected to an SMB
+	  bus.
+
+	  If unsure, say Y.
+
+	  This feature is not currently available as a loadable module.
+
+config RMI4_GENERIC
+	bool "RMI4 Generic driver"
+	depends on RMI4_BUS
+	help
+	  Say Y here if you want to support generic RMI4 devices.
+
+	  This is pretty much required if you want to do anything useful with
+	  your RMI device.
+
+	  This feature is not currently available as a loadable module.
+
+
+config RMI4_F1A
+	tristate "RMI4 Function 1A (capacitive button sensor)"
+	depends on RMI4_BUS && RMI4_GENERIC
+	help
+	  Say Y here if you want to add support for RMI4 function 1A.
+
+	  Function 1A provides self testing for touchscreens and touchpads.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rmi-f1a.
+
+config RMI4_F09
+	tristate "RMI4 Function 09 (self testing)"
+	depends on RMI4_BUS && RMI4_GENERIC
+	help
+	  Say Y here if you want to add support for RMI4 function 09.
+
+	  Function 09 provides self testing for touchscreens and touchpads.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rmi-f09.
+
+config RMI4_F11
+	tristate "RMI4 Function 11 (2D pointing)"
+	depends on RMI4_BUS && RMI4_GENERIC
+	help
+	  Say Y here if you want to add support for RMI4 function 11.
+
+	  Function 11 provides 2D multifinger pointing for touchscreens and
+	  touchpads.  For sensors that support relative pointing, F11 also
+	  provides mouse input.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rmi-f11.
+
+config RMI4_F11_PEN
+	bool "RMI4 F11 Pen Support"
+	depends on RMI4_F11
+	help
+	  Say Y here to add support for pen input to RMI4 function 11.
+
+	  If this feature is enabled, when pen inputs are detected they
+	  will be reported to the input stream as MT_TOOL_PEN.  Otherwise,
+	  pens will be treated the same as fingers.
+
+	  Not all UI implementations deal gracefully with pen discrimination.
+	  If your system is not recognizing pen touches and you know your
+	  sensor supports pen input, you probably want to turn this feature
+	  off.
+
+config RMI4_F11_TYPEB
+	bool "RMI4 F11 Type B Support"
+	depends on RMI4_F11
+	help
+	  Say Y here to add support for Type B multi-touch.
+
+	  If this feature is enabled, the driver will support type B multi-
+	  touch input.
+
+	  See Documentation/input/multi-touch-protocol for details.
+
+config RMI4_VIRTUAL_BUTTON
+	tristate "RMI4 Vitual Button"
+	depends on RMI4_F11
+	help
+	  Say Y here if you want to add support for RMI4 virtual button to F11.
+
+	  The virtual button feature implement the virtual button device in
+	  certain RMI4 touch sensors.
+
+	  This works only if your sensor supports F11 gestures.
+
+config RMI4_F17
+	tristate "RMI4 Function 17 (pointing sticks)"
+	depends on RMI4_BUS && RMI4_GENERIC
+	help
+	  Say Y here if you want to add support for RMI4 function 17.
+
+	  Function 19 provides support for capacitive and resistive
+	  pointing sticks.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rmi-f17.
+
+config RMI4_F19
+	tristate "RMI4 Function 19 (0D pointing)"
+	depends on RMI4_BUS && RMI4_GENERIC
+	help
+	  Say Y here if you want to add support for RMI4 function 19.
+
+	  Function 19 provides support for capacitive buttons for sensors
+	  that implement capacitive buttons.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rmi-f19.
+
+config RMI4_F21
+	tristate "RMI4 Function 21 (2D Force)"
+	depends on RMI4_BUS && RMI4_GENERIC
+	help
+	  Say Y here if you want to add support for RMI4 function 21.
+
+	  Function 21 provides 2D Force Sensing for ForcePad products.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rmi-f21.
+
+config RMI4_F30
+	tristate "RMI4 Function 30 (GPIO LED)"
+	depends on RMI4_BUS && RMI4_GENERIC
+	help
+	  Say Y here if you want to add support for RMI4 function 30.
+
+	  Function 30 provides GPIO LED support for sensors.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rmi-f30.
+
+config RMI4_F34
+	tristate "RMI4 Function 34 (device reflash)"
+	depends on RMI4_BUS && RMI4_GENERIC
+	help
+	  Say Y here if you want to add support for RMI4 function 34.
+
+	  Function 34 provides firmware upgrade capability for your sensor.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rmi-f34.
+
+config RMI4_F41
+	tristate "RMI4 Function 41 (active pen)"
+	depends on RMI4_BUS && RMI4_GENERIC
+	help
+	  Say Y here if you want to add support for RMI4 function 41.
+
+	  Function 41 provides active pen support for active pen enabled sensors.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rmi-f41.
+
+config RMI4_F54
+	tristate "RMI4 Function 54 (analog diagnostics)"
+	depends on RMI4_BUS && RMI4_GENERIC
+	help
+	  Say Y here if you want to add support for RMI4 function 54.
+
+	  Function 54 provides access to various diagnostic features in
+	  certain RMI4 touch sensors.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rmi-f54.
+
+config RMI4_DEV
+	tristate "Synaptics direct RMI device support (rmidev)"
+	depends on GPIO_SYSFS && (RMI4_I2C || RMI4_SPI)
+	help
+	  Say Y here to add support for rmidev.
+
+	  The rmidev feature implements a character device providing access
+	  to RMI4 sensor register maps.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rmi-dev.
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
new file mode 100644
index 0000000..4a14a21
--- /dev/null
+++ b/drivers/input/rmi4/Makefile
@@ -0,0 +1,18 @@
+obj-$(CONFIG_RMI4_BUS) += rmi_bus.o
+obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
+obj-$(CONFIG_RMI4_SPI) += rmi_spi.o
+obj-$(CONFIG_RMI4_SMB) += rmi_smbus.o
+obj-$(CONFIG_RMI4_GENERIC) += rmi_driver.o rmi_f01.o
+obj-$(CONFIG_RMI4_FWLIB) += rmi_fw_update.o
+obj-$(CONFIG_RMI4_F09) += rmi_f09.o
+obj-$(CONFIG_RMI4_F1A) += rmi_f1a.o
+obj-$(CONFIG_RMI4_F11) += rmi_f11.o
+obj-$(CONFIG_RMI4_F17) += rmi_f17.o
+obj-$(CONFIG_RMI4_F19) += rmi_f19.o
+obj-$(CONFIG_RMI4_F21) += rmi_f21.o
+obj-$(CONFIG_RMI4_F30) += rmi_f30.o
+obj-$(CONFIG_RMI4_F34) += rmi_f34.o
+obj-$(CONFIG_RMI4_F41) += rmi_f41.o
+obj-$(CONFIG_RMI4_F54) += rmi_f54.o
+obj-$(CONFIG_RMI4_DEV) += rmi_dev.o
+

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 5/17] input: rmidev character driver for RMI4 sensors
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (3 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 4/17] input: RMI4 configs and makefiles Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-27 18:49   ` Linus Walleij
  2012-08-17 22:17 ` [RFC PATCH 6/17] input: RMI4 firmware update Christopher Heiny
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

Driver for Synaptics touchscreens using RMI4 protocol.

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_dev.c |  448 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 448 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_dev.c b/drivers/input/rmi4/rmi_dev.c
new file mode 100644
index 0000000..080db55
--- /dev/null
+++ b/drivers/input/rmi4/rmi_dev.c
@@ -0,0 +1,448 @@
+/*
+ * 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 DEVICE_CLASS_NAME "rmidev"
+
+#define RMI_CHAR_DEV_TMPBUF_SZ 128
+#define RMI_REG_ADDR_PAGE_SELECT 0xFF
+#define REG_ADDR_LIMIT 0xFFFF
+
+struct rmidev_data {
+	/* mutex for file operation*/
+	struct mutex file_mutex;
+	/* main char dev structure */
+	struct cdev main_dev;
+
+	/* pointer to the corresponding RMI4 device.  We use this to do */
+	/* read, write, etc. */
+	struct rmi_device *rmi_dev;
+	/* reference count */
+	int ref_count;
+
+	struct class *device_class;
+};
+
+/*store dynamically allocated major number of char device*/
+static int rmidev_major_num;
+
+
+static struct class *rmidev_device_class;
+
+
+/* file operations for RMI char device */
+
+/*
+ * rmidev_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 rmidev_llseek(struct file *filp, loff_t off, int whence)
+{
+	loff_t newpos;
+	struct rmidev_data *data = filp->private_data;
+
+	if (IS_ERR(data)) {
+		pr_err("%s: pointer of char device is invalid", __func__);
+		return -EBADF;
+	}
+
+	mutex_lock(&(data->file_mutex));
+
+	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(&data->rmi_dev->dev, "newpos 0x%04x is invalid.\n",
+			(unsigned int)newpos);
+		newpos = -EINVAL;
+		goto clean_up;
+	}
+
+	filp->f_pos = newpos;
+
+clean_up:
+	mutex_unlock(&(data->file_mutex));
+	return newpos;
+}
+
+/*
+ *  rmidev_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 rmidev_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *f_pos)
+{
+	struct rmidev_data *data = filp->private_data;
+	ssize_t retval  = 0;
+	unsigned char tmpbuf[count+1];
+
+	/* 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(data)) {
+		pr_err("%s: pointer of char device is invalid", __func__);
+		return -EBADF;
+	}
+
+	mutex_lock(&(data->file_mutex));
+
+	retval = rmi_read_block(data->rmi_dev, *f_pos, tmpbuf, count);
+
+	if (retval < 0)
+		goto clean_up;
+
+	if (copy_to_user(buf, tmpbuf, count))
+		retval = -EFAULT;
+	else
+		*f_pos += retval;
+
+clean_up:
+
+	mutex_unlock(&(data->file_mutex));
+
+	return retval;
+}
+
+/*
+ * rmidev_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 rmidev_write(struct file *filp, const char __user *buf,
+		size_t count, loff_t *f_pos)
+{
+	struct rmidev_data *data = filp->private_data;
+	ssize_t retval  = 0;
+	unsigned char tmpbuf[count+1];
+
+	/* 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(data)) {
+		pr_err("%s: pointer of char device is invalid", __func__);
+		return -EBADF;
+	}
+
+	if (copy_from_user(tmpbuf, buf, count))
+		return -EFAULT;
+
+	mutex_lock(&(data->file_mutex));
+
+	retval = rmi_write_block(data->rmi_dev, *f_pos, tmpbuf, count);
+
+	if (retval >= 0)
+		*f_pos += count;
+
+	mutex_unlock(&(data->file_mutex));
+
+	return retval;
+}
+
+/*
+ * rmidev_open: - get a new handle for from RMI stream
+ * @inp : inode struture
+ * @filp: file structure for read/write
+ *
+ * @return 0 if succeeds
+ */
+static int rmidev_open(struct inode *inp, struct file *filp)
+{
+	struct rmidev_data *data = container_of(inp->i_cdev,
+			struct rmidev_data, main_dev);
+	int retval = 0;
+
+	filp->private_data = data;
+
+	if (!data->rmi_dev)
+		return -EACCES;
+
+	mutex_lock(&(data->file_mutex));
+	if (data->ref_count < 1)
+		data->ref_count++;
+	else
+		retval = -EACCES;
+
+	mutex_unlock(&(data->file_mutex));
+
+	return retval;
+}
+
+/*
+ *  rmidev_release: - release an existing handle
+ *  @inp: inode structure
+ *  @filp: file structure for read/write
+ *
+ *  @return 0 if succeeds
+ */
+static int rmidev_release(struct inode *inp, struct file *filp)
+{
+	struct rmidev_data *data = container_of(inp->i_cdev,
+			struct rmidev_data, main_dev);
+
+	if (!data->rmi_dev)
+		return -EACCES;
+
+	mutex_lock(&(data->file_mutex));
+
+	data->ref_count--;
+	if (data->ref_count < 0)
+		data->ref_count = 0;
+
+	mutex_unlock(&(data->file_mutex));
+
+	return 0;
+}
+
+static const struct file_operations rmidev_fops = {
+	.owner =    THIS_MODULE,
+	.llseek =   rmidev_llseek,
+	.read =     rmidev_read,
+	.write =    rmidev_write,
+	.open =     rmidev_open,
+	.release =  rmidev_release,
+};
+
+/*
+ * rmidev_device_cleanup - release memory or unregister driver
+ * @rmidev_data: instance data for a particular device.
+ *
+ */
+static void rmidev_device_cleanup(struct rmidev_data *data)
+{
+	dev_t devno;
+
+	/* Get rid of our char dev entries */
+	if (data) {
+		devno = data->main_dev.dev;
+
+		if (data->device_class)
+			device_destroy(data->device_class, devno);
+
+		cdev_del(&data->main_dev);
+		kfree(data);
+
+		/* cleanup_module is never called if registering failed */
+		unregister_chrdev_region(devno, 1);
+		pr_debug("%s: rmidev device 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;
+	/**mode = 0666*/
+	*mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+
+	return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev));
+}
+
+static int rmidev_init_device(struct rmi_char_device *cd)
+{
+	struct rmi_device *rmi_dev = cd->rmi_dev;
+	struct rmidev_data *data;
+	dev_t dev_no;
+	int retval;
+	struct device *device_ptr;
+
+	if (rmidev_major_num) {
+		dev_no = MKDEV(rmidev_major_num, cd->rmi_dev->number);
+		retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
+	} else {
+		retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
+		/* let kernel allocate a major for us */
+		rmidev_major_num = MAJOR(dev_no);
+		dev_info(&rmi_dev->dev, "Major number of rmidev: %d\n",
+				 rmidev_major_num);
+	}
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"Failed to get minor dev number %d, code %d.\n",
+			cd->rmi_dev->number, retval);
+		return retval;
+	} else
+		dev_info(&rmi_dev->dev, "Allocated rmidev %d %d.\n",
+			 MAJOR(dev_no), MINOR(dev_no));
+
+	data = kzalloc(sizeof(struct rmidev_data), GFP_KERNEL);
+	if (!data) {
+		dev_err(&rmi_dev->dev, "Failed to allocate rmidev_data.\n");
+		/* unregister the char device region */
+		__unregister_chrdev(rmidev_major_num, MINOR(dev_no), 1,
+				CHAR_DEVICE_NAME);
+		return -ENOMEM;
+	}
+
+	mutex_init(&data->file_mutex);
+
+	data->rmi_dev = cd->rmi_dev;
+	cd->data = data;
+
+	cdev_init(&data->main_dev, &rmidev_fops);
+
+	retval = cdev_add(&data->main_dev, dev_no, 1);
+	if (retval) {
+		dev_err(&cd->rmi_dev->dev, "Error %d adding rmi_char_dev.\n",
+			retval);
+		rmidev_device_cleanup(data);
+		return retval;
+	}
+
+	dev_set_name(&cd->dev, "rmidev%d", MINOR(dev_no));
+	data->device_class = rmidev_device_class;
+	device_ptr = device_create(
+			data->device_class,
+			NULL, dev_no, NULL,
+			CHAR_DEVICE_NAME"%d",
+			MINOR(dev_no));
+
+	if (IS_ERR(device_ptr)) {
+		dev_err(&cd->rmi_dev->dev, "Failed to create rmi device.\n");
+		rmidev_device_cleanup(data);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void rmidev_remove_device(struct rmi_char_device *cd)
+{
+	struct rmidev_data *data;
+
+	dev_dbg(&cd->dev, "%s: removing an rmidev device.\n", __func__);
+	if (!cd)
+		return;
+
+	data = cd->data;
+	if (data)
+		rmidev_device_cleanup(data);
+}
+
+static struct rmi_char_driver rmidev_driver = {
+	.driver = {
+		.name = "rmidev",
+		.owner = THIS_MODULE,
+	},
+
+	.init = rmidev_init_device,
+	.remove = rmidev_remove_device,
+};
+
+static int __init rmidev_init(void)
+{
+	int error = 0;
+	pr_debug("%s: rmi_dev initialization.\n", __func__);
+
+	/* create device node */
+	rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
+
+	if (IS_ERR(rmidev_device_class)) {
+		pr_err("%s: ERROR - Failed to create /dev/%s.\n", __func__,
+			CHAR_DEVICE_NAME);
+		return -ENODEV;
+	}
+	/* setup permission */
+	rmidev_device_class->devnode = rmi_char_devnode;
+
+	error = rmi_register_character_driver(&rmidev_driver);
+	if (error)
+		class_destroy(rmidev_device_class);
+	return error;
+}
+
+static void __exit rmidev_exit(void)
+{
+	pr_debug("%s: exiting.\n", __func__);
+	rmi_unregister_character_driver(&rmidev_driver);
+	class_destroy(rmidev_device_class);
+}
+
+module_init(rmidev_init);
+module_exit(rmidev_exit);
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
+MODULE_DESCRIPTION("RMI4 Char Device");
+MODULE_LICENSE("GPL");

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 6/17] input: RMI4 firmware update
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (4 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 5/17] input: rmidev character driver for RMI4 sensors Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-27 21:01   ` Linus Walleij
  2012-08-17 22:17 ` [RFC PATCH 7/17] input: RMI4 F01 device control Christopher Heiny
                   ` (12 subsequent siblings)
  18 siblings, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_fw_update.c |  724 ++++++++++++++++++++++++++++++++++++
 1 files changed, 724 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_fw_update.c b/drivers/input/rmi4/rmi_fw_update.c
new file mode 100644
index 0000000..7f6c315
--- /dev/null
+++ b/drivers/input/rmi4/rmi_fw_update.c
@@ -0,0 +1,724 @@
+/*
+ * Copyright (c) 2012 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define DEBUG
+
+#define RIM_HACK 1
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include <linux/kernel.h>
+#include<linux/moduleparam.h>
+#include <linux/rmi.h>
+#include <linux/time.h>
+#include "rmi_driver.h"
+#include "rmi_f01.h"
+#include "rmi_f34.h"
+
+#define HAS_BSR_MASK 0x20
+
+#define CHECKSUM_OFFSET 0
+#define BOOTLOADER_VERSION_OFFSET 0x07
+#define IMAGE_SIZE_OFFSET 0x08
+#define CONFIG_SIZE_OFFSET 0x0C
+#define PRODUCT_ID_OFFSET 0x10
+#define PRODUCT_ID_SIZE 10
+#define PRODUCT_INFO_OFFSET 0x1E
+#define PRODUCT_INFO_SIZE 2
+
+#define F01_RESET_MASK 0x01
+
+#define ENABLE_WAIT_US (300 * 1000)
+
+/** Image file V5, Option 0
+ */
+struct image_header {
+	u32 checksum;
+	unsigned int image_size;
+	unsigned int config_size;
+	unsigned char options;
+	unsigned char bootloader_version;
+	u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
+	unsigned char product_info[PRODUCT_INFO_SIZE];
+};
+
+static u32 extract_u32(const u8 *ptr)
+{
+	return (u32)ptr[0] +
+		(u32)ptr[1] * 0x100 +
+		(u32)ptr[2] * 0x10000 +
+		(u32)ptr[3] * 0x1000000;
+}
+
+struct reflash_data {
+	struct rmi_device *rmi_dev;
+	struct pdt_entry *f01_pdt;
+	union f01_basic_queries f01_queries;
+	union f01_device_control_0 f01_controls;
+	char product_id[RMI_PRODUCT_ID_LENGTH+1];
+	struct pdt_entry *f34_pdt;
+	u8 bootloader_id[2];
+	union f34_query_regs f34_queries;
+	union f34_control_status f34_controls;
+	const u8 *firmware_data;
+	const u8 *config_data;
+};
+
+/* If this parameter is true, we will update the firmware regardless of
+ * the versioning info.
+ */
+static bool force = 1;
+module_param(force, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(param, "Force reflash of RMI4 devices");
+
+/* If this parameter is not NULL, we'll use that name for the firmware image,
+ * instead of getting it from the F01 queries.
+ */
+static char *img_name;
+module_param(img_name, charp, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(param, "Name of the RMI4 firmware image");
+
+#define RMI4_IMAGE_FILE_REV1_OFFSET 30
+#define RMI4_IMAGE_FILE_REV2_OFFSET 31
+#define IMAGE_FILE_CHECKSUM_SIZE 4
+#define FIRMWARE_IMAGE_AREA_OFFSET 0x100
+
+static void extract_header(const u8 *data, int pos, struct image_header *header)
+{
+	header->checksum = extract_u32(&data[pos + CHECKSUM_OFFSET]);
+	header->bootloader_version = data[pos + BOOTLOADER_VERSION_OFFSET];
+	header->image_size = extract_u32(&data[pos + IMAGE_SIZE_OFFSET]);
+	header->config_size = extract_u32(&data[pos + CONFIG_SIZE_OFFSET]);
+	memcpy(header->product_id, &data[pos + PRODUCT_ID_OFFSET],
+	       RMI_PRODUCT_ID_LENGTH);
+	header->product_id[PRODUCT_ID_SIZE] = 0;
+	memcpy(header->product_info, &data[pos + PRODUCT_INFO_OFFSET],
+	       RMI_PRODUCT_ID_LENGTH);
+}
+
+static int rescan_pdt(struct reflash_data *data)
+{
+	int retval;
+	bool f01_found;
+	bool f34_found;
+	struct pdt_entry pdt_entry;
+	int i;
+	struct rmi_device *rmi_dev = data->rmi_dev;
+	struct pdt_entry *f34_pdt = data->f34_pdt;
+	struct pdt_entry *f01_pdt = data->f01_pdt;
+
+	/* Per spec, once we're in reflash we only need to look at the first
+	 * PDT page for potentially changed F01 and F34 information.
+	 */
+	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(&rmi_dev->dev,
+				"Read PDT entry at %#06x failed: %d.\n",
+				i, retval);
+			return retval;
+		}
+
+		if (RMI4_END_OF_PDT(pdt_entry.function_number))
+			break;
+
+		if (pdt_entry.function_number == 0x01) {
+			memcpy(f01_pdt, &pdt_entry, sizeof(pdt_entry));
+			f01_found = true;
+		} else if (pdt_entry.function_number == 0x34) {
+			memcpy(f34_pdt, &pdt_entry, sizeof(pdt_entry));
+			f34_found = true;
+		}
+	}
+
+	if (!f01_found) {
+		dev_err(&rmi_dev->dev, "Failed to find F01 PDT entry.\n");
+		retval = -ENODEV;
+	} else if (!f34_found) {
+		dev_err(&rmi_dev->dev, "Failed to find F34 PDT entry.\n");
+		retval = -ENODEV;
+	} else
+		retval = 0;
+
+	return retval;
+}
+
+static int read_f34_controls(struct reflash_data *data)
+{
+	int retval;
+
+	retval = rmi_read(data->rmi_dev, data->f34_controls.address,
+			  data->f34_controls.regs);
+	if (retval < 0)
+		return retval;
+
+	return 0;
+}
+
+static int read_f01_status(struct reflash_data *data,
+			   union f01_device_status *device_status)
+{
+	int retval;
+
+	retval = rmi_read(data->rmi_dev, data->f01_pdt->data_base_addr,
+			  device_status->regs);
+	if (retval < 0)
+		return retval;
+
+	return 0;
+}
+
+static int read_f01_controls(struct reflash_data *data)
+{
+	int retval;
+
+	retval = rmi_read(data->rmi_dev, data->f01_pdt->control_base_addr,
+			  data->f01_controls.regs);
+	if (retval < 0)
+		return retval;
+	return 0;
+}
+
+static int write_f01_controls(struct reflash_data *data)
+{
+	int retval;
+
+	retval = rmi_write(data->rmi_dev, data->f01_pdt->control_base_addr,
+			  data->f01_controls.regs[0]);
+	if (retval < 0)
+		return retval;
+	return 0;
+}
+
+#define MIN_SLEEP_TIME_US 50
+#define MAX_SLEEP_TIME_US 100
+
+/* Wait until the status is idle and we're ready to continue */
+static int wait_for_idle(struct reflash_data *data, int timeout_ms)
+{
+	int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1;
+	int count = 0;
+	union f34_control_status *controls = &data->f34_controls;
+	int retval;
+
+	do {
+		if (count || timeout_count == 1)
+			usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US);
+		retval = read_f34_controls(data);
+		count++;
+		if (retval < 0)
+			continue;
+		else if (IS_IDLE(controls)) {
+			if (!data->f34_controls.program_enabled) {
+				/** This works around a bug in certain device
+				 * firmwares, where the idle state is reached,
+				 * but the program_enabled bit is not yet set.
+				 */
+				dev_warn(&data->rmi_dev->dev, "Yikes!  We're not enabled!\n");
+				msleep(1000);
+				read_f34_controls(data);
+			}
+			return 0;
+		}
+	} while (count < timeout_count);
+
+	dev_err(&data->rmi_dev->dev,
+		"ERROR: Timeout waiting for idle status, last status: %#04x.\n",
+		controls->regs[0]);
+	dev_err(&data->rmi_dev->dev, "Command: %#04x\n", controls->command);
+	dev_err(&data->rmi_dev->dev, "Status:  %#04x\n", controls->status);
+	dev_err(&data->rmi_dev->dev, "Enabled: %d\n",
+			controls->program_enabled);
+	dev_err(&data->rmi_dev->dev, "Idle:    %d\n", IS_IDLE(controls));
+	return -ETIMEDOUT;
+}
+
+
+static int read_f01_queries(struct reflash_data *data)
+{
+	int retval;
+	u16 addr = data->f01_pdt->query_base_addr;
+
+	retval = rmi_read_block(data->rmi_dev, addr, data->f01_queries.regs,
+				ARRAY_SIZE(data->f01_queries.regs));
+	if (retval < 0) {
+		dev_err(&data->rmi_dev->dev,
+			"Failed to read F34 queries (code %d).\n", retval);
+		return retval;
+	}
+	addr += ARRAY_SIZE(data->f01_queries.regs);
+
+	retval = rmi_read_block(data->rmi_dev, addr, data->product_id,
+				RMI_PRODUCT_ID_LENGTH);
+	if (retval < 0) {
+		dev_err(&data->rmi_dev->dev,
+			"Failed to read product ID (code %d).\n", retval);
+		return retval;
+	}
+	data->product_id[RMI_PRODUCT_ID_LENGTH] = 0;
+	dev_info(&data->rmi_dev->dev, "F01 Product id:   %s\n",
+			data->product_id);
+	dev_info(&data->rmi_dev->dev, "F01 product info: %#04x %#04x\n",
+			data->f01_queries.productinfo_1,
+			data->f01_queries.productinfo_2);
+
+	return 0;
+}
+
+static int read_f34_queries(struct reflash_data *data)
+{
+	int retval;
+	u8 id_str[3];
+
+	retval = rmi_read_block(data->rmi_dev, data->f34_pdt->query_base_addr,
+				data->bootloader_id, 2);
+	if (retval < 0) {
+		dev_err(&data->rmi_dev->dev,
+			"Failed to read F34 bootloader_id (code %d).\n",
+			retval);
+		return retval;
+	}
+	retval = rmi_read_block(data->rmi_dev, data->f34_pdt->query_base_addr+2,
+			data->f34_queries.regs,
+			ARRAY_SIZE(data->f34_queries.regs));
+	if (retval < 0) {
+		dev_err(&data->rmi_dev->dev,
+			"Failed to read F34 queries (code %d).\n", retval);
+		return retval;
+	}
+	data->f34_queries.block_size =
+			le16_to_cpu(data->f34_queries.block_size);
+	data->f34_queries.fw_block_count =
+			le16_to_cpu(data->f34_queries.fw_block_count);
+	data->f34_queries.config_block_count =
+			le16_to_cpu(data->f34_queries.config_block_count);
+	id_str[0] = data->bootloader_id[0];
+	id_str[1] = data->bootloader_id[1];
+	id_str[2] = 0;
+#ifdef DEBUG
+	dev_info(&data->rmi_dev->dev, "Got F34 data->f34_queries.\n");
+	dev_info(&data->rmi_dev->dev, "F34 bootloader id: %s (%#04x %#04x)\n",
+		 id_str, data->bootloader_id[0], data->bootloader_id[1]);
+	dev_info(&data->rmi_dev->dev, "F34 has config id: %d\n",
+		 data->f34_queries.has_config_id);
+	dev_info(&data->rmi_dev->dev, "F34 unlocked:      %d\n",
+		 data->f34_queries.unlocked);
+	dev_info(&data->rmi_dev->dev, "F34 regMap:        %d\n",
+		 data->f34_queries.reg_map);
+	dev_info(&data->rmi_dev->dev, "F34 block size:    %d\n",
+		 data->f34_queries.block_size);
+	dev_info(&data->rmi_dev->dev, "F34 fw blocks:     %d\n",
+		 data->f34_queries.fw_block_count);
+	dev_info(&data->rmi_dev->dev, "F34 config blocks: %d\n",
+		 data->f34_queries.config_block_count);
+#endif
+
+	data->f34_controls.address = data->f34_pdt->data_base_addr +
+			F34_BLOCK_DATA_OFFSET + data->f34_queries.block_size;
+
+	return 0;
+}
+
+static int write_bootloader_id(struct reflash_data *data)
+{
+	int retval;
+	struct rmi_device *rmi_dev = data->rmi_dev;
+	struct pdt_entry *f34_pdt = data->f34_pdt;
+
+	retval = rmi_write_block(rmi_dev,
+			f34_pdt->data_base_addr + F34_BLOCK_DATA_OFFSET,
+			data->bootloader_id, ARRAY_SIZE(data->bootloader_id));
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"Failed to write bootloader ID. Code: %d.\n", retval);
+		return retval;
+	}
+
+	return 0;
+}
+
+static int write_f34_command(struct reflash_data *data, u8 command)
+{
+	int retval;
+	struct rmi_device *rmi_dev = data->rmi_dev;
+
+	retval = rmi_write(rmi_dev, data->f34_controls.address, command);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"Failed to write F34 command %#04x. Code: %d.\n",
+			command, retval);
+		return retval;
+	}
+
+	return 0;
+}
+
+static int enter_flash_programming(struct reflash_data *data)
+{
+	int retval;
+	union f01_device_status device_status;
+	struct rmi_device *rmi_dev = data->rmi_dev;
+
+	retval = write_bootloader_id(data);
+	if (retval < 0)
+		return retval;
+
+	dev_info(&rmi_dev->dev, "Enabling flash programming.\n");
+	retval = write_f34_command(data, F34_ENABLE_FLASH_PROG);
+	if (retval < 0)
+		return retval;
+
+#if	RIM_HACK
+	data->f01_controls.nosleep = true;
+	retval = write_f01_controls(data);
+	if (retval < 0)
+		return retval;
+#endif
+
+	retval = wait_for_idle(data, F34_ENABLE_WAIT_MS);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "Did not reach idle state after %d ms. Code: %d.\n",
+			F34_ENABLE_WAIT_MS, retval);
+		return retval;
+	}
+	if (!data->f34_controls.program_enabled) {
+		dev_err(&rmi_dev->dev, "Reached idle, but programming not enabled (current status register: %#04x).\n",
+					data->f34_controls.regs[0]);
+		return -EINVAL;
+	}
+	dev_info(&rmi_dev->dev, "HOORAY! Programming is enabled!\n");
+
+	retval = rescan_pdt(data);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "Failed to rescan pdt.  Code: %d.\n",
+			retval);
+		return retval;
+	}
+
+	retval = read_f01_status(data, &device_status);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "Failed to read F01 status after enabling reflash. Code: %d.\n",
+			retval);
+		return retval;
+	}
+	if (!(device_status.flash_prog)) {
+		dev_err(&rmi_dev->dev, "Device reports as not in flash programming mode.\n");
+		return -EINVAL;
+	}
+
+	retval = read_f34_queries(data);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
+			retval);
+		return retval;
+	}
+
+	retval = read_f01_controls(data);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "F01 controls read failed, code = %d.\n",
+			retval);
+		return retval;
+	}
+	data->f01_controls.nosleep = true;
+	data->f01_controls.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+
+	retval = write_f01_controls(data);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "F01 controls write failed, code = %d.\n",
+			retval);
+		return retval;
+	}
+
+	return retval;
+}
+
+static void reset_device(struct reflash_data *data)
+{
+	int retval;
+	struct rmi_device_platform_data *pdata =
+		to_rmi_platform_data(data->rmi_dev);
+
+	dev_info(&data->rmi_dev->dev, "Resetting...\n");
+	retval = rmi_write(data->rmi_dev, data->f01_pdt->command_base_addr,
+			   F01_RESET_MASK);
+	if (retval < 0)
+		dev_warn(&data->rmi_dev->dev,
+			 "WARNING - post-flash reset failed, code: %d.\n",
+			 retval);
+	msleep(pdata->reset_delay_ms);
+	dev_info(&data->rmi_dev->dev, "Reset completed.\n");
+}
+
+/*
+ * Send data to the device one block at a time.
+ */
+static int write_blocks(struct reflash_data *data, u8 *block_ptr,
+			u16 block_count, u8 cmd)
+{
+	int block_num;
+	u8 zeros[] = {0, 0};
+	int retval;
+	u16 addr = data->f34_pdt->data_base_addr + F34_BLOCK_DATA_OFFSET;
+
+	retval = rmi_write_block(data->rmi_dev, data->f34_pdt->data_base_addr,
+				 zeros, ARRAY_SIZE(zeros));
+	if (retval < 0) {
+		dev_err(&data->rmi_dev->dev, "Failed to write initial zeros. Code=%d.\n",
+			retval);
+		return retval;
+	}
+
+	for (block_num = 0; block_num < block_count; ++block_num) {
+		retval = rmi_write_block(data->rmi_dev, addr, block_ptr,
+					 data->f34_queries.block_size);
+		if (retval < 0) {
+			dev_err(&data->rmi_dev->dev, "Failed to write block %d. Code=%d.\n",
+				block_num, retval);
+			return retval;
+		}
+
+		retval = write_f34_command(data, cmd);
+		if (retval) {
+			dev_err(&data->rmi_dev->dev, "Failed to write command for block %d. Code=%d.\n",
+				block_num, retval);
+			return retval;
+		}
+
+
+		retval = wait_for_idle(data, F34_IDLE_WAIT_MS);
+		if (retval) {
+			dev_err(&data->rmi_dev->dev, "Failed to go idle after writing block %d. Code=%d.\n",
+				block_num, retval);
+			return retval;
+		}
+
+		block_ptr += data->f34_queries.block_size;
+	}
+
+	return 0;
+}
+
+static int write_firmware(struct reflash_data *data)
+{
+	return write_blocks(data, (u8 *) data->firmware_data,
+		data->f34_queries.fw_block_count, F34_WRITE_FW_BLOCK);
+}
+
+static int write_configuration(struct reflash_data *data)
+{
+	return write_blocks(data, (u8 *) data->config_data,
+		data->f34_queries.config_block_count, F34_WRITE_CONFIG_BLOCK);
+}
+
+static void reflash_firmware(struct reflash_data *data)
+{
+#ifdef DEBUG
+	struct timespec start;
+	struct timespec end;
+	s64 duration_ns;
+#endif
+	int retval = 0;
+
+	retval = enter_flash_programming(data);
+	if (retval)
+		return;
+
+	retval = write_bootloader_id(data);
+	if (retval)
+		return;
+
+#ifdef	DEBUG
+	dev_info(&data->rmi_dev->dev, "Erasing FW...\n");
+	getnstimeofday(&start);
+#endif
+	retval = write_f34_command(data, F34_ERASE_ALL);
+	if (retval)
+		return;
+
+	retval = wait_for_idle(data, F34_ERASE_WAIT_MS);
+	if (retval) {
+		dev_err(&data->rmi_dev->dev,
+			"Failed to reach idle state. Code: %d.\n", retval);
+		return;
+	}
+#ifdef	DEBUG
+	getnstimeofday(&end);
+	duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+	dev_info(&data->rmi_dev->dev,
+		 "Erase complete, time: %lld ns.\n", duration_ns);
+#endif
+
+	if (data->firmware_data) {
+#ifdef	DEBUG
+		dev_info(&data->rmi_dev->dev, "Writing firmware...\n");
+		getnstimeofday(&start);
+#endif
+		retval = write_firmware(data);
+		if (retval)
+			return;
+#ifdef	DEBUG
+		getnstimeofday(&end);
+		duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+		dev_info(&data->rmi_dev->dev,
+			 "Done writing FW, time: %lld ns.\n", duration_ns);
+#endif
+	}
+
+	if (data->config_data) {
+#ifdef	DEBUG
+		dev_info(&data->rmi_dev->dev, "Writing configuration...\n");
+		getnstimeofday(&start);
+#endif
+		retval = write_configuration(data);
+		if (retval)
+			return;
+#ifdef	DEBUG
+		getnstimeofday(&end);
+		duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+		dev_info(&data->rmi_dev->dev,
+			 "Done writing config, time: %lld ns.\n", duration_ns);
+#endif
+	}
+}
+
+/* Returns false if the firmware should not be reflashed.
+ */
+static bool go_nogo(struct reflash_data *data, struct image_header *header)
+{
+	union f01_device_status device_status;
+	int retval;
+
+	if (data->f01_queries.productinfo_1 < header->product_info[0] ||
+		data->f01_queries.productinfo_2 < header->product_info[1]) {
+		dev_info(&data->rmi_dev->dev,
+			 "FW product ID is older than image product ID.\n");
+		return true;
+	}
+
+	retval = read_f01_status(data, &device_status);
+	if (retval)
+		dev_err(&data->rmi_dev->dev,
+			"Failed to read F01 status. Code: %d.\n", retval);
+
+	return device_status.flash_prog || force;
+}
+
+void rmi4_fw_update(struct rmi_device *rmi_dev,
+		struct pdt_entry *f01_pdt, struct pdt_entry *f34_pdt)
+{
+#ifdef DEBUG
+	struct timespec start;
+	struct timespec end;
+	s64 duration_ns;
+#endif
+	char firmware_name[RMI_PRODUCT_ID_LENGTH + 12];
+	const struct firmware *fw_entry = NULL;
+	int retval;
+	struct image_header header;
+	union pdt_properties pdt_props;
+	struct reflash_data data = {
+		.rmi_dev = rmi_dev,
+		.f01_pdt = f01_pdt,
+		.f34_pdt = f34_pdt,
+	};
+	struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
+
+	dev_info(&rmi_dev->dev, "%s called.\n", __func__);
+#ifdef	DEBUG
+	getnstimeofday(&start);
+#endif
+
+	retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, pdt_props.regs);
+	if (retval < 0) {
+		dev_warn(&rmi_dev->dev,
+			 "Failed to read PDT props at %#06x (code %d). Assuming 0x00.\n",
+			 PDT_PROPERTIES_LOCATION, retval);
+	}
+	if (pdt_props.has_bsr) {
+		dev_warn(&rmi_dev->dev,
+			 "Firmware update for LTS not currently supported.\n");
+		return;
+	}
+
+	retval = read_f01_queries(&data);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "F01 queries failed, code = %d.\n",
+			retval);
+		return;
+	}
+	retval = read_f34_queries(&data);
+	if (retval) {
+		dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
+			retval);
+		return;
+	}
+	if (pdata->firmware_name && strlen(pdata->firmware_name))
+		snprintf(firmware_name, sizeof(firmware_name), "rmi4/%s.img",
+			pdata->firmware_name);
+	else
+		snprintf(firmware_name, sizeof(firmware_name), "rmi4/%s.img",
+			(img_name && strlen(img_name))
+				? img_name : data.product_id);
+	dev_info(&rmi_dev->dev, "Requesting %s.\n", firmware_name);
+	retval = request_firmware(&fw_entry, firmware_name, &rmi_dev->dev);
+	if (retval != 0) {
+		dev_err(&rmi_dev->dev, "Firmware %s not available, code = %d\n",
+			firmware_name, retval);
+		return;
+	}
+
+#ifdef	DEBUG
+	dev_info(&rmi_dev->dev, "Got firmware, size: %d.\n", fw_entry->size);
+	extract_header(fw_entry->data, 0, &header);
+	dev_info(&rmi_dev->dev, "Img checksum:           %#08X\n",
+		 header.checksum);
+	dev_info(&rmi_dev->dev, "Img image size:         %d\n",
+		 header.image_size);
+	dev_info(&rmi_dev->dev, "Img config size:        %d\n",
+		 header.config_size);
+	dev_info(&rmi_dev->dev, "Img bootloader version: %d\n",
+		 header.bootloader_version);
+	dev_info(&rmi_dev->dev, "Img product id:         %s\n",
+		 header.product_id);
+	dev_info(&rmi_dev->dev, "Img product info:       %#04x %#04x\n",
+		 header.product_info[0], header.product_info[1]);
+#endif
+
+	if (header.image_size)
+		data.firmware_data = fw_entry->data + F34_FW_IMAGE_OFFSET;
+	if (header.config_size)
+		data.config_data = fw_entry->data + F34_FW_IMAGE_OFFSET +
+			header.image_size;
+
+	if (go_nogo(&data, &header)) {
+		reflash_firmware(&data);
+		reset_device(&data);
+	} else
+		dev_info(&rmi_dev->dev, "Go/NoGo said don't reflash.\n");
+
+	if (fw_entry)
+		release_firmware(fw_entry);
+#ifdef	DEBUG
+	getnstimeofday(&end);
+	duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
+	dev_info(&rmi_dev->dev, "Time to reflash: %lld ns.\n", duration_ns);
+#endif
+}

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 7/17] input: RMI4 F01 device control
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (5 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 6/17] input: RMI4 firmware update Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-27 21:59   ` Linus Walleij
  2012-08-17 22:17 ` [RFC PATCH 8/17] input: RMI4 F09 Built-In Self Test Christopher Heiny
                   ` (11 subsequent siblings)
  18 siblings, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_f01.c | 1331 ++++++++++++++++++++++++++++++++++++++++++
 drivers/input/rmi4/rmi_f01.h |   88 +++
 2 files changed, 1419 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
new file mode 100644
index 0000000..312ad13
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -0,0 +1,1331 @@
+/*
+ * Copyright (c) 2011-2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+#include "rmi_f01.h"
+
+union f01_device_commands {
+	struct {
+		u8 reset:1;
+		u8 reserved:1;
+	};
+	u8 reg;
+};
+
+struct f01_device_control {
+	union f01_device_control_0 ctrl0;
+	u8 *interrupt_enable;
+	u8 doze_interval;
+	u8 wakeup_threshold;
+	u8 doze_holdoff;
+};
+
+union f01_query_42 {
+	struct {
+		u8 has_ds4_queries:1;
+		u8 has_multi_phy:1;
+		u8 has_guest:1;
+		u8 reserved:5;
+	};
+	u8 regs[1];
+};
+
+union f01_ds4_queries {
+	struct {
+		u8 length:4;
+		u8 reserved_1:4;
+
+		u8 has_package_id_query:1;
+		u8 has_packrat_query:1;
+		u8 has_reset_query:1;
+		u8 has_maskrev_query:1;
+		u8 reserved_2:4;
+
+		u8 has_i2c_control:1;
+		u8 has_spi_control:1;
+		u8 has_attn_control:1;
+		u8 reserved_3:5;
+
+		u8 reset_enabled:1;
+		u8 reset_polarity:1;
+		u8 pullup_enabled:1;
+		u8 reserved_4:1;
+		u8 reset_pin_number:4;
+	};
+	u8 regs[4];
+};
+
+struct f01_data {
+	struct f01_device_control device_control;
+	union f01_basic_queries basic_queries;
+	union f01_device_status device_status;
+	u8 product_id[RMI_PRODUCT_ID_LENGTH+1];
+
+	u16 interrupt_enable_addr;
+	u16 doze_interval_addr;
+	u16 wakeup_threshold_addr;
+	u16 doze_holdoff_addr;
+
+	int irq_count;
+	int num_of_irq_regs;
+
+#ifdef	CONFIG_PM
+	bool suspended;
+	bool old_nosleep;
+#endif
+};
+
+
+static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf);
+
+static ssize_t rmi_fn_01_productid_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+
+static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf);
+
+static ssize_t rmi_fn_01_datecode_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf);
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_interrupt_enable_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+
+static ssize_t rmi_fn_01_interrupt_enable_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_doze_interval_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+
+static ssize_t rmi_fn_01_doze_interval_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_wakeup_threshold_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+
+static ssize_t rmi_fn_01_wakeup_threshold_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_doze_holdoff_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+
+static ssize_t rmi_fn_01_doze_holdoff_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_reset_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_sleepmode_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+
+static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_nosleep_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_chargerinput_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_chargerinput_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_configured_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_unconfigured_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_flashprog_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_statuscode_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static int rmi_f01_alloc_memory(struct rmi_function_container *fc,
+					int num_of_irq_regs);
+
+static void rmi_f01_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f01_initialize(struct rmi_function_container *fc);
+
+static int rmi_f01_create_sysfs(struct rmi_function_container *fc);
+
+static int rmi_f01_config(struct rmi_function_container *fc);
+
+static int rmi_f01_reset(struct rmi_function_container *fc);
+
+
+static struct device_attribute fn_01_attrs[] = {
+	__ATTR(productinfo, RMI_RO_ATTR,
+	       rmi_fn_01_productinfo_show, rmi_store_error),
+	__ATTR(productid, RMI_RO_ATTR,
+	       rmi_fn_01_productid_show, rmi_store_error),
+	__ATTR(manufacturer, RMI_RO_ATTR,
+	       rmi_fn_01_manufacturer_show, rmi_store_error),
+	__ATTR(datecode, RMI_RO_ATTR,
+	       rmi_fn_01_datecode_show, rmi_store_error),
+
+	/* control register access */
+	__ATTR(sleepmode, RMI_RW_ATTR,
+	       rmi_fn_01_sleepmode_show, rmi_fn_01_sleepmode_store),
+	__ATTR(nosleep, RMI_RW_ATTR,
+	       rmi_fn_01_nosleep_show, rmi_fn_01_nosleep_store),
+	__ATTR(chargerinput, RMI_RW_ATTR,
+	       rmi_fn_01_chargerinput_show, rmi_fn_01_chargerinput_store),
+	__ATTR(reportrate, RMI_RW_ATTR,
+	       rmi_fn_01_reportrate_show, rmi_fn_01_reportrate_store),
+	__ATTR(interrupt_enable, RMI_RW_ATTR,
+	       rmi_fn_01_interrupt_enable_show,
+		rmi_fn_01_interrupt_enable_store),
+	__ATTR(doze_interval, RMI_RW_ATTR,
+	       rmi_fn_01_doze_interval_show,
+		rmi_fn_01_doze_interval_store),
+	__ATTR(wakeup_threshold, RMI_RW_ATTR,
+	       rmi_fn_01_wakeup_threshold_show,
+		rmi_fn_01_wakeup_threshold_store),
+	__ATTR(doze_holdoff, RMI_RW_ATTR,
+	       rmi_fn_01_doze_holdoff_show,
+		rmi_fn_01_doze_holdoff_store),
+
+	/* We make report rate RO, since the driver uses that to look for
+	 * resets.  We don't want someone faking us out by changing that
+	 * bit.
+	 */
+	__ATTR(configured, RMI_RO_ATTR,
+	       rmi_fn_01_configured_show, rmi_store_error),
+
+	/* Command register access. */
+	__ATTR(reset, RMI_WO_ATTR,
+	       rmi_show_error, rmi_fn_01_reset_store),
+
+	/* STatus register access. */
+	__ATTR(unconfigured, RMI_RO_ATTR,
+	       rmi_fn_01_unconfigured_show, rmi_store_error),
+	__ATTR(flashprog, RMI_RO_ATTR,
+	       rmi_fn_01_flashprog_show, rmi_store_error),
+	__ATTR(statuscode, RMI_RO_ATTR,
+	       rmi_fn_01_statuscode_show, rmi_store_error),
+};
+
+/* Utility routine to set the value of a bit field in a register. */
+int rmi_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.ctrl0.sleep_mode);
+}
+
+static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || !RMI_IS_VALID_SLEEPMODE(new_value)) {
+		dev_err(dev, "%s: Invalid sleep mode %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "Setting sleep mode to %ld.", new_value);
+	data->device_control.ctrl0.sleep_mode = new_value;
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write sleep mode, code %d.\n", retval);
+	return retval;
+}
+
+static ssize_t rmi_fn_01_nosleep_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+		data->device_control.ctrl0.nosleep);
+}
+
+static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf,
+				       size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 1) {
+		dev_err(dev, "%s: Invalid nosleep bit %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.ctrl0.nosleep = new_value;
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write nosleep bit.\n");
+	return retval;
+}
+
+static ssize_t rmi_fn_01_chargerinput_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.ctrl0.charger_input);
+}
+
+static ssize_t rmi_fn_01_chargerinput_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf,
+				       size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 1) {
+		dev_err(dev, "%s: Invalid chargerinput bit %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.ctrl0.charger_input = new_value;
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write chargerinput bit.\n");
+	return retval;
+}
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.ctrl0.report_rate);
+}
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf,
+				       size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 1) {
+		dev_err(dev, "%s: Invalid reportrate bit %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.ctrl0.report_rate = new_value;
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write reportrate bit.\n");
+	return retval;
+}
+
+static ssize_t rmi_fn_01_interrupt_enable_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f01_data *data;
+	int i, len, total_len = 0;
+	char *current_buf = buf;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	/* loop through each irq value and copy its
+	 * string representation into buf */
+	for (i = 0; i < data->irq_count; i++) {
+		int irq_reg;
+		int irq_shift;
+		int interrupt_enable;
+
+		irq_reg = i / 8;
+		irq_shift = i % 8;
+		interrupt_enable =
+		    ((data->device_control.interrupt_enable[irq_reg]
+			>> irq_shift) & 0x01);
+
+		/* get next irq value and write it to buf */
+		len = snprintf(current_buf, PAGE_SIZE - total_len,
+			"%u ", interrupt_enable);
+		/* bump up ptr to next location in buf if the
+		 * snprintf was valid.  Otherwise issue an error
+		 * and return. */
+		if (len > 0) {
+			current_buf += len;
+			total_len += len;
+		} else {
+			dev_err(dev, "%s: Failed to build interrupt_enable"
+				" 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_fn_01_interrupt_enable_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f01_data *data;
+	int i;
+	int irq_count = 0;
+	int retval = count;
+	int irq_reg = 0;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	for (i = 0; i < data->irq_count && *buf != 0;
+	     i++, buf += 2) {
+		int irq_shift;
+		int interrupt_enable;
+		int result;
+
+		irq_reg = i / 8;
+		irq_shift = i % 8;
+
+		/* get next interrupt mapping value and store and bump up to
+		 * point to next item in buf */
+		result = sscanf(buf, "%u", &interrupt_enable);
+		if ((result != 1) ||
+			(interrupt_enable != 0 && interrupt_enable != 1)) {
+			dev_err(dev,
+				"%s: Error - interrupt enable[%d]"
+				" is not a valid value 0x%x.\n",
+				__func__, i, interrupt_enable);
+			return -EINVAL;
+		}
+		if (interrupt_enable == 0) {
+			data->device_control.interrupt_enable[irq_reg] &=
+				(1 << irq_shift) ^ 0xFF;
+		} else
+			data->device_control.interrupt_enable[irq_reg] |=
+				(1 << irq_shift);
+		irq_count++;
+	}
+
+	/* Make sure the irq count matches */
+	if (irq_count != data->irq_count) {
+		dev_err(dev,
+			"%s: Error - interrupt enable count of %d"
+			" doesn't match device count of %d.\n",
+			 __func__, irq_count, data->irq_count);
+		return -EINVAL;
+	}
+
+	/* write back to the control register */
+	retval = rmi_write_block(fc->rmi_dev, data->interrupt_enable_addr,
+			data->device_control.interrupt_enable,
+			sizeof(u8)*(data->num_of_irq_regs));
+	if (retval < 0) {
+		dev_err(dev, "%s : Could not write interrupt_enable_store"
+			" to 0x%x\n", __func__, data->interrupt_enable_addr);
+		return retval;
+	}
+
+	return count;
+
+}
+
+static ssize_t rmi_fn_01_doze_interval_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.doze_interval);
+
+}
+
+static ssize_t rmi_fn_01_doze_interval_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	u16 ctrl_base_addr;
+
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 255) {
+		dev_err(dev, "%s: Invalid doze interval %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.doze_interval = new_value;
+	ctrl_base_addr = fc->fd.control_base_addr + sizeof(u8) +
+			(sizeof(u8)*(data->num_of_irq_regs));
+	dev_dbg(dev, "doze_interval store address %x, value %d",
+		ctrl_base_addr, data->device_control.doze_interval);
+
+	retval = rmi_write_block(fc->rmi_dev, data->doze_interval_addr,
+			&data->device_control.doze_interval,
+			sizeof(u8));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write doze interval.\n");
+	return retval;
+
+}
+
+static ssize_t rmi_fn_01_wakeup_threshold_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.wakeup_threshold);
+}
+
+static ssize_t rmi_fn_01_wakeup_threshold_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 255) {
+		dev_err(dev, "%s: Invalid wakeup threshold %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.doze_interval = new_value;
+	retval = rmi_write_block(fc->rmi_dev, data->wakeup_threshold_addr,
+			&data->device_control.wakeup_threshold,
+			sizeof(u8));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write wakeup threshold.\n");
+	return retval;
+
+}
+
+static ssize_t rmi_fn_01_doze_holdoff_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.doze_holdoff);
+
+}
+
+
+static ssize_t rmi_fn_01_doze_holdoff_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 255) {
+		dev_err(dev, "%s: Invalid doze holdoff %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.doze_interval = new_value;
+	retval = rmi_write_block(fc->rmi_dev, data->doze_holdoff_addr,
+			&data->device_control.doze_holdoff,
+			sizeof(u8));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write doze holdoff.\n");
+	return retval;
+
+}
+
+static ssize_t rmi_fn_01_configured_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.ctrl0.configured);
+}
+
+static ssize_t rmi_fn_01_unconfigured_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_status.unconfigured);
+}
+
+static ssize_t rmi_fn_01_flashprog_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_status.flash_prog);
+}
+
+static ssize_t rmi_fn_01_statuscode_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "0x%02x\n",
+			data->device_status.status_code);
+}
+
+/* why is this not done in init? */
+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;
+	int error;
+
+	error = rmi_f01_alloc_memory(fc, driver_data->num_of_irq_regs);
+	if (error < 0)
+		goto error_exit;
+
+	error = rmi_f01_initialize(fc);
+	if (error < 0)
+		goto error_exit;
+
+	error = rmi_f01_create_sysfs(fc);
+	if (error < 0)
+		goto error_exit;
+
+	return 0;
+
+ error_exit:
+	rmi_f01_free_memory(fc);
+
+	return error;
+}
+
+static int rmi_f01_alloc_memory(struct rmi_function_container *fc,
+	int num_of_irq_regs)
+{
+	struct f01_data *f01;
+
+	f01 = kzalloc(sizeof(struct f01_data), GFP_KERNEL);
+	if (!f01) {
+		dev_err(&fc->dev, "Failed to allocate fn_01_data.\n");
+		return -ENOMEM;
+	}
+
+	f01->device_control.interrupt_enable =
+		kzalloc(sizeof(u8)*(num_of_irq_regs), GFP_KERNEL);
+	if (!f01->device_control.interrupt_enable) {
+		kfree(f01);
+		return -ENOMEM;
+	}
+	fc->data = f01;
+
+	return 0;
+}
+
+static void rmi_f01_free_memory(struct rmi_function_container *fc)
+{
+	struct f01_data *f01 = fc->data;
+	kfree(f01->device_control.interrupt_enable);
+	kfree(fc->data);
+	fc->data = NULL;
+}
+
+static int rmi_f01_initialize(struct rmi_function_container *fc)
+{
+	u8 temp;
+	int retval;
+	u16 ctrl_base_addr;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_driver_data *driver_data = rmi_get_driverdata(rmi_dev);
+	struct f01_data *data = fc->data;
+	struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
+
+	/* Set the configured bit and (optionally) other important stuff
+	 * in the device control register. */
+	ctrl_base_addr = fc->fd.control_base_addr;
+	retval = rmi_read_block(rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read F01 control.\n");
+		return retval;
+	}
+	switch (pdata->power_management.nosleep) {
+	case RMI_F01_NOSLEEP_DEFAULT:
+		break;
+	case RMI_F01_NOSLEEP_OFF:
+		data->device_control.ctrl0.nosleep = 0;
+		break;
+	case RMI_F01_NOSLEEP_ON:
+		data->device_control.ctrl0.nosleep = 1;
+		break;
+	}
+	/* Sleep mode might be set as a hangover from a system crash or
+	 * reboot without power cycle.  If so, clear it so the sensor
+	 * is certain to function.
+	 */
+	if (data->device_control.ctrl0.sleep_mode != RMI_SLEEP_MODE_NORMAL) {
+		dev_warn(&fc->dev,
+			 "WARNING: Non-zero sleep mode found. Clearing...\n");
+		data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+	}
+
+	data->device_control.ctrl0.configured = 1;
+	retval = rmi_write_block(rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to write F01 control.\n");
+		return retval;
+	}
+
+	data->irq_count = driver_data->irq_count;
+	data->num_of_irq_regs = driver_data->num_of_irq_regs;
+	ctrl_base_addr += sizeof(union f01_device_control_0);
+
+	data->interrupt_enable_addr = ctrl_base_addr;
+	retval = rmi_read_block(rmi_dev, ctrl_base_addr,
+			data->device_control.interrupt_enable,
+			sizeof(u8) *(driver_data->num_of_irq_regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read F01 control interrupt enable register.\n");
+		goto error_exit;
+	}
+	ctrl_base_addr += driver_data->num_of_irq_regs;
+
+	/* dummy read in order to clear irqs */
+	retval = rmi_read(rmi_dev, fc->fd.data_base_addr + 1, &temp);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read Interrupt Status.\n");
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, fc->fd.query_base_addr,
+				data->basic_queries.regs,
+				sizeof(data->basic_queries.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read device query registers.\n");
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev,
+		fc->fd.query_base_addr + sizeof(data->basic_queries.regs),
+		data->product_id, RMI_PRODUCT_ID_LENGTH);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read product ID.\n");
+		return retval;
+	}
+	data->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
+	dev_info(&fc->dev, "found RMI device, manufacturer: %s, product: %s\n",
+		 data->basic_queries.manufacturer_id == 1 ?
+							"synaptics" : "unknown",
+		 data->product_id);
+
+	/* read control register */
+	if (data->basic_queries.has_adjustable_doze) {
+		data->doze_interval_addr = ctrl_base_addr;
+		ctrl_base_addr++;
+
+		if (pdata->power_management.doze_interval) {
+			data->device_control.doze_interval =
+				pdata->power_management.doze_interval;
+			retval = rmi_write(rmi_dev, data->doze_interval_addr,
+					data->device_control.doze_interval);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to configure F01 doze interval register.\n");
+				goto error_exit;
+			}
+		} else {
+			retval = rmi_read(rmi_dev, data->doze_interval_addr,
+					&data->device_control.doze_interval);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to read F01 doze interval register.\n");
+				goto error_exit;
+			}
+		}
+
+		data->wakeup_threshold_addr = ctrl_base_addr;
+		ctrl_base_addr++;
+
+		if (pdata->power_management.wakeup_threshold) {
+			data->device_control.wakeup_threshold =
+				pdata->power_management.wakeup_threshold;
+			retval = rmi_write(rmi_dev, data->wakeup_threshold_addr,
+					data->device_control.wakeup_threshold);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to configure F01 wakeup threshold register.\n");
+				goto error_exit;
+			}
+		} else {
+			retval = rmi_read(rmi_dev, data->wakeup_threshold_addr,
+					&data->device_control.wakeup_threshold);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to read F01 wakeup threshold register.\n");
+				goto error_exit;
+			}
+		}
+	}
+
+	if (data->basic_queries.has_adjustable_doze_holdoff) {
+		data->doze_holdoff_addr = ctrl_base_addr;
+		ctrl_base_addr++;
+
+		if (pdata->power_management.doze_holdoff) {
+			data->device_control.doze_holdoff =
+				pdata->power_management.doze_holdoff;
+			retval = rmi_write(rmi_dev, data->doze_holdoff_addr,
+					data->device_control.doze_holdoff);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to configure F01 "
+					"doze holdoff register.\n");
+				goto error_exit;
+			}
+		} else {
+			retval = rmi_read(rmi_dev, data->doze_holdoff_addr,
+					&data->device_control.doze_holdoff);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to read F01 doze"
+					" holdoff register.\n");
+				goto error_exit;
+			}
+		}
+	}
+
+	retval = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
+		data->device_status.regs, ARRAY_SIZE(data->device_status.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read device status.\n");
+		goto error_exit;
+	}
+
+	if (data->device_status.unconfigured) {
+		dev_err(&fc->dev,
+			"Device reset during configuration process, status: "
+			"%#02x!\n", data->device_status.status_code);
+		retval = -EINVAL;
+		goto error_exit;
+	}
+
+	return retval;
+
+ error_exit:
+	kfree(data);
+	return retval;
+}
+
+static int rmi_f01_create_sysfs(struct rmi_function_container *fc)
+{
+	int attr_count = 0;
+	int retval = 0;
+	struct f01_data *data = fc->data;
+
+	dev_dbg(&fc->dev, "Creating sysfs files.");
+	for (attr_count = 0; attr_count < ARRAY_SIZE(fn_01_attrs);
+			attr_count++) {
+		if (!strcmp("doze_interval", fn_01_attrs[attr_count].attr.name)
+			&& !data->basic_queries.has_lts) {
+			continue;
+		}
+		if (!strcmp("wakeup_threshold",
+			fn_01_attrs[attr_count].attr.name)
+			&& !data->basic_queries.has_adjustable_doze) {
+			continue;
+		}
+		if (!strcmp("doze_holdoff", fn_01_attrs[attr_count].attr.name)
+			&& !data->basic_queries.has_adjustable_doze_holdoff) {
+			continue;
+		}
+		retval = sysfs_create_file(&fc->dev.kobj,
+				      &fn_01_attrs[attr_count].attr);
+		if (retval < 0) {
+			dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+			       fn_01_attrs[attr_count].attr.name);
+			goto err_remove_sysfs;
+		}
+	}
+
+	return 0;
+
+err_remove_sysfs:
+	for (attr_count--; attr_count >= 0; attr_count--)
+		sysfs_remove_file(&fc->dev.kobj,
+				  &fn_01_attrs[attr_count].attr);
+
+	return retval;
+}
+
+static int rmi_f01_config(struct rmi_function_container *fc)
+{
+	struct f01_data *data = fc->data;
+	int retval;
+
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to write device_control.reg.\n");
+		return retval;
+	}
+
+	retval = rmi_write_block(fc->rmi_dev, data->interrupt_enable_addr,
+			data->device_control.interrupt_enable,
+			sizeof(u8)*(data->num_of_irq_regs));
+
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to write interrupt enable.\n");
+		return retval;
+	}
+	if (data->basic_queries.has_lts) {
+		retval = rmi_write_block(fc->rmi_dev, data->doze_interval_addr,
+				&data->device_control.doze_interval,
+				sizeof(u8));
+		if (retval < 0) {
+			dev_err(&fc->dev, "Failed to write doze interval.\n");
+			return retval;
+		}
+	}
+
+	if (data->basic_queries.has_adjustable_doze) {
+		retval = rmi_write_block(
+				fc->rmi_dev, data->wakeup_threshold_addr,
+				&data->device_control.wakeup_threshold,
+				sizeof(u8));
+		if (retval < 0) {
+			dev_err(&fc->dev, "Failed to write wakeup threshold.\n");
+			return retval;
+		}
+	}
+
+	if (data->basic_queries.has_adjustable_doze_holdoff) {
+		retval = rmi_write_block(fc->rmi_dev, data->doze_holdoff_addr,
+				&data->device_control.doze_holdoff,
+				sizeof(u8));
+		if (retval < 0) {
+			dev_err(&fc->dev, "Failed to write doze holdoff.\n");
+			return retval;
+		}
+	}
+	return 0;
+}
+
+static int rmi_f01_reset(struct rmi_function_container *fc)
+{
+	/*do nothing here */
+	return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int rmi_f01_suspend(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_driver_data *driver_data = rmi_get_driverdata(rmi_dev);
+	struct f01_data *data = driver_data->f01_container->data;
+	int retval = 0;
+
+	if (data->suspended)
+		return 0;
+
+	data->old_nosleep = data->device_control.ctrl0.nosleep;
+	data->device_control.ctrl0.nosleep = 0;
+	data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_SENSOR_SLEEP;
+
+	retval = rmi_write_block(rmi_dev,
+			driver_data->f01_container->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to write sleep mode. Code: %d.\n",
+			retval);
+		data->device_control.ctrl0.nosleep = data->old_nosleep;
+		data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+	} else {
+		data->suspended = true;
+		retval = 0;
+	}
+
+	return retval;
+}
+
+static int rmi_f01_resume(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_driver_data *driver_data = rmi_get_driverdata(rmi_dev);
+	struct f01_data *data = driver_data->f01_container->data;
+	int retval = 0;
+
+	if (!data->suspended)
+		return 0;
+
+	data->device_control.ctrl0.nosleep = data->old_nosleep;
+	data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+
+	retval = rmi_write_block(rmi_dev,
+			driver_data->f01_container->fd.control_base_addr,
+			data->device_control.ctrl0.regs,
+			ARRAY_SIZE(data->device_control.ctrl0.regs));
+	if (retval < 0)
+		dev_err(&fc->dev,
+			"Failed to restore normal operation. Code: %d.\n",
+			retval);
+	else {
+		data->suspended = false;
+		retval = 0;
+	}
+
+	return retval;
+}
+#endif /* CONFIG_PM */
+
+static int rmi_f01_init(struct rmi_function_container *fc)
+{
+	return 0;
+}
+
+static void rmi_f01_remove(struct rmi_function_container *fc)
+{
+	int attr_count;
+
+	for (attr_count = 0; attr_count < ARRAY_SIZE(fn_01_attrs);
+			attr_count++) {
+		sysfs_remove_file(&fc->dev.kobj, &fn_01_attrs[attr_count].attr);
+	}
+
+	rmi_f01_free_memory(fc);
+}
+
+static int rmi_f01_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct f01_data *data = fc->data;
+	int retval;
+
+	retval = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
+		data->device_status.regs, ARRAY_SIZE(data->device_status.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read device status, code: %d.\n",
+			retval);
+		return retval;
+	}
+	if (data->device_status.unconfigured) {
+		dev_warn(&fc->dev, "Device reset detected.\n");
+		retval = rmi_dev->driver->reset_handler(rmi_dev);
+		if (retval < 0)
+			return retval;
+	}
+	return 0;
+}
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x01,
+	.init = rmi_f01_init,
+	.config = rmi_f01_config,
+	.reset = rmi_f01_reset,
+	.attention = rmi_f01_attention,
+
+#ifdef	CONFIG_PM
+#if defined(CONFIG_HAS_EARLYSUSPEND) && \
+			!defined(CONFIG_RMI4_SPECIAL_EARLYSUSPEND)
+	.early_suspend = rmi_f01_suspend,
+	.late_resume = rmi_f01_resume,
+#else
+	.suspend = rmi_f01_suspend,
+	.resume = rmi_f01_resume,
+#endif  /* defined(CONFIG_HAS_EARLYSUSPEND) && !def... */
+#endif  /* CONFIG_PM */
+	.remove = rmi_f01_remove,
+};
+
+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");
+MODULE_VERSION(RMI_DRIVER_VERSION);
diff --git a/drivers/input/rmi4/rmi_f01.h b/drivers/input/rmi4/rmi_f01.h
new file mode 100644
index 0000000..8965c6f
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f01.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2012 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifndef _RMI_F01_H
+#define _RMI_F01_H
+
+#define RMI_PRODUCT_ID_LENGTH    10
+
+union f01_basic_queries {
+	struct {
+		u8 manufacturer_id:8;
+
+		u8 custom_map:1;
+		u8 non_compliant:1;
+		u8 has_lts:1;
+		u8 has_sensor_id:1;
+		u8 has_charger_input:1;
+		u8 has_adjustable_doze:1;
+		u8 has_adjustable_doze_holdoff:1;
+		u8 has_product_properties_2: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];
+};
+
+union f01_device_status {
+	struct {
+		u8 status_code:4;
+		u8 reserved:2;
+		u8 flash_prog:1;
+		u8 unconfigured:1;
+	};
+	u8 regs[1];
+};
+
+/* control register bits */
+#define RMI_SLEEP_MODE_NORMAL (0x00)
+#define RMI_SLEEP_MODE_SENSOR_SLEEP (0x01)
+#define RMI_SLEEP_MODE_RESERVED0 (0x02)
+#define RMI_SLEEP_MODE_RESERVED1 (0x03)
+
+#define RMI_IS_VALID_SLEEPMODE(mode) \
+	(mode >= RMI_SLEEP_MODE_NORMAL && mode <= RMI_SLEEP_MODE_RESERVED1)
+
+union f01_device_control_0 {
+	struct {
+		u8 sleep_mode:2;
+		u8 nosleep:1;
+		u8 reserved:2;
+		u8 charger_input:1;
+		u8 report_rate:1;
+		u8 configured:1;
+	};
+	u8 regs[1];
+};
+
+#endif

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 8/17] input: RMI4 F09 Built-In Self Test
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (6 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 7/17] input: RMI4 F01 device control Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-27 22:07   ` Linus Walleij
  2012-08-17 22:17 ` [RFC PATCH 9/17] input: RMI4 F11 multitouch sensing Christopher Heiny
                   ` (10 subsequent siblings)
  18 siblings, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_f09.c |  772 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 772 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f09.c b/drivers/input/rmi4/rmi_f09.c
new file mode 100644
index 0000000..ee3058e
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f09.c
@@ -0,0 +1,772 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+#define QUERY_BASE_INDEX 1
+
+/* 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 internal_limits:1;
+			u8 host_test_enable:1;
+		};
+		u8 f09_bist_query1;
+	};
+};
+
+struct f09_control {
+	union {
+		struct {
+			u8 test1_limit_low:8;
+			u8 test1_limit_high:8;
+			u8 test1_limit_diff:8;
+		};
+		u8 f09_control_test1[3];
+	};
+	union {
+		struct {
+			u8 test2_limit_low:8;
+			u8 test2_limit_high:8;
+			u8 test2_limit_diff:8;
+		};
+		u8 f09_control_test2[3];
+	};
+};
+
+struct f09_data {
+	u8 test_number_control;
+	u8 overall_bist_result;
+	u8 test_result1;
+	u8 test_result2;
+	u8 transmitter_number;
+
+	union {
+		struct {
+			u8 receiver_number:6;
+			u8 limit_failure_code:2;
+		};
+		u8 f09_bist_data2;
+	};
+};
+
+struct f09_cmd {
+	union {
+		struct {
+			u8 run_bist:1;
+		};
+		u8 f09_bist_cmd0;
+	};
+};
+
+struct rmi_fn_09_data {
+	struct f09_query query;
+	struct f09_data data;
+	struct f09_cmd cmd;
+	struct f09_control control;
+	signed char status;
+};
+
+
+static ssize_t rmi_f09_status_show(struct device *dev,
+				     struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_status_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+static ssize_t rmi_f09_limit_register_count_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_host_test_enable_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_host_test_enable_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count);
+
+static ssize_t rmi_f09_internal_limits_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_test_number_control_show(struct device *dev,
+				      struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_test_number_control_store(struct device *dev,
+				      struct device_attribute *attr,
+					const char *buf, size_t count);
+
+static ssize_t rmi_f09_run_bist_show(struct device *dev,
+				      struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_run_bist_store(struct device *dev,
+				      struct device_attribute *attr,
+					const char *buf, size_t count);
+
+static ssize_t rmi_f09_test_result1_show(struct device *dev,
+				      struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_test_result2_show(struct device *dev,
+				      struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_control_test1_show(struct device *dev,
+				      struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_control_test1_store(struct device *dev,
+				      struct device_attribute *attr,
+					const char *buf, size_t count);
+
+static ssize_t rmi_f09_control_test2_show(struct device *dev,
+				      struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_control_test2_store(struct device *dev,
+				      struct device_attribute *attr,
+					const char *buf, size_t count);
+
+static int rmi_f09_alloc_memory(struct rmi_function_container *fc);
+
+static void rmi_f09_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f09_initialize(struct rmi_function_container *fc);
+
+static int rmi_f09_config(struct rmi_function_container *fc);
+
+static int rmi_f09_reset(struct rmi_function_container *fc);
+
+static int rmi_f09_create_sysfs(struct rmi_function_container *fc);
+
+
+static struct device_attribute attrs[] = {
+	__ATTR(status, RMI_RW_ATTR,
+		   rmi_f09_status_show, rmi_f09_status_store),
+	__ATTR(limitRegisterCount, RMI_RO_ATTR,
+	       rmi_f09_limit_register_count_show, rmi_store_error),
+	__ATTR(hostTestEnable, RMI_RW_ATTR,
+	       rmi_f09_host_test_enable_show, rmi_f09_host_test_enable_store),
+	__ATTR(internalLimits, RMI_RO_ATTR,
+	       rmi_f09_internal_limits_show, rmi_store_error),
+	__ATTR(resultRegisterCount, RMI_RO_ATTR,
+	       rmi_f09_result_register_count_show, rmi_store_error),
+	__ATTR(overall_bist_result, RMI_RO_ATTR,
+	       rmi_f09_overall_bist_result_show, rmi_store_error),
+	__ATTR(test_number_control, RMI_RW_ATTR,
+	       rmi_f09_test_number_control_show,
+	       rmi_f09_test_number_control_store),
+	__ATTR(test_result1, RMI_RO_ATTR,
+	       rmi_f09_test_result1_show, rmi_store_error),
+	__ATTR(test_result2, RMI_RO_ATTR,
+	       rmi_f09_test_result2_show, rmi_store_error),
+	__ATTR(run_bist, RMI_RW_ATTR,
+	       rmi_f09_run_bist_show, rmi_f09_run_bist_store),
+	__ATTR(f09_control_test1, RMI_RW_ATTR,
+	       rmi_f09_control_test1_show, rmi_f09_control_test1_store),
+	__ATTR(f09_control_test2, RMI_RW_ATTR,
+	       rmi_f09_control_test2_show, rmi_f09_control_test2_store),
+};
+
+static int rmi_f09_init(struct rmi_function_container *fc)
+{
+	int rc;
+
+	dev_info(&fc->dev, "Intializing F09 values.");
+
+	rc = rmi_f09_alloc_memory(fc);
+	if (rc < 0)
+		goto error_exit;
+
+	rc = rmi_f09_initialize(fc);
+	if (rc < 0)
+		goto error_exit;
+
+	rc = rmi_f09_create_sysfs(fc);
+	if (rc < 0)
+		goto error_exit;
+
+	return 0;
+
+error_exit:
+	rmi_f09_free_memory(fc);
+
+	return rc;
+}
+
+static int rmi_f09_alloc_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_09_data *f09;
+
+	f09 = kzalloc(sizeof(struct rmi_fn_09_data), GFP_KERNEL);
+	if (!f09) {
+		dev_err(&fc->dev, "Failed to allocate rmi_fn_09_data.\n");
+		return -ENOMEM;
+	}
+	fc->data = f09;
+
+	return 0;
+}
+
+static void rmi_f09_free_memory(struct rmi_function_container *fc)
+{
+	kfree(fc->data);
+	fc->data = NULL;
+}
+
+static int rmi_f09_initialize(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 = fc->data;
+	u16 query_base_addr;
+	int rc;
+
+
+	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);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int rmi_f09_create_sysfs(struct rmi_function_container *fc)
+{
+	int attr_count = 0;
+	int rc;
+
+	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);
+			rc = -ENODEV;
+			goto err_remove_sysfs;
+		}
+	}
+
+	return 0;
+
+err_remove_sysfs:
+	for (attr_count--; attr_count >= 0; attr_count--)
+		sysfs_remove_file(&fc->dev.kobj,
+				  &attrs[attr_count].attr);
+
+	return rc;
+}
+
+static int rmi_f09_config(struct rmi_function_container *fc)
+{
+	/*we do nothing here. instead reset should notify the user.*/
+	return 0;
+}
+
+static int rmi_f09_reset(struct rmi_function_container *fc)
+{
+	struct  rmi_fn_09_data  *instance_data = fc->data;
+
+	instance_data->status = -ECONNRESET;
+
+	return 0;
+}
+
+static void rmi_f09_remove(struct rmi_function_container *fc)
+{
+	int attr_count;
+
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
+		sysfs_remove_file(&fc->dev.kobj,
+				  &attrs[attr_count].attr);
+
+	rmi_f09_free_memory(fc);
+}
+
+static int rmi_f09_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_fn_09_data *data = fc->data;
+	int error;
+	error = rmi_read_block(rmi_dev, fc->fd.command_base_addr,
+			(u8 *)&data->cmd, sizeof(data->cmd));
+
+	if (error < 0) {
+		dev_err(&fc->dev, "Failed to read command register.\n");
+		return error;
+	}
+	/* If the command register is cleared, meaning the value is 0 */
+	if (data->status == ECONNRESET) {
+		dev_warn(&rmi_dev->dev, "RESET occured: %#04x.\n",
+			data->status);
+	} else if (data->cmd.run_bist) {
+		dev_warn(&rmi_dev->dev,
+			"Command register not cleared: %#04x.\n",
+			data->cmd.run_bist);
+	}
+	return 0;
+}
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x09,
+	.init = rmi_f09_init,
+	.config = rmi_f09_config,
+	.attention = rmi_f09_attention,
+	.reset = rmi_f09_reset,
+	.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_status_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_09_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_f09_status_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_09_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	/* any write to status resets 1 */
+	instance_data->status = 0;
+
+	return 0;
+}
+
+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_host_test_enable_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.host_test_enable);
+}
+
+static ssize_t rmi_f09_host_test_enable_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const 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 - hostTestEnable has an invalid length.\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (new_value < 0 || new_value > 1) {
+		dev_err(dev, "%s: Invalid hostTestEnable bit %s.",
+			__func__, buf);
+		return -EINVAL;
+	}
+	data->query.host_test_enable = new_value;
+	result = rmi_write(fc->rmi_dev, fc->fd.query_base_addr,
+		data->query.host_test_enable);
+	if (result < 0) {
+		dev_err(dev, "%s: Could not write hostTestEnable to 0x%x\n",
+				__func__, fc->fd.query_base_addr);
+		return result;
+	}
+
+	return count;
+
+}
+
+static ssize_t rmi_f09_internal_limits_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.internal_limits);
+}
+
+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);
+}
+
+static ssize_t rmi_f09_overall_bist_result_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->data.overall_bist_result);
+}
+
+static ssize_t rmi_f09_test_result1_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->data.test_result1);
+}
+
+static ssize_t rmi_f09_test_result2_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->data.test_result2);
+}
+
+static ssize_t rmi_f09_run_bist_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->cmd.run_bist);
+}
+
+static ssize_t rmi_f09_run_bist_store(struct device *dev,
+				      struct device_attribute *attr,
+					const 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 - run_bist_store has an "
+		"invalid len.\n",
+		__func__);
+		return -EINVAL;
+	}
+
+	if (new_value < 0 || new_value > 1) {
+		dev_err(dev, "%s: Invalid run_bist bit %s.", __func__, buf);
+		return -EINVAL;
+	}
+	data->cmd.run_bist = new_value;
+	result = rmi_write(fc->rmi_dev, fc->fd.command_base_addr,
+		data->cmd.run_bist);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write run_bist_store to 0x%x\n",
+				__func__, fc->fd.command_base_addr);
+		return result;
+	}
+
+	return count;
+
+}
+
+static ssize_t rmi_f09_control_test1_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 %u %u\n",
+			data->control.test1_limit_low,
+			data->control.test1_limit_high,
+			data->control.test1_limit_diff);
+}
+
+static ssize_t rmi_f09_control_test1_store(struct device *dev,
+				      struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_09_data *data;
+	unsigned int new_low, new_high, new_diff;
+	int result;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	if (sscanf(buf, "%u %u %u", &new_low, &new_high, &new_diff) != 3) {
+		dev_err(dev,
+		"%s: Error - f09_control_test1_store has an "
+		"invalid len.\n",
+		__func__);
+		return -EINVAL;
+	}
+
+	if (new_low < 0 || new_low > 1 || new_high < 0 || new_high
+			|| new_diff < 0 || new_diff) {
+		dev_err(dev, "%s: Invalid f09_control_test1_diff bit %s.",
+			__func__, buf);
+		return -EINVAL;
+	}
+	data->control.test1_limit_low = new_low;
+	data->control.test1_limit_high = new_high;
+	data->control.test1_limit_diff = new_diff;
+	result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
+		data->control.test1_limit_low);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write f09_control_test1_limit_low to 0x%x\n",
+				__func__, fc->fd.control_base_addr);
+		return result;
+	}
+
+	result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
+		data->control.test1_limit_high);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write f09_control_test1_limit_high to 0x%x\n",
+				__func__, fc->fd.control_base_addr);
+		return result;
+	}
+
+	result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
+		data->control.test1_limit_diff);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write f09_control_test1_limit_diff to 0x%x\n",
+				__func__, fc->fd.control_base_addr);
+		return result;
+	}
+
+	return count;
+
+}
+
+static ssize_t rmi_f09_control_test2_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 %u %u\n",
+			data->control.test2_limit_low,
+			data->control.test2_limit_high,
+			data->control.test2_limit_diff);
+}
+
+static ssize_t rmi_f09_control_test2_store(struct device *dev,
+				      struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_09_data *data;
+	unsigned int new_low, new_high, new_diff;
+	int result;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	if (sscanf(buf, "%u %u %u", &new_low, &new_high, &new_diff) != 3) {
+		dev_err(dev,
+		"%s: Error - f09_control_test1_store has an "
+		"invalid len.\n",
+		__func__);
+		return -EINVAL;
+	}
+
+	if (new_low < 0 || new_low > 1 || new_high < 0 || new_high > 1 ||
+			new_diff < 0 || new_diff > 1) {
+		dev_err(dev, "%s: Invalid f09_control_test2_diff bit %s.",
+			__func__, buf);
+		return -EINVAL;
+	}
+	data->control.test2_limit_low = new_low;
+	data->control.test2_limit_high = new_high;
+	data->control.test2_limit_diff = new_diff;
+	result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
+		data->control.test2_limit_low);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write f09_control_test2_limit_low to 0x%x\n",
+				__func__, fc->fd.control_base_addr);
+		return result;
+	}
+
+	result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
+		data->control.test2_limit_high);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write f09_control_test2_limit_high to 0x%x\n",
+				__func__, fc->fd.control_base_addr);
+		return result;
+	}
+
+	result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
+		data->control.test2_limit_diff);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write f09_control_test2_limit_diff to 0x%x\n",
+				__func__, fc->fd.control_base_addr);
+		return result;
+	}
+
+	return count;
+
+}
+
+
+static ssize_t rmi_f09_test_number_control_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->data.test_number_control);
+}
+
+static ssize_t rmi_f09_test_number_control_store(struct device *dev,
+				      struct device_attribute *attr,
+					const 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 - test_number_control_store has an "
+		"invalid len.\n",
+		__func__);
+		return -EINVAL;
+	}
+
+	if (new_value < 0 || new_value > 1) {
+		dev_err(dev, "%s: Invalid test_number_control bit %s.",
+			__func__, buf);
+		return -EINVAL;
+	}
+	data->data.test_number_control = new_value;
+	result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
+		data->data.test_number_control);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write "
+				"test_number_control_store to 0x%x\n",
+				__func__, fc->fd.data_base_addr);
+		return result;
+	}
+
+	return 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");
+MODULE_VERSION(RMI_DRIVER_VERSION);

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 9/17] input: RMI4 F11 multitouch sensing
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (7 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 8/17] input: RMI4 F09 Built-In Self Test Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-27 22:50   ` Linus Walleij
  2012-08-17 22:17 ` [RFC PATCH 10/17] input: RM4 F17 Pointing sticks Christopher Heiny
                   ` (9 subsequent siblings)
  18 siblings, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_f11.c | 2439 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 2439 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
new file mode 100644
index 0000000..86d7693
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f11.c
@@ -0,0 +1,2439 @@
+/*
+ * Copyright (c) 2011,2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define FUNCTION_DATA f11_data
+#define FNUM 11
+
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#ifdef CONFIG_RMI4_F11_TYPEB
+#include <linux/mt.h>
+#endif
+#include <linux/rmi.h>
+#include "rmi_driver.h"
+
+#ifdef CONFIG_RMI4_DEBUG
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+/*#include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+#endif
+
+#define RESUME_REZERO (1 && defined(CONFIG_PM))
+#if RESUME_REZERO
+#include <linux/delay.h>
+#define DEFAULT_REZERO_WAIT_MS	40
+#endif
+
+#ifndef MT_TOOL_MAX
+#define MT_TOOL_MAX MT_TOOL_PEN
+#endif
+
+#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 FINGER_STATE_MASK	0x03
+#define GET_FINGER_STATE(f_states, i) \
+	((f_states[i / 4] >> (2 * (i % 4))) & FINGER_STATE_MASK)
+
+#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))
+#define INBOX(x, y, box) (x >= box.x && x < (box.x + box.width) \
+			&& y >= box.y && y < (box.y + box.height))
+
+#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_NAME_LENGTH 256
+
+/* Adding debugfs for flip, clip, offset and swap */
+#ifdef CONFIG_RMI4_DEBUG
+
+static int setup_debugfs(struct rmi_device *rmi_dev);
+static void teardown_debugfs(struct rmi_device *rmi_dev);
+#endif
+/* End adding debugfs */
+
+static ssize_t f11_relreport_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+
+static ssize_t f11_relreport_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+
+static ssize_t f11_maxPos_show(struct device *dev,
+				     struct device_attribute *attr, char *buf);
+
+static ssize_t f11_rezero_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+
+#if RESUME_REZERO
+static ssize_t f11_rezeroOnResume_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+
+static ssize_t f11_rezeroOnResume_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+static ssize_t f11_rezeroWait_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+
+static ssize_t f11_rezeroWait_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+#endif
+
+static void rmi_f11_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f11_initialize(struct rmi_function_container *fc);
+
+static int rmi_f11_create_sysfs(struct rmi_function_container *fc);
+
+static int rmi_f11_config(struct rmi_function_container *fc);
+
+static int rmi_f11_reset(struct rmi_function_container *fc);
+
+static int rmi_f11_register_devices(struct rmi_function_container *fc);
+
+static void rmi_f11_free_devices(struct rmi_function_container *fc);
+
+static void f11_set_abs_params(struct rmi_function_container *fc, int index);
+
+static struct device_attribute attrs[] = {
+	__ATTR(relreport, RMI_RW_ATTR, f11_relreport_show, f11_relreport_store),
+	__ATTR(maxPos, RMI_RO_ATTR, f11_maxPos_show, rmi_store_error),
+#if RESUME_REZERO
+	__ATTR(rezeroOnResume, RMI_RW_ATTR, f11_rezeroOnResume_show,
+		f11_rezeroOnResume_store),
+	__ATTR(rezeroWait, RMI_RW_ATTR, f11_rezeroWait_show,
+		f11_rezeroWait_store),
+#endif
+	__ATTR(rezero, RMI_WO_ATTR, rmi_show_error, 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;
+	};
+
+	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;
+	};
+};
+
+union f11_2d_query9 {
+	struct {
+		u8 has_pen:1;
+		u8 has_proximity:1;
+		u8 has_palm_det_sensitivity:1;
+		u8 has_suppress_on_palm_detect:1;
+		u8 has_two_pen_thresholds:1;
+		u8 has_contact_geometry:1;
+	};
+	u8 reg;
+};
+
+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];
+	};
+
+	union f11_2d_query9 query9;
+
+	union {
+		struct {
+			u8 nbr_touch_shapes:5;
+		};
+		u8 f11_2d_query10;
+	};
+};
+union f11_2d_ctrl0_9 {
+	struct {
+		/* F11_2D_Ctrl0 */
+		u8 reporting_mode:3;
+		u8 abs_pos_filt:1;
+		u8 rel_pos_filt:1;
+		u8 rel_ballistics:1;
+		u8 dribble:1;
+		u8 report_beyond_clip:1;
+		/* F11_2D_Ctrl1 */
+		u8 palm_detect_thres:4;
+		u8 motion_sensitivity:2;
+		u8 man_track_en:1;
+		u8 man_tracked_finger:1;
+		/* F11_2D_Ctrl2 and 3 */
+		u8 delta_x_threshold:8;
+		u8 delta_y_threshold:8;
+		/* F11_2D_Ctrl4 and 5 */
+		u8 velocity:8;
+		u8 acceleration:8;
+		/* F11_2D_Ctrl6 thru 9 */
+		u16 sensor_max_x_pos:12;
+		u8 ctrl7_reserved:4;
+		u16 sensor_max_y_pos:12;
+		u8 ctrl9_reserved:4;
+	};
+	struct {
+		u8 regs[10];
+		u16 address;
+	};
+};
+union f11_2d_ctrl10 {
+	struct {
+		u8 single_tap_int_enable:1;
+		u8 tap_n_hold_int_enable:1;
+		u8 double_tap_int_enable:1;
+		u8 early_tap_int_enable:1;
+		u8 flick_int_enable:1;
+		u8 press_int_enable:1;
+		u8 pinch_int_enable:1;
+	};
+	u8 reg;
+};
+
+union f11_2d_ctrl11 {
+	struct {
+		u8 palm_detect_int_enable:1;
+		u8 rotate_int_enable:1;
+		u8 touch_shape_int_enable:1;
+		u8 scroll_zone_int_enable:1;
+		u8 multi_finger_scroll_int_enable:1;
+	};
+	u8 reg;
+};
+
+union f11_2d_ctrl12 {
+	struct {
+		u8 sensor_map:7;
+		u8 xy_sel:1;
+	};
+	u8 reg;
+};
+
+union f11_2d_ctrl14 {
+	struct {
+		u8 sens_adjustment:5;
+		u8 hyst_adjustment:3;
+	};
+	u8 reg;
+};
+
+union f11_2d_ctrl15 {
+	struct {
+		u8 max_tap_time:8;
+	};
+	u8 reg;
+};
+
+union f11_2d_ctrl16 {
+	struct {
+		u8 min_press_time:8;
+	};
+	u8 reg;
+};
+
+union f11_2d_ctrl17 {
+	struct {
+		u8 max_tap_distance:8;
+	};
+	u8 reg;
+};
+
+union f11_2d_ctrl18_19 {
+	struct {
+		u8 min_flick_distance:8;
+		u8 min_flick_speed:8;
+	};
+	u8 reg[2];
+};
+
+union f11_2d_ctrl20_21 {
+	struct {
+		u8 pen_detect_enable:1;
+		u8 pen_jitter_filter_enable:1;
+		u8 ctrl20_reserved:6;
+		u8 pen_z_threshold:8;
+	};
+	u8 reg[2];
+};
+
+/* These are not accessible through sysfs yet. */
+union f11_2d_ctrl22_26 {
+	struct {
+		/* control 22 */
+		u8 proximity_detect_int_en:1;
+		u8 proximity_jitter_filter_en:1;
+		u8 f11_2d_ctrl6_b3__7:6;
+
+		/* control 23 */
+		u8 proximity_detection_z_threshold;
+
+		/* control 24 */
+		u8 proximity_delta_x_threshold;
+
+		/* control 25 */
+		u8 proximity_delta_y_threshold;
+
+		/* control 26 */
+		u8 proximity_delta_z_threshold;
+	};
+	u8 regs[5];
+};
+
+/* control 27 - haspalmdetectsensitivity or has suppressonpalmdetect */
+union f11_2d_ctrl27 {
+	struct {
+		u8 palm_detecy_sensitivity:4;
+		u8 suppress_on_palm_detect:1;
+		u8 f11_2d_ctrl27_b5__7:3;
+	};
+	u8 regs[1];
+};
+
+/* control 28 - has_multifingerscroll */
+union f11_2d_ctrl28 {
+	struct {
+		u8 multi_finger_scroll_mode:2;
+		u8 edge_motion_en:1;
+		u8 f11_2d_ctrl28b_3:1;
+		u8 multi_finger_scroll_momentum:4;
+	};
+	u8 regs[1];
+};
+
+/* control 29 & 30 - hasztuning */
+union f11_2d_ctrl29_30 {
+	struct {
+		u8 z_touch_threshold;
+		u8 z_touch_hysteresis;
+	};
+	struct {
+		u8 regs[2];
+		u16 address;
+	};
+};
+
+
+struct  f11_2d_ctrl {
+	union f11_2d_ctrl0_9 *ctrl0_9;
+	union f11_2d_ctrl10		*ctrl10;
+	union f11_2d_ctrl11		*ctrl11;
+	union f11_2d_ctrl12		*ctrl12;
+	u8				ctrl12_size;
+	union f11_2d_ctrl14		*ctrl14;
+	union f11_2d_ctrl15		*ctrl15;
+	union f11_2d_ctrl16		*ctrl16;
+	union f11_2d_ctrl17		*ctrl17;
+	union f11_2d_ctrl18_19		*ctrl18_19;
+	union f11_2d_ctrl20_21		*ctrl20_21;
+	union f11_2d_ctrl22_26 *ctrl22_26;
+	union f11_2d_ctrl27 *ctrl27;
+	union f11_2d_ctrl28 *ctrl28;
+	union f11_2d_ctrl29_30 *ctrl29_30;
+};
+
+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 {
+	u8				*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;
+	struct rmi_button_map virtualbutton_map;
+	char input_name[MAX_NAME_LENGTH];
+	char input_phys[MAX_NAME_LENGTH];
+	struct input_dev *input;
+	struct input_dev *mouse_input;
+};
+
+struct f11_data {
+	struct f11_2d_device_query dev_query;
+	struct f11_2d_ctrl dev_controls;
+	struct mutex dev_controls_mutex;
+#if	RESUME_REZERO
+	u16 rezero_wait_ms;
+	bool rezero_on_resume;
+#endif
+	struct f11_2d_sensor sensors[F11_MAX_NUM_OF_SENSORS];
+	bool type_b;
+};
+
+enum finger_state_values {
+	F11_NO_FINGER	= 0x00,
+	F11_PRESENT	= 0x01,
+	F11_INACCURATE	= 0x02,
+	F11_RESERVED	= 0x03
+};
+
+/* ctrl sysfs files */
+show_store_union_struct_prototype(abs_pos_filt)
+show_store_union_struct_prototype(z_touch_threshold)
+show_store_union_struct_prototype(z_touch_hysteresis)
+
+/* Adding debugfs for flip, clip, offset and swap */
+#ifdef CONFIG_RMI4_DEBUG
+
+struct f11_debugfs_data {
+	bool done;
+	struct rmi_device *rmi_dev;
+};
+
+static int debug_open(struct inode *inodep, struct file *filp)
+{
+	struct f11_debugfs_data *data;
+
+	data = kzalloc(sizeof(struct f11_debugfs_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->rmi_dev = inodep->i_private;
+	filp->private_data = data;
+	return 0;
+}
+
+static int debug_release(struct inode *inodep, struct file *filp)
+{
+	kfree(filp->private_data);
+	return 0;
+}
+
+#define FLIP_NAME "flip"
+#define CLIP_NAME "clip"
+#define OFFSET_NAME "offset"
+#define SWAP_NAME "swap"
+
+
+static ssize_t flip_read(struct file *filp, char __user *buffer, size_t size,
+		    loff_t *offset) {
+	struct f11_debugfs_data *dfs;
+	int retval;
+	char local_buf[size];
+	struct rmi_device_platform_data *data;
+	dfs = filp->private_data;
+	data = dfs->rmi_dev->phys->dev->platform_data;
+
+	if (dfs->done)
+		return 0;
+
+	dfs->done = 1;
+
+	retval = snprintf(local_buf, PAGE_SIZE, "%u %u\n",
+			data->axis_align.flip_x,
+			data->axis_align.flip_y);
+
+	if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+		return -EFAULT;
+
+	return retval;
+}
+
+static ssize_t flip_write(struct file *filp, const char __user *buffer,
+			   size_t size, loff_t *offset) {
+
+	struct f11_debugfs_data *dfs = filp->private_data;
+	int retval;
+	char local_buf[size];
+	unsigned int new_X;
+	unsigned int new_Y;
+	struct rmi_device_platform_data *data =
+			dfs->rmi_dev->phys->dev->platform_data;
+
+	retval = copy_from_user(local_buf, buffer, size);
+	if (retval)
+		return -EFAULT;
+
+	retval = sscanf(local_buf, "%u %u", &new_X, &new_Y);
+	if (retval != 2) {
+		dev_err(&dfs->rmi_dev->dev,
+			"Incorrect number of values provided for flip.");
+		return -EINVAL;
+	}
+	if (new_X < 0 || new_X > 1 || new_Y < 0 || new_Y > 1)
+		return -EINVAL;
+
+	dev_dbg(&dfs->rmi_dev->dev,
+		 "Setting flip to %u %u.\n", new_X, new_Y);
+	data->axis_align.flip_x = new_X;
+	data->axis_align.flip_y = new_Y;
+
+	return size;
+}
+
+static const struct file_operations flip_fops = {
+	.owner = THIS_MODULE,
+	.open = debug_open,
+	.release = debug_release,
+	.read = flip_read,
+	.write = flip_write,
+};
+
+
+static ssize_t offset_read(struct file *filp, char __user *buffer, size_t size,
+		    loff_t *offset) {
+
+	struct f11_debugfs_data *dfs;
+	int retval;
+	char local_buf[size];
+	struct rmi_device_platform_data *data;
+	dfs = filp->private_data;
+	data = dfs->rmi_dev->phys->dev->platform_data;
+
+	if (dfs->done)
+		return 0;
+
+	dfs->done = 1;
+	retval = snprintf(local_buf, PAGE_SIZE, "%u %u\n",
+			data->axis_align.offset_X,
+			data->axis_align.offset_Y);
+
+	if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+		return -EFAULT;
+
+	return retval;
+}
+
+static ssize_t offset_write(struct file *filp, const char __user *buffer,
+			   size_t size, loff_t *offset)
+{
+	struct f11_debugfs_data *dfs = filp->private_data;
+	int retval;
+	char local_buf[size];
+	int new_X;
+	int new_Y;
+	struct rmi_device_platform_data *data =
+			dfs->rmi_dev->phys->dev->platform_data;
+
+	retval = copy_from_user(local_buf, buffer, size);
+	if (retval)
+		return -EFAULT;
+	retval = sscanf(local_buf, "%u %u", &new_X, &new_Y);
+	if (retval != 2) {
+		dev_err(&dfs->rmi_dev->dev,
+			"Incorrect number of values provided for offset.");
+		return -EINVAL;
+	}
+
+	dev_dbg(&dfs->rmi_dev->dev,
+		 "Setting offset to %u %u.\n", new_X, new_Y);
+	data->axis_align.offset_X = new_X;
+	data->axis_align.offset_Y = new_Y;
+
+	return size;
+}
+
+static const struct file_operations offset_fops = {
+	.owner = THIS_MODULE,
+	.open = debug_open,
+	.release = debug_release,
+	.read = offset_read,
+	.write = offset_write,
+};
+
+static ssize_t clip_read(struct file *filp, char __user *buffer, size_t size,
+		    loff_t *offset) {
+
+	struct f11_debugfs_data *dfs;
+	int retval;
+	char local_buf[size];
+	struct rmi_device_platform_data *data;
+	dfs = filp->private_data;
+	data = dfs->rmi_dev->phys->dev->platform_data;
+
+	if (dfs->done)
+		return 0;
+
+	dfs->done = 1;
+
+	retval = snprintf(local_buf, PAGE_SIZE, "%u %u %u %u\n",
+			data->axis_align.clip_X_low,
+			data->axis_align.clip_X_high,
+			data->axis_align.clip_Y_low,
+			data->axis_align.clip_Y_high);
+
+	if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+		return -EFAULT;
+
+	return retval;
+}
+
+static ssize_t clip_write(struct file *filp, const char __user *buffer,
+			   size_t size, loff_t *offset)
+{
+	struct f11_debugfs_data *dfs = filp->private_data;
+	int retval;
+	char local_buf[size];
+	unsigned int new_X_low, new_X_high, new_Y_low, new_Y_high;
+
+	struct rmi_device_platform_data *data =
+			dfs->rmi_dev->phys->dev->platform_data;
+
+	retval = copy_from_user(local_buf, buffer, size);
+	if (retval)
+		return -EFAULT;
+
+	retval = sscanf(local_buf, "%u %u %u %u",
+		&new_X_low, &new_X_high, &new_Y_low, &new_Y_high);
+	if (retval != 4) {
+		dev_err(&dfs->rmi_dev->dev,
+			"Incorrect number of values provided for clip.");
+		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;
+
+	dev_dbg(&dfs->rmi_dev->dev,
+		 "Setting clip to %u %u %u %u.\n", new_X_low, new_X_high,
+			new_Y_low, new_Y_high);
+
+	data->axis_align.clip_X_low = new_X_low;
+	data->axis_align.clip_X_high = new_X_high;
+	data->axis_align.clip_Y_low = new_Y_low;
+	data->axis_align.clip_Y_high = new_Y_high;
+
+	return size;
+}
+
+static const struct file_operations clip_fops = {
+	.owner = THIS_MODULE,
+	.open = debug_open,
+	.release = debug_release,
+	.read = clip_read,
+	.write = clip_write,
+};
+
+static ssize_t swap_read(struct file *filp, char __user *buffer, size_t size,
+		    loff_t *offset) {
+
+	struct f11_debugfs_data *dfs;
+	int retval;
+	char local_buf[size];
+	struct rmi_device_platform_data *data;
+	dfs = filp->private_data;
+	data = dfs->rmi_dev->phys->dev->platform_data;
+
+	if (dfs->done)
+		return 0;
+
+	dfs->done = 1;
+
+	retval = snprintf(local_buf, PAGE_SIZE, "%u\n",
+			data->axis_align.swap_axes);
+
+	if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+		return -EFAULT;
+
+	return retval;
+}
+
+static ssize_t swap_write(struct file *filp, const char __user *buffer,
+			   size_t size, loff_t *offset)
+{
+	struct f11_debugfs_data *dfs = filp->private_data;
+	struct rmi_device_platform_data *data =
+			dfs->rmi_dev->phys->dev->platform_data;
+
+	int retval;
+	char local_buf[size];
+	int newSwap;
+
+	if (!dfs->rmi_dev->debugfs_root)
+		return -ENODEV;
+
+	retval = copy_from_user(local_buf, buffer, size);
+	if (retval)
+		return -EFAULT;
+	retval = sscanf(local_buf, "%u", &newSwap);
+	if (retval != 1) {
+		dev_err(&dfs->rmi_dev->dev,
+			"Incorrect number of values provided for swap.");
+		return -EINVAL;
+	}
+	if (newSwap < 0 || newSwap > 1)
+		return -EINVAL;
+
+	data->axis_align.swap_axes = newSwap;
+	return size;
+}
+
+static const struct file_operations swap_fops = {
+	.owner = THIS_MODULE,
+	.open = debug_open,
+	.release = debug_release,
+	.read = swap_read,
+	.write = swap_write,
+};
+
+static int setup_debugfs(struct rmi_device *rmi_dev)
+{
+	int retval = 0;
+	struct rmi_device_platform_data *data;
+	data = rmi_dev->phys->dev->platform_data;
+
+	if (!rmi_dev->debugfs_root)
+		return -ENODEV;
+
+	data->axis_align.debugfs_flip
+		= debugfs_create_file(FLIP_NAME, RMI_RW_ATTR,
+				rmi_dev->debugfs_root, rmi_dev, &flip_fops);
+	if (!data->axis_align.debugfs_flip
+		|| IS_ERR(data->axis_align.debugfs_flip)) {
+		dev_warn(&rmi_dev->dev, "Failed to create debugfs flip.\n");
+		data->axis_align.debugfs_flip = NULL;
+	}
+
+	data->axis_align.debugfs_clip
+		= debugfs_create_file(CLIP_NAME, RMI_RW_ATTR,
+				rmi_dev->debugfs_root, rmi_dev, &clip_fops);
+	if (!data->axis_align.debugfs_clip
+		|| IS_ERR(data->axis_align.debugfs_clip)) {
+		dev_warn(&rmi_dev->dev, "Failed to create debugfs clip.\n");
+		data->axis_align.debugfs_clip = NULL;
+	}
+
+	data->axis_align.debugfs_offset
+		= debugfs_create_file(OFFSET_NAME, RMI_RW_ATTR,
+				rmi_dev->debugfs_root, rmi_dev, &offset_fops);
+	if (!data->axis_align.debugfs_offset
+		|| IS_ERR(data->axis_align.debugfs_offset)) {
+		dev_warn(&rmi_dev->dev, "Failed to create debugfs offset.\n");
+		data->axis_align.debugfs_offset = NULL;
+	}
+
+	data->axis_align.debugfs_swap
+		= debugfs_create_file(SWAP_NAME, RMI_RW_ATTR,
+				rmi_dev->debugfs_root, rmi_dev, &swap_fops);
+	if (!data->axis_align.debugfs_swap
+		|| IS_ERR(data->axis_align.debugfs_swap)) {
+		dev_warn(&rmi_dev->dev, "Failed to create debugfs swap.\n");
+		data->axis_align.debugfs_swap = NULL;
+	}
+
+	return retval;
+}
+
+static void teardown_debugfs(struct rmi_device *rmi_dev)
+{
+	struct rmi_device_platform_data *data;
+	data = rmi_dev->phys->dev->platform_data;
+
+	if (!data->axis_align.debugfs_flip)
+		debugfs_remove(data->axis_align.debugfs_flip);
+
+	if (!data->axis_align.debugfs_clip)
+		debugfs_remove(data->axis_align.debugfs_clip);
+
+	if (!data->axis_align.debugfs_offset)
+		debugfs_remove(data->axis_align.debugfs_offset);
+
+	if (!data->axis_align.debugfs_swap)
+		debugfs_remove(data->axis_align.debugfs_swap);
+}
+
+#endif
+/* End adding debugfs */
+
+/* This is a group in case we add the other ctrls. */
+static struct attribute *attrs_ctrl0[] = {
+	attrify(abs_pos_filt),
+	NULL
+};
+static struct attribute_group attrs_control0 = GROUP(attrs_ctrl0);
+
+static struct attribute *attrs_ctrl29_30[] = {
+	attrify(z_touch_threshold),
+	attrify(z_touch_hysteresis),
+	NULL
+};
+static struct attribute_group attrs_control29_30 = GROUP(attrs_ctrl29_30);
+
+/** F11_INACCURATE state is overloaded to indicate pen present. */
+#define F11_PEN F11_INACCURATE
+
+static int get_tool_type(struct f11_2d_sensor *sensor, u8 finger_state)
+{
+#ifdef	CONFIG_RMI4_F11_PEN
+	if (sensor->sens_query.query9.has_pen && finger_state == F11_PEN)
+		return MT_TOOL_PEN;
+#endif
+	return MT_TOOL_FINGER;
+}
+
+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_data *f11,
+				   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);
+#ifndef CONFIG_RMI4_F11_PEN
+	/* Some UIs ignore W of zero, so we fudge it to 1 for pens. */
+	if (get_tool_type(sensor, finger_state) == MT_TOOL_PEN) {
+		w_max = max(1, w_max);
+		w_min = max(1, w_min);
+	}
+#endif
+
+#ifdef CONFIG_RMI4_F11_TYPEB
+	if (f11->type_b) {
+		input_mt_slot(sensor->input, n_finger);
+		input_mt_report_slot_state(sensor->input,
+					   get_tool_type(sensor, finger_state),
+					   finger_state);
+	} else
+		input_report_abs(sensor->input, ABS_MT_TOOL_TYPE,
+				 get_tool_type(sensor, finger_state));
+#else
+	input_report_abs(sensor->input, ABS_MT_TOOL_TYPE,
+				get_tool_type(sensor, finger_state));
+#endif
+
+#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);
+	input_report_abs(sensor->input, ABS_MT_TOOL_TYPE,
+				get_tool_type(sensor, finger_state));
+
+	/* MT sync between fingers */
+#ifdef CONFIG_RMI4_F11_TYPEB
+	if (!f11->type_b)
+		input_mt_sync(sensor->input);
+#else
+	input_mt_sync(sensor->input);
+#endif
+	sensor->finger_tracker[n_finger] = finger_state;
+}
+
+#ifdef CONFIG_RMI4_VIRTUAL_BUTTON
+static int rmi_f11_virtual_button_handler(struct f11_2d_sensor *sensor)
+{
+	int i;
+	int x;
+	int y;
+	struct rmi_button_map *virtualbutton_map;
+
+	if (sensor->sens_query.has_gestures &&
+		sensor->data.gest_1->single_tap) {
+		virtualbutton_map = &sensor->virtualbutton_map;
+		x = ((sensor->data.abs_pos[0].x_msb << 4) |
+			sensor->data.abs_pos[0].x_lsb);
+		y = ((sensor->data.abs_pos[0].y_msb << 4) |
+			sensor->data.abs_pos[0].y_lsb);
+		for (i = 0; i < virtualbutton_map->buttons; i++) {
+			if (INBOX(x, y, virtualbutton_map->map[i])) {
+				input_report_key(sensor->input,
+					virtualbutton_map->map[i].code, 1);
+				input_report_key(sensor->input,
+					virtualbutton_map->map[i].code, 0);
+				input_sync(sensor->input);
+				return 0;
+			}
+		}
+	}
+	return 0;
+}
+#else
+#define rmi_f11_virtual_button_handler(sensor)
+#endif
+static void rmi_f11_finger_handler(struct f11_data *f11,
+				   struct f11_2d_sensor *sensor)
+{
+	const u8 *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 = GET_FINGER_STATE(f_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(f11, 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 int 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 = 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 void f11_free_control_regs(struct f11_2d_ctrl *ctrl)
+{
+	kfree(ctrl->ctrl10);
+	kfree(ctrl->ctrl11);
+	kfree(ctrl->ctrl14);
+	kfree(ctrl->ctrl15);
+	kfree(ctrl->ctrl16);
+	kfree(ctrl->ctrl17);
+	kfree(ctrl->ctrl18_19);
+	kfree(ctrl->ctrl20_21);
+	kfree(ctrl->ctrl22_26);
+	kfree(ctrl->ctrl27);
+	kfree(ctrl->ctrl28);
+	kfree(ctrl->ctrl29_30);
+	ctrl->ctrl10 = NULL;
+	ctrl->ctrl11 = NULL;
+	ctrl->ctrl14 = NULL;
+	ctrl->ctrl15 = NULL;
+	ctrl->ctrl16 = NULL;
+	ctrl->ctrl17 = NULL;
+	ctrl->ctrl18_19 = NULL;
+	ctrl->ctrl20_21 = NULL;
+	ctrl->ctrl22_26 = NULL;
+	ctrl->ctrl27 = NULL;
+	ctrl->ctrl28 = NULL;
+	ctrl->ctrl29_30 = NULL;
+}
+
+static int f11_read_control_regs(struct rmi_device *rmi_dev,
+					   struct f11_2d_ctrl *ctrl,
+					   u16 ctrl_base_addr) {
+	u16 read_address = ctrl_base_addr;
+	int error = 0;
+
+	ctrl->ctrl0_9->address = read_address;
+	error = rmi_read_block(rmi_dev, read_address, ctrl->ctrl0_9->regs,
+		sizeof(ctrl->ctrl0_9->regs));
+	if (error < 0) {
+		dev_err(&rmi_dev->dev,
+			"Failed to read F11 ctrl0, code: %d.\n", error);
+		return error;
+	}
+	read_address = read_address + sizeof(ctrl->ctrl0_9->regs);
+
+	if (ctrl->ctrl10) {
+		error = rmi_read_block(rmi_dev, read_address,
+			&ctrl->ctrl10->reg, sizeof(union f11_2d_ctrl10));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read F11 ctrl10, code: %d.\n",
+				error);
+			return error;
+		}
+		read_address = read_address + sizeof(union f11_2d_ctrl10);
+	}
+
+	if (ctrl->ctrl11) {
+		error = rmi_read_block(rmi_dev, read_address,
+			&ctrl->ctrl11->reg, sizeof(union f11_2d_ctrl11));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read F11 ctrl11, code: %d.\n",
+				error);
+			return error;
+		}
+		read_address = read_address + sizeof(union f11_2d_ctrl11);
+	}
+
+	if (ctrl->ctrl14) {
+		error = rmi_read_block(rmi_dev, read_address,
+			&ctrl->ctrl14->reg, sizeof(union f11_2d_ctrl14));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read F11 ctrl14, code: %d.\n",
+				error);
+			return error;
+		}
+		read_address = read_address + sizeof(union f11_2d_ctrl14);
+	}
+
+	if (ctrl->ctrl15) {
+		error = rmi_read_block(rmi_dev, read_address,
+			&ctrl->ctrl15->reg, sizeof(union f11_2d_ctrl15));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read F11 ctrl15, code: %d.\n",
+				error);
+			return error;
+		}
+		read_address = read_address + sizeof(union f11_2d_ctrl15);
+	}
+
+	if (ctrl->ctrl16) {
+		error = rmi_read_block(rmi_dev, read_address,
+			&ctrl->ctrl16->reg, sizeof(union f11_2d_ctrl16));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read F11 ctrl16, code: %d.\n",
+				error);
+			return error;
+		}
+		read_address = read_address + sizeof(union f11_2d_ctrl16);
+	}
+
+	if (ctrl->ctrl17) {
+		error = rmi_read_block(rmi_dev, read_address,
+			&ctrl->ctrl17->reg, sizeof(union f11_2d_ctrl17));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read F11 ctrl17, code: %d.\n",
+				error);
+			return error;
+		}
+		read_address = read_address + sizeof(union f11_2d_ctrl17);
+	}
+
+	if (ctrl->ctrl18_19) {
+		error = rmi_read_block(rmi_dev, read_address,
+			ctrl->ctrl18_19->reg, sizeof(union f11_2d_ctrl18_19));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read F11 ctrl18_19, code: %d.\n",
+				error);
+			return error;
+		}
+		read_address = read_address + sizeof(union f11_2d_ctrl18_19);
+	}
+
+	if (ctrl->ctrl20_21) {
+		error = rmi_read_block(rmi_dev, read_address,
+			ctrl->ctrl20_21->reg, sizeof(union f11_2d_ctrl20_21));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read F11 ctrl20_21, code: %d.\n",
+				error);
+			return error;
+		}
+		read_address = read_address + sizeof(union f11_2d_ctrl20_21);
+	}
+
+	if (ctrl->ctrl22_26) {
+		error = rmi_read_block(rmi_dev, read_address,
+			ctrl->ctrl22_26->regs, sizeof(union f11_2d_ctrl22_26));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read F11 ctrl22_26, code: %d.\n",
+				error);
+			return error;
+		}
+		read_address = read_address + sizeof(union f11_2d_ctrl22_26);
+	}
+
+	if (ctrl->ctrl27) {
+		error = rmi_read_block(rmi_dev, read_address,
+			ctrl->ctrl27->regs, sizeof(union f11_2d_ctrl27));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read F11 ctrl27, code: %d.\n",
+				error);
+			return error;
+		}
+		read_address = read_address + sizeof(union f11_2d_ctrl27);
+	}
+
+	if (ctrl->ctrl28) {
+		error = rmi_read_block(rmi_dev, read_address,
+			ctrl->ctrl28->regs, sizeof(union f11_2d_ctrl28));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read F11 ctrl28, code: %d.\n",
+				error);
+			return error;
+		}
+		read_address = read_address + sizeof(union f11_2d_ctrl28);
+	}
+
+	if (ctrl->ctrl29_30) {
+		ctrl->ctrl29_30->address = read_address;
+		error = rmi_read_block(rmi_dev, read_address,
+			ctrl->ctrl29_30->regs, sizeof(ctrl->ctrl29_30->regs));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read F11 ctrl29_30, code: %d.\n",
+				error);
+			return error;
+		}
+		read_address = read_address + sizeof(ctrl->ctrl29_30->regs);
+	}
+	return 0;
+}
+
+static int f11_allocate_control_regs(struct rmi_device *rmi_dev,
+				struct f11_2d_device_query *device_query,
+				struct f11_2d_sensor_query *sensor_query,
+				struct f11_2d_ctrl *ctrl,
+				u16 ctrl_base_addr) {
+
+	int error = 0;
+	ctrl->ctrl0_9 = kzalloc(sizeof(union f11_2d_ctrl0_9),
+				       GFP_KERNEL);
+	if (!ctrl->ctrl0_9) {
+		error = -ENOMEM;
+		goto error_exit;
+	}
+	if (sensor_query->f11_2d_query7__8[0]) {
+		ctrl->ctrl10 = kzalloc(sizeof(union f11_2d_ctrl10),
+				       GFP_KERNEL);
+		if (!ctrl->ctrl10) {
+			error = -ENOMEM;
+			goto error_exit;
+		}
+	}
+
+	if (sensor_query->f11_2d_query7__8[1]) {
+		ctrl->ctrl11 = kzalloc(sizeof(union f11_2d_ctrl11),
+				       GFP_KERNEL);
+		if (!ctrl->ctrl11) {
+			error = -ENOMEM;
+			goto error_exit;
+		}
+	}
+
+	if (device_query->has_query9 && sensor_query->query9.has_pen) {
+		ctrl->ctrl20_21 = kzalloc(sizeof(union f11_2d_ctrl20_21),
+					  GFP_KERNEL);
+		if (!ctrl->ctrl20_21) {
+			error = -ENOMEM;
+			goto error_exit;
+		}
+	}
+
+	if (device_query->has_query9 && sensor_query->query9.has_proximity) {
+		ctrl->ctrl22_26 = kzalloc(sizeof(union f11_2d_ctrl22_26),
+					  GFP_KERNEL);
+		if (!ctrl->ctrl22_26) {
+			error = -ENOMEM;
+			goto error_exit;
+		}
+	}
+
+	if (device_query->has_query9 &&
+		(sensor_query->query9.has_palm_det_sensitivity ||
+		sensor_query->query9.has_suppress_on_palm_detect)) {
+		ctrl->ctrl27 = kzalloc(sizeof(union f11_2d_ctrl27),
+					  GFP_KERNEL);
+		if (!ctrl->ctrl27) {
+			error = -ENOMEM;
+			goto error_exit;
+		}
+	}
+
+	if (sensor_query->has_multi_finger_scroll) {
+		ctrl->ctrl28 = kzalloc(sizeof(union f11_2d_ctrl28),
+					  GFP_KERNEL);
+		if (!ctrl->ctrl28) {
+			error = -ENOMEM;
+			goto error_exit;
+		}
+	}
+
+	if (device_query->has_query11 && device_query->has_z_tuning) {
+		ctrl->ctrl29_30 = kzalloc(sizeof(union f11_2d_ctrl29_30),
+					  GFP_KERNEL);
+		if (!ctrl->ctrl29_30) {
+			error = -ENOMEM;
+			goto error_exit;
+		}
+	}
+
+	return f11_read_control_regs(rmi_dev, ctrl, ctrl_base_addr);
+
+error_exit:
+	f11_free_control_regs(ctrl);
+	return error;
+}
+
+static int f11_write_control_regs(struct rmi_device *rmi_dev,
+					struct f11_2d_sensor_query *query,
+					struct f11_2d_ctrl *ctrl,
+					u16 ctrl_base_addr)
+{
+	u16 write_address = ctrl_base_addr;
+	int error;
+
+	error = rmi_write_block(rmi_dev, write_address,
+				ctrl->ctrl0_9->regs,
+				 sizeof(ctrl->ctrl0_9->regs));
+	if (error < 0)
+		return error;
+	write_address += sizeof(ctrl->ctrl0_9);
+
+	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->ctrl14->reg, 1);
+		if (error < 0)
+			return error;
+		write_address++;
+	}
+
+	if (ctrl->ctrl15) {
+		error = rmi_write_block(rmi_dev, write_address,
+				&ctrl->ctrl15->reg, 1);
+		if (error < 0)
+			return error;
+		write_address++;
+	}
+
+	if (ctrl->ctrl16) {
+		error = rmi_write_block(rmi_dev, write_address,
+				&ctrl->ctrl16->reg, 1);
+		if (error < 0)
+			return error;
+		write_address++;
+	}
+
+	if (ctrl->ctrl17) {
+		error = rmi_write_block(rmi_dev, write_address,
+				&ctrl->ctrl17->reg, 1);
+		if (error < 0)
+			return error;
+		write_address++;
+	}
+
+	if (ctrl->ctrl18_19) {
+		error = rmi_write_block(rmi_dev, write_address,
+			ctrl->ctrl18_19->reg, sizeof(union f11_2d_ctrl18_19));
+		if (error < 0)
+			return error;
+		write_address += sizeof(union f11_2d_ctrl18_19);
+	}
+
+	if (ctrl->ctrl20_21) {
+		error = rmi_write_block(rmi_dev, write_address,
+					ctrl->ctrl20_21->reg,
+					sizeof(union f11_2d_ctrl20_21));
+		if (error < 0)
+			return error;
+		write_address += sizeof(union f11_2d_ctrl20_21);
+	}
+
+	if (ctrl->ctrl22_26) {
+		error = rmi_write_block(rmi_dev, write_address,
+					ctrl->ctrl22_26->regs,
+					sizeof(union f11_2d_ctrl22_26));
+		if (error < 0)
+			return error;
+		write_address += sizeof(union f11_2d_ctrl22_26);
+	}
+
+	if (ctrl->ctrl27) {
+		error = rmi_write_block(rmi_dev, write_address,
+					ctrl->ctrl27->regs,
+					sizeof(union f11_2d_ctrl27));
+		if (error < 0)
+			return error;
+		write_address += sizeof(union f11_2d_ctrl27);
+	}
+
+	if (ctrl->ctrl28) {
+		error = rmi_write_block(rmi_dev, write_address,
+					ctrl->ctrl28->regs,
+					sizeof(union f11_2d_ctrl28));
+		if (error < 0)
+			return error;
+		write_address += sizeof(union f11_2d_ctrl28);
+	}
+
+	if (ctrl->ctrl29_30) {
+		error = rmi_write_block(rmi_dev, write_address,
+					ctrl->ctrl29_30->regs,
+					sizeof(union f11_2d_ctrl29_30));
+		if (error < 0)
+			return error;
+		write_address += sizeof(union f11_2d_ctrl29_30);
+	}
+
+	return 0;
+}
+
+static int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev,
+			struct f11_2d_sensor_query *query, u16 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 *f11 = fc->data;
+	struct f11_2d_sensor *sensor = &f11->sensors[index];
+	struct input_dev *input = sensor->input;
+	int device_x_max =
+		f11->dev_controls.ctrl0_9->sensor_max_x_pos;
+	int device_y_max =
+		f11->dev_controls.ctrl0_9->sensor_max_y_pos;
+	int x_min, x_max, y_min, y_max;
+	if (sensor->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 = sensor->axis_align.clip_X_low;
+	if (sensor->axis_align.clip_X_high)
+		x_max = min((int) device_x_max,
+			sensor->axis_align.clip_X_high);
+	else
+		x_max = device_x_max;
+
+	y_min = sensor->axis_align.clip_Y_low;
+	if (sensor->axis_align.clip_Y_high)
+		y_max = min((int) device_y_max,
+			sensor->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);
+#ifdef CONFIG_RMI4_F11_TYPEB
+	if (f11->type_b)
+		input_mt_init_slots(input, sensor->nbr_fingers);
+#endif
+#ifdef	CONFIG_RMI4_F11_PEN
+	if (sensor->sens_query.query9.has_pen)
+		input_set_abs_params(input, ABS_MT_TOOL_TYPE,
+				     0, MT_TOOL_MAX, 0, 0);
+	else
+		input_set_abs_params(input, ABS_MT_TOOL_TYPE,
+				     0, MT_TOOL_MAX, 0, 0);
+#else
+	input_set_abs_params(input, ABS_MT_TOOL_TYPE, 0, MT_TOOL_FINGER, 0, 0);
+#endif
+}
+
+static int rmi_f11_init(struct rmi_function_container *fc)
+{
+	int rc;
+
+	rc = rmi_f11_initialize(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	rc = rmi_f11_register_devices(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	rc = rmi_f11_create_sysfs(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	return 0;
+
+err_free_data:
+	rmi_f11_free_memory(fc);
+
+	return rc;
+}
+
+static void rmi_f11_free_memory(struct rmi_function_container *fc)
+{
+	struct f11_data *f11 = fc->data;
+	int i;
+
+	if (f11) {
+		f11_free_control_regs(&f11->dev_controls);
+		for (i = 0; i < f11->dev_query.nbr_of_sensors + 1; i++)
+			kfree(f11->sensors[i].virtualbutton_map.map);
+		kfree(f11);
+		fc->data = NULL;
+	}
+}
+
+
+static int rmi_f11_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct f11_data *f11;
+	u8 query_offset;
+	u16 query_base_addr;
+	u16 control_base_addr;
+	u16 max_x_pos, max_y_pos, temp;
+	int rc;
+	int i;
+	struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
+
+	dev_dbg(&fc->dev, "Initializing F11 values for %s.\n",
+		 pdata->sensor_name);
+
+	/*
+	** 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;
+#if	RESUME_REZERO
+	f11->rezero_on_resume = true;
+	f11->rezero_wait_ms = DEFAULT_REZERO_WAIT_MS;
+#endif
+	#ifdef CONFIG_RMI4_F11_TYPEB
+	f11->type_b = pdata->f11_type_b;
+#endif
+
+	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)
+		return rc;
+
+	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)
+			return rc;
+		query_offset += rc;
+
+		if (f11->dev_query.has_query9) {
+			rc = rmi_read(rmi_dev, query_offset,
+				      &f11->sensors[i].sens_query.query9.reg);
+			if (rc < 0) {
+				dev_err(&fc->dev, "Failed to read query 9.\n");
+				return rc;
+			}
+			query_offset += rc;
+		}
+
+		rc = f11_allocate_control_regs(rmi_dev,
+				&f11->dev_query, &f11->sensors[i].sens_query,
+				&f11->dev_controls, control_base_addr);
+		if (rc < 0) {
+			dev_err(&fc->dev,
+				"Failed to initialize F11 control params.\n");
+			return rc;
+		}
+
+		f11->sensors[i].axis_align = pdata->axis_align;
+
+		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)
+			return rc;
+
+		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)
+			return rc;
+
+		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 = f11_2d_construct_data(&f11->sensors[i]);
+		if (rc < 0)
+			return rc;
+	}
+#ifdef CONFIG_RMI4_DEBUG
+	rc = setup_debugfs(rmi_dev);
+	if (rc < 0)
+		dev_warn(&fc->dev, "Failed to setup debugfs. Code: %d.\n",
+			 rc);
+#endif
+	mutex_init(&f11->dev_controls_mutex);
+	return 0;
+}
+
+static int rmi_f11_register_devices(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct f11_data *f11 = fc->data;
+	struct input_dev *input_dev;
+	struct input_dev *input_dev_mouse;
+	int sensors_itertd = 0;
+	int i;
+	int rc;
+#ifdef CONFIG_RMI4_VIRTUAL_BUTTON
+	struct rmi_button_map *vm_sensor;
+	struct rmi_button_map *vm_pdata;
+	struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
+#endif
+
+	for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) {
+		sensors_itertd = i;
+		input_dev = input_allocate_device();
+		if (!input_dev) {
+			rc = -ENOMEM;
+			goto error_unregister;
+		}
+
+		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);
+#ifdef INPUT_PROP_DIRECT
+		set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+#endif
+
+		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) {
+			input_free_device(input_dev);
+			f11->sensors[i].input = NULL;
+			goto error_unregister;
+		}
+
+		/* how to register the virtualbutton device */
+#ifdef CONFIG_RMI4_VIRTUAL_BUTTON
+		if (f11->sensors[i].sens_query.has_gestures) {
+			int j;
+
+			vm_sensor = &f11->sensors[i].virtualbutton_map;
+			vm_pdata = pdata->f11_button_map;
+			if (!vm_pdata) {
+				dev_err(&fc->dev, "Failed to get the pdata virtualbutton map.\n");
+				goto error_unregister;
+			}
+			vm_sensor->buttons = vm_pdata->buttons;
+			vm_sensor->map = kcalloc(vm_pdata->buttons,
+					sizeof(struct virtualbutton_map),
+					GFP_KERNEL);
+			if (!vm_sensor->map) {
+				dev_err(&fc->dev, "Failed to allocate the virtualbutton map.\n");
+				rc = -ENOMEM;
+				goto error_unregister;
+			}
+
+			/* manage button map using input subsystem */
+			input_dev->keycode = vm_sensor->map;
+			input_dev->keycodesize =
+					sizeof(struct virtualbutton_map);
+			input_dev->keycodemax = vm_pdata->buttons;
+
+			/* set bits for each button... */
+			for (j = 0; j < vm_pdata->buttons; j++) {
+				memcpy(&vm_sensor->map[j], &vm_pdata->map[j],
+					sizeof(struct virtualbutton_map));
+				set_bit(vm_sensor->map[j].code,
+					f11->sensors[i].input->keybit);
+			}
+		}
+
+#endif
+
+		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 error_unregister;
+			}
+
+			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) {
+				input_free_device(input_dev_mouse);
+				f11->sensors[i].mouse_input = NULL;
+				goto error_unregister;
+			}
+
+			set_bit(BTN_RIGHT, input_dev_mouse->keybit);
+		}
+
+	}
+
+	return 0;
+
+error_unregister:
+	for (; sensors_itertd > 0; sensors_itertd--) {
+		if (f11->sensors[sensors_itertd].input) {
+			if (f11->sensors[sensors_itertd].mouse_input) {
+				input_unregister_device(
+				   f11->sensors[sensors_itertd].mouse_input);
+				f11->sensors[sensors_itertd].mouse_input = NULL;
+			}
+			input_unregister_device(f11->sensors[i].input);
+			f11->sensors[i].input = NULL;
+		}
+		kfree(f11->sensors[i].virtualbutton_map.map);
+	}
+
+	return rc;
+}
+
+static void rmi_f11_free_devices(struct rmi_function_container *fc)
+{
+	struct f11_data *f11 = fc->data;
+	int i;
+
+	for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) {
+		if (f11->sensors[i].input)
+			input_unregister_device(f11->sensors[i].input);
+		if (f11->sensors[i].sens_query.has_rel &&
+				f11->sensors[i].mouse_input)
+			input_unregister_device(f11->sensors[i].mouse_input);
+	}
+}
+
+static int rmi_f11_create_sysfs(struct rmi_function_container *fc)
+{
+	int attr_count = 0;
+	int rc;
+	struct f11_data *f11 = fc->data;
+
+	dev_dbg(&fc->dev, "Creating sysfs files.\n");
+	/* Set up sysfs device attributes. */
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+		if (sysfs_create_file
+		    (&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
+			dev_err(&fc->dev,
+				"Failed to create sysfs file for %s.",
+				attrs[attr_count].attr.name);
+			rc = -ENODEV;
+			goto err_remove_sysfs;
+		}
+	}
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_control0) < 0) {
+		dev_err(&fc->dev, "Failed to create query sysfs files.\n");
+		return -ENODEV;
+	}
+	if (f11->dev_controls.ctrl29_30) {
+		if (sysfs_create_group(&fc->dev.kobj,
+			&attrs_control29_30) < 0) {
+			dev_err(&fc->dev,
+				"Failed to create query sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	return 0;
+
+err_remove_sysfs:
+	for (attr_count--; attr_count >= 0; attr_count--)
+		sysfs_remove_file(&fc->dev.kobj, &attrs[attr_count].attr);
+	sysfs_remove_group(&fc->dev.kobj, &attrs_control0);
+	if (f11->dev_controls.ctrl29_30)
+		sysfs_remove_group(&fc->dev.kobj, &attrs_control29_30);
+	return rc;
+}
+
+static int rmi_f11_config(struct rmi_function_container *fc)
+{
+	struct f11_data *f11 = fc->data;
+	int i;
+	int rc;
+
+	for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) {
+		rc = f11_write_control_regs(fc->rmi_dev,
+				   &f11->sensors[i].sens_query,
+				   &f11->dev_controls,
+				   fc->fd.query_base_addr);
+		if (rc < 0)
+			return rc;
+	}
+
+	return 0;
+}
+
+static int rmi_f11_reset(struct rmi_function_container *fc)
+{
+	/* we do nothing here */
+	return 0;
+}
+
+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;
+	u16 data_base_addr = fc->fd.data_base_addr;
+	u16 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, &f11->sensors[i]);
+		rmi_f11_virtual_button_handler(&f11->sensors[i]);
+		data_base_addr_offset += f11->sensors[i].pkt_size;
+	}
+
+	return 0;
+}
+
+#if RESUME_REZERO
+static int rmi_f11_resume(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct f11_data *data = fc->data;
+	/* Command register always reads as 0, so we can just use a local. */
+	union f11_2d_commands commands = {};
+	int retval = 0;
+
+	dev_dbg(&fc->dev, "Resuming...\n");
+	if (!data->rezero_on_resume)
+		return 0;
+
+	if (data->rezero_wait_ms)
+		mdelay(data->rezero_wait_ms);
+
+	commands.rezero = 1;
+	retval = rmi_write_block(rmi_dev, fc->fd.command_base_addr,
+			&commands.reg, sizeof(commands.reg));
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev, "%s: failed to issue rezero command, error = %d.",
+			__func__, retval);
+		return retval;
+	}
+
+	return retval;
+}
+#endif /* RESUME_REZERO */
+
+static void rmi_f11_remove(struct rmi_function_container *fc)
+{
+	int attr_count = 0;
+	struct f11_data *f11 = fc->data;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+#ifdef	CONFIG_RMI4_DEBUG
+	teardown_debugfs(rmi_dev);
+#endif
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
+		sysfs_remove_file(&fc->dev.kobj,
+				  &attrs[attr_count].attr);
+
+	sysfs_remove_group(&fc->dev.kobj, &attrs_control0);
+	if (f11->dev_controls.ctrl29_30)
+		sysfs_remove_group(&fc->dev.kobj, &attrs_control29_30);
+
+	rmi_f11_free_devices(fc);
+
+	rmi_f11_free_memory(fc);
+
+}
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x11,
+	.init = rmi_f11_init,
+	.config = rmi_f11_config,
+	.reset = rmi_f11_reset,
+	.attention = rmi_f11_attention,
+	.remove = rmi_f11_remove,
+#if	RESUME_REZERO
+#if defined(CONFIG_HAS_EARLYSUSPEND) && \
+			!defined(CONFIG_RMI4_SPECIAL_EARLYSUSPEND)
+	.late_resume = rmi_f11_resume
+#else
+	.resume = rmi_f11_resume
+#endif  /* defined(CONFIG_HAS_EARLYSUSPEND) && !def... */
+#endif
+};
+
+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 f11_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 f11_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 f11_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 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;
+}
+
+#if RESUME_REZERO
+static ssize_t f11_rezeroOnResume_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct rmi_function_container *fc = NULL;
+	unsigned int newValue;
+	struct f11_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	if (sscanf(buf, "%u", &newValue) != 1)
+		return -EINVAL;
+	if (newValue < 0 || newValue > 1) {
+		dev_err(dev, "rezeroOnResume must be either 1 or 0.\n");
+		return -EINVAL;
+	}
+
+	instance_data->rezero_on_resume = (newValue != 0);
+
+	return count;
+}
+
+static ssize_t f11_rezeroOnResume_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->rezero_on_resume);
+}
+
+static ssize_t f11_rezeroWait_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct rmi_function_container *fc = NULL;
+	unsigned int newValue;
+	struct f11_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	if (sscanf(buf, "%u", &newValue) != 1)
+		return -EINVAL;
+	if (newValue < 0) {
+		dev_err(dev, "rezeroWait must be 0 or greater.\n");
+		return -EINVAL;
+	}
+
+	instance_data->rezero_wait_ms = (newValue != 0);
+
+	return count;
+}
+
+static ssize_t f11_rezeroWait_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->rezero_wait_ms);
+}
+
+#endif
+
+/* Control sysfs files */
+show_store_union_struct_unsigned(dev_controls, ctrl0_9, abs_pos_filt)
+show_store_union_struct_unsigned(dev_controls, ctrl29_30, z_touch_threshold)
+show_store_union_struct_unsigned(dev_controls, ctrl29_30, z_touch_hysteresis)
+module_init(rmi_f11_module_init);
+module_exit(rmi_f11_module_exit);
+
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com");
+MODULE_DESCRIPTION("RMI F11 module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 10/17] input: RM4 F17 Pointing sticks
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (8 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 9/17] input: RMI4 F11 multitouch sensing Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-17 22:17 ` [RFC PATCH 11/17] input: RMI4 F19 capacitive buttons Christopher Heiny
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_f17.c |  713 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 713 insertions(+), 0 deletions(-)

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

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 11/17] input: RMI4 F19 capacitive buttons
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (9 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 10/17] input: RM4 F17 Pointing sticks Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-17 22:17 ` [RFC PATCH 12/17] input: RMI4 F1A simple " Christopher Heiny
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_f19.c |  869 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 869 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f19.c b/drivers/input/rmi4/rmi_f19.c
new file mode 100644
index 0000000..9e9ea93
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f19.c
@@ -0,0 +1,869 @@
+/*
+ * Copyright (c) 2012 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#define FUNCTION_DATA f19_data
+#define FNUM 19
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+#define QUERY_BASE_INDEX 1
+#define MAX_LEN 256
+#define MAX_BUFFER_LEN 80
+
+#define SENSOR_MAP_MIN			0
+#define SENSOR_MAP_MAX			127
+#define SENSITIVITY_ADJ_MIN		0
+#define SENSITIVITY_ADJ_MAX		31
+#define HYSTERESIS_THRESHOLD_MIN	0
+#define HYSTERESIS_THRESHOLD_MAX	15
+
+union f19_0d_query {
+	struct {
+		u8 configurable:1;
+		u8 has_sensitivity_adjust:1;
+		u8 has_hysteresis_threshold:1;
+		u8 reserved_1:5;
+
+		u8 button_count:5;
+		u8 reserved_2:3;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f19_0d_control_0 {
+	struct {
+		u8 button_usage:2;
+		u8 filter_mode:2;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+/* rewrite control regs */
+struct f19_0d_control_1n {
+	u8 int_enabled_button;
+};
+
+struct f19_0d_control_1 {
+	struct f19_0d_control_1n *regs;
+	u16 address;
+	u8 length;
+} __attribute__((__packed__));
+
+struct f19_0d_control_2n {
+	u8 single_button;
+};
+
+struct f19_0d_control_2 {
+	struct f19_0d_control_2n *regs;
+	u16 address;
+	u8 length;
+} __attribute__((__packed__));
+
+struct f19_0d_control_3n {
+	u8 sensor_map_button:7;
+};
+
+struct f19_0d_control_3 {
+	struct f19_0d_control_3n *regs;
+	u16 address;
+	u8 length;
+} __attribute__((__packed__));
+
+struct f19_0d_control_4n {
+	u8 sensitivity_button:7;
+};
+
+struct f19_0d_control_4 {
+	struct f19_0d_control_4n *regs;
+	u16 address;
+	u8 length;
+} __attribute__((__packed__));
+
+union f19_0d_control_5 {
+	struct {
+		u8 sensitivity_adj:5;
+	};
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f19_0d_control_6 {
+	struct {
+		u8 hysteresis_threshold:4;
+	};
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+struct f19_0d_control {
+	union f19_0d_control_0 *reg_0;
+	struct f19_0d_control_1 *reg_1;
+	struct f19_0d_control_2 *reg_2;
+	struct f19_0d_control_3 *reg_3;
+	struct f19_0d_control_4 *reg_4;
+	union f19_0d_control_5 *reg_5;
+	union f19_0d_control_6 *reg_6;
+};
+
+/* data specific to fn $19 that needs to be kept around */
+struct f19_data {
+	struct f19_0d_control control;
+	union f19_0d_query query;
+	u8 button_rezero;
+	unsigned char button_count;
+	unsigned char button_bitmask_size;
+	unsigned char *button_data_buffer;
+	unsigned short *button_map;
+	char input_name[MAX_LEN];
+	char input_phys[MAX_LEN];
+	struct input_dev *input;
+	struct mutex control_mutex;
+	struct mutex data_mutex;
+};
+
+
+static int rmi_f19_alloc_memory(struct rmi_function_container *fc);
+
+static void rmi_f19_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f19_initialize(struct rmi_function_container *fc);
+
+static int rmi_f19_register_device(struct rmi_function_container *fc);
+
+static int rmi_f19_create_sysfs(struct rmi_function_container *fc);
+
+static int rmi_f19_config(struct rmi_function_container *fc);
+
+static int rmi_f19_reset(struct rmi_function_container *fc);
+
+/* Query sysfs files */
+show_union_struct_prototype(configurable)
+show_union_struct_prototype(has_sensitivity_adjust)
+show_union_struct_prototype(has_hysteresis_threshold)
+show_union_struct_prototype(button_count)
+
+static struct attribute *attrs[] = {
+	attrify(configurable),
+	attrify(has_sensitivity_adjust),
+	attrify(has_hysteresis_threshold),
+	attrify(button_count),
+	NULL
+};
+
+static struct attribute_group attrs_query = GROUP(attrs);
+/* Control sysfs files */
+show_store_union_struct_prototype(button_usage)
+show_store_union_struct_prototype(filter_mode)
+show_store_union_struct_prototype(int_enabled_button)
+show_store_union_struct_prototype(single_button)
+show_store_union_struct_prototype(sensitivity_button)
+show_store_union_struct_prototype(sensitivity_adj)
+show_store_union_struct_prototype(hysteresis_threshold)
+
+
+static struct attribute *attrsCtrl[] = {
+	attrify(button_usage),
+	attrify(filter_mode),
+	attrify(int_enabled_button),
+	attrify(single_button),
+	attrify(sensitivity_button),
+	NULL
+};
+static struct attribute_group attrs_control = GROUP(attrsCtrl);
+
+static ssize_t rmi_fn_19_sensor_map_button_show(struct device *dev,
+				     struct device_attribute *attr, char *buf);
+static ssize_t rmi_fn_19_sensor_map_button_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+static struct device_attribute sensor_map_attr = __ATTR(sensor_map_button,
+			RMI_RW_ATTR, rmi_fn_19_sensor_map_button_show,
+			rmi_fn_19_sensor_map_button_store);
+
+static struct device_attribute sensor_map_ro_attr =  __ATTR(sensor_map_button,
+			RMI_RO_ATTR, rmi_fn_19_sensor_map_button_show,
+			 rmi_store_error);
+
+/* Command sysfs files */
+static ssize_t f19_rezero_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count);
+static DEVICE_ATTR(rezero, RMI_WO_ATTR,
+		rmi_show_error,
+		f19_rezero_store);
+
+static struct attribute *attrsCommand[] = {
+	attrify(rezero),
+	NULL
+};
+static struct attribute_group attrs_command = GROUP(attrsCommand);
+
+int rmi_f19_read_control_parameters(struct rmi_device *rmi_dev,
+	struct f19_data *f19)
+{
+	int retval = 0;
+	union f19_0d_query *query = &f19->query;
+	struct f19_0d_control *control = &f19->control;
+
+
+	retval = rmi_read_block(rmi_dev, control->reg_0->address,
+			(u8 *)control->reg_0->regs,
+			sizeof(control->reg_0->regs));
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev, "Could not read control reg0 to %#06x.\n",
+				control->reg_0->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_1->address,
+			(u8 *)control->reg_1->regs, f19->button_bitmask_size);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev, "Could not read control reg1 to %#06x.\n",
+			 control->reg_1->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_2->address,
+			(u8 *)control->reg_2->regs, f19->button_bitmask_size);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev, "Could not read control reg2 to %#06x.\n",
+			 control->reg_2->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_3->address,
+			(u8 *)control->reg_3->regs, f19->button_count);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev, "Could not read control reg3 to %#06x.\n",
+			 control->reg_3->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_4->address,
+			(u8 *)control->reg_4->regs, f19->button_count);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev, "Could not read control reg3 to %#06x.\n",
+			 control->reg_4->address);
+		return retval;
+	}
+
+	if (query->has_sensitivity_adjust) {
+		retval = rmi_read_block(rmi_dev, control->reg_5->address,
+				(u8 *)control->reg_5->regs,
+				sizeof(control->reg_5->regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg5 to %#06x.\n",
+				control->reg_5->address);
+			return retval;
+		}
+	}
+	if (query->has_hysteresis_threshold) {
+		retval = rmi_read_block(rmi_dev, control->reg_6->address,
+				(u8 *)control->reg_6->regs,
+				sizeof(control->reg_6->regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg6 to %#06x.\n",
+				control->reg_6->address);
+			return retval;
+		}
+	}
+	return 0;
+}
+static int rmi_f19_init(struct rmi_function_container *fc)
+{
+	int rc = rmi_f19_alloc_memory(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	rc = rmi_f19_initialize(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	rc = rmi_f19_register_device(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	rc = rmi_f19_create_sysfs(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	return 0;
+
+err_free_data:
+	rmi_f19_free_memory(fc);
+	return rc;
+}
+
+
+static int rmi_f19_alloc_memory(struct rmi_function_container *fc)
+{
+	struct f19_data *f19;
+	int rc;
+	u16 ctrl_base_addr;
+
+	/* allow memory for fn19 data */
+	f19 = kzalloc(sizeof(struct f19_data), GFP_KERNEL);
+	if (!f19) {
+		dev_err(&fc->dev, "Failed to allocate function data.\n");
+		return -ENOMEM;
+	}
+	fc->data = f19;
+	rc = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+				(u8 *)&f19->query,
+				sizeof(f19->query.regs));
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to read query register.\n");
+		return rc;
+	}
+	f19->query.address = fc->fd.query_base_addr;
+
+	f19->button_bitmask_size = sizeof(u8)*(f19->query.button_count + 7) / 8;
+	f19->button_data_buffer = kcalloc(f19->button_bitmask_size,
+		    sizeof(unsigned char), GFP_KERNEL);
+	if (!f19->button_data_buffer) {
+		dev_err(&fc->dev, "Failed to allocate button data buffer.\n");
+		return -ENOMEM;
+	}
+
+	f19->button_map = kcalloc(f19->query.button_count,
+				sizeof(unsigned short), GFP_KERNEL);
+	if (!f19->button_map) {
+		dev_err(&fc->dev, "Failed to allocate button map.\n");
+		return -ENOMEM;
+	}
+
+	/* allocate memory for control reg */
+	/* reg 0 */
+	ctrl_base_addr = fc->fd.control_base_addr;
+	f19->control.reg_0 =
+			kzalloc(sizeof(f19->control.reg_0->regs), GFP_KERNEL);
+	if (!f19->control.reg_0) {
+		dev_err(&fc->dev, "Failed to allocate reg_0 control registers.");
+		return -ENOMEM;
+	}
+	f19->control.reg_0->address = ctrl_base_addr;
+	ctrl_base_addr += sizeof(f19->control.reg_0->regs);
+
+	/* reg 1 */
+	f19->control.reg_1 =
+			kzalloc(sizeof(struct f19_0d_control_1), GFP_KERNEL);
+	if (!f19->control.reg_1) {
+		dev_err(&fc->dev, "Failed to allocate reg_1 control registers.");
+		return -ENOMEM;
+	}
+	f19->control.reg_1->regs =
+			kzalloc(f19->button_bitmask_size, GFP_KERNEL);
+	if (!f19->control.reg_1->regs) {
+		dev_err(&fc->dev, "Failed to allocate reg_1->regs control registers.");
+		return -ENOMEM;
+	}
+	f19->control.reg_1->address = ctrl_base_addr;
+	f19->control.reg_1->length = f19->button_bitmask_size;
+	ctrl_base_addr += f19->button_bitmask_size;
+
+	/* reg 2 */
+	f19->control.reg_2 =
+			kzalloc(sizeof(struct f19_0d_control_2), GFP_KERNEL);
+	if (!f19->control.reg_2) {
+		dev_err(&fc->dev, "Failed to allocate reg_2 control registers.");
+		return -ENOMEM;
+	}
+	f19->control.reg_2->regs =
+			kzalloc(f19->button_bitmask_size, GFP_KERNEL);
+	if (!f19->control.reg_2->regs) {
+		dev_err(&fc->dev, "Failed to allocate reg_2->regs control registers.");
+		return -ENOMEM;
+	}
+	f19->control.reg_2->address = ctrl_base_addr;
+	f19->control.reg_2->length = f19->button_bitmask_size;
+	ctrl_base_addr += f19->button_bitmask_size;
+
+	/* reg 3 */
+	f19->control.reg_3 =
+			kzalloc(sizeof(struct f19_0d_control_3), GFP_KERNEL);
+	if (!f19->control.reg_3) {
+		dev_err(&fc->dev, "Failed to allocate reg_3 control registers.");
+		return -ENOMEM;
+	}
+	f19->control.reg_3->regs =
+			kzalloc(f19->query.button_count, GFP_KERNEL);
+	if (!f19->control.reg_3->regs) {
+		dev_err(&fc->dev, "Failed to allocate reg_3->regs control registers.");
+		return -ENOMEM;
+	}
+	f19->control.reg_3->address = ctrl_base_addr;
+	f19->control.reg_3->length = f19->query.button_count;
+	ctrl_base_addr += f19->query.button_count;
+
+	/* reg 4 */
+	f19->control.reg_4 =
+			kzalloc(sizeof(struct f19_0d_control_4), GFP_KERNEL);
+	if (!f19->control.reg_4) {
+		dev_err(&fc->dev, "Failed to allocate reg_3 control registers.");
+		return -ENOMEM;
+	}
+	f19->control.reg_4->regs =
+			kzalloc(f19->query.button_count, GFP_KERNEL);
+	if (!f19->control.reg_4->regs) {
+		dev_err(&fc->dev, "Failed to allocate reg_3->regs control registers.");
+		return -ENOMEM;
+	}
+	f19->control.reg_4->address = ctrl_base_addr;
+	f19->control.reg_4->length = f19->query.button_count;
+	ctrl_base_addr += f19->query.button_count;
+
+	/* reg 5 */
+	if (f19->query.has_sensitivity_adjust) {
+		f19->control.reg_5 =
+			kzalloc(sizeof(f19->control.reg_5->regs), GFP_KERNEL);
+		if (!f19->control.reg_5) {
+			dev_err(&fc->dev, "Failed to allocate reg_5 control registers.");
+			return -ENOMEM;
+		}
+		f19->control.reg_5->address = ctrl_base_addr;
+		ctrl_base_addr += sizeof(f19->control.reg_5->regs);
+	}
+	/* reg 6 */
+	if (f19->query.has_hysteresis_threshold) {
+		f19->control.reg_6 =
+			kzalloc(sizeof(f19->control.reg_6->regs), GFP_KERNEL);
+		if (!f19->control.reg_6) {
+			dev_err(&fc->dev, "Failed to allocate reg_6 control registers.");
+			return -ENOMEM;
+		}
+		f19->control.reg_6->address = ctrl_base_addr;
+	}
+	return 0;
+}
+
+
+
+static void rmi_f19_free_memory(struct rmi_function_container *fc)
+{
+	union f19_0d_query *query;
+	struct f19_data *f19 = fc->data;
+
+	query = &f19->query;
+	sysfs_remove_group(&fc->dev.kobj, &attrs_query);
+	sysfs_remove_group(&fc->dev.kobj, &attrs_control);
+	if (query->has_sensitivity_adjust)
+		sysfs_remove_file(&fc->dev.kobj, attrify(sensitivity_adj));
+
+	if (query->has_hysteresis_threshold)
+		sysfs_remove_file(&fc->dev.kobj,
+				attrify(hysteresis_threshold));
+	sysfs_remove_group(&fc->dev.kobj, &attrs_command);
+	if (query->configurable)
+		sysfs_remove_file(&fc->dev.kobj, &sensor_map_attr.attr);
+	else
+		sysfs_remove_file(&fc->dev.kobj, &sensor_map_ro_attr.attr);
+
+	if (f19) {
+		kfree(f19->button_data_buffer);
+		kfree(f19->button_map);
+		kfree(f19->control.reg_0);
+		kfree(f19->control.reg_1);
+		kfree(f19->control.reg_1->regs);
+		kfree(f19->control.reg_2);
+		kfree(f19->control.reg_2->regs);
+		kfree(f19->control.reg_3);
+		kfree(f19->control.reg_3->regs);
+		kfree(f19->control.reg_5);
+		kfree(f19->control.reg_6);
+		kfree(f19);
+		fc->data = NULL;
+	}
+}
+
+static int rmi_f19_initialize(struct rmi_function_container *fc)
+{
+	int i;
+	int rc;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_device_platform_data *pdata;
+	struct f19_data *f19 = fc->data;
+
+	/* 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");
+		return rc;
+	}
+	f19->button_rezero = f19->button_rezero & 1;
+
+	pdata = to_rmi_platform_data(rmi_dev);
+	if (pdata) {
+		if (!pdata->f19_button_map)
+			dev_warn(&fc->dev, "F19 button_map is NULL");
+		else if (!pdata->f19_button_map->map)
+			dev_warn(&fc->dev,
+				 "Platformdata button map is missing!\n");
+		else {
+			if (pdata->f19_button_map->nbuttons !=
+						f19->query.button_count)
+				dev_warn(&fc->dev, "Platformdata button map size (%d) != number of buttons on device (%d) - ignored.\n",
+					pdata->f19_button_map->nbuttons,
+					f19->query.button_count);
+			f19->button_count = min(pdata->f19_button_map->nbuttons,
+					 (u8) f19->query.button_count);
+			for (i = 0; i < f19->button_count; i++)
+				f19->button_map[i] =
+					pdata->f19_button_map->map[i];
+		}
+	}
+	rc = rmi_f19_read_control_parameters(rmi_dev, f19);
+	if (rc < 0) {
+		dev_err(&fc->dev,
+			"Failed to initialize F19 control params.\n");
+		return rc;
+	}
+
+	mutex_init(&f19->control_mutex);
+	mutex_init(&f19->data_mutex);
+	return 0;
+}
+
+static int rmi_f19_register_device(struct rmi_function_container *fc)
+{
+	int i;
+	int rc;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct f19_data *f19 = fc->data;
+	struct input_dev *input_dev = input_allocate_device();
+
+	if (!input_dev) {
+		dev_err(&fc->dev, "Failed to allocate input device.\n");
+		return -ENOMEM;
+	}
+
+	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);
+
+	/* manage button map using input subsystem */
+	input_dev->keycode = f19->button_map;
+	input_dev->keycodesize = sizeof(f19->button_map);
+	input_dev->keycodemax = f19->button_count;
+
+	/* 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 error_free_device;
+	}
+
+	return 0;
+
+error_free_device:
+	input_free_device(input_dev);
+
+	return rc;
+}
+
+
+static int rmi_f19_create_sysfs(struct rmi_function_container *fc)
+{
+	struct f19_data *f19 = fc->data;
+	union f19_0d_query *query = &f19->query;
+
+	dev_dbg(&fc->dev, "Creating sysfs files.\n");
+
+	/* Set up sysfs device attributes. */
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_query) < 0) {
+		dev_err(&fc->dev, "Failed to create query sysfs files.");
+		return -ENODEV;
+	}
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_control) < 0) {
+		dev_err(&fc->dev, "Failed to create control sysfs files.");
+		return -ENODEV;
+	}
+	if (query->has_sensitivity_adjust) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(sensitivity_adj)) < 0) {
+			dev_err(&fc->dev,
+				"Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+	if (query->has_hysteresis_threshold) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(hysteresis_threshold)) < 0) {
+			dev_err(&fc->dev,
+				"Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_command) < 0) {
+		dev_err(&fc->dev, "Failed to create command sysfs files.");
+		return -ENODEV;
+	}
+	if (query->configurable) {
+		if (sysfs_create_file(&fc->dev.kobj,
+					&sensor_map_attr.attr) < 0) {
+			dev_err(&fc->dev,
+				"Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	} else {
+		if (sysfs_create_file(&fc->dev.kobj,
+					&sensor_map_ro_attr.attr) < 0) {
+			dev_err(&fc->dev,
+				"Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+	return 0;
+}
+
+static int rmi_f19_config(struct rmi_function_container *fc)
+{
+	struct f19_data *f19 = fc->data;
+	int retval = 0;
+	union f19_0d_query *query = &f19->query;
+	struct f19_0d_control *control = &f19->control;
+
+	retval = rmi_write_block(fc->rmi_dev, control->reg_0->address,
+			(u8 *)control->reg_0->regs,
+			sizeof(u8));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not write control reg0 to %#06x.\n",
+				control->reg_0->address);
+		return retval;
+	}
+
+	retval = rmi_write_block(fc->rmi_dev, control->reg_1->address,
+			(u8 *)control->reg_1->regs, f19->button_bitmask_size);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not write control reg1 to %#06x.\n",
+			 control->reg_1->address);
+		return retval;
+	}
+
+	retval = rmi_write_block(fc->rmi_dev, control->reg_2->address,
+			(u8 *)control->reg_2->regs, f19->button_bitmask_size);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not write control reg2 to %#06x.\n",
+			 control->reg_2->address);
+		return retval;
+	}
+
+	retval = rmi_write_block(fc->rmi_dev, control->reg_3->address,
+			(u8 *)control->reg_3->regs, query->button_count);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not write control reg3 to %#06x.\n",
+			 control->reg_3->address);
+		return retval;
+	}
+
+	retval = rmi_write_block(fc->rmi_dev, control->reg_4->address,
+			(u8 *)control->reg_4->regs, query->button_count);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not write control reg3 to %#06x.\n",
+			 control->reg_4->address);
+		return retval;
+	}
+
+	if (query->has_sensitivity_adjust) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_5->address,
+				(u8 *)control->reg_5->regs,
+				sizeof(control->reg_5->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev,
+				"Could not write control reg5 to %#06x.\n",
+				control->reg_5->address);
+			return retval;
+		}
+	}
+	if (query->has_hysteresis_threshold) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_6->address,
+				(u8 *)control->reg_6->regs,
+				sizeof(control->reg_6->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev,
+				"Could not write control reg6 to %#06x.\n",
+				control->reg_6->address);
+			return retval;
+		}
+	}
+	return 0;
+}
+
+
+static int rmi_f19_reset(struct rmi_function_container *fc)
+{
+	/* we do nnothing here */
+	return 0;
+}
+
+
+static void rmi_f19_remove(struct rmi_function_container *fc)
+{
+	struct f19_data *f19 = fc->data;
+
+	input_unregister_device(f19->input);
+	rmi_f19_free_memory(fc);
+}
+
+static int rmi_f19_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	int error;
+	int button;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct f19_data *f19 = fc->data;
+	u16 data_base_addr = fc->fd.data_base_addr;
+
+	/* Read the button data. */
+	error = rmi_read_block(rmi_dev, data_base_addr, f19->button_data_buffer,
+			f19->button_bitmask_size);
+	if (error < 0) {
+		dev_err(&fc->dev, "%s: Failed to read button data registers.\n",
+			__func__);
+		return error;
+	}
+
+	/* Generate events for buttons. */
+	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 / 8;
+		/* bit shift to get button's status */
+		button_shift = button % 8;
+		button_status =
+		    ((f19->button_data_buffer[button_reg] >> button_shift)
+			& 0x01) != 0;
+		/* Generate an event here. */
+		input_report_key(f19->input, f19->button_map[button],
+				 button_status);
+	}
+
+	input_sync(f19->input); /* sync after groups of events */
+	return 0;
+}
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x19,
+	.init = rmi_f19_init,
+	.config = rmi_f19_config,
+	.reset = rmi_f19_reset,
+	.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);
+}
+
+/* sysfs functions */
+/* Query */
+simple_show_union_struct_unsigned(query, configurable)
+simple_show_union_struct_unsigned(query, has_sensitivity_adjust)
+simple_show_union_struct_unsigned(query, has_hysteresis_threshold)
+simple_show_union_struct_unsigned(query, button_count)
+
+/* Control */
+show_store_union_struct_unsigned(control, reg_0, button_usage)
+show_store_union_struct_unsigned(control, reg_0, filter_mode)
+show_store_union_struct_unsigned(control, reg_5, sensitivity_adj)
+show_store_union_struct_unsigned(control, reg_6, hysteresis_threshold)
+
+/* repeated register functions */
+show_store_repeated_union_struct_unsigned(control, reg_1, int_enabled_button)
+show_store_repeated_union_struct_unsigned(control, reg_2, single_button)
+show_store_repeated_union_struct_unsigned(control, reg_3, sensor_map_button)
+show_store_repeated_union_struct_unsigned(control, reg_4, sensitivity_button)
+
+/* command */
+static ssize_t f19_rezero_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t count)
+{
+	unsigned int new_value;
+	int len;
+	struct f19_data *f19;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	f19 = 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;
+	}
+	f19->button_rezero = new_value & 1;
+	len = rmi_write(fc->rmi_dev, fc->fd.command_base_addr,
+		f19->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;
+}
+
+
+
+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");
+MODULE_VERSION(RMI_DRIVER_VERSION);

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 12/17] input: RMI4 F1A simple capacitive buttons
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (10 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 11/17] input: RMI4 F19 capacitive buttons Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-17 22:17 ` [RFC PATCH 13/17] input: RMI4 F21 Force sensing Christopher Heiny
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_f1a.c | 1025 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1025 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f1a.c b/drivers/input/rmi4/rmi_f1a.c
new file mode 100644
index 0000000..a7cccfa
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f1a.c
@@ -0,0 +1,1025 @@
+/*
+ * Copyright (c) 2012 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#define FUNCTION_DATA f1a_data
+#define FNUM 1a
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+#define QUERY_BASE_INDEX 1
+#define MAX_NAME_LEN 256
+#define MAX_BUFFER_LEN 80
+
+#define FILTER_MODE_MIN				0
+#define FILTER_MODE_MAX				3
+#define MULTI_BUTTON_REPORT_MIN			0
+#define MULTI_BUTTON_REPORT_MAX			3
+#define TX_RX_BUTTON_MIN			0
+#define TX_RX_BUTTON_MAX			255
+#define THREADHOLD_BUTTON_MIN			0
+#define THREADHOLD_BUTTON_MAX			255
+#define RELEASE_THREADHOLD_BUTTON_MIN		0
+#define RELEASE_THREADHOLD_BUTTON_MAX		255
+#define STRONGEST_BUTTON_HYSTERESIS_MIN		0
+#define STRONGEST_BUTTON_HYSTERESIS_MAX		255
+#define FILTER_STRENGTH_MIN			0
+#define FILTER_STRENGTH_MAX			255
+
+union f1a_0d_query {
+	struct {
+		u8 max_button_count:3;
+		u8 reserved:5;
+
+		u8 has_general_control:1;
+		u8 has_interrupt_enable:1;
+		u8 has_multibutton_select:1;
+		u8 has_tx_rx_map:1;
+		u8 has_perbutton_threshold:1;
+		u8 has_release_threshold:1;
+		u8 has_strongestbtn_hysteresis:1;
+		u8 has_filter_strength:1;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f1a_0d_control_0 {
+	struct {
+		u8 multibutton_report:2;
+		u8 filter_mode:2;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+struct f1a_0d_control_1n {
+	u8 interrupt_enabled_button;
+};
+
+struct f1a_0d_control_1 {
+	struct f1a_0d_control_1n *regs;
+	u16 address;
+	u8 length;
+} __attribute__((__packed__));
+
+struct f1a_0d_control_2n {
+	u8 multi_button;
+};
+
+struct f1a_0d_control_2 {
+	struct f1a_0d_control_2n *regs;
+	u16 address;
+	u8 length;
+} __attribute__((__packed__));
+
+struct f1a_0d_control_3_4n {
+	u8 transmitterbtn;
+	u8 receiverbtn;
+} __attribute__((__packed__));
+
+struct f1a_0d_control_3_4 {
+	struct f1a_0d_control_3_4n *regs;
+	u16 address;
+	u8 length;
+} __attribute__((__packed__));
+
+struct f1a_0d_control_5n {
+	u8 threshold_button;
+};
+
+struct f1a_0d_control_5 {
+	struct f1a_0d_control_5n *regs;
+	u16 address;
+	u8 length;
+} __attribute__((__packed__));
+
+union f1a_0d_control_6 {
+	struct {
+		u8 button_release_threshold;
+	};
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f1a_0d_control_7 {
+	struct {
+		u8 strongest_button_hysteresis;
+	};
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f1a_0d_control_8 {
+	struct {
+		u8 filter_strength;
+	};
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+struct f1a_0d_control {
+	union f1a_0d_control_0 *reg_0;
+	struct f1a_0d_control_1 *reg_1;
+	struct f1a_0d_control_2 *reg_2;
+	struct f1a_0d_control_3_4 *reg_3_4;
+	struct f1a_0d_control_5 *reg_5;
+	union f1a_0d_control_6 *reg_6;
+	union f1a_0d_control_7 *reg_7;
+	union f1a_0d_control_8 *reg_8;
+};
+
+/* data specific to fn $1a that needs to be kept around */
+struct f1a_data {
+	struct f1a_0d_control control;
+	union f1a_0d_query query;
+	u8 sensor_button_count;
+	int button_bitmask_size;
+	u8 *button_data_buffer;
+	u8 button_map_size;
+	u8 *button_map;
+	char input_name[MAX_NAME_LEN];
+	char input_phys[MAX_NAME_LEN];
+	struct input_dev *input;
+	struct mutex control_mutex;
+	struct mutex data_mutex;
+};
+
+
+static int rmi_f1a_alloc_memory(struct rmi_function_container *fc);
+
+static void rmi_f1a_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f1a_initialize(struct rmi_function_container *fc);
+
+static int rmi_f1a_register_device(struct rmi_function_container *fc);
+
+static int rmi_f1a_create_sysfs(struct rmi_function_container *fc);
+
+static int rmi_f1a_config(struct rmi_function_container *fc);
+
+static int rmi_f1a_reset(struct rmi_function_container *fc);
+
+/* Query sysfs files */
+show_union_struct_prototype(max_button_count)
+show_union_struct_prototype(has_general_control)
+show_union_struct_prototype(has_interrupt_enable)
+show_union_struct_prototype(has_multibutton_select)
+show_union_struct_prototype(has_tx_rx_map)
+show_union_struct_prototype(has_perbutton_threshold)
+show_union_struct_prototype(has_release_threshold)
+show_union_struct_prototype(has_strongestbtn_hysteresis)
+show_union_struct_prototype(has_filter_strength)
+
+static struct attribute *attrs[] = {
+	attrify(max_button_count),
+	attrify(has_general_control),
+	attrify(has_interrupt_enable),
+	attrify(has_multibutton_select),
+	attrify(has_tx_rx_map),
+	attrify(has_perbutton_threshold),
+	attrify(has_release_threshold),
+	attrify(has_strongestbtn_hysteresis),
+	attrify(has_filter_strength),
+	NULL
+};
+
+static struct attribute_group attrs_query = GROUP(attrs);
+
+/* Control sysfs files */
+show_store_union_struct_prototype(multibutton_report)
+show_store_union_struct_prototype(filter_mode)
+show_store_union_struct_prototype(interrupt_enabled_button)
+show_store_union_struct_prototype(multi_button)
+show_union_struct_prototype(tx_rx_map)
+show_store_union_struct_prototype(threshold_button)
+show_store_union_struct_prototype(button_release_threshold)
+show_store_union_struct_prototype(strongest_button_hysteresis)
+show_store_union_struct_prototype(filter_strength)
+
+static int rmi_f1a_read_control_parameters(struct rmi_device *rmi_dev,
+	struct f1a_data *f1a)
+{
+	int retval = 0;
+	union f1a_0d_query *query = &f1a->query;
+	struct f1a_0d_control *control = &f1a->control;
+
+	if (query->has_general_control) {
+		retval = rmi_read_block(rmi_dev, control->reg_0->address,
+				(u8 *)control->reg_0->regs,
+				sizeof(control->reg_0->regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg0 to %#06x.\n",
+					control->reg_0->address);
+			return retval;
+		}
+	}
+
+	if (query->has_interrupt_enable) {
+		retval = rmi_read_block(rmi_dev, control->reg_1->address,
+			(u8 *)control->reg_1->regs, f1a->button_bitmask_size);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg1 to %#06x.\n",
+				 control->reg_1->address);
+			return retval;
+		}
+	}
+
+	if (query->has_multibutton_select) {
+		retval = rmi_read_block(rmi_dev, control->reg_2->address,
+			(u8 *)control->reg_2->regs, f1a->button_bitmask_size);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg2 to %#06x.\n",
+				 control->reg_2->address);
+			return retval;
+		}
+	}
+
+	if (query->has_tx_rx_map) {
+		retval = rmi_read_block(rmi_dev, control->reg_3_4->address,
+			(u8 *)control->reg_3_4->regs,
+			sizeof(struct f1a_0d_control_3_4n) *
+					f1a->sensor_button_count);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg 3_4 to %#06x.\n",
+				 control->reg_3_4->address);
+			return retval;
+		}
+	}
+
+	if (query->has_perbutton_threshold) {
+		retval = rmi_read_block(rmi_dev, control->reg_5->address,
+			(u8 *)control->reg_5->regs, f1a->sensor_button_count);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg 5 to %#06x.\n",
+				 control->reg_5->address);
+			return retval;
+		}
+	}
+
+	if (query->has_release_threshold) {
+		retval = rmi_read_block(rmi_dev, control->reg_6->address,
+				(u8 *)control->reg_6->regs,
+				sizeof(control->reg_6->regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg 6 to %#06x.\n",
+					control->reg_6->address);
+			return retval;
+		}
+	}
+
+	if (query->has_strongestbtn_hysteresis) {
+		retval = rmi_read_block(rmi_dev, control->reg_7->address,
+				(u8 *)control->reg_7->regs,
+				sizeof(control->reg_7->regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg 7 to %#06x.\n",
+					control->reg_7->address);
+			return retval;
+		}
+	}
+
+	if (query->has_filter_strength) {
+		retval = rmi_read_block(rmi_dev, control->reg_8->address,
+				(u8 *)control->reg_8->regs,
+				sizeof(control->reg_8->regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg 8 to %#06x.\n",
+					control->reg_8->address);
+			return retval;
+		}
+	}
+	return 0;
+}
+
+
+static int rmi_f1a_init(struct rmi_function_container *fc)
+{
+	int rc;
+
+	rc = rmi_f1a_alloc_memory(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	rc = rmi_f1a_initialize(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	rc = rmi_f1a_register_device(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	rc = rmi_f1a_create_sysfs(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	return 0;
+
+err_free_data:
+	rmi_f1a_free_memory(fc);
+
+	return rc;
+}
+
+
+static int rmi_f1a_alloc_memory(struct rmi_function_container *fc)
+{
+	struct f1a_data *f1a;
+	int rc;
+	int regSize;
+	u16 ctrl_base_addr;
+
+	f1a = kzalloc(sizeof(struct f1a_data), GFP_KERNEL);
+	if (!f1a) {
+		dev_err(&fc->dev, "Failed to allocate function data.\n");
+		return -ENOMEM;
+	}
+	fc->data = f1a;
+
+	rc = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+			f1a->query.regs, sizeof(f1a->query.regs));
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to read query register.\n");
+		return rc;
+	}
+
+	f1a->sensor_button_count = f1a->query.max_button_count+1;
+
+	f1a->button_bitmask_size =
+			sizeof(u8)*(f1a->sensor_button_count + 7) / 8;
+	f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size,
+			sizeof(u8), GFP_KERNEL);
+	if (!f1a->button_data_buffer) {
+		dev_err(&fc->dev, "Failed to allocate button data buffer.\n");
+		return -ENOMEM;
+	}
+
+	f1a->button_map = kcalloc(f1a->sensor_button_count,
+				sizeof(unsigned char), GFP_KERNEL);
+	if (!f1a->button_map) {
+		dev_err(&fc->dev, "Failed to allocate button map.\n");
+		return -ENOMEM;
+	}
+
+	/* allocate memory for control reg */
+	/* reg 0 */
+	ctrl_base_addr = fc->fd.control_base_addr;
+	if (f1a->query.has_general_control) {
+		f1a->control.reg_0 =
+			kzalloc(sizeof(f1a->control.reg_0->regs), GFP_KERNEL);
+		if (!f1a->control.reg_0) {
+			dev_err(&fc->dev, "Failed to allocate reg_0 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_0->address = ctrl_base_addr;
+		ctrl_base_addr += sizeof(f1a->control.reg_0->regs);
+	}
+	/* reg 1 */
+	if (f1a->query.has_interrupt_enable) {
+		f1a->control.reg_1 =
+			kzalloc(sizeof(struct f1a_0d_control_1), GFP_KERNEL);
+		if (!f1a->control.reg_1) {
+			dev_err(&fc->dev, "Failed to allocate reg_1 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_1->regs =
+				kzalloc(f1a->button_bitmask_size, GFP_KERNEL);
+		if (!f1a->control.reg_1->regs) {
+			dev_err(&fc->dev, "Failed to allocate reg_1->regs control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_1->address = ctrl_base_addr;
+		f1a->control.reg_1->length = f1a->button_bitmask_size;
+		ctrl_base_addr += f1a->button_bitmask_size;
+	}
+
+	/* reg 2 */
+	if (f1a->query.has_multibutton_select) {
+		f1a->control.reg_2 =
+			kzalloc(sizeof(struct f1a_0d_control_2), GFP_KERNEL);
+		if (!f1a->control.reg_2) {
+			dev_err(&fc->dev, "Failed to allocate reg_2 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_2->regs =
+				kzalloc(f1a->button_bitmask_size, GFP_KERNEL);
+		if (!f1a->control.reg_2->regs) {
+			dev_err(&fc->dev, "Failed to allocate reg_2->regs control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_2->address = ctrl_base_addr;
+		f1a->control.reg_2->length = f1a->button_bitmask_size;
+		ctrl_base_addr += f1a->button_bitmask_size;
+	}
+
+	/* reg 3_4*/
+	if (f1a->query.has_tx_rx_map) {
+		f1a->control.reg_3_4 =
+			kzalloc(sizeof(struct f1a_0d_control_3_4), GFP_KERNEL);
+		if (!f1a->control.reg_3_4) {
+			dev_err(&fc->dev, "Failed to allocate reg_3_4 control registers.");
+			return -ENOMEM;
+		}
+		regSize = sizeof(struct f1a_0d_control_3_4n);
+		f1a->control.reg_3_4->regs =
+			kzalloc(regSize * f1a->sensor_button_count, GFP_KERNEL);
+		if (!f1a->control.reg_3_4->regs) {
+			dev_err(&fc->dev, "Failed to allocate reg_3_4->regs control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_3_4->address = ctrl_base_addr;
+		f1a->control.reg_3_4->length =
+					regSize * f1a->sensor_button_count;
+		ctrl_base_addr += regSize * f1a->sensor_button_count;
+	}
+
+	/* reg 5 */
+	if (f1a->query.has_perbutton_threshold) {
+		f1a->control.reg_5 =
+			kzalloc(sizeof(struct f1a_0d_control_5), GFP_KERNEL);
+		if (!f1a->control.reg_5) {
+			dev_err(&fc->dev, "Failed to allocate reg_5 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_5->regs =
+				kzalloc(f1a->sensor_button_count, GFP_KERNEL);
+		if (!f1a->control.reg_5->regs) {
+			dev_err(&fc->dev, "Failed to allocate reg_5->regs control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_5->address = ctrl_base_addr;
+		f1a->control.reg_5->length = f1a->sensor_button_count;
+		ctrl_base_addr += f1a->sensor_button_count;
+	}
+
+	/* reg 6 */
+	if (f1a->query.has_release_threshold) {
+		f1a->control.reg_6 =
+			kzalloc(sizeof(f1a->control.reg_6->regs), GFP_KERNEL);
+		if (!f1a->control.reg_6) {
+			dev_err(&fc->dev, "Failed to allocate reg_6 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_6->address = ctrl_base_addr;
+		ctrl_base_addr += sizeof(f1a->control.reg_6->regs);
+	}
+	/* reg 7 */
+	if (f1a->query.has_strongestbtn_hysteresis) {
+		f1a->control.reg_7 =
+			kzalloc(sizeof(f1a->control.reg_7->regs), GFP_KERNEL);
+		if (!f1a->control.reg_7) {
+			dev_err(&fc->dev, "Failed to allocate reg_7 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_7->address = ctrl_base_addr;
+		ctrl_base_addr += sizeof(f1a->control.reg_7->regs);
+	}
+	/* reg 8 */
+	if (f1a->query.has_filter_strength) {
+		f1a->control.reg_8 =
+			kzalloc(sizeof(f1a->control.reg_8->regs), GFP_KERNEL);
+		if (!f1a->control.reg_8) {
+			dev_err(&fc->dev, "Failed to allocate reg_8 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_8->address = ctrl_base_addr;
+		ctrl_base_addr += sizeof(f1a->control.reg_8->regs);
+	}
+	return 0;
+}
+
+
+
+static void rmi_f1a_free_memory(struct rmi_function_container *fc)
+{
+	union f1a_0d_query *query;
+	struct f1a_data *f1a = fc->data;
+
+	query = &f1a->query;
+	sysfs_remove_group(&fc->dev.kobj, &attrs_query);
+
+	if (query->has_general_control) {
+		sysfs_remove_file(&fc->dev.kobj, attrify(multibutton_report));
+		sysfs_remove_file(&fc->dev.kobj, attrify(filter_mode));
+	}
+
+	if (query->has_interrupt_enable)
+		sysfs_remove_file(&fc->dev.kobj,
+			attrify(interrupt_enabled_button));
+
+	if (query->has_multibutton_select)
+		sysfs_remove_file(&fc->dev.kobj, attrify(multi_button));
+
+	if (query->has_tx_rx_map)
+		sysfs_remove_file(&fc->dev.kobj, attrify(tx_rx_map));
+
+	if (query->has_perbutton_threshold)
+		sysfs_remove_file(&fc->dev.kobj, attrify(threshold_button));
+
+	if (query->has_release_threshold)
+		sysfs_remove_file(&fc->dev.kobj,
+				attrify(button_release_threshold));
+
+	if (query->has_strongestbtn_hysteresis)
+		sysfs_remove_file(&fc->dev.kobj,
+				attrify(strongest_button_hysteresis));
+
+	if (query->has_filter_strength)
+		sysfs_remove_file(&fc->dev.kobj, attrify(filter_strength));
+
+	if (f1a) {
+		kfree(f1a->button_data_buffer);
+		kfree(f1a->button_map);
+		kfree(f1a->control.reg_0);
+		if (f1a->control.reg_1) {
+			kfree(f1a->control.reg_1->regs);
+			kfree(f1a->control.reg_1);
+		}
+		if (f1a->control.reg_2) {
+			kfree(f1a->control.reg_2->regs);
+			kfree(f1a->control.reg_2);
+		}
+		if (f1a->control.reg_3_4) {
+			kfree(f1a->control.reg_3_4->regs);
+			kfree(f1a->control.reg_3_4);
+		}
+		if (f1a->control.reg_5) {
+			kfree(f1a->control.reg_5->regs);
+			kfree(f1a->control.reg_5);
+		}
+		kfree(f1a->control.reg_5);
+		kfree(f1a->control.reg_6);
+		kfree(f1a->control.reg_7);
+		kfree(f1a->control.reg_8);
+		kfree(f1a);
+		fc->data = NULL;
+	}
+}
+
+
+static int rmi_f1a_initialize(struct rmi_function_container *fc)
+{
+	int i;
+	int retval;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_device_platform_data *pdata;
+	struct f1a_data *f1a = fc->data;
+
+	dev_dbg(&fc->dev, "Intializing F1A values.");
+
+	/* initial all default values for f1a data here */
+	pdata = to_rmi_platform_data(rmi_dev);
+	if (pdata) {
+		if (!pdata->f1a_button_map)
+			dev_warn(&fc->dev, "%s - button_map is NULL", __func__);
+		else if (!pdata->f1a_button_map->map)
+			dev_warn(&fc->dev,
+				 "Platformdata button map is missing!\n");
+		else {
+			if (pdata->f1a_button_map->nbuttons !=
+					f1a->sensor_button_count)
+				dev_warn(&fc->dev,
+					"Platform data buttonmap size(%d) != number buttons on device(%d)\n",
+					pdata->f1a_button_map->nbuttons,
+					f1a->sensor_button_count);
+			f1a->button_map_size = min(f1a->sensor_button_count,
+					 pdata->f1a_button_map->nbuttons);
+			for (i = 0; i < f1a->button_map_size; i++)
+				f1a->button_map[i] =
+					pdata->f1a_button_map->map[i];
+		}
+	}
+
+	retval = rmi_f1a_read_control_parameters(rmi_dev, f1a);
+	if (retval < 0) {
+		dev_err(&fc->dev,
+			"Failed to initialize F1a control params.\n");
+		return retval;
+	}
+
+	mutex_init(&f1a->control_mutex);
+	mutex_init(&f1a->data_mutex);
+	return 0;
+}
+
+
+
+static int rmi_f1a_register_device(struct rmi_function_container *fc)
+{
+	int i;
+	int rc;
+	struct input_dev *input_dev;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct f1a_data *f1a = fc->data;
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&fc->dev, "Failed to allocate input device.\n");
+		return -ENOMEM;
+	}
+
+	f1a->input = input_dev;
+	snprintf(f1a->input_name, MAX_NAME_LEN, "%sfn%02x",
+			dev_name(&rmi_dev->dev), fc->fd.function_number);
+	input_dev->name = f1a->input_name;
+	snprintf(f1a->input_phys, MAX_NAME_LEN, "%s/input0", input_dev->name);
+	input_dev->phys = f1a->input_phys;
+	input_dev->dev.parent = &rmi_dev->dev;
+	input_set_drvdata(input_dev, f1a);
+
+	/* Set up any input events. */
+	set_bit(EV_SYN, input_dev->evbit);
+	set_bit(EV_KEY, input_dev->evbit);
+
+	/* manage button map using input subsystem */
+	input_dev->keycode = f1a->button_map;
+	input_dev->keycodesize = sizeof(f1a->button_map);
+	input_dev->keycodemax = f1a->button_map_size;
+
+	/* set bits for each button. */
+	for (i = 0; i < f1a->button_map_size; i++) {
+		set_bit(f1a->button_map[i], input_dev->keybit);
+		input_set_capability(input_dev, EV_KEY, f1a->button_map[i]);
+	}
+
+	rc = input_register_device(input_dev);
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to register input device.\n");
+		goto error_free_device;
+	}
+
+	return 0;
+
+error_free_device:
+	input_free_device(input_dev);
+
+	return rc;
+}
+
+
+static int rmi_f1a_create_sysfs(struct rmi_function_container *fc)
+{
+	struct f1a_data *f19 = fc->data;
+	union f1a_0d_query *query = &f19->query;
+
+	dev_dbg(&fc->dev, "Creating sysfs files.\n");
+	/* Set up sysfs device attributes. */
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_query) < 0) {
+		dev_err(&fc->dev, "Failed to create query sysfs files.");
+		return -ENODEV;
+	}
+
+	if (query->has_general_control) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(multibutton_report)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(filter_mode)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_interrupt_enable) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(interrupt_enabled_button)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_multibutton_select) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(multi_button)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_tx_rx_map) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(tx_rx_map)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_perbutton_threshold) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(threshold_button)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_release_threshold) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(button_release_threshold)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_strongestbtn_hysteresis) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(strongest_button_hysteresis)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_filter_strength) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(filter_strength)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+	return 0;
+}
+
+
+
+static int rmi_f1a_config(struct rmi_function_container *fc)
+{
+	int retval;
+	struct f1a_data *f1a = fc->data;
+	union f1a_0d_query *query = &f1a->query;
+	struct f1a_0d_control *control = &f1a->control;
+
+	if (query->has_general_control) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_0->address,
+			(u8 *)control->reg_0->regs,
+			sizeof(control->reg_0->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write reg0 to 0x%x\n",
+					__func__, control->reg_0->address);
+			return retval;
+		}
+	}
+	if (query->has_interrupt_enable) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_1->address,
+				(u8 *)control->reg_1->regs,
+				f1a->button_bitmask_size);
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write reg 1 to 0x%x\n",
+				__func__, control->reg_1->address);
+			return retval;
+		}
+	}
+	if (query->has_multibutton_select) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_2->address,
+				(u8 *)control->reg_2->regs,
+				f1a->button_bitmask_size);
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write reg 2 to 0x%x\n",
+				__func__, control->reg_2->address);
+			return -EINVAL;
+		}
+	}
+	if (query->has_tx_rx_map) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_3_4->address,
+				(u8 *)control->reg_3_4->regs,
+				f1a->sensor_button_count);
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write reg_3_4 to 0x%x\n",
+					__func__, control->reg_3_4->address);
+			return -EINVAL;
+		}
+	}
+	if (query->has_perbutton_threshold) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_5->address,
+				(u8 *)control->reg_5->regs,
+				f1a->sensor_button_count);
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write to reg 5 to 0x%x\n",
+				__func__, control->reg_5->address);
+			return retval;
+		}
+	}
+	if (query->has_release_threshold) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_6->address,
+			(u8 *)control->reg_6->regs,
+			sizeof(control->reg_6->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write  reg 6 to 0x%x\n",
+				__func__, control->reg_6->address);
+			return -EINVAL;
+		}
+	}
+	if (query->has_strongestbtn_hysteresis) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_7->address,
+			(u8 *)control->reg_7->regs,
+			sizeof(control->reg_7->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write reg 7 to 0x%x\n",
+				__func__, control->reg_7->address);
+			return -EINVAL;
+		}
+	}
+	if (query->has_filter_strength) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_8->address,
+			(u8 *)control->reg_8->regs,
+			sizeof(control->reg_8->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write reg 8 to 0x%x\n",
+				__func__, control->reg_8->address);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+
+static int rmi_f1a_reset(struct rmi_function_container *fc)
+{
+	/* we do nnothing here */
+	return 0;
+}
+
+
+static void rmi_f1a_remove(struct rmi_function_container *fc)
+{
+	struct f1a_data *f1a = fc->data;
+
+	input_unregister_device(f1a->input);
+
+	rmi_f1a_free_memory(fc);
+}
+
+static int rmi_f1a_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	int error;
+	int button;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct f1a_data *f1a = fc->data;
+	u16 data_base_addr = fc->fd.data_base_addr;
+
+	/* Read the button data. */
+	error = rmi_read_block(rmi_dev, data_base_addr, f1a->button_data_buffer,
+			f1a->button_bitmask_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 < f1a->sensor_button_count; button++) {
+		int button_reg;
+		int button_shift;
+		bool button_status;
+
+		/* determine which data byte the button status is in */
+		button_reg = button / 8;
+		/* bit shift to get button's status */
+		button_shift = button % 8;
+		button_status =
+		    ((f1a->button_data_buffer[button_reg] >> button_shift)
+			& 0x01) != 0;
+
+		dev_dbg(&fc->dev, "%s: Button %d (code %d) -> %d.\n",
+			__func__, button, f1a->button_map[button],
+				button_status);
+		/* Generate an event here. */
+		input_report_key(f1a->input, f1a->button_map[button],
+					button_status);
+	}
+
+	input_sync(f1a->input); /* sync after groups of events */
+	return 0;
+}
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x1a,
+	.init = rmi_f1a_init,
+	.config = rmi_f1a_config,
+	.reset = rmi_f1a_reset,
+	.attention = rmi_f1a_attention,
+	.remove = rmi_f1a_remove
+};
+
+static int __init rmi_f1a_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_f1a_module_exit(void)
+{
+	rmi_unregister_function_driver(&function_handler);
+}
+
+/* sysfs functions */
+/* Query */
+simple_show_union_struct_unsigned(query, max_button_count)
+simple_show_union_struct_unsigned(query, has_general_control)
+simple_show_union_struct_unsigned(query, has_interrupt_enable)
+simple_show_union_struct_unsigned(query, has_multibutton_select)
+simple_show_union_struct_unsigned(query, has_tx_rx_map)
+simple_show_union_struct_unsigned(query, has_perbutton_threshold)
+simple_show_union_struct_unsigned(query, has_release_threshold)
+simple_show_union_struct_unsigned(query, has_strongestbtn_hysteresis)
+simple_show_union_struct_unsigned(query, has_filter_strength)
+
+/* Control */
+show_store_union_struct_unsigned(control, reg_0, multibutton_report)
+show_store_union_struct_unsigned(control, reg_0, filter_mode)
+show_store_union_struct_unsigned(control, reg_6, button_release_threshold)
+show_store_union_struct_unsigned(control, reg_7, strongest_button_hysteresis)
+show_store_union_struct_unsigned(control, reg_8, filter_strength)
+
+/* repeated register functions */
+show_store_repeated_union_struct_unsigned(control, reg_1,
+						interrupt_enabled_button)
+show_store_repeated_union_struct_unsigned(control, reg_2, multi_button)
+show_store_repeated_union_struct_unsigned(control, reg_5, threshold_button)
+
+static ssize_t rmi_fn_1a_tx_rx_map_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rmi_function_container *fc;
+	struct FUNCTION_DATA *data;
+	struct f1a_0d_control *control;
+	int reg_length;
+	int result, size = 0;
+	char *temp;
+	int i;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	control = &data->control;
+	/* Read current regtype values */
+	reg_length = control->reg_3_4->length;
+	result = rmi_read_block(fc->rmi_dev, control->reg_3_4->address,
+			(u8 *)control->reg_3_4->regs,
+			reg_length * sizeof(u8));
+
+	if (result < 0) {
+		dev_dbg(dev, "%s : Could not read regtype at 0x%x\n Data may be outdated.",
+				__func__, control->reg_3_4->address);
+	}
+	temp = buf;
+	for (i = 0; i < reg_length/2; i++) {
+		result = snprintf(temp, PAGE_SIZE - size, "%u-%u ",
+				control->reg_3_4->regs[i].transmitterbtn,
+				control->reg_3_4->regs[i].receiverbtn);
+		if (result < 0) {
+			dev_err(dev, "%s : Could not write output.", __func__);
+			return result;
+		}
+		size += result;
+		temp += result;
+	}
+	result = snprintf(temp, PAGE_SIZE - size, "\n");
+	if (result < 0) {
+			dev_err(dev, "%s : Could not write output.", __func__);
+			return result;
+	}
+	return size + result;
+}
+
+module_init(rmi_f1a_module_init);
+module_exit(rmi_f1a_module_exit);
+
+MODULE_AUTHOR("Vivian Ly <vly@synaptics.com>");
+MODULE_DESCRIPTION("RMI F1a module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 13/17] input: RMI4 F21 Force sensing
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (11 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 12/17] input: RMI4 F1A simple " Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-17 22:17 ` [RFC PATCH 14/17] input: RMI4 F30 GPIO/LED control Christopher Heiny
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_f21.c |  832 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 832 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f21.c b/drivers/input/rmi4/rmi_f21.c
new file mode 100644
index 0000000..2e37c75
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f21.c
@@ -0,0 +1,832 @@
+/*
+ * Copyright (c) 2012-2012 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define FUNCTION_DATA rmi_fn_21_data
+#define FNUM 21
+
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+union f21_2df_query {
+	struct {
+		/* Query 0 */
+		u8 max_force_sensor_count:3;
+		u8 f21_2df_query0_b3__6:4;
+		u8 has_high_resolution:1;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f21_2df_control_0__3 {
+	struct {
+		/* Control 0 */
+		u8 reporting_mode:3;
+		u8 no_rezero:1;
+		u8 ctl0_reserved:4;
+
+		/* Control 1 */
+		u8 force_click_threshold:8;
+
+		/* Control 2 */
+		u8 int_en_force_0:1;
+		u8 int_en_force_1:1;
+		u8 int_en_force_2:1;
+		u8 int_en_force_3:1;
+		u8 int_en_force_4:1;
+		u8 int_en_force_5:1;
+		u8 int_en_force_6:1;
+		u8 int_en_click:1;
+
+		/* Control 3 */
+		u8 force_interrupt_threshold:8;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[4];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+struct f21_2df_control_4n {
+	/*Control 4.* */
+	u8 one_newton_linear_calibration:7;
+	u8 use_cfg_cal:1;
+} __attribute__((__packed__));
+
+struct f21_2df_control_4 {
+		struct f21_2df_control_4n *regs;
+		u16 address;
+		u8 length;
+} __attribute__((__packed__));
+
+struct f21_2df_control_5n {
+	/* Control 5 */
+	u8 one_newton_nonlinear_calibration;
+};
+
+struct f21_2df_control_5 {
+		struct f21_2df_control_5n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f21_2df_control_6n {
+	/*Control 6.* */
+	u8 x_location;
+};
+
+struct f21_2df_control_6 {
+		struct f21_2df_control_6n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f21_2df_control_7n {
+	/*Control 7.* */
+	u8 y_location;
+};
+
+struct f21_2df_control_7 {
+		struct f21_2df_control_7n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f21_2df_control_8n {
+	/*Control 8.* */
+	u8 transmitter_force_sensor;
+};
+
+struct f21_2df_control_8 {
+		struct f21_2df_control_8n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f21_2df_control_9n {
+	/*Control 9.* */
+	u8 receiver_force_sensor;
+};
+
+struct f21_2df_control_9 {
+		struct f21_2df_control_9n *regs;
+		u16 address;
+		u8 length;
+};
+
+
+#define RMI_F21_NUM_CTRL_REGS 8
+struct f21_2df_control {
+	/* Control 0-3 */
+	union f21_2df_control_0__3 *reg_0__3;
+
+	/* Control 4 */
+	struct f21_2df_control_4 *reg_4;
+
+	/* Control 5 */
+	struct f21_2df_control_5 *reg_5;
+
+	/* Control 6 */
+	struct f21_2df_control_6 *reg_6;
+
+	/* Control 7 */
+	struct f21_2df_control_7 *reg_7;
+
+	/* Control 8 */
+	struct f21_2df_control_8 *reg_8;
+
+	/* Control 9 */
+	struct f21_2df_control_9 *reg_9;
+};
+
+union f21_2df_data_2 {
+	struct {
+		/* Data 2 */
+		u8 force_click:1;
+		u8 f21_2df_control0_b2__7:7;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+struct f21_2df_data {
+	/* Data 0 */
+	struct {
+		u8 *force_hi_lo;
+		u16 address;
+	} reg_0__1;
+
+	/* Data 2 */
+	union f21_2df_data_2 *reg_2;
+};
+
+#define F21_REZERO_CMD 0x01
+
+/* data specific to fn $21 that needs to be kept around */
+struct rmi_fn_21_data {
+	union f21_2df_query query;
+	struct f21_2df_control control;
+	struct f21_2df_data data;
+
+	struct mutex control_mutex;
+	struct mutex data_mutex;
+};
+
+static int rmi_f21_alloc_memory(struct rmi_function_container *fc);
+
+static void rmi_f21_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f21_initialize(struct rmi_function_container *fc);
+
+static int rmi_f21_config(struct rmi_function_container *fc);
+
+static int rmi_f21_create_sysfs(struct rmi_function_container *fc);
+
+/* Sysfs files */
+
+/* Query sysfs files */
+
+
+show_union_struct_prototype(max_force_sensor_count)
+show_union_struct_prototype(has_high_resolution)
+
+static struct attribute *attrs[] = {
+	attrify(max_force_sensor_count),
+	attrify(has_high_resolution),
+	NULL
+};
+static struct attribute_group attrs_query = GROUP(attrs);
+/* Control sysfs files */
+
+show_store_union_struct_prototype(reporting_mode)
+show_store_union_struct_prototype(no_rezero)
+show_store_union_struct_prototype(force_click_threshold)
+show_store_union_struct_prototype(int_en_force_0)
+show_store_union_struct_prototype(int_en_force_1)
+show_store_union_struct_prototype(int_en_force_2)
+show_store_union_struct_prototype(int_en_force_3)
+show_store_union_struct_prototype(int_en_force_4)
+show_store_union_struct_prototype(int_en_force_5)
+show_store_union_struct_prototype(int_en_force_6)
+show_store_union_struct_prototype(int_en_click)
+show_store_union_struct_prototype(force_interrupt_threshold)
+
+show_store_union_struct_prototype(use_cfg_cal)
+show_store_union_struct_prototype(one_newton_linear_calibration)
+show_store_union_struct_prototype(one_newton_nonlinear_calibration)
+show_store_union_struct_prototype(x_location)
+show_store_union_struct_prototype(y_location)
+show_store_union_struct_prototype(transmitter_force_sensor)
+show_store_union_struct_prototype(receiver_force_sensor)
+
+
+static struct attribute *attrs2[] = {
+	attrify(reporting_mode),
+	attrify(no_rezero),
+	attrify(force_click_threshold),
+	attrify(int_en_click),
+	attrify(force_interrupt_threshold),
+	attrify(use_cfg_cal),
+	attrify(one_newton_linear_calibration),
+	attrify(one_newton_nonlinear_calibration),
+	attrify(x_location),
+	attrify(y_location),
+	attrify(transmitter_force_sensor),
+	attrify(receiver_force_sensor),
+	NULL
+};
+static struct attribute_group attrs_control = GROUP(attrs2);
+
+/* Data sysfs files */
+show_union_struct_prototype(force)
+show_union_struct_prototype(force_click)
+
+static struct attribute *attrs3[] = {
+	attrify(force),
+	attrify(force_click),
+	NULL
+};
+static struct attribute_group attrs_data = GROUP(attrs3);
+
+/* Command sysfs files */
+
+static ssize_t rmi_fn_21_rezero_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count);
+DEVICE_ATTR(rezero, RMI_WO_ATTR,
+		rmi_show_error,
+		rmi_fn_21_rezero_store);
+
+static struct attribute *attrs4[] = {
+	attrify(rezero),
+	NULL
+};
+static struct attribute_group attrs_command = GROUP(attrs4);
+
+static int rmi_f21_init(struct rmi_function_container *fc)
+{
+	int retval = 0;
+
+	dev_info(&fc->dev, "Intializing F21.");
+
+	retval = rmi_f21_alloc_memory(fc);
+	if (retval < 0)
+		goto error_exit;
+
+	retval = rmi_f21_initialize(fc);
+	if (retval < 0)
+		goto error_exit;
+
+	retval = rmi_f21_create_sysfs(fc);
+	if (retval < 0)
+		goto error_exit;
+
+	return retval;
+
+error_exit:
+	rmi_f21_free_memory(fc);
+
+	return retval;
+}
+
+static int rmi_f21_alloc_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_21_data *f21;
+
+	f21 = kzalloc(sizeof(struct rmi_fn_21_data), GFP_KERNEL);
+	if (!f21) {
+		dev_err(&fc->dev, "Failed to allocate rmi_fn_21_data.\n");
+		return -ENOMEM;
+	}
+	fc->data = f21;
+
+	return 0;
+}
+
+
+static void rmi_f21_free_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_21_data *f21 = fc->data;
+	u8 int_num = f21->query.max_force_sensor_count;
+	sysfs_remove_group(&fc->dev.kobj, &attrs_query);
+	sysfs_remove_group(&fc->dev.kobj, &attrs_control);
+	switch (int_num) {
+	case 7:
+		sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_6));
+	case 6:
+		sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_5));
+	case 5:
+		sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_4));
+	case 4:
+		sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_3));
+	case 3:
+		sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_2));
+	case 2:
+		sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_1));
+	case 1:
+		sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_0));
+	default:
+		break;
+	}
+	sysfs_remove_group(&fc->dev.kobj, &attrs_data);
+	sysfs_remove_group(&fc->dev.kobj, &attrs_command);
+	if (f21) {
+		kfree(f21->control.reg_0__3);
+		kfree(f21->control.reg_4->regs);
+		kfree(f21->control.reg_4);
+		kfree(f21->control.reg_5->regs);
+		kfree(f21->control.reg_5);
+		kfree(f21->control.reg_6->regs);
+		kfree(f21->control.reg_6);
+		kfree(f21->control.reg_7->regs);
+		kfree(f21->control.reg_7);
+		kfree(f21->control.reg_8->regs);
+		kfree(f21->control.reg_8);
+		kfree(f21);
+		fc->data = NULL;
+	}
+}
+
+
+static int rmi_f21_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_fn_21_data *f21 = fc->data;
+	int retval = 0;
+	u16 next_loc;
+
+	/* Read F21 Query Data */
+	f21->query.address = fc->fd.query_base_addr;
+	retval = rmi_read_block(fc->rmi_dev, f21->query.address,
+		(u8 *)&f21->query, sizeof(f21->query.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read query registers from %#06x.\n",
+				f21->query.address);
+		return retval;
+	}
+
+	/* Initialize Control Data */
+	next_loc = fc->fd.control_base_addr;
+
+	f21->control.reg_0__3 =
+			kzalloc(sizeof(union f21_2df_control_0__3), GFP_KERNEL);
+	if (!f21->control.reg_0__3) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f21->control.reg_0__3->address = next_loc;
+	next_loc += sizeof(f21->control.reg_0__3->regs);
+	retval = rmi_read_block(fc->rmi_dev, f21->control.reg_0__3->address,
+			f21->control.reg_0__3->regs,
+			sizeof(f21->control.reg_0__3->regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read control registers 0-3 from %#06x.\n",
+			f21->control.reg_0__3->address);
+		return retval;
+	}
+
+	f21->control.reg_4 =
+			kzalloc(sizeof(struct f21_2df_control_4), GFP_KERNEL);
+	if (!f21->control.reg_4) {
+		dev_err(&fc->dev, "Failed to allocate control register.");
+		return -ENOMEM;
+	}
+	f21->control.reg_4->length = f21->query.max_force_sensor_count;
+	f21->control.reg_4->regs = kzalloc(sizeof(struct f21_2df_control_4n) *
+			f21->control.reg_4->length, GFP_KERNEL);
+	if (!f21->control.reg_4->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f21->control.reg_4->address = next_loc;
+	next_loc += f21->control.reg_4->length;
+	retval = rmi_read_block(fc->rmi_dev, f21->control.reg_4->address,
+		(u8 *) f21->control.reg_4->regs, f21->control.reg_4->length);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read Control register 4 from %#06x.\n",
+			f21->control.reg_4->address);
+		return retval;
+	}
+
+
+	f21->control.reg_5 =
+			kzalloc(sizeof(struct f21_2df_control_5), GFP_KERNEL);
+	if (!f21->control.reg_5) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f21->control.reg_5->length = f21->query.max_force_sensor_count;
+	f21->control.reg_5->regs = kzalloc(sizeof(struct f21_2df_control_5n) *
+				f21->control.reg_5->length, GFP_KERNEL);
+	if (!f21->control.reg_5->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f21->control.reg_5->address = next_loc;
+	next_loc += f21->control.reg_5->length;
+	retval = rmi_read_block(fc->rmi_dev, f21->control.reg_5->address,
+				(u8 *) f21->control.reg_5->regs,
+				f21->control.reg_5->length);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read Control register 5 from %#06x.\n",
+				f21->control.reg_5->address);
+		return retval;
+	}
+
+	f21->control.reg_6 =
+			kzalloc(sizeof(struct f21_2df_control_6), GFP_KERNEL);
+	if (!f21->control.reg_6) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f21->control.reg_6->length = f21->query.max_force_sensor_count;
+	f21->control.reg_6->regs = kzalloc(sizeof(struct f21_2df_control_6n) *
+				f21->control.reg_6->length, GFP_KERNEL);
+	if (!f21->control.reg_6->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f21->control.reg_6->address = next_loc;
+	next_loc += f21->control.reg_6->length;
+	retval = rmi_read_block(fc->rmi_dev, f21->control.reg_6->address,
+			(u8 *) f21->control.reg_6->regs,
+				sizeof(f21->control.reg_6->regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read control register 6 from %#06x.\n",
+					f21->control.reg_6->address);
+		return retval;
+	}
+
+	f21->control.reg_7 =
+			kzalloc(sizeof(struct f21_2df_control_7), GFP_KERNEL);
+	if (!f21->control.reg_7) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f21->control.reg_7->length = f21->query.max_force_sensor_count;
+	f21->control.reg_7->regs = kzalloc(sizeof(struct f21_2df_control_7n) *
+				f21->control.reg_7->length, GFP_KERNEL);
+	if (!f21->control.reg_7->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f21->control.reg_7->address = next_loc;
+	next_loc += f21->control.reg_7->length;
+	retval = rmi_read_block(fc->rmi_dev, f21->control.reg_7->address,
+		(u8 *) f21->control.reg_7->regs, f21->control.reg_7->length);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read control register 7 from %#06x.\n",
+				f21->control.reg_7->address);
+		return retval;
+	}
+
+	f21->control.reg_8 =
+			kzalloc(sizeof(struct f21_2df_control_8), GFP_KERNEL);
+	if (!f21->control.reg_8) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f21->control.reg_8->length = f21->query.max_force_sensor_count;
+	f21->control.reg_8->regs = kzalloc(sizeof(struct f21_2df_control_8n) *
+					f21->control.reg_8->length, GFP_KERNEL);
+	if (!f21->control.reg_8->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f21->control.reg_8->address = next_loc;
+	next_loc += f21->control.reg_8->length;
+	retval = rmi_read_block(fc->rmi_dev, f21->control.reg_8->address,
+		(u8 *) f21->control.reg_8->regs, f21->control.reg_8->length);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read control register 8 from %#06x.\n",
+					f21->control.reg_8->address);
+		return retval;
+	}
+
+	f21->control.reg_9 =
+			kzalloc(sizeof(struct f21_2df_control_9), GFP_KERNEL);
+	if (!f21->control.reg_9) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f21->control.reg_9->length = f21->query.max_force_sensor_count;
+	f21->control.reg_9->regs =
+			kzalloc(sizeof(struct f21_2df_control_9n)
+			*f21->control.reg_9->length, GFP_KERNEL);
+	if (!f21->control.reg_9->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f21->control.reg_9->address = next_loc;
+	retval = rmi_read_block(fc->rmi_dev, f21->control.reg_9->address,
+		(u8 *) f21->control.reg_9->regs, f21->control.reg_9->length);
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read control register 9 from %#06x.\n",
+					f21->control.reg_9->address);
+		return retval;
+	}
+
+
+	/* initialize data registers */
+	next_loc = fc->fd.data_base_addr;
+
+	f21->data.reg_0__1.force_hi_lo = kzalloc(
+			2 * f21->query.max_force_sensor_count * sizeof(u8),
+			GFP_KERNEL);
+	if (!f21->data.reg_0__1.force_hi_lo) {
+		dev_err(&fc->dev, "Failed to allocate data registers.");
+		return -ENOMEM;
+	}
+	f21->data.reg_0__1.address = next_loc;
+	next_loc += 2 * f21->query.max_force_sensor_count;
+
+	f21->data.reg_2 =
+			kzalloc(sizeof(union f21_2df_data_2), GFP_KERNEL);
+	if (!f21->data.reg_2) {
+		dev_err(&fc->dev, "Failed to allocate data registers.");
+		return -ENOMEM;
+	}
+	f21->control.reg_0__3->address = next_loc;
+
+	mutex_init(&f21->control_mutex);
+	return 0;
+}
+
+
+static int rmi_f21_create_sysfs(struct rmi_function_container *fc)
+{
+	struct rmi_fn_21_data *f21 = fc->data;
+	u8 int_num = f21->query.max_force_sensor_count;
+	int rc;
+
+	if (int_num > 7)
+		int_num = 7;
+	dev_dbg(&fc->dev, "Creating sysfs files.");
+
+	/* Set up sysfs device attributes. */
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_query) < 0) {
+		dev_err(&fc->dev, "Failed to create query sysfs files.");
+		return -ENODEV;
+	}
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_control) < 0) {
+		dev_err(&fc->dev, "Failed to create control sysfs files.");
+		return -ENODEV;
+	}
+	switch (int_num) {
+	case 7:
+		rc = sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_6));
+		break;
+	case 6:
+		rc = sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_5));
+		break;
+	case 5:
+		rc = sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_4));
+		break;
+	case 4:
+		rc = sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_3));
+		break;
+	case 3:
+		rc = sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_2));
+		break;
+	case 2:
+		rc = sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_1));
+		break;
+	case 1:
+		rc = sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_0));
+		break;
+	default:
+		rc = 0;
+		break;
+	}
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to create control sysfs files.\n");
+		return rc;
+	}
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_data) < 0) {
+		dev_err(&fc->dev, "Failed to create data sysfs files.\n");
+		return -ENODEV;
+	}
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_command) < 0) {
+		dev_err(&fc->dev, "Failed to create command sysfs files.\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+
+static int rmi_f21_config(struct rmi_function_container *fc)
+{
+	struct rmi_fn_21_data *data = fc->data;
+/* repeated register functions */
+
+	/* Write Control Register values back to device */
+	rmi_write_block(fc->rmi_dev, data->control.reg_0__3->address,
+				(u8 *)data->control.reg_0__3,
+				sizeof(data->control.reg_0__3->regs));
+
+	rmi_write_block(fc->rmi_dev, data->control.reg_4->address,
+			(u8 *) data->control.reg_4->regs,
+			data->control.reg_4->length);
+
+	rmi_write_block(fc->rmi_dev, data->control.reg_5->address,
+			(u8 *) data->control.reg_5->regs,
+			data->control.reg_5->length);
+
+	rmi_write_block(fc->rmi_dev, data->control.reg_6->address,
+			(u8 *) data->control.reg_6->regs,
+			data->control.reg_6->length);
+
+	rmi_write_block(fc->rmi_dev, data->control.reg_7->address,
+			(u8 *) data->control.reg_7->regs,
+			data->control.reg_7->length);
+
+	rmi_write_block(fc->rmi_dev, data->control.reg_8->address,
+			(u8 *) data->control.reg_8->regs,
+			data->control.reg_8->length);
+
+	rmi_write_block(fc->rmi_dev, data->control.reg_9->address,
+			(u8 *) data->control.reg_9->regs,
+			data->control.reg_9->length);
+
+	return 0;
+}
+
+static void rmi_f21_remove(struct rmi_function_container *fc)
+{
+
+	dev_info(&fc->dev, "Removing F21.");
+	rmi_f21_free_memory(fc);
+}
+
+/* sysfs functions */
+/* Query */
+simple_show_union_struct_unsigned(query, max_force_sensor_count)
+simple_show_union_struct_unsigned(query, has_high_resolution)
+
+/* Control */
+show_store_union_struct_unsigned(control, reg_0__3, reporting_mode)
+show_store_union_struct_unsigned(control, reg_0__3, no_rezero)
+
+show_store_union_struct_unsigned(control, reg_0__3, force_click_threshold)
+
+show_store_union_struct_unsigned(control, reg_0__3, int_en_force_0)
+show_store_union_struct_unsigned(control, reg_0__3, int_en_force_1)
+show_store_union_struct_unsigned(control, reg_0__3, int_en_force_2)
+show_store_union_struct_unsigned(control, reg_0__3, int_en_force_3)
+show_store_union_struct_unsigned(control, reg_0__3, int_en_force_4)
+show_store_union_struct_unsigned(control, reg_0__3, int_en_force_5)
+show_store_union_struct_unsigned(control, reg_0__3, int_en_force_6)
+show_store_union_struct_unsigned(control, reg_0__3, int_en_click)
+show_store_union_struct_unsigned(control, reg_0__3, force_interrupt_threshold)
+
+
+/* repeated register functions */
+show_store_repeated_union_struct_unsigned(control, reg_4, use_cfg_cal)
+show_store_repeated_union_struct_unsigned(control, reg_4,
+					  one_newton_linear_calibration)
+show_store_repeated_union_struct_unsigned(control, reg_5,
+					  one_newton_nonlinear_calibration)
+show_store_repeated_union_struct_unsigned(control, reg_6, x_location)
+show_store_repeated_union_struct_unsigned(control, reg_7, y_location)
+show_store_repeated_union_struct_unsigned(control, reg_8,
+					  transmitter_force_sensor)
+show_store_repeated_union_struct_unsigned(control, reg_9,
+					  receiver_force_sensor)
+
+/* Data */
+static ssize_t rmi_fn_21_force_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf) {
+	struct rmi_function_container *fc;
+	struct FUNCTION_DATA *data;
+	int reg_length;
+	int result, size = 0;
+	char *temp;
+	int i;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	/* Read current regtype values */
+	reg_length = data->query.max_force_sensor_count;
+	result = rmi_read_block(fc->rmi_dev, data->data.reg_0__1.address,
+			data->data.reg_0__1.force_hi_lo,
+			2 * reg_length * sizeof(u8));
+
+	if (result < 0) {
+		dev_err(dev, "Could not read regtype at %#06x. Data may be outdated.",
+					data->data.reg_0__1.address);
+	}
+	temp = buf;
+	for (i = 0; i < reg_length; i++) {
+		result = snprintf(temp, PAGE_SIZE - size, "%d ",
+			data->data.reg_0__1.force_hi_lo[i] * (2 << 3)
+			+ data->data.reg_0__1.force_hi_lo[i + reg_length]);
+		if (result < 0) {
+			dev_err(dev, "%s : Could not write output.", __func__);
+			return result;
+		}
+		size += result;
+		temp += result;
+	}
+	result = snprintf(temp, PAGE_SIZE - size, "\n");
+	if (result < 0) {
+			dev_err(dev, "%s : Could not write output.", __func__);
+			return result;
+	}
+	return size + result;
+}
+
+show_union_struct_unsigned(data, reg_2, force_click)
+
+/* Command */
+
+static ssize_t rmi_fn_21_rezero_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_21_data *f21;
+	struct rmi_driver *driver;
+	u8 command;
+
+	fc = to_rmi_function_container(dev);
+	f21 = 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)F21_REZERO_CMD;
+
+	/* 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 struct rmi_function_handler function_handler = {
+	.func = 0x21,
+	.init = rmi_f21_init,
+	.config = rmi_f21_config,
+	.reset = NULL,
+	.attention = NULL,
+	.remove = rmi_f21_remove
+};
+
+static int __init rmi_f21_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_f21_module_exit(void)
+{
+	rmi_unregister_function_driver(&function_handler);
+}
+
+module_init(rmi_f21_module_init);
+module_exit(rmi_f21_module_exit);
+
+MODULE_AUTHOR("Daniel Rosenberg <daniel.rosenberg@synaptics.com>");
+MODULE_DESCRIPTION("RMI F21 module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 14/17] input: RMI4 F30 GPIO/LED control
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (12 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 13/17] input: RMI4 F21 Force sensing Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-27 22:58   ` Linus Walleij
  2012-08-17 22:17 ` [RFC PATCH 15/17] input: RMI4 F34 device reflash Christopher Heiny
                   ` (4 subsequent siblings)
  18 siblings, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_f30.c | 1247 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1247 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c
new file mode 100644
index 0000000..4bc2932
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f30.c
@@ -0,0 +1,1247 @@
+/*
+ * Copyright (c) 2012 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#define FUNCTION_DATA rmi_fn_30_data
+#define FNUM 30
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+
+#define MAX_LEN 256
+
+/* data specific to fn $30 that needs to be kept around */
+union f30_query {
+	struct {
+		u8 extended_patterns:1;
+		u8 has_mappable_buttons:1;
+		u8 has_led:1;
+		u8 has_gpio:1;
+		u8 has_haptic:1;
+		u8 has_gpio_driver_control:1;
+		u8 gpio_led_count:5;
+	};
+	struct {
+		u8 regs[2];
+		u16 address;
+	};
+};
+
+struct f30_gpio_ctrl_0n {
+	u8 led_sel;
+};
+
+struct f30_gpio_ctrl_0 {
+	struct f30_gpio_ctrl_0n *regs;
+	u16 address;
+	u8 length;
+};
+
+union f30_gpio_ctrl_1 {
+	struct {
+		u8 gpio_debounce:1;
+		u8 reserved:3;
+		u8 halt:1;
+		u8 halted:1;
+		u8 reserved2:2;
+	};
+	struct {
+		u8 regs[1];
+		u16 address;
+	};
+};
+
+struct f30_gpio_ctrl_2n {
+	u8 dir;
+};
+
+struct f30_gpio_ctrl_2 {
+	struct f30_gpio_ctrl_2n *regs;
+	u16 address;
+	u8 length;
+};
+
+struct f30_gpio_ctrl_3n {
+	u8 gpiodata;
+};
+
+struct f30_gpio_ctrl_3 {
+	struct f30_gpio_ctrl_3n *regs;
+	u16 address;
+	u8 length;
+};
+
+struct f30_gpio_ctrl_4n {
+	u8 led_act;
+};
+
+struct f30_gpio_ctrl_4 {
+	struct f30_gpio_ctrl_4n *regs;
+	u16 address;
+	u8 length;
+};
+
+struct f30_gpio_ctrl_5n {
+	u8 ramp_period_a;
+	u8 ramp_period_b;
+};
+
+struct f30_gpio_ctrl_5 {
+	struct f30_gpio_ctrl_5n *regs;
+	u16 address;
+	u8 length;
+};
+
+union f30_gpio_ctrl_6n {
+	struct {
+		u8 reserved:1;
+		u8 SPCTRL:3;
+		u8 STRPD:1;
+		u8 reserved2:1;
+		u8 STRPU:1;
+		u8 reserved3:1;
+	};
+	struct {
+		u8 brightness:4;
+		u8 pattern:4;
+	};
+};
+
+struct f30_gpio_ctrl_6 {
+	union f30_gpio_ctrl_6n *regs;
+	u16 address;
+	u8 length;
+};
+
+struct f30_gpio_ctrl_7n {
+	u8 capacity_btn_nbr:5;
+	u8 valid:1;
+	u8 invert:1;
+	u8 open_drain:1;
+};
+
+struct f30_gpio_ctrl_7 {
+	struct f30_gpio_ctrl_7n *regs;
+	u16 address;
+	u8 length;
+};
+
+struct f30_gpio_ctrl_8n {
+	u8 gpio_ctrl8_0;
+	u8 gpio_ctrl8_1;
+};
+
+struct f30_gpio_ctrl_8 {
+	struct f30_gpio_ctrl_8n *regs;
+	u16 address;
+	u8 length;
+};
+
+union f30_gpio_ctrl_9 {
+	struct {
+		u8 haptic_duration;
+	};
+	struct {
+		u8 regs[1];
+		u16 address;
+	};
+};
+
+struct f30_control {
+	struct f30_gpio_ctrl_0 *reg_0;
+	union f30_gpio_ctrl_1 *reg_1;
+	struct f30_gpio_ctrl_2 *reg_2;
+	struct f30_gpio_ctrl_3 *reg_3;
+	struct f30_gpio_ctrl_4 *reg_4;
+	struct f30_gpio_ctrl_5 *reg_5;
+	struct f30_gpio_ctrl_6 *reg_6;
+	struct f30_gpio_ctrl_7 *reg_7;
+	struct f30_gpio_ctrl_8 *reg_8;
+	union f30_gpio_ctrl_9 *reg_9;
+};
+
+struct f30_data_0n {
+	u8 gpi_led_data:1;
+};
+
+struct f30_data_0 {
+	struct f30_data_0n *regs;
+	u16 address;
+	u8 length;
+};
+
+struct f30_data {
+	struct f30_data_0 *datareg_0;
+	u16 address;
+	u8 length;
+};
+
+struct rmi_fn_30_data {
+	union f30_query query;
+	struct f30_data data;
+	struct f30_control control;
+	unsigned char gpioled_count;
+	unsigned char gpioled_bitmask_size;
+	unsigned char gpioled_byte_size;
+	unsigned char *button_data_buffer;
+	unsigned char button_bitmask_size;
+	unsigned short *gpioled_map;
+	char input_name[MAX_LEN];
+	char input_phys[MAX_LEN];
+	struct input_dev *input;
+	struct mutex control_mutex;
+	struct mutex data_mutex;
+};
+
+static int rmi_f30_alloc_memory(struct rmi_function_container *fc);
+
+static void rmi_f30_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f30_initialize(struct rmi_function_container *fc);
+
+static int rmi_f30_register_device(struct rmi_function_container *fc);
+
+static int rmi_f30_config(struct rmi_function_container *fc);
+
+static int rmi_f30_create_sysfs(struct rmi_function_container *fc);
+
+
+/* Query sysfs files */
+
+show_union_struct_prototype(extended_patterns)
+show_union_struct_prototype(has_mappable_buttons)
+show_union_struct_prototype(has_led)
+show_union_struct_prototype(has_gpio)
+show_union_struct_prototype(has_haptic)
+show_union_struct_prototype(has_gpio_driver_control)
+show_union_struct_prototype(gpio_led_count)
+
+static struct attribute *attrs1[] = {
+	attrify(extended_patterns),
+	attrify(has_mappable_buttons),
+	attrify(has_led),
+	attrify(has_gpio),
+	attrify(has_haptic),
+	attrify(has_gpio_driver_control),
+	attrify(gpio_led_count),
+	NULL
+};
+
+static struct attribute_group attrs_query = GROUP(attrs1);
+
+/* Control sysfs files */
+
+show_store_union_struct_prototype(led_sel)
+show_store_union_struct_prototype(gpio_debounce)
+show_store_union_struct_prototype(halt)
+show_store_union_struct_prototype(halted)
+show_store_union_struct_prototype(dir)
+show_store_union_struct_prototype(gpiodata)
+show_store_union_struct_prototype(led_act)
+show_store_union_struct_prototype(ramp_period_a)
+show_store_union_struct_prototype(ramp_period_b)
+show_store_union_struct_prototype(SPCTRL)
+show_store_union_struct_prototype(STRPD)
+show_store_union_struct_prototype(STRPU)
+show_store_union_struct_prototype(brightness)
+show_store_union_struct_prototype(pattern)
+
+show_store_union_struct_prototype(capacity_btn_nbr)
+show_store_union_struct_prototype(valid)
+show_store_union_struct_prototype(invert)
+show_store_union_struct_prototype(open_drain)
+show_store_union_struct_prototype(gpio_ctrl8_0)
+show_store_union_struct_prototype(gpio_ctrl8_1)
+show_store_union_struct_prototype(haptic_duration)
+
+/* Data sysfs files */
+show_store_union_struct_prototype(gpi_led_data)
+
+static struct attribute *attrs_ctrl_reg_0[] = {
+	attrify(led_sel),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_1[] = {
+	attrify(gpio_debounce),
+	attrify(halt),
+	attrify(halted),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_2[] = {
+	attrify(dir),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_3[] = {
+	attrify(gpiodata),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_4[] = {
+	attrify(led_act),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_5[] = {
+	attrify(ramp_period_a),
+	attrify(ramp_period_b),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_6_gpio[] = {
+	attrify(SPCTRL),
+	attrify(STRPD),
+	attrify(STRPU),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_6_led[] = {
+	attrify(brightness),
+	attrify(pattern),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_7[] = {
+	attrify(capacity_btn_nbr),
+	attrify(valid),
+	attrify(invert),
+	attrify(open_drain),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_8[] = {
+	attrify(gpio_ctrl8_0),
+	attrify(gpio_ctrl8_1),
+	NULL
+};
+
+static struct attribute *attrs_ctrl_reg_9[] = {
+	attrify(haptic_duration),
+	NULL
+};
+
+static struct attribute_group attrs_ctrl_regs[] = {
+	GROUP(attrs_ctrl_reg_0),
+	GROUP(attrs_ctrl_reg_1),
+	GROUP(attrs_ctrl_reg_2),
+	GROUP(attrs_ctrl_reg_3),
+	GROUP(attrs_ctrl_reg_4),
+	GROUP(attrs_ctrl_reg_5),
+	GROUP(attrs_ctrl_reg_6_gpio),
+	GROUP(attrs_ctrl_reg_6_led),
+	GROUP(attrs_ctrl_reg_7),
+	GROUP(attrs_ctrl_reg_8),
+	GROUP(attrs_ctrl_reg_9),
+};
+
+bool f30_attrs_regs_exist[ARRAY_SIZE(attrs_ctrl_regs)];
+
+static struct attribute *attrs_gpileddata[] = {
+	attrify(gpi_led_data),
+	NULL
+};
+
+static struct attribute_group attrs_data = GROUP(attrs_gpileddata);
+
+int rmi_f30_read_control_parameters(struct rmi_device *rmi_dev,
+	struct rmi_fn_30_data *f30)
+{
+	int retval = 0;
+	struct f30_control *control = &f30->control;
+
+	retval = rmi_read_block(rmi_dev, control->reg_0->address,
+			(u8 *)control->reg_0->regs,
+			control->reg_0->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg0 to 0x%x\n",
+			__func__, control->reg_0->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_1->address,
+			(u8 *)control->reg_1->regs,
+			sizeof(control->reg_1->regs));
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg1 to 0x%x\n",
+			 __func__, control->reg_1->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_2->address,
+			(u8 *)control->reg_2->regs, control->reg_2->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg_2 to 0x%x\n",
+			 __func__, control->reg_2->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_3->address,
+			(u8 *)control->reg_3->regs, control->reg_3->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg_3 to 0x%x\n",
+			 __func__, control->reg_3->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_4->address,
+			(u8 *)control->reg_4->regs, control->reg_4->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg4 to 0x%x\n",
+			 __func__, control->reg_4->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_5->address,
+			(u8 *)control->reg_5->regs,
+			control->reg_5->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg5 to 0x%x\n", __func__,
+			control->reg_5->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_6->address,
+			(u8 *)control->reg_6->regs,
+			control->reg_6->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg6 to 0x%x\n", __func__,
+		control->reg_6->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_7->address,
+		(u8 *)control->reg_7->regs,
+		control->reg_7->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg7 to 0x%x\n", __func__,
+		control->reg_7->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_8->address,
+		(u8 *)control->reg_8->regs,
+		control->reg_8->length);
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg8 to 0x%x\n", __func__,
+		control->reg_8->address);
+		return retval;
+	}
+
+	retval = rmi_read_block(rmi_dev, control->reg_9->address,
+		(u8 *)control->reg_9->regs,
+		sizeof(control->reg_9->regs));
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"%s : Could not read control reg9 to 0x%x\n", __func__,
+		control->reg_9->address);
+		return retval;
+	}
+	return 0;
+}
+
+static int rmi_f30_init(struct rmi_function_container *fc)
+{
+	int rc;
+	struct rmi_fn_30_data *f30 = fc->data;
+
+	rc = rmi_f30_alloc_memory(fc);
+	if (rc < 0)
+		goto error_exit;
+
+	rc = rmi_f30_initialize(fc);
+	if (rc < 0)
+		goto error_exit;
+
+	rc = rmi_f30_register_device(fc);
+	if (rc < 0)
+		goto error_exit;
+
+	rc = rmi_f30_create_sysfs(fc);
+	if (rc < 0)
+		goto error_uregister_exit;
+	return 0;
+
+error_uregister_exit:
+	input_unregister_device(f30->input);
+
+error_exit:
+	rmi_f30_free_memory(fc);
+
+	return rc;
+
+}
+
+static inline int rmi_f30_alloc_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_30_data *f30;
+	int retval;
+
+	f30 = kzalloc(sizeof(struct rmi_fn_30_data), GFP_KERNEL);
+	if (!f30) {
+		dev_err(&fc->dev, "Failed to allocate rmi_fn_30_data.\n");
+		return -ENOMEM;
+	}
+	fc->data = f30;
+
+	retval = rmi_read_block(fc->rmi_dev,
+						fc->fd.query_base_addr,
+						f30->query.regs,
+					ARRAY_SIZE(f30->query.regs));
+
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to read query register.\n");
+		return retval;
+	}
+
+	f30->gpioled_count = f30->query.gpio_led_count;
+	f30->button_bitmask_size = sizeof(u8)*(f30->gpioled_count + 7) / 8;
+	f30->button_data_buffer =
+	    kcalloc(f30->button_bitmask_size,
+		    sizeof(unsigned char), GFP_KERNEL);
+	if (!f30->button_data_buffer) {
+		dev_err(&fc->dev, "Failed to allocate button data buffer.\n");
+		return -ENOMEM;
+	}
+
+	f30->gpioled_map = kcalloc(f30->gpioled_count,
+				sizeof(unsigned short), GFP_KERNEL);
+	if (!f30->gpioled_map) {
+		dev_err(&fc->dev, "Failed to allocate button map.\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static inline void rmi_f30_free_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_30_data *f30 = fc->data;
+	u8 reg_num = 0;
+	sysfs_remove_group(&fc->dev.kobj, &attrs_query);
+	sysfs_remove_group(&fc->dev.kobj, &attrs_data);
+	for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++)
+		sysfs_remove_group(&fc->dev.kobj, &attrs_ctrl_regs[reg_num]);
+	if (f30) {
+		if (f30->control.reg_0)
+			kfree(f30->control.reg_0->regs);
+		kfree(f30->control.reg_0);
+
+		if (f30->control.reg_1)
+			kfree(f30->control.reg_1->regs);
+		kfree(f30->control.reg_1);
+
+		if (f30->control.reg_2)
+			kfree(f30->control.reg_2->regs);
+		kfree(f30->control.reg_2);
+
+		if (f30->control.reg_3)
+			kfree(f30->control.reg_3->regs);
+		kfree(f30->control.reg_3);
+
+		if (f30->control.reg_4)
+			kfree(f30->control.reg_4->regs);
+		kfree(f30->control.reg_4);
+
+		if (f30->control.reg_5)
+			kfree(f30->control.reg_5->regs);
+		kfree(f30->control.reg_5);
+
+		if (f30->control.reg_6)
+			kfree(f30->control.reg_6->regs);
+		kfree(f30->control.reg_6);
+
+		if (f30->control.reg_7)
+			kfree(f30->control.reg_7->regs);
+		kfree(f30->control.reg_7);
+
+		if (f30->control.reg_8)
+			kfree(f30->control.reg_8->regs);
+		kfree(f30->control.reg_8);
+
+		if (f30->control.reg_9)
+			kfree(f30->control.reg_9->regs);
+		kfree(f30->control.reg_9);
+
+		if (!f30->data.datareg_0)
+			kfree(f30->data.datareg_0->regs);
+		kfree(f30->data.datareg_0);
+
+		kfree(f30->button_data_buffer);
+		kfree(f30->gpioled_map);
+		kfree(f30);
+		fc->data = NULL;
+	}
+}
+
+int rmi_f30_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	int data_base_addr = fc->fd.data_base_addr;
+	struct rmi_fn_30_data *f30 = fc->data;
+	int error;
+	int gpiled;
+	bool gpiled_status = false;
+	int status = 0;
+
+	/* Read the button data. */
+
+	error = rmi_read_block(rmi_dev, data_base_addr,
+			f30->button_data_buffer,
+			f30->button_bitmask_size);
+	if (error < 0) {
+		dev_err(&fc->dev,
+			"%s: Failed to read button data registers.\n",
+			__func__);
+		return error;
+	}
+
+	/* Read the gpi led data. */
+	f30->data.address = fc->fd.data_base_addr;
+	error = rmi_read_block(fc->rmi_dev, f30->data.address,
+		(u8 *)&f30->data, f30->gpioled_count);
+
+	if (error < 0) {
+		dev_err(&fc->dev, "%s: Failed to read f30 data registers.\n",
+			__func__);
+		return error;
+	}
+	/* Generate events for buttons that change state. */
+	for (gpiled = 0; gpiled < f30->gpioled_count; gpiled++) {
+		status = f30->data.datareg_0->regs[gpiled].gpi_led_data;
+		dev_warn(&fc->dev,
+			"rmi_f30 attention gpiled=%d data status=%d\n",
+			gpiled,
+			f30->data.datareg_0->regs[gpiled].gpi_led_data);
+		/* check if gpio */
+		if (!(f30->control.reg_0->regs[gpiled].led_sel)) {
+			if (f30->control.reg_2->regs[gpiled].dir == 0) {
+				gpiled_status = status != 0;
+
+		/* if the gpiled data state changed from the
+		* last time report it and store the new state */
+		/* Generate an event here. */
+			dev_warn(&fc->dev,
+			"rmi_f30 attention call input_report_key\n");
+			input_report_key(f30->input,
+				f30->data.datareg_0->regs[gpiled].gpi_led_data,
+				gpiled_status);
+			}
+		}
+	}
+	input_sync(f30->input); /* sync after groups of events */
+	return 0;
+}
+
+static int rmi_f30_register_device(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct input_dev *input_dev;
+	struct rmi_fn_30_data *f30 = fc->data;
+	int i;
+	int rc;
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&fc->dev, "Failed to allocate input device.\n");
+		return -ENOMEM;
+	}
+
+	f30->input = input_dev;
+	snprintf(f30->input_name, MAX_LEN, "%sfn%02x", dev_name(&rmi_dev->dev),
+		fc->fd.function_number);
+	input_dev->name = f30->input_name;
+	snprintf(f30->input_phys, MAX_LEN, "%s/input0", input_dev->name);
+	input_dev->phys = f30->input_phys;
+	input_dev->dev.parent = &rmi_dev->dev;
+	input_set_drvdata(input_dev, f30);
+
+	/* Set up any input events. */
+	set_bit(EV_SYN, input_dev->evbit);
+	set_bit(EV_KEY, input_dev->evbit);
+	input_dev->keycode = f30->gpioled_map;
+	input_dev->keycodesize = 1;
+	input_dev->keycodemax = f30->gpioled_count;
+	/* set bits for each qpio led pin... */
+	for (i = 0; i < f30->gpioled_count; i++) {
+		set_bit(f30->gpioled_map[i], input_dev->keybit);
+		input_set_capability(input_dev, EV_KEY, f30->gpioled_map[i]);
+	}
+
+	rc = input_register_device(input_dev);
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to register input device.\n");
+		goto error_free_device;
+	}
+	return 0;
+
+error_free_device:
+	input_free_device(input_dev);
+
+	return rc;
+}
+
+
+static int rmi_f30_config(struct rmi_function_container *fc)
+{
+	struct rmi_fn_30_data *data = fc->data;
+	int gpio_led_cnt = data->query.gpio_led_count;
+	int bytecnt = gpio_led_cnt / 7 + 1;
+	int regs_size = 0;
+	int rc;
+	/* repeated register functions */
+
+	/* Write Control Register values back to device */
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_0->address,
+				(u8 *)data->control.reg_0,
+				bytecnt * sizeof(struct f30_gpio_ctrl_0n));
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 0 to 0x%x\n",
+				__func__, rc, data->control.reg_0->address);
+		return rc;
+	}
+
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_1->address,
+			(u8 *) data->control.reg_1->regs,
+			sizeof(union f30_gpio_ctrl_1));
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 1 to 0x%x\n",
+				__func__, rc, data->control.reg_1->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_2->length;
+
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_2->address,
+			(u8 *) data->control.reg_2->regs,
+			regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 2 to 0x%x\n",
+				__func__, rc, data->control.reg_2->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_3->length;
+
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_3->address,
+			(u8 *) data->control.reg_3->regs,
+			regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 3 to 0x%x\n",
+				__func__, rc, data->control.reg_3->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_4->length;
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_4->address,
+			(u8 *) data->control.reg_4->regs,
+			regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 4 to 0x%x\n",
+				__func__, rc, data->control.reg_4->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_5->length;
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_5->address,
+			(u8 *) data->control.reg_5->regs,
+			regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 5 to 0x%x\n",
+				__func__, rc, data->control.reg_5->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_6->length;
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_6->address,
+			(u8 *) data->control.reg_6->regs,
+			regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 6 to 0x%x\n",
+				__func__, rc, data->control.reg_6->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_7->length;
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_7->address,
+			(u8 *) data->control.reg_7->regs,
+			regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 7 to 0x%x\n",
+			__func__, rc, data->control.reg_7->address);
+		return rc;
+	}
+
+	regs_size = data->control.reg_8->length;
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_8->address,
+			(u8 *) data->control.reg_8->regs, regs_size);
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 9 to 0x%x\n",
+			__func__, rc, data->control.reg_8->address);
+		return rc;
+	}
+
+	rc = rmi_write_block(fc->rmi_dev, data->control.reg_9->address,
+			(u8 *) data->control.reg_9->regs,
+			sizeof(union f30_gpio_ctrl_9));
+	if (rc < 0) {
+		dev_err(&fc->dev, "%s error %d: Could not write control 9 to 0x%x\n",
+				__func__, rc, data->control.reg_9->address);
+		return rc;
+	}
+
+	return 0;
+}
+
+static inline int rmi_f30_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_device_platform_data *pdata;
+	struct rmi_fn_30_data *instance_data = fc->data;
+
+	int retval = 0;
+	u16 next_loc;
+	int  gpio_led_cnt = 0;
+	int regs_size = 0;
+	u8 reg_num = 0;
+	int reg_flg;
+	int hasgpio, hasled, hasmbtn, hashaptic;
+	struct f30_control *control = &instance_data->control;
+
+	/* Read F30 Query Data */
+	instance_data->query.address = fc->fd.query_base_addr;
+	retval = rmi_read_block(fc->rmi_dev, instance_data->query.address,
+		(u8 *)&instance_data->query, sizeof(instance_data->query));
+	if (retval < 0) {
+		dev_err(&fc->dev,
+		"Could not read query registers from 0x%04x\n",
+		instance_data->query.address);
+		return retval;
+	}
+
+	/* initialize gpioled_map data */
+	hasgpio = instance_data->query.has_gpio;
+	hasled = instance_data->query.has_led;
+	hasmbtn = instance_data->query.has_mappable_buttons;
+	hashaptic = instance_data->query.has_haptic ;
+	gpio_led_cnt = instance_data->query.gpio_led_count;
+
+	pdata = to_rmi_platform_data(rmi_dev);
+	if (pdata) {
+		if (!pdata->gpioled_map) {
+			dev_warn(&fc->dev,
+			"%s - gpioled_map is NULL", __func__);
+		} else if (pdata->gpioled_map->ngpioleds != gpio_led_cnt) {
+			dev_warn(&fc->dev,
+				"Platformdata gpioled map size (%d) != number "
+				"of buttons on device (%d) - ignored.\n",
+				pdata->gpioled_map->ngpioleds,
+				gpio_led_cnt);
+		} else if (!pdata->gpioled_map->map) {
+			dev_warn(&fc->dev,
+				 "Platformdata button map is missing!\n");
+		} else {
+			int i;
+			for (i = 0; i < pdata->gpioled_map->ngpioleds; i++)
+				instance_data->gpioled_map[i] =
+					pdata->gpioled_map->map[i];
+		}
+	}
+
+	/* Initialize Control Data */
+
+	next_loc = fc->fd.control_base_addr;
+
+	/* calculate reg size */
+
+	instance_data->gpioled_bitmask_size = sizeof(u8)*(gpio_led_cnt + 7) / 8;
+	instance_data->gpioled_byte_size = sizeof(u8)*gpio_led_cnt;
+
+	/* reg_0 */
+	control->reg_0 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_0), GFP_KERNEL);
+	if (!control->reg_0) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+
+	if (hasgpio && hasled)
+		reg_flg = 1;
+
+	f30_attrs_regs_exist[reg_num] = true;
+	regs_size = max(sizeof(struct f30_gpio_ctrl_0n) * reg_flg *
+					instance_data->gpioled_bitmask_size, 1);
+	control->reg_0->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+
+	if (!control->reg_0->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_0->address = next_loc;
+	control->reg_0->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_1 */
+	control->reg_1 =
+			kzalloc(sizeof(union f30_gpio_ctrl_1), GFP_KERNEL);
+	if (!control->reg_1) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	f30_attrs_regs_exist[reg_num] = true;
+	reg_num++;
+	instance_data->control.reg_1->address = next_loc;
+	next_loc += regs_size;
+
+	/* reg_2 */
+	instance_data->control.reg_2 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_2), GFP_KERNEL);
+	if (!control->reg_2) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+
+	reg_flg = hasgpio;
+	f30_attrs_regs_exist[reg_num] = true;
+	regs_size = max(sizeof(struct f30_gpio_ctrl_2n)*reg_flg*
+					instance_data->gpioled_bitmask_size, 1);
+	control->reg_2->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+
+	if (!control->reg_2->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_2->address = next_loc;
+	control->reg_2->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_3 */
+	instance_data->control.reg_3 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_3), GFP_KERNEL);
+	if (!control->reg_3) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+
+	reg_flg = hasgpio;
+	f30_attrs_regs_exist[reg_num] = true;
+	regs_size = max(sizeof(struct f30_gpio_ctrl_3n) * reg_flg *
+					instance_data->gpioled_bitmask_size, 1);
+	control->reg_3->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+
+	if (!control->reg_3->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_3->address = next_loc;
+	control->reg_3->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_4 */
+	control->reg_4 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_4), GFP_KERNEL);
+	if (!control->reg_4) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+
+	reg_flg = hasled;
+	f30_attrs_regs_exist[reg_num] = true;
+	regs_size = max(sizeof(struct f30_gpio_ctrl_4n)*reg_flg*
+					instance_data->gpioled_bitmask_size,
+					sizeof(struct f30_gpio_ctrl_4n));
+	control->reg_4->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+	if (!control->reg_4->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_4->address = next_loc;
+	control->reg_4->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_5 */
+	control->reg_5 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_5), GFP_KERNEL);
+	if (!control->reg_5) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+
+	reg_flg = hasled;
+	f30_attrs_regs_exist[reg_num] = true;
+	regs_size = max(6 * reg_flg, 2);
+	control->reg_5->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+	if (!control->reg_5->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_5->address = next_loc;
+	control->reg_5->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_6 */
+	control->reg_6 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_6), GFP_KERNEL);
+	if (!control->reg_6) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	reg_flg = hasled || (!hasled
+		&& instance_data->query.has_gpio_driver_control);
+
+	regs_size = max(sizeof(union f30_gpio_ctrl_6n)*reg_flg*gpio_led_cnt,
+					sizeof(union f30_gpio_ctrl_6n));
+	if (!hasled
+		&& instance_data->query.has_gpio_driver_control)
+		f30_attrs_regs_exist[reg_num] = true;
+
+	reg_num++;
+	if (hasled)
+		f30_attrs_regs_exist[reg_num] = true;
+
+	reg_num++;
+
+	control->reg_6->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+	if (!control->reg_6->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_6->address = next_loc;
+	control->reg_6->length = regs_size;
+	next_loc += regs_size;
+
+	/* reg_7 */
+	reg_flg = hasmbtn;
+	control->reg_7 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_7), GFP_KERNEL);
+	if (!control->reg_7) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	if (hasmbtn)
+		regs_size = sizeof(struct f30_gpio_ctrl_7n)*gpio_led_cnt;
+	else
+		regs_size = sizeof(struct f30_gpio_ctrl_7n);
+
+	regs_size = max(sizeof(struct f30_gpio_ctrl_7n)*reg_flg*
+					gpio_led_cnt,
+					sizeof(struct f30_gpio_ctrl_7n));
+	f30_attrs_regs_exist[reg_num] = true;
+	control->reg_7->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+	if (!control->reg_7->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_7->address = next_loc;
+	control->reg_7->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_8 */
+	control->reg_8 =
+			kzalloc(sizeof(struct f30_gpio_ctrl_8), GFP_KERNEL);
+	if (!control->reg_8) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+
+	regs_size = max(sizeof(struct f30_gpio_ctrl_8n)*hashaptic*
+					gpio_led_cnt,
+					sizeof(struct f30_gpio_ctrl_8n));
+	f30_attrs_regs_exist[reg_num] = true;
+	control->reg_8->regs =
+			kzalloc(regs_size, GFP_KERNEL);
+	if (!control->reg_8->regs) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_8->address = next_loc;
+	control->reg_8->length = regs_size;
+	next_loc += regs_size;
+	reg_num++;
+
+	/* reg_9 */
+	control->reg_9 =
+			kzalloc(sizeof(union f30_gpio_ctrl_9), GFP_KERNEL);
+	if (!control->reg_9) {
+		dev_err(&fc->dev, "Failed to allocate control register.");
+		return -ENOMEM;
+	}
+	if (instance_data->query.has_haptic)
+		f30_attrs_regs_exist[reg_num] = true;
+	control->reg_9->address = next_loc;
+	next_loc += sizeof(control->reg_9->regs);
+
+	/* data reg_0 */
+	instance_data->data.datareg_0 =
+			kzalloc(sizeof(struct f30_data_0), GFP_KERNEL);
+	if (!instance_data->data.datareg_0) {
+		dev_err(&fc->dev, "Failed to allocate control register.");
+		return -ENOMEM;
+	}
+
+	regs_size = sizeof(struct f30_data_0n)*
+				instance_data->gpioled_byte_size;
+	instance_data->data.datareg_0->address = fc->fd.data_base_addr;
+	next_loc += sizeof(instance_data->data.datareg_0->regs);
+
+	retval = rmi_f30_read_control_parameters(rmi_dev, instance_data);
+	if (retval < 0) {
+		dev_err(&fc->dev,
+			"Failed to initialize F19 control params.\n");
+		return retval;
+	}
+
+	mutex_init(&instance_data->control_mutex);
+	mutex_init(&instance_data->data_mutex);
+	return 0;
+}
+
+static int rmi_f30_create_sysfs(struct rmi_function_container *fc)
+{
+	u8 reg_num;
+
+	dev_dbg(&fc->dev, "Creating sysfs files.");
+
+	/* Set up sysfs device attributes. */
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_query) < 0) {
+		dev_err(&fc->dev, "Failed to create query sysfs files.");
+		return -ENODEV;
+	}
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_data) < 0) {
+		dev_err(&fc->dev, "Failed to create data sysfs files.");
+		return -ENODEV;
+	}
+
+	for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs);
+		reg_num++) {
+		if (f30_attrs_regs_exist[reg_num]) {
+			if (sysfs_create_group(&fc->dev.kobj,
+					&attrs_ctrl_regs[reg_num]) < 0) {
+				dev_err(&fc->dev,
+					"Failed to create "
+					"sysfs file group for reg"
+					"group %d.",
+					reg_num);
+				return -ENODEV;
+			}
+
+		}
+	}
+
+	return 0;
+}
+
+static void rmi_f30_remove(struct rmi_function_container *fc)
+{
+	dev_info(&fc->dev, "Removing F30.");
+	rmi_f30_free_memory(fc);
+}
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x30,
+	.init = rmi_f30_init,
+	.config = rmi_f30_config,
+	.attention = rmi_f30_attention,
+	.remove = rmi_f30_remove
+};
+
+static int __init rmi_f30_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_f30_module_exit(void)
+{
+	rmi_unregister_function_driver(&function_handler);
+}
+
+/* sysfs functions */
+/* Query */
+simple_show_union_struct_unsigned(query, extended_patterns)
+simple_show_union_struct_unsigned(query, has_mappable_buttons)
+simple_show_union_struct_unsigned(query, has_led)
+simple_show_union_struct_unsigned(query, has_gpio)
+simple_show_union_struct_unsigned(query, has_haptic)
+simple_show_union_struct_unsigned(query, has_gpio_driver_control)
+simple_show_union_struct_unsigned(query, gpio_led_count)
+
+/* Control */
+show_store_union_struct_unsigned(control, reg_1, gpio_debounce)
+show_store_union_struct_unsigned(control, reg_1, halt)
+show_store_union_struct_unsigned(control, reg_1, halted)
+show_store_union_struct_unsigned(control, reg_9, haptic_duration)
+
+/* repeated register functions */
+show_store_repeated_union_struct_unsigned(control, reg_0, led_sel)
+show_store_repeated_union_struct_unsigned(control, reg_2, dir)
+show_store_repeated_union_struct_unsigned(control, reg_3, gpiodata)
+show_store_repeated_union_struct_unsigned(control, reg_4, led_act)
+show_store_repeated_union_struct_unsigned(control, reg_5, ramp_period_a)
+show_store_repeated_union_struct_unsigned(control, reg_5, ramp_period_b)
+show_store_repeated_union_struct_unsigned(control, reg_6, SPCTRL)
+show_store_repeated_union_struct_unsigned(control, reg_6, STRPD)
+show_store_repeated_union_struct_unsigned(control, reg_6, STRPU)
+show_store_repeated_union_struct_unsigned(control, reg_6, brightness)
+show_store_repeated_union_struct_unsigned(control, reg_6, pattern)
+show_store_repeated_union_struct_unsigned(control, reg_7, capacity_btn_nbr)
+show_store_repeated_union_struct_unsigned(control, reg_7, valid)
+show_store_repeated_union_struct_unsigned(control, reg_7, invert)
+show_store_repeated_union_struct_unsigned(control, reg_7, open_drain)
+show_store_repeated_union_struct_unsigned(control, reg_8, gpio_ctrl8_0)
+show_store_repeated_union_struct_unsigned(control, reg_8, gpio_ctrl8_1)
+
+/* Data */
+show_store_repeated_union_struct_unsigned(data, datareg_0, gpi_led_data)
+
+module_init(rmi_f30_module_init);
+module_exit(rmi_f30_module_exit);
+
+MODULE_AUTHOR("Allie Xiong <axiong@Synaptics.com>");
+MODULE_DESCRIPTION("RMI f30 module");
+MODULE_LICENSE("GPL");

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 15/17] input: RMI4 F34 device reflash
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (13 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 14/17] input: RMI4 F30 GPIO/LED control Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-17 22:17 ` [RFC PATCH 16/17] input: RMI4 F41 Active pen 2D input Christopher Heiny
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_f34.c |  917 ++++++++++++++++++++++++++++++++++++++++++
 drivers/input/rmi4/rmi_f34.h |   74 ++++
 2 files changed, 991 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c
new file mode 100644
index 0000000..a07f7f9
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f34.c
@@ -0,0 +1,917 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include "rmi_driver.h"
+#include "rmi_f34.h"
+
+#define BLK_SZ_OFF	3
+#define IMG_BLK_CNT_OFF	5
+#define CFG_BLK_CNT_OFF	7
+
+#define BLK_NUM_OFF 2
+
+/* 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;
+	struct mutex attn_mutex;
+};
+
+static ssize_t rmi_fn_34_status_show(struct device *dev,
+				     struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_34_status_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count);
+
+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);
+
+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);
+
+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 int rmi_f34_alloc_memory(struct rmi_function_container *fc);
+
+static void rmi_f34_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f34_initialize(struct rmi_function_container *fc);
+
+static int rmi_f34_config(struct rmi_function_container *fc);
+
+static int rmi_f34_reset(struct rmi_function_container *fc);
+
+static int rmi_f34_create_sysfs(struct rmi_function_container *fc);
+
+
+
+static struct device_attribute attrs[] = {
+	__ATTR(status, RMI_RW_ATTR,
+	       rmi_fn_34_status_show, rmi_fn_34_status_store),
+
+	/* 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;
+
+	dev_info(&fc->dev, "Intializing f34 values.");
+
+	/* init instance data, fill in values and create any sysfs files */
+	retval = rmi_f34_alloc_memory(fc);
+	if (retval < 0)
+		goto exit_free_data;
+
+	retval = rmi_f34_initialize(fc);
+	if (retval < 0)
+		goto exit_free_data;
+
+	retval = rmi_f34_create_sysfs(fc);
+	if (retval < 0)
+		goto exit_free_data;
+
+	return 0;
+
+exit_free_data:
+	rmi_f34_free_memory(fc);
+
+	return retval;
+}
+
+static int rmi_f34_alloc_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_34_data *f34;
+
+	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;
+
+	return 0;
+}
+
+static void rmi_f34_free_memory(struct rmi_function_container *fc)
+{
+	kfree(fc->data);
+	fc->data = NULL;
+}
+
+static int rmi_f34_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_device_platform_data *pdata;
+	int retval = 0;
+	struct rmi_fn_34_data *f34 = fc->data;
+	u16 query_base_addr;
+	u16 control_base_addr;
+	unsigned char buf[2];
+
+	pdata = to_rmi_platform_data(rmi_dev);
+	dev_dbg(&fc->dev, "Initializing F34 values for %s.\n",
+		pdata->sensor_name);
+
+	mutex_init(&f34->attn_mutex);
+
+	/* 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);
+		return retval;
+	}
+
+	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);
+		return retval;
+	}
+	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);
+		return retval;
+	}
+	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);
+		return retval;
+	}
+	batohs(&f34->configblockcount, buf);
+
+	return 0;
+}
+
+static int rmi_f34_create_sysfs(struct rmi_function_container *fc)
+{
+	int attr_count = 0;
+	int rc;
+
+	dev_dbg(&fc->dev, "Creating sysfs files.");
+
+	/* 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. */
+	rc = sysfs_create_bin_file(&fc->dev.kobj,
+				&dev_attr_data);
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to create sysfs file for F34 data "
+		     "(error = %d).\n", rc);
+		return -ENODEV;
+	}
+
+	/* Set up sysfs device attributes. */
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+		if (sysfs_create_file
+		    (&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
+			dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+			     attrs[attr_count].attr.name);
+			rc = -ENODEV;
+			goto err_remove_sysfs;
+		}
+	}
+
+	return 0;
+
+err_remove_sysfs:
+	sysfs_remove_bin_file(&fc->dev.kobj, &dev_attr_data);
+
+	for (attr_count--; attr_count >= 0; attr_count--)
+		sysfs_remove_file(&fc->dev.kobj,
+				  &attrs[attr_count].attr);
+	return rc;
+}
+
+static int rmi_f34_config(struct rmi_function_container *fc)
+{
+	/* for this function we should do nothing here */
+	return 0;
+}
+
+
+static int rmi_f34_reset(struct rmi_function_container *fc)
+{
+	struct  rmi_fn_34_data  *instance_data = fc->data;
+
+	instance_data->status = ECONNRESET;
+
+	return 0;
+}
+
+static void rmi_f34_remove(struct rmi_function_container *fc)
+{
+	int attr_count;
+
+	sysfs_remove_bin_file(&fc->dev.kobj,
+						  &dev_attr_data);
+
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
+		sysfs_remove_file(&fc->dev.kobj,
+				  &attrs[attr_count].attr);
+
+	rmi_f34_free_memory(fc);
+}
+
+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;
+
+	if (instance_data->status == ECONNRESET)
+		return instance_data->status;
+
+	/* 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 == F34_STATUS_IDLE) &&
+		(instance_data->cmd == F34_ENABLE_FLASH_PROG))
+		instance_data->inflashprogmode = true;
+
+	return retval;
+}
+
+int rmi_f34_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	int retval;
+	struct rmi_fn_34_data *data = fc->data;
+
+	mutex_lock(&data->attn_mutex);
+	retval = f34_read_status(fc);
+	mutex_unlock(&data->attn_mutex);
+	return retval;
+}
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x34,
+	.init = rmi_f34_init,
+	.config = rmi_f34_config,
+	.reset = rmi_f34_reset,
+	.attention = rmi_f34_attention,
+	.remove = rmi_f34_remove
+};
+
+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;
+
+	mutex_lock(&instance_data->attn_mutex);
+	retval = f34_read_status(fc);
+	mutex_unlock(&instance_data->attn_mutex);
+
+	if (retval < 0)
+		return retval;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->status);
+}
+
+
+static ssize_t rmi_fn_34_status_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;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	instance_data->status = 0;
+
+	return 0;
+}
+
+
+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 != F34_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 F34_ENABLE_FLASH_PROG:
+	case F34_ERASE_ALL:
+	case F34_ERASE_CONFIG:
+	case F34_WRITE_FW_BLOCK:
+	case F34_READ_CONFIG_BLOCK:
+	case F34_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 == F34_ENABLE_FLASH_PROG)
+			instance_data->inflashprogmode = true;
+
+		/* set status to indicate we are in progress */
+		instance_data->status = F34_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 != F34_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;
+}
+
+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)
+{
+	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;
+}
+
+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)
+{
+	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("William Manson <wmanson@synaptics.com");
+MODULE_DESCRIPTION("RMI F34 module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
diff --git a/drivers/input/rmi4/rmi_f34.h b/drivers/input/rmi4/rmi_f34.h
new file mode 100644
index 0000000..85f3343
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f34.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifndef _RMI_F34_H
+#define _RMI_F34_H
+
+/* F34 image file offsets. */
+#define F34_FW_IMAGE_OFFSET 0x100
+
+/* F34 register offsets. */
+#define F34_BLOCK_DATA_OFFSET	2
+
+/* F34 commands */
+#define F34_WRITE_FW_BLOCK        0x2
+#define F34_ERASE_ALL             0x3
+#define F34_READ_CONFIG_BLOCK     0x5
+#define F34_WRITE_CONFIG_BLOCK    0x6
+#define F34_ERASE_CONFIG          0x7
+#define F34_ENABLE_FLASH_PROG     0xf
+
+#define F34_STATUS_IN_PROGRESS    0xff
+#define F34_STATUS_IDLE		  0x80
+
+#define F34_IDLE_WAIT_MS 500
+#define F34_ENABLE_WAIT_MS 300
+#define F34_ERASE_WAIT_MS (5 * 1000)
+
+union f34_query_regs {
+	struct {
+		u16 reg_map:1;
+		u16 unlocked:1;
+		u16 has_config_id:1;
+		u16 reserved:5;
+		u16 block_size:16;
+		u16 fw_block_count:16;
+		u16 config_block_count:16;
+	} __attribute__ ((__packed__));
+	struct {
+		u8 regs[7];
+		u16 address;
+	};
+};
+
+union f34_control_status {
+	struct {
+		u8 command:4;
+		u8 status:3;
+		u8 program_enabled:1;
+	} __attribute__ ((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	};
+};
+
+#define IS_IDLE(ctl_ptr) ((!ctl_ptr->status) && (!ctl_ptr->command))
+
+#endif

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 16/17] input: RMI4 F41 Active pen 2D input
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (14 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 15/17] input: RMI4 F34 device reflash Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-17 22:17 ` [RFC PATCH 17/17] input: RMI4 F54 analog data reporting Christopher Heiny
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_f41.c | 1020 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1020 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f41.c b/drivers/input/rmi4/rmi_f41.c
new file mode 100644
index 0000000..1bac4e3
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f41.c
@@ -0,0 +1,1020 @@
+/*
+ * Copyright (c) 2012 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define FUNCTION_DATA rmi_fn_41_data
+#define FNUM 41
+
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+#define MAX_STR_LEN 256
+
+union f41_ap_query {
+	struct {
+		/* Query 0 */
+		u8 has_reduced_reporting:1;
+		u8 has_modal_control:1;
+		u8 has_force:1;
+		u8 has_orientation:1;
+		u8 has_serial_number:1;
+		u8 has_battery:1;
+		u8 has_z:1;
+		u8 has_single_tap:1;
+
+		/* Query 1 */
+		u8 number_of_buttons:3;
+		u8 f41_ap_query1_b3__7:5;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+
+union f41_ap_control_0 {
+	struct {
+		/* control 0 */
+		u8 reporting_mode:2;
+		u8 modal_ctrl:1;
+		u8 f41_ap_control0_b3__4:2;
+		u8 single_tap_interrupt_enable:1;
+		u8 in_range_interrupt_enable:1;
+		u8 new_sn_interrupt_enable:1;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f41_ap_control_1 {
+	struct {
+		/* control 1 */
+		u8 button_1_interrupt_enable:1;
+		u8 button_2_interrupt_enable:1;
+		u8 button_3_interrupt_enable:1;
+		u8 button_4_interrupt_enable:1;
+		u8 button_5_interrupt_enable:1;
+		u8 button_6_interrupt_enable:1;
+		u8 button_7_interrupt_enable:1;
+		u8 f41_ap_control1_b7:1;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f41_ap_control_2__5 {
+	struct {
+		u16 max_x_position;
+		u16 max_y_position;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[4];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f41_ap_control_6__9 {
+	struct {
+		u16 x_reduced_reporting_distance;
+		u16 y_reduced_reporting_distance;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[4];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f41_ap_control_10 {
+	struct {
+		u8 max_tap_time;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+struct f41_ap_control {
+	union f41_ap_control_0 *reg_0;
+	union f41_ap_control_1 *reg_1;
+	union f41_ap_control_2__5 *reg_2__5;
+	union f41_ap_control_6__9 *reg_6__9;
+	union f41_ap_control_10 *reg_10;
+};
+
+
+union f41_ap_data_0__3 {
+	struct {
+		/* data 0-1 */
+		u16 x_position;
+
+		/* data 2-3 */
+		u16 y_position;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[4];
+	} __attribute__((__packed__));
+};
+
+union f41_ap_data_4 {
+	struct {
+		/* data 4 */
+		u8 force;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+	} __attribute__((__packed__));
+};
+
+union f41_ap_data_5 {
+	struct {
+		/* data 5 */
+		u8 button_1_state:1;
+		u8 button_2_state:1;
+		u8 button_3_state:1;
+		u8 button_4_state:1;
+		u8 button_5_state:1;
+		u8 button_6_state:1;
+		u8 button_7_state:1;
+		u8 f41_ap_data_5_b7:1;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+	} __attribute__((__packed__));
+};
+
+union f41_ap_data_6 {
+	struct {
+		/* data 6 */
+		u8 single_tap:1;
+		u8 in_range:1;
+		u8 new_serial_number:1;
+		u8 f41_ap_data_6_b3__7:5;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+	} __attribute__((__packed__));
+};
+
+union f41_ap_data_7__8 {
+	struct {
+		/* data 7 */
+		u8 azimuth;
+
+		/* data 8 */
+		u8 altitude;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+	} __attribute__((__packed__));
+};
+
+union f41_ap_data_9 {
+	struct {
+		/* data 9 */
+		u8 z_position;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+	} __attribute__((__packed__));
+};
+
+union f41_ap_data_10 {
+	struct {
+		/* data 10 */
+		u8 battery_level;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+	} __attribute__((__packed__));
+};
+
+union f41_ap_data_11__14 {
+	struct {
+		/* data 11-14 */
+		u32 pen_serial_number;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[4];
+	} __attribute__((__packed__));
+};
+
+struct f41_ap_data {
+	union f41_ap_data_0__3 *reg_0__3;
+	union f41_ap_data_4 *reg_4;
+	union f41_ap_data_5 *reg_5;
+	union f41_ap_data_6 *reg_6;
+	union f41_ap_data_7__8 *reg_7__8;
+	union f41_ap_data_9 *reg_9;
+	union f41_ap_data_10 *reg_10;
+	union f41_ap_data_11__14 *reg_11__14;
+};
+
+/* Query */
+show_union_struct_prototype(has_reduced_reporting)
+show_union_struct_prototype(has_modal_control)
+show_union_struct_prototype(has_force)
+show_union_struct_prototype(has_orientation)
+show_union_struct_prototype(has_serial_number)
+show_union_struct_prototype(has_battery)
+show_union_struct_prototype(has_z)
+show_union_struct_prototype(has_single_tap)
+show_union_struct_prototype(number_of_buttons)
+
+static struct attribute *attrs_q[] = {
+	attrify(has_reduced_reporting),
+	attrify(has_modal_control),
+	attrify(has_force),
+	attrify(has_orientation),
+	attrify(has_serial_number),
+	attrify(has_battery),
+	attrify(has_z),
+	attrify(has_single_tap),
+	attrify(number_of_buttons),
+	NULL
+};
+static struct attribute_group attrs_query = GROUP(attrs_q);
+
+/* control 0 */
+show_store_union_struct_prototype(reporting_mode)
+show_store_union_struct_prototype(modal_ctrl)
+show_store_union_struct_prototype(single_tap_interrupt_enable)
+show_store_union_struct_prototype(in_range_interrupt_enable)
+show_store_union_struct_prototype(new_sn_interrupt_enable)
+
+static struct attribute *attrs_reg_0a[] = {
+	attrify(reporting_mode),
+	NULL
+};
+static struct attribute *attrs_reg_0b[] = {
+	attrify(modal_ctrl),
+	NULL
+};
+static struct attribute *attrs_reg_0c[] = {
+	attrify(single_tap_interrupt_enable),
+	NULL
+};
+static struct attribute *attrs_reg_0d[] = {
+	attrify(in_range_interrupt_enable),
+	NULL
+};
+static struct attribute *attrs_reg_0e[] = {
+	attrify(new_sn_interrupt_enable),
+	NULL
+};
+
+/* control 1 */
+show_store_union_struct_prototype(button_1_interrupt_enable)
+show_store_union_struct_prototype(button_2_interrupt_enable)
+show_store_union_struct_prototype(button_3_interrupt_enable)
+show_store_union_struct_prototype(button_4_interrupt_enable)
+show_store_union_struct_prototype(button_5_interrupt_enable)
+show_store_union_struct_prototype(button_6_interrupt_enable)
+show_store_union_struct_prototype(button_7_interrupt_enable)
+static struct attribute *attrs_reg_1[] = {
+	attrify(button_1_interrupt_enable),
+	attrify(button_2_interrupt_enable),
+	attrify(button_3_interrupt_enable),
+	attrify(button_4_interrupt_enable),
+	attrify(button_5_interrupt_enable),
+	attrify(button_6_interrupt_enable),
+	attrify(button_7_interrupt_enable),
+	NULL
+};
+
+/* control 2-5 */
+show_store_union_struct_prototype(max_x_position)
+show_store_union_struct_prototype(max_y_position)
+static struct attribute *attrs_reg_2__5[] = {
+	attrify(max_x_position),
+	attrify(max_y_position),
+	NULL
+};
+
+/* control 6-9 */
+show_store_union_struct_prototype(x_reduced_reporting_distance)
+show_store_union_struct_prototype(y_reduced_reporting_distance)
+static struct attribute *attrs_reg_6__9[] = {
+	attrify(x_reduced_reporting_distance),
+	attrify(y_reduced_reporting_distance),
+	NULL
+};
+
+/* control 10 */
+show_store_union_struct_prototype(max_tap_time)
+static struct attribute *attrs_reg_10[] = {
+	attrify(max_tap_time),
+	NULL
+};
+
+
+static struct attribute_group attrs_ctrl[] = {
+	GROUP(attrs_reg_0a),
+	GROUP(attrs_reg_0b),
+	GROUP(attrs_reg_0c),
+	GROUP(attrs_reg_0d),
+	GROUP(attrs_reg_0e),
+	GROUP(attrs_reg_1),
+	GROUP(attrs_reg_2__5),
+	GROUP(attrs_reg_6__9),
+	GROUP(attrs_reg_10)
+};
+
+enum ctrl_reg_group {
+	f41_ctrl_reg_0a = 0,
+	f41_ctrl_reg_0b = 1,
+	f41_ctrl_reg_0c = 2,
+	f41_ctrl_reg_0d = 3,
+	f41_ctrl_reg_0e = 4,
+	f41_ctrl_reg_1 = 5,
+	f41_ctrl_reg_2__5 = 6,
+	f41_ctrl_reg_6__9 = 7,
+	f41_ctrl_reg_10 = 8
+};
+
+/* data 0-3 */
+show_union_struct_prototype(x_position)
+show_union_struct_prototype(y_position)
+static struct attribute *attrs_data_reg_0__3[] = {
+	attrify(x_position),
+	attrify(y_position),
+	NULL
+};
+
+/* data 4 */
+show_union_struct_prototype(force)
+static struct attribute *attrs_data_reg_4[] = {
+	attrify(force),
+	NULL
+};
+
+/* data 5 */
+/* Handled in button map */
+
+/* data 6 */
+show_union_struct_prototype(single_tap)
+show_union_struct_prototype(in_range)
+show_union_struct_prototype(new_serial_number)
+static struct attribute *attrs_data_reg_6a[] = {
+	attrify(single_tap),
+	NULL
+};
+static struct attribute *attrs_data_reg_6b[] = {
+	attrify(in_range),
+	NULL
+};
+static struct attribute *attrs_data_reg_6c[] = {
+	attrify(new_serial_number),
+	NULL
+};
+
+/* data 7-8 */
+show_union_struct_prototype(azimuth)
+show_union_struct_prototype(altitude)
+static struct attribute *attrs_data_reg_7__8[] = {
+	attrify(azimuth),
+	attrify(altitude),
+	NULL
+};
+
+/* data 9 */
+show_union_struct_prototype(z_position)
+static struct attribute *attrs_data_reg_9[] = {
+	attrify(z_position),
+	NULL
+};
+
+/* data 10 */
+show_union_struct_prototype(battery_level)
+static struct attribute *attrs_data_reg_10[] = {
+	attrify(battery_level),
+	NULL
+};
+
+/* data 11-14 */
+show_union_struct_prototype(pen_serial_number)
+static struct attribute *attrs_data_reg_11__14[] = {
+	attrify(pen_serial_number),
+	NULL
+};
+
+static struct attribute_group attrs_data[] = {
+	GROUP(attrs_data_reg_0__3),
+	GROUP(attrs_data_reg_4),
+	GROUP(attrs_data_reg_6a),
+	GROUP(attrs_data_reg_6b),
+	GROUP(attrs_data_reg_6c),
+	GROUP(attrs_data_reg_7__8),
+	GROUP(attrs_data_reg_9),
+	GROUP(attrs_data_reg_10),
+	GROUP(attrs_data_reg_11__14)
+};
+
+enum data_reg_group {
+	f41_data_reg_0__3 = 0,
+	f41_data_reg_4 = 1,
+	f41_data_reg_6a = 2,
+	f41_data_reg_6b = 3,
+	f41_data_reg_6c = 4,
+	f41_data_reg_7__8 = 5,
+	f41_data_reg_9 = 6,
+	f41_data_reg_10 = 7,
+	f41_data_reg_11__14 = 8
+};
+
+/* data specific to fn $41 that needs to be kept around */
+struct rmi_fn_41_data {
+	union f41_ap_query query;
+	struct f41_ap_control control;
+	struct f41_ap_data data;
+	u8 *data_block;
+	u8 data_block_size;
+
+	bool attrs_ctrl_regs_exist[ARRAY_SIZE(attrs_ctrl)];
+	bool attrs_data_regs_exist[ARRAY_SIZE(attrs_data)];
+
+	struct mutex control_mutex;
+	struct mutex data_mutex;
+
+	unsigned char button_count;
+	unsigned short *button_map;
+	char input_name[MAX_STR_LEN];
+	char input_phys[MAX_STR_LEN];
+	struct input_dev *input;
+};
+
+static void rmi_f41_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f41_initialize(struct rmi_function_container *fc);
+
+static int rmi_f41_register_device(struct rmi_function_container *fc);
+
+static int rmi_f41_config(struct rmi_function_container *fc);
+
+static int rmi_f41_create_sysfs(struct rmi_function_container *fc);
+
+static int rmi_f41_init(struct rmi_function_container *fc)
+{
+	int retval = 0;
+
+	dev_dbg(&fc->dev, "Intializing F41.");
+
+	retval = rmi_f41_initialize(fc);
+	if (retval < 0)
+		goto error_exit;
+
+	retval = rmi_f41_register_device(fc);
+	if (retval < 0)
+		goto error_exit;
+
+	retval = rmi_f41_create_sysfs(fc);
+	if (retval < 0)
+		goto error_exit;
+
+	return retval;
+
+error_exit:
+	rmi_f41_free_memory(fc);
+
+	return retval;
+}
+
+
+static void rmi_f41_free_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_41_data *f41 = fc->data;
+	int i;
+
+	/* query */
+	sysfs_remove_group(&fc->dev.kobj, &attrs_query);
+	/* control */
+	for (i = 0; i < ARRAY_SIZE(attrs_ctrl); i++) {
+		if (f41->attrs_ctrl_regs_exist[i])
+			sysfs_remove_group(&fc->dev.kobj, &attrs_ctrl[i]);
+	}
+	for (i = 0; i < ARRAY_SIZE(attrs_data); i++) {
+		if (f41->attrs_data_regs_exist[i])
+			sysfs_remove_group(&fc->dev.kobj, &attrs_data[i]);
+	}
+	if (f41) {
+		kfree(f41->control.reg_0);
+		kfree(f41->control.reg_1);
+		kfree(f41->control.reg_2__5);
+		kfree(f41->control.reg_6__9);
+		kfree(f41->control.reg_10);
+
+		kfree(f41->data_block);
+		kfree(f41->button_map);
+		kfree(f41);
+		fc->data = NULL;
+	}
+}
+
+
+static int rmi_f41_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_fn_41_data *f41;
+	struct rmi_device_platform_data *pdata;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	int retval = 0;
+	u16 next_loc;
+	int i;
+	u8 *data_loc;
+
+	f41 = kzalloc(sizeof(struct rmi_fn_41_data), GFP_KERNEL);
+	if (!f41) {
+		dev_err(&fc->dev, "Failed to allocate rmi_fn_41_data.\n");
+		return -ENOMEM;
+	}
+	fc->data = f41;
+
+	/* Read F41 Query Data */
+	f41->query.address = fc->fd.query_base_addr;
+	retval = rmi_read_block(fc->rmi_dev, f41->query.address,
+		(u8 *)&f41->query, sizeof(f41->query.regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read query registers from 0x%04x\n",
+				f41->query.address);
+		return retval;
+	}
+
+	/* Initialize Control Data */
+	next_loc = fc->fd.control_base_addr;
+
+	if (f41->query.has_reduced_reporting == 1 ||
+			f41->query.has_modal_control == 1 ||
+			f41->query.has_single_tap == 1 ||
+			f41->query.has_z == 1 ||
+			f41->query.has_serial_number == 1) {
+		f41->control.reg_0 = kzalloc(sizeof(union f41_ap_control_0),
+				 GFP_KERNEL);
+		if (!f41->control.reg_0) {
+			dev_err(&fc->dev, "Failed to allocate control register 0.");
+			return -ENOMEM;
+		}
+		f41->control.reg_0->address = next_loc;
+		next_loc += sizeof(f41->control.reg_0->regs);
+		retval = rmi_read_block(fc->rmi_dev,
+				f41->control.reg_0->address,
+				f41->control.reg_0->regs,
+				sizeof(f41->control.reg_0->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev, "Could not read Control register 0 from 0x%04x\n",
+					f41->control.reg_0->address);
+			return retval;
+		}
+	}
+
+	f41->attrs_ctrl_regs_exist[f41_ctrl_reg_0a] =
+					f41->query.has_reduced_reporting == 1;
+	f41->attrs_ctrl_regs_exist[f41_ctrl_reg_0b] =
+					f41->query.has_modal_control == 1;
+	f41->attrs_ctrl_regs_exist[f41_ctrl_reg_0c] =
+					f41->query.has_single_tap == 1;
+	f41->attrs_ctrl_regs_exist[f41_ctrl_reg_0d] =
+					f41->query.has_z == 1;
+	f41->attrs_ctrl_regs_exist[f41_ctrl_reg_0e] =
+					f41->query.has_serial_number == 1;
+
+	if (f41->query.number_of_buttons > 0) {
+		f41->control.reg_1 = kzalloc(sizeof(union f41_ap_control_1),
+							GFP_KERNEL);
+		if (!f41->control.reg_1) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		f41->control.reg_1->address = next_loc;
+		next_loc += sizeof(f41->control.reg_1->regs);
+		retval = rmi_read_block(fc->rmi_dev,
+				f41->control.reg_1->address,
+				f41->control.reg_1->regs,
+				sizeof(f41->control.reg_1->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev, "Could not read Control register 1 from 0x%04x\n",
+						f41->control.reg_1->address);
+			return retval;
+		}
+		f41->attrs_ctrl_regs_exist[f41_ctrl_reg_1] = true;
+	}
+
+	f41->control.reg_2__5 =	kzalloc(sizeof(union f41_ap_control_2__5),
+					GFP_KERNEL);
+	if (!f41->control.reg_2__5) {
+		dev_err(&fc->dev, "Failed to allocate control registers 2-5.");
+		return -ENOMEM;
+	}
+	f41->control.reg_2__5->address = next_loc;
+	next_loc += sizeof(f41->control.reg_2__5->regs);
+	retval = rmi_read_block(fc->rmi_dev, f41->control.reg_2__5->address,
+				f41->control.reg_2__5->regs,
+				sizeof(f41->control.reg_2__5->regs));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read Control registers 2-5 from 0x%04x\n",
+				f41->control.reg_2__5->address);
+		return retval;
+	}
+	f41->attrs_ctrl_regs_exist[f41_ctrl_reg_2__5] = true;
+
+	if (f41->query.has_reduced_reporting == 1) {
+		f41->control.reg_6__9 =
+			kzalloc(sizeof(union f41_ap_control_6__9), GFP_KERNEL);
+		if (!f41->control.reg_6__9) {
+			dev_err(&fc->dev, "Failed to allocate control registers 6-9.");
+			return -ENOMEM;
+		}
+		f41->control.reg_6__9->address = next_loc;
+		next_loc += sizeof(f41->control.reg_6__9->regs);
+		retval = rmi_read_block(fc->rmi_dev,
+				f41->control.reg_6__9->address,
+				f41->control.reg_6__9->regs,
+				sizeof(f41->control.reg_6__9->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev, "Could not read Control registers 6-9 from 0x%04x\n",
+						f41->control.reg_6__9->address);
+			return retval;
+		}
+		f41->attrs_ctrl_regs_exist[f41_ctrl_reg_6__9] = true;
+	}
+
+	if (f41->query.has_single_tap == 1) {
+		f41->control.reg_10 = kzalloc(sizeof(union f41_ap_control_10),
+							GFP_KERNEL);
+		if (!f41->control.reg_10) {
+			dev_err(&fc->dev, "Failed to allocate control register 10.");
+			return -ENOMEM;
+		}
+		f41->control.reg_10->address = next_loc;
+		next_loc += sizeof(f41->control.reg_10->regs);
+		retval = rmi_read_block(fc->rmi_dev,
+					f41->control.reg_10->address,
+					f41->control.reg_10->regs,
+					sizeof(f41->control.reg_10->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev, "Could not read Control register 10 from 0x%04x\n",
+						f41->control.reg_10->address);
+			return retval;
+		}
+		f41->attrs_ctrl_regs_exist[f41_ctrl_reg_10] = true;
+	}
+
+
+	/* initialize data registers */
+
+	f41->data_block = kzalloc(sizeof(union f41_ap_data_0__3)
+					+ sizeof(union f41_ap_data_4)
+					+ sizeof(union f41_ap_data_5)
+					+ sizeof(union f41_ap_data_6)
+					+ sizeof(union f41_ap_data_7__8)
+					+ sizeof(union f41_ap_data_9)
+					+ sizeof(union f41_ap_data_10)
+					+ sizeof(union f41_ap_data_11__14),
+				GFP_KERNEL);
+	if (!f41->data_block) {
+		dev_err(&fc->dev, "Failed to allocate data registers.");
+			return -ENOMEM;
+	}
+	data_loc = f41->data_block;
+	/* data 0-3 */
+
+	f41->data.reg_0__3 = (union f41_ap_data_0__3 *) data_loc;
+	data_loc += sizeof(union f41_ap_data_0__3);
+	f41->attrs_data_regs_exist[f41_data_reg_0__3] = true;
+
+	/* data 4 */
+	if (f41->query.has_force == 1) {
+		f41->data.reg_4 = (union f41_ap_data_4 *) data_loc;
+		data_loc += sizeof(union f41_ap_data_4);
+		f41->attrs_data_regs_exist[f41_data_reg_4] = true;
+	}
+
+	/* data 5 */
+	/* button map */
+	f41->button_map = kcalloc(f41->query.number_of_buttons,
+					sizeof(unsigned short), GFP_KERNEL);
+	if (!f41->button_map) {
+		dev_err(&fc->dev, "Failed to allocate button map.\n");
+		return -ENOMEM;
+	}
+	if (f41->query.number_of_buttons > 0) {
+		f41->data.reg_5 = (union f41_ap_data_5 *) data_loc;
+		data_loc += sizeof(union f41_ap_data_5);
+	}
+	pdata = to_rmi_platform_data(rmi_dev);
+	if (pdata) {
+		if (!pdata->f41_button_map)
+			dev_warn(&fc->dev, "%s - button_map is NULL", __func__);
+		else if (!pdata->f41_button_map->map)
+			dev_warn(&fc->dev,
+				 "Platformdata button map is missing!\n");
+		else {
+			if (pdata->f41_button_map->nbuttons !=
+						f41->query.number_of_buttons)
+				dev_warn(&fc->dev,
+					"Platformdata button map size (%d) != number of buttons on device (%d).\n",
+					pdata->f41_button_map->nbuttons,
+					f41->query.number_of_buttons);
+			f41->button_count = min(f41->query.number_of_buttons,
+					 pdata->f41_button_map->nbuttons);
+			for (i = 0; i < f41->button_count; i++)
+				f41->button_map[i] =
+					pdata->f41_button_map->map[i];
+		}
+	}
+
+	/* data 6 */
+	if (f41->query.has_z == 1 || f41->query.has_single_tap == 1
+					|| f41->query.has_serial_number == 1) {
+		f41->data.reg_6 = (union f41_ap_data_6 *) data_loc;
+		data_loc += sizeof(union f41_ap_data_6);
+	}
+	f41->attrs_data_regs_exist[f41_data_reg_6a] =
+					f41->query.has_single_tap == 1;
+	f41->attrs_data_regs_exist[f41_data_reg_6b] = f41->query.has_z == 1;
+	f41->attrs_data_regs_exist[f41_data_reg_6c] =
+					f41->query.has_serial_number == 1;
+
+	/* data 7-8 */
+	if (f41->query.has_orientation == 1) {
+		f41->data.reg_7__8 = (union f41_ap_data_7__8 *) data_loc;
+		data_loc += sizeof(union f41_ap_data_7__8);
+		f41->attrs_data_regs_exist[f41_data_reg_7__8] = true;
+	}
+
+	/* data 9 */
+	if (f41->query.has_z == 1) {
+		f41->data.reg_9 = (union f41_ap_data_9 *) data_loc;
+		data_loc += sizeof(union f41_ap_data_9);
+		f41->attrs_data_regs_exist[f41_data_reg_9] = true;
+	}
+
+	/* data 10 */
+	if (f41->query.has_battery == 1) {
+		f41->data.reg_10 = (union f41_ap_data_10 *) data_loc;
+		data_loc += sizeof(union f41_ap_data_10);
+		f41->attrs_data_regs_exist[f41_data_reg_10] = true;
+	}
+
+	/* data 11-14 */
+	if (f41->query.has_serial_number > 0) {
+		f41->data.reg_11__14 = (union f41_ap_data_11__14 *) data_loc;
+		data_loc += sizeof(union f41_ap_data_11__14);
+		f41->attrs_data_regs_exist[f41_data_reg_11__14] = true;
+	}
+
+	f41->data_block_size = data_loc - f41->data_block;
+	mutex_init(&f41->control_mutex);
+	mutex_init(&f41->data_mutex);
+	return 0;
+}
+
+
+static int rmi_f41_create_sysfs(struct rmi_function_container *fc)
+{
+	u8 attr_num;
+	struct rmi_fn_41_data *f41 = fc->data;
+	dev_dbg(&fc->dev, "Creating F41 sysfs files.");
+
+	/* Set up sysfs device attributes. */
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_query) < 0) {
+		dev_err(&fc->dev, "Failed to create query sysfs files.");
+		return -ENODEV;
+	}
+	for (attr_num = 0; attr_num < ARRAY_SIZE(attrs_ctrl); attr_num++) {
+		if (f41->attrs_ctrl_regs_exist[attr_num]) {
+			if (sysfs_create_group(&fc->dev.kobj,
+					&attrs_ctrl[attr_num]) < 0) {
+				dev_err(&fc->dev, "Failed to create sysfs file group for reg group %d.",
+								attr_num);
+				return -ENODEV;
+			}
+		}
+	}
+	for (attr_num = 0; attr_num < ARRAY_SIZE(attrs_data); attr_num++) {
+		if (f41->attrs_data_regs_exist[attr_num]) {
+			if (sysfs_create_group(&fc->dev.kobj,
+					&attrs_data[attr_num]) < 0) {
+				dev_err(&fc->dev, "Failed to create sysfs file group for reg group %d.",
+								attr_num);
+				return -ENODEV;
+			}
+		}
+	}
+	return 0;
+}
+
+
+static int rmi_f41_config(struct rmi_function_container *fc)
+{
+	struct rmi_fn_41_data *f41 = fc->data;
+
+	/* Write Control Register values back to device */
+	if (f41->attrs_ctrl_regs_exist[f41_ctrl_reg_0a]
+		|| f41->attrs_ctrl_regs_exist[f41_ctrl_reg_0b]
+		|| f41->attrs_ctrl_regs_exist[f41_ctrl_reg_0c]
+		|| f41->attrs_ctrl_regs_exist[f41_ctrl_reg_0d]
+		|| f41->attrs_ctrl_regs_exist[f41_ctrl_reg_0e])
+		rmi_write_block(fc->rmi_dev, f41->control.reg_0->address,
+					(u8 *)f41->control.reg_0,
+					sizeof(f41->control.reg_0->regs));
+	if (f41->attrs_ctrl_regs_exist[f41_ctrl_reg_1])
+		rmi_write_block(fc->rmi_dev, f41->control.reg_1->address,
+					(u8 *)f41->control.reg_1,
+					sizeof(f41->control.reg_1->regs));
+	if (f41->attrs_ctrl_regs_exist[f41_ctrl_reg_2__5])
+		rmi_write_block(fc->rmi_dev, f41->control.reg_2__5->address,
+					(u8 *)f41->control.reg_2__5,
+					sizeof(f41->control.reg_2__5->regs));
+	if (f41->attrs_ctrl_regs_exist[f41_ctrl_reg_6__9])
+		rmi_write_block(fc->rmi_dev, f41->control.reg_6__9->address,
+					(u8 *)f41->control.reg_6__9,
+					sizeof(f41->control.reg_6__9->regs));
+	if (f41->attrs_ctrl_regs_exist[f41_ctrl_reg_10])
+		rmi_write_block(fc->rmi_dev, f41->control.reg_10->address,
+					(u8 *)f41->control.reg_10,
+					sizeof(f41->control.reg_10->regs));
+
+	return 0;
+}
+
+static void rmi_f41_remove(struct rmi_function_container *fc)
+{
+	struct rmi_fn_41_data *f41 = fc->data;
+	dev_dbg(&fc->dev, "Removing F41.");
+	input_unregister_device(f41->input);
+	rmi_f41_free_memory(fc);
+}
+
+/* sysfs functions */
+/* Query */
+simple_show_union_struct_unsigned(query, has_reduced_reporting)
+simple_show_union_struct_unsigned(query, has_modal_control)
+simple_show_union_struct_unsigned(query, has_force)
+simple_show_union_struct_unsigned(query, has_orientation)
+simple_show_union_struct_unsigned(query, has_serial_number)
+simple_show_union_struct_unsigned(query, has_battery)
+simple_show_union_struct_unsigned(query, has_z)
+simple_show_union_struct_unsigned(query, has_single_tap)
+simple_show_union_struct_unsigned(query, number_of_buttons)
+
+/* Control */
+show_store_union_struct_unsigned(control, reg_0, reporting_mode)
+show_store_union_struct_unsigned(control, reg_0, modal_ctrl)
+show_store_union_struct_unsigned(control, reg_0, single_tap_interrupt_enable)
+show_store_union_struct_unsigned(control, reg_0, in_range_interrupt_enable)
+show_store_union_struct_unsigned(control, reg_0, new_sn_interrupt_enable)
+show_store_union_struct_unsigned(control, reg_1, button_1_interrupt_enable)
+show_store_union_struct_unsigned(control, reg_1, button_2_interrupt_enable)
+show_store_union_struct_unsigned(control, reg_1, button_3_interrupt_enable)
+show_store_union_struct_unsigned(control, reg_1, button_4_interrupt_enable)
+show_store_union_struct_unsigned(control, reg_1, button_5_interrupt_enable)
+show_store_union_struct_unsigned(control, reg_1, button_6_interrupt_enable)
+show_store_union_struct_unsigned(control, reg_1, button_7_interrupt_enable)
+show_store_union_struct_unsigned(control, reg_2__5, max_x_position)
+show_store_union_struct_unsigned(control, reg_2__5, max_y_position)
+show_store_union_struct_unsigned(control,
+					reg_6__9, x_reduced_reporting_distance)
+show_store_union_struct_unsigned(control,
+					reg_6__9, y_reduced_reporting_distance)
+show_store_union_struct_unsigned(control, reg_10, max_tap_time)
+
+
+/* Data */
+simple_show_union_struct_unsigned2(data, reg_0__3, x_position)
+simple_show_union_struct_unsigned2(data, reg_0__3, y_position)
+simple_show_union_struct_unsigned2(data, reg_4, force)
+simple_show_union_struct_unsigned2(data, reg_6, single_tap)
+simple_show_union_struct_unsigned2(data, reg_6, in_range)
+simple_show_union_struct_unsigned2(data, reg_6, new_serial_number)
+simple_show_union_struct_unsigned2(data, reg_7__8, azimuth)
+simple_show_union_struct_unsigned2(data, reg_7__8, altitude)
+simple_show_union_struct_unsigned2(data, reg_9, z_position)
+simple_show_union_struct_unsigned2(data, reg_10, battery_level)
+simple_show_union_struct_unsigned2(data, reg_11__14, pen_serial_number)
+
+static int rmi_f41_register_device(struct rmi_function_container *fc)
+{
+	int i;
+	int rc;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_fn_41_data *f41 = fc->data;
+	struct input_dev *input_dev = input_allocate_device();
+
+	if (!input_dev) {
+		dev_err(&fc->dev, "Failed to allocate input device.\n");
+		return -ENOMEM;
+	}
+
+	f41->input = input_dev;
+	snprintf(f41->input_name, MAX_STR_LEN, "%sfn%02x",
+			dev_name(&rmi_dev->dev), fc->fd.function_number);
+	input_dev->name = f41->input_name;
+	snprintf(f41->input_phys, MAX_STR_LEN, "%s/input0", input_dev->name);
+	input_dev->phys = f41->input_phys;
+	input_dev->dev.parent = &rmi_dev->dev;
+	input_set_drvdata(input_dev, f41);
+
+	/* 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 < f41->button_count; i++)
+		set_bit(f41->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 error_free_device;
+	}
+
+	return 0;
+
+error_free_device:
+	input_free_device(input_dev);
+
+	return rc;
+}
+
+static int rmi_f41_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	int error;
+	int button;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_fn_41_data *f41 = fc->data;
+
+
+	/* Read all the data registers. */
+	error = rmi_read_block(rmi_dev, fc->fd.data_base_addr, f41->data_block,
+			f41->data_block_size);
+	if (error < 0) {
+		dev_err(&fc->dev, "Failed to read data registers.\n");
+		return error;
+	}
+
+	/* Generate events for buttons that change state. */
+	for (button = 0; button < f41->button_count; button++) {
+		bool button_status;
+		/* bit shift to get button's status */
+		button_status =
+			((f41->data.reg_5->regs[0] >> button) & 0x01) != 0;
+		/* Generate an event here. */
+		input_report_key(f41->input, f41->button_map[button],
+				 button_status);
+	}
+
+	input_sync(f41->input); /* sync after groups of events */
+	return 0;
+}
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x41,
+	.init = rmi_f41_init,
+	.config = rmi_f41_config,
+	.remove = rmi_f41_remove,
+	.attention = rmi_f41_attention
+};
+
+static int __init rmi_f41_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_f41_module_exit(void)
+{
+	rmi_unregister_function_driver(&function_handler);
+}
+
+module_init(rmi_f41_module_init);
+module_exit(rmi_f41_module_exit);
+
+MODULE_AUTHOR("Daniel Rosenberg <daniel.rosenberg@synaptics.com>");
+MODULE_DESCRIPTION("RMI F41 module");
+MODULE_LICENSE("GPL");

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [RFC PATCH 17/17] input: RMI4 F54 analog data reporting
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (15 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 16/17] input: RMI4 F41 Active pen 2D input Christopher Heiny
@ 2012-08-17 22:17 ` Christopher Heiny
  2012-08-27 23:01   ` Linus Walleij
  2012-08-22 12:50 ` [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Linus Walleij
  2012-08-27 23:05 ` Linus Walleij
  18 siblings, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-08-17 22:17 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

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

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

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_f54.c | 2247 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 2247 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c
new file mode 100644
index 0000000..9c19799
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f54.c
@@ -0,0 +1,2247 @@
+/*
+ * Copyright (c) 2011-2012 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/hrtimer.h>
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+
+#define FUNCTION_DATA rmi_fn_54_data
+#define FNUM 54
+
+#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
+
+/* define fn $54 commands */
+#define GET_REPORT                1
+#define FORCE_CAL                 2
+
+#define NO_AUTO_CAL_MASK 1
+/* 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 */
+union f54_ad_query {
+	struct {
+		/* query 0 */
+		u8 num_of_rx_electrodes;
+
+		/* query 1 */
+		u8 num_of_tx_electrodes;
+
+		/* 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;
+
+		/* query 3.0 and 3.1 */
+		u16 clock_rate;
+
+		/* query 4 */
+		u8 touch_controller_family;
+
+		/* query 5 */
+		u8 has_pixel_touch_threshold_adjustment:1;
+		u8 f54_ad_query5_b1__7:7;
+
+		/* query 6 */
+		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;
+
+		/* query 7 */
+		u8 curve_compensation_mode:2;
+		u8 f54_ad_query7_b2__7:6;
+
+		/* query 8 */
+		u8 f54_ad_query2_b0:1;
+		u8 has_iir_filter:1;
+		u8 has_cmn_removal:1;
+		u8 has_cmn_maximum:1;
+		u8 has_touch_hysteresis:1;
+		u8 has_edge_compensation:1;
+		u8 has_per_frequency_noise_control:1;
+		u8 f54_ad_query8_b7:1;
+
+		u8 f54_ad_query9;
+		u8 f54_ad_query10;
+
+		/* query 11 */
+		u8 f54_ad_query11_b0__6:7;
+		u8 has_query_15:1;
+
+		/* query 12 */
+		u8 number_of_sensing_frequencies:4;
+		u8 f54_ad_query12_b4__7:4;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[14];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+/* And now for the very large amount of control registers */
+
+/* Ctrl registers */
+
+union f54_ad_control_0 {
+	/* control 0 */
+	struct {
+		u8 no_relax:1;
+		u8 no_scan:1;
+		u8 f54_ad_ctrl0_b2__7:6;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_1 {
+	/* control 1 */
+	struct {
+		/* control 1 */
+		u8 bursts_per_cluster:4;
+		u8 f54_ad_ctrl1_b4__7:4;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_2 {
+	/* control 2 */
+	struct {
+		u16 saturation_cap;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_3 {
+	/* control 3 */
+	struct {
+		u16 pixel_touch_threshold;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_4__6 {
+	struct {
+		/* control 4 */
+		u8 rx_feedback_cap:2;
+		u8 f54_ad_ctrl4_b2__7:6;
+
+		/* control 5 */
+		u8 low_ref_cap:2;
+		u8 low_ref_feedback_cap:2;
+		u8 low_ref_polarity:1;
+		u8 f54_ad_ctrl5_b5__7:3;
+
+		/* control 6 */
+		u8 high_ref_cap:2;
+		u8 high_ref_feedback_cap:2;
+		u8 high_ref_polarity:1;
+		u8 f54_ad_ctrl6_b5__7:3;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[3];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_7 {
+	struct {
+		/* control 7 */
+		u8 cbc_cap:2;
+		u8 cbc_polarity:2;
+		u8 cbc_tx_carrier_selection:1;
+		u8 f54_ad_ctrl6_b5__7:3;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_8__9 {
+	struct {
+		/* control 8 */
+		u16 integration_duration:10;
+		u16 f54_ad_ctrl8_b10__15:6;
+		/* control 9 */
+		u8 reset_duration;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[3];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_10 {
+	struct {
+		/* control 10 */
+		u8 noise_sensing_bursts_per_image:4;
+		u8 f54_ad_ctrl10_b4__7:4;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_11 {
+	struct {
+		/* control 11 */
+		u8 reserved;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_12__13 {
+	struct {
+		/* control 12 */
+		u8 slow_relaxation_rate;
+
+		/* control 13 */
+		u8 fast_relaxation_rate;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_14 {
+	struct {
+		/* control 14 */
+			u8 rxs_on_xaxis:1;
+			u8 curve_comp_on_txs:1;
+			u8 f54_ad_ctrl14b2__7:6;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+struct f54_ad_control_15n {
+	/*Control 15.* */
+	u8 sensor_rx_assignment;
+};
+
+struct f54_ad_control_15 {
+		struct f54_ad_control_15n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_16n {
+	/*Control 16.* */
+	u8 sensor_tx_assignment;
+};
+
+struct f54_ad_control_16 {
+		struct f54_ad_control_16n *regs;
+		u16 address;
+		u8 length;
+};
+
+
+/* control 17 */
+struct f54_ad_control_17n {
+	u8 burst_countb10__8:3;
+	u8 disable:1;
+	u8 f54_ad_ctrlb4:1;
+	u8 filter_bandwidth:3;
+};
+
+struct f54_ad_control_17 {
+		struct f54_ad_control_17n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_18n {
+	/*Control 18.* */
+	u8 burst_countb7__0n;
+};
+
+struct f54_ad_control_18 {
+		struct f54_ad_control_18n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_19n {
+	/*Control 19.* */
+	u8 stretch_duration;
+};
+
+struct f54_ad_control_19 {
+		struct f54_ad_control_19n *regs;
+		u16 address;
+		u8 length;
+};
+
+union f54_ad_control_20 {
+	struct {
+		/* control 20 */
+		u8 disable_noise_mitigation:1;
+		u8 f54_ad_ctrl20b2__7:7;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_21 {
+	struct {
+		/* control 21 */
+		u16 freq_shift_noise_threshold;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_22__26 {
+	struct {
+		/* control 22 */
+		/* u8 noise_density_threshold; */
+		u8 f54_ad_ctrl22;
+
+		/* control 23 */
+		u16 medium_noise_threshold;
+
+		/* control 24 */
+		u16 high_noise_threshold;
+
+		/* control 25 */
+		u8 noise_density;
+
+		/* control 26 */
+		u8 frame_count;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[7];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_27 {
+	struct {
+		/* control 27 */
+		u8 iir_filter_coef;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_28 {
+	struct {
+		/* control 28 */
+		u16 quiet_threshold;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+
+union f54_ad_control_29 {
+	struct {
+		/* control 29 */
+		u8 f54_ad_ctrl20b0__6:7;
+		u8 cmn_filter_disable:1;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_30 {
+	struct {
+		/* control 30 */
+		u8 cmn_filter_max;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_31 {
+	struct {
+		/* control 31 */
+		u8 touch_hysteresis;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f54_ad_control_32__35 {
+	struct {
+		/* control 32 */
+		u16 rx_low_edge_comp;
+
+		/* control 33 */
+		u16 rx_high_edge_comp;
+
+		/* control 34 */
+		u16 tx_low_edge_comp;
+
+		/* control 35 */
+		u16 tx_high_edge_comp;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[8];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+struct f54_ad_control_36n {
+	/*Control 36.* */
+	u8 axis1_comp;
+};
+
+struct f54_ad_control_36 {
+		struct f54_ad_control_36n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_37n {
+	/*Control 37.* */
+	u8 axis2_comp;
+};
+
+struct f54_ad_control_37 {
+		struct f54_ad_control_37n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_38n {
+	/*Control 38.* */
+	u8 noise_control_1;
+};
+
+struct f54_ad_control_38 {
+		struct f54_ad_control_38n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_39n {
+	/*Control 39.* */
+	u8 noise_control_2;
+};
+
+struct f54_ad_control_39 {
+		struct f54_ad_control_39n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control_40n {
+	/*Control 40.* */
+	u8 noise_control_3;
+};
+
+struct f54_ad_control_40 {
+		struct f54_ad_control_40n *regs;
+		u16 address;
+		u8 length;
+};
+
+struct f54_ad_control {
+	union f54_ad_control_0 *reg_0;
+	union f54_ad_control_1 *reg_1;
+	union f54_ad_control_2 *reg_2;
+	union f54_ad_control_3 *reg_3;
+	union f54_ad_control_4__6 *reg_4__6;
+	union f54_ad_control_7 *reg_7;
+	union f54_ad_control_8__9 *reg_8__9;
+	union f54_ad_control_10 *reg_10;
+	union f54_ad_control_11 *reg_11;
+	union f54_ad_control_12__13 *reg_12__13;
+	union f54_ad_control_14 *reg_14;
+	/* control 15 */
+	struct f54_ad_control_15 *reg_15;
+	/* control 16 */
+	struct f54_ad_control_16 *reg_16;
+
+	/* This register is n repetitions of f54_ad_control_17 */
+	struct f54_ad_control_17 *reg_17;
+
+	/* control 18 */
+	struct f54_ad_control_18 *reg_18;
+
+	/* control 19 */
+	struct f54_ad_control_19 *reg_19;
+
+	union f54_ad_control_20 *reg_20;
+	union f54_ad_control_21 *reg_21;
+	union f54_ad_control_22__26 *reg_22__26;
+	union f54_ad_control_27 *reg_27;
+	union f54_ad_control_28 *reg_28;
+	union f54_ad_control_29 *reg_29;
+	union f54_ad_control_30 *reg_30;
+	union f54_ad_control_31 *reg_31;
+	union f54_ad_control_32__35 *reg_32__35;
+	/* control 36 */
+	struct f54_ad_control_36 *reg_36;
+
+	/* control 37 */
+	struct f54_ad_control_37 *reg_37;
+
+	/* control 38 */
+	struct f54_ad_control_38 *reg_38;
+
+	/* control 39 */
+	struct f54_ad_control_39 *reg_39;
+
+	/* control 40 */
+	struct f54_ad_control_40 *reg_40;
+};
+
+/* define report types */
+enum f54_report_types {
+	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
+};
+
+/* sysfs functions */
+show_store_union_struct_prototype(report_type)
+
+store_union_struct_prototype(get_report)
+
+store_union_struct_prototype(force_cal)
+
+show_union_struct_prototype(status)
+
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+					struct bin_attribute *attributes,
+					char *buf, loff_t pos, size_t count);
+
+show_union_struct_prototype(num_of_rx_electrodes)
+show_union_struct_prototype(num_of_tx_electrodes)
+show_union_struct_prototype(has_image16)
+show_union_struct_prototype(has_image8)
+show_union_struct_prototype(has_baseline)
+show_union_struct_prototype(clock_rate)
+show_union_struct_prototype(touch_controller_family)
+show_union_struct_prototype(has_pixel_touch_threshold_adjustment)
+show_union_struct_prototype(has_sensor_assignment)
+show_union_struct_prototype(has_interference_metric)
+show_union_struct_prototype(has_sense_frequency_control)
+show_union_struct_prototype(has_firmware_noise_mitigation)
+show_union_struct_prototype(has_two_byte_report_rate)
+show_union_struct_prototype(has_one_byte_report_rate)
+show_union_struct_prototype(has_relaxation_control)
+show_union_struct_prototype(curve_compensation_mode)
+show_union_struct_prototype(has_iir_filter)
+show_union_struct_prototype(has_cmn_removal)
+show_union_struct_prototype(has_cmn_maximum)
+show_union_struct_prototype(has_touch_hysteresis)
+show_union_struct_prototype(has_edge_compensation)
+show_union_struct_prototype(has_per_frequency_noise_control)
+show_union_struct_prototype(number_of_sensing_frequencies)
+show_store_union_struct_prototype(no_auto_cal)
+show_store_union_struct_prototype(fifoindex)
+
+/* Repeated Control Registers */
+show_union_struct_prototype(sensor_rx_assignment)
+show_union_struct_prototype(sensor_tx_assignment)
+show_union_struct_prototype(filter_bandwidth)
+show_union_struct_prototype(disable)
+show_union_struct_prototype(burst_count)
+show_union_struct_prototype(stretch_duration)
+show_store_union_struct_prototype(axis1_comp)
+show_store_union_struct_prototype(axis2_comp)
+show_union_struct_prototype(noise_control_1)
+show_union_struct_prototype(noise_control_2)
+show_union_struct_prototype(noise_control_3)
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+show_store_union_struct_prototype(no_relax)
+show_store_union_struct_prototype(no_scan)
+show_store_union_struct_prototype(bursts_per_cluster)
+show_store_union_struct_prototype(saturation_cap)
+show_store_union_struct_prototype(pixel_touch_threshold)
+show_store_union_struct_prototype(rx_feedback_cap)
+show_store_union_struct_prototype(low_ref_cap)
+show_store_union_struct_prototype(low_ref_feedback_cap)
+show_store_union_struct_prototype(low_ref_polarity)
+show_store_union_struct_prototype(high_ref_cap)
+show_store_union_struct_prototype(high_ref_feedback_cap)
+show_store_union_struct_prototype(high_ref_polarity)
+show_store_union_struct_prototype(cbc_cap)
+show_store_union_struct_prototype(cbc_polarity)
+show_store_union_struct_prototype(cbc_tx_carrier_selection)
+show_store_union_struct_prototype(integration_duration)
+show_store_union_struct_prototype(reset_duration)
+show_store_union_struct_prototype(noise_sensing_bursts_per_image)
+show_store_union_struct_prototype(slow_relaxation_rate)
+show_store_union_struct_prototype(fast_relaxation_rate)
+show_store_union_struct_prototype(rxs_on_xaxis)
+show_store_union_struct_prototype(curve_comp_on_txs)
+show_store_union_struct_prototype(disable_noise_mitigation)
+show_store_union_struct_prototype(freq_shift_noise_threshold)
+/*show_store_union_struct_prototype(noise_density_threshold)*/
+show_store_union_struct_prototype(medium_noise_threshold)
+show_store_union_struct_prototype(high_noise_threshold)
+show_store_union_struct_prototype(noise_density)
+show_store_union_struct_prototype(frame_count)
+show_store_union_struct_prototype(iir_filter_coef)
+show_store_union_struct_prototype(quiet_threshold)
+show_store_union_struct_prototype(cmn_filter_disable)
+show_store_union_struct_prototype(cmn_filter_max)
+show_store_union_struct_prototype(touch_hysteresis)
+show_store_union_struct_prototype(rx_low_edge_comp)
+show_store_union_struct_prototype(rx_high_edge_comp)
+show_store_union_struct_prototype(tx_low_edge_comp)
+show_store_union_struct_prototype(tx_high_edge_comp)
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+static struct attribute *attrs[] = {
+	attrify(report_type),
+	attrify(get_report),
+	attrify(force_cal),
+	attrify(status),
+	attrify(num_of_rx_electrodes),
+	attrify(num_of_tx_electrodes),
+	attrify(has_image16),
+	attrify(has_image8),
+	attrify(has_baseline),
+	attrify(clock_rate),
+	attrify(touch_controller_family),
+	attrify(has_pixel_touch_threshold_adjustment),
+	attrify(has_sensor_assignment),
+	attrify(has_interference_metric),
+	attrify(has_sense_frequency_control),
+	attrify(has_firmware_noise_mitigation),
+	attrify(has_two_byte_report_rate),
+	attrify(has_one_byte_report_rate),
+	attrify(has_relaxation_control),
+	attrify(curve_compensation_mode),
+	attrify(has_iir_filter),
+	attrify(has_cmn_removal),
+	attrify(has_cmn_maximum),
+	attrify(has_touch_hysteresis),
+	attrify(has_edge_compensation),
+	attrify(has_per_frequency_noise_control),
+	attrify(number_of_sensing_frequencies),
+	attrify(no_auto_cal),
+	attrify(fifoindex),
+	NULL
+};
+
+static struct attribute_group attrs_query = GROUP(attrs);
+
+struct bin_attribute dev_rep_data = {
+	.attr = {
+		 .name = "rep_data",
+		 .mode = RMI_RO_ATTR},
+	.size = 0,
+	.read = rmi_fn_54_data_read,
+};
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+static struct attribute *attrs_reg_0[] = {
+	attrify(no_relax),
+	attrify(no_scan),
+	NULL
+};
+
+static struct attribute *attrs_reg_1[] = {
+	attrify(bursts_per_cluster),
+	NULL
+};
+
+static struct attribute *attrs_reg_2[] = {
+	attrify(saturation_cap),
+	NULL
+};
+
+static struct attribute *attrs_reg_3[] = {
+	attrify(pixel_touch_threshold),
+	NULL
+};
+
+static struct attribute *attrs_reg_4__6[] = {
+	attrify(rx_feedback_cap),
+	attrify(low_ref_cap),
+	attrify(low_ref_feedback_cap),
+	attrify(low_ref_polarity),
+	attrify(high_ref_cap),
+	attrify(high_ref_feedback_cap),
+	attrify(high_ref_polarity),
+	NULL
+};
+
+static struct attribute *attrs_reg_7[] = {
+	attrify(cbc_cap),
+	attrify(cbc_polarity),
+	attrify(cbc_tx_carrier_selection),
+	NULL
+};
+
+static struct attribute *attrs_reg_8__9[] = {
+	attrify(integration_duration),
+	attrify(reset_duration),
+	NULL
+};
+
+static struct attribute *attrs_reg_10[] = {
+	attrify(noise_sensing_bursts_per_image),
+	NULL
+};
+
+static struct attribute *attrs_reg_12__13[] = {
+	attrify(slow_relaxation_rate),
+	attrify(fast_relaxation_rate),
+	NULL
+};
+
+static struct attribute *attrs_reg_14__16[] = {
+	attrify(rxs_on_xaxis),
+	attrify(curve_comp_on_txs),
+	attrify(sensor_rx_assignment),
+	attrify(sensor_tx_assignment),
+	NULL
+};
+
+static struct attribute *attrs_reg_17__19[] = {
+	attrify(filter_bandwidth),
+	attrify(disable),
+	attrify(burst_count),
+	attrify(stretch_duration),
+	NULL
+};
+
+static struct attribute *attrs_reg_20[] = {
+	attrify(disable_noise_mitigation),
+	NULL
+};
+
+static struct attribute *attrs_reg_21[] = {
+	attrify(freq_shift_noise_threshold),
+	NULL
+};
+
+static struct attribute *attrs_reg_22__26[] = {
+	/*attrify(noise_density_threshold),*/
+	attrify(medium_noise_threshold),
+	attrify(high_noise_threshold),
+	attrify(noise_density),
+	attrify(frame_count),
+	NULL
+};
+
+static struct attribute *attrs_reg_27[] = {
+	attrify(iir_filter_coef),
+	NULL
+};
+
+static struct attribute *attrs_reg_28[] = {
+	attrify(quiet_threshold),
+	NULL
+};
+
+static struct attribute *attrs_reg_29[] = {
+	attrify(cmn_filter_disable),
+	NULL
+};
+
+static struct attribute *attrs_reg_30[] = {
+	attrify(cmn_filter_max),
+	NULL
+};
+
+static struct attribute *attrs_reg_31[] = {
+	attrify(touch_hysteresis),
+	NULL
+};
+
+static struct attribute *attrs_reg_32__35[] = {
+	attrify(rx_low_edge_comp),
+	attrify(rx_high_edge_comp),
+	attrify(tx_low_edge_comp),
+	attrify(tx_high_edge_comp),
+	NULL
+};
+
+static struct attribute *attrs_reg_36[] = {
+	attrify(axis1_comp),
+	NULL
+};
+
+static struct attribute *attrs_reg_37[] = {
+	attrify(axis2_comp),
+	NULL
+};
+
+static struct attribute *attrs_reg_38__40[] = {
+	attrify(noise_control_1),
+	attrify(noise_control_2),
+	attrify(noise_control_3),
+	NULL
+};
+
+static struct attribute_group attrs_ctrl_regs[] = {
+	GROUP(attrs_reg_0),
+	GROUP(attrs_reg_1),
+	GROUP(attrs_reg_2),
+	GROUP(attrs_reg_3),
+	GROUP(attrs_reg_4__6),
+	GROUP(attrs_reg_7),
+	GROUP(attrs_reg_8__9),
+	GROUP(attrs_reg_10),
+	GROUP(attrs_reg_12__13),
+	GROUP(attrs_reg_14__16),
+	GROUP(attrs_reg_17__19),
+	GROUP(attrs_reg_20),
+	GROUP(attrs_reg_21),
+	GROUP(attrs_reg_22__26),
+	GROUP(attrs_reg_27),
+	GROUP(attrs_reg_28),
+	GROUP(attrs_reg_29),
+	GROUP(attrs_reg_30),
+	GROUP(attrs_reg_31),
+	GROUP(attrs_reg_32__35),
+	GROUP(attrs_reg_36),
+	GROUP(attrs_reg_37),
+	GROUP(attrs_reg_38__40)
+};
+
+/* data specific to fn $54 that needs to be kept around */
+struct rmi_fn_54_data {
+	union f54_ad_query query;
+	struct f54_ad_control control;
+	bool attrs_ctrl_regs_exist[ARRAY_SIZE(attrs_ctrl_regs)];
+	enum f54_report_types report_type;
+	u16 fifoindex;
+	signed char status;
+	bool no_auto_cal;
+	unsigned int report_size;
+	unsigned char *report_data;
+	unsigned int bufsize;
+	struct mutex data_mutex;
+	struct mutex status_mutex;
+	struct mutex control_mutex;
+#if F54_WATCHDOG
+	struct hrtimer watchdog;
+#endif
+	struct rmi_function_container *fc;
+	struct work_struct work;
+};
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+#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_alloc_memory(struct rmi_function_container *fc);
+
+static void rmi_f54_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f54_initialize(struct rmi_function_container *fc);
+
+static int rmi_f54_reset(struct rmi_function_container *fc);
+
+static int rmi_f54_create_sysfs(struct rmi_function_container *fc);
+
+static int rmi_f54_init(struct rmi_function_container *fc)
+{
+	int retval = 0;
+	struct rmi_fn_54_data *f54;
+
+	retval = rmi_f54_alloc_memory(fc);
+	if (retval < 0)
+		goto error_exit;
+
+	retval = rmi_f54_initialize(fc);
+	if (retval < 0)
+		goto error_exit;
+
+	retval = rmi_f54_create_sysfs(fc);
+	if (retval < 0)
+		goto error_exit;
+	f54 = fc->data;
+	f54->status = IDLE;
+	return retval;
+
+error_exit:
+	rmi_f54_free_memory(fc);
+
+	return retval;
+}
+
+static int rmi_f54_alloc_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *f54;
+
+	f54 = kzalloc(sizeof(struct rmi_fn_54_data), GFP_KERNEL);
+	if (!f54) {
+		dev_err(&fc->dev, "Failed to allocate rmi_fn_54_data.\n");
+		return -ENOMEM;
+	}
+	fc->data = f54;
+	f54->fc = fc;
+
+
+	return 0;
+}
+
+static void rmi_f54_free_memory(struct rmi_function_container *fc)
+{
+	int reg_num;
+	struct rmi_fn_54_data *f54 = fc->data;
+	sysfs_remove_group(&fc->dev.kobj, &attrs_query);
+	for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++)
+		sysfs_remove_group(&fc->dev.kobj, &attrs_ctrl_regs[reg_num]);
+	sysfs_remove_bin_file(&fc->dev.kobj, &dev_rep_data);
+	if (f54) {
+		kfree(f54->report_data);
+		kfree(f54);
+		fc->data = NULL;
+	}
+}
+
+static int rmi_f54_reset(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *data = fc->data;
+	struct rmi_driver *driver = fc->rmi_dev->driver;
+
+#if F54_WATCHDOG
+	hrtimer_cancel(&data->watchdog);
+#endif
+
+	mutex_lock(&data->status_mutex);
+	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 = -ECONNRESET;
+	mutex_unlock(&data->status_mutex);
+
+	return 0;
+}
+
+static void rmi_f54_remove(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *data = fc->data;
+
+	dev_info(&fc->dev, "Removing F54.");
+
+	#if F54_WATCHDOG
+	/* Stop timer */
+	hrtimer_cancel(&data->watchdog);
+	#endif
+
+	rmi_f54_free_memory(fc);
+}
+
+static int rmi_f54_create_sysfs(struct rmi_function_container *fc)
+{
+	int reg_num;
+	int retval;
+	struct rmi_fn_54_data *f54 = fc->data;
+	dev_dbg(&fc->dev, "Creating sysfs files.");
+	/* Set up sysfs device attributes. */
+
+	if (sysfs_create_group(&fc->dev.kobj, &attrs_query) < 0) {
+		dev_err(&fc->dev, "Failed to create query sysfs files.");
+		return -ENODEV;
+	}
+	for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++) {
+		if (f54->attrs_ctrl_regs_exist[reg_num]) {
+			retval = sysfs_create_group(&fc->dev.kobj,
+					&attrs_ctrl_regs[reg_num]);
+			if (retval < 0) {
+				dev_err(&fc->dev, "Failed to create sysfs file group for reg group %d, error = %d.",
+							reg_num, retval);
+				return -ENODEV;
+			}
+		}
+	}
+
+	/* 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);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+
+
+static int rmi_f54_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *f54 = fc->data;
+	struct f54_ad_control *control;
+	int retval = 0;
+	u8 size = 0;
+	u16 next_loc;
+	u8 reg_num;
+
+#if F54_WATCHDOG
+	/* Set up watchdog timer to catch unanswered get_report commands */
+	hrtimer_init(&f54->watchdog, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	f54->watchdog.function = clear_status;
+
+	/* work function to do unlocking */
+	INIT_WORK(&f54->work, clear_status_worker);
+#endif
+
+	/* Read F54 Query Data */
+	retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+		(u8 *)&f54->query, sizeof(f54->query));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read query registers from 0x%04x\n",
+				fc->fd.query_base_addr);
+		return retval;
+	}
+
+	/* Initialize the control registers */
+	next_loc = fc->fd.control_base_addr;
+	reg_num = 0;
+	control = &f54->control;
+
+	f54->attrs_ctrl_regs_exist[reg_num] = true;
+	reg_num++;
+	control->reg_0 = kzalloc(sizeof(union f54_ad_control_0), GFP_KERNEL);
+	if (!control->reg_0) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_0->address = next_loc;
+	next_loc += sizeof(control->reg_0->regs);
+
+	if (f54->query.touch_controller_family == 0
+			|| f54->query.touch_controller_family == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_1 = kzalloc(sizeof(union f54_ad_control_1),
+								GFP_KERNEL);
+		if (!control->reg_1) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_1->address = next_loc;
+		next_loc += sizeof(control->reg_1->regs);
+	}
+	reg_num++;
+
+	f54->attrs_ctrl_regs_exist[reg_num] = true;
+	reg_num++;
+	control->reg_2 = kzalloc(sizeof(union f54_ad_control_2), GFP_KERNEL);
+	if (!control->reg_2) {
+		dev_err(&fc->dev, "Failed to allocate control registers.");
+		return -ENOMEM;
+	}
+	control->reg_2->address = next_loc;
+	next_loc += sizeof(control->reg_2->regs);
+
+	if (f54->query.has_pixel_touch_threshold_adjustment == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+
+		control->reg_3 = kzalloc(sizeof(union f54_ad_control_3),
+								GFP_KERNEL);
+		if (!control->reg_3) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_3->address = next_loc;
+		next_loc += sizeof(control->reg_3->regs);
+	}
+	reg_num++;
+
+	if (f54->query.touch_controller_family == 0
+		|| f54->query.touch_controller_family == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_4__6 = kzalloc(sizeof(union f54_ad_control_4__6),
+								GFP_KERNEL);
+		if (!control->reg_4__6) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_4__6->address = next_loc;
+		next_loc += sizeof(control->reg_4__6->regs);
+	}
+	reg_num++;
+
+	if (f54->query.touch_controller_family == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_7 = kzalloc(sizeof(union f54_ad_control_7),
+								GFP_KERNEL);
+		if (!control->reg_7) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_7->address = next_loc;
+		next_loc += sizeof(control->reg_7->regs);
+	}
+	reg_num++;
+
+	if (f54->query.touch_controller_family == 0
+		|| f54->query.touch_controller_family == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_8__9 = kzalloc(sizeof(union f54_ad_control_8__9),
+								GFP_KERNEL);
+		if (!control->reg_8__9) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_8__9->address = next_loc;
+		next_loc += sizeof(control->reg_8__9->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_interference_metric == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_10 = kzalloc(sizeof(union f54_ad_control_10),
+								GFP_KERNEL);
+		if (!control->reg_10) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_10->address = next_loc;
+		next_loc += sizeof(control->reg_10->regs);
+	}
+	reg_num++;
+
+	/* F54 Control Register 11 is reserved */
+	next_loc++;
+
+	if (f54->query.has_relaxation_control == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_12__13 = kzalloc(
+			sizeof(union f54_ad_control_12__13), GFP_KERNEL);
+		if (!control->reg_12__13) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_12__13->address = next_loc;
+		next_loc += sizeof(control->reg_12__13->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_sensor_assignment == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_14 = kzalloc(sizeof(union f54_ad_control_14),
+								GFP_KERNEL);
+		if (!control->reg_14) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_15 =
+			kzalloc(sizeof(struct f54_ad_control_15), GFP_KERNEL);
+		if (!control->reg_15) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_15->length = f54->query.num_of_rx_electrodes;
+		control->reg_15->regs =
+				kzalloc(control->reg_15->length *
+					sizeof(struct f54_ad_control_15n),
+					GFP_KERNEL);
+		if (!control->reg_15->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_16 =
+			kzalloc(sizeof(struct f54_ad_control_16), GFP_KERNEL);
+		if (!control->reg_16) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_16->length = f54->query.num_of_tx_electrodes;
+		control->reg_16->regs =
+				kzalloc(control->reg_16->length *
+					sizeof(struct f54_ad_control_16n),
+					GFP_KERNEL);
+		if (!control->reg_16->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_14->address = next_loc;
+		next_loc += sizeof(control->reg_14->regs);
+		control->reg_15->address = next_loc;
+		next_loc += control->reg_15->length;
+		control->reg_16->address = next_loc;
+		next_loc += control->reg_16->length;
+	}
+	reg_num++;
+
+	if (f54->query.has_sense_frequency_control == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		size = f54->query.number_of_sensing_frequencies;
+
+		control->reg_17 =
+			kzalloc(sizeof(struct f54_ad_control_17), GFP_KERNEL);
+		if (!control->reg_17) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_17->length = size;
+		control->reg_17->regs = kzalloc(size *
+				sizeof(struct f54_ad_control_17n), GFP_KERNEL);
+		if (!control->reg_17->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_18 =
+			kzalloc(sizeof(struct f54_ad_control_18), GFP_KERNEL);
+		if (!control->reg_18) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_18->length = size;
+		control->reg_18->regs = kzalloc(size *
+				sizeof(struct f54_ad_control_18n), GFP_KERNEL);
+		if (!control->reg_18->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_19 =
+			kzalloc(sizeof(struct f54_ad_control_19), GFP_KERNEL);
+		if (!control->reg_19) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_19->length = size;
+		control->reg_19->regs = kzalloc(size *
+				sizeof(struct f54_ad_control_19n), GFP_KERNEL);
+		if (!control->reg_19->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_17->address = next_loc;
+		next_loc += size;
+		control->reg_18->address = next_loc;
+		next_loc += size;
+		control->reg_19->address = next_loc;
+		next_loc += size;
+	}
+	reg_num++;
+
+	f54->attrs_ctrl_regs_exist[reg_num] = true;
+	control->reg_20 = kzalloc(sizeof(union f54_ad_control_20), GFP_KERNEL);
+	control->reg_20->address = next_loc;
+	next_loc += sizeof(control->reg_20->regs);
+	reg_num++;
+
+	if (f54->query.has_sense_frequency_control == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_21 = kzalloc(sizeof(union f54_ad_control_21),
+								GFP_KERNEL);
+		control->reg_21->address = next_loc;
+		next_loc += sizeof(control->reg_21->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_sense_frequency_control == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_22__26 = kzalloc(
+			sizeof(union f54_ad_control_22__26), GFP_KERNEL);
+		control->reg_22__26->address = next_loc;
+		next_loc += sizeof(control->reg_22__26->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_iir_filter == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_27 = kzalloc(sizeof(union f54_ad_control_27),
+								GFP_KERNEL);
+		control->reg_27->address = next_loc;
+		next_loc += sizeof(control->reg_27->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_firmware_noise_mitigation == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_28 = kzalloc(sizeof(union f54_ad_control_28),
+								GFP_KERNEL);
+		control->reg_28->address = next_loc;
+		next_loc += sizeof(control->reg_28->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_cmn_removal == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_29 = kzalloc(sizeof(union f54_ad_control_29),
+								GFP_KERNEL);
+		control->reg_29->address = next_loc;
+		next_loc += sizeof(control->reg_29->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_cmn_maximum == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_30 = kzalloc(sizeof(union f54_ad_control_30),
+								GFP_KERNEL);
+		control->reg_30->address = next_loc;
+		next_loc += sizeof(control->reg_30->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_touch_hysteresis == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_31 = kzalloc(sizeof(union f54_ad_control_31),
+								GFP_KERNEL);
+		control->reg_31->address = next_loc;
+		next_loc += sizeof(control->reg_31->regs);
+	}
+	reg_num++;
+
+	if (f54->query.has_interference_metric == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+		control->reg_32__35 = kzalloc(
+			sizeof(union f54_ad_control_32__35), GFP_KERNEL);
+		control->reg_32__35->address = next_loc;
+		next_loc += sizeof(control->reg_32__35->regs);
+	}
+	reg_num++;
+
+	if (f54->query.curve_compensation_mode == 1) {
+		size = max(f54->query.num_of_rx_electrodes,
+				f54->query.num_of_tx_electrodes);
+	}
+	if (f54->query.curve_compensation_mode == 2)
+		size = f54->query.num_of_rx_electrodes;
+	if (f54->query.curve_compensation_mode == 1
+			|| f54->query.curve_compensation_mode == 2) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+
+		control->reg_36 =
+			kzalloc(sizeof(struct f54_ad_control_36), GFP_KERNEL);
+		if (!control->reg_36) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_36->length = size;
+		control->reg_36->regs =
+			kzalloc(size * sizeof(struct f54_ad_control_36n),
+				GFP_KERNEL);
+		if (!control->reg_36->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_36->address = next_loc;
+		next_loc += size;
+	}
+	reg_num++;
+
+	if (f54->query.curve_compensation_mode == 2) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+
+		control->reg_37 =
+			kzalloc(sizeof(struct f54_ad_control_37), GFP_KERNEL);
+		if (!control->reg_37) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_37->length = f54->query.num_of_tx_electrodes;
+		control->reg_37->regs = kzalloc(control->reg_37->length *
+				sizeof(struct f54_ad_control_37n),
+				GFP_KERNEL);
+		if (!control->reg_37->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_37->address = next_loc;
+		next_loc += control->reg_37->length;
+	}
+	reg_num++;
+
+	if (f54->query.has_per_frequency_noise_control == 1) {
+		f54->attrs_ctrl_regs_exist[reg_num] = true;
+
+		control->reg_38 =
+			kzalloc(sizeof(struct f54_ad_control_38), GFP_KERNEL);
+		if (!control->reg_38) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_38->length =
+				f54->query.number_of_sensing_frequencies;
+		control->reg_38->regs = kzalloc(control->reg_38->length *
+					sizeof(struct f54_ad_control_38n),
+					GFP_KERNEL);
+		if (!control->reg_38->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_39 =
+			kzalloc(sizeof(struct f54_ad_control_39), GFP_KERNEL);
+		if (!control->reg_39) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_39->length =
+				f54->query.number_of_sensing_frequencies;
+		control->reg_39->regs = kzalloc(control->reg_39->length *
+					sizeof(struct f54_ad_control_39n),
+					GFP_KERNEL);
+		if (!control->reg_39->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_40 =
+			kzalloc(sizeof(struct f54_ad_control_40), GFP_KERNEL);
+		if (!control->reg_40) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+		control->reg_40->length =
+				f54->query.number_of_sensing_frequencies;
+		control->reg_40->regs = kzalloc(control->reg_40->length *
+				sizeof(struct f54_ad_control_40n), GFP_KERNEL);
+		if (!control->reg_40->regs) {
+			dev_err(&fc->dev, "Failed to allocate control registers.");
+			return -ENOMEM;
+		}
+
+		control->reg_38->address = next_loc;
+		next_loc += control->reg_38->length;
+		control->reg_39->address = next_loc;
+		next_loc += control->reg_39->length;
+		control->reg_40->address = next_loc;
+		next_loc += control->reg_40->length;
+	}
+	reg_num++;
+
+	mutex_init(&f54->data_mutex);
+
+	mutex_init(&f54->status_mutex);
+
+	mutex_init(&f54->control_mutex);
+
+	return retval;
+}
+
+static void set_report_size(struct rmi_fn_54_data *data)
+{
+	u8 rx = data->query.num_of_rx_electrodes;
+	u8 tx = data->query.num_of_tx_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);
+	/* 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.num_of_tx_electrodes;
+									i++) {
+			for (j = 0; j <
+			     data->query.num_of_rx_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) {
+		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 %#06x.\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 *f54;
+
+	fc = to_rmi_function_container(dev);
+	f54 = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", f54->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);
+}
+
+simple_show_union_struct_unsigned(query, num_of_rx_electrodes)
+simple_show_union_struct_unsigned(query, num_of_tx_electrodes)
+simple_show_union_struct_unsigned(query, has_image16)
+simple_show_union_struct_unsigned(query, has_image8)
+simple_show_union_struct_unsigned(query, has_baseline)
+simple_show_union_struct_unsigned(query, clock_rate)
+simple_show_union_struct_unsigned(query, touch_controller_family)
+simple_show_union_struct_unsigned(query, has_pixel_touch_threshold_adjustment)
+simple_show_union_struct_unsigned(query, has_sensor_assignment)
+simple_show_union_struct_unsigned(query, has_interference_metric)
+simple_show_union_struct_unsigned(query, has_sense_frequency_control)
+simple_show_union_struct_unsigned(query, has_firmware_noise_mitigation)
+simple_show_union_struct_unsigned(query, has_two_byte_report_rate)
+simple_show_union_struct_unsigned(query, has_one_byte_report_rate)
+simple_show_union_struct_unsigned(query, has_relaxation_control)
+simple_show_union_struct_unsigned(query, curve_compensation_mode)
+simple_show_union_struct_unsigned(query, has_iir_filter)
+simple_show_union_struct_unsigned(query, has_cmn_removal)
+simple_show_union_struct_unsigned(query, has_cmn_maximum)
+simple_show_union_struct_unsigned(query, has_touch_hysteresis)
+simple_show_union_struct_unsigned(query, has_edge_compensation)
+simple_show_union_struct_unsigned(query, has_per_frequency_noise_control)
+simple_show_union_struct_unsigned(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 *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+				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 -EINVAL;
+	/* 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 & NO_AUTO_CAL_MASK) == val)
+		return count;
+	/* Write the control back to the control register (F54_AD_Ctrl0)
+	 * Ignores everything but bit 0 */
+	data = (data & ~NO_AUTO_CAL_MASK) | (val & NO_AUTO_CAL_MASK);
+	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 */
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+				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;
+	}
+}
+
+/* Repeated Register sysfs functions */
+show_repeated_union_struct_unsigned(control, reg_15, sensor_rx_assignment)
+show_repeated_union_struct_unsigned(control, reg_16, sensor_tx_assignment)
+
+show_repeated_union_struct_unsigned(control, reg_17, filter_bandwidth)
+show_repeated_union_struct_unsigned(control, reg_17, disable)
+
+
+static ssize_t rmi_fn_54_burst_count_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+	int result, size = 0;
+	char *temp;
+	int i;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	mutex_lock(&data->control_mutex);
+	/* Read current control values */
+	result = rmi_read_block(fc->rmi_dev, data->control.reg_17->address,
+			(u8 *) data->control.reg_17->regs,
+			data->control.reg_17->length * sizeof(u8));
+	if (result < 0) {
+		dev_err(dev, "%s : Could not read control at 0x%x\n"
+					"Data may be outdated.", __func__,
+					data->control.reg_17->address);
+	}
+
+	result = rmi_read_block(fc->rmi_dev, data->control.reg_18->address,
+			(u8 *)data->control.reg_18->regs,
+			data->control.reg_18->length * sizeof(u8));
+	if (result < 0) {
+		dev_err(dev, "%s : Could not read control at 0x%x\n"
+					"Data may be outdated.", __func__,
+					data->control.reg_18->address);
+	}
+	mutex_unlock(&data->control_mutex);
+	temp = buf;
+	for (i = 0; i < data->control.reg_17->length; i++) {
+		result = snprintf(temp, PAGE_SIZE - size, "%u ", (1<<8) *
+			data->control.reg_17->regs[i].burst_countb10__8 +
+			data->control.reg_18->regs[i].burst_countb7__0n);
+		size += result;
+		temp += result;
+	}
+	return size + snprintf(temp, PAGE_SIZE - size, "\n");
+}
+
+show_repeated_union_struct_unsigned(control, reg_19, stretch_duration)
+show_store_repeated_union_struct_unsigned(control, reg_36, axis1_comp)
+show_store_repeated_union_struct_unsigned(control, reg_37, axis2_comp)
+
+show_repeated_union_struct_unsigned(control, reg_38, noise_control_1)
+show_repeated_union_struct_unsigned(control, reg_39, noise_control_2)
+show_repeated_union_struct_unsigned(control, reg_40, noise_control_3)
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+show_store_union_struct_unsigned(control, reg_0, no_relax)
+show_store_union_struct_unsigned(control, reg_0, no_scan)
+show_store_union_struct_unsigned(control, reg_1, bursts_per_cluster)
+show_store_union_struct_unsigned(control, reg_2, saturation_cap)
+show_store_union_struct_unsigned(control, reg_3, pixel_touch_threshold)
+show_store_union_struct_unsigned(control, reg_4__6, rx_feedback_cap)
+show_store_union_struct_unsigned(control, reg_4__6, low_ref_cap)
+show_store_union_struct_unsigned(control, reg_4__6, low_ref_feedback_cap)
+show_store_union_struct_unsigned(control, reg_4__6, low_ref_polarity)
+show_store_union_struct_unsigned(control, reg_4__6, high_ref_cap)
+show_store_union_struct_unsigned(control, reg_4__6, high_ref_feedback_cap)
+show_store_union_struct_unsigned(control, reg_4__6, high_ref_polarity)
+show_store_union_struct_unsigned(control, reg_7, cbc_cap)
+show_store_union_struct_unsigned(control, reg_7, cbc_polarity)
+show_store_union_struct_unsigned(control, reg_7, cbc_tx_carrier_selection)
+show_store_union_struct_unsigned(control, reg_8__9, integration_duration)
+show_store_union_struct_unsigned(control, reg_8__9, reset_duration)
+show_store_union_struct_unsigned(control, reg_10,
+						noise_sensing_bursts_per_image)
+show_store_union_struct_unsigned(control, reg_12__13, slow_relaxation_rate)
+show_store_union_struct_unsigned(control, reg_12__13, fast_relaxation_rate)
+show_store_union_struct_unsigned(control, reg_14, rxs_on_xaxis)
+show_store_union_struct_unsigned(control, reg_14, curve_comp_on_txs)
+show_store_union_struct_unsigned(control, reg_20, disable_noise_mitigation)
+show_store_union_struct_unsigned(control, reg_21, freq_shift_noise_threshold)
+show_store_union_struct_unsigned(control, reg_22__26, medium_noise_threshold)
+show_store_union_struct_unsigned(control, reg_22__26, high_noise_threshold)
+show_store_union_struct_unsigned(control, reg_22__26, noise_density)
+show_store_union_struct_unsigned(control, reg_22__26, frame_count)
+show_store_union_struct_unsigned(control, reg_27, iir_filter_coef)
+show_store_union_struct_unsigned(control, reg_28, quiet_threshold)
+show_store_union_struct_unsigned(control, reg_29, cmn_filter_disable)
+show_store_union_struct_unsigned(control, reg_30, cmn_filter_max)
+show_store_union_struct_unsigned(control, reg_31, touch_hysteresis)
+show_store_union_struct_unsigned(control, reg_32__35, rx_low_edge_comp)
+show_store_union_struct_unsigned(control, reg_32__35, rx_high_edge_comp)
+show_store_union_struct_unsigned(control, reg_32__35, tx_low_edge_comp)
+show_store_union_struct_unsigned(control, reg_32__35, tx_high_edge_comp)
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+static struct rmi_function_handler function_handler = {
+	.func = 0x54,
+	.init = rmi_f54_init,
+	.config = NULL,
+	.reset = rmi_f54_reset,
+	.attention = rmi_f54_attention,
+	.remove = rmi_f54_remove
+};
+
+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");
+MODULE_VERSION(RMI_DRIVER_VERSION);

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (16 preceding siblings ...)
  2012-08-17 22:17 ` [RFC PATCH 17/17] input: RMI4 F54 analog data reporting Christopher Heiny
@ 2012-08-22 12:50 ` Linus Walleij
  2012-08-22 21:29   ` Christopher Heiny
  2012-08-27 23:20   ` Christopher Heiny
  2012-08-27 23:05 ` Linus Walleij
  18 siblings, 2 replies; 44+ messages in thread
From: Linus Walleij @ 2012-08-22 12:50 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

On Sat, Aug 18, 2012 at 12:17 AM, Christopher Heiny
<cheiny@synaptics.com> wrote:

> This patch implements a driver supporting Synaptics ClearPad and other
> touchscreen sensors that use the RMI4 protocol, as defined here:

Nice!

> This patch is against the v2.6.38 tag of Linus' kernel tree, commit
> 521cb40b0c44418a4fd36dc633f575813d59a43d.  This will be our last patch against
> such an old kernel version, future patches will be against 3.x kernels.

Please use the head of the subsystem tree to do this work.
In this case, use Dmitry's git.

Currently I just cannot test this because we have no such old codebase
around.

But I will attempt to review the patch set!

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 1/17] input: RMI4 public header file and documentation.
  2012-08-17 22:17 ` [RFC PATCH 1/17] input: RMI4 public header file and documentation Christopher Heiny
@ 2012-08-22 19:08   ` Linus Walleij
  2012-08-22 21:45     ` Dmitry Torokhov
  2012-08-22 23:35     ` Rafael J. Wysocki
  0 siblings, 2 replies; 44+ messages in thread
From: Linus Walleij @ 2012-08-22 19:08 UTC (permalink / raw)
  To: Christopher Heiny, Rafael J. Wysocki
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Henrik Rydberg

On Sat, Aug 18, 2012 at 12:17 AM, Christopher Heiny
<cheiny@synaptics.com> wrote:

(...)
> +#ifdef CONFIG_HAS_EARLYSUSPEND
> +#include <linux/earlysuspend.h>
> +#endif

This does not exist in the mainline kernel, I can see you're having fun with
Android ... check with Rafael on how to really handle this in a contemporary
kernel.

> +/**
> + * struct rmi_f11_axis_alignment - target axis alignment
> + * @swap_axes: set to TRUE if desired to swap x- and y-axis
> + * @flip_x: set to TRUE if desired to flip direction on x-axis
> + * @flip_y: set to TRUE if desired to flip direction on y-axis

This kerneldoc is quite incomplete. There is a *lot* of members below
that are not documented.

> + */
> +struct rmi_f11_2d_axis_alignment {
> +       bool swap_axes;
> +       bool flip_x;
> +       bool flip_y;
> +       int clip_X_low;
> +       int clip_Y_low;
> +       int clip_X_high;
> +       int clip_Y_high;
> +       int offset_X;
> +       int offset_Y;
> +       int rel_report_enabled;
> +
> +#ifdef CONFIG_RMI4_DEBUG
> +       struct dentry *debugfs_flip;
> +       struct dentry *debugfs_clip;
> +       struct dentry *debugfs_offset;
> +       struct dentry *debugfs_swap;
> +       u16 reg_debug_addr;
> +       u8 reg_debug_size;
> +#endif
> +};

(...)
> +struct rmi_f11_virtualbutton_map {
> +       u8 buttons;
> +       struct virtualbutton_map *map;
> +};

<newline> (nitpick)

> +struct rmi_device_platform_data_spi {
> +       int block_delay_us;
> +       int split_read_block_delay_us;
> +       int read_delay_us;
> +       int write_delay_us;
> +       int split_read_byte_delay_us;
> +       int pre_delay_us;
> +       int post_delay_us;
> +
> +       void *cs_assert_data;
> +       int (*cs_assert) (const void *cs_assert_data, const bool assert);
> +};
> +
> +/**
> + * struct rmi_device_platform_data - system specific configuration info.
> + *
> + * @firmware_name - if specified will override default firmware name,
> + * for reflashing.
> + *
> + * @f11_type_b - Force F11 to treat the sensor as a multitouch Type B sensor.
> + * This is useful for older Type B RMI4 sensors that can't be queried for this
> + * information, but will cause problems if you force a Type A sensor to be
> + * treated as type_b.
> + *
> + * @pre_suspend - this will be called before any other suspend operations are
> + * done.
> + * @post_suspend - this will be called after all suspend operations are
> + * completed.  This is the ONLY safe place to power off an RMI sensor
> + * during the suspend process.
> + * @pre_resume - this is called before any other resume operations.  If you
> + * powered off the RMI4 sensor in post_suspend(), then you MUST power it back
> + * here, and you MUST wait an appropriate time for the ASIC to come up
> + * (100ms to 200ms, depending on the sensor) before returning.
> + * @pm_data - this will be passed to the various (pre|post)_(suspend/resume)
> + * functions.

This kerneldoc is incomplete, maybe intentional, I don't know...

(...)
> +struct rmi_function_handler {
> +       int func;
> +       int (*init)(struct rmi_function_container *fc);
> +       int (*config)(struct rmi_function_container *fc);
> +       int (*reset)(struct rmi_function_container *fc);
> +       int (*attention)(struct rmi_function_container *fc, u8 *irq_bits);
> +#ifdef CONFIG_PM
> +       int (*suspend)(struct rmi_function_container *fc);
> +       int (*resume)(struct rmi_function_container *fc);
> +#if defined(CONFIG_HAS_EARLYSUSPEND) && \
> +               !defined(CONFIG_RMI4_SPECIAL_EARLYSUSPEND)

There is this earlysuspend again, now with some turbo-version
which is "special" as well :-)

> +       int (*early_suspend)(struct rmi_function_container *fc);
> +       int (*late_resume)(struct rmi_function_container *fc);
> +#endif
> +#endif
> +       void (*remove)(struct rmi_function_container *fc);
> +};

(...)
> +/**
> + * struct rmi_device - represents an RMI device
> + * @dev: The device created for the RMI bus
> + * @number: Unique number for the device on the bus.
> + * @driver: Pointer to associated driver
> + * @phys: Pointer to the physical interface
> + * @early_suspend_handler: Pointers to early_suspend and late_resume, if
> + * configured.
> + *
> + * This structs represent an RMI device.
> + *
> + */
> +struct rmi_device {
> +       struct device dev;
> +       int number;
> +
> +       struct rmi_driver *driver;
> +       struct rmi_phys_device *phys;
> +
> +#ifdef CONFIG_HAS_EARLYSUSPEND
> +       struct early_suspend early_suspend_handler;
> +#endif

...and here...

> +#ifdef CONFIG_RMI4_DEBUG
> +       struct dentry *debugfs_root;
> +#endif
> +};

(...)
> +/* 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.
> + */
> +static inline void batohs(unsigned short *dest, unsigned char *src)

Change this prototype to use kernel-internal types, and what is
wron with using the return value??

static inline void batohs(unsigned short *dest, unsigned char *src)

> +{
> +       *dest = src[1] * 0x100 + src[0];
> +}

Doing this instead of casting is good style. But I would have done like this:

static inline u16 batohs(u8 *src)
{
   return src[1] << 8 | src[0];
}

Which the compiler *hopufully will emit, but since we're doing
arithmetic, not maths, please use shifts and OR:s.

And at the call sites, instead of

unsigned short mr16;
unsigned char mr_arrary[2];
batohs(&mr16, &mr_array[0]);

Just:

u16 mr16;
u8 mr_array[2];

mr16 = batohs(&mr_array[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.
> + */
> +static inline void hstoba(unsigned char *dest, unsigned short src)

Here yuou need to pass a pointer to the destination for sure.

> +{
> +       dest[0] = src % 0x100;
> +       dest[1] = src / 0x100;
> +}

I would have done the above like this:

static inline void hstoba(u8 *dest, u16 src)
{
  dest[0] = src & 0xFF;
  dest[1] = src >> 8;
}

(...)
> +/* utility function for bit access of u8*'s */

No, do not reinvent the wheel. Use
#include <linux/bitmap.h>
(which in turn will include <linux/bitops.h> for you)
Those bitmaps use unsigned int but that does not
really matter, they should work just fine using the lower
8 bits.

> +void u8_set_bit(u8 *target, int pos);

Use set_bit() from <linux/bitops.h>

> +void u8_clear_bit(u8 *target, int pos);

Use clear_bit() from <linux/bitops.h>

> +bool u8_is_set(u8 *target, int pos);

Use test_bit() from <linux/bitops.h>

> +bool u8_is_any_set(u8 *target, int size);

Use find_first_bit(), if this returns 0 none is set, !find_first_bit() for
example serves the same purpose.

> +void u8_or(u8 *dest, u8* target1, u8* target2, int size);

Use bitmap_or()

> +void u8_and(u8 *dest, u8* target1, u8* target2, int size);

Use bitmap_and()

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
  2012-08-22 12:50 ` [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Linus Walleij
@ 2012-08-22 21:29   ` Christopher Heiny
  2012-08-27 23:20   ` Christopher Heiny
  1 sibling, 0 replies; 44+ messages in thread
From: Christopher Heiny @ 2012-08-22 21:29 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

On 08/22/2012 05:50 AM, Linus Walleij wrote:
> On Sat, Aug 18, 2012 at 12:17 AM, Christopher Heiny
> <cheiny@synaptics.com> wrote:
>
>> This patch implements a driver supporting Synaptics ClearPad and other
>> touchscreen sensors that use the RMI4 protocol, as defined here:
>
> Nice!
>
>> This patch is against the v2.6.38 tag of Linus' kernel tree, commit
>> 521cb40b0c44418a4fd36dc633f575813d59a43d.  This will be our last patch against
>> such an old kernel version, future patches will be against 3.x kernels.
>
> Please use the head of the subsystem tree to do this work.
> In this case, use Dmitry's git.
>
> Currently I just cannot test this because we have no such old codebase
> around.

Yeah, we've been in a bind in that regard, mostly due to customer 
requirements to support Android Gingerbread.  At least we were able to 
drop Froyo support this time around. :-P

Going forward, though, we'll be tracking much newer kernels for our 
development, and hopefully getting patches out more frequently as well.


> But I will attempt to review the patch set!

Thanks!  We look forward to your input.

					Chris

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 1/17] input: RMI4 public header file and documentation.
  2012-08-22 19:08   ` Linus Walleij
@ 2012-08-22 21:45     ` Dmitry Torokhov
  2012-08-23  0:26       ` Christopher Heiny
  2012-08-22 23:35     ` Rafael J. Wysocki
  1 sibling, 1 reply; 44+ messages in thread
From: Dmitry Torokhov @ 2012-08-22 21:45 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Christopher Heiny, Rafael J. Wysocki, Jean Delvare, Linux Kernel,
	Linux Input, Allie Xiong, William Manson, Peichen Chang,
	Joerie de Gram, Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Henrik Rydberg

On Wednesday, August 22, 2012 09:08:00 PM Linus Walleij wrote:
> On Sat, Aug 18, 2012 at 12:17 AM, Christopher Heiny
> <cheiny@synaptics.com> wrote:
> 
> > +/* 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.

Is RMI-endiannes different from BE and LE (joking)? Please simply state what 
on-wire endianness is and use xx_to_cpu() or cpu_to_xx() as needed. I think I 
mentioned it about a year ago already...

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 1/17] input: RMI4 public header file and documentation.
  2012-08-22 19:08   ` Linus Walleij
  2012-08-22 21:45     ` Dmitry Torokhov
@ 2012-08-22 23:35     ` Rafael J. Wysocki
  1 sibling, 0 replies; 44+ messages in thread
From: Rafael J. Wysocki @ 2012-08-22 23:35 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Christopher Heiny, Dmitry Torokhov, Jean Delvare, Linux Kernel,
	Linux Input, Allie Xiong, William Manson, Peichen Chang,
	Joerie de Gram, Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Henrik Rydberg

On Wednesday, August 22, 2012, Linus Walleij wrote:
> On Sat, Aug 18, 2012 at 12:17 AM, Christopher Heiny
> <cheiny@synaptics.com> wrote:
> 
> (...)
> > +#ifdef CONFIG_HAS_EARLYSUSPEND
> > +#include <linux/earlysuspend.h>
> > +#endif
> 
> This does not exist in the mainline kernel, I can see you're having fun with
> Android ... check with Rafael on how to really handle this in a contemporary
> kernel.

You can't.  Early suspend is not going to be merged.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 1/17] input: RMI4 public header file and documentation.
  2012-08-22 21:45     ` Dmitry Torokhov
@ 2012-08-23  0:26       ` Christopher Heiny
  0 siblings, 0 replies; 44+ messages in thread
From: Christopher Heiny @ 2012-08-23  0:26 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linus Walleij, Rafael J. Wysocki, Jean Delvare, Linux Kernel,
	Linux Input, Allie Xiong, William Manson, Peichen Chang,
	Joerie de Gram, Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Henrik Rydberg

On 08/22/2012 02:45 PM, Dmitry Torokhov wrote:
> On Wednesday, August 22, 2012 09:08:00 PM Linus Walleij wrote:
>> >On Sat, Aug 18, 2012 at 12:17 AM, Christopher Heiny
>> ><cheiny@synaptics.com>  wrote:
>> >
>>> > >+/* 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.
> Is RMI-endiannes different from BE and LE (joking)? Please simply state what
> on-wire endianness is and use xx_to_cpu() or cpu_to_xx() as needed. I think I
> mentioned it about a year ago already...

Gronk. I thought I'd killed that.  But obviously didn't do a thorough job.

[Slinks off in embarrassment.]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 2/17] input: RMI4 core bus and sensor drivers.
  2012-08-17 22:17 ` [RFC PATCH 2/17] input: RMI4 core bus and sensor drivers Christopher Heiny
@ 2012-08-23  8:55   ` Linus Walleij
  2012-09-25 23:53     ` Christopher Heiny
  0 siblings, 1 reply; 44+ messages in thread
From: Linus Walleij @ 2012-08-23  8:55 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Greg KH, Rafael J. Wysocki, Magnus Damm

The subject of the patch sounds like something Greg should have a look
at at some point.

On Sat, Aug 18, 2012 at 12:17 AM, Christopher Heiny
<cheiny@synaptics.com> wrote:

> Driver for Synaptics touchscreens using RMI4 protocol.

This doesn't match the subject. Does it really driver touchscreens
too? It certainly contains infrastructure. This blurb should be pretty verbose
for the core driver.

(...)
> +DEFINE_MUTEX(rmi_bus_mutex);
> +
> +static struct rmi_function_list {
> +       struct list_head list;
> +       struct rmi_function_handler *fh;
> +} rmi_supported_functions;
> +
> +static struct rmi_character_driver_list {
> +       struct list_head list;
> +       struct rmi_char_driver *cd;
> +} rmi_character_drivers;

We usually do not staticize structs and split definition and
usage please:

struct foo {
..
};

struct foo bar; /* Instance */

> +static atomic_t physical_device_count/* = ATOMIC_INIT(0) ?*/;

Yes please. (Assign it with that.)

(...)
> +static int rmi_bus_match(struct device *dev, struct device_driver *driver)

Why are you re-implementing bus matching? Is there something wrong
with using the core

> +#ifdef CONFIG_PM
> +static int rmi_bus_suspend(struct device *dev)
> +{
> +       struct device_driver *driver = dev->driver;
> +#ifdef GENERIC_SUBSYS_PM_OPS
> +       const struct dev_pm_ops *pm;
> +#endif

Why should generic subsys PM ops be optional if you're using
CONFIG_PM? Just cut these #ifdefs.

(...)
> +static int rmi_bus_probe(struct device *dev)
> +{
> +       struct rmi_driver *driver;
> +       struct rmi_device *rmi_dev = to_rmi_device(dev);
> +
> +       driver = rmi_dev->driver;
> +       if (driver && driver->probe)
> +               return driver->probe(rmi_dev);
> +
> +       return 0;

Should this really return 0? Isn't this a failure?

(The rest of this file looks like it'll work to me, but I'm not
smart with such things.)

> +++ b/drivers/input/rmi4/rmi_driver.c
(...)
> +#ifdef CONFIG_RMI4_DEBUG
> +#include <linux/debugfs.h>
> +#include <linux/fs.h>
> +#include <linux/uaccess.h>

uaccess seems to be used by non-debug code in this file as well, but
I may be wrong.

> +#endif
> +
> +#define REGISTER_DEBUG 0

Why not make a Kconfig for this debug option if it's really
useful?

> +#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

More out-of-tree Android stuff?

(...)
> +#define PHYS_NAME "phys"

"rmi4-phys"? Phys is a bit un-specific.

(...)
> +static int setup_debugfs(struct rmi_device *rmi_dev)
> +{
> +       struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
> +#ifdef CONFIG_RMI4_SPI
> +       struct rmi_phys_info *info = &rmi_dev->phys->info;
> +#endif

If something named "phys" is turned on by a plugin for "SPI"
then something is conceptually wrong. Either you consequently
name everything *PHYS or *SPI, and if you want to abstract
away the particulars behind some physical layer you cannot name
the switch *SPI.

> +/* Useful helper functions for u8* */
> +
> +void u8_set_bit(u8 *target, int pos)
> +{
> +       target[pos/8] |= 1<<pos%8;
> +}

Now these helper functions you already reimplemented in
a header file, and here is yet ANOTHER implementatin, with
different code! And I said please use <linux/bitops.h> and
<linux/bitmap.h>, so I'm saying the same again.

> +void u8_clear_bit(u8 *target, int pos)

Dito.

> +bool u8_is_set(u8 *target, int pos)

Dito.

> +bool u8_is_any_set(u8 *target, int size)

Dito.

> +void u8_or(u8 *dest, u8 *target1, u8 *target2, int size)

Dito.

> +void u8_and(u8 *dest, u8 *target1, u8 *target2, int size)

Dito.

(...)
> +/* Utility routine to set bits in a register. */
> +int rmi_set_bits(struct rmi_device *rmi_dev, u16 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;

It's more nice if that function could also return negative error codes
so if (retval <= 0) ...error.

> +/* Utility routine to clear bits in a register. */
> +int rmi_clear_bits(struct rmi_device *rmi_dev, u16 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;

Dito.

> +       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);
> +
> +       if (!data) {
> +               dev_err(&rmi_dev->dev, "WTF: No driver data in %s\n", __func__);

Yeah I just love acronyms like that in the code, keep it ;-)

(...)
> +static void construct_mask(u8 *mask, int num, int pos)
> +{
> +       int i;
> +
> +       for (i = 0; i < num; i++)
> +               u8_set_bit(mask, pos+i);
> +}

Just use bitmap_set(dst, pos, nbits) from <linux/bitmap.h>

(...)
> +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_dbg(&rmi_dev->dev,
> +                        "Not ready to handle interrupts yet!\n");
> +               return 0;
> +       }
> +
> +       return process_interrupt_requests(rmi_dev);
> +}

I guess this function mandates that the interrupts handled must
be threaded? Suggest adding might_sleep(); from <linux/kernel.h>
into this function so the runtime checks get correct.

(...)
> +       return 0;
> +}
> +
> +
> +
> +/*
> + * Construct a function's IRQ mask. This should
> + * be called once and stored.
> + */

Lots of blank lines there, you can never get enough whitespace eh? ;-)

> +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);
> +
> +       u8 *irq_mask = kcalloc(data->num_of_irq_regs, sizeof(u8), GFP_KERNEL);
> +       if (irq_mask)
> +               construct_mask(irq_mask, fc->num_of_irqs, fc->irq_pos);
> +
> +       return irq_mask;
> +}

I just get the feeling that you're trying to reimplement parts of
struct irq_chip and the associated machinery in kernel/irq/*

Please check how e.g. several GPIO drivers utilized the IRQ chips
for virtual IRQ domains, i.e. interrupts from say a GPIO expander
chip cascaded off a "hard" IRQ line on another GPIO chip.

To Linux these are usually just another range of IRQs, and the
space of virtual IRQs is way larger than the number of interrupt
lines the SoC interrupt controller can handle, it just spreads out,
hierarchically.

c.f. the small and nice new Emma driver from Magnus Damm:
drivers/gpio/gpio-em.c

> +/*
> + * 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)
(...)
> +static int rmi_driver_irq_restore(struct rmi_device *rmi_dev)
(...)

These look just like mask/unmask operations of a struct irq_chip.
Using threaded interrupts and ONESHOT flagged IRQs this
should work just fine with present infrastructure I think.

(...)
> +static int rmi_driver_probe(struct rmi_device *rmi_dev)

Should this be tagged __devinit?

> +{
> +       struct rmi_driver_data *data = NULL;
> +       struct rmi_function_container *fc;
> +       struct rmi_device_platform_data *pdata;
> +       int retval = 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);

Since rmi_dev contains a struct device, could you use managed
resources?

data = devm_kzalloc(dev, ...);

Check
Documentation/driver-model/devres.txt
in recent kernels.

This lets you cut down the code by getting rid of a lot of
errorpath free():ing and stuff.

> +       if (!data) {
> +               dev_err(dev, "%s: Failed to allocate driver data.\n", __func__);
> +               return -ENOMEM;
> +       }
> +       INIT_LIST_HEAD(&data->rmi_functions.list);
> +       rmi_set_driverdata(rmi_dev, data);
> +       mutex_init(&data->pdt_mutex);
> +
> +       if (!pdata->reset_delay_ms)
> +               pdata->reset_delay_ms = DEFAULT_RESET_DELAY_MS;
> +       retval = do_initial_reset(rmi_dev);
> +       if (retval)
> +               dev_warn(dev, "RMI initial reset failed! Soldiering on.\n");

Not that I have a clue what "soldiering" is...

> +       retval = rmi_scan_pdt(rmi_dev);
> +       if (retval) {
> +               dev_err(dev, "PDT scan for %s failed with code %d.\n",
> +                       pdata->sensor_name, retval);
> +               goto err_free_data;
> +       }
> +
> +       if (!data->f01_container) {
> +               dev_err(dev, "missing F01 container!\n");
> +               retval = -EINVAL;
> +               goto err_free_data;
> +       }
> +
> +       data->f01_container->irq_mask = kcalloc(data->num_of_irq_regs,
> +                       sizeof(u8), GFP_KERNEL);

Actually pretty cool that you use kcalloc, I always forget to use
that. devm_kcalloc() with managed resources tho...

(...)

Then follows a large block of PM stuff that I think you should
let Rafael look at.

> +static struct rmi_driver sensor_driver = {
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "rmi_generic",
> +#ifdef UNIVERSAL_DEV_PM_OPS

I really think you should try to kill this #ifdef.

> +               .pm = &rmi_driver_pm,
> +#endif
> +       },
> +       .probe = rmi_driver_probe,
> +       .irq_handler = rmi_driver_irq_handler,
> +       .reset_handler = rmi_driver_reset_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)
> +};

This is quite comprehensive, the basic API is looking good.

(...)
> +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)

Disable drivers from userspace? Is this really a good idea?

(...)
> +static ssize_t rmi_driver_reg_store(struct device *dev,
> +                                       struct device_attribute *attr,
> +                                       const char *buf, size_t count)
> +{
> +       int retval;
> +       u32 address;    /* use large addr here so we can catch overflow */
> +       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

1. Use debugfs for this, not sysfs.

2. Please consider using seq_file for this, it doesn't require you to
handle your own buffers, and I think the kernel already contains
nice hexdump helpers you can use for this.

For seqfile examples see end of drivers/pinctrl/core.c.

(...)
> +++ b/drivers/input/rmi4/rmi_driver.h
(...)
> +/* Sysfs related macros */
> +
> +/* You must define FUNCTION_DATA and FNUM to use these functions. */
> +#define RMI4_SYSFS_DEBUG (defined(CONFIG_RMI4_DEBUG) || defined(CONFIG_ANDROID))

CONFIG_ANDROID doesn't look like something we have in  the kernel currently,
there are some drivers in drivers/staging but they are not to be dependent upon.

> +#if defined(FNUM) && defined(FUNCTION_DATA)
> +
> +#define tricat(x, y, z) tricat_(x, y, z)
> +
> +#define tricat_(x, y, z) x##y##z
> +
> +#define show_union_struct_prototype(propname)\
> +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(\
> +                                       struct device *dev, \
> +                                       struct device_attribute *attr, \
> +                                       char *buf);\
> +\
> +static DEVICE_ATTR(propname, RMI_RO_ATTR,\
> +               tricat(rmi_fn_, FNUM, _##propname##_show), \
> +               rmi_store_error);
> +
> +#define store_union_struct_prototype(propname)\
> +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(\
> +                                       struct device *dev,\
> +                                       struct device_attribute *attr,\
> +                                       const char *buf, size_t count);\
> +\
> +static DEVICE_ATTR(propname, RMI_WO_ATTR,\
> +               rmi_show_error,\
> +               tricat(rmi_fn_, FNUM, _##propname##_store));
> +
> +#define show_store_union_struct_prototype(propname)\
> +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(\
> +                                       struct device *dev,\
> +                                       struct device_attribute *attr,\
> +                                       char *buf);\
> +\
> +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(\
> +                                       struct device *dev,\
> +                                       struct device_attribute *attr,\
> +                                       const char *buf, size_t count);\
> +\
> +static DEVICE_ATTR(propname, RMI_RW_ATTR,\
> +               tricat(rmi_fn_, FNUM, _##propname##_show),\
> +               tricat(rmi_fn_, FNUM, _##propname##_store));
> +
> +#define simple_show_union_struct(regtype, propname, fmt)\
> +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
> +                               struct device_attribute *attr, char *buf) {\
> +       struct rmi_function_container *fc;\
> +       struct FUNCTION_DATA *data;\
> +\
> +       fc = to_rmi_function_container(dev);\
> +       data = fc->data;\
> +\
> +       return snprintf(buf, PAGE_SIZE, fmt,\
> +                       data->regtype.propname);\
> +}
> +
> +#define simple_show_union_struct2(regtype, reg_group, propname, fmt)\
> +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
> +                               struct device_attribute *attr, char *buf) {\
> +       struct rmi_function_container *fc;\
> +       struct FUNCTION_DATA *data;\
> +\
> +       fc = to_rmi_function_container(dev);\
> +       data = fc->data;\
> +\
> +       return snprintf(buf, PAGE_SIZE, fmt,\
> +                       data->regtype.reg_group->propname);\
> +}
> +
> +#define show_union_struct(regtype, reg_group, propname, fmt)\
> +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(\
> +                                       struct device *dev,\
> +                                       struct device_attribute *attr,\
> +                                       char *buf) {\
> +       struct rmi_function_container *fc;\
> +       struct FUNCTION_DATA *data;\
> +       int result;\
> +\
> +       fc = to_rmi_function_container(dev);\
> +       data = fc->data;\
> +\
> +       mutex_lock(&data->regtype##_mutex);\
> +       /* Read current regtype values */\
> +       result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
> +                               (u8 *)data->regtype.reg_group,\
> +                               sizeof(data->regtype.reg_group->regs));\
> +       mutex_unlock(&data->regtype##_mutex);\
> +       if (result < 0) {\
> +               dev_dbg(dev, "%s : Could not read regtype at 0x%x\\n",\
> +                                       __func__,\
> +                                       data->regtype.reg_group->address);\
> +               return result;\
> +       } \
> +       return snprintf(buf, PAGE_SIZE, fmt,\
> +                       data->regtype.reg_group->propname);\
> +}
> +
> +#define show_store_union_struct(regtype, reg_group, propname, fmt)\
> +show_union_struct(regtype, reg_group, propname, fmt)\
> +\
> +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(\
> +                                       struct device *dev,\
> +                                       struct device_attribute *attr,\
> +                                       const char *buf, size_t count) {\
> +       int result;\
> +       unsigned long val;\
> +       unsigned long old_val;\
> +       struct rmi_function_container *fc;\
> +       struct FUNCTION_DATA *data;\
> +\
> +       fc = to_rmi_function_container(dev);\
> +       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;\
> +       /* Check value maybe */\
> +\
> +       /* Read current regtype values */\
> +       mutex_lock(&data->regtype##_mutex);\
> +       result =\
> +           rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
> +                               (u8 *)data->regtype.reg_group,\
> +                               sizeof(data->regtype.reg_group->regs));\
> +\
> +       if (result < 0) {\
> +               mutex_unlock(&data->regtype##_mutex);\
> +               dev_dbg(dev, "%s : Could not read regtype at 0x%x\\n",\
> +                                        __func__,\
> +                                       data->regtype.reg_group->address);\
> +               return result;\
> +       } \
> +       /* if the current regtype registers are already set as we want them,\
> +        * do nothing to them */\
> +       if (data->regtype.reg_group->propname == val) {\
> +               mutex_unlock(&data->regtype##_mutex);\
> +               return count;\
> +       } \
> +       /* Write the regtype back to the regtype register */\
> +       old_val = data->regtype.reg_group->propname;\
> +       data->regtype.reg_group->propname = val;\
> +       result =\
> +           rmi_write_block(fc->rmi_dev, data->regtype.reg_group->address,\
> +                               (u8 *)data->regtype.reg_group,\
> +                               sizeof(data->regtype.reg_group->regs));\
> +       if (result < 0) {\
> +               dev_dbg(dev, "%s : Could not write regtype to 0x%x\\n",\
> +                                       __func__,\
> +                                       data->regtype.reg_group->address);\
> +               /* revert change to local value if value not written */\
> +               data->regtype.reg_group->propname = old_val;\
> +               mutex_unlock(&data->regtype##_mutex);\
> +               return result;\
> +       } \
> +       mutex_unlock(&data->regtype##_mutex);\
> +       return count;\
> +}
> +
> +
> +#define show_repeated_union_struct(regtype, reg_group, propname, fmt)\
> +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
> +                                       struct device_attribute *attr,\
> +                                       char *buf) {\
> +       struct rmi_function_container *fc;\
> +       struct FUNCTION_DATA *data;\
> +       int reg_length;\
> +       int result, size = 0;\
> +       char *temp;\
> +       int i;\
> +\
> +       fc = to_rmi_function_container(dev);\
> +       data = fc->data;\
> +       mutex_lock(&data->regtype##_mutex);\
> +\
> +       /* Read current regtype values */\
> +       reg_length = data->regtype.reg_group->length;\
> +       result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
> +                       (u8 *) data->regtype.reg_group->regs,\
> +                       reg_length * sizeof(u8));\
> +       mutex_unlock(&data->regtype##_mutex);\
> +       if (result < 0) {\
> +               dev_dbg(dev, "%s : Could not read regtype at 0x%x\n"\
> +                                       "Data may be outdated.", __func__,\
> +                                       data->regtype.reg_group->address);\
> +       } \
> +       temp = buf;\
> +       for (i = 0; i < reg_length; i++) {\
> +               result = snprintf(temp, PAGE_SIZE - size, fmt " ",\
> +                               data->regtype.reg_group->regs[i].propname);\
> +               if (result < 0) {\
> +                       dev_err(dev, "%s : Could not write output.", __func__);\
> +                       return result;\
> +               } \
> +               size += result;\
> +               temp += result;\
> +       } \
> +       result = snprintf(temp, PAGE_SIZE - size, "\n");\
> +       if (result < 0) {\
> +                       dev_err(dev, "%s : Could not write output.", __func__);\
> +                       return result;\
> +       } \
> +       return size + result;\
> +}
> +
> +#define show_store_repeated_union_struct(regtype, reg_group, propname, fmt)\
> +show_repeated_union_struct(regtype, reg_group, propname, fmt)\
> +\
> +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(struct device *dev,\
> +                                  struct device_attribute *attr,\
> +                                  const char *buf, size_t count) {\
> +       struct rmi_function_container *fc;\
> +       struct FUNCTION_DATA *data;\
> +       int reg_length;\
> +       int result;\
> +       const char *temp;\
> +       int i;\
> +       unsigned int newval;\
> +\
> +       fc = to_rmi_function_container(dev);\
> +       data = fc->data;\
> +       mutex_lock(&data->regtype##_mutex);\
> +\
> +       /* Read current regtype values */\
> +\
> +       reg_length = data->regtype.reg_group->length;\
> +       result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
> +                       (u8 *) data->regtype.reg_group->regs,\
> +                       reg_length * sizeof(u8));\
> +\
> +       if (result < 0) {\
> +               dev_dbg(dev, "%s: Could not read regtype at %#06x. "\
> +                                       "Data may be outdated.", __func__,\
> +                                       data->regtype.reg_group->address);\
> +       } \
> +       \
> +       /* parse input */\
> +       temp = buf;\
> +       for (i = 0; i < reg_length; i++) {\
> +               if (sscanf(temp, fmt, &newval) == 1) {\
> +                       data->regtype.reg_group->regs[i].propname = newval;\
> +               } else {\
> +                       /* If we don't read a value for each position, abort, \
> +                        * restore previous values locally by rereading */\
> +                       result = rmi_read_block(fc->rmi_dev, \
> +                                       data->regtype.reg_group->address,\
> +                                       (u8 *) data->regtype.reg_group->regs,\
> +                                       reg_length * sizeof(u8));\
> +\
> +                       if (result < 0) {\
> +                               dev_dbg(dev, "%s: Couldn't read regtype at "\
> +                                       "%#06x. Local data may be inaccurate", \
> +                                       __func__,\
> +                                       data->regtype.reg_group->address);\
> +                       } \
> +                       return -EINVAL;\
> +               } \
> +               /* move to next number */\
> +               while (*temp != 0) {\
> +                       temp++;\
> +                       if (isspace(*(temp - 1)) && !isspace(*temp))\
> +                               break;\
> +               } \
> +       } \
> +       result = rmi_write_block(fc->rmi_dev, data->regtype.reg_group->address,\
> +                       (u8 *) data->regtype.reg_group->regs,\
> +                       reg_length * sizeof(u8));\
> +       mutex_unlock(&data->regtype##_mutex);\
> +       if (result < 0) {\
> +               dev_dbg(dev, "%s: Could not write new values to %#06x\n", \
> +                               __func__, data->regtype.reg_group->address);\
> +               return result;\
> +       } \
> +       return count;\
> +}

This looks bizarre, convert them to plain static inline functions if you want
to have them in the header file.

However I suspect you may be duplicating part of existing sysfs
macros, and in particular it looks like you're actually duplicating
the debug facility in Mark Brows recently integrated regmap.

Please consult regmap to see if you can use this!

> +/* Create templates for given types */
> +#define simple_show_union_struct_unsigned(regtype, propname)\
> +simple_show_union_struct(regtype, propname, "%u\n")
> +
> +#define simple_show_union_struct_unsigned2(regtype, reg_group, propname)\
> +simple_show_union_struct2(regtype, reg_group, propname, "%u\n")
> +
> +#define show_union_struct_unsigned(regtype, reg_group, propname)\
> +show_union_struct(regtype, reg_group, propname, "%u\n")
> +
> +#define show_store_union_struct_unsigned(regtype, reg_group, propname)\
> +show_store_union_struct(regtype, reg_group, propname, "%u\n")
> +
> +#define show_repeated_union_struct_unsigned(regtype, reg_group, propname)\
> +show_repeated_union_struct(regtype, reg_group, propname, "%u")
> +
> +#define show_store_repeated_union_struct_unsigned(regtype, reg_group, propname)\
> +show_store_repeated_union_struct(regtype, reg_group, propname, "%u")
> +
> +/* Remove access to raw format string versions */
> +/*#undef simple_show_union_struct
> +#undef show_union_struct_unsigned
> +#undef show_store_union_struct
> +#undef show_repeated_union_struct
> +#undef show_store_repeated_union_struct*/

This looks like trying to reimplement ioctl() in sysfs.

If what you want is to send big structs in/out of the kernel,
use either ioctl() on device nodes (should be trivial since input
is using real device nodes) or use configfs.

The rest looks pretty nice to me ... but you most certainly need
review of the device model and bus code.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 3/17] input: RMI4 physical layer drivers for I2C and SPI
  2012-08-17 22:17 ` [RFC PATCH 3/17] input: RMI4 physical layer drivers for I2C and SPI Christopher Heiny
@ 2012-08-23 13:21   ` Linus Walleij
  0 siblings, 0 replies; 44+ messages in thread
From: Linus Walleij @ 2012-08-23 13:21 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Mark Brown

On Sat, Aug 18, 2012 at 12:17 AM, Christopher Heiny
<cheiny@synaptics.com> wrote:

>  drivers/input/rmi4/rmi_i2c.c |  452 +++++++++++++++++++++
>  drivers/input/rmi4/rmi_spi.c |  911 ++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 1363 insertions(+), 0 deletions(-)

NB: Mark Brown is looking after SPI FTM so include him!

(...)
> diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c
(...)
> +#define COMMS_DEBUG 0
> +
> +#define IRQ_DEBUG 0
> +
> +#if COMMS_DEBUG || IRQ_DEBUG
> +#define DEBUG
> +#endif

Don't do things like this. Debugging shall always be switched on from the
Kconfig menus.

Look at this from drivers/pinctrl/:

Kconfig:
config DEBUG_PINCTRL
        bool "Debug PINCTRL calls"
        depends on DEBUG_KERNEL
        help
          Say Y here to add some extra checks and diagnostics to PINCTRL calls.

Makefile:
ccflags-$(CONFIG_DEBUG_PINCTRL) += -DDEBUG

Other more specific defines: same thing. Use Kconfig.
The kernel also has a native concept of verbos debugging
see e.g. drivers/dma*:

config DMADEVICES_VDEBUG
        bool "DMA Engine verbose debugging"
        depends on DMADEVICES_DEBUG != n
        help
          This is an option for use by developers; most people should
          say N here.  This enables deeper (more verbose) debugging of
          the DMA engine core and drivers.

ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG

(...)
> +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(pdata->attn_gpio));
> +#endif

If you absolutely cannot just use dev_dbg() or dev_vdbg(), then
define your own irq debugging macro on top:

#ifdef IRQ_DEBUG
#define dev_irqdbg        dev_irqdbg
#else
#define dev_irqdbg(dev, format, arg...)                           \
({                                                              \
        if (0)                                                  \
                dev_printk(KERN_DEBUG, dev, format, ##arg);     \
        0;                                                      \
})
#endif

(...)
> +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

Same comment, mutatis mutandis.

(...)
> +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);
> +}

Do you really need a separate function to just call another function?
Inline this instead.

(...)
> +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;
> +       }
> +       dev_info(&client->dev, "Probing %s at %#02x (IRQ %d).\n",
> +               pdata->sensor_name ? pdata->sensor_name : "-no name-",
> +               client->addr, pdata->attn_gpio);
> +
> +       if (pdata->gpio_config) {
> +               dev_info(&client->dev, "Configuring GPIOs.\n");
> +               error = pdata->gpio_config(pdata->gpio_data, true);
> +               if (error < 0) {
> +                       dev_err(&client->dev, "Failed to configure GPIOs, code: %d.\n",
> +                               error);
> +                       return error;
> +               }
> +               dev_info(&client->dev, "Done with GPIO configuration.\n");
> +       }
> +
> +       error = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
> +       if (!error) {
> +               dev_err(&client->dev, "i2c_check_functionality error %d.\n",
> +                       error);
> +               return error;
> +       }
> +
> +       rmi_phys = kzalloc(sizeof(struct rmi_phys_device), GFP_KERNEL);

Could you use devm_kzalloc(&client->dev, ... ) ?

> +
> +       data = kzalloc(sizeof(struct rmi_i2c_data), GFP_KERNEL);

Dito.

> +       data->enabled = true;   /* We plan to come up enabled. */
> +       data->irq = gpio_to_irq(pdata->attn_gpio);
> +       if (pdata->level_triggered) {
> +               data->irq_flags = IRQF_ONESHOT |
> +                       ((pdata->attn_polarity == RMI_ATTN_ACTIVE_HIGH) ?
> +                       IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW);
> +       } else {
> +               data->irq_flags =

I'm uncertain to whether this is not IRQF_ONESHOT, I realized why
you do this for edge triggers but will the threads handle this properly?
The irq thread will have to be fully reentrant.

> +                       (pdata->attn_polarity == RMI_ATTN_ACTIVE_HIGH) ?
> +                       IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
> +       }
> +       data->phys = rmi_phys;
(...)
> +       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_gpio;
> +       }
> +       i2c_set_clientdata(client, rmi_phys);

Especially since it's associated with this one client, devm_kzalloc() and
friends are apropriate.

> +#if defined(CONFIG_RMI4_DEV)

Nowadays much code is moving away from using any #ifdef:s
and instead relying on constructs using IS_ENABLED from
<linux/kconfig.h> like this:

if (IS_ENABLED(CONFIG_FOO)) {}

If that resolves to if (0) the compiler will strip out the
code.

(...)
> +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;
> +
> +       disable_device(phys);
> +       rmi_unregister_phys_device(phys);
> +       kfree(phys->data);
> +       kfree(phys);

So these kfree():s would not be needed if you used devm_*

(...)
> +static const struct i2c_device_id rmi_id[] = {
> +       { "rmi", 0 },
> +       { "rmi_i2c", 0 },
> +       { }
> +};

Why is just "rmi" an acceptable device ID? Since the
SPI driver also allows the same it's a bit confusing.

(...)
> +++ b/drivers/input/rmi4/rmi_spi.c
> +#define COMMS_DEBUG 0
> +#define FF_DEBUG 0

Same comment as about the other custom debug stuff.

(...)
> +static char *spi_v1_proto_name = "spi";
> +static char *spi_v2_proto_name = "spiv2";

const!

And can this be a bit more specific, like "rmi-spiv1", "rmi-spiv2"?

(...)
> +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(pdata->attn_gpio) ==
> +                     pdata->attn_polarity) {
> +               phys->info.attn_count++;
> +               complete(&data->irq_comp);

Nice use of completion here!

(...)
> +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 FF_DEBUG
> +       bool bad_data = true;
> +#endif
> +#if COMMS_DEBUG || FF_DEBUG
> +       int i;
> +#endif

This is overzealous I think, this #ifdeffery is looking complex.

(...)
> +#if COMMS_DEBUG
> +       if (n_tx) {
> +               dev_dbg(&client->dev, "SPI sends %d bytes: ", n_tx);
> +               for (i = 0; i < n_tx; i++)
> +                       pr_info("%02X ", txbuf[i]);
> +               pr_info("\n");
> +       }

Atleast move the definiton of i into this #ifdef, or break it out as a
separate function which in turn is ifdeffed.

(...)
> +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);
> +
> +       return (error == 1) ? 0 : error;
> +}

Isn't this overabstracted? Cut this function and call rmi_spi_v1_write_block()
directly instead.

(...)
> +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;
> +}

Dito.

(...)
> +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);
> +
> +       return (error == 1) ? 0 : error;
> +}

Dito.

(...)
> +static int rmi_spi_check_device(struct rmi_phys_device *rmi_phys)
> +{
> +       u8 buf[6];
> +       int error;
> +       int i;
> +
> +       /* 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.
> +        */

This is not looking good. It's like there are bugs in some SPI drivers
and then you put on some duct-tape to fix it?

You must rely on the SPI framework to do its work.
So instead fix the bug in the SPI driver, not at this level in the stack.

I think I know where the bug is, actually:

For each SPI struct spi_transfer transfer, there is a field named
.delay_usecs that can set the delay for a certain transfer. This
must be handled in the driver.

So set this delay in the struct spi_transfer, then debug the
driver, because maybe not all of them are respecting that
delay (but they should!) consult the code in the function
giveback() in drivers/spi/spi-pl022.c for example.

If it's a bug in the RMI4 device-side stack that's another
issue, then the fix needs to be here, of course, but I
didn't get that impression?

(...)
> +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 retval;
> +
> +       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;
> +       retval = spi_setup(spi);
> +       if (retval < 0) {
> +               dev_err(&spi->dev, "spi_setup failed!\n");
> +               return retval;
> +       }
> +
> +       if (pdata->gpio_config) {
> +               retval = pdata->gpio_config(pdata->gpio_data, true);
> +               if (retval < 0) {
> +                       dev_err(&spi->dev, "Failed to setup GPIOs, code: %d.\n",
> +                               retval);
> +                       return retval;
> +               }
> +       }
> +
> +       rmi_phys = kzalloc(sizeof(struct rmi_phys_device), GFP_KERNEL);

Again this can use devm_kzalloc(&spi->dev, ...)

> +       if (!rmi_phys)
> +               return -ENOMEM;
> +
> +       data = kzalloc(sizeof(struct rmi_spi_data), GFP_KERNEL);

Here too...

(...)
> +       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;

I really like how you auto-detect and switch vtable for the
different protocol versions here.

(...)
> +err_data:
> +       kfree(data);
> +err_phys:
> +       kfree(rmi_phys);

Managed resources simplifies the error path here a lot.

> +       return retval;
> +}

(...)
> +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;
> +
> +       disable_device(phys);
> +       rmi_unregister_phys_device(phys);
> +       kfree(phys->data);
> +       kfree(phys);

And here.

> +       if (pd->gpio_config)
> +               pd->gpio_config(pdata->gpio_data, false);
> +
> +       return 0;
> +}
> +
> +static const struct spi_device_id rmi_id[] = {
> +       { "rmi", 0 },

Why? Why not just "rmi-spi"

Apart from this it looks really nice, and has progressed enormously!

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 4/17] input: RMI4 configs and makefiles
  2012-08-17 22:17 ` [RFC PATCH 4/17] input: RMI4 configs and makefiles Christopher Heiny
@ 2012-08-27 18:39   ` Linus Walleij
  0 siblings, 0 replies; 44+ messages in thread
From: Linus Walleij @ 2012-08-27 18:39 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

On Fri, Aug 17, 2012 at 3:17 PM, Christopher Heiny <cheiny@synaptics.com> wrote:

> diff --git a/drivers/input/Makefile b/drivers/input/Makefile
> index 09614ce..cfb383e 100644
> --- a/drivers/input/Makefile
> +++ b/drivers/input/Makefile
> @@ -23,6 +23,6 @@ obj-$(CONFIG_INPUT_TABLET)    += tablet/
>  obj-$(CONFIG_INPUT_TOUCHSCREEN)        += touchscreen/
>  obj-$(CONFIG_INPUT_MISC)       += misc/
>
> -obj-$(CONFIG_INPUT_APMPOWER)   += apm-power.o
> +obj-$(CONFIG_INPUT_RMI4) += rmi4/
>
> -obj-$(CONFIG_XEN_KBDDEV_FRONTEND)      += xen-kbdfront.o
> +obj-$(CONFIG_INPUT_APMPOWER)   += apm-power.o

Don't touch the other stuff, you just deleted the Xen Kbddev here!

> diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
(...)
> +config RMI4_BUS
> +       bool "Synaptics RMI4 bus support"
> +       help
> +         Say Y here if you want to support the Synaptics RMI4 bus.  This is
> +         required for all RMI4 device support.
> +
> +         If unsure, say Y.

Are RMI4 devices so common that this should be the recommendation?

Also put the debug options into this file instead of random local
#defines in the other files.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 5/17] input: rmidev character driver for RMI4 sensors
  2012-08-17 22:17 ` [RFC PATCH 5/17] input: rmidev character driver for RMI4 sensors Christopher Heiny
@ 2012-08-27 18:49   ` Linus Walleij
  2012-09-05  0:26     ` Christopher Heiny
  0 siblings, 1 reply; 44+ messages in thread
From: Linus Walleij @ 2012-08-27 18:49 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

On Fri, Aug 17, 2012 at 3:17 PM, Christopher Heiny <cheiny@synaptics.com> wrote:

> Driver for Synaptics touchscreens using RMI4 protocol.

Really? This looks more like some custom char driver to get a pipe
into the device from userspace. Put in a proper description of what this
is for.

If the purpose is to read/write arbitrary addresses in the device,
you should use regmap's debugfs interface for this instead,
it is much better suited for the task.

(...)
> +#define RMI_CHAR_DEV_TMPBUF_SZ 128
> +#define RMI_REG_ADDR_PAGE_SELECT 0xFF
> +#define REG_ADDR_LIMIT 0xFFFF
> +
> +struct rmidev_data {
> +       /* mutex for file operation*/
> +       struct mutex file_mutex;
> +       /* main char dev structure */
> +       struct cdev main_dev;
> +
> +       /* pointer to the corresponding RMI4 device.  We use this to do */
> +       /* read, write, etc. */
> +       struct rmi_device *rmi_dev;
> +       /* reference count */
> +       int ref_count;

Something tells me you should atleast use <linux/kref.h> for this.
It also solves a few atomicity problems in a good standard way.
See Documentation/kref.txt

> +/*store dynamically allocated major number of char device*/
> +static int rmidev_major_num;

You need to patch your desired major number into
Documentation/devices.txt

> +static struct class *rmidev_device_class;

Last time discussed with Greg, class devices were deprecated,
and you should just use a bus instead. (But not sure.)

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 6/17] input: RMI4 firmware update
  2012-08-17 22:17 ` [RFC PATCH 6/17] input: RMI4 firmware update Christopher Heiny
@ 2012-08-27 21:01   ` Linus Walleij
  0 siblings, 0 replies; 44+ messages in thread
From: Linus Walleij @ 2012-08-27 21:01 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

On Fri, Aug 17, 2012 at 3:17 PM, Christopher Heiny <cheiny@synaptics.com> wrote:

No commit message. Describe exactly what this feature is for and how
it works and how it differs from standard kernel firmware loading.
(Where the kernel loads firmware into devices at boot time.)
i.e. mention that all devices have their own flash memory etc.

> +#define DEBUG

No, use previously describe Kconfig approach.

> +#define RIM_HACK 1

Que ce que c'est?

Should this also be a Kconfig, or mayble always enabled simply,
and detected at runtime if need be?

(...)
> +#define HAS_BSR_MASK 0x20

"Has*" sounds like something boolean, I guess this bit tells if you
have BSR, so say:

#include <linux/bitops.h>

#define HAS_BSR BIT(5)

> +#define PRODUCT_ID_OFFSET 0x10
> +#define PRODUCT_ID_SIZE 10
> +#define PRODUCT_INFO_OFFSET 0x1E
> +#define PRODUCT_INFO_SIZE 2

It's really nice that you have this info in the product.

> +/** Image file V5, Option 0

Pls actually add some kerneldoc here :-)
This is malformed as it looks now, anything following /** will be parsed.

> + */
> +struct image_header {
> +       u32 checksum;
> +       unsigned int image_size;

Should this be size_t?

> +       unsigned int config_size;

size_t?

> +       unsigned char options;

u8? Some kind of enum? (If it's a bitfield use u8.)

> +       unsigned char bootloader_version;

u8?

> +       u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
> +       unsigned char product_info[PRODUCT_INFO_SIZE];

u8[]?

> +};
> +
> +static u32 extract_u32(const u8 *ptr)
> +{
> +       return (u32)ptr[0] +
> +               (u32)ptr[1] * 0x100 +
> +               (u32)ptr[2] * 0x10000 +
> +               (u32)ptr[3] * 0x1000000;

This looks very dangerous from an endianness point of view.
If you run this driver in a big endian system, what happens?

> +}
> +
> +struct reflash_data {

This would be nice to kerneldoc.

> +       struct rmi_device *rmi_dev;
> +       struct pdt_entry *f01_pdt;
> +       union f01_basic_queries f01_queries;
> +       union f01_device_control_0 f01_controls;
> +       char product_id[RMI_PRODUCT_ID_LENGTH+1];
> +       struct pdt_entry *f34_pdt;
> +       u8 bootloader_id[2];
> +       union f34_query_regs f34_queries;
> +       union f34_control_status f34_controls;
> +       const u8 *firmware_data;
> +       const u8 *config_data;
> +};
> +
> +/* If this parameter is true, we will update the firmware regardless of
> + * the versioning info.
> + */
> +static bool force = 1;

= true;

> +module_param(force, bool, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(param, "Force reflash of RMI4 devices");

Looks reasonable...

> +
> +/* If this parameter is not NULL, we'll use that name for the firmware image,
> + * instead of getting it from the F01 queries.
> + */
> +static char *img_name;
> +module_param(img_name, charp, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(param, "Name of the RMI4 firmware image");

Is there a default name?

(...)
> +#define MIN_SLEEP_TIME_US 50
> +#define MAX_SLEEP_TIME_US 100
> +
> +/* Wait until the status is idle and we're ready to continue */
> +static int wait_for_idle(struct reflash_data *data, int timeout_ms)
> +{
> +       int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1;

Use DIV_ROUND_UP() from <linux/kernel.h>:

= DIV_ROUND_UP((timeout_ms * 1000), MAX_SLEEP_TIME_US);

> +       int count = 0;

Consider just int i;

(...)
> +static int read_f34_queries(struct reflash_data *data)
> +{
> +       int retval;
> +       u8 id_str[3];

If it's a string it should be char id_str[3]; right?

(...)
> +#ifdef DEBUG
> +       dev_info(&data->rmi_dev->dev, "Got F34 data->f34_queries.\n");

This is equivalent to using dev_dbg() so use that.

> +       dev_info(&data->rmi_dev->dev, "F34 bootloader id: %s (%#04x %#04x)\n",
> +                id_str, data->bootloader_id[0], data->bootloader_id[1]);
> +       dev_info(&data->rmi_dev->dev, "F34 has config id: %d\n",
> +                data->f34_queries.has_config_id);
> +       dev_info(&data->rmi_dev->dev, "F34 unlocked:      %d\n",
> +                data->f34_queries.unlocked);
> +       dev_info(&data->rmi_dev->dev, "F34 regMap:        %d\n",
> +                data->f34_queries.reg_map);
> +       dev_info(&data->rmi_dev->dev, "F34 block size:    %d\n",
> +                data->f34_queries.block_size);
> +       dev_info(&data->rmi_dev->dev, "F34 fw blocks:     %d\n",
> +                data->f34_queries.fw_block_count);
> +       dev_info(&data->rmi_dev->dev, "F34 config blocks: %d\n",
> +                data->f34_queries.config_block_count);

Dito.

(...)
> +static int enter_flash_programming(struct reflash_data *data)
> +{
> +       int retval;
> +       union f01_device_status device_status;
> +       struct rmi_device *rmi_dev = data->rmi_dev;
> +
> +       retval = write_bootloader_id(data);
> +       if (retval < 0)
> +               return retval;
> +
> +       dev_info(&rmi_dev->dev, "Enabling flash programming.\n");
> +       retval = write_f34_command(data, F34_ENABLE_FLASH_PROG);
> +       if (retval < 0)
> +               return retval;
> +
> +#if    RIM_HACK
> +       data->f01_controls.nosleep = true;
> +       retval = write_f01_controls(data);
> +       if (retval < 0)
> +               return retval;
> +#endif

So just default-enable this since it's defined to 1? BTW what is an RIM?

(...)
> +       dev_info(&rmi_dev->dev, "HOORAY! Programming is enabled!\n");

I always enjoy it when the kernel is happy ;-)

(...)
> +static int write_firmware(struct reflash_data *data)
> +{
> +       return write_blocks(data, (u8 *) data->firmware_data,
> +               data->f34_queries.fw_block_count, F34_WRITE_FW_BLOCK);
> +}
> +
> +static int write_configuration(struct reflash_data *data)
> +{
> +       return write_blocks(data, (u8 *) data->config_data,
> +               data->f34_queries.config_block_count, F34_WRITE_CONFIG_BLOCK);
> +}

Now there are these one-function call functions again, are you sure you
can't just inline these?

> +static void reflash_firmware(struct reflash_data *data)
> +{
> +#ifdef DEBUG
> +       struct timespec start;
> +       struct timespec end;
> +       s64 duration_ns;
> +#endif

This actually seems to be a valid case for #ifdef:ing!

> +       int retval = 0;
> +
> +       retval = enter_flash_programming(data);
> +       if (retval)
> +               return;
> +
> +       retval = write_bootloader_id(data);
> +       if (retval)
> +               return;
> +
> +#ifdef DEBUG
> +       dev_info(&data->rmi_dev->dev, "Erasing FW...\n");

dev_dbg() includes the DEBUG flag.

> +       getnstimeofday(&start);

But this is a

> +#endif
> +       retval = write_f34_command(data, F34_ERASE_ALL);
> +       if (retval)
> +               return;
> +
> +       retval = wait_for_idle(data, F34_ERASE_WAIT_MS);
> +       if (retval) {
> +               dev_err(&data->rmi_dev->dev,
> +                       "Failed to reach idle state. Code: %d.\n", retval);
> +               return;
> +       }
> +#ifdef DEBUG
> +       getnstimeofday(&end);
> +       duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
> +       dev_info(&data->rmi_dev->dev,
> +                "Erase complete, time: %lld ns.\n", duration_ns);

dev_dbg()

> +#endif
> +
> +       if (data->firmware_data) {
> +#ifdef DEBUG
> +               dev_info(&data->rmi_dev->dev, "Writing firmware...\n");
> +               getnstimeofday(&start);
> +#endif
> +               retval = write_firmware(data);
> +               if (retval)
> +                       return;
> +#ifdef DEBUG
> +               getnstimeofday(&end);
> +               duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
> +               dev_info(&data->rmi_dev->dev,
> +                        "Done writing FW, time: %lld ns.\n", duration_ns);
> +#endif
> +       }
> +
> +       if (data->config_data) {
> +#ifdef DEBUG
> +               dev_info(&data->rmi_dev->dev, "Writing configuration...\n");
> +               getnstimeofday(&start);
> +#endif
> +               retval = write_configuration(data);
> +               if (retval)
> +                       return;
> +#ifdef DEBUG
> +               getnstimeofday(&end);
> +               duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start);
> +               dev_info(&data->rmi_dev->dev,
> +                        "Done writing config, time: %lld ns.\n", duration_ns);
> +#endif
> +       }
> +}

Hard to avoid #ifdef:ing here, but maybe you could use some stub functions.

(...)
> +void rmi4_fw_update(struct rmi_device *rmi_dev,
> +               struct pdt_entry *f01_pdt, struct pdt_entry *f34_pdt)
> +{
> +#ifdef DEBUG
> +       struct timespec start;
> +       struct timespec end;
> +       s64 duration_ns;
> +#endif
> +       char firmware_name[RMI_PRODUCT_ID_LENGTH + 12];
> +       const struct firmware *fw_entry = NULL;
> +       int retval;
> +       struct image_header header;
> +       union pdt_properties pdt_props;
> +       struct reflash_data data = {
> +               .rmi_dev = rmi_dev,
> +               .f01_pdt = f01_pdt,
> +               .f34_pdt = f34_pdt,
> +       };
> +       struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
> +
> +       dev_info(&rmi_dev->dev, "%s called.\n", __func__);
> +#ifdef DEBUG
> +       getnstimeofday(&start);
> +#endif
> +
> +       retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, pdt_props.regs);
> +       if (retval < 0) {
> +               dev_warn(&rmi_dev->dev,
> +                        "Failed to read PDT props at %#06x (code %d). Assuming 0x00.\n",
> +                        PDT_PROPERTIES_LOCATION, retval);
> +       }
> +       if (pdt_props.has_bsr) {
> +               dev_warn(&rmi_dev->dev,
> +                        "Firmware update for LTS not currently supported.\n");
> +               return;
> +       }
> +
> +       retval = read_f01_queries(&data);
> +       if (retval) {
> +               dev_err(&rmi_dev->dev, "F01 queries failed, code = %d.\n",
> +                       retval);
> +               return;
> +       }
> +       retval = read_f34_queries(&data);
> +       if (retval) {
> +               dev_err(&rmi_dev->dev, "F34 queries failed, code = %d.\n",
> +                       retval);
> +               return;
> +       }
> +       if (pdata->firmware_name && strlen(pdata->firmware_name))
> +               snprintf(firmware_name, sizeof(firmware_name), "rmi4/%s.img",
> +                       pdata->firmware_name);

Aha this works out nicely with the firmware name from pdata...

> +       else
> +               snprintf(firmware_name, sizeof(firmware_name), "rmi4/%s.img",
> +                       (img_name && strlen(img_name))
> +                               ? img_name : data.product_id);

And in the other case as module parameter right?

> +       dev_info(&rmi_dev->dev, "Requesting %s.\n", firmware_name);
> +       retval = request_firmware(&fw_entry, firmware_name, &rmi_dev->dev);
> +       if (retval != 0) {
> +               dev_err(&rmi_dev->dev, "Firmware %s not available, code = %d\n",
> +                       firmware_name, retval);
> +               return;
> +       }
> +
> +#ifdef DEBUG

I don't see why this would be #ifdef DEBUG, it seems to be very useful
information
to always have in the dmesg.

> +       dev_info(&rmi_dev->dev, "Got firmware, size: %d.\n", fw_entry->size);

dev_dbg() if you insist to have this as debug only.

> +       extract_header(fw_entry->data, 0, &header);
> +       dev_info(&rmi_dev->dev, "Img checksum:           %#08X\n",
> +                header.checksum);
> +       dev_info(&rmi_dev->dev, "Img image size:         %d\n",
> +                header.image_size);
> +       dev_info(&rmi_dev->dev, "Img config size:        %d\n",
> +                header.config_size);
> +       dev_info(&rmi_dev->dev, "Img bootloader version: %d\n",
> +                header.bootloader_version);
> +       dev_info(&rmi_dev->dev, "Img product id:         %s\n",
> +                header.product_id);
> +       dev_info(&rmi_dev->dev, "Img product info:       %#04x %#04x\n",
> +                header.product_info[0], header.product_info[1]);
> +#endif

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 7/17] input: RMI4 F01 device control
  2012-08-17 22:17 ` [RFC PATCH 7/17] input: RMI4 F01 device control Christopher Heiny
@ 2012-08-27 21:59   ` Linus Walleij
  0 siblings, 0 replies; 44+ messages in thread
From: Linus Walleij @ 2012-08-27 21:59 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Henrik Rydberg

On Fri, Aug 17, 2012 at 3:17 PM, Christopher Heiny <cheiny@synaptics.com> wrote:

(...)
> +++ b/drivers/input/rmi4/rmi_f01.c
(...)
> +union f01_device_commands {
> +       struct {
> +               u8 reset:1;
> +               u8 reserved:1;

I would just use bool for one-bit variables.

But I see that some protocol people often cast a piece of data on top
of  a struct, and have it packed to fit on top of each other.

It looks like that is what is going on here.

> +       };
> +       u8 reg;
> +};

If you're doing bitstuffing, use
__attribute__((__packed__));
after each struct.

> +struct f01_device_control {
> +       union f01_device_control_0 ctrl0;
> +       u8 *interrupt_enable;
> +       u8 doze_interval;
> +       u8 wakeup_threshold;
> +       u8 doze_holdoff;
> +};

These could use some kerneldoc.

__attribute__((__packed__));

> +union f01_query_42 {
> +       struct {
> +               u8 has_ds4_queries:1;
> +               u8 has_multi_phy:1;
> +               u8 has_guest:1;
> +               u8 reserved:5;
> +       };
> +       u8 regs[1];
> +};

__attribute__((__packed__));

> +union f01_ds4_queries {
> +       struct {
> +               u8 length:4;
> +               u8 reserved_1:4;
> +
> +               u8 has_package_id_query:1;
> +               u8 has_packrat_query:1;
> +               u8 has_reset_query:1;
> +               u8 has_maskrev_query:1;
> +               u8 reserved_2:4;
> +
> +               u8 has_i2c_control:1;
> +               u8 has_spi_control:1;
> +               u8 has_attn_control:1;
> +               u8 reserved_3:5;
> +
> +               u8 reset_enabled:1;
> +               u8 reset_polarity:1;
> +               u8 pullup_enabled:1;
> +               u8 reserved_4:1;
> +               u8 reset_pin_number:4;
> +       };
> +       u8 regs[4];
> +};

__attribute__((__packed__));


(...)
> +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),
> +       __ATTR(interrupt_enable, RMI_RW_ATTR,
> +              rmi_fn_01_interrupt_enable_show,
> +               rmi_fn_01_interrupt_enable_store),
> +       __ATTR(doze_interval, RMI_RW_ATTR,
> +              rmi_fn_01_doze_interval_show,
> +               rmi_fn_01_doze_interval_store),
> +       __ATTR(wakeup_threshold, RMI_RW_ATTR,
> +              rmi_fn_01_wakeup_threshold_show,
> +               rmi_fn_01_wakeup_threshold_store),
> +       __ATTR(doze_holdoff, RMI_RW_ATTR,
> +              rmi_fn_01_doze_holdoff_show,
> +               rmi_fn_01_doze_holdoff_store),
> +
> +       /* We make report rate RO, since the driver uses that to look for
> +        * resets.  We don't want someone faking us out by changing that
> +        * bit.
> +        */
> +       __ATTR(configured, RMI_RO_ATTR,
> +              rmi_fn_01_configured_show, rmi_store_error),
> +
> +       /* Command register access. */
> +       __ATTR(reset, RMI_WO_ATTR,
> +              rmi_show_error, rmi_fn_01_reset_store),
> +
> +       /* STatus register access. */
> +       __ATTR(unconfigured, RMI_RO_ATTR,
> +              rmi_fn_01_unconfigured_show, rmi_store_error),
> +       __ATTR(flashprog, RMI_RO_ATTR,
> +              rmi_fn_01_flashprog_show, rmi_store_error),
> +       __ATTR(statuscode, RMI_RO_ATTR,
> +              rmi_fn_01_statuscode_show, rmi_store_error),
> +};

Can you partition this into one set of stuff that goes into sysfs and
another part that goes into debugfs, depending on usage?

The sysfs portions need to be documented in
Documentation/ABI/testing/*

> +/* Utility routine to set the value of a bit field in a register. */
> +int rmi_set_bit_field(struct rmi_device *rmi_dev,

This is not what the function is doing. It is doing
read/mask/set/write, so rmi_mask_and_set() is a better name.

> +                     unsigned short address,

u16?

> +                     unsigned char field_mask,

u8?

Just "mask" is fine.

> +                     unsigned char bits)

u8?

Call it "set" (IMHO)?

(...)
> +static ssize_t rmi_fn_01_interrupt_enable_store(struct device *dev,
> +                                         struct device_attribute *attr,
> +                                         const char *buf, size_t count)
> +{
> +       struct rmi_function_container *fc;
> +       struct f01_data *data;
> +       int i;
> +       int irq_count = 0;
> +       int retval = count;
> +       int irq_reg = 0;
> +
> +       fc = to_rmi_function_container(dev);
> +       data = fc->data;
> +       for (i = 0; i < data->irq_count && *buf != 0;
> +            i++, buf += 2) {
> +               int irq_shift;
> +               int interrupt_enable;
> +               int result;
> +
> +               irq_reg = i / 8;
> +               irq_shift = i % 8;
> +
> +               /* get next interrupt mapping value and store and bump up to
> +                * point to next item in buf */
> +               result = sscanf(buf, "%u", &interrupt_enable);
> +               if ((result != 1) ||
> +                       (interrupt_enable != 0 && interrupt_enable != 1)) {
> +                       dev_err(dev,
> +                               "%s: Error - interrupt enable[%d]"
> +                               " is not a valid value 0x%x.\n",
> +                               __func__, i, interrupt_enable);
> +                       return -EINVAL;
> +               }
> +               if (interrupt_enable == 0) {
> +                       data->device_control.interrupt_enable[irq_reg] &=
> +                               (1 << irq_shift) ^ 0xFF;
> +               } else
> +                       data->device_control.interrupt_enable[irq_reg] |=
> +                               (1 << irq_shift);
> +               irq_count++;
> +       }
> +
> +       /* Make sure the irq count matches */
> +       if (irq_count != data->irq_count) {
> +               dev_err(dev,
> +                       "%s: Error - interrupt enable count of %d"
> +                       " doesn't match device count of %d.\n",
> +                        __func__, irq_count, data->irq_count);
> +               return -EINVAL;
> +       }
> +
> +       /* write back to the control register */
> +       retval = rmi_write_block(fc->rmi_dev, data->interrupt_enable_addr,
> +                       data->device_control.interrupt_enable,
> +                       sizeof(u8)*(data->num_of_irq_regs));
> +       if (retval < 0) {
> +               dev_err(dev, "%s : Could not write interrupt_enable_store"
> +                       " to 0x%x\n", __func__, data->interrupt_enable_addr);
> +               return retval;
> +       }
> +
> +       return count;
> +
> +}

I cannot figure out why you would want to enable/disable IRQs from
sysfs nodes? Please describe the usecase... If this is only for
debugging and testing, debugfs is the right place.

(...)
> +static int rmi_f01_reset(struct rmi_function_container *fc)
> +{
> +       /*do nothing here */
> +       return 0;
> +}

Doesn't look very useful?

> +static int rmi_f01_init(struct rmi_function_container *fc)
> +{
> +       return 0;
> +}

Neither does this.

> +static struct rmi_function_handler function_handler = {
> +       .func = 0x01,
> +       .init = rmi_f01_init,
> +       .config = rmi_f01_config,
> +       .reset = rmi_f01_reset,
> +       .attention = rmi_f01_attention,

So make .reset and .init by checking for them being NULL in the
bus driver and avoid calling them in this case like that:

if (!func->init)
   return 0;

> +#ifdef CONFIG_PM
> +#if defined(CONFIG_HAS_EARLYSUSPEND) && \
> +                       !defined(CONFIG_RMI4_SPECIAL_EARLYSUSPEND)
> +       .early_suspend = rmi_f01_suspend,
> +       .late_resume = rmi_f01_resume,
> +#else
> +       .suspend = rmi_f01_suspend,
> +       .resume = rmi_f01_resume,
> +#endif  /* defined(CONFIG_HAS_EARLYSUSPEND) && !def... */
> +#endif  /* CONFIG_PM */

More Android stuff not in the mainline kernel...

(...)
> +++ b/drivers/input/rmi4/rmi_f01.h
(...)
> +union f01_basic_queries {
> +       struct {
> +               u8 manufacturer_id:8;
> +
> +               u8 custom_map:1;
> +               u8 non_compliant:1;
> +               u8 has_lts:1;
> +               u8 has_sensor_id:1;
> +               u8 has_charger_input:1;
> +               u8 has_adjustable_doze:1;
> +               u8 has_adjustable_doze_holdoff:1;
> +               u8 has_product_properties_2: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];
> +};

__attribute__((packed));

?

> +union f01_device_status {
> +       struct {
> +               u8 status_code:4;
> +               u8 reserved:2;
> +               u8 flash_prog:1;
> +               u8 unconfigured:1;
> +       };
> +       u8 regs[1];
> +};

__attribute__((packed));

?


> +/* 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)

Funny reserved modes but whatever...

> +
> +#define RMI_IS_VALID_SLEEPMODE(mode) \
> +       (mode >= RMI_SLEEP_MODE_NORMAL && mode <= RMI_SLEEP_MODE_RESERVED1)
> +
> +union f01_device_control_0 {
> +       struct {
> +               u8 sleep_mode:2;
> +               u8 nosleep:1;
> +               u8 reserved:2;
> +               u8 charger_input:1;
> +               u8 report_rate:1;
> +               u8 configured:1;
> +       };
> +       u8 regs[1];
> +};

__attribute__((packed));

?

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 8/17] input: RMI4 F09 Built-In Self Test
  2012-08-17 22:17 ` [RFC PATCH 8/17] input: RMI4 F09 Built-In Self Test Christopher Heiny
@ 2012-08-27 22:07   ` Linus Walleij
  2012-09-05  0:21     ` Christopher Heiny
  0 siblings, 1 reply; 44+ messages in thread
From: Linus Walleij @ 2012-08-27 22:07 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Henrik Rydberg

On Fri, Aug 17, 2012 at 3:17 PM, Christopher Heiny <cheiny@synaptics.com> wrote:

Put in a verbose description of what this is.

(...)
> +++ b/drivers/input/rmi4/rmi_f09.c

> +/* 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 internal_limits:1;
> +                       u8 host_test_enable:1;
> +               };
> +               u8 f09_bist_query1;
> +       };
> +};

__attribute__((packed));

?

> +struct f09_control {
> +       union {
> +               struct {
> +                       u8 test1_limit_low:8;
> +                       u8 test1_limit_high:8;
> +                       u8 test1_limit_diff:8;
> +               };
> +               u8 f09_control_test1[3];
> +       };
> +       union {
> +               struct {
> +                       u8 test2_limit_low:8;
> +                       u8 test2_limit_high:8;
> +                       u8 test2_limit_diff:8;
> +               };
> +               u8 f09_control_test2[3];
> +       };
> +};

__attribute__((packed));

?

> +struct f09_data {
> +       u8 test_number_control;
> +       u8 overall_bist_result;
> +       u8 test_result1;
> +       u8 test_result2;
> +       u8 transmitter_number;
> +
> +       union {
> +               struct {
> +                       u8 receiver_number:6;
> +                       u8 limit_failure_code:2;
> +               };
> +               u8 f09_bist_data2;
> +       };
> +};

__attribute__((packed));

?

> +struct f09_cmd {
> +       union {
> +               struct {
> +                       u8 run_bist:1;
> +               };
> +               u8 f09_bist_cmd0;
> +       };
> +};

__attribute__((packed));

?

(...)
> +static struct device_attribute attrs[] = {
> +       __ATTR(status, RMI_RW_ATTR,
> +                  rmi_f09_status_show, rmi_f09_status_store),
> +       __ATTR(limitRegisterCount, RMI_RO_ATTR,
> +              rmi_f09_limit_register_count_show, rmi_store_error),
> +       __ATTR(hostTestEnable, RMI_RW_ATTR,
> +              rmi_f09_host_test_enable_show, rmi_f09_host_test_enable_store),
> +       __ATTR(internalLimits, RMI_RO_ATTR,
> +              rmi_f09_internal_limits_show, rmi_store_error),
> +       __ATTR(resultRegisterCount, RMI_RO_ATTR,
> +              rmi_f09_result_register_count_show, rmi_store_error),
> +       __ATTR(overall_bist_result, RMI_RO_ATTR,
> +              rmi_f09_overall_bist_result_show, rmi_store_error),
> +       __ATTR(test_number_control, RMI_RW_ATTR,
> +              rmi_f09_test_number_control_show,
> +              rmi_f09_test_number_control_store),
> +       __ATTR(test_result1, RMI_RO_ATTR,
> +              rmi_f09_test_result1_show, rmi_store_error),
> +       __ATTR(test_result2, RMI_RO_ATTR,
> +              rmi_f09_test_result2_show, rmi_store_error),
> +       __ATTR(run_bist, RMI_RW_ATTR,
> +              rmi_f09_run_bist_show, rmi_f09_run_bist_store),
> +       __ATTR(f09_control_test1, RMI_RW_ATTR,
> +              rmi_f09_control_test1_show, rmi_f09_control_test1_store),
> +       __ATTR(f09_control_test2, RMI_RW_ATTR,
> +              rmi_f09_control_test2_show, rmi_f09_control_test2_store),
> +};

If this is *only* for tests, then for sure this should be in debugfs?

> +static int rmi_f09_alloc_memory(struct rmi_function_container *fc)
(...)
> +static void rmi_f09_free_memory(struct rmi_function_container *fc)

Why do you need separate functions for these two?

If they are only used from one place (which I suspect) then just
put the code at that site.

(...)
> +static int rmi_f09_initialize(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 = fc->data;
> +       u16 query_base_addr;
> +       int rc;
> +
> +
> +       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);
> +               return rc;
> +       }
> +
> +       return 0;
> +}

Similar here. Cannot this be brought into the only call site?

> +static int rmi_f09_config(struct rmi_function_container *fc)
> +{
> +       /*we do nothing here. instead reset should notify the user.*/
> +       return 0;
> +}

Make it optional and just don't define it.

> +static int rmi_f09_reset(struct rmi_function_container *fc)
> +{
> +       struct  rmi_fn_09_data  *instance_data = fc->data;
> +
> +       instance_data->status = -ECONNRESET;
> +
> +       return 0;
> +}

Dito.

(Already remarked this at the last patch.)

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 9/17] input: RMI4 F11 multitouch sensing
  2012-08-17 22:17 ` [RFC PATCH 9/17] input: RMI4 F11 multitouch sensing Christopher Heiny
@ 2012-08-27 22:50   ` Linus Walleij
  0 siblings, 0 replies; 44+ messages in thread
From: Linus Walleij @ 2012-08-27 22:50 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Henrik Rydberg

This patch should definately be reviewed by Henrik Rydberg so include him on
the next iteration.

On Fri, Aug 17, 2012 at 3:17 PM, Christopher Heiny <cheiny@synaptics.com> wrote:

Verbose description of what the patch does please.

(...)
> +++ b/drivers/input/rmi4/rmi_f11.c
(...)
> +#ifdef CONFIG_RMI4_F11_TYPEB
> +#include <linux/mt.h>
> +#endif

It doesn't *HURT* if you include one file to many so
just skip the #ifdef.

> +#include <linux/rmi.h>
> +#include "rmi_driver.h"
> +
> +#ifdef CONFIG_RMI4_DEBUG

Same here, just leave all headers in, skip the #ifdef

> +#include <linux/debugfs.h>
> +#include <linux/fs.h>
> +/*#include <asm/uaccess.h> */

Delete that line

> +#include <linux/uaccess.h>
> +#endif
> +
> +#define RESUME_REZERO (1 && defined(CONFIG_PM))
> +#if RESUME_REZERO
> +#include <linux/delay.h>
> +#define DEFAULT_REZERO_WAIT_MS 40
> +#endif

Dito.

> +#define GET_FINGER_STATE(f_states, i) \
> +       ((f_states[i / 4] >> (2 * (i % 4))) & FINGER_STATE_MASK)

Convert to a static inline function.

> +#define INBOX(x, y, box) (x >= box.x && x < (box.x + box.width) \
> +                       && y >= box.y && y < (box.y + box.height))

Dito.

> +/* Adding debugfs for flip, clip, offset and swap */
> +#ifdef CONFIG_RMI4_DEBUG
> +
> +static int setup_debugfs(struct rmi_device *rmi_dev);
> +static void teardown_debugfs(struct rmi_device *rmi_dev);
> +#endif
> +/* End adding debugfs */

Better to depend on CONFIG_DEBUG_FS

(...)
> +#if RESUME_REZERO

This RESUME_REZERO business scares me off. First it should
be a Kconfig thing, and why is it optional? Also put in a comment
explaining what it's all about.

> +static struct device_attribute attrs[] = {
> +       __ATTR(relreport, RMI_RW_ATTR, f11_relreport_show, f11_relreport_store),
> +       __ATTR(maxPos, RMI_RO_ATTR, f11_maxPos_show, rmi_store_error),
> +#if RESUME_REZERO
> +       __ATTR(rezeroOnResume, RMI_RW_ATTR, f11_rezeroOnResume_show,
> +               f11_rezeroOnResume_store),
> +       __ATTR(rezeroWait, RMI_RW_ATTR, f11_rezeroWait_show,
> +               f11_rezeroWait_store),
> +#endif
> +       __ATTR(rezero, RMI_WO_ATTR, rmi_show_error, f11_rezero_store)
> +};

Documentation/ABI/testing/* needs these, plus consider moving some
of these to debugfs.

> +union f11_2d_commands {
> +       struct {
> +               u8 rezero:1;
> +       };
> +       u8 reg;

Now I think I can see what you're trying to do. The .reg
is just there to make sure this thing is padded to 8 bits right?

__attribute__((packed)); ?

> +struct f11_2d_device_query {
> +       union {
> +               struct {
> +                       u8 nbr_of_sensors:3;
> +                       u8 has_query9:1;
> +                       u8 has_query11:1;
> +               };
> +               u8 f11_2d_query0;
> +       };
> +
> +       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;
> +       };
> +};

__attribute__((packed)); ?

> +union f11_2d_query9 {
> +       struct {
> +               u8 has_pen:1;
> +               u8 has_proximity:1;
> +               u8 has_palm_det_sensitivity:1;
> +               u8 has_suppress_on_palm_detect:1;
> +               u8 has_two_pen_thresholds:1;
> +               u8 has_contact_geometry:1;
> +       };
> +       u8 reg;
> +};

__attribute__((packed)); ?

> +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];
> +       };
> +
> +       union f11_2d_query9 query9;
> +
> +       union {
> +               struct {
> +                       u8 nbr_touch_shapes:5;
> +               };
> +               u8 f11_2d_query10;
> +       };
> +};

__attribute__((packed)); ?

> +union f11_2d_ctrl0_9 {
> +       struct {
> +               /* F11_2D_Ctrl0 */
> +               u8 reporting_mode:3;
> +               u8 abs_pos_filt:1;
> +               u8 rel_pos_filt:1;
> +               u8 rel_ballistics:1;
> +               u8 dribble:1;
> +               u8 report_beyond_clip:1;
> +               /* F11_2D_Ctrl1 */
> +               u8 palm_detect_thres:4;
> +               u8 motion_sensitivity:2;
> +               u8 man_track_en:1;
> +               u8 man_tracked_finger:1;
> +               /* F11_2D_Ctrl2 and 3 */
> +               u8 delta_x_threshold:8;
> +               u8 delta_y_threshold:8;
> +               /* F11_2D_Ctrl4 and 5 */
> +               u8 velocity:8;
> +               u8 acceleration:8;
> +               /* F11_2D_Ctrl6 thru 9 */
> +               u16 sensor_max_x_pos:12;
> +               u8 ctrl7_reserved:4;
> +               u16 sensor_max_y_pos:12;
> +               u8 ctrl9_reserved:4;
> +       };
> +       struct {
> +               u8 regs[10];
> +               u16 address;
> +       };
> +};

__attribute__((packed)); ?

> +union f11_2d_ctrl10 {
> +       struct {
> +               u8 single_tap_int_enable:1;
> +               u8 tap_n_hold_int_enable:1;
> +               u8 double_tap_int_enable:1;
> +               u8 early_tap_int_enable:1;
> +               u8 flick_int_enable:1;
> +               u8 press_int_enable:1;
> +               u8 pinch_int_enable:1;
> +       };
> +       u8 reg;
> +};

__attribute__((packed)); ?

> +union f11_2d_ctrl11 {
> +       struct {
> +               u8 palm_detect_int_enable:1;
> +               u8 rotate_int_enable:1;
> +               u8 touch_shape_int_enable:1;
> +               u8 scroll_zone_int_enable:1;
> +               u8 multi_finger_scroll_int_enable:1;
> +       };
> +       u8 reg;
> +};

__attribute__((packed)); ?

> +union f11_2d_ctrl12 {
> +       struct {
> +               u8 sensor_map:7;
> +               u8 xy_sel:1;
> +       };
> +       u8 reg;
> +};

__attribute__((packed)); ?

> +union f11_2d_ctrl14 {
> +       struct {
> +               u8 sens_adjustment:5;
> +               u8 hyst_adjustment:3;
> +       };
> +       u8 reg;
> +};

__attribute__((packed)); ?

> +union f11_2d_ctrl15 {
> +       struct {
> +               u8 max_tap_time:8;
> +       };
> +       u8 reg;
> +};

__attribute__((packed)); ?

> +union f11_2d_ctrl16 {
> +       struct {
> +               u8 min_press_time:8;
> +       };
> +       u8 reg;
> +};

__attribute__((packed)); ?

> +union f11_2d_ctrl17 {
> +       struct {
> +               u8 max_tap_distance:8;
> +       };
> +       u8 reg;
> +};

__attribute__((packed)); ?

> +union f11_2d_ctrl18_19 {
> +       struct {
> +               u8 min_flick_distance:8;
> +               u8 min_flick_speed:8;
> +       };
> +       u8 reg[2];
> +};

__attribute__((packed)); ?

> +union f11_2d_ctrl20_21 {
> +       struct {
> +               u8 pen_detect_enable:1;
> +               u8 pen_jitter_filter_enable:1;
> +               u8 ctrl20_reserved:6;
> +               u8 pen_z_threshold:8;
> +       };
> +       u8 reg[2];
> +};

__attribute__((packed)); ?

> +/* These are not accessible through sysfs yet. */
> +union f11_2d_ctrl22_26 {
> +       struct {
> +               /* control 22 */
> +               u8 proximity_detect_int_en:1;
> +               u8 proximity_jitter_filter_en:1;
> +               u8 f11_2d_ctrl6_b3__7:6;
> +
> +               /* control 23 */
> +               u8 proximity_detection_z_threshold;
> +
> +               /* control 24 */
> +               u8 proximity_delta_x_threshold;
> +
> +               /* control 25 */
> +               u8 proximity_delta_y_threshold;
> +
> +               /* control 26 */
> +               u8 proximity_delta_z_threshold;
> +       };
> +       u8 regs[5];
> +};

__attribute__((packed)); ?

> +/* control 27 - haspalmdetectsensitivity or has suppressonpalmdetect */
> +union f11_2d_ctrl27 {
> +       struct {
> +               u8 palm_detecy_sensitivity:4;
> +               u8 suppress_on_palm_detect:1;
> +               u8 f11_2d_ctrl27_b5__7:3;
> +       };
> +       u8 regs[1];
> +};

__attribute__((packed)); ?

> +/* control 28 - has_multifingerscroll */
> +union f11_2d_ctrl28 {
> +       struct {
> +               u8 multi_finger_scroll_mode:2;
> +               u8 edge_motion_en:1;
> +               u8 f11_2d_ctrl28b_3:1;
> +               u8 multi_finger_scroll_momentum:4;
> +       };
> +       u8 regs[1];
> +};

__attribute__((packed)); ?

> +/* control 29 & 30 - hasztuning */
> +union f11_2d_ctrl29_30 {
> +       struct {
> +               u8 z_touch_threshold;
> +               u8 z_touch_hysteresis;
> +       };
> +       struct {
> +               u8 regs[2];
> +               u16 address;
> +       };
> +};

__attribute__((packed)); ?

> +struct  f11_2d_ctrl {
> +       union f11_2d_ctrl0_9 *ctrl0_9;
> +       union f11_2d_ctrl10             *ctrl10;
> +       union f11_2d_ctrl11             *ctrl11;
> +       union f11_2d_ctrl12             *ctrl12;
> +       u8                              ctrl12_size;
> +       union f11_2d_ctrl14             *ctrl14;
> +       union f11_2d_ctrl15             *ctrl15;
> +       union f11_2d_ctrl16             *ctrl16;
> +       union f11_2d_ctrl17             *ctrl17;
> +       union f11_2d_ctrl18_19          *ctrl18_19;
> +       union f11_2d_ctrl20_21          *ctrl20_21;
> +       union f11_2d_ctrl22_26 *ctrl22_26;
> +       union f11_2d_ctrl27 *ctrl27;
> +       union f11_2d_ctrl28 *ctrl28;
> +       union f11_2d_ctrl29_30 *ctrl29_30;
> +};

__attribute__((packed)); ?

> +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;
> +};

__attribute__((packed)); ?

> +struct f11_2d_data_6_7 {
> +       s8 delta_x;
> +       s8 delta_y;
> +};

__attribute__((packed)); ?

> +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;
> +};

__attribute__((packed)); ?

> +struct f11_2d_data_9 {
> +       u8 palm_detect:1;
> +       u8 rotate:1;
> +       u8 shape:1;
> +       u8 scrollzone:1;
> +       u8 finger_count:3;
> +};

__attribute__((packed)); ?

(...)
> +/* Adding debugfs for flip, clip, offset and swap */
> +#ifdef CONFIG_RMI4_DEBUG

I think this should be just switched on #CONFIG_DEBUG_FS

(...)
> +static int get_tool_type(struct f11_2d_sensor *sensor, u8 finger_state)
> +{
> +#ifdef CONFIG_RMI4_F11_PEN
> +       if (sensor->sens_query.query9.has_pen && finger_state == F11_PEN)
> +               return MT_TOOL_PEN;
> +#endif
> +       return MT_TOOL_FINGER;
> +}

Consider the case where we build a single kernel used on two devices:
one has a pen input and another one has a finger input.

What do you config in your kernel build?

I'm uncertain about the above code, but I hope you can just
define that you want pen support and have both work just
as well.

(...)
> +#ifdef ABS_MT_PRESSURE

Isn't it CONFIG_ABS_MT_PRESSURE?

> +       input_report_abs(sensor->input, ABS_MT_PRESSURE, z);
> +#endif

Henrik will have to answer but why is this optional?

Can't your driver just select ABS_MT_PRESSURE and just always
report this?

> +static int f11_allocate_control_regs(struct rmi_device *rmi_dev,
> +                               struct f11_2d_device_query *device_query,
> +                               struct f11_2d_sensor_query *sensor_query,
> +                               struct f11_2d_ctrl *ctrl,
> +                               u16 ctrl_base_addr) {
> +
> +       int error = 0;
> +       ctrl->ctrl0_9 = kzalloc(sizeof(union f11_2d_ctrl0_9),
> +                                      GFP_KERNEL);
> +       if (!ctrl->ctrl0_9) {
> +               error = -ENOMEM;
> +               goto error_exit;
> +       }
> +       if (sensor_query->f11_2d_query7__8[0]) {
> +               ctrl->ctrl10 = kzalloc(sizeof(union f11_2d_ctrl10),
> +                                      GFP_KERNEL);
> +               if (!ctrl->ctrl10) {
> +                       error = -ENOMEM;
> +                       goto error_exit;
> +               }
> +       }
> +
> +       if (sensor_query->f11_2d_query7__8[1]) {
> +               ctrl->ctrl11 = kzalloc(sizeof(union f11_2d_ctrl11),
> +                                      GFP_KERNEL);
> +               if (!ctrl->ctrl11) {
> +                       error = -ENOMEM;
> +                       goto error_exit;
> +               }
> +       }
> +
> +       if (device_query->has_query9 && sensor_query->query9.has_pen) {
> +               ctrl->ctrl20_21 = kzalloc(sizeof(union f11_2d_ctrl20_21),
> +                                         GFP_KERNEL);
> +               if (!ctrl->ctrl20_21) {
> +                       error = -ENOMEM;
> +                       goto error_exit;
> +               }
> +       }
> +
> +       if (device_query->has_query9 && sensor_query->query9.has_proximity) {
> +               ctrl->ctrl22_26 = kzalloc(sizeof(union f11_2d_ctrl22_26),
> +                                         GFP_KERNEL);
> +               if (!ctrl->ctrl22_26) {
> +                       error = -ENOMEM;
> +                       goto error_exit;
> +               }
> +       }
> +
> +       if (device_query->has_query9 &&
> +               (sensor_query->query9.has_palm_det_sensitivity ||
> +               sensor_query->query9.has_suppress_on_palm_detect)) {
> +               ctrl->ctrl27 = kzalloc(sizeof(union f11_2d_ctrl27),
> +                                         GFP_KERNEL);
> +               if (!ctrl->ctrl27) {
> +                       error = -ENOMEM;
> +                       goto error_exit;
> +               }
> +       }
> +
> +       if (sensor_query->has_multi_finger_scroll) {
> +               ctrl->ctrl28 = kzalloc(sizeof(union f11_2d_ctrl28),
> +                                         GFP_KERNEL);
> +               if (!ctrl->ctrl28) {
> +                       error = -ENOMEM;
> +                       goto error_exit;
> +               }
> +       }
> +
> +       if (device_query->has_query11 && device_query->has_z_tuning) {
> +               ctrl->ctrl29_30 = kzalloc(sizeof(union f11_2d_ctrl29_30),
> +                                         GFP_KERNEL);
> +               if (!ctrl->ctrl29_30) {
> +                       error = -ENOMEM;
> +                       goto error_exit;
> +               }
> +       }
> +
> +       return f11_read_control_regs(rmi_dev, ctrl, ctrl_base_addr);
> +
> +error_exit:
> +       f11_free_control_regs(ctrl);
> +       return error;
> +}

Can't you use devm_kzalloc() for all of these and move them to the call
site?

(...)
> +static int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev,
> +                       struct f11_2d_sensor_query *query, u16 query_base_addr)
> +{
> +       if (query->has_abs) {
(...)
> +       if (query->has_rel) {
(...)
> +       if (query->has_gestures) {
(...)
> +       if (query->has_touch_shapes) {
(...)

This runtime construct is really nice.

> +static void rmi_f11_free_memory(struct rmi_function_container *fc)
> +{
> +       struct f11_data *f11 = fc->data;
> +       int i;
> +
> +       if (f11) {
> +               f11_free_control_regs(&f11->dev_controls);
> +               for (i = 0; i < f11->dev_query.nbr_of_sensors + 1; i++)
> +                       kfree(f11->sensors[i].virtualbutton_map.map);
> +               kfree(f11);
> +               fc->data = NULL;
> +       }
> +}

So with devm* allocators you can cut down on this.

(...)
> +                       /* set bits for each button... */
> +                       for (j = 0; j < vm_pdata->buttons; j++) {
> +                               memcpy(&vm_sensor->map[j], &vm_pdata->map[j],
> +                                       sizeof(struct virtualbutton_map));
> +                               set_bit(vm_sensor->map[j].code,
> +                                       f11->sensors[i].input->keybit);

Hm here you are using the nice bitops set_bit whereas other code isn't...

> +                       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;

Describe magic numbers.

18d1 is particularly strange since in usb.ids this is Google Inc.

I think you would want to use 0x06cb (Synaptics)

I always was under the impression that USB, PCI (etc) IDs were
in the same number registry since they are often the same.

> +                       input_dev_mouse->id.product = 0x0210;

Usually some company-assigned person managed these numbers for e.g.
USB and PCI. Please check with her/him what to use here if possible.

(---)
> +#if    RESUME_REZERO

#ifdef?

> +#if defined(CONFIG_HAS_EARLYSUSPEND) && \
> +                       !defined(CONFIG_RMI4_SPECIAL_EARLYSUSPEND)
> +       .late_resume = rmi_f11_resume

Funny Android stuff... not supported.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 14/17] input: RMI4 F30 GPIO/LED control
  2012-08-17 22:17 ` [RFC PATCH 14/17] input: RMI4 F30 GPIO/LED control Christopher Heiny
@ 2012-08-27 22:58   ` Linus Walleij
  2012-09-05  0:28     ` Christopher Heiny
  0 siblings, 1 reply; 44+ messages in thread
From: Linus Walleij @ 2012-08-27 22:58 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

GPIO/LED, nice since I'm a GPIO maintainer I'll take a closer look.

If the bus will start doing a lot of non-input business it should live under
drivers/mfd but I think this is just one exception, right?

On Fri, Aug 17, 2012 at 3:17 PM, Christopher Heiny <cheiny@synaptics.com> wrote:

(...)
> diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c

> +#include <linux/kernel.h>
> +#include <linux/rmi.h>
> +#include <linux/input.h>
> +#include <linux/slab.h>
> +#include "rmi_driver.h"

The non-existance of <linux/gpio.h> and <linux/leds.h> tells us that something
is very wrong.

You should not model these GPIOs and LEDs by a set of obscure
sysfs attributes, instead use the proper kernel subsystems that
already exist for handling this! LEDs and GPIOs already have their
own (standardized) userspace sysfs interfaces.

Reading the code I see that this is what happens here, so please rewrite
this to be a real GPIO+LED driver using struct gpio_chip and
the same for LEDs.

Be inspired by drivers/gpio/* and drivers/leds/*

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 17/17] input: RMI4 F54 analog data reporting
  2012-08-17 22:17 ` [RFC PATCH 17/17] input: RMI4 F54 analog data reporting Christopher Heiny
@ 2012-08-27 23:01   ` Linus Walleij
  2012-09-05  0:38     ` Christopher Heiny
  0 siblings, 1 reply; 44+ messages in thread
From: Linus Walleij @ 2012-08-27 23:01 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Jonathan Cameron

On Fri, Aug 17, 2012 at 3:17 PM, Christopher Heiny <cheiny@synaptics.com> wrote:

Verbose commit message.

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

> +#include <linux/hrtimer.h>
> +#include <linux/kernel.h>
> +#include <linux/rmi.h>
> +#include <linux/slab.h>
> +#include <linux/version.h>
> +#include <linux/delay.h>

This is not using the existing in-kernel framework for ADC (which I think is
what you're doing), in this case use the IIO subsystem so consult
drivers/iio/adc and be inspired.

Just exposing a set of weird sysfs files to userspace is not proper.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
  2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (17 preceding siblings ...)
  2012-08-22 12:50 ` [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Linus Walleij
@ 2012-08-27 23:05 ` Linus Walleij
  18 siblings, 0 replies; 44+ messages in thread
From: Linus Walleij @ 2012-08-27 23:05 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

On Fri, Aug 17, 2012 at 3:17 PM, Christopher Heiny <cheiny@synaptics.com> wrote:

> This patch implements a driver supporting Synaptics ClearPad and other
> touchscreen sensors that use the RMI4 protocol, as defined here:

OK I looked over the parts I understood, to my surprise there is a
custom GPIO, LED and ADC driver in there, and none of them use
the proper kernel subsystems.

You may want to expose the bus in <linux/input/rmi4.h> or so, and
put the drivers for these into e.g.

drivers/gpio/gpio-rmi4.c
drivers/leds/leds-rmi4.c
drivers/iio/adc/rmi4.c

Which would be nice. (Kconfig can make sure they're only available
when enabling the bus.)

As for the input stuff I mainly looked at the stuff I understand...

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
  2012-08-22 12:50 ` [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Linus Walleij
  2012-08-22 21:29   ` Christopher Heiny
@ 2012-08-27 23:20   ` Christopher Heiny
  2012-08-28  0:12     ` Linus Walleij
  1 sibling, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-08-27 23:20 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

On 08/22/2012 05:50 AM, Linus Walleij wrote:
> On Sat, Aug 18, 2012 at 12:17 AM, Christopher Heiny
> <cheiny@synaptics.com>  wrote:
>
>> >This patch implements a driver supporting Synaptics ClearPad and other
>> >touchscreen sensors that use the RMI4 protocol, as defined here:
> Nice!
>
>> >This patch is against the v2.6.38 tag of Linus' kernel tree, commit
>> >521cb40b0c44418a4fd36dc633f575813d59a43d.  This will be our last patch against
>> >such an old kernel version, future patches will be against 3.x kernels.
> Please use the head of the subsystem tree to do this work.
> In this case, use Dmitry's git.
>
> Currently I just cannot test this because we have no such old codebase
> around.
>
> But I will attempt to review the patch set!

Thanks for all the feedback so far - it has been quite valuable.  I'm 
not going to try to respond to every comment, especially since most of 
it will be of a "Yeah - good idea" nature.

A couple of general things:


The "phys" debugfs interface is intended to tell you about the physical 
interface a particular RMI4 sensor is using (either SPI or I2C or 
whatever), including the name of the interface, the number of 
reads/writes done, number of bytes that have been read/written, number 
of errors, and so on.

We chose to to call name it phys so that the debug tools would be able 
to find this interface no matter what the physical connection was, even 
when new physical layer support (for example, SMbus) is introduced.



EARLY_SUSPEND/LATE_RESUME and other power management stuff.  We're 
caught in a bind here.  Most of our customers are using some flavor of 
Android.  They have the expectation that our driver will (a) support the 
Android power management model, and (b) be contributed into the mainline 
kernel without change.  Yes, I know these are contradictory 
requirements, given that Android specific features are not in the mainline.

With the upcoming rebase of the code to more modern kernels, we'll be 
able to eliminate a bunch of those dependencies.  But the only way to 
eliminate them entirely would be to maintain mainline and Android 
versions of the driver, which would drain resources from developing core 
features and fixing bugs.  So for now we've got a single code base. 
When we finally submit a patch and the only response is "everything is 
fine but that Android stuff", we'll probably change that policy within 
48 hours (to include time needed for celebration and subsequent hangover 
recovery :-) ).

					Cheers,
						Chris

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
  2012-08-27 23:20   ` Christopher Heiny
@ 2012-08-28  0:12     ` Linus Walleij
  0 siblings, 0 replies; 44+ messages in thread
From: Linus Walleij @ 2012-08-28  0:12 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

On Mon, Aug 27, 2012 at 4:20 PM, Christopher Heiny <cheiny@synaptics.com> wrote:

> EARLY_SUSPEND/LATE_RESUME and other power management stuff.  We're caught in
> a bind here.  Most of our customers are using some flavor of Android.  They
> have the expectation that our driver will (a) support the Android power
> management model, and (b) be contributed into the mainline kernel without
> change.  Yes, I know these are contradictory requirements, given that
> Android specific features are not in the mainline.
>
> With the upcoming rebase of the code to more modern kernels, we'll be able
> to eliminate a bunch of those dependencies.  But the only way to eliminate
> them entirely would be to maintain mainline and Android versions of the
> driver, which would drain resources from developing core features and fixing
> bugs.  So for now we've got a single code base. When we finally submit a
> patch and the only response is "everything is fine but that Android stuff",
> we'll probably change that policy within 48 hours (to include time needed
> for celebration and subsequent hangover recovery :-) ).

I'd suggest you rebase and test them with Android and the android hooks
all over the place, but when you send it out to community, remove these
#ifdef sections.

This way the delta between what's in the mainline kernel and what you're
maintaining internally will ideally be reduced to a few Android-enablement
patches.

But it's all up to the subsystem maintainer, so I'd ask Dmitry about this.

By the way, this patch set has come a long way, and I would suggest
to try merging core support and touch to begin with, so you have the
core in place, then you can work on individual function drivers one at
a time. This would make the patch bombs a bit smaller.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 8/17] input: RMI4 F09 Built-In Self Test
  2012-08-27 22:07   ` Linus Walleij
@ 2012-09-05  0:21     ` Christopher Heiny
  0 siblings, 0 replies; 44+ messages in thread
From: Christopher Heiny @ 2012-09-05  0:21 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Henrik Rydberg

On 08/27/2012 03:07 PM, Linus Walleij wrote:
[snip]
>> >+static struct device_attribute attrs[] = {
>> >+       __ATTR(status, RMI_RW_ATTR,
>> >+                  rmi_f09_status_show, rmi_f09_status_store),
>> >+       __ATTR(limitRegisterCount, RMI_RO_ATTR,
>> >+              rmi_f09_limit_register_count_show, rmi_store_error),
>> >+       __ATTR(hostTestEnable, RMI_RW_ATTR,
>> >+              rmi_f09_host_test_enable_show, rmi_f09_host_test_enable_store),
>> >+       __ATTR(internalLimits, RMI_RO_ATTR,
>> >+              rmi_f09_internal_limits_show, rmi_store_error),
>> >+       __ATTR(resultRegisterCount, RMI_RO_ATTR,
>> >+              rmi_f09_result_register_count_show, rmi_store_error),
>> >+       __ATTR(overall_bist_result, RMI_RO_ATTR,
>> >+              rmi_f09_overall_bist_result_show, rmi_store_error),
>> >+       __ATTR(test_number_control, RMI_RW_ATTR,
>> >+              rmi_f09_test_number_control_show,
>> >+              rmi_f09_test_number_control_store),
>> >+       __ATTR(test_result1, RMI_RO_ATTR,
>> >+              rmi_f09_test_result1_show, rmi_store_error),
>> >+       __ATTR(test_result2, RMI_RO_ATTR,
>> >+              rmi_f09_test_result2_show, rmi_store_error),
>> >+       __ATTR(run_bist, RMI_RW_ATTR,
>> >+              rmi_f09_run_bist_show, rmi_f09_run_bist_store),
>> >+       __ATTR(f09_control_test1, RMI_RW_ATTR,
>> >+              rmi_f09_control_test1_show, rmi_f09_control_test1_store),
>> >+       __ATTR(f09_control_test2, RMI_RW_ATTR,
>> >+              rmi_f09_control_test2_show, rmi_f09_control_test2_store),
>> >+};
> If this is*only*  for tests, then for sure this should be in debugfs?

F09 is used in the final product (for example, a phone or tablet) both 
on the production line and to diagnose failures in returned products. 
We can't be certain that the phone/tablet/whatever manufacturer will 
include debugfs in their production kernel, and if they don't they 
almost certainly won't want to install a different kernel on the 
production line to run a test, so we provided a sysfs interface to this.

>
>> >+static int rmi_f09_alloc_memory(struct rmi_function_container *fc)
> (...)
>> >+static void rmi_f09_free_memory(struct rmi_function_container *fc)
> Why do you need separate functions for these two?
>
> If they are only used from one place (which I suspect) then just
> put the code at that site.

Some of the other modules have fairly large and complicated 
alloc_memory() and free_memory() implementations, so we adopted this as 
a general convention in all the RMI function implementations.  But as 
you suggested elsewhere, using devm_kzalloc could tidy things up a lot, 
in which case the functions could be merged back into their callers.

[snip]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 5/17] input: rmidev character driver for RMI4 sensors
  2012-08-27 18:49   ` Linus Walleij
@ 2012-09-05  0:26     ` Christopher Heiny
  2012-09-05  8:29       ` Linus Walleij
  0 siblings, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-09-05  0:26 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

On 08/27/2012 11:49 AM, Linus Walleij wrote:
> On Fri, Aug 17, 2012 at 3:17 PM, Christopher Heiny <cheiny@synaptics.com> wrote:
>
>> Driver for Synaptics touchscreens using RMI4 protocol.
>
> Really? This looks more like some custom char driver to get a pipe
> into the device from userspace. Put in a proper description of what this
> is for.
>
> If the purpose is to read/write arbitrary addresses in the device,
> you should use regmap's debugfs interface for this instead,
> it is much better suited for the task.

We'll look into using regmap not only for that, but possibly for general 
register access.  It has the potential to greatly simply both the 
arbitrary register access, as well as the driver itself.

>
> (...)
>> +#define RMI_CHAR_DEV_TMPBUF_SZ 128
>> +#define RMI_REG_ADDR_PAGE_SELECT 0xFF
>> +#define REG_ADDR_LIMIT 0xFFFF
>> +
>> +struct rmidev_data {
>> +       /* mutex for file operation*/
>> +       struct mutex file_mutex;
>> +       /* main char dev structure */
>> +       struct cdev main_dev;
>> +
>> +       /* pointer to the corresponding RMI4 device.  We use this to do */
>> +       /* read, write, etc. */
>> +       struct rmi_device *rmi_dev;
>> +       /* reference count */
>> +       int ref_count;
>
> Something tells me you should atleast use <linux/kref.h> for this.
> It also solves a few atomicity problems in a good standard way.
> See Documentation/kref.txt

Roger.


>> +/*store dynamically allocated major number of char device*/
>> +static int rmidev_major_num;
>
> You need to patch your desired major number into
> Documentation/devices.txt'

We were going by the recommendation in Linux Device Drivers (3rd 
edition) to use dynamic major number allocation via alloc_chrdev_region. 
  In particular in section 3.2.3 it says "new numbers are not being 
assigned".  I guess at this point we need to know whether the info in 
LDD3 is authoritative or not.  We can always add a number to 
Documentation/devices.txt if that's the right thing to do, but I'd like 
to make sure our next submission isn't bounced because we did that, 
turning the process into Patch Ping-Pong :-).

In any case, it's likely that switching to the regmap interface would 
make this question irrelevant.


>> +static struct class *rmidev_device_class;
>
> Last time discussed with Greg, class devices were deprecated,
> and you should just use a bus instead. (But not sure.)

The references I found online weren't clear on this, so more 
investigation is required.  We'll defer that till we find out if the 
regmap changes eliminate the need for this.


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 14/17] input: RMI4 F30 GPIO/LED control
  2012-08-27 22:58   ` Linus Walleij
@ 2012-09-05  0:28     ` Christopher Heiny
  0 siblings, 0 replies; 44+ messages in thread
From: Christopher Heiny @ 2012-09-05  0:28 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Joerie de Gram, Wolfram Sang,
	Mathieu Poirier, Linus Walleij, Naveen Kumar Gaddipati

On 08/27/2012 03:58 PM, Linus Walleij wrote:
> GPIO/LED, nice since I'm a GPIO maintainer I'll take a closer look.
>
> If the bus will start doing a lot of non-input business it should live under
> drivers/mfd but I think this is just one exception, right?
>
> On Fri, Aug 17, 2012 at 3:17 PM, Christopher Heiny <cheiny@synaptics.com> wrote:
>
> (...)
>> diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c
>
>> +#include <linux/kernel.h>
>> +#include <linux/rmi.h>
>> +#include <linux/input.h>
>> +#include <linux/slab.h>
>> +#include "rmi_driver.h"
>
> The non-existance of <linux/gpio.h> and <linux/leds.h> tells us that something
> is very wrong.
>
> You should not model these GPIOs and LEDs by a set of obscure
> sysfs attributes, instead use the proper kernel subsystems that
> already exist for handling this! LEDs and GPIOs already have their
> own (standardized) userspace sysfs interfaces.
>
> Reading the code I see that this is what happens here, so please rewrite
> this to be a real GPIO+LED driver using struct gpio_chip and
> the same for LEDs.
>
> Be inspired by drivers/gpio/* and drivers/leds/*

Roger.  We'll rework this and resubmit at a later date.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 17/17] input: RMI4 F54 analog data reporting
  2012-08-27 23:01   ` Linus Walleij
@ 2012-09-05  0:38     ` Christopher Heiny
  0 siblings, 0 replies; 44+ messages in thread
From: Christopher Heiny @ 2012-09-05  0:38 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Jonathan Cameron

On 08/27/2012 04:01 PM, Linus Walleij wrote:
>
>> >Signed-off-by: Christopher Heiny<cheiny@synaptics.com>
>> >+#include <linux/hrtimer.h>
>> >+#include <linux/kernel.h>
>> >+#include <linux/rmi.h>
>> >+#include <linux/slab.h>
>> >+#include <linux/version.h>
>> >+#include <linux/delay.h>
> This is not using the existing in-kernel framework for ADC (which I think is
> what you're doing), in this case use the IIO subsystem so consult
> drivers/iio/adc and be inspired.
>
> Just exposing a set of weird sysfs files to userspace is not proper.

I don't think the Industrial I/O ADC framework is appropriate for this. 
  F54 isn't intended to provide access to a general purpose ADC feature, 
but rather for diagnostic access to the touchscreen's capacitive sense 
features, which results in a fairly weird (as you noticed) interface. 
Trying to hammer on it to fit into iio/adc would require a lot of 
effort, and wind up making it look like something it really isn't.

However, it makes sense to move this whole interface to debugfs, given 
its intended model of use.



^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 5/17] input: rmidev character driver for RMI4 sensors
  2012-09-05  0:26     ` Christopher Heiny
@ 2012-09-05  8:29       ` Linus Walleij
  0 siblings, 0 replies; 44+ messages in thread
From: Linus Walleij @ 2012-09-05  8:29 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati

On Wed, Sep 5, 2012 at 2:26 AM, Christopher Heiny <cheiny@synaptics.com> wrote:
> On 08/27/2012 11:49 AM, Linus Walleij wrote:
>>
>> You need to patch your desired major number into
>> Documentation/devices.txt'
>
> We were going by the recommendation in Linux Device Drivers (3rd edition) to
> use dynamic major number allocation via alloc_chrdev_region.  In particular
> in section 3.2.3 it says "new numbers are not being assigned".  I guess at
> this point we need to know whether the info in LDD3 is authoritative or not.

You're right, go for dynamic numbers. I was plain wrong.

>>> +static struct class *rmidev_device_class;
>>
>>
>> Last time discussed with Greg, class devices were deprecated,
>> and you should just use a bus instead. (But not sure.)
>
> The references I found online weren't clear on this, so more investigation
> is required.  We'll defer that till we find out if the regmap changes
> eliminate the need for this.

Just push Greg to review next version and he'll tell you what to
do about this, no problem.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 2/17] input: RMI4 core bus and sensor drivers.
  2012-08-23  8:55   ` Linus Walleij
@ 2012-09-25 23:53     ` Christopher Heiny
  2012-09-26 11:39       ` Linus Walleij
  0 siblings, 1 reply; 44+ messages in thread
From: Christopher Heiny @ 2012-09-25 23:53 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Greg KH, Rafael J. Wysocki, Magnus Damm

Sorry about the delay in following up on this one - we're going over the 
feedback, and realized we'd missed this comment.


On 08/23/2012 01:55 AM, Linus Walleij wrote:
>> +/* Create templates for given types */
>> >+#define simple_show_union_struct_unsigned(regtype, propname)\
>> >+simple_show_union_struct(regtype, propname, "%u\n")
>> >+
>> >+#define simple_show_union_struct_unsigned2(regtype, reg_group, propname)\
>> >+simple_show_union_struct2(regtype, reg_group, propname, "%u\n")
>> >+
>> >+#define show_union_struct_unsigned(regtype, reg_group, propname)\
>> >+show_union_struct(regtype, reg_group, propname, "%u\n")
>> >+
>> >+#define show_store_union_struct_unsigned(regtype, reg_group, propname)\
>> >+show_store_union_struct(regtype, reg_group, propname, "%u\n")
>> >+
>> >+#define show_repeated_union_struct_unsigned(regtype, reg_group, propname)\
>> >+show_repeated_union_struct(regtype, reg_group, propname, "%u")
>> >+
>> >+#define show_store_repeated_union_struct_unsigned(regtype, reg_group, propname)\
>> >+show_store_repeated_union_struct(regtype, reg_group, propname, "%u")
>> >+
>> >+/* Remove access to raw format string versions */
>> >+/*#undef simple_show_union_struct
>> >+#undef show_union_struct_unsigned
>> >+#undef show_store_union_struct
>> >+#undef show_repeated_union_struct
>> >+#undef show_store_repeated_union_struct*/
> This looks like trying to reimplement ioctl() in sysfs.
>
> If what you want is to send big structs in/out of the kernel,
> use either ioctl() on device nodes (should be trivial since input
> is using real device nodes) or use configfs.

I'm a little confused.  There's repeated emphasis in the kernel doc that 
you shouldn't use ioctl() anymore - use sysfs instead.  So we've been 
using sysfs, though it seems somewhat klutzy.  If it's actually OK to 
use ioctl(), that could simplify things.  On the other hand, using 
configfs might be more appropriate.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC PATCH 2/17] input: RMI4 core bus and sensor drivers.
  2012-09-25 23:53     ` Christopher Heiny
@ 2012-09-26 11:39       ` Linus Walleij
  0 siblings, 0 replies; 44+ messages in thread
From: Linus Walleij @ 2012-09-26 11:39 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Allie Xiong, William Manson, Peichen Chang, Joerie de Gram,
	Wolfram Sang, Mathieu Poirier, Linus Walleij,
	Naveen Kumar Gaddipati, Greg KH, Rafael J. Wysocki, Magnus Damm

On Wed, Sep 26, 2012 at 1:53 AM, Christopher Heiny <cheiny@synaptics.com> wrote:

>>> >+/* Remove access to raw format string versions */
>>> >+/*#undef simple_show_union_struct
>>> >+#undef show_union_struct_unsigned
>>> >+#undef show_store_union_struct
>>> >+#undef show_repeated_union_struct
>>> >+#undef show_store_repeated_union_struct*/
>>
>> This looks like trying to reimplement ioctl() in sysfs.
>>
>> If what you want is to send big structs in/out of the kernel,
>> use either ioctl() on device nodes (should be trivial since input
>> is using real device nodes) or use configfs.
>
> I'm a little confused.  There's repeated emphasis in the kernel doc that you
> shouldn't use ioctl() anymore - use sysfs instead.  So we've been using
> sysfs, though it seems somewhat klutzy.  If it's actually OK to use ioctl(),
> that could simplify things.  On the other hand, using configfs might be more
> appropriate.

OK yes configfs is said to be ideal for large configuration chunks,
I haven't really used it.

sysfs has this concept of one value per file, and that turns into the
above serialization/marshalling code if followed, so it doesn't look
good. Maybe configfs is the silver bullet.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 44+ messages in thread

end of thread, other threads:[~2012-09-26 11:39 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 1/17] input: RMI4 public header file and documentation Christopher Heiny
2012-08-22 19:08   ` Linus Walleij
2012-08-22 21:45     ` Dmitry Torokhov
2012-08-23  0:26       ` Christopher Heiny
2012-08-22 23:35     ` Rafael J. Wysocki
2012-08-17 22:17 ` [RFC PATCH 2/17] input: RMI4 core bus and sensor drivers Christopher Heiny
2012-08-23  8:55   ` Linus Walleij
2012-09-25 23:53     ` Christopher Heiny
2012-09-26 11:39       ` Linus Walleij
2012-08-17 22:17 ` [RFC PATCH 3/17] input: RMI4 physical layer drivers for I2C and SPI Christopher Heiny
2012-08-23 13:21   ` Linus Walleij
2012-08-17 22:17 ` [RFC PATCH 4/17] input: RMI4 configs and makefiles Christopher Heiny
2012-08-27 18:39   ` Linus Walleij
2012-08-17 22:17 ` [RFC PATCH 5/17] input: rmidev character driver for RMI4 sensors Christopher Heiny
2012-08-27 18:49   ` Linus Walleij
2012-09-05  0:26     ` Christopher Heiny
2012-09-05  8:29       ` Linus Walleij
2012-08-17 22:17 ` [RFC PATCH 6/17] input: RMI4 firmware update Christopher Heiny
2012-08-27 21:01   ` Linus Walleij
2012-08-17 22:17 ` [RFC PATCH 7/17] input: RMI4 F01 device control Christopher Heiny
2012-08-27 21:59   ` Linus Walleij
2012-08-17 22:17 ` [RFC PATCH 8/17] input: RMI4 F09 Built-In Self Test Christopher Heiny
2012-08-27 22:07   ` Linus Walleij
2012-09-05  0:21     ` Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 9/17] input: RMI4 F11 multitouch sensing Christopher Heiny
2012-08-27 22:50   ` Linus Walleij
2012-08-17 22:17 ` [RFC PATCH 10/17] input: RM4 F17 Pointing sticks Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 11/17] input: RMI4 F19 capacitive buttons Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 12/17] input: RMI4 F1A simple " Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 13/17] input: RMI4 F21 Force sensing Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 14/17] input: RMI4 F30 GPIO/LED control Christopher Heiny
2012-08-27 22:58   ` Linus Walleij
2012-09-05  0:28     ` Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 15/17] input: RMI4 F34 device reflash Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 16/17] input: RMI4 F41 Active pen 2D input Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 17/17] input: RMI4 F54 analog data reporting Christopher Heiny
2012-08-27 23:01   ` Linus Walleij
2012-09-05  0:38     ` Christopher Heiny
2012-08-22 12:50 ` [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Linus Walleij
2012-08-22 21:29   ` Christopher Heiny
2012-08-27 23:20   ` Christopher Heiny
2012-08-28  0:12     ` Linus Walleij
2012-08-27 23:05 ` Linus Walleij

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).