linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
@ 2011-12-22  2:09 Christopher Heiny
  2011-12-22  2:09 ` [RFC PATCH 1/11] input: RMI4 public header file and documentation Christopher Heiny
                   ` (12 more replies)
  0 siblings, 13 replies; 34+ messages in thread
From: Christopher Heiny @ 2011-12-22  2:09 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Joerie de Gram, 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 represents a complete refactoring and rewrite of the previously
submitted patches, and supersedes all those patches.  We believe that all
outstanding issues arising from the previous submissions have been addressed.
However, because of the quantity of new code, we are submitting this patch
as an RFC and requesting feedback from the kernel community.


This patch is against the v2.6.38 tag of Linus' kernel tree, commit
521cb40b0c44418a4fd36dc633f575813d59a43d.


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
        * F19 capacitive buttons
        * F34 device reflash
        * F54 analog data reporting

    - character device access to the RMI4 register map via rmidev

    - deletion of more legacy code and behavior

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.


Due to the large amount of code, we've broken this patch into 11 parts, as
follows:
    01 - header files and documenation
    02 - core sensor and bus implementation
    03 - physical layer drivers
    04 - Kconfigs and Makefiles
    05 - rmidev
    06..11 - 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] 34+ messages in thread

* [RFC PATCH 1/11] input: RMI4 public header file and documentation.
  2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
@ 2011-12-22  2:09 ` Christopher Heiny
  2012-01-06  6:35   ` Dmitry Torokhov
  2011-12-22  2:09 ` [RFC PATCH 2/11] input: RMI4 core bus and sensor drivers Christopher Heiny
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 34+ messages in thread
From: Christopher Heiny @ 2011-12-22  2:09 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Joerie de Gram, 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>

---

 Documentation/input/rmidev.txt   |  144 ++++++++++
 Documentation/input/rmisysfs.txt |  181 +++++++++++++
 include/linux/rmi.h              |  556 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 881 insertions(+), 0 deletions(-)

diff --git a/Documentation/input/rmidev.txt b/Documentation/input/rmidev.txt
new file mode 100644
index 0000000..317fb1f
--- /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 correspond /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 while the touchscreen
+driver is enabled are not defined.
+
+
+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..dfed42e
--- /dev/null
+++ b/Documentation/input/rmisysfs.txt
@@ -0,0 +1,181 @@
+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.
+
+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.
+
+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.
+
+
+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
\ No newline at end of file
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
new file mode 100644
index 0000000..85b5956
--- /dev/null
+++ b/include/linux/rmi.h
@@ -0,0 +1,556 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _RMI_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_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
+
+#define PDT_START_SCAN_LOCATION 0x00e9
+
+enum rmi_attn_polarity {
+	RMI_ATTN_ACTIVE_LOW = 0,
+	RMI_ATTN_ACTIVE_HIGH = 1
+};
+
+/**
+ * struct rmi_f11_axis_alignmen - 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;
+};
+
+struct rmi_f19_button_map {
+	unsigned char nbuttons;
+	unsigned char *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 {
+	char *driver_name;
+	char *sensor_name;	/* Used for diagnostics. */
+
+	int attn_gpio;
+	enum rmi_attn_polarity attn_polarity;
+	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_f11_2d_axis_alignment axis_align;
+	struct rmi_f19_button_map *button_map;
+
+#ifdef	CONFIG_PM
+	void *pm_data;
+	int (*pre_suspend) (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);
+#ifdef CONFIG_HAS_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;
+
+	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
+};
+#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);
+#ifdef CONFIG_HAS_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;
+
+	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] 34+ messages in thread

* [RFC PATCH 2/11] input: RMI4 core bus and sensor drivers.
  2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
  2011-12-22  2:09 ` [RFC PATCH 1/11] input: RMI4 public header file and documentation Christopher Heiny
@ 2011-12-22  2:09 ` Christopher Heiny
  2012-01-02  6:38   ` Shubhrajyoti
  2012-01-06  2:34   ` Dmitry Torokhov
  2011-12-22  2:09 ` [RFC PATCH 3/11] input: RMI4 physical layer drivers for I2C and SPI Christopher Heiny
                   ` (10 subsequent siblings)
  12 siblings, 2 replies; 34+ messages in thread
From: Christopher Heiny @ 2011-12-22  2:09 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Joerie de Gram, 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>

---

 drivers/input/rmi4/rmi_bus.c    |  436 ++++++++++++
 drivers/input/rmi4/rmi_driver.c | 1488 +++++++++++++++++++++++++++++++++++++++
 drivers/input/rmi4/rmi_driver.h |   97 +++
 3 files changed, 2021 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..e32d4ad
--- /dev/null
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/rmi.h>
+
+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 int physical_device_count;
+
+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, "Matching %s.\n", 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_err(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)
+{
+#ifdef GENERIC_SUBSYS_PM_OPS
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm && pm->suspend)
+		return pm->suspend(dev);
+#endif
+
+	return 0;
+}
+
+static int rmi_bus_resume(struct device *dev)
+{
+#ifdef GENERIC_SUBSYS_PM_OPS
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm && pm->resume)
+		return pm->resume(dev);
+#endif
+
+	return 0;
+}
+#endif
+
+static int rmi_bus_probe(struct device *dev)
+{
+	struct rmi_driver *driver;
+	struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+	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
+};
+
+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;
+
+	mutex_lock(&rmi_bus_mutex);
+	rmi_dev->number = physical_device_count;
+	physical_device_count++;
+	mutex_unlock(&rmi_bus_mutex);
+
+	dev_set_name(&rmi_dev->dev, "sensor%02d", rmi_dev->number);
+	pr_debug("%s: Registered %s as %s.\n", __func__, pdata->sensor_name,
+		dev_name(&rmi_dev->dev));
+
+	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;
+
+	device_unregister(&rmi_dev->dev);
+	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_fh_add(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;
+
+	fh_dup = rmi_get_function_handler(fh->func);
+	if (fh_dup) {
+		pr_err("%s: function f%.2x already registered!\n", __func__,
+			fh->func);
+		return -EINVAL;
+	}
+
+	entry = kzalloc(sizeof(struct rmi_function_list), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+
+	entry->fh = fh;
+	list_add_tail(&entry->list, &rmi_supported_functions.list);
+
+	/* notify devices of the new function handler */
+	bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_add);
+
+	return 0;
+}
+EXPORT_SYMBOL(rmi_register_function_driver);
+
+static int __rmi_bus_fh_remove(struct device *dev, void *data)
+{
+	struct rmi_driver *driver;
+	struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+	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);
+
+	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;
+
+	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);
+
+	error = bus_register(&rmi_bus_type);
+	if (error < 0) {
+		pr_err("%s: error registering the RMI bus: %d\n", __func__,
+		       error);
+		return error;
+	}
+	pr_info("%s: successfully registered RMI bus.\n", __func__);
+
+	return 0;
+}
+
+static void __exit rmi_bus_exit(void)
+{
+	struct rmi_function_list *entry, *n;
+
+	list_for_each_entry_safe(entry, n, &rmi_supported_functions.list,
+				 list) {
+		list_del(&entry->list);
+		kfree(entry);
+	}
+
+	bus_unregister(&rmi_bus_type);
+}
+
+module_init(rmi_bus_init);
+module_exit(rmi_bus_exit);
+
+MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
+MODULE_DESCRIPTION("RMI bus");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
new file mode 100644
index 0000000..07097bb
--- /dev/null
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -0,0 +1,1488 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This driver adds support for generic RMI4 devices from Synpatics. It
+ * implements the mandatory f01 RMI register and depends on the presence of
+ * other required RMI functions.
+ *
+ * The RMI4 specification can be found here (URL split after files/ for
+ * style reasons):
+ * http://www.synaptics.com/sites/default/files/
+ *           511-000136-01-Rev-E-RMI4%20Intrfacing%20Guide.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/pm.h>
+#include <linux/rmi.h>
+#include "rmi_driver.h"
+
+#define DELAY_DEBUG 0
+#define REGISTER_DEBUG 0
+
+#define PDT_END_SCAN_LOCATION	0x0005
+#define PDT_PROPERTIES_LOCATION 0x00EF
+#define BSR_LOCATION 0x00FE
+#define HAS_BSR_MASK 0x20
+#define HAS_NONSTANDARD_PDT_MASK 0x40
+#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
+#define RMI4_MAX_PAGE 0xff
+#define RMI4_PAGE_SIZE 0x100
+
+#define RMI_DEVICE_RESET_CMD	0x01
+#define DEFAULT_RESET_DELAY_MS	20
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void rmi_driver_early_suspend(struct early_suspend *h);
+static void rmi_driver_late_resume(struct early_suspend *h);
+#endif
+
+/* sysfs files for attributes for driver values. */
+static ssize_t rmi_driver_hasbsr_show(struct device *dev,
+				      struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_driver_bsr_show(struct device *dev,
+				   struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_driver_bsr_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count);
+
+static ssize_t rmi_driver_enabled_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf);
+
+static ssize_t rmi_driver_enabled_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count);
+
+static ssize_t rmi_driver_phys_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf);
+
+static ssize_t rmi_driver_version_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf);
+
+#if REGISTER_DEBUG
+static ssize_t rmi_driver_reg_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count);
+#endif
+
+#if DELAY_DEBUG
+static ssize_t rmi_delay_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf);
+
+static ssize_t rmi_delay_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count);
+#endif
+
+static 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(hasbsr, RMI_RO_ATTR,
+	       rmi_driver_hasbsr_show, rmi_store_error),
+	__ATTR(bsr, RMI_RW_ATTR,
+	       rmi_driver_bsr_show, rmi_driver_bsr_store),
+	__ATTR(enabled, RMI_RW_ATTR,
+	       rmi_driver_enabled_show, rmi_driver_enabled_store),
+	__ATTR(phys, RMI_RO_ATTR,
+	       rmi_driver_phys_show, rmi_store_error),
+#if REGISTER_DEBUG
+	__ATTR(reg, RMI_WO_ATTR,
+	       rmi_show_error, rmi_driver_reg_store),
+#endif
+#if DELAY_DEBUG
+	__ATTR(delay, RMI_RW_ATTR,
+	       rmi_delay_show, rmi_delay_store),
+#endif
+	__ATTR(version, RMI_RO_ATTR,
+	       rmi_driver_version_show, rmi_store_error),
+};
+
+/* 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];
+}
+
+static bool has_bsr(struct rmi_driver_data *data)
+{
+	return (data->pdt_props & HAS_BSR_MASK) != 0;
+}
+
+/* Utility routine to set bits in a register. */
+int rmi_set_bits(struct rmi_device *rmi_dev, unsigned short address,
+		 unsigned char bits)
+{
+	unsigned char reg_contents;
+	int retval;
+
+	retval = rmi_read_block(rmi_dev, address, &reg_contents, 1);
+	if (retval)
+		return retval;
+	reg_contents = reg_contents | bits;
+	retval = rmi_write_block(rmi_dev, address, &reg_contents, 1);
+	if (retval == 1)
+		return 0;
+	else if (retval == 0)
+		return -EIO;
+	return retval;
+}
+EXPORT_SYMBOL(rmi_set_bits);
+
+/* Utility routine to clear bits in a register. */
+int rmi_clear_bits(struct rmi_device *rmi_dev, unsigned short address,
+		   unsigned char bits)
+{
+	unsigned char reg_contents;
+	int retval;
+
+	retval = rmi_read_block(rmi_dev, address, &reg_contents, 1);
+	if (retval)
+		return retval;
+	reg_contents = reg_contents & ~bits;
+	retval = rmi_write_block(rmi_dev, address, &reg_contents, 1);
+	if (retval == 1)
+		return 0;
+	else if (retval == 0)
+		return -EIO;
+	return retval;
+}
+EXPORT_SYMBOL(rmi_clear_bits);
+
+static void rmi_free_function_list(struct rmi_device *rmi_dev)
+{
+	struct rmi_function_container *entry, *n;
+	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+	if (!data) {
+		dev_err(&rmi_dev->dev, "WTF: No driver data in %s\n", __func__);
+		return;
+	}
+
+	if (list_empty(&data->rmi_functions.list))
+		return;
+
+	list_for_each_entry_safe(entry, n, &data->rmi_functions.list, list) {
+		kfree(entry->irq_mask);
+		list_del(&entry->list);
+	}
+}
+
+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, "%s: Register F%02X.\n", __func__,
+			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;
+	}
+
+	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;
+				init_one_function(rmi_dev, entry);
+			}
+		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);
+
+	list_for_each_entry_safe(entry, temp, &data->rmi_functions.list,
+									list) {
+		if (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 error;
+
+	/* Device control (F01) is handled before anything else. */
+
+	if (data->f01_container->fh->reset) {
+		error = data->f01_container->fh->reset(data->f01_container);
+		if (error < 0) {
+			dev_err(dev, "%s: f%.2x"
+					" reset handler failed:"
+					" %d\n", __func__,
+					data->f01_container->fh->func, error);
+			return error;
+		}
+	}
+
+	list_for_each_entry(entry, &data->rmi_functions.list, list) {
+		if (entry->fh->reset) {
+			error = entry->fh->reset(entry);
+			if (error < 0) {
+				dev_err(dev, "%s: f%.2x"
+						" reset handler failed:"
+						" %d\n", __func__,
+						entry->fh->func, error);
+				return error;
+			}
+		}
+
+	}
+
+	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 error;
+
+	/* Device control (F01) is handled before anything else. */
+
+	if (data->f01_container->fh->config) {
+		error = data->f01_container->fh->config(data->f01_container);
+		if (error < 0) {
+			dev_err(dev, "%s: f%.2x"
+					" config handler failed:"
+					" %d\n", __func__,
+					data->f01_container->fh->func, error);
+			return error;
+		}
+	}
+
+	list_for_each_entry(entry, &data->rmi_functions.list, list) {
+		if (entry->fh->config) {
+			error = entry->fh->config(entry);
+			if (error < 0) {
+				dev_err(dev, "%s: f%.2x"
+						" config handler failed:"
+						" %d\n", __func__,
+						entry->fh->func, error);
+				return error;
+			}
+		}
+	}
+
+	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, "%s: failed to read irqs.", __func__);
+		return error;
+	}
+	/* Device control (F01) is handled before anything else. */
+	u8_and(irq_bits, irq_status, data->f01_container->irq_mask,
+			data->num_of_irq_regs);
+	if (u8_is_any_set(irq_bits, data->num_of_irq_regs))
+		data->f01_container->fh->attention(
+				data->f01_container, irq_bits);
+
+	u8_and(irq_status, irq_status, data->current_irq_mask,
+	       data->num_of_irq_regs);
+	/* At this point, irq_status has all bits that are set in the
+	 * interrupt status register and are enabled.
+	 */
+
+	list_for_each_entry(entry, &data->rmi_functions.list, list)
+		if (entry->irq_mask && entry->fh && entry->fh->attention) {
+			u8_and(irq_bits, irq_status, entry->irq_mask,
+			       data->num_of_irq_regs);
+			if (u8_is_any_set(irq_bits, data->num_of_irq_regs)) {
+				error = entry->fh->attention(entry, irq_bits);
+				if (error < 0)
+					dev_err(dev, "%s: f%.2x"
+						" attention handler failed:"
+						" %d\n", __func__,
+						entry->fh->func, error);
+			}
+		}
+
+	return 0;
+}
+
+static int rmi_driver_irq_handler(struct rmi_device *rmi_dev, int irq)
+{
+	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+	/* Can get called before the driver is fully ready to deal with
+	 * interrupts.
+	 */
+	if (!data || !data->f01_container || !data->f01_container->fh) {
+		dev_warn(&rmi_dev->dev,
+			 "Not ready to handle interrupts yet!\n");
+		return 0;
+	}
+
+	return process_interrupt_requests(rmi_dev);
+}
+
+
+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;
+	}
+
+	dev_err(&rmi_dev->dev, "DEBUG 5!\n");
+
+	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 = kzalloc(sizeof(u8)*data->num_of_irq_regs, GFP_KERNEL);
+	if (irq_mask)
+		construct_mask(irq_mask, fc->num_of_irqs, fc->irq_pos);
+
+	return irq_mask;
+}
+
+/*
+ * This pair of functions allows functions like function 54 to request to have
+ * other interupts disabled until the restore function is called. Only one store
+ * happens at a time.
+ */
+static int rmi_driver_irq_save(struct rmi_device *rmi_dev, u8 * new_ints)
+{
+	int retval = 0;
+	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+	struct device *dev = &rmi_dev->dev;
+
+	mutex_lock(&data->irq_mutex);
+	if (!data->irq_stored) {
+		/* Save current enabled interupts */
+		retval = rmi_read_block(rmi_dev,
+				data->f01_container->fd.control_base_addr+1,
+				data->irq_mask_store, data->num_of_irq_regs);
+		if (retval < 0) {
+			dev_err(dev, "%s: Failed to read enabled interrupts!",
+								__func__);
+			goto error_unlock;
+		}
+		/*
+		 * Disable every interupt except for function 54
+		 * TODO:Will also want to not disable function 1-like functions.
+		 * No need to take care of this now, since there's no good way
+		 * to identify them.
+		 */
+		retval = rmi_write_block(rmi_dev,
+				data->f01_container->fd.control_base_addr+1,
+				new_ints, data->num_of_irq_regs);
+		if (retval < 0) {
+			dev_err(dev, "%s: Failed to change enabled interrupts!",
+								__func__);
+			goto error_unlock;
+		}
+		memcpy(data->current_irq_mask, new_ints,
+					data->num_of_irq_regs * sizeof(u8));
+		data->irq_stored = true;
+	} else {
+		retval = -ENOSPC; /* No space to store IRQs.*/
+		dev_err(dev, "%s: Attempted to save values when"
+						" already stored!", __func__);
+	}
+
+error_unlock:
+	mutex_unlock(&data->irq_mutex);
+	return retval;
+}
+
+static int rmi_driver_irq_restore(struct rmi_device *rmi_dev)
+{
+	int retval = 0;
+	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+	struct device *dev = &rmi_dev->dev;
+	mutex_lock(&data->irq_mutex);
+
+	if (data->irq_stored) {
+		retval = rmi_write_block(rmi_dev,
+				data->f01_container->fd.control_base_addr+1,
+				data->irq_mask_store, data->num_of_irq_regs);
+		if (retval < 0) {
+			dev_err(dev, "%s: Failed to write enabled interupts!",
+								__func__);
+			goto error_unlock;
+		}
+		memcpy(data->current_irq_mask, data->irq_mask_store,
+					data->num_of_irq_regs * sizeof(u8));
+		data->irq_stored = false;
+	} else {
+		retval = -EINVAL;
+		dev_err(dev, "%s: Attempted to restore values when not stored!",
+			__func__);
+	}
+
+error_unlock:
+	mutex_unlock(&data->irq_mutex);
+	return retval;
+}
+
+static int rmi_driver_fn_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_info(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);
+		kfree(fc);
+		goto error_free_data;
+	}
+
+	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;
+	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);
+
+	dev_info(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, "%s: Register F01.\n", __func__);
+	retval = device_register(&fc->dev);
+	if (retval) {
+		dev_err(dev, "%s: Failed device_register for F01.\n", __func__);
+		goto error_free_data;
+	}
+
+	data->f01_container = fc;
+
+	return retval;
+
+error_free_data:
+	kfree(fc);
+	return retval;
+}
+
+/*
+ * Scan the PDT for F01 so we can force a reset before anything else
+ * is done.  This forces the sensor into a known state, and also
+ * forces application of any pending updates from reflashing the
+ * firmware or configuration.  We have to do this before actually
+ * building the PDT because the reflash might cause various registers
+ * to move around.
+ */
+static int do_initial_reset(struct rmi_device *rmi_dev)
+{
+	struct pdt_entry pdt_entry;
+	int page;
+	struct device *dev = &rmi_dev->dev;
+	bool done = false;
+	int i;
+	int retval;
+	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);
+				return 0;
+			}
+		}
+	}
+
+	dev_warn(dev, "WARNING: Failed to find F01 for initial reset.\n");
+	return -ENODEV;
+}
+
+static int rmi_scan_pdt(struct rmi_device *rmi_dev)
+{
+	struct rmi_driver_data *data;
+	struct pdt_entry pdt_entry;
+	int page;
+	struct device *dev = &rmi_dev->dev;
+	int irq_count = 0;
+	bool done = false;
+	int i;
+	int retval;
+
+	dev_info(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;
+		}
+	}
+	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 error = 0;
+	struct device *dev = &rmi_dev->dev;
+	int attr_count = 0;
+
+	dev_dbg(dev, "%s: Starting probe.\n", __func__);
+
+	pdata = to_rmi_platform_data(rmi_dev);
+
+	data = kzalloc(sizeof(struct rmi_driver_data), GFP_KERNEL);
+	if (!data) {
+		dev_err(dev, "%s: Failed to allocate driver data.\n", __func__);
+		return -ENOMEM;
+	}
+	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;
+	error = do_initial_reset(rmi_dev);
+	if (error)
+		dev_warn(dev, "RMI initial reset failed! Soldiering on.\n");
+
+
+	error = rmi_scan_pdt(rmi_dev);
+	if (error) {
+		dev_err(dev, "PDT scan for %s failed with code %d.\n",
+			pdata->sensor_name, error);
+		goto err_free_data;
+	}
+
+	if (!data->f01_container) {
+		dev_err(dev, "missing F01 container!\n");
+		error = -EINVAL;
+		goto err_free_data;
+	}
+
+	data->f01_container->irq_mask = kzalloc(
+			sizeof(u8)*data->num_of_irq_regs, GFP_KERNEL);
+	if (!data->f01_container->irq_mask) {
+		dev_err(dev, "Failed to allocate F01 IRQ mask.\n");
+		error = -ENOMEM;
+		goto err_free_data;
+	}
+	construct_mask(data->f01_container->irq_mask,
+		       data->f01_container->num_of_irqs,
+		       data->f01_container->irq_pos);
+	list_for_each_entry(fc, &data->rmi_functions.list, list)
+		fc->irq_mask = rmi_driver_irq_get_mask(rmi_dev, fc);
+
+	error = rmi_driver_f01_init(rmi_dev);
+	if (error < 0) {
+		dev_err(dev, "Failed to initialize F01.\n");
+		goto err_free_data;
+	}
+
+	error = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION,
+			 (char *) &data->pdt_props);
+	if (error < 0) {
+		/* we'll print out a warning and continue since
+		 * failure to get the PDT properties is not a cause to fail
+		 */
+		dev_warn(dev, "Could not read PDT properties from 0x%04x. "
+			 "Assuming 0x00.\n", PDT_PROPERTIES_LOCATION);
+	}
+
+	dev_dbg(dev, "%s: Creating sysfs files.", __func__);
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+		error = device_create_file(dev, &attrs[attr_count]);
+		if (error < 0) {
+			dev_err(dev, "%s: Failed to create sysfs file %s.\n",
+				__func__, attrs[attr_count].attr.name);
+			goto err_free_data;
+		}
+	}
+
+	__mutex_init(&data->irq_mutex, "irq_mutex", &data->irq_key);
+	data->current_irq_mask = kzalloc(sizeof(u8)*data->num_of_irq_regs,
+					 GFP_KERNEL);
+	if (!data->current_irq_mask) {
+		dev_err(dev, "Failed to allocate current_irq_mask.\n");
+		error = -ENOMEM;
+		goto err_free_data;
+	}
+	error = rmi_read_block(rmi_dev,
+				data->f01_container->fd.control_base_addr+1,
+				data->current_irq_mask, data->num_of_irq_regs);
+	if (error < 0) {
+		dev_err(dev, "%s: Failed to read current IRQ mask.\n",
+			__func__);
+		goto err_free_data;
+	}
+	data->irq_mask_store = kzalloc(sizeof(u8)*data->num_of_irq_regs,
+				       GFP_KERNEL);
+	if (!data->irq_mask_store) {
+		dev_err(dev, "Failed to allocate mask store.\n");
+		error = -ENOMEM;
+		goto err_free_data;
+	}
+
+#ifdef	CONFIG_PM
+	data->pm_data = pdata->pm_data;
+	data->pre_suspend = pdata->pre_suspend;
+	data->post_resume = pdata->post_resume;
+
+	mutex_init(&data->suspend_mutex);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	rmi_dev->early_suspend_handler.level =
+		EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+	rmi_dev->early_suspend_handler.suspend = rmi_driver_early_suspend;
+	rmi_dev->early_suspend_handler.resume = rmi_driver_late_resume;
+	register_early_suspend(&rmi_dev->early_suspend_handler);
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+#endif /* CONFIG_PM */
+	data->enabled = true;
+
+	dev_info(dev, "connected RMI device manufacturer: %s product: %s\n",
+		 data->manufacturer_id == 1 ? "synaptics" : "unknown",
+		 data->product_id);
+
+	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) {
+		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 error;
+}
+
+#ifdef CONFIG_PM
+static int rmi_driver_suspend(struct device *dev)
+{
+	struct rmi_device *rmi_dev;
+	struct rmi_driver_data *data;
+	struct rmi_function_container *entry;
+	int retval = 0;
+
+	rmi_dev = to_rmi_device(dev);
+	data = rmi_get_driverdata(rmi_dev);
+
+	mutex_lock(&data->suspend_mutex);
+	if (data->suspended)
+		goto exit;
+
+#ifndef	CONFIG_HAS_EARLYSUSPEND
+	if (data->pre_suspend) {
+		retval = data->pre_suspend(data->pm_data);
+		if (retval)
+			goto exit;
+	}
+#endif  /* !CONFIG_HAS_EARLYSUSPEND */
+
+	list_for_each_entry(entry, &data->rmi_functions.list, list)
+		if (entry->fh && entry->fh->suspend) {
+			retval = entry->fh->suspend(entry);
+			if (retval < 0)
+				goto exit;
+		}
+
+	if (data->f01_container && data->f01_container->fh
+				&& data->f01_container->fh->suspend) {
+		retval = data->f01_container->fh->suspend(data->f01_container);
+		if (retval < 0)
+			goto exit;
+	}
+	data->suspended = true;
+
+exit:
+	mutex_unlock(&data->suspend_mutex);
+	return retval;
+}
+
+static int rmi_driver_resume(struct device *dev)
+{
+	struct rmi_device *rmi_dev;
+	struct rmi_driver_data *data;
+	struct rmi_function_container *entry;
+	int retval = 0;
+
+	rmi_dev = to_rmi_device(dev);
+	data = rmi_get_driverdata(rmi_dev);
+
+	mutex_lock(&data->suspend_mutex);
+	if (!data->suspended)
+		goto exit;
+
+	if (data->f01_container && data->f01_container->fh
+				&& data->f01_container->fh->resume) {
+		retval = data->f01_container->fh->resume(data->f01_container);
+		if (retval < 0)
+			goto exit;
+	}
+
+	list_for_each_entry(entry, &data->rmi_functions.list, list)
+		if (entry->fh && entry->fh->resume) {
+			retval = entry->fh->resume(entry);
+			if (retval < 0)
+				goto exit;
+		}
+
+#ifndef	CONFIG_HAS_EARLYSUSPEND
+	if (data->post_resume) {
+		retval = data->post_resume(data->pm_data);
+		if (retval)
+			goto exit;
+	}
+#endif  /* !CONFIG_HAS_EARLYSUSPEND */
+
+	data->suspended = false;
+
+exit:
+	mutex_unlock(&data->suspend_mutex);
+	return retval;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void rmi_driver_early_suspend(struct early_suspend *h)
+{
+	struct rmi_device *rmi_dev =
+	    container_of(h, struct rmi_device, early_suspend_handler);
+	struct rmi_driver_data *data;
+	struct rmi_function_container *entry;
+	int retval = 0;
+
+	data = rmi_get_driverdata(rmi_dev);
+	dev_dbg(&rmi_dev->dev, "Early suspend.\n");
+
+	mutex_lock(&data->suspend_mutex);
+	if (data->suspended)
+		goto exit;
+
+	if (data->pre_suspend) {
+		retval = data->pre_suspend(data->pm_data);
+		if (retval)
+			goto exit;
+	}
+
+	list_for_each_entry(entry, &data->rmi_functions.list, list)
+		if (entry->fh && entry->fh->early_suspend) {
+			retval = entry->fh->early_suspend(entry);
+			if (retval < 0)
+				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)
+			goto exit;
+	}
+	data->suspended = true;
+
+exit:
+	mutex_unlock(&data->suspend_mutex);
+}
+
+static void rmi_driver_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);
+	dev_dbg(&rmi_dev->dev, "Late resume.\n");
+
+	mutex_lock(&data->suspend_mutex);
+	if (!data->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)
+			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)
+				goto exit;
+		}
+
+	if (data->post_resume) {
+		retval = data->post_resume(data->pm_data);
+		if (retval)
+			goto exit;
+	}
+
+	data->suspended = false;
+
+exit:
+	mutex_unlock(&data->suspend_mutex);
+}
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+#endif /* CONFIG_PM */
+
+static int __devexit rmi_driver_remove(struct rmi_device *rmi_dev)
+{
+	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+	struct rmi_function_container *entry;
+	int i;
+
+	list_for_each_entry(entry, &data->rmi_functions.list, list)
+		if (entry->fh && entry->fh->remove)
+			entry->fh->remove(entry);
+
+	rmi_free_function_list(rmi_dev);
+	for (i = 0; i < ARRAY_SIZE(attrs); i++)
+		device_remove_file(&rmi_dev->dev, &attrs[i]);
+	kfree(data->f01_container->irq_mask);
+	kfree(data->irq_mask_store);
+	kfree(data->current_irq_mask);
+	kfree(data);
+
+	return 0;
+}
+
+#ifdef UNIVERSAL_DEV_PM_OPS
+static UNIVERSAL_DEV_PM_OPS(rmi_driver_pm, rmi_driver_suspend,
+			    rmi_driver_resume, NULL);
+#endif
+
+static struct rmi_driver sensor_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "rmi-generic",
+#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_hasbsr_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct rmi_device *rmi_dev;
+	struct rmi_driver_data *data;
+	rmi_dev = to_rmi_device(dev);
+	data = rmi_get_driverdata(rmi_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", has_bsr(data));
+}
+
+static ssize_t rmi_driver_bsr_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct rmi_device *rmi_dev;
+	struct rmi_driver_data *data;
+	rmi_dev = to_rmi_device(dev);
+	data = rmi_get_driverdata(rmi_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", data->bsr);
+}
+
+static ssize_t rmi_driver_bsr_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	int retval;
+	unsigned long val;
+	struct rmi_device *rmi_dev;
+	struct rmi_driver_data *data;
+
+	rmi_dev = to_rmi_device(dev);
+	data = rmi_get_driverdata(rmi_dev);
+
+	/* need to convert the string data to an actual value */
+	retval = strict_strtoul(buf, 10, &val);
+	if (retval < 0) {
+		dev_err(dev, "Invalid value '%s' written to BSR.\n", buf);
+		return -EINVAL;
+	}
+
+	retval = rmi_write(rmi_dev, BSR_LOCATION, (unsigned char)val);
+	if (retval) {
+		dev_err(dev, "%s : failed to write bsr %u to 0x%x\n",
+			__func__, (unsigned int)val, BSR_LOCATION);
+		return retval;
+	}
+
+	data->bsr = val;
+
+	return count;
+}
+
+static void disable_sensor(struct rmi_device *rmi_dev)
+{
+	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+	rmi_dev->phys->disable_device(rmi_dev->phys);
+
+	data->enabled = false;
+}
+
+static int enable_sensor(struct rmi_device *rmi_dev)
+{
+	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+	int retval = 0;
+
+	retval = rmi_dev->phys->enable_device(rmi_dev->phys);
+	/* non-zero means error occurred */
+	if (retval)
+		return retval;
+
+	data->enabled = true;
+
+	return 0;
+}
+
+static ssize_t rmi_driver_enabled_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct rmi_device *rmi_dev;
+	struct rmi_driver_data *data;
+
+	rmi_dev = to_rmi_device(dev);
+	data = rmi_get_driverdata(rmi_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", data->enabled);
+}
+
+static ssize_t rmi_driver_enabled_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	int retval;
+	int new_value;
+	struct rmi_device *rmi_dev;
+	struct rmi_driver_data *data;
+
+	rmi_dev = to_rmi_device(dev);
+	data = rmi_get_driverdata(rmi_dev);
+
+	if (sysfs_streq(buf, "0"))
+		new_value = false;
+	else if (sysfs_streq(buf, "1"))
+		new_value = true;
+	else
+		return -EINVAL;
+
+	if (new_value) {
+		retval = enable_sensor(rmi_dev);
+		if (retval) {
+			dev_err(dev, "Failed to enable sensor, code=%d.\n",
+				retval);
+			return -EIO;
+		}
+	} else {
+		disable_sensor(rmi_dev);
+	}
+
+	return count;
+}
+
+#if REGISTER_DEBUG
+static ssize_t rmi_driver_reg_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	int retval;
+	unsigned int address;
+	unsigned int bytes;
+	struct rmi_device *rmi_dev;
+	struct rmi_driver_data *data;
+	u8 readbuf[128];
+	unsigned char outbuf[512];
+	unsigned char *bufptr = outbuf;
+	int i;
+
+	rmi_dev = to_rmi_device(dev);
+	data = rmi_get_driverdata(rmi_dev);
+
+	retval = sscanf(buf, "%x %u", &address, &bytes);
+	if (retval != 2) {
+		dev_err(dev, "Invalid input (code %d) for reg store: %s",
+			retval, buf);
+		return -EINVAL;
+	}
+	if (address < 0 || address > 0xFFFF) {
+		dev_err(dev, "Invalid address for reg store '%#06x'.\n",
+			address);
+		return -EINVAL;
+	}
+	if (bytes < 0 || bytes >= sizeof(readbuf) || address+bytes > 65535) {
+		dev_err(dev, "Invalid byte count for reg store '%d'.\n",
+			bytes);
+		return -EINVAL;
+	}
+
+	retval = rmi_read_block(rmi_dev, address, readbuf, bytes);
+	if (retval != bytes) {
+		dev_err(dev, "Failed to read %d registers at %#06x, code %d.\n",
+			bytes, address, retval);
+		return retval;
+	}
+
+	dev_info(dev, "Reading %d bytes from %#06x.\n", bytes, address);
+	for (i = 0; i < bytes; i++) {
+		retval = snprintf(bufptr, 4, "%02X ", readbuf[i]);
+		if (retval < 0) {
+			dev_err(dev, "Failed to format string. Code: %d",
+				retval);
+			return retval;
+		}
+		bufptr += retval;
+	}
+	dev_info(dev, "%s\n", outbuf);
+
+	return count;
+}
+#endif
+
+#if DELAY_DEBUG
+static ssize_t rmi_delay_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	int retval;
+	struct rmi_device *rmi_dev;
+	struct rmi_device_platform_data *pdata;
+	unsigned int new_read_delay;
+	unsigned int new_write_delay;
+	unsigned int new_block_delay;
+	unsigned int new_pre_delay;
+	unsigned int new_post_delay;
+
+	retval = sscanf(buf, "%u %u %u %u %u", &new_read_delay,
+			&new_write_delay, &new_block_delay,
+			&new_pre_delay, &new_post_delay);
+	if (retval != 5) {
+		dev_err(dev, "Incorrect number of values provided for delay.");
+		return -EINVAL;
+	}
+	if (new_read_delay < 0) {
+		dev_err(dev, "Byte delay must be positive microseconds.\n");
+		return -EINVAL;
+	}
+	if (new_write_delay < 0) {
+		dev_err(dev, "Write delay must be positive microseconds.\n");
+		return -EINVAL;
+	}
+	if (new_block_delay < 0) {
+		dev_err(dev, "Block delay must be positive microseconds.\n");
+		return -EINVAL;
+	}
+	if (new_pre_delay < 0) {
+		dev_err(dev,
+			"Pre-transfer delay must be positive microseconds.\n");
+		return -EINVAL;
+	}
+	if (new_post_delay < 0) {
+		dev_err(dev,
+			"Post-transfer delay must be positive microseconds.\n");
+		return -EINVAL;
+	}
+
+	rmi_dev = to_rmi_device(dev);
+	pdata = rmi_dev->phys->dev->platform_data;
+
+	dev_info(dev, "Setting delays to %u %u %u %u %u.\n", new_read_delay,
+		 new_write_delay, new_block_delay, new_pre_delay,
+		 new_post_delay);
+	pdata->spi_data.read_delay_us = new_read_delay;
+	pdata->spi_data.write_delay_us = new_write_delay;
+	pdata->spi_data.block_delay_us = new_block_delay;
+	pdata->spi_data.pre_delay_us = new_pre_delay;
+	pdata->spi_data.post_delay_us = new_post_delay;
+
+	return count;
+}
+
+static ssize_t rmi_delay_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct rmi_device *rmi_dev;
+	struct rmi_device_platform_data *pdata;
+
+	rmi_dev = to_rmi_device(dev);
+	pdata = rmi_dev->phys->dev->platform_data;
+
+	return snprintf(buf, PAGE_SIZE, "%d %d %d %d %d\n",
+		pdata->spi_data.read_delay_us, pdata->spi_data.write_delay_us,
+		pdata->spi_data.block_delay_us,
+		pdata->spi_data.pre_delay_us, pdata->spi_data.post_delay_us);
+}
+#endif
+
+static ssize_t rmi_driver_phys_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct rmi_device *rmi_dev;
+	struct rmi_phys_info *info;
+
+	rmi_dev = to_rmi_device(dev);
+	info = &rmi_dev->phys->info;
+
+	return snprintf(buf, PAGE_SIZE, "%-5s %ld %ld %ld %ld %ld %ld %ld\n",
+		 info->proto ? info->proto : "unk",
+		 info->tx_count, info->tx_bytes, info->tx_errs,
+		 info->rx_count, info->rx_bytes, info->rx_errs,
+		 info->attn_count);
+}
+
+static ssize_t rmi_driver_version_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+		 RMI_DRIVER_VERSION_STRING);
+}
+
+static int __init rmi_driver_init(void)
+{
+	return rmi_register_driver(&sensor_driver);
+}
+
+static void __exit rmi_driver_exit(void)
+{
+	rmi_unregister_driver(&sensor_driver);
+}
+
+module_init(rmi_driver_init);
+module_exit(rmi_driver_exit);
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com");
+MODULE_DESCRIPTION("RMI generic driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
new file mode 100644
index 0000000..dc0d9c5
--- /dev/null
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _RMI_DRIVER_H
+#define _RMI_DRIVER_H
+
+#define RMI_DRIVER_MAJOR_VERSION     1
+#define RMI_DRIVER_MINOR_VERSION     3
+#define RMI_DRIVER_SUB_MINOR_VERSION 0
+
+/* TODO: Figure out some way to construct this string in the define macro
+ * using the values defined above.
+ */
+#define RMI_DRIVER_VERSION_STRING "1.3.0"
+
+
+#define RMI_PRODUCT_ID_LENGTH    10
+#define RMI_PRODUCT_INFO_LENGTH   2
+#define RMI_DATE_CODE_LENGTH      3
+
+struct rmi_driver_data {
+	struct rmi_function_container rmi_functions;
+
+	struct rmi_function_container *f01_container;
+
+	int num_of_irq_regs;
+	u8 *current_irq_mask;
+	u8 *irq_mask_store;
+	bool irq_stored;
+	struct mutex irq_mutex;
+	struct lock_class_key irq_key;
+	struct mutex pdt_mutex;
+
+	unsigned char pdt_props;
+	unsigned char bsr;
+	bool enabled;
+
+	u8 manufacturer_id;
+	/* product id + null termination */
+	u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
+
+#ifdef CONFIG_PM
+	bool suspended;
+	struct mutex suspend_mutex;
+
+	void *pm_data;
+	int (*pre_suspend) (const void *pm_data);
+	int (*post_resume) (const void *pm_data);
+#endif
+
+	void *data;
+};
+
+struct pdt_entry {
+	u8 query_base_addr:8;
+	u8 command_base_addr:8;
+	u8 control_base_addr:8;
+	u8 data_base_addr:8;
+	u8 interrupt_source_count:3;
+	u8 bits3and4:2;
+	u8 function_version:2;
+	u8 bit7:1;
+	u8 function_number:8;
+};
+
+int rmi_driver_f01_init(struct rmi_device *rmi_dev);
+
+static inline void copy_pdt_entry_to_fd(struct pdt_entry *pdt,
+				 struct rmi_function_descriptor *fd,
+				 u16 page_start)
+{
+	fd->query_base_addr = pdt->query_base_addr + page_start;
+	fd->command_base_addr = pdt->command_base_addr + page_start;
+	fd->control_base_addr = pdt->control_base_addr + page_start;
+	fd->data_base_addr = pdt->data_base_addr + page_start;
+	fd->function_number = pdt->function_number;
+	fd->interrupt_source_count = pdt->interrupt_source_count;
+	fd->function_version = pdt->function_version;
+}
+
+#endif
+

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

* [RFC PATCH 3/11] input: RMI4 physical layer drivers for I2C and SPI
  2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
  2011-12-22  2:09 ` [RFC PATCH 1/11] input: RMI4 public header file and documentation Christopher Heiny
  2011-12-22  2:09 ` [RFC PATCH 2/11] input: RMI4 core bus and sensor drivers Christopher Heiny
@ 2011-12-22  2:09 ` Christopher Heiny
  2011-12-22  2:09 ` [RFC PATCH 4/11] input: RMI4 KConfigs and Makefiles Christopher Heiny
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2011-12-22  2:09 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Joerie de Gram, 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>

---

 drivers/input/rmi4/rmi_i2c.c |  428 +++++++++++++++++++++
 drivers/input/rmi4/rmi_spi.c |  875 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1303 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..88b4a11
--- /dev/null
+++ b/drivers/input/rmi4/rmi_i2c.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/lockdep.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/rmi.h>
+
+#define COMMS_DEBUG 0
+
+#define IRQ_DEBUG 0
+
+#define RMI_PAGE_SELECT_REGISTER 0xff
+#define RMI_I2C_PAGE(addr) (((addr) >> 8) & 0xff)
+
+static char *phys_proto_name = "i2c";
+
+struct rmi_i2c_data {
+	struct mutex page_mutex;
+	int page;
+	int enabled;
+	int irq;
+	int irq_flags;
+	struct rmi_phys_device *phys;
+};
+
+static irqreturn_t rmi_i2c_irq_thread(int irq, void *p)
+{
+	struct rmi_phys_device *phys = p;
+	struct rmi_device *rmi_dev = phys->rmi_dev;
+	struct rmi_driver *driver = rmi_dev->driver;
+	struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+
+#if IRQ_DEBUG
+	dev_dbg(phys->dev, "ATTN gpio, value: %d.\n",
+			gpio_get_value(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
+	int i;
+#endif
+
+	txbuf[0] = addr & 0xff;
+	memcpy(txbuf + 1, buf, len);
+
+	mutex_lock(&data->page_mutex);
+
+	if (RMI_I2C_PAGE(addr) != data->page) {
+		retval = rmi_set_page(phys, RMI_I2C_PAGE(addr));
+		if (retval < 0)
+			goto exit;
+	}
+
+#if COMMS_DEBUG
+	dev_dbg(&client->dev, "RMI4 I2C writes %d bytes: ", sizeof(txbuf));
+	for (i = 0; i < sizeof(txbuf); i++)
+		dev_dbg(&client->dev, "%02x ", txbuf[i]);
+	dev_dbg(&client->dev, "\n");
+#endif
+
+	phys->info.tx_count++;
+	phys->info.tx_bytes += sizeof(txbuf);
+	retval = i2c_master_send(client, txbuf, sizeof(txbuf));
+	if (retval < 0)
+		phys->info.tx_errs++;
+
+exit:
+	mutex_unlock(&data->page_mutex);
+	return retval;
+}
+
+static int rmi_i2c_write(struct rmi_phys_device *phys, u16 addr, u8 data)
+{
+	int retval = rmi_i2c_write_block(phys, addr, &data, 1);
+	return (retval < 0) ? retval : 0;
+}
+
+static int rmi_i2c_read_block(struct rmi_phys_device *phys, u16 addr, u8 *buf,
+			      int len)
+{
+	struct i2c_client *client = to_i2c_client(phys->dev);
+	struct rmi_i2c_data *data = phys->data;
+	u8 txbuf[1] = {addr & 0xff};
+	int retval;
+#if	COMMS_DEBUG
+	int i;
+#endif
+
+	mutex_lock(&data->page_mutex);
+
+	if (RMI_I2C_PAGE(addr) != data->page) {
+		retval = rmi_set_page(phys, RMI_I2C_PAGE(addr));
+		if (retval < 0)
+			goto exit;
+	}
+
+#if COMMS_DEBUG
+	dev_dbg(&client->dev, "RMI4 I2C writes 1 bytes: %02x\n", txbuf[0]);
+#endif
+	phys->info.tx_count++;
+	phys->info.tx_bytes += sizeof(txbuf);
+	retval = i2c_master_send(client, txbuf, sizeof(txbuf));
+	if (retval != sizeof(txbuf)) {
+		phys->info.tx_errs++;
+		retval = (retval < 0) ? retval : -EIO;
+		goto exit;
+	}
+
+	retval = i2c_master_recv(client, buf, len);
+
+	phys->info.rx_count++;
+	phys->info.rx_bytes += len;
+	if (retval < 0)
+		phys->info.rx_errs++;
+#if COMMS_DEBUG
+	else {
+		dev_dbg(&client->dev, "RMI4 I2C received %d bytes: ", len);
+		for (i = 0; i < len; i++)
+			dev_dbg(&client->dev, "%02x ", buf[i]);
+		dev_dbg(&client->dev, "\n");
+	}
+#endif
+
+exit:
+	mutex_unlock(&data->page_mutex);
+	return retval;
+}
+
+static int rmi_i2c_read(struct rmi_phys_device *phys, u16 addr, u8 *buf)
+{
+	int retval = rmi_i2c_read_block(phys, addr, buf, 1);
+	return (retval < 0) ? retval : 0;
+}
+
+
+static int acquire_attn_irq(struct rmi_i2c_data *data)
+{
+	return request_threaded_irq(data->irq, NULL, rmi_i2c_irq_thread,
+			data->irq_flags, dev_name(data->phys->dev), data->phys);
+}
+
+static int enable_device(struct rmi_phys_device *phys)
+{
+	int retval = 0;
+
+	struct rmi_i2c_data *data = phys->data;
+
+	if (data->enabled)
+		return 0;
+
+	retval = acquire_attn_irq(data);
+	if (retval)
+		goto error_exit;
+
+	data->enabled = true;
+	dev_dbg(phys->dev, "Physical device enabled.\n");
+	return 0;
+
+error_exit:
+	dev_err(phys->dev, "Failed to enable physical device. Code=%d.\n",
+		retval);
+	return retval;
+}
+
+
+static void disable_device(struct rmi_phys_device *phys)
+{
+	struct rmi_i2c_data *data = phys->data;
+
+	if (!data->enabled)
+		return;
+
+	disable_irq(data->irq);
+	free_irq(data->irq, data->phys);
+
+	dev_dbg(phys->dev, "Physical device disabled.\n");
+	data->enabled = false;
+}
+
+
+static int __devinit rmi_i2c_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	struct rmi_phys_device *rmi_phys;
+	struct rmi_i2c_data *data;
+	struct rmi_device_platform_data *pdata = client->dev.platform_data;
+	int error;
+
+	if (!pdata) {
+		dev_err(&client->dev, "no platform data\n");
+		return -EINVAL;
+	}
+	pr_info("%s: Probing %s (IRQ %d).\n", __func__,
+		pdata->sensor_name ? pdata->sensor_name : "-no name-",
+		pdata->attn_gpio);
+
+	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);
+	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;
+	}
+
+	if (pdata->gpio_config) {
+		error = pdata->gpio_config(pdata->gpio_data, true);
+		if (error < 0) {
+			dev_err(&client->dev, "failed to setup irq %d\n",
+				pdata->attn_gpio);
+			goto err_data;
+		}
+	}
+
+	error = rmi_register_phys_device(rmi_phys);
+	if (error) {
+		dev_err(&client->dev,
+			"failed to register physical driver at 0x%.2X.\n",
+			client->addr);
+		goto err_data;
+	}
+	i2c_set_clientdata(client, rmi_phys);
+
+	if (pdata->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, "%s: WARNING: Failed to "
+				 "export ATTN gpio!\n", __func__);
+		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), "%s: WARNING: "
+				"Failed to symlink ATTN gpio!\n", __func__);
+			error = 0;
+		} else {
+			dev_info(&(rmi_phys->rmi_dev->dev),
+				"%s: Exported GPIO %d.", __func__,
+				pdata->attn_gpio);
+		}
+	}
+#endif /* CONFIG_RMI4_DEV */
+
+	dev_info(&client->dev, "registered rmi i2c driver at 0x%.2X.\n",
+			client->addr);
+	return 0;
+
+err_unregister:
+	rmi_unregister_phys_device(rmi_phys);
+err_data:
+	kfree(data);
+err_phys:
+	kfree(rmi_phys);
+	return error;
+}
+
+static int __devexit rmi_i2c_remove(struct i2c_client *client)
+{
+	struct rmi_phys_device *phys = i2c_get_clientdata(client);
+	struct rmi_device_platform_data *pd = client->dev.platform_data;
+
+	rmi_unregister_phys_device(phys);
+	kfree(phys->data);
+	kfree(phys);
+
+	if (pd->gpio_config)
+		pd->gpio_config(&client->dev, false);
+
+	return 0;
+}
+
+static const struct i2c_device_id rmi_id[] = {
+	{ "rmi", 0 },
+	{ "rmi-i2c", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, rmi_id);
+
+static struct i2c_driver rmi_i2c_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "rmi-i2c"
+	},
+	.id_table	= rmi_id,
+	.probe		= rmi_i2c_probe,
+	.remove		= __devexit_p(rmi_i2c_remove),
+};
+
+static int __init rmi_i2c_init(void)
+{
+	return i2c_add_driver(&rmi_i2c_driver);
+}
+
+static void __exit rmi_i2c_exit(void)
+{
+	i2c_del_driver(&rmi_i2c_driver);
+}
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
+MODULE_DESCRIPTION("RMI I2C driver");
+MODULE_LICENSE("GPL");
+
+module_init(rmi_i2c_init);
+module_exit(rmi_i2c_exit);
diff --git a/drivers/input/rmi4/rmi_spi.c b/drivers/input/rmi4/rmi_spi.c
new file mode 100644
index 0000000..725c8f1
--- /dev/null
+++ b/drivers/input/rmi4/rmi_spi.c
@@ -0,0 +1,875 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/gpio.h>
+#include <linux/rmi.h>
+
+#define COMMS_DEBUG 0
+
+#define RMI_PROTOCOL_VERSION_ADDRESS	0xa0fd
+#define SPI_V2_UNIFIED_READ		0xc0
+#define SPI_V2_WRITE			0x40
+#define SPI_V2_PREPARE_SPLIT_READ	0xc8
+#define SPI_V2_EXECUTE_SPLIT_READ	0xca
+
+#define RMI_SPI_BLOCK_DELAY_US		65
+#define RMI_SPI_BYTE_DELAY_US		65
+#define RMI_SPI_WRITE_DELAY_US		0
+
+#define RMI_V1_READ_FLAG		0x80
+
+#define RMI_PAGE_SELECT_REGISTER 0x00FF
+#define RMI_SPI_PAGE(addr) (((addr) >> 8) & 0x80)
+
+#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 COMMS_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_tx > 1 ? pdata->spi_data.split_read_byte_delay_us : 0;
+		write_delay = 0;
+	}
+
+	if (n_tx) {
+		phys->info.tx_count++;
+		phys->info.tx_bytes += n_tx;
+		if (write_delay)
+			xfer_count += n_tx;
+		else
+			xfer_count += 1;
+	}
+
+	if (n_rx) {
+		phys->info.rx_count++;
+		phys->info.rx_bytes += n_rx;
+		if (byte_delay)
+			xfer_count += n_rx;
+		else
+			xfer_count += 1;
+	}
+
+	xfers = kcalloc(xfer_count,
+			    sizeof(struct spi_transfer), GFP_KERNEL);
+	if (!xfers)
+		return -ENOMEM;
+
+	spi_message_init(&message);
+
+	if (n_tx) {
+		if (write_delay) {
+			for (xfer_index = 0; xfer_index < n_tx;
+					xfer_index++) {
+				memset(&xfers[xfer_index], 0,
+				       sizeof(struct spi_transfer));
+				xfers[xfer_index].len = 1;
+				xfers[xfer_index].delay_usecs = write_delay;
+				xfers[xfer_index].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++)
+			dev_dbg(&client->dev, "%02X ", txbuf[i]);
+		dev_dbg(&client->dev, "\n");
+	}
+#endif
+
+	/* do the i/o */
+	if (pdata->spi_data.cs_assert) {
+		status = pdata->spi_data.cs_assert(
+			pdata->spi_data.cs_assert_data, true);
+		if (!status) {
+			dev_err(phys->dev, "Failed to assert CS.");
+			/* nonzero means error */
+			status = -1;
+			goto error_exit;
+		} else
+			status = 0;
+	}
+
+	if (pdata->spi_data.pre_delay_us)
+		udelay(pdata->spi_data.pre_delay_us);
+
+	status = spi_sync(client, &message);
+
+	if (pdata->spi_data.post_delay_us)
+		udelay(pdata->spi_data.post_delay_us);
+
+	if (pdata->spi_data.cs_assert) {
+		status = pdata->spi_data.cs_assert(
+			pdata->spi_data.cs_assert_data, false);
+		if (!status) {
+			dev_err(phys->dev, "Failed to deassert CS.");
+			/* nonzero means error */
+			status = -1;
+			goto error_exit;
+		} else
+			status = 0;
+	}
+
+	if (status == 0) {
+		memcpy(rxbuf, local_buf + n_tx, n_rx);
+		status = message.status;
+	} else {
+		phys->info.tx_errs++;
+		phys->info.rx_errs++;
+		dev_err(phys->dev, "spi_sync failed with error code %d.",
+		       status);
+	}
+
+#if COMMS_DEBUG
+	if (n_rx) {
+		dev_dbg(&client->dev, "SPI received %d bytes: ", n_rx);
+		for (i = 0; i  n_rx; i++)
+			dev_dbg(&client->dev, "%02X ", rxbuf[i]);
+		dev_dbg(&client->dev, "\n");
+	}
+#endif
+
+error_exit:
+	kfree(xfers);
+	return status;
+}
+
+static int rmi_spi_v2_write_block(struct rmi_phys_device *phys, u16 addr,
+				  u8 *buf, int len)
+{
+	struct rmi_spi_data *data = phys->data;
+	u8 txbuf[len + 4];
+	int error;
+
+	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;
+	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;
+	}
+
+	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);
+	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;
+
+	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);
+			goto err_data;
+		}
+	}
+
+	retval = rmi_spi_check_device(rmi_phys);
+	if (retval < 0)
+		goto err_data;
+
+	/* 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_data;
+	}
+	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_data;
+	}
+
+	retval = rmi_register_phys_device(rmi_phys);
+	if (retval) {
+		dev_err(&spi->dev, "failed to register physical driver\n");
+		goto err_data;
+	}
+
+	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_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;
+
+	rmi_unregister_phys_device(phys);
+	kfree(phys->data);
+	kfree(phys);
+
+	if (pd->gpio_config)
+		pd->gpio_config(&spi->dev, false);
+
+	return 0;
+}
+
+static const struct spi_device_id rmi_id[] = {
+	{ "rmi", 0 },
+	{ "rmi-spi", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, rmi_id);
+
+static struct spi_driver rmi_spi_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "rmi-spi",
+	},
+	.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_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
+MODULE_DESCRIPTION("RMI SPI driver");
+MODULE_LICENSE("GPL");
+
+module_init(rmi_spi_init);
+module_exit(rmi_spi_exit);

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

* [RFC PATCH 4/11] input: RMI4 KConfigs and Makefiles.
  2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (2 preceding siblings ...)
  2011-12-22  2:09 ` [RFC PATCH 3/11] input: RMI4 physical layer drivers for I2C and SPI Christopher Heiny
@ 2011-12-22  2:09 ` Christopher Heiny
  2011-12-22  2:09 ` [RFC PATCH 5/11] input: rmidev character driver for RMI4 sensors Christopher Heiny
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2011-12-22  2:09 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Joerie de Gram, 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>


---

 drivers/input/Kconfig       |    2 +
 drivers/input/Makefile      |    2 +
 drivers/input/rmi4/Kconfig  |  116 +++++++++++++++++++++++++++++++++++++++++++
 drivers/input/rmi4/Makefile |   30 +++++++++++
 4 files changed, 150 insertions(+), 0 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..18383b9 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -23,6 +23,8 @@ obj-$(CONFIG_INPUT_TABLET)	+= tablet/
 obj-$(CONFIG_INPUT_TOUCHSCREEN)	+= touchscreen/
 obj-$(CONFIG_INPUT_MISC)	+= misc/
 
+obj-y += rmi4/
+
 obj-$(CONFIG_INPUT_APMPOWER)	+= apm-power.o
 
 obj-$(CONFIG_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
new file mode 100644
index 0000000..06a6a07
--- /dev/null
+++ b/drivers/input/rmi4/Kconfig
@@ -0,0 +1,116 @@
+#
+# RMI4 configuration
+#
+config RMI4_BUS
+	bool "Synaptics RMI4 bus support"
+	help
+	  Say Y here if you want to support the Synaptics RMI4 bus.
+
+	  If unsure, say Y.
+
+	  This feature is not currently 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_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_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_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_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_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..be0c54b
--- /dev/null
+++ b/drivers/input/rmi4/Makefile
@@ -0,0 +1,30 @@
+obj-$(CONFIG_RMI4_BUS) += rmi_bus.o
+obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
+obj-$(CONFIG_RMI4_SPI) += rmi_spi.o
+obj-$(CONFIG_RMI4_GENERIC) += rmi_driver.o rmi_f01.o
+obj-$(CONFIG_RMI4_F09) += rmi_f09.o
+obj-$(CONFIG_RMI4_F11) += rmi_f11.o
+obj-$(CONFIG_RMI4_F19) += rmi_f19.o
+obj-$(CONFIG_RMI4_F34) += rmi_f34.o
+obj-$(CONFIG_RMI4_F54) += rmi_f54.o
+obj-$(CONFIG_RMI4_DEV) += rmi_dev.o
+
+ifeq ($(KERNELRELEASE),)
+
+# KERNELDIR ?= /home/<AndroidKernelDirectory>
+PWD := $(shell pwd)
+
+.PHONY: build clean
+
+build:
+	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
+
+clean:
+	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c
+else
+
+$(info Building with KERNELRELEASE = ${KERNELRELEASE})
+obj-m :=    rmi_dev.o
+
+endif
+

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

* [RFC PATCH 5/11] input: rmidev character driver for RMI4 sensors
  2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (3 preceding siblings ...)
  2011-12-22  2:09 ` [RFC PATCH 4/11] input: RMI4 KConfigs and Makefiles Christopher Heiny
@ 2011-12-22  2:09 ` Christopher Heiny
  2011-12-22  2:09 ` [RFC PATCH 6/11] input: RMI4 F09 - self test Christopher Heiny
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2011-12-22  2:09 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Joerie de Gram, 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>


---

 drivers/input/rmi4/rmi_dev.c |  446 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 446 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..a7299cb
--- /dev/null
+++ b/drivers/input/rmi4/rmi_dev.c
@@ -0,0 +1,446 @@
+/*
+ * 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;
+	else
+		*f_pos += retval;
+
+	if (copy_to_user(buf, tmpbuf, count))
+		retval = -EFAULT;
+
+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,
+};
+
+/*
+ * rmi_char_dev_clean_up - release memory or unregister driver
+ * @rmi_char_dev: rmi_char_dev structure
+ *
+ */
+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)
+{
+
+	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;
+
+	return rmi_register_character_driver(&rmidev_driver);
+}
+
+static void __exit rmidev_exit(void)
+{
+	pr_debug("%s: exiting.\n", __func__);
+	rmi_unregister_character_driver(&rmidev_driver);
+	class_unregister(rmidev_device_class);
+	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] 34+ messages in thread

* [RFC PATCH 6/11] input: RMI4 F09 - self test.
  2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (4 preceding siblings ...)
  2011-12-22  2:09 ` [RFC PATCH 5/11] input: rmidev character driver for RMI4 sensors Christopher Heiny
@ 2011-12-22  2:09 ` Christopher Heiny
  2011-12-22  2:09 ` [RFC PATCH 7/11] input: RMI4 F01 - device control Christopher Heiny
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2011-12-22  2:09 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Joerie de Gram, 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>


---

 drivers/input/rmi4/rmi_f09.c |  411 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 411 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..dd00d8c
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f09.c
@@ -0,0 +1,411 @@
+/*
+ * 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/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.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 {
+	/* test1 */
+	u8 test1_limit_low;
+	u8 test1_limit_high;
+	u8 test1_limit_diff;
+	/* test2 */
+	u8 test2_limit_low;
+	u8 test2_limit_high;
+	u8 test2_limit_diff;
+};
+
+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;
+	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_overall_bist_result_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+*/
+
+static inline int rmi_f09_alloc_memory(struct rmi_function_container *fc);
+
+static inline void rmi_f09_free_memory(struct rmi_function_container *fc);
+
+static inline 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 inline 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),
+};
+
+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 inline 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 inline void rmi_f09_free_memory(struct rmi_function_container *fc)
+{
+	kfree(fc->data);
+	fc->data = NULL;
+}
+
+static inline 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;
+	u8 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 inline 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 struct rmi_function_handler function_handler = {
+	.func = 0x09,
+	.init = rmi_f09_init,
+	.config = rmi_f09_config,
+	.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);
+}
+
+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");
+

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

* [RFC PATCH 7/11] input: RMI4 F01 - device control
  2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (5 preceding siblings ...)
  2011-12-22  2:09 ` [RFC PATCH 6/11] input: RMI4 F09 - self test Christopher Heiny
@ 2011-12-22  2:09 ` Christopher Heiny
  2011-12-22  2:09 ` [RFC PATCH 8/11] input: RMI4 F54 - analog data reporting Christopher Heiny
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2011-12-22  2:09 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Joerie de Gram, 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>

---

 drivers/input/rmi4/rmi_f01.c |  867 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 867 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..7bd5381
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -0,0 +1,867 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+/* control register bits */
+#define RMI_SLEEP_MODE_NORMAL (0x00)
+#define RMI_SLEEP_MODE_SENSOR_SLEEP (0x01)
+#define RMI_SLEEP_MODE_RESERVED0 (0x02)
+#define RMI_SLEEP_MODE_RESERVED1 (0x03)
+
+#define RMI_IS_VALID_SLEEPMODE(mode) \
+	(mode >= RMI_SLEEP_MODE_NORMAL && mode <= RMI_SLEEP_MODE_RESERVED1)
+
+union f01_device_commands {
+	struct {
+		u8 reset:1;
+		u8 reserved:1;
+	};
+	u8 reg;
+};
+
+union f01_device_control {
+	struct {
+		u8 sleep_mode:2;
+		u8 nosleep:1;
+		u8 reserved:2;
+		u8 charger_input:1;
+		u8 report_rate:1;
+		u8 configured:1;
+	};
+	u8 reg;
+};
+
+union f01_device_status {
+	struct {
+		u8 status_code:4;
+		u8 reserved:2;
+		u8 flash_prog:1;
+		u8 unconfigured:1;
+	};
+	u8 reg;
+};
+
+union f01_basic_queries {
+	struct {
+		u8 manufacturer_id:8;
+
+		u8 custom_map:1;
+		u8 non_compliant:1;
+		u8 q1_bit_2:1;
+		u8 has_sensor_id:1;
+		u8 has_charger_input:1;
+		u8 has_adjustable_doze:1;
+		u8 has_adjustable_doze_holdoff:1;
+		u8 q1_bit_7:1;
+
+		u8 productinfo_1:7;
+		u8 q2_bit_7:1;
+		u8 productinfo_2:7;
+		u8 q3_bit_7:1;
+
+		u8 year:5;
+		u8 month:4;
+		u8 day:5;
+		u8 cp1:1;
+		u8 cp2:1;
+		u8 wafer_id1_lsb:8;
+		u8 wafer_id1_msb:8;
+		u8 wafer_id2_lsb:8;
+		u8 wafer_id2_msb:8;
+		u8 wafer_id3_lsb:8;
+	};
+	u8 regs[11];
+};
+
+struct f01_data {
+	union f01_device_control device_control;
+	union f01_basic_queries basic_queries;
+	union f01_device_status device_status;
+	u8 product_id[RMI_PRODUCT_ID_LENGTH+1];
+
+#ifdef	CONFIG_PM
+	bool suspended;
+	bool old_nosleep;
+#endif
+};
+
+
+static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf);
+
+static ssize_t rmi_fn_01_productid_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+
+static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf);
+
+static ssize_t rmi_fn_01_datecode_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf);
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_reset_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_sleepmode_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+
+static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_nosleep_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_chargerinput_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_chargerinput_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_configured_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_unconfigured_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_flashprog_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static ssize_t rmi_fn_01_statuscode_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+static inline int rmi_f01_alloc_memory(struct rmi_function_container *fc);
+
+static inline void rmi_f01_free_memory(struct rmi_function_container *fc);
+
+static inline int rmi_f01_initialize(struct rmi_function_container *fc);
+
+static inline 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),
+	/* We make report rate RO, since the driver uses that to look for
+	 * resets.  We don't want someone faking us out by changing that
+	 * bit.
+	 */
+	__ATTR(configured, RMI_RO_ATTR,
+	       rmi_fn_01_configured_show, rmi_store_error),
+
+	/* Command register access. */
+	__ATTR(reset, RMI_WO_ATTR,
+	       rmi_show_error, rmi_fn_01_reset_store),
+
+	/* STatus register access. */
+	__ATTR(unconfigured, RMI_RO_ATTR,
+	       rmi_fn_01_unconfigured_show, rmi_store_error),
+	__ATTR(flashprog, RMI_RO_ATTR,
+	       rmi_fn_01_flashprog_show, rmi_store_error),
+	__ATTR(statuscode, RMI_RO_ATTR,
+	       rmi_fn_01_statuscode_show, rmi_store_error),
+};
+
+/* Utility routine to set the value of a bit field in a register. */
+int rmi_set_bit_field(struct rmi_device *rmi_dev,
+		      unsigned short address,
+		      unsigned char field_mask,
+		      unsigned char bits)
+{
+	unsigned char reg_contents;
+	int retval;
+
+	retval = rmi_read(rmi_dev, address, &reg_contents);
+	if (retval)
+		return retval;
+	reg_contents = (reg_contents & ~field_mask) | bits;
+	retval = rmi_write(rmi_dev, address, reg_contents);
+	if (retval == 1)
+		return 0;
+	else if (retval == 0)
+		return -EIO;
+	return retval;
+}
+
+static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n",
+			data->basic_queries.productinfo_1,
+			data->basic_queries.productinfo_2);
+}
+
+static ssize_t rmi_fn_01_productid_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", data->product_id);
+}
+
+static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "0x%02x\n",
+			data->basic_queries.manufacturer_id);
+}
+
+static ssize_t rmi_fn_01_datecode_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "20%02u-%02u-%02u\n",
+			data->basic_queries.year,
+			data->basic_queries.month,
+			data->basic_queries.day);
+}
+
+static ssize_t rmi_fn_01_reset_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct rmi_function_container *fc = NULL;
+	unsigned int reset;
+	int retval = 0;
+	/* Command register always reads as 0, so we can just use a local. */
+	union f01_device_commands commands = {};
+
+	fc = to_rmi_function_container(dev);
+
+	if (sscanf(buf, "%u", &reset) != 1)
+		return -EINVAL;
+	if (reset < 0 || reset > 1)
+		return -EINVAL;
+
+	/* Per spec, 0 has no effect, so we skip it entirely. */
+	if (reset) {
+		commands.reset = 1;
+		retval = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+				&commands.reg, sizeof(commands.reg));
+		if (retval < 0) {
+			dev_err(dev, "%s: failed to issue reset command, "
+				"error = %d.", __func__, retval);
+			return retval;
+		}
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_01_sleepmode_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE,
+			"%d\n", data->device_control.sleep_mode);
+}
+
+static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || !RMI_IS_VALID_SLEEPMODE(new_value)) {
+		dev_err(dev, "%s: Invalid sleep mode %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "Setting sleep mode to %ld.", new_value);
+	data->device_control.sleep_mode = new_value;
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			&data->device_control.reg,
+			sizeof(data->device_control.reg));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write sleep mode, code %d.\n", retval);
+	return retval;
+}
+
+static ssize_t rmi_fn_01_nosleep_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", data->device_control.nosleep);
+}
+
+static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf,
+				       size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 1) {
+		dev_err(dev, "%s: Invalid nosleep bit %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.nosleep = new_value;
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			&data->device_control.reg,
+			sizeof(data->device_control.reg));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write nosleep bit.\n");
+	return retval;
+}
+
+static ssize_t rmi_fn_01_chargerinput_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.charger_input);
+}
+
+static ssize_t rmi_fn_01_chargerinput_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf,
+				       size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 1) {
+		dev_err(dev, "%s: Invalid chargerinput bit %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.charger_input = new_value;
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			&data->device_control.reg,
+			sizeof(data->device_control.reg));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write chargerinput bit.\n");
+	return retval;
+}
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.report_rate);
+}
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf,
+				       size_t count)
+{
+	struct f01_data *data = NULL;
+	unsigned long new_value;
+	int retval;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	retval = strict_strtoul(buf, 10, &new_value);
+	if (retval < 0 || new_value < 0 || new_value > 1) {
+		dev_err(dev, "%s: Invalid reportrate bit %s.", __func__, buf);
+		return -EINVAL;
+	}
+
+	data->device_control.report_rate = new_value;
+	retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+			&data->device_control.reg,
+			sizeof(data->device_control.reg));
+	if (retval >= 0)
+		retval = count;
+	else
+		dev_err(dev, "Failed to write reportrate bit.\n");
+	return retval;
+}
+
+static ssize_t rmi_fn_01_configured_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_control.configured);
+}
+
+static ssize_t rmi_fn_01_unconfigured_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_status.unconfigured);
+}
+
+static ssize_t rmi_fn_01_flashprog_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			data->device_status.flash_prog);
+}
+
+static ssize_t rmi_fn_01_statuscode_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct f01_data *data = NULL;
+	struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "0x%02x\n",
+			data->device_status.status_code);
+}
+
+/* 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);
+	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 inline int rmi_f01_alloc_memory(struct rmi_function_container *fc)
+{
+	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;
+	}
+	fc->data = f01;
+
+	return 0;
+}
+
+static inline void rmi_f01_free_memory(struct rmi_function_container *fc)
+{
+	kfree(fc->data);
+	fc->data = NULL;
+}
+
+static inline int rmi_f01_initialize(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 = fc->data;
+	u8 temp;
+	int error;
+
+	/* Set the configured bit. */
+	error = rmi_read_block(rmi_dev, fc->fd.control_base_addr,
+			&data->device_control.reg,
+			sizeof(data->device_control.reg));
+	if (error < 0) {
+		dev_err(&fc->dev, "Failed to read F01 control.\n");
+		return error;
+	}
+
+	/* Sleep mode might be set as a hangover from a system crash or
+	 * reboot without power cycle.  If so, clear it so the sensor
+	 * is certain to function.
+	 */
+	if (data->device_control.sleep_mode != RMI_SLEEP_MODE_NORMAL) {
+		dev_warn(&fc->dev,
+			 "WARNING: Non-zero sleep mode found. Clearing...\n");
+		data->device_control.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+	}
+
+	data->device_control.configured = 1;
+	error = rmi_write_block(rmi_dev, fc->fd.control_base_addr,
+			&data->device_control.reg,
+			sizeof(data->device_control.reg));
+	if (error < 0) {
+		dev_err(&fc->dev, "Failed to write F01 control.\n");
+		return error;
+	}
+
+	/* dummy read in order to clear irqs */
+	error = rmi_read(rmi_dev, fc->fd.data_base_addr + 1, &temp);
+	if (error < 0) {
+		dev_err(&fc->dev, "Failed to read Interrupt Status.\n");
+		return error;
+	}
+
+	error = rmi_read_block(rmi_dev, fc->fd.query_base_addr,
+				data->basic_queries.regs,
+				sizeof(data->basic_queries.regs));
+	if (error < 0) {
+		dev_err(&fc->dev, "Failed to read device query registers.\n");
+		return error;
+	}
+	driver_data->manufacturer_id = data->basic_queries.manufacturer_id;
+
+	error = rmi_read_block(rmi_dev,
+		fc->fd.query_base_addr + sizeof(data->basic_queries.regs),
+		data->product_id, RMI_PRODUCT_ID_LENGTH);
+	if (error < 0) {
+		dev_err(&fc->dev, "Failed to read product ID.\n");
+		return error;
+	}
+	data->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
+	memcpy(driver_data->product_id, data->product_id,
+	       sizeof(data->product_id));
+
+	error = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
+			&data->device_status.reg,
+			sizeof(data->device_status.reg));
+	if (error < 0) {
+		dev_err(&fc->dev, "Failed to read device status.\n");
+		return error;
+	}
+	if (data->device_status.unconfigured) {
+		dev_err(&fc->dev,
+			"Device reset during configuration process, status: "
+			"%#02x!\n", data->device_status.status_code);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static inline int rmi_f01_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(fn_01_attrs);
+		 attr_count++) {
+		if (sysfs_create_file
+		    (&fc->dev.kobj, &fn_01_attrs[attr_count].attr) < 0) {
+			dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+			     fn_01_attrs[attr_count].attr.name);
+			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,
+				  &fn_01_attrs[attr_count].attr);
+
+	return rc;
+}
+
+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.reg,
+			sizeof(data->device_control.reg));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to write device_control.reg.\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;
+
+	dev_dbg(&fc->dev, "Suspending...\n");
+	if (data->suspended)
+		return 0;
+
+	data->old_nosleep = data->device_control.nosleep;
+	data->device_control.nosleep = 0;
+	data->device_control.sleep_mode = RMI_SLEEP_MODE_SENSOR_SLEEP;
+
+	retval = rmi_write_block(rmi_dev,
+			driver_data->f01_container->fd.control_base_addr,
+			&data->device_control.reg,
+			sizeof(data->device_control.reg));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Failed to write sleep mode. "
+			"Code: %d.\n", retval);
+		data->device_control.nosleep = data->old_nosleep;
+		data->device_control.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+	} else {
+		data->suspended = true;
+		retval = 0;
+	}
+
+	return retval;
+}
+
+static int rmi_f01_resume(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_driver_data *driver_data = rmi_get_driverdata(rmi_dev);
+	struct f01_data *data = driver_data->f01_container->data;
+	int retval = 0;
+
+	dev_dbg(&fc->dev, "Resuming...\n");
+	if (!data->suspended)
+		return 0;
+
+	data->device_control.nosleep = data->old_nosleep;
+	data->device_control.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+
+	retval = rmi_write_block(rmi_dev,
+			driver_data->f01_container->fd.control_base_addr,
+			&data->device_control.reg,
+			sizeof(data->device_control.reg));
+	if (retval < 0)
+		dev_err(&fc->dev, "Failed to restore normal operation. "
+			"Code: %d.\n", retval);
+	else {
+		data->suspended = false;
+		retval = 0;
+	}
+
+	return retval;
+}
+#endif /* CONFIG_PM */
+
+static int rmi_f01_init(struct rmi_function_container *fc)
+{
+	return 0;
+}
+
+static int rmi_f01_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct f01_data *data = fc->data;
+	int error;
+
+	error = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
+			&data->device_status.reg,
+			sizeof(data->device_status.reg));
+	if (error < 0) {
+		dev_err(&fc->dev, "Failed to read device status.\n");
+		return error;
+	}
+
+	if (data->device_status.unconfigured) {
+		error = rmi_dev->driver->reset_handler(rmi_dev);
+		if (error < 0)
+			return error;
+	}
+
+	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
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	.early_suspend = rmi_f01_suspend,
+	.late_resume = rmi_f01_resume,
+#else
+	.suspend = rmi_f01_suspend,
+	.resume = rmi_f01_resume,
+#endif  /* CONFIG_HAS_EARLYSUSPEND */
+#endif  /* CONFIG_PM */
+};
+
+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");

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

* [RFC PATCH 8/11] input: RMI4 F54 - analog data reporting
  2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (6 preceding siblings ...)
  2011-12-22  2:09 ` [RFC PATCH 7/11] input: RMI4 F01 - device control Christopher Heiny
@ 2011-12-22  2:09 ` Christopher Heiny
  2011-12-22  2:10 ` [RFC PATCH 9/11] input: RMI F34 - firmware reflash Christopher Heiny
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2011-12-22  2:09 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Joerie de Gram, 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>

---

 drivers/input/rmi4/rmi_f54.c | 1506 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1506 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..5119184
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f54.c
@@ -0,0 +1,1506 @@
+
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/hrtimer.h>
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include "rmi_driver.h"
+/* Set this to 1 for raw hex dump of returned data. */
+#define RAW_HEX 0
+/* Set this to 1 for human readable dump of returned data. */
+#define HUMAN_READABLE 0
+/* The watchdog timer can be useful when debugging certain firmware related
+ * issues.
+ */
+#define F54_WATCHDOG 1
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+#define KERNEL_VERSION_ABOVE_2_6_32 1
+#endif
+
+/* define fn $54 commands */
+#define GET_REPORT                1
+#define FORCE_CAL                 2
+
+/* status */
+#define BUSY 1
+#define IDLE 0
+
+/* Offsets for data */
+#define RMI_F54_REPORT_DATA_OFFSET	3
+#define RMI_F54_FIFO_OFFSET		1
+#define RMI_F54_NUM_TX_OFFSET		1
+#define RMI_F54_NUM_RX_OFFSET		0
+
+/* Fixed sizes of reports */
+#define RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE 4
+#define RMI_54_HIGH_RESISTANCE_SIZE 6
+
+/* definitions for F54 Query Registers in ultra-portable unionstruct form */
+struct f54_ad_query {
+	/* query 0 */
+	u8 number_of_receiver_electrodes;
+
+	/* query 1 */
+	u8 number_of_transmitter_electrodes;
+
+	union {
+		struct {
+			/* query2 */
+			u8 f54_ad_query2_b0__1:2;
+			u8 has_baseline:1;
+			u8 has_image8:1;
+			u8 f54_ad_query2_b4__5:2;
+			u8 has_image16:1;
+			u8 f54_ad_query2_b7:1;
+		};
+		u8 f54_ad_query2;
+	};
+
+	/* query 3.0 and 3.1 */
+	u16 clock_rate;
+
+	/* query 4 */
+	u8 touch_controller_family;
+
+	/* query 5 */
+	union {
+		struct {
+			u8 has_pixel_touch_threshold_adjustment:1;
+			u8 f54_ad_query5_b1__7:7;
+		};
+		u8 f54_ad_query5;
+	};
+
+	/* query 6 */
+	union {
+		struct {
+		u8 has_sensor_assignment:1;
+		u8 has_interference_metric:1;
+		u8 has_sense_frequency_control:1;
+		u8 has_firmware_noise_mitigation:1;
+		u8 f54_ad_query6_b4:1;
+		u8 has_two_byte_report_rate:1;
+		u8 has_one_byte_report_rate:1;
+		u8 has_relaxation_control:1;
+		};
+		u8 f54_ad_query6;
+	};
+
+	/* query 7 */
+	union {
+		struct {
+			u8 curve_compensation_mode:2;
+			u8 f54_ad_query7_b2__7:6;
+		};
+		u8 f54_ad_query7;
+	};
+
+	/* query 8 */
+	union {
+		struct {
+		u8 f54_ad_query2_b0:1;
+		u8 has_iir_filter:1;
+		u8 has_cmn_removal:1;
+		u8 has_cmn_maximum:1;
+		u8 has_pixel_threshold_hysteresis:1;
+		u8 has_edge_compensation:1;
+		u8 has_perf_frequency_noisecontrol:1;
+		u8 f54_ad_query8_b7:1;
+		};
+		u8 f54_ad_query8;
+	};
+
+	u8 f54_ad_query9;
+	u8 f54_ad_query10;
+	u8 f54_ad_query11;
+
+	/* query 12 */
+	union {
+		struct {
+			u8 number_of_sensing_frequencies:4;
+			u8 f54_ad_query12_b4__7:4;
+		};
+		u8 f54_ad_query12;
+	};
+};
+
+/* define report types */
+enum f54_report_types {
+	/* The numbering should follow automatically, here for clarity */
+	F54_8BIT_IMAGE = 1,
+	F54_16BIT_IMAGE = 2,
+	F54_RAW_16BIT_IMAGE = 3,
+	F54_HIGH_RESISTANCE = 4,
+	F54_TX_TO_TX_SHORT = 5,
+	F54_RX_TO_RX1 = 7,
+	F54_TRUE_BASELINE = 9,
+	F54_FULL_RAW_CAP_MIN_MAX = 13,
+	F54_RX_OPENS1 = 14,
+	F54_TX_OPEN = 15,
+	F54_TX_TO_GROUND = 16,
+	F54_RX_TO_RX2 = 17,
+	F54_RX_OPENS2 = 18,
+	F54_FULL_RAW_CAP = 19,
+	F54_FULL_RAW_CAP_RX_COUPLING_COMP = 20
+};
+
+/* data specific to fn $54 that needs to be kept around */
+struct rmi_fn_54_data {
+	struct f54_ad_query query;
+	u8 cmd;
+	enum f54_report_types report_type;
+	u16 fifoindex;
+	signed char status;
+	bool no_auto_cal;
+	/*
+	 * May need to do something to make sure this reflects what is currently
+	 * in data.
+	 */
+	unsigned int report_size;
+	unsigned char *report_data;
+	unsigned int bufsize;
+	struct mutex data_mutex;
+	struct lock_class_key data_key;
+	struct mutex status_mutex;
+	struct lock_class_key status_key;
+#if F54_WATCHDOG
+	struct hrtimer watchdog;
+#endif
+	struct rmi_function_container *fc;
+	struct work_struct work;
+};
+
+/* sysfs functions */
+static ssize_t rmi_fn_54_report_type_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_report_type_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_get_report_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_force_cal_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_status_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_54_status_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count);
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+#else
+static ssize_t rmi_fn_54_data_read(struct kobject *kobj,
+#endif
+					struct bin_attribute *attributes,
+					char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_54_num_rx_electrodes_show(struct device *dev,
+				     struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_num_tx_electrodes_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_image16_show(struct device *dev,
+				     struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_image8_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_baseline_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_clock_rate_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_54_touch_controller_family_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_54_has_pixel_touch_threshold_adjustment_show(
+		struct device *dev, struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_sensor_assignment_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_interference_metric_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_sense_frequency_control_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_firmware_noise_mitigation_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_two_byte_report_rate_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_one_byte_report_rate_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_relaxation_control_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_curve_compensation_mode_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_iir_filter_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_cmn_removal_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_cmn_maximum_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_pixel_threshold_hysteresis_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_edge_compensation_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_perf_frequency_noisecontrol_show(
+		struct device *dev, struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_number_of_sensing_frequencies_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_no_auto_cal_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_no_auto_cal_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_fifoindex_show(struct device *dev,
+				  struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_fifoindex_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count);
+
+
+static inline int rmi_f54_alloc_memory(struct rmi_function_container *fc);
+
+static inline void rmi_f54_free_memory(struct rmi_function_container *fc);
+
+static inline int rmi_f54_initialize(struct rmi_function_container *fc);
+
+static int rmi_f54_config(struct rmi_function_container *fc);
+
+static int rmi_f54_reset(struct rmi_function_container *fc);
+
+static inline int rmi_f54_create_sysfs(struct rmi_function_container *fc);
+
+
+static struct device_attribute attrs[] = {
+	__ATTR(report_type, RMI_RW_ATTR,
+		rmi_fn_54_report_type_show, rmi_fn_54_report_type_store),
+	__ATTR(get_report, RMI_WO_ATTR,
+		rmi_show_error, rmi_fn_54_get_report_store),
+	__ATTR(force_cal, RMI_WO_ATTR,
+		   rmi_show_error, rmi_fn_54_force_cal_store),
+	__ATTR(status, RMI_RW_ATTR,
+		rmi_fn_54_status_show, rmi_fn_54_status_store),
+	__ATTR(num_rx_electrodes, RMI_RO_ATTR,
+		rmi_fn_54_num_rx_electrodes_show, rmi_store_error),
+	__ATTR(num_tx_electrodes, RMI_RO_ATTR,
+		rmi_fn_54_num_tx_electrodes_show, rmi_store_error),
+	__ATTR(has_image16, RMI_RO_ATTR,
+		rmi_fn_54_has_image16_show, rmi_store_error),
+	__ATTR(has_image8, RMI_RO_ATTR,
+		rmi_fn_54_has_image8_show, rmi_store_error),
+	__ATTR(has_baseline, RMI_RO_ATTR,
+		rmi_fn_54_has_baseline_show, rmi_store_error),
+	__ATTR(clock_rate, RMI_RO_ATTR,
+		rmi_fn_54_clock_rate_show, rmi_store_error),
+	__ATTR(touch_controller_family, RMI_RO_ATTR,
+		rmi_fn_54_touch_controller_family_show, rmi_store_error),
+	__ATTR(has_pixel_touch_threshold_adjustment, RMI_RO_ATTR,
+		rmi_fn_54_has_pixel_touch_threshold_adjustment_show
+							, rmi_store_error),
+	__ATTR(has_sensor_assignment, RMI_RO_ATTR,
+		rmi_fn_54_has_sensor_assignment_show, rmi_store_error),
+	__ATTR(has_interference_metric, RMI_RO_ATTR,
+		rmi_fn_54_has_interference_metric_show, rmi_store_error),
+	__ATTR(has_sense_frequency_control, RMI_RO_ATTR,
+		rmi_fn_54_has_sense_frequency_control_show, rmi_store_error),
+	__ATTR(has_firmware_noise_mitigation, RMI_RO_ATTR,
+		rmi_fn_54_has_firmware_noise_mitigation_show, rmi_store_error),
+	__ATTR(has_two_byte_report_rate, RMI_RO_ATTR,
+		rmi_fn_54_has_two_byte_report_rate_show, rmi_store_error),
+	__ATTR(has_one_byte_report_rate, RMI_RO_ATTR,
+		rmi_fn_54_has_one_byte_report_rate_show, rmi_store_error),
+	__ATTR(has_relaxation_control, RMI_RO_ATTR,
+		rmi_fn_54_has_relaxation_control_show, rmi_store_error),
+	__ATTR(curve_compensation_mode, RMI_RO_ATTR,
+		rmi_fn_54_curve_compensation_mode_show, rmi_store_error),
+	__ATTR(has_iir_filter, RMI_RO_ATTR,
+		rmi_fn_54_has_iir_filter_show, rmi_store_error),
+	__ATTR(has_cmn_removal, RMI_RO_ATTR,
+		rmi_fn_54_has_cmn_removal_show, rmi_store_error),
+	__ATTR(has_cmn_maximum, RMI_RO_ATTR,
+		rmi_fn_54_has_cmn_maximum_show, rmi_store_error),
+	__ATTR(has_pixel_threshold_hysteresis, RMI_RO_ATTR,
+		rmi_fn_54_has_pixel_threshold_hysteresis_show, rmi_store_error),
+	__ATTR(has_edge_compensation, RMI_RO_ATTR,
+		rmi_fn_54_has_edge_compensation_show, rmi_store_error),
+	__ATTR(has_perf_frequency_noisecontrol, RMI_RO_ATTR,
+	      rmi_fn_54_has_perf_frequency_noisecontrol_show, rmi_store_error),
+	__ATTR(number_of_sensing_frequencies, RMI_RO_ATTR,
+		rmi_fn_54_number_of_sensing_frequencies_show, rmi_store_error),
+	__ATTR(no_auto_cal, RMI_RW_ATTR,
+		rmi_fn_54_no_auto_cal_show, rmi_fn_54_no_auto_cal_store),
+	__ATTR(fifoindex, RMI_RW_ATTR,
+		rmi_fn_54_fifoindex_show, rmi_fn_54_fifoindex_store),
+};
+
+struct bin_attribute dev_rep_data = {
+	.attr = {
+		 .name = "rep_data",
+		 .mode = RMI_RO_ATTR},
+	.size = 0,
+	.read = rmi_fn_54_data_read,
+};
+
+#if F54_WATCHDOG
+static enum hrtimer_restart clear_status(struct hrtimer *timer);
+
+static void clear_status_worker(struct work_struct *work);
+#endif
+
+
+
+static int rmi_f54_init(struct rmi_function_container *fc)
+{
+	int retval = 0;
+
+	dev_info(&fc->dev, "Intializing 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;
+
+	return retval;
+
+error_exit:
+	rmi_f54_free_memory(fc);
+
+	return retval;
+}
+
+
+
+static inline 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;
+
+	return 0;
+}
+
+
+static inline void rmi_f54_free_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *f54 = fc->data;
+
+	if (f54) {
+		kfree(f54->report_data);
+		kfree(f54);
+		fc->data = NULL;
+	}
+}
+
+
+static inline int rmi_f54_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *instance_data = fc->data;
+	int retval = 0;
+
+	instance_data->fc = fc;
+
+#if F54_WATCHDOG
+	/* Set up watchdog timer to catch unanswered get_report commands */
+	hrtimer_init(&instance_data->watchdog, CLOCK_MONOTONIC,
+							HRTIMER_MODE_REL);
+	instance_data->watchdog.function = clear_status;
+
+	/* work function to do unlocking */
+	INIT_WORK(&instance_data->work, clear_status_worker);
+#endif
+
+	/* Read F54 Query Data */
+	retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+		(u8 *)&instance_data->query, sizeof(instance_data->query));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read query registers"
+			" from 0x%04x\n", fc->fd.query_base_addr);
+		return retval;
+	}
+
+	__mutex_init(&instance_data->data_mutex, "data_mutex",
+		     &instance_data->data_key);
+
+	__mutex_init(&instance_data->status_mutex, "status_mutex",
+		     &instance_data->status_key);
+
+	instance_data->status = IDLE;
+
+	return 0;
+}
+
+
+static inline int rmi_f54_create_sysfs(struct rmi_function_container *fc)
+{
+	int attr_count = 0;
+	int rc;
+
+	dev_dbg(&fc->dev, "Creating sysfs files.");
+
+	/* Binary sysfs file to report the data back */
+	rc = sysfs_create_bin_file(&fc->dev.kobj, &dev_rep_data);
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to create sysfs file for F54 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_rep_data);
+
+	for (attr_count--; attr_count >= 0; attr_count--)
+		sysfs_remove_file(&fc->dev.kobj,
+				  &attrs[attr_count].attr);
+
+	return rc;
+}
+
+
+static int rmi_f54_config(struct rmi_function_container *fc)
+{
+	/*we shouldn't do anything here.
+	 * (reset should write a 1 to a "beenreset" sysfs file)
+     */
+	return 0;
+}
+
+
+
+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;
+	int attr_count = 0;
+
+	dev_info(&fc->dev, "Removing F54.");
+
+	#if F54_WATCHDOG
+	/* Stop timer */
+	hrtimer_cancel(&data->watchdog);
+	#endif
+
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
+		sysfs_remove_file(&fc->dev.kobj, &attrs[attr_count].attr);
+
+	sysfs_remove_bin_file(&fc->dev.kobj, &dev_rep_data);
+
+	rmi_f54_free_memory(fc);
+}
+
+
+static void set_report_size(struct rmi_fn_54_data *data)
+{
+	u8 rx = data->query.number_of_receiver_electrodes;
+	u8 tx = data->query.number_of_transmitter_electrodes;
+	switch (data->report_type) {
+	case F54_8BIT_IMAGE:
+		data->report_size = rx * tx;
+		break;
+	case F54_16BIT_IMAGE:
+	case F54_RAW_16BIT_IMAGE:
+	case F54_TRUE_BASELINE:
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+		data->report_size = 2 * rx * tx;
+		break;
+	case F54_HIGH_RESISTANCE:
+		data->report_size = RMI_54_HIGH_RESISTANCE_SIZE;
+		break;
+	case F54_FULL_RAW_CAP_MIN_MAX:
+		data->report_size = RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE;
+		break;
+	case F54_TX_TO_TX_SHORT:
+	case F54_TX_OPEN:
+	case F54_TX_TO_GROUND:
+		data->report_size =  (tx + 7) / 8;
+		break;
+	case F54_RX_TO_RX1:
+	case F54_RX_OPENS1:
+		if (rx < tx)
+			data->report_size = 2 * rx * rx;
+		else
+			data->report_size = 2 * rx * tx;
+		break;
+	case F54_RX_TO_RX2:
+	case F54_RX_OPENS2:
+		if (rx <= tx)
+			data->report_size = 0;
+		else
+			data->report_size = 2 * rx * (rx - tx);
+		break;
+	default:
+		data->report_size = 0;
+	}
+}
+
+int rmi_f54_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	struct rmi_driver *driver = fc->rmi_dev->driver;
+	char fifo[2];
+	struct rmi_fn_54_data *data = fc->data;
+	int error = 0;
+
+	set_report_size(data);
+	if (data->report_size == 0) {
+		dev_err(&fc->dev, "Invalid report type set in %s. "
+				"This should never happen.\n", __func__);
+		error = -EINVAL;
+		goto error_exit;
+	}
+	/*
+	 * We need to ensure the buffer is big enough. A Buffer size of 0 means
+	 * that the buffer has not been allocated.
+	 */
+	if (data->bufsize < data->report_size) {
+		mutex_lock(&data->data_mutex);
+		if (data->bufsize > 0)
+			kfree(data->report_data);
+		data->report_data = kzalloc(data->report_size, GFP_KERNEL);
+		if (!data->report_data) {
+			dev_err(&fc->dev, "Failed to allocate report_data.\n");
+			error = -ENOMEM;
+			data->bufsize = 0;
+			mutex_unlock(&data->data_mutex);
+			goto error_exit;
+		}
+		data->bufsize = data->report_size;
+		mutex_unlock(&data->data_mutex);
+	}
+	dev_vdbg(&fc->dev, "F54 Interrupt handler is running.\nSize: %d\n",
+		 data->report_size);
+	/* Write 0 to fifohi and fifolo. */
+	fifo[0] = 0;
+	fifo[1] = 0;
+	error = rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr
+				+ RMI_F54_FIFO_OFFSET, fifo,	sizeof(fifo));
+	if (error < 0)
+		dev_err(&fc->dev, "Failed to write fifo to zero!\n");
+	else
+		error = rmi_read_block(fc->rmi_dev,
+			fc->fd.data_base_addr + RMI_F54_REPORT_DATA_OFFSET,
+			data->report_data, data->report_size);
+	if (error < 0)
+		dev_err(&fc->dev, "F54 data read failed. Code: %d.\n", error);
+	else if (error != data->report_size) {
+		error = -EINVAL;
+		goto error_exit;
+	}
+#if RAW_HEX
+	int l;
+	/* Debugging: Print out the file in hex. */
+	pr_info("Report data (raw hex):\n");
+	for (l = 0; l < data->report_size; l += 2) {
+		pr_info("%03d: 0x%02x%02x\n", l/2,
+			data->report_data[l+1], data->report_data[l]);
+	}
+#endif
+#if HUMAN_READABLE
+	/* Debugging: Print out file in human understandable image */
+	switch (data->report_type) {
+	case F54_16BIT_IMAGE:
+	case F54_RAW_16BIT_IMAGE:
+	case F54_TRUE_BASELINE:
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+		pr_info("Report data (Image):\n");
+		int i, j, k;
+		char c[2];
+		short s;
+		k = 0;
+		for (i = 0; i < data->query.number_of_transmitter_electrodes;
+									i++) {
+			for (j = 0; j <
+			     data->query.number_of_receiver_electrodes; j++) {
+				c[0] = data->report_data[k];
+				c[1] = data->report_data[k+1];
+				memcpy(&s, &c, 2);
+				if (s < -64)
+					printk(".");
+				else if (s < 0)
+					printk("-");
+				else if (s > 64)
+					printk("*");
+				else if (s > 0)
+					printk("+");
+				else
+					printk("0");
+				k += 2;
+			}
+			pr_info("\n");
+		}
+		pr_info("EOF\n");
+		break;
+	default:
+		pr_info("Report type %d debug image not supported",
+							data->report_type);
+	}
+#endif
+	error = IDLE;
+error_exit:
+	mutex_lock(&data->status_mutex);
+	/* Turn back on other interupts, if it
+	 * appears that we turned them off. */
+	if (driver->restore_irq_mask) {
+		dev_dbg(&fc->dev, "Restoring interupts!\n");
+		driver->restore_irq_mask(fc->rmi_dev);
+	} else {
+		dev_err(&fc->dev, "No way to restore interrupts!\n");
+	}
+	data->status = error;
+	mutex_unlock(&data->status_mutex);
+	return data->status;
+}
+
+
+#if F54_WATCHDOG
+static void clear_status_worker(struct work_struct *work)
+{
+	struct rmi_fn_54_data *data = container_of(work,
+					struct rmi_fn_54_data, work);
+	struct rmi_function_container *fc = data->fc;
+	struct rmi_driver *driver = fc->rmi_dev->driver;
+	char command;
+	int result;
+
+	mutex_lock(&data->status_mutex);
+	if (data->status == BUSY) {
+		pr_debug("F54 Timout Occured: Determining status.\n");
+		result = rmi_read_block(fc->rmi_dev, fc->fd.command_base_addr,
+								&command, 1);
+		if (result < 0) {
+			dev_err(&fc->dev, "Could not read get_report register "
+				"from 0x%04x\n", fc->fd.command_base_addr);
+			data->status = -ETIMEDOUT;
+		} else {
+			if (command & GET_REPORT) {
+				dev_warn(&fc->dev, "Report type unsupported!");
+				data->status = -EINVAL;
+			} else {
+				data->status = -ETIMEDOUT;
+			}
+		}
+		if (driver->restore_irq_mask) {
+			dev_dbg(&fc->dev, "Restoring interupts!\n");
+			driver->restore_irq_mask(fc->rmi_dev);
+		} else {
+			dev_err(&fc->dev, "No way to restore interrupts!\n");
+		}
+	}
+	mutex_unlock(&data->status_mutex);
+}
+
+static enum hrtimer_restart clear_status(struct hrtimer *timer)
+{
+	struct rmi_fn_54_data *data = container_of(timer,
+					struct rmi_fn_54_data, watchdog);
+	schedule_work(&(data->work));
+	return HRTIMER_NORESTART;
+}
+#endif
+
+/* Check if report_type is valid */
+static bool is_report_type_valid(enum f54_report_types reptype)
+{
+	/* Basic checks on report_type to ensure we write a valid type
+	 * to the sensor.
+	 * TODO: Check Query3 to see if some specific reports are
+	 * available. This is currently listed as a reserved register.
+	 */
+	switch (reptype) {
+	case F54_8BIT_IMAGE:
+	case F54_16BIT_IMAGE:
+	case F54_RAW_16BIT_IMAGE:
+	case F54_HIGH_RESISTANCE:
+	case F54_TX_TO_TX_SHORT:
+	case F54_RX_TO_RX1:
+	case F54_TRUE_BASELINE:
+	case F54_FULL_RAW_CAP_MIN_MAX:
+	case F54_RX_OPENS1:
+	case F54_TX_OPEN:
+	case F54_TX_TO_GROUND:
+	case F54_RX_TO_RX2:
+	case F54_RX_OPENS2:
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+		return true;
+		break;
+	default:
+		return false;
+	}
+}
+
+/* SYSFS file show/store functions */
+static ssize_t rmi_fn_54_report_type_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->report_type);
+}
+
+static ssize_t rmi_fn_54_report_type_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count) {
+	int result;
+	unsigned long val;
+	unsigned char data;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	/* need to convert the string data to an actual value */
+	result = strict_strtoul(buf, 10, &val);
+	if (result)
+		return result;
+	if (!is_report_type_valid(val)) {
+		dev_err(dev, "%s : Report type %d is invalid.\n",
+					__func__, (u8) val);
+		return -EINVAL;
+	}
+	mutex_lock(&instance_data->status_mutex);
+	if (instance_data->status != BUSY) {
+		instance_data->report_type = (enum f54_report_types)val;
+		data = (char)val;
+		/* Write the Report Type back to the first Block
+		 * Data registers (F54_AD_Data0). */
+		result =
+		    rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr,
+								&data, 1);
+		mutex_unlock(&instance_data->status_mutex);
+		if (result < 0) {
+			dev_err(dev, "%s : Could not write report type to"
+				" 0x%x\n", __func__, fc->fd.data_base_addr);
+			return result;
+		}
+		return count;
+	} else {
+		dev_err(dev, "%s : Report type cannot be changed in the middle"
+				" of command.\n", __func__);
+		mutex_unlock(&instance_data->status_mutex);
+		return -EINVAL;
+	}
+}
+
+static ssize_t rmi_fn_54_get_report_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count) {
+	unsigned long val;
+	int error, result;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+	struct rmi_driver *driver;
+	u8 command;
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	driver = fc->rmi_dev->driver;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+	if (error)
+		return error;
+	/* Do nothing if not set to 1. This prevents accidental commands. */
+	if (val != 1)
+		return count;
+	command = (unsigned char)GET_REPORT;
+	/* Basic checks on report_type to ensure we write a valid type
+	 * to the sensor.
+	 * TODO: Check Query3 to see if some specific reports are
+	 * available. This is currently listed as a reserved register.
+	 */
+	if (!is_report_type_valid(instance_data->report_type)) {
+		dev_err(dev, "%s : Report type %d is invalid.\n",
+				__func__, instance_data->report_type);
+		return -EINVAL;
+	}
+	mutex_lock(&instance_data->status_mutex);
+	if (instance_data->status != IDLE) {
+		if (instance_data->status != BUSY) {
+			dev_err(dev, "F54 status is in an abnormal state: 0x%x",
+							instance_data->status);
+		}
+		mutex_unlock(&instance_data->status_mutex);
+		return count;
+	}
+	/* Store interrupts */
+	/* Do not exit if we fail to turn off interupts. We are likely
+	 * to still get useful data. The report data can, however, be
+	 * corrupted, and there may be unexpected behavior.
+	 */
+	dev_dbg(dev, "Storing and overriding interupts\n");
+	if (driver->store_irq_mask)
+		driver->store_irq_mask(fc->rmi_dev,
+					fc->irq_mask);
+	else
+		dev_err(dev, "No way to store interupts!\n");
+	instance_data->status = BUSY;
+
+	/* small delay to avoid race condition in firmare. This value is a bit
+	 * higher than absolutely necessary. Should be removed once issue is
+	 * resolved in firmware. */
+
+	mdelay(2);
+
+	/* Write the command to the command register */
+	result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+						&command, 1);
+	mutex_unlock(&instance_data->status_mutex);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write command to 0x%x\n",
+				__func__, fc->fd.command_base_addr);
+		return result;
+	}
+#if F54_WATCHDOG
+	/* start watchdog timer */
+	hrtimer_start(&instance_data->watchdog, ktime_set(1, 0),
+							HRTIMER_MODE_REL);
+#endif
+	return count;
+}
+
+
+static ssize_t rmi_fn_54_force_cal_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count) {
+	unsigned long val;
+	int error, result;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+	struct rmi_driver *driver;
+	u8 command;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	driver = fc->rmi_dev->driver;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+	if (error)
+		return error;
+	/* Do nothing if not set to 1. This prevents accidental commands. */
+	if (val != 1)
+		return count;
+
+	command = (unsigned char)FORCE_CAL;
+
+	if (instance_data->status == BUSY)
+		return -EBUSY;
+	/* Write the command to the command register */
+	result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+						&command, 1);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write command to 0x%x\n",
+				__func__, fc->fd.command_base_addr);
+		return result;
+	}
+	return count;
+}
+
+static ssize_t rmi_fn_54_status_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", instance_data->status);
+}
+
+
+
+static ssize_t rmi_fn_54_status_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	mutex_lock(&instance_data->status_mutex);
+	/* any write to status resets it */
+	instance_data->status = 0;
+	mutex_unlock(&instance_data->status_mutex);
+
+	return 0;
+}
+
+
+static ssize_t rmi_fn_54_num_rx_electrodes_show(struct device *dev,
+				     struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+				data->query.number_of_receiver_electrodes);
+}
+
+static ssize_t rmi_fn_54_num_tx_electrodes_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.number_of_transmitter_electrodes);
+}
+
+static ssize_t rmi_fn_54_has_image16_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_image16);
+}
+
+static ssize_t rmi_fn_54_has_image8_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_image8);
+}
+
+static ssize_t rmi_fn_54_has_baseline_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_baseline);
+}
+
+static ssize_t rmi_fn_54_clock_rate_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.clock_rate);
+}
+
+
+static ssize_t rmi_fn_54_touch_controller_family_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.touch_controller_family);
+}
+
+
+static ssize_t rmi_fn_54_has_pixel_touch_threshold_adjustment_show(
+		struct device *dev, struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_pixel_touch_threshold_adjustment);
+}
+
+static ssize_t rmi_fn_54_has_sensor_assignment_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_sensor_assignment);
+}
+
+static ssize_t rmi_fn_54_has_interference_metric_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_interference_metric);
+}
+
+static ssize_t rmi_fn_54_has_sense_frequency_control_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_sense_frequency_control);
+}
+
+static ssize_t rmi_fn_54_has_firmware_noise_mitigation_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_firmware_noise_mitigation);
+}
+
+static ssize_t rmi_fn_54_has_two_byte_report_rate_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_two_byte_report_rate);
+}
+
+static ssize_t rmi_fn_54_has_one_byte_report_rate_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_one_byte_report_rate);
+}
+
+static ssize_t rmi_fn_54_has_relaxation_control_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_relaxation_control);
+}
+
+static ssize_t rmi_fn_54_curve_compensation_mode_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.curve_compensation_mode);
+}
+
+static ssize_t rmi_fn_54_has_iir_filter_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_iir_filter);
+}
+
+static ssize_t rmi_fn_54_has_cmn_removal_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_cmn_removal);
+}
+
+static ssize_t rmi_fn_54_has_cmn_maximum_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_cmn_maximum);
+}
+
+static ssize_t rmi_fn_54_has_pixel_threshold_hysteresis_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_pixel_threshold_hysteresis);
+}
+
+static ssize_t rmi_fn_54_has_edge_compensation_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_edge_compensation);
+}
+
+static ssize_t rmi_fn_54_has_perf_frequency_noisecontrol_show(
+		struct device *dev, struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_perf_frequency_noisecontrol);
+}
+
+static ssize_t rmi_fn_54_number_of_sensing_frequencies_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.number_of_sensing_frequencies);
+}
+
+
+static ssize_t rmi_fn_54_no_auto_cal_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+				instance_data->no_auto_cal ? 1 : 0);
+}
+
+static ssize_t rmi_fn_54_no_auto_cal_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count) {
+	int result;
+	unsigned long val;
+	unsigned char data;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	/* need to convert the string data to an actual value */
+	result = strict_strtoul(buf, 10, &val);
+
+	/* if an error occured, return it */
+	if (result)
+		return result;
+	/* Do nothing if not 0 or 1. This prevents accidental commands. */
+	if (val > 1)
+		return count;
+	/* Read current control values */
+	result =
+	    rmi_read_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1);
+
+	/* if the current control registers are already set as we want them, do
+	 * nothing to them */
+	if ((data & 1) == val)
+		return count;
+	/* Write the control back to the control register (F54_AD_Ctrl0)
+	 * Ignores everything but bit 0 */
+	data = (data & ~1) | (val & 0x01); /* bit mask for lowest bit */
+	result =
+	    rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write control to 0x%x\n",
+		       __func__, fc->fd.control_base_addr);
+		return result;
+	}
+	/* update our internal representation iff the write succeeds */
+	instance_data->no_auto_cal = (val == 1);
+	return count;
+}
+
+static ssize_t rmi_fn_54_fifoindex_show(struct device *dev,
+				  struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+	struct rmi_driver *driver;
+	unsigned char temp_buf[2];
+	int retval;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	driver = fc->rmi_dev->driver;
+
+	/* Read fifoindex from device */
+	retval = rmi_read_block(fc->rmi_dev,
+				fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET,
+				temp_buf, ARRAY_SIZE(temp_buf));
+
+	if (retval < 0) {
+		dev_err(dev, "Could not read fifoindex from 0x%04x\n",
+		       fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET);
+		return retval;
+	}
+	batohs(&instance_data->fifoindex, temp_buf);
+	return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->fifoindex);
+}
+
+static ssize_t rmi_fn_54_fifoindex_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	int error;
+	unsigned long val;
+	unsigned char data[2];
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+
+	if (error)
+		return error;
+
+	instance_data->fifoindex = val;
+
+	/* Write the FifoIndex back to the first data registers. */
+	hstoba(data, (unsigned short)val);
+
+	error = rmi_write_block(fc->rmi_dev,
+				fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET,
+				data,
+				ARRAY_SIZE(data));
+
+	if (error < 0) {
+		dev_err(dev, "%s : Could not write fifoindex to 0x%x\n",
+		       __func__, fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET);
+		return error;
+	}
+	return count;
+}
+
+/* Provide access to last report */
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+#else
+static ssize_t rmi_fn_54_data_read(struct kobject *kobj,
+#endif
+				struct bin_attribute *attributes,
+				char *buf, loff_t pos, size_t count)
+{
+	struct device *dev;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	dev = container_of(kobj, struct device, kobj);
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	mutex_lock(&instance_data->data_mutex);
+	if (count < instance_data->report_size) {
+		dev_err(dev,
+			"%s: F54 report size too large for buffer: %d."
+				" Need at least: %d for Report type: %d.\n",
+			__func__, count, instance_data->report_size,
+			instance_data->report_type);
+		mutex_unlock(&instance_data->data_mutex);
+		return -EINVAL;
+	}
+	if (instance_data->report_data) {
+		/* Copy data from instance_data to buffer */
+		memcpy(buf, instance_data->report_data,
+					instance_data->report_size);
+		mutex_unlock(&instance_data->data_mutex);
+		dev_dbg(dev, "%s: Presumably successful.", __func__);
+		return instance_data->report_size;
+	} else {
+		dev_err(dev, "%s: F54 report_data does not exist!\n", __func__);
+		mutex_unlock(&instance_data->data_mutex);
+		return -EINVAL;
+	}
+}
+
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x54,
+	.init = rmi_f54_init,
+	.config = rmi_f54_config,
+	.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)
+{
+	/* There is no function handler specific data to be freed here.
+	 * Function container specific data will be freed as part of the
+	 * unregistration process.
+	 */
+	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");
+

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

* [RFC PATCH 9/11] input: RMI F34 - firmware reflash
  2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (7 preceding siblings ...)
  2011-12-22  2:09 ` [RFC PATCH 8/11] input: RMI4 F54 - analog data reporting Christopher Heiny
@ 2011-12-22  2:10 ` Christopher Heiny
  2011-12-22  2:10 ` [RFC PATCH 10/11] input: RMI4 F19 - capacitive buttons Christopher Heiny
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2011-12-22  2:10 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Joerie de Gram, 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>

---

 drivers/input/rmi4/rmi_f34.c |  962 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 962 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..99dd759
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f34.c
@@ -0,0 +1,962 @@
+/*
+ * 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/kernel.h>
+#include <linux/mutex.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include "rmi_driver.h"
+
+/* define fn $34 commands */
+#define WRITE_FW_BLOCK            0x2
+#define ERASE_ALL                 0x3
+#define READ_CONFIG_BLOCK         0x5
+#define WRITE_CONFIG_BLOCK        0x6
+#define ERASE_CONFIG              0x7
+#define ENABLE_FLASH_PROG         0xf
+
+#define STATUS_IN_PROGRESS        0xff
+#define STATUS_IDLE		  0x80
+
+#define PDT_START_SCAN_LOCATION	0x00e9
+#define PDT_END_SCAN_LOCATION	0x0005
+
+#define BLK_SZ_OFF	3
+#define IMG_BLK_CNT_OFF	5
+#define CFG_BLK_CNT_OFF	7
+
+#define BLK_NUM_OFF 2
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+#define KERNEL_VERSION_ABOVE_2_6_32 1
+#endif
+
+/* data specific to fn $34 that needs to be kept around */
+struct rmi_fn_34_data {
+	unsigned char status;
+	unsigned char cmd;
+	unsigned short bootloaderid;
+	unsigned short blocksize;
+	unsigned short imageblockcount;
+	unsigned short configblockcount;
+	unsigned short blocknum;
+	bool inflashprogmode;
+	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);
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_34_data_read(struct file *data_file, struct kobject *kobj,
+				   struct bin_attribute *attributes,
+				   char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_34_data_write(struct file *data_file,
+				    struct kobject *kobj,
+				    struct bin_attribute *attributes, char *buf,
+				    loff_t pos, size_t count);
+#else
+static ssize_t rmi_fn_34_data_read(struct kobject *kobj,
+				   struct bin_attribute *attributes,
+				   char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_34_data_write(struct kobject *kobj,
+				    struct bin_attribute *attributes, char *buf,
+				    loff_t pos, size_t count);
+#endif
+
+static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf);
+
+static ssize_t rmi_fn_34_bootloaderid_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf, size_t count);
+
+static ssize_t rmi_fn_34_blocksize_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+
+static ssize_t rmi_fn_34_imageblockcount_show(struct device *dev,
+					      struct device_attribute *attr,
+					      char *buf);
+
+static ssize_t rmi_fn_34_configblockcount_show(struct device *dev,
+					       struct device_attribute *attr,
+					       char *buf);
+
+static ssize_t rmi_fn_34_blocknum_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf);
+
+static ssize_t rmi_fn_34_blocknum_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count);
+
+static ssize_t rmi_fn_34_rescanPDT_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count);
+
+
+static inline int rmi_f34_alloc_memory(struct rmi_function_container *fc);
+
+static inline void rmi_f34_free_memory(struct rmi_function_container *fc);
+
+static inline 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 inline 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 inline 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 inline void rmi_f34_free_memory(struct rmi_function_container *fc)
+{
+	kfree(fc->data);
+	fc->data = NULL;
+}
+
+static inline 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 inline 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 == STATUS_IDLE) &&
+		(instance_data->cmd == ENABLE_FLASH_PROG))
+		instance_data->inflashprogmode = true;
+
+	return retval;
+}
+
+int rmi_f34_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	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 != ENABLE_FLASH_PROG) && !instance_data->inflashprogmode) {
+		dev_err(dev, "%s: CANNOT SEND CMD %d TO SENSOR - "
+			"NOT IN FLASH PROG MODE\n"
+			, __func__, data_base_addr);
+		return -EINVAL;
+	}
+
+	instance_data->cmd = val;
+
+	/* Validate command value and (if necessary) write it to the command
+	 * register.
+	 */
+	switch (instance_data->cmd) {
+	case ENABLE_FLASH_PROG:
+	case ERASE_ALL:
+	case ERASE_CONFIG:
+	case WRITE_FW_BLOCK:
+	case READ_CONFIG_BLOCK:
+	case WRITE_CONFIG_BLOCK:
+		/* Reset the status to indicate we are in progress on a cmd. */
+		/* The status will change when the ATTN interrupt happens
+		   and the status of the cmd that was issued is read from
+		   the F34_Flash_Data3 register - result should be 0x80 for
+		   success - any other value indicates an error */
+
+		/* Issue the command to the device. */
+		error = rmi_write(fc->rmi_dev,
+				data_base_addr + instance_data->blocksize +
+				BLK_NUM_OFF, instance_data->cmd);
+
+		if (error < 0) {
+			dev_err(dev, "%s: Could not write command 0x%02x "
+				"to 0x%04x\n", __func__, instance_data->cmd,
+				data_base_addr + instance_data->blocksize +
+				BLK_NUM_OFF);
+			return error;
+		}
+
+		if (instance_data->cmd == ENABLE_FLASH_PROG)
+			instance_data->inflashprogmode = true;
+
+		/* set status to indicate we are in progress */
+		instance_data->status = STATUS_IN_PROGRESS;
+		break;
+	default:
+		dev_dbg(dev, "%s: RMI4 function $34 - "
+				"unknown command 0x%02lx.\n", __func__, val);
+		count = -EINVAL;
+		break;
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_34_blocknum_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->blocknum);
+}
+
+static ssize_t rmi_fn_34_blocknum_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	int error;
+	unsigned long val;
+	unsigned char data[2];
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+	u16 data_base_addr;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	data_base_addr = fc->fd.data_base_addr;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+
+	if (error)
+		return error;
+
+	instance_data->blocknum = val;
+
+	/* Write the Block Number data back to the first two Block
+	 * Data registers (F34_Flash_Data_0 and F34_Flash_Data_1). */
+	hstoba(data, (unsigned short)val);
+
+	error = rmi_write_block(fc->rmi_dev,
+				data_base_addr,
+				data,
+				ARRAY_SIZE(data));
+
+	if (error < 0) {
+		dev_err(dev, "%s : Could not write block number %u to 0x%x\n",
+		       __func__, instance_data->blocknum, data_base_addr);
+		return error;
+	}
+
+	return count;
+}
+
+static ssize_t rmi_fn_34_rescanPDT_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+	struct rmi_device *rmi_dev;
+	struct rmi_driver_data *driver_data;
+	struct pdt_entry pdt_entry;
+	bool fn01found = false;
+	bool fn34found = false;
+	unsigned int rescan;
+	int irq_count = 0;
+	int retval = 0;
+	int i;
+
+	/* Rescan of the PDT is needed since issuing the Flash Enable cmd
+	 * the device registers for Fn$01 and Fn$34 moving around because
+	 * of the change from Bootloader mode to Flash Programming mode
+	 * may change to a different PDT with only Fn$01 and Fn$34 that
+	 * could have addresses for query, control, data, command registers
+	 * that differ from the PDT scan done at device initialization. */
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	rmi_dev = fc->rmi_dev;
+	driver_data = rmi_get_driverdata(rmi_dev);
+
+	/* Make sure we are only in Flash Programming mode  - DON'T
+	 * ALLOW THIS IN UI MODE. */
+	if (instance_data->cmd != ENABLE_FLASH_PROG) {
+		dev_err(dev, "%s: NOT IN FLASH PROG MODE - CAN'T RESCAN PDT.\n"
+				, __func__);
+		return -EINVAL;
+	}
+
+	/* The only good value to write to this is 1, we allow 0, but with
+	 * no effect (this is consistent with the way the command bit works. */
+	if (sscanf(buf, "%u", &rescan) != 1)
+		return -EINVAL;
+	if (rescan < 0 || rescan > 1)
+		return -EINVAL;
+
+	/* 0 has no effect, so we skip it entirely. */
+	if (rescan) {
+		/* rescan the PDT - filling in Fn01 and Fn34 addresses -
+		 * this is only temporary - the device will need to be reset
+		 * to return the PDT to the normal values. */
+
+		/* mini-parse the PDT - we only have to get Fn$01 and Fn$34 and
+		   since we are Flash Programming mode we only have page 0. */
+		for (i = PDT_START_SCAN_LOCATION; i >= PDT_END_SCAN_LOCATION;
+			i -= sizeof(pdt_entry)) {
+			retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry,
+					       sizeof(pdt_entry));
+			if (retval != sizeof(pdt_entry)) {
+				dev_err(dev, "%s: err frm rmi_read_block pdt "
+					"entry data from PDT, "
+					"error = %d.", __func__, retval);
+				return retval;
+			}
+
+			if ((pdt_entry.function_number == 0x00) ||
+				(pdt_entry.function_number == 0xff))
+				break;
+
+			dev_dbg(dev, "%s: Found F%.2X\n",
+				__func__, pdt_entry.function_number);
+
+			/* f01 found - just fill in the new addresses in
+			 * the existing fc. */
+			if (pdt_entry.function_number == 0x01) {
+				struct rmi_function_container *f01_fc =
+					driver_data->f01_container;
+				fn01found = true;
+				f01_fc->fd.query_base_addr =
+					pdt_entry.query_base_addr;
+				f01_fc->fd.command_base_addr =
+				  pdt_entry.command_base_addr;
+				f01_fc->fd.control_base_addr =
+				  pdt_entry.control_base_addr;
+				f01_fc->fd.data_base_addr =
+				  pdt_entry.data_base_addr;
+				f01_fc->fd.function_number =
+				  pdt_entry.function_number;
+				f01_fc->fd.interrupt_source_count =
+				  pdt_entry.interrupt_source_count;
+				f01_fc->num_of_irqs =
+				  pdt_entry.interrupt_source_count;
+				f01_fc->irq_pos = irq_count;
+
+				irq_count += f01_fc->num_of_irqs;
+
+				if (fn34found)
+					break;
+			}
+
+			/* f34 found - just fill in the new addresses in
+			 * the existing fc. */
+			if (pdt_entry.function_number == 0x34) {
+				fn34found = true;
+				fc->fd.query_base_addr =
+				  pdt_entry.query_base_addr;
+				fc->fd.command_base_addr =
+				  pdt_entry.command_base_addr;
+				fc->fd.control_base_addr =
+				  pdt_entry.control_base_addr;
+				fc->fd.data_base_addr =
+				  pdt_entry.data_base_addr;
+				fc->fd.function_number =
+				  pdt_entry.function_number;
+				fc->fd.interrupt_source_count =
+				  pdt_entry.interrupt_source_count;
+				fc->num_of_irqs =
+				  pdt_entry.interrupt_source_count;
+				fc->irq_pos = irq_count;
+
+				irq_count += fc->num_of_irqs;
+
+				if (fn01found)
+					break;
+			}
+
+		}
+
+		if (!fn01found || !fn34found) {
+			dev_err(dev, "%s: failed to find fn$01 or fn$34 trying "
+				"to do rescan PDT.\n"
+				, __func__);
+			return -EINVAL;
+		}
+	}
+
+	return count;
+}
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_34_data_read(struct file *data_file,
+				struct kobject *kobj,
+				struct bin_attribute *attributes,
+				char *buf,
+				loff_t pos,
+				size_t count)
+#else
+static ssize_t rmi_fn_34_data_read(struct kobject *kobj,
+				struct bin_attribute *attributes,
+				char *buf,
+				loff_t pos,
+				size_t count)
+#endif
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+	u16 data_base_addr;
+	int error;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	data_base_addr = fc->fd.data_base_addr;
+
+	if (count != instance_data->blocksize) {
+		dev_err(dev,
+			"%s : Incorrect F34 block size %d. "
+			"Expected size %d.\n",
+			__func__, count, instance_data->blocksize);
+		return -EINVAL;
+	}
+
+	/* Read the data from flash into buf.  The app layer will be blocked
+	 * at reading from the sysfs file.  When we return the count (or
+	 * error if we fail) the app will resume. */
+	error = rmi_read_block(fc->rmi_dev, data_base_addr + BLK_NUM_OFF,
+			(unsigned char *)buf, count);
+
+	if (error < 0) {
+		dev_err(dev, "%s : Could not read data from 0x%04x\n",
+		       __func__, data_base_addr + BLK_NUM_OFF);
+		return error;
+	}
+
+	return count;
+}
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_34_data_write(struct file *data_file,
+				struct kobject *kobj,
+				struct bin_attribute *attributes,
+				char *buf,
+				loff_t pos,
+				size_t count)
+#else
+static ssize_t rmi_fn_34_data_write(struct kobject *kobj,
+				struct bin_attribute *attributes,
+				char *buf,
+				loff_t pos,
+				size_t count)
+#endif
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct rmi_function_container *fc;
+	struct rmi_fn_34_data *instance_data;
+	u16 data_base_addr;
+	int error;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	data_base_addr = fc->fd.data_base_addr;
+
+	/* Write the data from buf to flash. The app layer will be
+	 * blocked at writing to the sysfs file.  When we return the
+	 * count (or error if we fail) the app will resume. */
+
+	if (count != instance_data->blocksize) {
+		dev_err(dev,
+			"%s : Incorrect F34 block size %d. "
+			"Expected size %d.\n",
+			__func__, count, instance_data->blocksize);
+		return -EINVAL;
+	}
+
+	/* Write the data block - only if the count is non-zero  */
+	if (count) {
+		error = rmi_write_block(fc->rmi_dev,
+				data_base_addr + BLK_NUM_OFF,
+				(unsigned char *)buf,
+				count);
+
+		if (error < 0) {
+			dev_err(dev, "%s : Could not write block data "
+				"to 0x%x\n", __func__,
+				data_base_addr + BLK_NUM_OFF);
+			return error;
+		}
+	}
+
+	return count;
+}
+
+static int __init rmi_f34_module_init(void)
+{
+	int error;
+
+	error = rmi_register_function_driver(&function_handler);
+	if (error < 0) {
+		pr_err("%s : register failed !\n", __func__);
+		return error;
+	}
+
+	return 0;
+}
+
+static void rmi_f34_module_exit(void)
+{
+	rmi_unregister_function_driver(&function_handler);
+}
+
+module_init(rmi_f34_module_init);
+module_exit(rmi_f34_module_exit);
+
+MODULE_AUTHOR("William Manson <wmanson@synaptics.com");
+MODULE_DESCRIPTION("RMI F34 module");
+MODULE_LICENSE("GPL");
+

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

* [RFC PATCH 10/11] input: RMI4 F19 - capacitive buttons
  2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (8 preceding siblings ...)
  2011-12-22  2:10 ` [RFC PATCH 9/11] input: RMI F34 - firmware reflash Christopher Heiny
@ 2011-12-22  2:10 ` Christopher Heiny
  2012-01-05  7:53   ` Dmitry Torokhov
  2011-12-22  2:10 ` [RFC PATCH 11/11] input: RMI4 F11 - multifinger pointing Christopher Heiny
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 34+ messages in thread
From: Christopher Heiny @ 2011-12-22  2:10 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Joerie de Gram, 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>

---

 drivers/input/rmi4/rmi_f19.c | 1571 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1571 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..a678f30
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f19.c
@@ -0,0 +1,1571 @@
+/*
+ * 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/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#define QUERY_BASE_INDEX 1
+#define MAX_LEN 256
+
+struct f19_0d_query {
+	union {
+		struct {
+			u8 configurable:1;
+			u8 has_sensitivity_adjust:1;
+			u8 has_hysteresis_threshold:1;
+		};
+		u8 f19_0d_query0;
+	};
+	u8 f19_0d_query1:5;
+};
+
+struct f19_0d_control_0 {
+	union {
+		struct {
+			u8 button_usage:2;
+			u8 filter_mode:2;
+		};
+		u8 f19_0d_control0;
+	};
+};
+
+struct f19_0d_control_1 {
+	u8 int_enabled_button;
+};
+
+struct f19_0d_control_2 {
+	u8 single_button;
+};
+
+struct f19_0d_control_3_4 {
+	u8 sensor_map_button:7;
+	/*u8 sensitivity_button;*/
+};
+
+struct f19_0d_control_5 {
+	u8 sensitivity_adj;
+};
+struct f19_0d_control_6 {
+	u8 hysteresis_threshold;
+};
+
+struct f19_0d_control {
+	struct f19_0d_control_0 *general_control;
+	struct f19_0d_control_1 *button_int_enable;
+	struct f19_0d_control_2 *single_button_participation;
+	struct f19_0d_control_3_4 *sensor_map;
+	struct f19_0d_control_5 *all_button_sensitivity_adj;
+	struct f19_0d_control_6 *all_button_hysteresis_threshold;
+};
+/* data specific to fn $19 that needs to be kept around */
+struct f19_data {
+	struct f19_0d_control *button_control;
+	struct f19_0d_query button_query;
+	u8 button_rezero;
+	bool *button_down;
+	unsigned char button_count;
+	unsigned char button_data_buffer_size;
+	unsigned char *button_data_buffer;
+	unsigned char *button_map;
+	char input_name[MAX_LEN];
+	char input_phys[MAX_LEN];
+	struct input_dev *input;
+};
+
+static ssize_t rmi_f19_button_count_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+
+static ssize_t rmi_f19_button_map_show(struct device *dev,
+				      struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f19_button_map_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count);
+static ssize_t rmi_f19_rezero_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+static ssize_t rmi_f19_rezero_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+static ssize_t rmi_f19_has_hysteresis_threshold_show(struct device *dev,
+				      struct device_attribute *attr, char *buf);
+static ssize_t rmi_f19_has_sensitivity_adjust_show(struct device *dev,
+				      struct device_attribute *attr, char *buf);
+static ssize_t rmi_f19_configurable_show(struct device *dev,
+				      struct device_attribute *attr, char *buf);
+static ssize_t rmi_f19_filter_mode_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+static ssize_t rmi_f19_filter_mode_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+static ssize_t rmi_f19_button_usage_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+static ssize_t rmi_f19_button_usage_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+static ssize_t rmi_f19_interrupt_enable_button_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+static ssize_t rmi_f19_interrupt_enable_button_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+static ssize_t rmi_f19_single_button_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+static ssize_t rmi_f19_single_button_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+static ssize_t rmi_f19_sensor_map_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+static ssize_t rmi_f19_sensor_map_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+static ssize_t rmi_f19_sensitivity_adjust_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+static ssize_t rmi_f19_sensitivity_adjust_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+static ssize_t rmi_f19_hysteresis_threshold_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf);
+static ssize_t rmi_f19_hysteresis_threshold_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+
+
+
+static inline int rmi_f19_alloc_memory(struct rmi_function_container *fc);
+
+static inline void rmi_f19_free_memory(struct rmi_function_container *fc);
+
+static inline int rmi_f19_initialize(struct rmi_function_container *fc);
+
+static inline int rmi_f19_register_device(struct rmi_function_container *fc);
+
+static inline 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);
+
+static struct device_attribute attrs[] = {
+	__ATTR(button_count, RMI_RO_ATTR,
+		rmi_f19_button_count_show, rmi_store_error),
+	__ATTR(button_map, RMI_RW_ATTR,
+		rmi_f19_button_map_show, rmi_f19_button_map_store),
+	__ATTR(rezero, RMI_RW_ATTR,
+		rmi_f19_rezero_show, rmi_f19_rezero_store),
+	__ATTR(has_hysteresis_threshold, RMI_RO_ATTR,
+		rmi_f19_has_hysteresis_threshold_show, rmi_store_error),
+	__ATTR(has_sensitivity_adjust, RMI_RO_ATTR,
+		rmi_f19_has_sensitivity_adjust_show, rmi_store_error),
+	__ATTR(configurable, RMI_RO_ATTR,
+		rmi_f19_configurable_show, rmi_store_error),
+	__ATTR(filter_mode, RMI_RW_ATTR,
+		rmi_f19_filter_mode_show, rmi_f19_filter_mode_store),
+	__ATTR(button_usage, RMI_RW_ATTR,
+		rmi_f19_button_usage_show, rmi_f19_button_usage_store),
+	__ATTR(interrupt_enable_button, RMI_RW_ATTR,
+		rmi_f19_interrupt_enable_button_show,
+		rmi_f19_interrupt_enable_button_store),
+	__ATTR(single_button, RMI_RW_ATTR,
+		rmi_f19_single_button_show, rmi_f19_single_button_store),
+	__ATTR(sensor_map, RMI_RW_ATTR,
+		rmi_f19_sensor_map_show, rmi_f19_sensor_map_store),
+	__ATTR(sensitivity_adjust, RMI_RW_ATTR,
+		rmi_f19_sensitivity_adjust_show,
+		rmi_f19_sensitivity_adjust_store),
+	__ATTR(hysteresis_threshold, RMI_RW_ATTR,
+		rmi_f19_hysteresis_threshold_show,
+		rmi_f19_hysteresis_threshold_store)
+};
+
+
+int rmi_f19_read_control_parameters(struct rmi_device *rmi_dev,
+	struct f19_0d_control *button_control,
+	unsigned char button_count,
+	unsigned char int_button_enabled_count,
+	u8 ctrl_base_addr)
+{
+	int error = 0;
+	int i;
+
+	if (button_control->general_control) {
+		error = rmi_read_block(rmi_dev, ctrl_base_addr,
+				(u8 *)button_control->general_control,
+				sizeof(struct f19_0d_control_0));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read f19_0d_control_0, code:"
+				" %d.\n", error);
+			return error;
+		}
+		ctrl_base_addr = ctrl_base_addr +
+				sizeof(struct f19_0d_control_0);
+	}
+
+	if (button_control->button_int_enable) {
+		for (i = 0; i < int_button_enabled_count; i++) {
+			error = rmi_read_block(rmi_dev, ctrl_base_addr,
+				(u8 *)&button_control->button_int_enable[i],
+				sizeof(struct f19_0d_control_1));
+			if (error < 0) {
+				dev_err(&rmi_dev->dev,
+					"Failed to read f19_0d_control_2,"
+					" code: %d.\n", error);
+				return error;
+			}
+			ctrl_base_addr = ctrl_base_addr +
+				sizeof(struct f19_0d_control_1);
+		}
+	}
+
+	if (button_control->single_button_participation) {
+		for (i = 0; i < int_button_enabled_count; i++) {
+			error = rmi_read_block(rmi_dev, ctrl_base_addr,
+					(u8 *)&button_control->
+						single_button_participation[i],
+					sizeof(struct f19_0d_control_2));
+			if (error < 0) {
+				dev_err(&rmi_dev->dev,
+					"Failed to read f19_0d_control_2,"
+					" code: %d.\n", error);
+				return error;
+			}
+			ctrl_base_addr = ctrl_base_addr +
+				sizeof(struct f19_0d_control_2);
+		}
+	}
+
+	if (button_control->sensor_map) {
+		for (i = 0; i < button_count; i++) {
+			error = rmi_read_block(rmi_dev, ctrl_base_addr,
+					(u8 *)&button_control->sensor_map[i],
+					sizeof(struct f19_0d_control_3_4));
+			if (error < 0) {
+				dev_err(&rmi_dev->dev,
+				"Failed to read f19_0d_control_3_4,"
+				" code: %d.\n", error);
+				return error;
+			}
+			ctrl_base_addr = ctrl_base_addr +
+				sizeof(struct f19_0d_control_3_4);
+		}
+	}
+
+	if (button_control->all_button_sensitivity_adj) {
+		error = rmi_read_block(rmi_dev, ctrl_base_addr,
+				(u8 *)button_control->
+					all_button_sensitivity_adj,
+				sizeof(struct f19_0d_control_5));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read f19_0d_control_5,"
+				" code: %d.\n", error);
+			return error;
+		}
+		ctrl_base_addr = ctrl_base_addr +
+			sizeof(struct f19_0d_control_5);
+	}
+
+	if (button_control->all_button_hysteresis_threshold) {
+		error = rmi_read_block(rmi_dev, ctrl_base_addr,
+				(u8 *)button_control->
+					all_button_hysteresis_threshold,
+				sizeof(struct f19_0d_control_6));
+		if (error < 0) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read f19_0d_control_6,"
+				" code: %d.\n", error);
+			return error;
+		}
+		ctrl_base_addr = ctrl_base_addr +
+			sizeof(struct f19_0d_control_6);
+	}
+	return 0;
+}
+
+
+static int rmi_f19_init(struct rmi_function_container *fc)
+{
+	int rc;
+
+	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 inline int rmi_f19_alloc_memory(struct rmi_function_container *fc)
+{
+	struct f19_data *f19;
+	int rc;
+
+	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->button_query,
+						sizeof(struct f19_0d_query));
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to read query register.\n");
+		return rc;
+	}
+
+	f19->button_count = f19->button_query.f19_0d_query1;
+
+	f19->button_down = kcalloc(f19->button_count,
+			sizeof(bool), GFP_KERNEL);
+	if (!f19->button_down) {
+		dev_err(&fc->dev, "Failed to allocate button state buffer.\n");
+		return -ENOMEM;
+	}
+
+	f19->button_data_buffer_size = (f19->button_count + 7) / 8;
+	f19->button_data_buffer =
+	    kcalloc(f19->button_data_buffer_size,
+		    sizeof(unsigned char), GFP_KERNEL);
+	if (!f19->button_data_buffer) {
+		dev_err(&fc->dev, "Failed to allocate button data buffer.\n");
+		return -ENOMEM;
+	}
+
+	f19->button_map = kcalloc(f19->button_count,
+				sizeof(unsigned char), GFP_KERNEL);
+	if (!f19->button_map) {
+		dev_err(&fc->dev, "Failed to allocate button map.\n");
+		return -ENOMEM;
+	}
+
+	f19->button_control = kzalloc(sizeof(struct f19_0d_control),
+				GFP_KERNEL);
+	if (!f19->button_control) {
+		dev_err(&fc->dev, "Failed to allocate button_control.\n");
+		return -ENOMEM;
+	}
+
+	f19->button_control->general_control =
+		kzalloc(sizeof(struct f19_0d_control_0), GFP_KERNEL);
+	if (!f19->button_control->general_control) {
+		dev_err(&fc->dev, "Failed to allocate"
+				" f19_0d_control_0.\n");
+		return -ENOMEM;
+	}
+
+	f19->button_control->button_int_enable =
+		kzalloc(f19->button_data_buffer_size *
+			sizeof(struct f19_0d_control_2), GFP_KERNEL);
+	if (!f19->button_control->button_int_enable) {
+		dev_err(&fc->dev, "Failed to allocate f19_0d_control_1.\n");
+		return -ENOMEM;
+	}
+
+	f19->button_control->single_button_participation =
+		kzalloc(f19->button_data_buffer_size *
+			sizeof(struct f19_0d_control_2), GFP_KERNEL);
+	if (!f19->button_control->single_button_participation) {
+		dev_err(&fc->dev, "Failed to allocate"
+			" f19_0d_control_2.\n");
+		return -ENOMEM;
+	}
+
+	f19->button_control->sensor_map =
+		kzalloc(f19->button_count *
+			sizeof(struct f19_0d_control_3_4), GFP_KERNEL);
+	if (!f19->button_control->sensor_map) {
+		dev_err(&fc->dev, "Failed to allocate"
+			" f19_0d_control_3_4.\n");
+		return -ENOMEM;
+	}
+
+	f19->button_control->all_button_sensitivity_adj =
+		kzalloc(sizeof(struct f19_0d_control_5), GFP_KERNEL);
+	if (!f19->button_control->all_button_sensitivity_adj) {
+		dev_err(&fc->dev, "Failed to allocate"
+			" f19_0d_control_5.\n");
+		return -ENOMEM;
+	}
+
+	f19->button_control->all_button_hysteresis_threshold =
+		kzalloc(sizeof(struct f19_0d_control_6), GFP_KERNEL);
+	if (!f19->button_control->all_button_hysteresis_threshold) {
+		dev_err(&fc->dev, "Failed to allocate"
+			" f19_0d_control_6.\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+
+
+static inline void rmi_f19_free_memory(struct rmi_function_container *fc)
+{
+	struct f19_data *f19 = fc->data;
+
+	if (f19) {
+		kfree(f19->button_down);
+		kfree(f19->button_data_buffer);
+		kfree(f19->button_map);
+		if (f19->button_control) {
+			kfree(f19->button_control->general_control);
+			kfree(f19->button_control->button_int_enable);
+			kfree(f19->button_control->single_button_participation);
+			kfree(f19->button_control->sensor_map);
+			kfree(f19->button_control->all_button_sensitivity_adj);
+			kfree(f19->button_control->
+				  all_button_hysteresis_threshold);
+			kfree(f19->button_control);
+		}
+		kfree(f19);
+		fc->data = NULL;
+	}
+}
+
+
+static inline int rmi_f19_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_device_platform_data *pdata;
+	struct f19_data *f19 = fc->data;
+	int i;
+	int rc;
+
+	dev_info(&fc->dev, "Intializing F19 values.");
+
+	/* 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->button_map) {
+			dev_warn(&fc->dev, "%s - button_map is NULL", __func__);
+		} else if (pdata->button_map->nbuttons != f19->button_count) {
+			dev_warn(&fc->dev,
+				"Platformdata button map size (%d) != number "
+				"of buttons on device (%d) - ignored.\n",
+				pdata->button_map->nbuttons,
+				f19->button_count);
+		} else if (!pdata->button_map->map) {
+			dev_warn(&fc->dev,
+				 "Platformdata button map is missing!\n");
+		} else {
+			for (i = 0; i < pdata->button_map->nbuttons; i++)
+				f19->button_map[i] = pdata->button_map->map[i];
+		}
+	}
+
+	rc = rmi_f19_read_control_parameters(rmi_dev,
+						  f19->button_control,
+						  f19->button_count,
+						  f19->button_data_buffer_size,
+						  fc->fd.control_base_addr);
+	if (rc < 0) {
+		dev_err(&fc->dev,
+			"Failed to initialize F19 control params.\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+
+
+static inline int rmi_f19_register_device(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct input_dev *input_dev;
+	struct f19_data *f19 = 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;
+	}
+
+	f19->input = input_dev;
+	snprintf(f19->input_name, MAX_LEN, "%sfn%02x", dev_name(&rmi_dev->dev),
+		fc->fd.function_number);
+	input_dev->name = f19->input_name;
+	snprintf(f19->input_phys, MAX_LEN, "%s/input0", input_dev->name);
+	input_dev->phys = f19->input_phys;
+	input_dev->dev.parent = &rmi_dev->dev;
+	input_set_drvdata(input_dev, f19);
+
+	/* Set up any input events. */
+	set_bit(EV_SYN, input_dev->evbit);
+	set_bit(EV_KEY, input_dev->evbit);
+	/* set bits for each button... */
+	for (i = 0; i < f19->button_count; i++)
+		set_bit(f19->button_map[i], input_dev->keybit);
+	rc = input_register_device(input_dev);
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to register input device.\n");
+		goto error_free_device;
+	}
+
+	return 0;
+
+error_free_device:
+	input_free_device(input_dev);
+
+	return rc;
+}
+
+
+static inline int rmi_f19_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 rmi_f19_config(struct rmi_function_container *fc)
+{
+	struct f19_data *data;
+	int retval;
+	int ctrl_base_addr;
+	int button_reg;
+
+	data = fc->data;
+
+	ctrl_base_addr = fc->fd.control_base_addr;
+
+	button_reg = (data->button_count / 7) + 1;
+
+	retval = rmi_write_block(fc->rmi_dev, ctrl_base_addr,
+		(u8 *)data->button_control->general_control,
+			sizeof(struct f19_0d_control_0));
+	if (retval < 0) {
+		dev_err(&fc->dev, "%s : Could not write general_control to 0x%x\n",
+				__func__, fc->fd.control_base_addr);
+		return retval;
+	}
+
+	ctrl_base_addr += sizeof(struct f19_0d_control_0);
+	retval = rmi_write_block(fc->rmi_dev, ctrl_base_addr,
+		(u8 *)data->button_control->button_int_enable,
+			sizeof(struct f19_0d_control_1)*(button_reg + 1));
+	if (retval < 0) {
+		dev_err(&fc->dev, "%s : Could not write interrupt_enable_store"
+			" to 0x%x\n", __func__, ctrl_base_addr);
+		return retval;
+	}
+
+	ctrl_base_addr += sizeof(struct f19_0d_control_2)*(button_reg + 1);
+	retval = rmi_write_block(fc->rmi_dev, ctrl_base_addr,
+		(u8 *)data->button_control->single_button_participation,
+			sizeof(struct f19_0d_control_2)*(button_reg + 1));
+	if (retval < 0) {
+		dev_err(&fc->dev,
+				"%s : Could not write interrupt_enable_store to"
+				" 0x%x\n", __func__, ctrl_base_addr);
+		return -EINVAL;
+	}
+
+	ctrl_base_addr = fc->fd.control_base_addr +
+		sizeof(struct f19_0d_control_0) +
+		sizeof(struct f19_0d_control_1)*button_reg +
+		sizeof(struct f19_0d_control_2)*button_reg;
+	retval = rmi_write_block(fc->rmi_dev, ctrl_base_addr,
+		(u8 *)data->button_control->sensor_map,
+			sizeof(struct f19_0d_control_3_4)*data->button_count);
+	if (retval < 0) {
+		dev_err(&fc->dev, "%s : Could not sensor_map_store to 0x%x\n",
+				__func__, ctrl_base_addr);
+		return -EINVAL;
+	}
+		/* write back to the control register */
+	ctrl_base_addr += sizeof(struct f19_0d_control_3_4)*data->button_count;
+	retval = rmi_write_block(fc->rmi_dev, ctrl_base_addr,
+		(u8 *)data->button_control->all_button_sensitivity_adj,
+			sizeof(struct f19_0d_control_5));
+	if (retval < 0) {
+		dev_err(&fc->dev, "%s : Could not sensitivity_adjust_store to"
+			" 0x%x\n", __func__, ctrl_base_addr);
+		return retval;
+	}
+
+	ctrl_base_addr += sizeof(struct f19_0d_control_5);
+	retval = rmi_write_block(fc->rmi_dev, ctrl_base_addr,
+		(u8 *)data->button_control->all_button_sensitivity_adj,
+			sizeof(struct f19_0d_control_6));
+	if (retval < 0) {
+		dev_err(&fc->dev, "%s : Could not write all_button hysteresis "
+			"threshold to 0x%x\n", __func__, ctrl_base_addr);
+		return -EINVAL;
+	}
+
+	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;
+	int attr_count = 0;
+
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
+		sysfs_remove_file(&fc->dev.kobj,
+				  &attrs[attr_count].attr);
+
+	input_unregister_device(f19->input);
+
+	rmi_f19_free_memory(fc);
+}
+
+int rmi_f19_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct f19_data *f19 = fc->data;
+	u8 data_base_addr = fc->fd.data_base_addr;
+	int error;
+	int button;
+
+	/* Read the button data. */
+
+	error = rmi_read_block(rmi_dev, data_base_addr, f19->button_data_buffer,
+			f19->button_data_buffer_size);
+	if (error < 0) {
+		dev_err(&fc->dev, "%s: Failed to read button data registers.\n",
+			__func__);
+		return error;
+	}
+
+	/* Generate events for buttons that change state. */
+	for (button = 0; button < f19->button_count;
+	     button++) {
+		int button_reg;
+		int button_shift;
+		bool button_status;
+
+		/* determine which data byte the button status is in */
+		button_reg = button / 7;
+		/* bit shift to get button's status */
+		button_shift = button % 8;
+		button_status =
+		    ((f19->button_data_buffer[button_reg] >> button_shift)
+			& 0x01) != 0;
+
+		/* if the button state changed from the last time report it
+		 * and store the new state */
+		if (button_status != f19->button_down[button]) {
+			dev_dbg(&fc->dev, "%s: Button %d (code %d) -> %d.\n",
+				__func__, button, f19->button_map[button],
+				 button_status);
+			/* Generate an event here. */
+			input_report_key(f19->input, f19->button_map[button],
+					 button_status);
+			f19->button_down[button] = button_status;
+		}
+	}
+
+	input_sync(f19->input); /* sync after groups of events */
+	return 0;
+}
+
+static 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);
+}
+
+static ssize_t rmi_f19_filter_mode_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->button_control->general_control->filter_mode);
+
+}
+
+static ssize_t rmi_f19_filter_mode_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+	unsigned int new_value;
+	int result;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	if (sscanf(buf, "%u", &new_value) != 1) {
+		dev_err(dev,
+		"%s: Error - filter_mode_store has an "
+		"invalid len.\n",
+		__func__);
+		return -EINVAL;
+	}
+
+	if (new_value < 0 || new_value > 4) {
+		dev_err(dev, "%s: Error - filter_mode_store has an "
+		"invalid value %d.\n",
+		__func__, new_value);
+		return -EINVAL;
+	}
+	data->button_control->general_control->filter_mode = new_value;
+	result = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+		(u8 *)data->button_control->general_control,
+			sizeof(struct f19_0d_control_0));
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write filter_mode_store to 0x%x\n",
+				__func__, fc->fd.control_base_addr);
+		return result;
+	}
+
+	return count;
+}
+
+static ssize_t rmi_f19_button_usage_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->button_control->general_control->button_usage);
+
+}
+
+static ssize_t rmi_f19_button_usage_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+	unsigned int new_value;
+	int result;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	if (sscanf(buf, "%u", &new_value) != 1) {
+		dev_err(dev,
+		"%s: Error - button_usage_store has an "
+		"invalid len.\n",
+		__func__);
+		return -EINVAL;
+	}
+
+	if (new_value < 0 || new_value > 4) {
+		dev_err(dev, "%s: Error - button_usage_store has an "
+		"invalid value %d.\n",
+		__func__, new_value);
+		return -EINVAL;
+	}
+	data->button_control->general_control->button_usage = new_value;
+	result = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+		(u8 *)data->button_control->general_control,
+			sizeof(struct f19_0d_control_0));
+	if (result < 0) {
+		dev_err(dev, "%s: Could not write button_usage_store to 0x%x\n",
+				__func__, fc->fd.control_base_addr);
+		return result;
+	}
+
+	return count;
+
+}
+
+static ssize_t rmi_f19_interrupt_enable_button_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+	int i, len, total_len = 0;
+	char *current_buf = buf;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	/* loop through each button map value and copy its
+	 * string representation into buf */
+	for (i = 0; i < data->button_count; i++) {
+		int button_reg;
+		int button_shift;
+		int interrupt_button;
+
+		button_reg = i / 7;
+		button_shift = i % 8;
+		interrupt_button =
+		    ((data->button_control->
+			button_int_enable[button_reg].int_enabled_button >>
+				button_shift) & 0x01);
+
+		/* get next button mapping value and write it to buf */
+		len = snprintf(current_buf, PAGE_SIZE - total_len,
+			"%u ", interrupt_button);
+		/* bump up ptr to next location in buf if the
+		 * snprintf was valid.  Otherwise issue an error
+		 * and return. */
+		if (len > 0) {
+			current_buf += len;
+			total_len += len;
+		} else {
+			dev_err(dev, "%s: Failed to build interrupt button"
+				" buffer, code = %d.\n", __func__, len);
+			return snprintf(buf, PAGE_SIZE, "unknown\n");
+		}
+	}
+	len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
+	if (len > 0)
+		total_len += len;
+	else
+		dev_warn(dev, "%s: Failed to append carriage return.\n",
+			 __func__);
+	return total_len;
+
+}
+
+static ssize_t rmi_f19_interrupt_enable_button_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+	int i;
+	int button_count = 0;
+	int retval = count;
+	int button_reg = 0;
+	int ctrl_bass_addr;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	for (i = 0; i < data->button_count && *buf != 0;
+	     i++) {
+		int button_shift;
+		int button;
+
+		button_reg = i / 7;
+		button_shift = i % 8;
+		/* get next button mapping value and store and bump up to
+		 * point to next item in buf */
+		sscanf(buf, "%u", &button);
+
+		if (button != 0 && button != 1) {
+			dev_err(dev,
+				"%s: Error - interrupt enable button for"
+				" button %d is not a valid value 0x%x.\n",
+				__func__, i, button);
+			return -EINVAL;
+		}
+
+		if (button_shift == 0)
+			data->button_control->button_int_enable[button_reg].
+				int_enabled_button = 0;
+		data->button_control->button_int_enable[button_reg].
+			int_enabled_button |= (button << button_shift);
+		button_count++;
+		/* bump up buf to point to next item to read */
+		while (*buf != 0) {
+			buf++;
+			if (*(buf - 1) == ' ')
+				break;
+		}
+	}
+
+	/* Make sure the button count matches */
+	if (button_count != data->button_count) {
+		dev_err(dev,
+			"%s: Error - interrupt enable button count of %d"
+			" doesn't match device button count of %d.\n",
+			 __func__, button_count, data->button_count);
+		return -EINVAL;
+	}
+
+	/* write back to the control register */
+	ctrl_bass_addr = fc->fd.control_base_addr +
+			sizeof(struct f19_0d_control_0);
+	retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+		(u8 *)data->button_control->button_int_enable,
+			sizeof(struct f19_0d_control_1)*(button_reg + 1));
+	if (retval < 0) {
+		dev_err(dev, "%s : Could not write interrupt_enable_store"
+			" to 0x%x\n", __func__, ctrl_bass_addr);
+		return retval;
+	}
+
+	return count;
+}
+
+static ssize_t rmi_f19_single_button_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+	int i, len, total_len = 0;
+	char *current_buf = buf;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	/* loop through each button map value and copy its
+	 * string representation into buf */
+	for (i = 0; i < data->button_count; i++) {
+		int button_reg;
+		int button_shift;
+		int single_button;
+
+		button_reg = i / 7;
+		button_shift = i % 8;
+		single_button = ((data->button_control->
+			single_button_participation[button_reg].single_button
+			>> button_shift) & 0x01);
+
+		/* get next button mapping value and write it to buf */
+		len = snprintf(current_buf, PAGE_SIZE - total_len,
+			"%u ", single_button);
+		/* bump up ptr to next location in buf if the
+		 * snprintf was valid.  Otherwise issue an error
+		 * and return. */
+		if (len > 0) {
+			current_buf += len;
+			total_len += len;
+		} else {
+			dev_err(dev, "%s: Failed to build signle button buffer"
+				", code = %d.\n", __func__, len);
+			return snprintf(buf, PAGE_SIZE, "unknown\n");
+		}
+	}
+	len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
+	if (len > 0)
+		total_len += len;
+	else
+		dev_warn(dev, "%s: Failed to append carriage return.\n",
+			 __func__);
+
+	return total_len;
+
+}
+
+static ssize_t rmi_f19_single_button_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+	int i;
+	int button_count = 0;
+	int retval = count;
+	int ctrl_bass_addr;
+	int button_reg = 0;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	for (i = 0; i < data->button_count && *buf != 0;
+	     i++) {
+		int button_shift;
+		int button;
+
+		button_reg = i / 7;
+		button_shift = i % 8;
+		/* get next button mapping value and store and bump up to
+		 * point to next item in buf */
+		sscanf(buf, "%u", &button);
+
+		if (button != 0 && button != 1) {
+			dev_err(dev,
+				"%s: Error - single button for button %d"
+				" is not a valid value 0x%x.\n",
+				__func__, i, button);
+			return -EINVAL;
+		}
+		if (button_shift == 0)
+			data->button_control->
+				single_button_participation[button_reg].
+				single_button = 0;
+		data->button_control->single_button_participation[button_reg].
+			single_button |=  (button << button_shift);
+		button_count++;
+		/* bump up buf to point to next item to read */
+		while (*buf != 0) {
+			buf++;
+			if (*(buf - 1) == ' ')
+				break;
+		}
+	}
+
+	/* Make sure the button count matches */
+	if (button_count != data->button_count) {
+		dev_err(dev,
+		    "%s: Error - single button count of %d doesn't match"
+		     " device button count of %d.\n", __func__, button_count,
+		     data->button_count);
+		return -EINVAL;
+	}
+	/* write back to the control register */
+	ctrl_bass_addr = fc->fd.control_base_addr +
+		sizeof(struct f19_0d_control_0) +
+		sizeof(struct f19_0d_control_2)*(button_reg + 1);
+	retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+		(u8 *)data->button_control->single_button_participation,
+			sizeof(struct f19_0d_control_2)*(button_reg + 1));
+	if (retval < 0) {
+		dev_err(dev, "%s : Could not write interrupt_enable_store to"
+			" 0x%x\n", __func__, ctrl_bass_addr);
+		return -EINVAL;
+	}
+	return count;
+}
+
+static ssize_t rmi_f19_sensor_map_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+	int i, len, total_len = 0;
+	char *current_buf = buf;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	for (i = 0; i < data->button_count; i++) {
+		len = snprintf(current_buf, PAGE_SIZE - total_len,
+			"%u ", data->button_control->sensor_map[i].
+			sensor_map_button);
+		/* bump up ptr to next location in buf if the
+		 * snprintf was valid.  Otherwise issue an error
+		 * and return. */
+		if (len > 0) {
+			current_buf += len;
+			total_len += len;
+		} else {
+			dev_err(dev, "%s: Failed to build sensor map buffer, "
+				"code = %d.\n", __func__, len);
+			return snprintf(buf, PAGE_SIZE, "unknown\n");
+		}
+	}
+	len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
+	if (len > 0)
+		total_len += len;
+	else
+		dev_warn(dev, "%s: Failed to append carriage return.\n",
+			 __func__);
+	return total_len;
+
+
+}
+
+static ssize_t rmi_f19_sensor_map_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+	int sensor_map;
+	int i;
+	int retval = count;
+	int button_count = 0;
+	int ctrl_bass_addr;
+	int button_reg;
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	if (data->button_query.configurable == 0) {
+		dev_err(dev,
+			"%s: Error - sensor map is not configuralbe at"
+			" run-time", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < data->button_count && *buf != 0; i++) {
+		/* get next button mapping value and store and bump up to
+		 * point to next item in buf */
+		sscanf(buf, "%u", &sensor_map);
+
+		/* Make sure the key is a valid key */
+		if (sensor_map < 0 || sensor_map > 127) {
+			dev_err(dev,
+				"%s: Error - sensor map for button %d is"
+				" not a valid value 0x%x.\n",
+				__func__, i, sensor_map);
+			return -EINVAL;
+		}
+
+		data->button_control->sensor_map[i].sensor_map_button =
+			sensor_map;
+		button_count++;
+
+		/* bump up buf to point to next item to read */
+		while (*buf != 0) {
+			buf++;
+			if (*(buf - 1) == ' ')
+				break;
+		}
+	}
+
+	if (button_count != data->button_count) {
+		dev_err(dev,
+		    "%s: Error - button map count of %d doesn't match device "
+		     "button count of %d.\n", __func__, button_count,
+		     data->button_count);
+		return -EINVAL;
+	}
+
+	/* write back to the control register */
+	button_reg = (button_count / 7) + 1;
+	ctrl_bass_addr = fc->fd.control_base_addr +
+		sizeof(struct f19_0d_control_0) +
+		sizeof(struct f19_0d_control_1)*button_reg +
+		sizeof(struct f19_0d_control_2)*button_reg;
+	retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+		(u8 *)data->button_control->sensor_map,
+			sizeof(struct f19_0d_control_3_4)*button_count);
+	if (retval < 0) {
+		dev_err(dev, "%s : Could not sensor_map_store to 0x%x\n",
+				__func__, ctrl_bass_addr);
+		return -EINVAL;
+	}
+	return count;
+}
+
+static ssize_t rmi_f19_sensitivity_adjust_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", data->button_control->
+		all_button_sensitivity_adj->sensitivity_adj);
+
+}
+
+static ssize_t rmi_f19_sensitivity_adjust_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+	unsigned int new_value;
+	int len;
+	int ctrl_bass_addr;
+	int button_reg;
+
+	fc = to_rmi_function_container(dev);
+
+	data = fc->data;
+
+	if (data->button_query.configurable == 0) {
+		dev_err(dev,
+			"%s: Error - sensitivity_adjust is not"
+			" configuralbe at run-time", __func__);
+		return -EINVAL;
+	}
+
+	len = sscanf(buf, "%u", &new_value);
+	if (new_value < 0 || new_value > 31)
+		return -EINVAL;
+
+	data->button_control->all_button_sensitivity_adj->sensitivity_adj =
+		 new_value;
+	/* write back to the control register */
+	button_reg = (data->button_count / 7) + 1;
+	ctrl_bass_addr = fc->fd.control_base_addr +
+		sizeof(struct f19_0d_control_0) +
+		sizeof(struct f19_0d_control_1)*button_reg +
+		sizeof(struct f19_0d_control_2)*button_reg +
+		sizeof(struct f19_0d_control_3_4)*data->button_count;
+	len = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+		(u8 *)data->button_control->all_button_sensitivity_adj,
+			sizeof(struct f19_0d_control_5));
+	if (len < 0) {
+		dev_err(dev, "%s : Could not sensitivity_adjust_store to"
+			" 0x%x\n", __func__, ctrl_bass_addr);
+		return len;
+	}
+
+	return len;
+}
+
+static ssize_t rmi_f19_hysteresis_threshold_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", data->button_control->
+		all_button_hysteresis_threshold->hysteresis_threshold);
+
+}
+static ssize_t rmi_f19_hysteresis_threshold_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+	unsigned int new_value;
+	int len;
+	int ctrl_bass_addr;
+	int button_reg;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	len = sscanf(buf, "%u", &new_value);
+	if (new_value < 0 || new_value > 15) {
+		dev_err(dev, "%s: Error - hysteresis_threshold_store has an "
+		"invalid value %d.\n",
+		__func__, new_value);
+		return -EINVAL;
+	}
+	data->button_control->all_button_hysteresis_threshold->
+		hysteresis_threshold = new_value;
+	/* write back to the control register */
+	button_reg = (data->button_count / 7) + 1;
+	ctrl_bass_addr = fc->fd.control_base_addr +
+		sizeof(struct f19_0d_control_0) +
+		sizeof(struct f19_0d_control_1)*button_reg +
+		sizeof(struct f19_0d_control_2)*button_reg +
+		sizeof(struct f19_0d_control_3_4)*data->button_count+
+		sizeof(struct f19_0d_control_5);
+	len = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+		(u8 *)data->button_control->all_button_sensitivity_adj,
+			sizeof(struct f19_0d_control_6));
+	if (len < 0) {
+		dev_err(dev, "%s : Could not write all_button hysteresis "
+			"threshold to 0x%x\n", __func__, ctrl_bass_addr);
+		return -EINVAL;
+	}
+
+	return count;
+}
+
+static ssize_t rmi_f19_has_hysteresis_threshold_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->button_query.has_hysteresis_threshold);
+}
+
+static ssize_t rmi_f19_has_sensitivity_adjust_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->button_query.has_sensitivity_adjust);
+}
+
+static ssize_t rmi_f19_configurable_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->button_query.configurable);
+}
+
+static ssize_t rmi_f19_rezero_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->button_rezero);
+
+}
+
+static ssize_t rmi_f19_rezero_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+	unsigned int new_value;
+	int len;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	len = sscanf(buf, "%u", &new_value);
+	if (new_value != 0 && new_value != 1) {
+		dev_err(dev,
+			"%s: Error - rezero is not a "
+			"valid value 0x%x.\n",
+			__func__, new_value);
+		return -EINVAL;
+	}
+	data->button_rezero = new_value & 1;
+	len = rmi_write(fc->rmi_dev, fc->fd.command_base_addr,
+		data->button_rezero);
+
+	if (len < 0) {
+		dev_err(dev, "%s : Could not write rezero to 0x%x\n",
+				__func__, fc->fd.command_base_addr);
+		return -EINVAL;
+	}
+	return count;
+}
+
+static ssize_t rmi_f19_button_count_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->button_count);
+}
+
+static ssize_t rmi_f19_button_map_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+	int i, len, total_len = 0;
+	char *current_buf = buf;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	/* loop through each button map value and copy its
+	 * string representation into buf */
+	for (i = 0; i < data->button_count; i++) {
+		/* get next button mapping value and write it to buf */
+		len = snprintf(current_buf, PAGE_SIZE - total_len,
+			"%u ", data->button_map[i]);
+		/* bump up ptr to next location in buf if the
+		 * snprintf was valid.  Otherwise issue an error
+		 * and return. */
+		if (len > 0) {
+			current_buf += len;
+			total_len += len;
+		} else {
+			dev_err(dev, "%s: Failed to build button map buffer, "
+				"code = %d.\n", __func__, len);
+			return snprintf(buf, PAGE_SIZE, "unknown\n");
+		}
+	}
+	len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
+	if (len > 0)
+		total_len += len;
+	else
+		dev_warn(dev, "%s: Failed to append carriage return.\n",
+			 __func__);
+	return total_len;
+}
+
+static ssize_t rmi_f19_button_map_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,
+				size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f19_data *data;
+	unsigned int button;
+	int i;
+	int retval = count;
+	int button_count = 0;
+	unsigned char temp_button_map[KEY_MAX];
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	/* Do validation on the button map data passed in.  Store button
+	 * mappings into a temp buffer and then verify button count and
+	 * data prior to clearing out old button mappings and storing the
+	 * new ones. */
+	for (i = 0; i < data->button_count && *buf != 0;
+	     i++) {
+		/* get next button mapping value and store and bump up to
+		 * point to next item in buf */
+		sscanf(buf, "%u", &button);
+
+		/* Make sure the key is a valid key */
+		if (button > KEY_MAX) {
+			dev_err(dev,
+				"%s: Error - button map for button %d is not a"
+				" valid value 0x%x.\n", __func__, i, button);
+			retval = -EINVAL;
+			goto err_ret;
+		}
+
+		temp_button_map[i] = button;
+		button_count++;
+
+		/* bump up buf to point to next item to read */
+		while (*buf != 0) {
+			buf++;
+			if (*(buf - 1) == ' ')
+				break;
+		}
+	}
+
+	/* Make sure the button count matches */
+	if (button_count != data->button_count) {
+		dev_err(dev,
+		    "%s: Error - button map count of %d doesn't match device "
+		     "button count of %d.\n", __func__, button_count,
+		     data->button_count);
+		retval = -EINVAL;
+		goto err_ret;
+	}
+
+	/* Clear the key bits for the old button map. */
+	for (i = 0; i < button_count; i++)
+		clear_bit(data->button_map[i], data->input->keybit);
+
+	/* Switch to the new map. */
+	memcpy(data->button_map, temp_button_map,
+	       data->button_count);
+
+	/* Loop through the key map and set the key bit for the new mapping. */
+	for (i = 0; i < button_count; i++)
+		set_bit(data->button_map[i], data->input->keybit);
+
+err_ret:
+	return retval;
+}
+
+module_init(rmi_f19_module_init);
+module_exit(rmi_f19_module_exit);
+
+MODULE_AUTHOR("Vivian Ly <vly@synaptics.com>");
+MODULE_DESCRIPTION("RMI F19 module");
+MODULE_LICENSE("GPL");
+

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

* [RFC PATCH 11/11] input: RMI4 F11 - multifinger pointing
  2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (9 preceding siblings ...)
  2011-12-22  2:10 ` [RFC PATCH 10/11] input: RMI4 F19 - capacitive buttons Christopher Heiny
@ 2011-12-22  2:10 ` Christopher Heiny
  2012-01-01 13:51 ` [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Linus Walleij
  2012-01-05  7:58 ` Dmitry Torokhov
  12 siblings, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2011-12-22  2:10 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Christopher Heiny,
	Joerie de Gram, 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>

---

 drivers/input/rmi4/rmi_f11.c | 1860 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1860 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..9516995
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f11.c
@@ -0,0 +1,1860 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/rmi.h>
+
+#define RESUME_REZERO (1 && defined(CONFIG_PM))
+#if RESUME_REZERO
+#include <linux/delay.h>
+#define DEFAULT_REZERO_WAIT_MS	40
+#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 F11_FINGER_STATE_MASK	0x03
+#define F11_FINGER_STATE_SIZE	0x02
+#define F11_FINGER_STATE_MASK_N(i) \
+		(F11_FINGER_STATE_MASK << (i%4 * F11_FINGER_STATE_SIZE))
+
+#define F11_FINGER_STATE_VAL_N(f_state, i) \
+		(f_state >> (i%4 * F11_FINGER_STATE_SIZE))
+
+#define F11_CTRL_SENSOR_MAX_X_POS_OFFSET	6
+#define F11_CTRL_SENSOR_MAX_Y_POS_OFFSET	8
+
+#define F11_CEIL(x, y) (((x) + ((y)-1)) / (y))
+
+/* By default, we'll support two fingers if we can't figure out how many we
+ * really need to handle.
+ */
+#define DEFAULT_NR_OF_FINGERS 2
+#define DEFAULT_XY_MAX 9999
+#define DEFAULT_MAX_ABS_MT_PRESSURE 255
+#define DEFAULT_MAX_ABS_MT_TOUCH 15
+#define DEFAULT_MAX_ABS_MT_ORIENTATION 1
+#define DEFAULT_MIN_ABS_MT_TRACKING_ID 1
+#define DEFAULT_MAX_ABS_MT_TRACKING_ID 10
+#define MAX_LEN 256
+
+static ssize_t f11_flip_show(struct device *dev,
+				   struct device_attribute *attr, char *buf);
+
+static ssize_t f11_flip_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count);
+
+static ssize_t f11_clip_show(struct device *dev,
+				   struct device_attribute *attr, char *buf);
+
+static ssize_t f11_clip_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count);
+
+static ssize_t f11_offset_show(struct device *dev,
+				     struct device_attribute *attr, char *buf);
+
+static ssize_t f11_offset_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t count);
+
+static ssize_t f11_swap_show(struct device *dev,
+				   struct device_attribute *attr, char *buf);
+
+static ssize_t f11_swap_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count);
+
+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 inline void rmi_f11_free_memory(struct rmi_function_container *fc);
+
+static inline int rmi_f11_initialize(struct rmi_function_container *fc);
+
+static inline 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 inline int rmi_f11_register_devices(struct rmi_function_container *fc);
+
+static inline void rmi_f11_free_devices(struct rmi_function_container *fc);
+
+
+static struct device_attribute attrs[] = {
+	__ATTR(flip, RMI_RW_ATTR, f11_flip_show, f11_flip_store),
+	__ATTR(clip, RMI_RW_ATTR, f11_clip_show, f11_clip_store),
+	__ATTR(offset, RMI_RW_ATTR, f11_offset_show, f11_offset_store),
+	__ATTR(swap, RMI_RW_ATTR, f11_swap_show, f11_swap_store),
+	__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];
+	};
+
+	/* Empty */
+	union f11_2d_query9 *query9;
+
+	union {
+		struct {
+			u8 nbr_touch_shapes:5;
+		};
+		u8 f11_2d_query10;
+	};
+};
+
+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];
+};
+
+struct  f11_2d_ctrl {
+
+	union {
+		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;
+		};
+		u8 ctrl0_9[10];
+	};
+
+	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;
+};
+
+struct f11_2d_data_0 {
+	u8 finger_n;
+};
+
+struct f11_2d_data_1_5 {
+	u8 x_msb;
+	u8 y_msb;
+	u8 x_lsb:4;
+	u8 y_lsb:4;
+	u8 w_y:4;
+	u8 w_x:4;
+	u8 z;
+};
+
+struct f11_2d_data_6_7 {
+	s8 delta_x;
+	s8 delta_y;
+};
+
+struct f11_2d_data_8 {
+	u8 single_tap:1;
+	u8 tap_and_hold:1;
+	u8 double_tap:1;
+	u8 early_tap:1;
+	u8 flick:1;
+	u8 press:1;
+	u8 pinch:1;
+};
+
+struct f11_2d_data_9 {
+	u8 palm_detect:1;
+	u8 rotate:1;
+	u8 shape:1;
+	u8 scrollzone:1;
+	u8 finger_count:3;
+};
+
+struct f11_2d_data_10 {
+	u8 pinch_motion;
+};
+
+struct f11_2d_data_10_12 {
+	u8 x_flick_dist;
+	u8 y_flick_dist;
+	u8 flick_time;
+};
+
+struct f11_2d_data_11_12 {
+	u8 motion;
+	u8 finger_separation;
+};
+
+struct f11_2d_data_13 {
+	u8 shape_n;
+};
+
+struct f11_2d_data_14_15 {
+	u8 horizontal;
+	u8 vertical;
+};
+
+struct f11_2d_data_14_17 {
+	u8 x_low;
+	u8 y_right;
+	u8 x_upper;
+	u8 y_left;
+};
+
+struct f11_2d_data {
+	const struct f11_2d_data_0	*f_state;
+	const struct f11_2d_data_1_5	*abs_pos;
+	const struct f11_2d_data_6_7	*rel_pos;
+	const struct f11_2d_data_8	*gest_1;
+	const struct f11_2d_data_9	*gest_2;
+	const struct f11_2d_data_10	*pinch;
+	const struct f11_2d_data_10_12	*flick;
+	const struct f11_2d_data_11_12	*rotate;
+	const struct f11_2d_data_13	*shapes;
+	const struct f11_2d_data_14_15	*multi_scroll;
+	const struct f11_2d_data_14_17	*scroll_zones;
+};
+
+struct f11_2d_sensor {
+	struct rmi_f11_2d_axis_alignment axis_align;
+	struct f11_2d_sensor_query sens_query;
+	struct f11_2d_data data;
+	u16 max_x;
+	u16 max_y;
+	u8 nbr_fingers;
+	u8 finger_tracker[F11_MAX_NUM_OF_FINGERS];
+	u8 *data_pkt;
+	int pkt_size;
+	u8 sensor_index;
+	char input_name[MAX_LEN];
+	char input_phys[MAX_LEN];
+	struct input_dev *input;
+	struct input_dev *mouse_input;
+};
+
+struct f11_data {
+	struct f11_2d_device_query dev_query;
+	struct f11_2d_ctrl dev_controls;
+#if	RESUME_REZERO
+	u16 rezero_wait_ms;
+	bool rezero_on_resume;
+#endif
+	struct f11_2d_sensor sensors[F11_MAX_NUM_OF_SENSORS];
+};
+
+enum finger_state_values {
+	F11_NO_FINGER	= 0x00,
+	F11_PRESENT	= 0x01,
+	F11_INACCURATE	= 0x02,
+	F11_RESERVED	= 0x03
+};
+
+static void rmi_f11_rel_pos_report(struct f11_2d_sensor *sensor, u8 n_finger)
+{
+	struct f11_2d_data *data = &sensor->data;
+	struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align;
+	s8 x, y;
+	s8 temp;
+
+	x = data->rel_pos[n_finger].delta_x;
+	y = data->rel_pos[n_finger].delta_y;
+
+	x = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)x));
+	y = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)y));
+
+	if (axis_align->swap_axes) {
+		temp = x;
+		x = y;
+		y = temp;
+	}
+	if (axis_align->flip_x)
+		x = min(F11_REL_POS_MAX, -x);
+	if (axis_align->flip_y)
+		y = min(F11_REL_POS_MAX, -y);
+
+	if (x || y) {
+		input_report_rel(sensor->input, REL_X, x);
+		input_report_rel(sensor->input, REL_Y, y);
+		input_report_rel(sensor->mouse_input, REL_X, x);
+		input_report_rel(sensor->mouse_input, REL_Y, y);
+	}
+	input_sync(sensor->mouse_input);
+}
+
+static void rmi_f11_abs_pos_report(struct f11_2d_sensor *sensor,
+					u8 finger_state, u8 n_finger)
+{
+	struct f11_2d_data *data = &sensor->data;
+	struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align;
+	int prev_state = sensor->finger_tracker[n_finger];
+	int x, y, z;
+	int w_x, w_y, w_max, w_min, orient;
+	int temp;
+
+	if (prev_state && !finger_state) {
+		/* this is a release */
+		x = y = z = w_max = w_min = orient = 0;
+	} else if (!prev_state && !finger_state) {
+		/* nothing to report */
+		return;
+	} else {
+		x = ((data->abs_pos[n_finger].x_msb << 4) |
+			data->abs_pos[n_finger].x_lsb);
+		y = ((data->abs_pos[n_finger].y_msb << 4) |
+			data->abs_pos[n_finger].y_lsb);
+		z = data->abs_pos[n_finger].z;
+		w_x = data->abs_pos[n_finger].w_x;
+		w_y = data->abs_pos[n_finger].w_y;
+		w_max = max(w_x, w_y);
+		w_min = min(w_x, w_y);
+
+		if (axis_align->swap_axes) {
+			temp = x;
+			x = y;
+			y = temp;
+			temp = w_x;
+			w_x = w_y;
+			w_y = temp;
+		}
+
+		orient = w_x > w_y ? 1 : 0;
+
+		if (axis_align->flip_x)
+			x = max(sensor->max_x - x, 0);
+
+		if (axis_align->flip_y)
+			y = max(sensor->max_y - y, 0);
+
+		/*
+		** here checking if X offset or y offset are specified is
+		**  redundant.  We just add the offsets or, clip the values
+		**
+		** note: offsets need to be done before clipping occurs,
+		** or we could get funny values that are outside
+		** clipping boundaries.
+		*/
+		x += axis_align->offset_X;
+		y += axis_align->offset_Y;
+		x =  max(axis_align->clip_X_low, x);
+		y =  max(axis_align->clip_Y_low, y);
+		if (axis_align->clip_X_high)
+			x = min(axis_align->clip_X_high, x);
+		if (axis_align->clip_Y_high)
+			y =  min(axis_align->clip_Y_high, y);
+
+	}
+
+	pr_debug("%s: f_state[%d]:%d - x:%d y:%d z:%d w_max:%d w_min:%d\n",
+		__func__, n_finger, finger_state, x, y, z, w_max, w_min);
+
+
+#ifdef ABS_MT_PRESSURE
+	input_report_abs(sensor->input, ABS_MT_PRESSURE, z);
+#endif
+	input_report_abs(sensor->input, ABS_MT_TOUCH_MAJOR, w_max);
+	input_report_abs(sensor->input, ABS_MT_TOUCH_MINOR, w_min);
+	input_report_abs(sensor->input, ABS_MT_ORIENTATION, orient);
+	input_report_abs(sensor->input, ABS_MT_POSITION_X, x);
+	input_report_abs(sensor->input, ABS_MT_POSITION_Y, y);
+	input_report_abs(sensor->input, ABS_MT_TRACKING_ID, n_finger);
+
+	/* MT sync between fingers */
+	input_mt_sync(sensor->input);
+	sensor->finger_tracker[n_finger] = finger_state;
+}
+
+static void rmi_f11_finger_handler(struct f11_2d_sensor *sensor)
+{
+	const struct f11_2d_data_0 *f_state = sensor->data.f_state;
+	u8 finger_state;
+	u8 finger_pressed_count;
+	u8 i;
+
+	for (i = 0, finger_pressed_count = 0; i < sensor->nbr_fingers; i++) {
+		/* Possible of having 4 fingers per f_statet register */
+		finger_state = (f_state[i >> 2].finger_n &
+					F11_FINGER_STATE_MASK_N(i));
+		finger_state = F11_FINGER_STATE_VAL_N(finger_state, i);
+
+		if (finger_state == F11_RESERVED) {
+			pr_err("%s: Invalid finger state[%d]:0x%02x.", __func__,
+					i, finger_state);
+			continue;
+		} else if ((finger_state == F11_PRESENT) ||
+				(finger_state == F11_INACCURATE)) {
+			finger_pressed_count++;
+		}
+
+		if (sensor->data.abs_pos)
+			rmi_f11_abs_pos_report(sensor, finger_state, i);
+
+		if (sensor->data.rel_pos)
+			rmi_f11_rel_pos_report(sensor, i);
+	}
+	input_report_key(sensor->input, BTN_TOUCH, finger_pressed_count);
+	input_sync(sensor->input);
+}
+
+static inline int f11_2d_construct_data(struct f11_2d_sensor *sensor)
+{
+	struct f11_2d_sensor_query *query = &sensor->sens_query;
+	struct f11_2d_data *data = &sensor->data;
+	int i;
+
+	sensor->nbr_fingers = (query->number_of_fingers == 5 ? 10 :
+				query->number_of_fingers + 1);
+
+	sensor->pkt_size = F11_CEIL(sensor->nbr_fingers, 4);
+
+	if (query->has_abs)
+		sensor->pkt_size += (sensor->nbr_fingers * 5);
+
+	if (query->has_rel)
+		sensor->pkt_size +=  (sensor->nbr_fingers * 2);
+
+	/* Check if F11_2D_Query7 is non-zero */
+	if (query->f11_2d_query7__8[0])
+		sensor->pkt_size += sizeof(u8);
+
+	/* Check if F11_2D_Query7 or F11_2D_Query8 is non-zero */
+	if (query->f11_2d_query7__8[0] || query->f11_2d_query7__8[1])
+		sensor->pkt_size += sizeof(u8);
+
+	if (query->has_pinch || query->has_flick || query->has_rotate) {
+		sensor->pkt_size += 3;
+		if (!query->has_flick)
+			sensor->pkt_size--;
+		if (!query->has_rotate)
+			sensor->pkt_size--;
+	}
+
+	if (query->has_touch_shapes)
+		sensor->pkt_size += F11_CEIL(query->nbr_touch_shapes + 1, 8);
+
+	sensor->data_pkt = kzalloc(sensor->pkt_size, GFP_KERNEL);
+	if (!sensor->data_pkt)
+		return -ENOMEM;
+
+	data->f_state = (struct f11_2d_data_0 *)sensor->data_pkt;
+	i = F11_CEIL(sensor->nbr_fingers, 4);
+
+	if (query->has_abs) {
+		data->abs_pos = (struct f11_2d_data_1_5 *)
+				&sensor->data_pkt[i];
+		i += (sensor->nbr_fingers * 5);
+	}
+
+	if (query->has_rel) {
+		data->rel_pos = (struct f11_2d_data_6_7 *)
+				&sensor->data_pkt[i];
+		i += (sensor->nbr_fingers * 2);
+	}
+
+	if (query->f11_2d_query7__8[0]) {
+		data->gest_1 = (struct f11_2d_data_8 *)&sensor->data_pkt[i];
+		i++;
+	}
+
+	if (query->f11_2d_query7__8[0] || query->f11_2d_query7__8[1]) {
+		data->gest_2 = (struct f11_2d_data_9 *)&sensor->data_pkt[i];
+		i++;
+	}
+
+	if (query->has_pinch) {
+		data->pinch = (struct f11_2d_data_10 *)&sensor->data_pkt[i];
+		i++;
+	}
+
+	if (query->has_flick) {
+		if (query->has_pinch) {
+			data->flick = (struct f11_2d_data_10_12 *)data->pinch;
+			i += 2;
+		} else {
+			data->flick = (struct f11_2d_data_10_12 *)
+					&sensor->data_pkt[i];
+			i += 3;
+		}
+	}
+
+	if (query->has_rotate) {
+		if (query->has_flick) {
+			data->rotate = (struct f11_2d_data_11_12 *)
+					(data->flick + 1);
+		} else {
+			data->rotate = (struct f11_2d_data_11_12 *)
+					&sensor->data_pkt[i];
+			i += 2;
+		}
+	}
+
+	if (query->has_touch_shapes)
+		data->shapes = (struct f11_2d_data_13 *)&sensor->data_pkt[i];
+
+	return 0;
+}
+
+static 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);
+	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;
+}
+
+static int f11_read_control_regs(struct rmi_device *rmi_dev,
+					   struct f11_2d_ctrl *ctrl,
+					   int ctrl_base_addr) {
+	int read_address = ctrl_base_addr;
+	int error = 0;
+
+	error = rmi_read_block(rmi_dev, read_address, ctrl->ctrl0_9,
+		sizeof(ctrl->ctrl0_9));
+	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);
+
+	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);
+	}
+
+	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,
+				int ctrl_base_addr) {
+	int error = 0;
+
+	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;
+		}
+	}
+
+	return f11_read_control_regs(rmi_dev, ctrl, ctrl_base_addr);
+
+error_exit:
+	f11_free_control_regs(ctrl);
+	return error;
+}
+
+static inline int f11_write_control_regs(struct rmi_device *rmi_dev,
+					struct f11_2d_sensor_query *query,
+					struct f11_2d_ctrl *ctrl,
+					int ctrl_base_addr)
+{
+	int write_address = ctrl_base_addr;
+	int error;
+
+	error = rmi_write_block(rmi_dev, write_address,
+				ctrl->ctrl0_9, sizeof(ctrl->ctrl0_9));
+	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);
+	}
+
+	return 0;
+}
+
+static inline int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev,
+			struct f11_2d_sensor_query *query, u8 query_base_addr)
+{
+	int query_size;
+	int rc;
+
+	rc = rmi_read_block(rmi_dev, query_base_addr, query->f11_2d_query1__4,
+					sizeof(query->f11_2d_query1__4));
+	if (rc < 0)
+		return rc;
+	query_size = rc;
+
+	if (query->has_abs) {
+		rc = rmi_read(rmi_dev, query_base_addr + query_size,
+					&query->f11_2d_query5);
+		if (rc < 0)
+			return rc;
+		query_size++;
+	}
+
+	if (query->has_rel) {
+		rc = rmi_read(rmi_dev, query_base_addr + query_size,
+					&query->f11_2d_query6);
+		if (rc < 0)
+			return rc;
+		query_size++;
+	}
+
+	if (query->has_gestures) {
+		rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
+					query->f11_2d_query7__8,
+					sizeof(query->f11_2d_query7__8));
+		if (rc < 0)
+			return rc;
+		query_size += sizeof(query->f11_2d_query7__8);
+	}
+
+	if (query->has_touch_shapes) {
+		rc = rmi_read(rmi_dev, query_base_addr + query_size,
+					&query->f11_2d_query10);
+		if (rc < 0)
+			return rc;
+		query_size++;
+	}
+
+	return query_size;
+}
+
+/* This operation is done in a number of places, so we have a handy routine
+ * for it.
+ */
+static void f11_set_abs_params(struct rmi_function_container *fc, int index)
+{
+	struct f11_data *instance_data =  fc->data;
+	struct input_dev *input = instance_data->sensors[index].input;
+	int device_x_max =
+		instance_data->dev_controls.sensor_max_x_pos;
+	int device_y_max =
+		instance_data->dev_controls.sensor_max_y_pos;
+	int x_min, x_max, y_min, y_max;
+
+	if (instance_data->sensors[index].axis_align.swap_axes) {
+		int temp = device_x_max;
+		device_x_max = device_y_max;
+		device_y_max = temp;
+	}
+
+	/* Use the max X and max Y read from the device, or the clip values,
+	 * whichever is stricter.
+	 */
+	x_min = instance_data->sensors[index].axis_align.clip_X_low;
+	if (instance_data->sensors[index].axis_align.clip_X_high)
+		x_max = min((int) device_x_max,
+			instance_data->sensors[index].axis_align.clip_X_high);
+	else
+		x_max = device_x_max;
+
+	y_min = instance_data->sensors[index].axis_align.clip_Y_low;
+	if (instance_data->sensors[index].axis_align.clip_Y_high)
+		y_max = min((int) device_y_max,
+			instance_data->sensors[index].axis_align.clip_Y_high);
+	else
+		y_max = device_y_max;
+
+	dev_dbg(&fc->dev, "Set ranges X=[%d..%d] Y=[%d..%d].",
+			x_min, x_max, y_min, y_max);
+
+#ifdef ABS_MT_PRESSURE
+		input_set_abs_params(input, ABS_MT_PRESSURE, 0,
+				DEFAULT_MAX_ABS_MT_PRESSURE, 0, 0);
+#endif
+		input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
+				0, DEFAULT_MAX_ABS_MT_TOUCH, 0, 0);
+		input_set_abs_params(input, ABS_MT_TOUCH_MINOR,
+				0, DEFAULT_MAX_ABS_MT_TOUCH, 0, 0);
+		input_set_abs_params(input, ABS_MT_ORIENTATION,
+				0, DEFAULT_MAX_ABS_MT_ORIENTATION, 0, 0);
+		input_set_abs_params(input, ABS_MT_TRACKING_ID,
+				DEFAULT_MIN_ABS_MT_TRACKING_ID,
+				DEFAULT_MAX_ABS_MT_TRACKING_ID, 0, 0);
+		/* TODO get max_x_pos (and y) from control registers. */
+		input_set_abs_params(input, ABS_MT_POSITION_X,
+				x_min, x_max, 0, 0);
+		input_set_abs_params(input, ABS_MT_POSITION_Y,
+				y_min, y_max, 0, 0);
+}
+
+
+static int rmi_f11_init(struct rmi_function_container *fc)
+{
+	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 inline void rmi_f11_free_memory(struct rmi_function_container *fc)
+{
+	struct f11_data *f11 = fc->data;
+
+	if (f11) {
+		f11_free_control_regs(&f11->dev_controls);
+		kfree(f11);
+		fc->data = NULL;
+	}
+}
+
+
+static inline int rmi_f11_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_device_platform_data *pdata;
+	struct f11_data *f11;
+	u8 query_offset;
+	u8 query_base_addr;
+	u8 control_base_addr;
+	u16 max_x_pos, max_y_pos, temp;
+	int rc;
+	int i;
+
+	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
+
+	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;
+		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;
+	}
+
+	return 0;
+}
+
+
+static inline 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;
+
+	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);
+
+		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;
+		}
+
+		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;
+		}
+	}
+
+	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 inline int rmi_f11_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 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;
+	u8 data_base_addr = fc->fd.data_base_addr;
+	int data_base_addr_offset = 0;
+	int error;
+	int i;
+
+	for (i = 0; i < f11->dev_query.nbr_of_sensors + 1; i++) {
+		error = rmi_read_block(rmi_dev,
+				data_base_addr + data_base_addr_offset,
+				f11->sensors[i].data_pkt,
+				f11->sensors[i].pkt_size);
+		if (error < 0)
+			return error;
+
+		rmi_f11_finger_handler(&f11->sensors[i]);
+		data_base_addr_offset += f11->sensors[i].pkt_size;
+	}
+
+	return 0;
+}
+
+#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;
+
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+		sysfs_remove_file(&fc->dev.kobj,
+				  &attrs[attr_count].attr);
+	}
+
+	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
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	,
+	.late_resume = rmi_f11_resume
+#else
+	.resume = rmi_f11_resume
+#endif  /* CONFIG_HAS_EARLYSUSPEND */
+#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_flip_show(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f11_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u %u\n",
+			data->sensors[0].axis_align.flip_x,
+			data->sensors[0].axis_align.flip_y);
+}
+
+static ssize_t f11_flip_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf,
+				    size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f11_data *instance_data;
+	unsigned int new_X, new_Y;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+
+	if (sscanf(buf, "%u %u", &new_X, &new_Y) != 2)
+		return -EINVAL;
+	if (new_X < 0 || new_X > 1 || new_Y < 0 || new_Y > 1)
+		return -EINVAL;
+	instance_data->sensors[0].axis_align.flip_x = new_X;
+	instance_data->sensors[0].axis_align.flip_y = new_Y;
+
+	return count;
+}
+
+static ssize_t f11_swap_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f11_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			instance_data->sensors[0].axis_align.swap_axes);
+}
+
+static ssize_t f11_swap_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f11_data *instance_data;
+	unsigned int newSwap;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+
+	if (sscanf(buf, "%u", &newSwap) != 1)
+		return -EINVAL;
+	if (newSwap < 0 || newSwap > 1)
+		return -EINVAL;
+	instance_data->sensors[0].axis_align.swap_axes = newSwap;
+
+	f11_set_abs_params(fc, 0);
+
+	return count;
+}
+
+static ssize_t 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_offset_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct rmi_function_container *fc;
+	struct f11_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d %d\n",
+			instance_data->sensors[0].axis_align.offset_X,
+			instance_data->sensors[0].axis_align.offset_Y);
+}
+
+static ssize_t f11_offset_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f11_data *instance_data;
+	int new_X, new_Y;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+
+	if (sscanf(buf, "%d %d", &new_X, &new_Y) != 2)
+		return -EINVAL;
+	instance_data->sensors[0].axis_align.offset_X = new_X;
+	instance_data->sensors[0].axis_align.offset_Y = new_Y;
+
+	return count;
+}
+
+static ssize_t f11_clip_show(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+
+	struct rmi_function_container *fc;
+	struct f11_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u %u %u %u\n",
+			instance_data->sensors[0].axis_align.clip_X_low,
+			instance_data->sensors[0].axis_align.clip_X_high,
+			instance_data->sensors[0].axis_align.clip_Y_low,
+			instance_data->sensors[0].axis_align.clip_Y_high);
+}
+
+static ssize_t f11_clip_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf,
+				    size_t count)
+{
+	struct rmi_function_container *fc;
+	struct f11_data *instance_data;
+	unsigned int new_X_low, new_X_high, new_Y_low, new_Y_high;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	if (sscanf(buf, "%u %u %u %u",
+		   &new_X_low, &new_X_high, &new_Y_low, &new_Y_high) != 4)
+		return -EINVAL;
+	if (new_X_low < 0 || new_X_low >= new_X_high || new_Y_low < 0
+	    || new_Y_low >= new_Y_high)
+		return -EINVAL;
+	instance_data->sensors[0].axis_align.clip_X_low = new_X_low;
+	instance_data->sensors[0].axis_align.clip_X_high = new_X_high;
+	instance_data->sensors[0].axis_align.clip_Y_low = new_Y_low;
+	instance_data->sensors[0].axis_align.clip_Y_high = new_Y_high;
+
+	/*
+	** for now, we assume this is sensor index 0
+	*/
+	f11_set_abs_params(fc, 0);
+
+	return count;
+}
+
+static ssize_t 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
+
+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");
+

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

* Re: [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
  2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (10 preceding siblings ...)
  2011-12-22  2:10 ` [RFC PATCH 11/11] input: RMI4 F11 - multifinger pointing Christopher Heiny
@ 2012-01-01 13:51 ` Linus Walleij
  2012-01-04  1:51   ` Christopher Heiny
  2012-01-05  7:58 ` Dmitry Torokhov
  12 siblings, 1 reply; 34+ messages in thread
From: Linus Walleij @ 2012-01-01 13:51 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Joerie de Gram, Linus Walleij, Naveen Kumar Gaddipati

On Thu, Dec 22, 2011 at 3:09 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:

This looks like a fundamental rewrite an huge improvement over the
previous RMI4 patches, good job!

I am not very deeply versed in the input subsystem but overall it looks
good to me, nice structure etc. I hope to be able to get back to a detailed
review and per-patch Acked-by:s ASAP.

Yours,
Linus Walleij

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

* Re: [RFC PATCH 2/11] input: RMI4 core bus and sensor drivers.
  2011-12-22  2:09 ` [RFC PATCH 2/11] input: RMI4 core bus and sensor drivers Christopher Heiny
@ 2012-01-02  6:38   ` Shubhrajyoti
  2012-01-05 20:49     ` Christopher Heiny
  2012-01-06  1:56     ` Christopher Heiny
  2012-01-06  2:34   ` Dmitry Torokhov
  1 sibling, 2 replies; 34+ messages in thread
From: Shubhrajyoti @ 2012-01-02  6:38 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Joerie de Gram, Linus Walleij, Naveen Kumar Gaddipati

Hi Christopher,

On Thursday 22 December 2011 07:39 AM, Christopher Heiny wrote:
> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Linus Walleij <linus.walleij@stericsson.com>
> Cc: Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
> Cc: Joeri de Gram <j.de.gram@gmail.com>
>
> ---
>
>  drivers/input/rmi4/rmi_bus.c    |  436 ++++++++++++
>  drivers/input/rmi4/rmi_driver.c | 1488 +++++++++++++++++++++++++++++++++++++++
>  drivers/input/rmi4/rmi_driver.h |   97 +++
>  3 files changed, 2021 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..e32d4ad
> --- /dev/null
> +++ b/drivers/input/rmi4/rmi_bus.c
> @@ -0,0 +1,436 @@
> +/*
> + * Copyright (c) 2011 Synaptics Incorporated
> + * Copyright (c) 2011 Unixphere
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/pm.h>
> +#include <linux/slab.h>
> +#include <linux/list.h>
> +#include <linux/rmi.h>
> +
> +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 int physical_device_count;
> +
> +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, "Matching %s.\n", 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_err(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)
> +{
> +#ifdef GENERIC_SUBSYS_PM_OPS
> +	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
> +
> +	if (pm && pm->suspend)
> +		return pm->suspend(dev);
If driver-pm- suspend is not there should you not fall back to . suspend ?

> +#endif
> +
> +	return 0;
> +}
> +
> +static int rmi_bus_resume(struct device *dev)
> +{
> +#ifdef GENERIC_SUBSYS_PM_OPS
> +	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
> +
> +	if (pm && pm->resume)
> +		return pm->resume(dev);
same here?
> +#endif
> +
> +	return 0;
> +}
> +#endif
> +
> +static int rmi_bus_probe(struct device *dev)



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

* Re: [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
  2012-01-01 13:51 ` [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Linus Walleij
@ 2012-01-04  1:51   ` Christopher Heiny
  0 siblings, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2012-01-04  1:51 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Joerie de Gram, Linus Walleij, Naveen Kumar Gaddipati

On 01/01/2012 05:51 AM, Linus Walleij wrote:
> On Thu, Dec 22, 2011 at 3:09 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:
>
> This looks like a fundamental rewrite an huge improvement over the
> previous RMI4 patches, good job!
>
> I am not very deeply versed in the input subsystem but overall it looks
> good to me, nice structure etc. I hope to be able to get back to a detailed
> review and per-patch Acked-by:s ASAP.

Thanks!

We realized we were in a red queen's race by trying to both implement 
customer requests and restructure the system at the same time, so 
contracted Unixphere to help with the restructure.  We think the result 
was pretty good, and it's nice to hear compliments.

We'll look forward to your input after the detailed review.

					Thanks again,
						Chris

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

* Re: [RFC PATCH 10/11] input: RMI4 F19 - capacitive buttons
  2011-12-22  2:10 ` [RFC PATCH 10/11] input: RMI4 F19 - capacitive buttons Christopher Heiny
@ 2012-01-05  7:53   ` Dmitry Torokhov
  2012-01-06  0:05     ` Christopher Heiny
  0 siblings, 1 reply; 34+ messages in thread
From: Dmitry Torokhov @ 2012-01-05  7:53 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Jean Delvare, Linux Kernel, Linux Input, Joerie de Gram,
	Linus Walleij, Naveen Kumar Gaddipati

Hi Christopher,

On Wed, Dec 21, 2011 at 06:10:01PM -0800, Christopher Heiny wrote:
> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>

A bunch of comments generally applicable to all patches.

I have not looked at the core thoroughly yet.

> 
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Linus Walleij <linus.walleij@stericsson.com>
> Cc: Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
> Cc: Joeri de Gram <j.de.gram@gmail.com>
> 
> ---
> 
>  drivers/input/rmi4/rmi_f19.c | 1571 ++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 1571 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..a678f30
> --- /dev/null
> +++ b/drivers/input/rmi4/rmi_f19.c
> @@ -0,0 +1,1571 @@
> +/*
> + * 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/kernel.h>
> +#include <linux/rmi.h>
> +#include <linux/input.h>
> +#include <linux/slab.h>
> +
> +#define QUERY_BASE_INDEX 1
> +#define MAX_LEN 256
> +
> +struct f19_0d_query {
> +	union {
> +		struct {
> +			u8 configurable:1;
> +			u8 has_sensitivity_adjust:1;
> +			u8 has_hysteresis_threshold:1;
> +		};
> +		u8 f19_0d_query0;
> +	};
> +	u8 f19_0d_query1:5;
> +};
> +
> +struct f19_0d_control_0 {
> +	union {
> +		struct {
> +			u8 button_usage:2;
> +			u8 filter_mode:2;
> +		};
> +		u8 f19_0d_control0;
> +	};

Anonymous union in a struct is kind of pointless...

> +};
> +
> +struct f19_0d_control_1 {
> +	u8 int_enabled_button;
> +};
> +
> +struct f19_0d_control_2 {
> +	u8 single_button;
> +};
> +
> +struct f19_0d_control_3_4 {
> +	u8 sensor_map_button:7;
> +	/*u8 sensitivity_button;*/
> +};
> +
> +struct f19_0d_control_5 {
> +	u8 sensitivity_adj;
> +};
> +struct f19_0d_control_6 {
> +	u8 hysteresis_threshold;
> +};
> +
> +struct f19_0d_control {
> +	struct f19_0d_control_0 *general_control;

Single instance, does not need to be allocated separately.

> +	struct f19_0d_control_1 *button_int_enable;
> +	struct f19_0d_control_2 *single_button_participation;
> +	struct f19_0d_control_3_4 *sensor_map;

This should probably be an array of

struct f19_button_ctrl {
	struct f19_0d_control_1 int_enable;
	struct f19_0d_control_2 participation;
	struct f19_0d_control_3_4 sensor_map;
};

located at the end of f19_0d_control structure so it can be all
allocated in one shot.

> +	struct f19_0d_control_5 *all_button_sensitivity_adj;

Single instance, does not need to be allocated separately.

> +	struct f19_0d_control_6 *all_button_hysteresis_threshold;

Single instance, does not need to be allocated separately.

> +};
> +/* data specific to fn $19 that needs to be kept around */
> +struct f19_data {
> +	struct f19_0d_control *button_control;
> +	struct f19_0d_query button_query;
> +	u8 button_rezero;
> +	bool *button_down;

Just let input core track this for you.

> +	unsigned char button_count;
> +	unsigned char button_data_buffer_size;

Your fingers must be hurting by the time you finished writing this
driver...

> +	unsigned char *button_data_buffer;
> +	unsigned char *button_map;

Linux keycodes are wider than 8 bits, should be unsigned short.

> +	char input_name[MAX_LEN];
> +	char input_phys[MAX_LEN];
> +	struct input_dev *input;
> +};
> +
> +static ssize_t rmi_f19_button_count_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf);
> +
> +static ssize_t rmi_f19_button_map_show(struct device *dev,
> +				      struct device_attribute *attr, char *buf);
> +
> +static ssize_t rmi_f19_button_map_store(struct device *dev,
> +				       struct device_attribute *attr,
> +				       const char *buf, size_t count);
> +static ssize_t rmi_f19_rezero_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf);
> +static ssize_t rmi_f19_rezero_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count);
> +static ssize_t rmi_f19_has_hysteresis_threshold_show(struct device *dev,
> +				      struct device_attribute *attr, char *buf);
> +static ssize_t rmi_f19_has_sensitivity_adjust_show(struct device *dev,
> +				      struct device_attribute *attr, char *buf);
> +static ssize_t rmi_f19_configurable_show(struct device *dev,
> +				      struct device_attribute *attr, char *buf);
> +static ssize_t rmi_f19_filter_mode_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf);
> +static ssize_t rmi_f19_filter_mode_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count);
> +static ssize_t rmi_f19_button_usage_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf);
> +static ssize_t rmi_f19_button_usage_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count);
> +static ssize_t rmi_f19_interrupt_enable_button_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf);
> +static ssize_t rmi_f19_interrupt_enable_button_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count);
> +static ssize_t rmi_f19_single_button_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf);
> +static ssize_t rmi_f19_single_button_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count);
> +static ssize_t rmi_f19_sensor_map_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf);
> +static ssize_t rmi_f19_sensor_map_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count);
> +static ssize_t rmi_f19_sensitivity_adjust_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf);
> +static ssize_t rmi_f19_sensitivity_adjust_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count);
> +static ssize_t rmi_f19_hysteresis_threshold_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf);
> +static ssize_t rmi_f19_hysteresis_threshold_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count);
> +
> +
> +
> +static inline int rmi_f19_alloc_memory(struct rmi_function_container *fc);
> +
> +static inline void rmi_f19_free_memory(struct rmi_function_container *fc);
> +
> +static inline int rmi_f19_initialize(struct rmi_function_container *fc);
> +
> +static inline int rmi_f19_register_device(struct rmi_function_container *fc);
> +
> +static inline 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);
> +
> +static struct device_attribute attrs[] = {
> +	__ATTR(button_count, RMI_RO_ATTR,
> +		rmi_f19_button_count_show, rmi_store_error),

Why not NULL instead of rmi_store_error?

> +	__ATTR(button_map, RMI_RW_ATTR,
> +		rmi_f19_button_map_show, rmi_f19_button_map_store),
> +	__ATTR(rezero, RMI_RW_ATTR,
> +		rmi_f19_rezero_show, rmi_f19_rezero_store),
> +	__ATTR(has_hysteresis_threshold, RMI_RO_ATTR,
> +		rmi_f19_has_hysteresis_threshold_show, rmi_store_error),
> +	__ATTR(has_sensitivity_adjust, RMI_RO_ATTR,
> +		rmi_f19_has_sensitivity_adjust_show, rmi_store_error),
> +	__ATTR(configurable, RMI_RO_ATTR,
> +		rmi_f19_configurable_show, rmi_store_error),
> +	__ATTR(filter_mode, RMI_RW_ATTR,
> +		rmi_f19_filter_mode_show, rmi_f19_filter_mode_store),
> +	__ATTR(button_usage, RMI_RW_ATTR,
> +		rmi_f19_button_usage_show, rmi_f19_button_usage_store),
> +	__ATTR(interrupt_enable_button, RMI_RW_ATTR,
> +		rmi_f19_interrupt_enable_button_show,
> +		rmi_f19_interrupt_enable_button_store),
> +	__ATTR(single_button, RMI_RW_ATTR,
> +		rmi_f19_single_button_show, rmi_f19_single_button_store),
> +	__ATTR(sensor_map, RMI_RW_ATTR,
> +		rmi_f19_sensor_map_show, rmi_f19_sensor_map_store),
> +	__ATTR(sensitivity_adjust, RMI_RW_ATTR,
> +		rmi_f19_sensitivity_adjust_show,
> +		rmi_f19_sensitivity_adjust_store),
> +	__ATTR(hysteresis_threshold, RMI_RW_ATTR,
> +		rmi_f19_hysteresis_threshold_show,
> +		rmi_f19_hysteresis_threshold_store)
> +};
> +
> +
> +int rmi_f19_read_control_parameters(struct rmi_device *rmi_dev,
> +	struct f19_0d_control *button_control,
> +	unsigned char button_count,
> +	unsigned char int_button_enabled_count,
> +	u8 ctrl_base_addr)
> +{
> +	int error = 0;
> +	int i;
> +
> +	if (button_control->general_control) {

It can't be NULL.

> +		error = rmi_read_block(rmi_dev, ctrl_base_addr,
> +				(u8 *)button_control->general_control,
> +				sizeof(struct f19_0d_control_0));
> +		if (error < 0) {
> +			dev_err(&rmi_dev->dev,
> +				"Failed to read f19_0d_control_0, code:"
> +				" %d.\n", error);
> +			return error;
> +		}
> +		ctrl_base_addr = ctrl_base_addr +
> +				sizeof(struct f19_0d_control_0);
> +	}
> +
> +	if (button_control->button_int_enable) {

Neither can this.

> +		for (i = 0; i < int_button_enabled_count; i++) {
> +			error = rmi_read_block(rmi_dev, ctrl_base_addr,
> +				(u8 *)&button_control->button_int_enable[i],
> +				sizeof(struct f19_0d_control_1));
> +			if (error < 0) {
> +				dev_err(&rmi_dev->dev,
> +					"Failed to read f19_0d_control_2,"
> +					" code: %d.\n", error);
> +				return error;
> +			}
> +			ctrl_base_addr = ctrl_base_addr +
> +				sizeof(struct f19_0d_control_1);
> +		}
> +	}
> +
> +	if (button_control->single_button_participation) {

Nor this.

> +		for (i = 0; i < int_button_enabled_count; i++) {
> +			error = rmi_read_block(rmi_dev, ctrl_base_addr,
> +					(u8 *)&button_control->
> +						single_button_participation[i],
> +					sizeof(struct f19_0d_control_2));
> +			if (error < 0) {
> +				dev_err(&rmi_dev->dev,
> +					"Failed to read f19_0d_control_2,"
> +					" code: %d.\n", error);
> +				return error;
> +			}
> +			ctrl_base_addr = ctrl_base_addr +
> +				sizeof(struct f19_0d_control_2);
> +		}
> +	}
> +
> +	if (button_control->sensor_map) {

Nor this...

> +		for (i = 0; i < button_count; i++) {
> +			error = rmi_read_block(rmi_dev, ctrl_base_addr,
> +					(u8 *)&button_control->sensor_map[i],
> +					sizeof(struct f19_0d_control_3_4));
> +			if (error < 0) {
> +				dev_err(&rmi_dev->dev,
> +				"Failed to read f19_0d_control_3_4,"
> +				" code: %d.\n", error);
> +				return error;
> +			}
> +			ctrl_base_addr = ctrl_base_addr +
> +				sizeof(struct f19_0d_control_3_4);
> +		}
> +	}
> +
> +	if (button_control->all_button_sensitivity_adj) {

Nor this.

> +		error = rmi_read_block(rmi_dev, ctrl_base_addr,
> +				(u8 *)button_control->
> +					all_button_sensitivity_adj,
> +				sizeof(struct f19_0d_control_5));
> +		if (error < 0) {
> +			dev_err(&rmi_dev->dev,
> +				"Failed to read f19_0d_control_5,"
> +				" code: %d.\n", error);
> +			return error;
> +		}
> +		ctrl_base_addr = ctrl_base_addr +
> +			sizeof(struct f19_0d_control_5);
> +	}
> +
> +	if (button_control->all_button_hysteresis_threshold) {

...

> +		error = rmi_read_block(rmi_dev, ctrl_base_addr,
> +				(u8 *)button_control->
> +					all_button_hysteresis_threshold,
> +				sizeof(struct f19_0d_control_6));
> +		if (error < 0) {
> +			dev_err(&rmi_dev->dev,
> +				"Failed to read f19_0d_control_6,"
> +				" code: %d.\n", error);
> +			return error;
> +		}
> +		ctrl_base_addr = ctrl_base_addr +
> +			sizeof(struct f19_0d_control_6);
> +	}
> +	return 0;
> +}
> +
> +
> +static int rmi_f19_init(struct rmi_function_container *fc)
> +{
> +	int rc;
> +
> +	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 inline int rmi_f19_alloc_memory(struct rmi_function_container *fc)

Please drop inline throughout, let compiler do its job.

> +{
> +	struct f19_data *f19;
> +	int rc;
> +
> +	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->button_query,
> +						sizeof(struct f19_0d_query));
> +	if (rc < 0) {
> +		dev_err(&fc->dev, "Failed to read query register.\n");
> +		return rc;
> +	}
> +
> +	f19->button_count = f19->button_query.f19_0d_query1;
> +
> +	f19->button_down = kcalloc(f19->button_count,
> +			sizeof(bool), GFP_KERNEL);
> +	if (!f19->button_down) {
> +		dev_err(&fc->dev, "Failed to allocate button state buffer.\n");
> +		return -ENOMEM;
> +	}
> +
> +	f19->button_data_buffer_size = (f19->button_count + 7) / 8;

	DIV_ROUND_UP(f19->button_count, 8);

> +	f19->button_data_buffer =
> +	    kcalloc(f19->button_data_buffer_size,
> +		    sizeof(unsigned char), GFP_KERNEL);
> +	if (!f19->button_data_buffer) {
> +		dev_err(&fc->dev, "Failed to allocate button data buffer.\n");
> +		return -ENOMEM;
> +	}
> +
> +	f19->button_map = kcalloc(f19->button_count,
> +				sizeof(unsigned char), GFP_KERNEL);
> +	if (!f19->button_map) {
> +		dev_err(&fc->dev, "Failed to allocate button map.\n");
> +		return -ENOMEM;
> +	}
> +
> +	f19->button_control = kzalloc(sizeof(struct f19_0d_control),
> +				GFP_KERNEL);

If you allocate a single copy of this structure why isn't it a member of
struct f19_data to begin with?

> +	if (!f19->button_control) {
> +		dev_err(&fc->dev, "Failed to allocate button_control.\n");
> +		return -ENOMEM;
> +	}
> +
> +	f19->button_control->general_control =
> +		kzalloc(sizeof(struct f19_0d_control_0), GFP_KERNEL);

This a single byte, why do you need to allocate it separately?

> +	if (!f19->button_control->general_control) {
> +		dev_err(&fc->dev, "Failed to allocate"
> +				" f19_0d_control_0.\n");
> +		return -ENOMEM;
> +	}
> +
> +	f19->button_control->button_int_enable =
> +		kzalloc(f19->button_data_buffer_size *
> +			sizeof(struct f19_0d_control_2), GFP_KERNEL);
> +	if (!f19->button_control->button_int_enable) {
> +		dev_err(&fc->dev, "Failed to allocate f19_0d_control_1.\n");
> +		return -ENOMEM;
> +	}
> +
> +	f19->button_control->single_button_participation =
> +		kzalloc(f19->button_data_buffer_size *
> +			sizeof(struct f19_0d_control_2), GFP_KERNEL);
> +	if (!f19->button_control->single_button_participation) {
> +		dev_err(&fc->dev, "Failed to allocate"
> +			" f19_0d_control_2.\n");

Do not split error messages; it's OK if they exceed 80 column limit.

> +		return -ENOMEM;
> +	}
> +
> +	f19->button_control->sensor_map =
> +		kzalloc(f19->button_count *
> +			sizeof(struct f19_0d_control_3_4), GFP_KERNEL);
> +	if (!f19->button_control->sensor_map) {
> +		dev_err(&fc->dev, "Failed to allocate"
> +			" f19_0d_control_3_4.\n");
> +		return -ENOMEM;
> +	}
> +
> +	f19->button_control->all_button_sensitivity_adj =
> +		kzalloc(sizeof(struct f19_0d_control_5), GFP_KERNEL);
> +	if (!f19->button_control->all_button_sensitivity_adj) {
> +		dev_err(&fc->dev, "Failed to allocate"
> +			" f19_0d_control_5.\n");
> +		return -ENOMEM;
> +	}
> +
> +	f19->button_control->all_button_hysteresis_threshold =
> +		kzalloc(sizeof(struct f19_0d_control_6), GFP_KERNEL);
> +	if (!f19->button_control->all_button_hysteresis_threshold) {
> +		dev_err(&fc->dev, "Failed to allocate"
> +			" f19_0d_control_6.\n");
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +
> +
> +static inline void rmi_f19_free_memory(struct rmi_function_container *fc)
> +{
> +	struct f19_data *f19 = fc->data;
> +
> +	if (f19) {

Can it be NULL? How?

> +		kfree(f19->button_down);
> +		kfree(f19->button_data_buffer);
> +		kfree(f19->button_map);
> +		if (f19->button_control) {
> +			kfree(f19->button_control->general_control);
> +			kfree(f19->button_control->button_int_enable);
> +			kfree(f19->button_control->single_button_participation);
> +			kfree(f19->button_control->sensor_map);
> +			kfree(f19->button_control->all_button_sensitivity_adj);
> +			kfree(f19->button_control->
> +				  all_button_hysteresis_threshold);
> +			kfree(f19->button_control);
> +		}
> +		kfree(f19);
> +		fc->data = NULL;
> +	}
> +}
> +
> +
> +static inline int rmi_f19_initialize(struct rmi_function_container *fc)
> +{
> +	struct rmi_device *rmi_dev = fc->rmi_dev;
> +	struct rmi_device_platform_data *pdata;
> +	struct f19_data *f19 = fc->data;
> +	int i;
> +	int rc;
> +
> +	dev_info(&fc->dev, "Intializing F19 values.");

dev_dbg()

> +
> +	/* 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->button_map) {
> +			dev_warn(&fc->dev, "%s - button_map is NULL", __func__);
> +		} else if (pdata->button_map->nbuttons != f19->button_count) {
> +			dev_warn(&fc->dev,
> +				"Platformdata button map size (%d) != number "
> +				"of buttons on device (%d) - ignored.\n",
> +				pdata->button_map->nbuttons,
> +				f19->button_count);
> +		} else if (!pdata->button_map->map) {
> +			dev_warn(&fc->dev,
> +				 "Platformdata button map is missing!\n");
> +		} else {
> +			for (i = 0; i < pdata->button_map->nbuttons; i++)
> +				f19->button_map[i] = pdata->button_map->map[i];
> +		}
> +	}
> +
> +	rc = rmi_f19_read_control_parameters(rmi_dev,
> +						  f19->button_control,
> +						  f19->button_count,
> +						  f19->button_data_buffer_size,
> +						  fc->fd.control_base_addr);
> +	if (rc < 0) {
> +		dev_err(&fc->dev,
> +			"Failed to initialize F19 control params.\n");
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +
> +
> +static inline int rmi_f19_register_device(struct rmi_function_container *fc)
> +{
> +	struct rmi_device *rmi_dev = fc->rmi_dev;
> +	struct input_dev *input_dev;
> +	struct f19_data *f19 = 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;
> +	}
> +
> +	f19->input = input_dev;
> +	snprintf(f19->input_name, MAX_LEN, "%sfn%02x", dev_name(&rmi_dev->dev),
> +		fc->fd.function_number);
> +	input_dev->name = f19->input_name;
> +	snprintf(f19->input_phys, MAX_LEN, "%s/input0", input_dev->name);
> +	input_dev->phys = f19->input_phys;
> +	input_dev->dev.parent = &rmi_dev->dev;
> +	input_set_drvdata(input_dev, f19);
> +
> +	/* Set up any input events. */
> +	set_bit(EV_SYN, input_dev->evbit);
> +	set_bit(EV_KEY, input_dev->evbit);

__set_bit(), no need to lock the bus.

> +	/* 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 inline int rmi_f19_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;
> +		}
> +	}

This is called attribute group, please use it.

> +
> +	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_f19_config(struct rmi_function_container *fc)
> +{
> +	struct f19_data *data;
> +	int retval;
> +	int ctrl_base_addr;
> +	int button_reg;
> +
> +	data = fc->data;
> +
> +	ctrl_base_addr = fc->fd.control_base_addr;
> +
> +	button_reg = (data->button_count / 7) + 1;


Please combine variable declarations and initializations - this makes
code a bit less verbose and easier to read.

> +
> +	retval = rmi_write_block(fc->rmi_dev, ctrl_base_addr,
> +		(u8 *)data->button_control->general_control,
> +			sizeof(struct f19_0d_control_0));
> +	if (retval < 0) {
> +		dev_err(&fc->dev, "%s : Could not write general_control to 0x%x\n",
> +				__func__, fc->fd.control_base_addr);
> +		return retval;
> +	}
> +
> +	ctrl_base_addr += sizeof(struct f19_0d_control_0);
> +	retval = rmi_write_block(fc->rmi_dev, ctrl_base_addr,
> +		(u8 *)data->button_control->button_int_enable,
> +			sizeof(struct f19_0d_control_1)*(button_reg + 1));
> +	if (retval < 0) {
> +		dev_err(&fc->dev, "%s : Could not write interrupt_enable_store"
> +			" to 0x%x\n", __func__, ctrl_base_addr);
> +		return retval;
> +	}
> +
> +	ctrl_base_addr += sizeof(struct f19_0d_control_2)*(button_reg + 1);
> +	retval = rmi_write_block(fc->rmi_dev, ctrl_base_addr,
> +		(u8 *)data->button_control->single_button_participation,
> +			sizeof(struct f19_0d_control_2)*(button_reg + 1));
> +	if (retval < 0) {
> +		dev_err(&fc->dev,
> +				"%s : Could not write interrupt_enable_store to"
> +				" 0x%x\n", __func__, ctrl_base_addr);
> +		return -EINVAL;
> +	}
> +
> +	ctrl_base_addr = fc->fd.control_base_addr +
> +		sizeof(struct f19_0d_control_0) +
> +		sizeof(struct f19_0d_control_1)*button_reg +
> +		sizeof(struct f19_0d_control_2)*button_reg;
> +	retval = rmi_write_block(fc->rmi_dev, ctrl_base_addr,
> +		(u8 *)data->button_control->sensor_map,
> +			sizeof(struct f19_0d_control_3_4)*data->button_count);
> +	if (retval < 0) {
> +		dev_err(&fc->dev, "%s : Could not sensor_map_store to 0x%x\n",
> +				__func__, ctrl_base_addr);
> +		return -EINVAL;
> +	}
> +		/* write back to the control register */
> +	ctrl_base_addr += sizeof(struct f19_0d_control_3_4)*data->button_count;
> +	retval = rmi_write_block(fc->rmi_dev, ctrl_base_addr,
> +		(u8 *)data->button_control->all_button_sensitivity_adj,
> +			sizeof(struct f19_0d_control_5));
> +	if (retval < 0) {
> +		dev_err(&fc->dev, "%s : Could not sensitivity_adjust_store to"
> +			" 0x%x\n", __func__, ctrl_base_addr);
> +		return retval;
> +	}
> +
> +	ctrl_base_addr += sizeof(struct f19_0d_control_5);
> +	retval = rmi_write_block(fc->rmi_dev, ctrl_base_addr,
> +		(u8 *)data->button_control->all_button_sensitivity_adj,
> +			sizeof(struct f19_0d_control_6));
> +	if (retval < 0) {
> +		dev_err(&fc->dev, "%s : Could not write all_button hysteresis "
> +			"threshold to 0x%x\n", __func__, ctrl_base_addr);
> +		return -EINVAL;
> +	}
> +
> +	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;
> +	int attr_count = 0;
> +
> +	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
> +		sysfs_remove_file(&fc->dev.kobj,
> +				  &attrs[attr_count].attr);
> +
> +	input_unregister_device(f19->input);
> +
> +	rmi_f19_free_memory(fc);
> +}
> +
> +int rmi_f19_attention(struct rmi_function_container *fc, u8 *irq_bits)

static.

> +{
> +	struct rmi_device *rmi_dev = fc->rmi_dev;
> +	struct f19_data *f19 = fc->data;
> +	u8 data_base_addr = fc->fd.data_base_addr;
> +	int error;
> +	int button;
> +
> +	/* Read the button data. */
> +
> +	error = rmi_read_block(rmi_dev, data_base_addr, f19->button_data_buffer,
> +			f19->button_data_buffer_size);
> +	if (error < 0) {
> +		dev_err(&fc->dev, "%s: Failed to read button data registers.\n",
> +			__func__);
> +		return error;
> +	}
> +
> +	/* Generate events for buttons that change state. */
> +	for (button = 0; button < f19->button_count;
> +	     button++) {
> +		int button_reg;
> +		int button_shift;
> +		bool button_status;
> +
> +		/* determine which data byte the button status is in */
> +		button_reg = button / 7;
> +		/* bit shift to get button's status */
> +		button_shift = button % 8;
> +		button_status =
> +		    ((f19->button_data_buffer[button_reg] >> button_shift)
> +			& 0x01) != 0;
> +
> +		/* if the button state changed from the last time report it
> +		 * and store the new state */
> +		if (button_status != f19->button_down[button]) {
> +			dev_dbg(&fc->dev, "%s: Button %d (code %d) -> %d.\n",
> +				__func__, button, f19->button_map[button],
> +				 button_status);
> +			/* Generate an event here. */
> +			input_report_key(f19->input, f19->button_map[button],
> +					 button_status);
> +			f19->button_down[button] = button_status;
> +		}
> +	}

All of the above could be reduced to:

	for (button = 0; button < f19->button_count; button++)
		input_report_key(f19->input, f19->button_map[button],
				 test_bit(button, f19->button_data_buffer);

> +
> +	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);
> +}
> +
> +static ssize_t rmi_f19_filter_mode_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n",
> +			data->button_control->general_control->filter_mode);
> +
> +}
> +
> +static ssize_t rmi_f19_filter_mode_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +	unsigned int new_value;
> +	int result;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +	if (sscanf(buf, "%u", &new_value) != 1) {
> +		dev_err(dev,
> +		"%s: Error - filter_mode_store has an "
> +		"invalid len.\n",
> +		__func__);
> +		return -EINVAL;
> +	}
> +
> +	if (new_value < 0 || new_value > 4) {
> +		dev_err(dev, "%s: Error - filter_mode_store has an "
> +		"invalid value %d.\n",
> +		__func__, new_value);
> +		return -EINVAL;
> +	}
> +	data->button_control->general_control->filter_mode = new_value;
> +	result = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
> +		(u8 *)data->button_control->general_control,
> +			sizeof(struct f19_0d_control_0));
> +	if (result < 0) {
> +		dev_err(dev, "%s : Could not write filter_mode_store to 0x%x\n",
> +				__func__, fc->fd.control_base_addr);
> +		return result;
> +	}
> +
> +	return count;
> +}
> +
> +static ssize_t rmi_f19_button_usage_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n",
> +			data->button_control->general_control->button_usage);
> +
> +}
> +
> +static ssize_t rmi_f19_button_usage_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +	unsigned int new_value;
> +	int result;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +	if (sscanf(buf, "%u", &new_value) != 1) {
> +		dev_err(dev,
> +		"%s: Error - button_usage_store has an "
> +		"invalid len.\n",
> +		__func__);
> +		return -EINVAL;
> +	}
> +
> +	if (new_value < 0 || new_value > 4) {
> +		dev_err(dev, "%s: Error - button_usage_store has an "
> +		"invalid value %d.\n",
> +		__func__, new_value);
> +		return -EINVAL;
> +	}
> +	data->button_control->general_control->button_usage = new_value;
> +	result = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
> +		(u8 *)data->button_control->general_control,
> +			sizeof(struct f19_0d_control_0));
> +	if (result < 0) {
> +		dev_err(dev, "%s: Could not write button_usage_store to 0x%x\n",
> +				__func__, fc->fd.control_base_addr);
> +		return result;
> +	}
> +
> +	return count;
> +
> +}
> +
> +static ssize_t rmi_f19_interrupt_enable_button_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +	int i, len, total_len = 0;
> +	char *current_buf = buf;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +	/* loop through each button map value and copy its
> +	 * string representation into buf */
> +	for (i = 0; i < data->button_count; i++) {
> +		int button_reg;
> +		int button_shift;
> +		int interrupt_button;
> +
> +		button_reg = i / 7;
> +		button_shift = i % 8;
> +		interrupt_button =
> +		    ((data->button_control->
> +			button_int_enable[button_reg].int_enabled_button >>
> +				button_shift) & 0x01);
> +
> +		/* get next button mapping value and write it to buf */
> +		len = snprintf(current_buf, PAGE_SIZE - total_len,
> +			"%u ", interrupt_button);
> +		/* bump up ptr to next location in buf if the
> +		 * snprintf was valid.  Otherwise issue an error
> +		 * and return. */
> +		if (len > 0) {
> +			current_buf += len;
> +			total_len += len;
> +		} else {
> +			dev_err(dev, "%s: Failed to build interrupt button"
> +				" buffer, code = %d.\n", __func__, len);
> +			return snprintf(buf, PAGE_SIZE, "unknown\n");
> +		}
> +	}
> +	len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
> +	if (len > 0)
> +		total_len += len;
> +	else
> +		dev_warn(dev, "%s: Failed to append carriage return.\n",
> +			 __func__);
> +	return total_len;
> +
> +}
> +
> +static ssize_t rmi_f19_interrupt_enable_button_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +	int i;
> +	int button_count = 0;
> +	int retval = count;
> +	int button_reg = 0;
> +	int ctrl_bass_addr;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +	for (i = 0; i < data->button_count && *buf != 0;
> +	     i++) {
> +		int button_shift;
> +		int button;
> +
> +		button_reg = i / 7;
> +		button_shift = i % 8;
> +		/* get next button mapping value and store and bump up to
> +		 * point to next item in buf */
> +		sscanf(buf, "%u", &button);
> +
> +		if (button != 0 && button != 1) {
> +			dev_err(dev,
> +				"%s: Error - interrupt enable button for"
> +				" button %d is not a valid value 0x%x.\n",
> +				__func__, i, button);
> +			return -EINVAL;
> +		}
> +
> +		if (button_shift == 0)
> +			data->button_control->button_int_enable[button_reg].
> +				int_enabled_button = 0;
> +		data->button_control->button_int_enable[button_reg].
> +			int_enabled_button |= (button << button_shift);
> +		button_count++;
> +		/* bump up buf to point to next item to read */
> +		while (*buf != 0) {
> +			buf++;
> +			if (*(buf - 1) == ' ')
> +				break;
> +		}
> +	}
> +
> +	/* Make sure the button count matches */
> +	if (button_count != data->button_count) {
> +		dev_err(dev,
> +			"%s: Error - interrupt enable button count of %d"
> +			" doesn't match device button count of %d.\n",
> +			 __func__, button_count, data->button_count);
> +		return -EINVAL;
> +	}
> +
> +	/* write back to the control register */
> +	ctrl_bass_addr = fc->fd.control_base_addr +
> +			sizeof(struct f19_0d_control_0);
> +	retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
> +		(u8 *)data->button_control->button_int_enable,
> +			sizeof(struct f19_0d_control_1)*(button_reg + 1));
> +	if (retval < 0) {
> +		dev_err(dev, "%s : Could not write interrupt_enable_store"
> +			" to 0x%x\n", __func__, ctrl_bass_addr);
> +		return retval;
> +	}
> +
> +	return count;
> +}
> +
> +static ssize_t rmi_f19_single_button_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +	int i, len, total_len = 0;
> +	char *current_buf = buf;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +	/* loop through each button map value and copy its
> +	 * string representation into buf */
> +	for (i = 0; i < data->button_count; i++) {
> +		int button_reg;
> +		int button_shift;
> +		int single_button;
> +
> +		button_reg = i / 7;
> +		button_shift = i % 8;
> +		single_button = ((data->button_control->
> +			single_button_participation[button_reg].single_button
> +			>> button_shift) & 0x01);
> +
> +		/* get next button mapping value and write it to buf */
> +		len = snprintf(current_buf, PAGE_SIZE - total_len,
> +			"%u ", single_button);
> +		/* bump up ptr to next location in buf if the
> +		 * snprintf was valid.  Otherwise issue an error
> +		 * and return. */
> +		if (len > 0) {
> +			current_buf += len;
> +			total_len += len;
> +		} else {
> +			dev_err(dev, "%s: Failed to build signle button buffer"
> +				", code = %d.\n", __func__, len);
> +			return snprintf(buf, PAGE_SIZE, "unknown\n");
> +		}
> +	}
> +	len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
> +	if (len > 0)
> +		total_len += len;
> +	else
> +		dev_warn(dev, "%s: Failed to append carriage return.\n",
> +			 __func__);
> +
> +	return total_len;
> +
> +}
> +
> +static ssize_t rmi_f19_single_button_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +	int i;
> +	int button_count = 0;
> +	int retval = count;
> +	int ctrl_bass_addr;
> +	int button_reg = 0;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +	for (i = 0; i < data->button_count && *buf != 0;
> +	     i++) {
> +		int button_shift;
> +		int button;
> +
> +		button_reg = i / 7;
> +		button_shift = i % 8;
> +		/* get next button mapping value and store and bump up to
> +		 * point to next item in buf */
> +		sscanf(buf, "%u", &button);
> +
> +		if (button != 0 && button != 1) {
> +			dev_err(dev,
> +				"%s: Error - single button for button %d"
> +				" is not a valid value 0x%x.\n",
> +				__func__, i, button);
> +			return -EINVAL;
> +		}
> +		if (button_shift == 0)
> +			data->button_control->
> +				single_button_participation[button_reg].
> +				single_button = 0;
> +		data->button_control->single_button_participation[button_reg].
> +			single_button |=  (button << button_shift);
> +		button_count++;
> +		/* bump up buf to point to next item to read */
> +		while (*buf != 0) {
> +			buf++;
> +			if (*(buf - 1) == ' ')
> +				break;
> +		}
> +	}
> +
> +	/* Make sure the button count matches */
> +	if (button_count != data->button_count) {
> +		dev_err(dev,
> +		    "%s: Error - single button count of %d doesn't match"
> +		     " device button count of %d.\n", __func__, button_count,
> +		     data->button_count);
> +		return -EINVAL;
> +	}
> +	/* write back to the control register */
> +	ctrl_bass_addr = fc->fd.control_base_addr +
> +		sizeof(struct f19_0d_control_0) +
> +		sizeof(struct f19_0d_control_2)*(button_reg + 1);
> +	retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
> +		(u8 *)data->button_control->single_button_participation,
> +			sizeof(struct f19_0d_control_2)*(button_reg + 1));
> +	if (retval < 0) {
> +		dev_err(dev, "%s : Could not write interrupt_enable_store to"
> +			" 0x%x\n", __func__, ctrl_bass_addr);
> +		return -EINVAL;
> +	}
> +	return count;
> +}
> +
> +static ssize_t rmi_f19_sensor_map_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +	int i, len, total_len = 0;
> +	char *current_buf = buf;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +
> +	for (i = 0; i < data->button_count; i++) {
> +		len = snprintf(current_buf, PAGE_SIZE - total_len,
> +			"%u ", data->button_control->sensor_map[i].
> +			sensor_map_button);
> +		/* bump up ptr to next location in buf if the
> +		 * snprintf was valid.  Otherwise issue an error
> +		 * and return. */
> +		if (len > 0) {
> +			current_buf += len;
> +			total_len += len;
> +		} else {
> +			dev_err(dev, "%s: Failed to build sensor map buffer, "
> +				"code = %d.\n", __func__, len);
> +			return snprintf(buf, PAGE_SIZE, "unknown\n");
> +		}
> +	}
> +	len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
> +	if (len > 0)
> +		total_len += len;
> +	else
> +		dev_warn(dev, "%s: Failed to append carriage return.\n",
> +			 __func__);
> +	return total_len;
> +
> +
> +}
> +
> +static ssize_t rmi_f19_sensor_map_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +	int sensor_map;
> +	int i;
> +	int retval = count;
> +	int button_count = 0;
> +	int ctrl_bass_addr;
> +	int button_reg;
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +
> +	if (data->button_query.configurable == 0) {
> +		dev_err(dev,
> +			"%s: Error - sensor map is not configuralbe at"
> +			" run-time", __func__);

This is not driver error, return error silently.

> +		return -EINVAL;
> +	}

If sensor is not cinfigurable maybe attributes mode should be adjusted
to be read-only?

> +
> +	for (i = 0; i < data->button_count && *buf != 0; i++) {
> +		/* get next button mapping value and store and bump up to
> +		 * point to next item in buf */
> +		sscanf(buf, "%u", &sensor_map);
> +
> +		/* Make sure the key is a valid key */
> +		if (sensor_map < 0 || sensor_map > 127) {
> +			dev_err(dev,
> +				"%s: Error - sensor map for button %d is"
> +				" not a valid value 0x%x.\n",
> +				__func__, i, sensor_map);
> +			return -EINVAL;
> +		}
> +
> +		data->button_control->sensor_map[i].sensor_map_button =
> +			sensor_map;
> +		button_count++;
> +
> +		/* bump up buf to point to next item to read */
> +		while (*buf != 0) {
> +			buf++;
> +			if (*(buf - 1) == ' ')
> +				break;
> +		}
> +	}
> +
> +	if (button_count != data->button_count) {
> +		dev_err(dev,
> +		    "%s: Error - button map count of %d doesn't match device "
> +		     "button count of %d.\n", __func__, button_count,
> +		     data->button_count);
> +		return -EINVAL;
> +	}
> +
> +	/* write back to the control register */
> +	button_reg = (button_count / 7) + 1;
> +	ctrl_bass_addr = fc->fd.control_base_addr +
> +		sizeof(struct f19_0d_control_0) +
> +		sizeof(struct f19_0d_control_1)*button_reg +
> +		sizeof(struct f19_0d_control_2)*button_reg;
> +	retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
> +		(u8 *)data->button_control->sensor_map,
> +			sizeof(struct f19_0d_control_3_4)*button_count);
> +	if (retval < 0) {
> +		dev_err(dev, "%s : Could not sensor_map_store to 0x%x\n",
> +				__func__, ctrl_bass_addr);
> +		return -EINVAL;
> +	}
> +	return count;
> +}
> +
> +static ssize_t rmi_f19_sensitivity_adjust_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n", data->button_control->
> +		all_button_sensitivity_adj->sensitivity_adj);
> +
> +}
> +
> +static ssize_t rmi_f19_sensitivity_adjust_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +	unsigned int new_value;
> +	int len;
> +	int ctrl_bass_addr;
> +	int button_reg;
> +
> +	fc = to_rmi_function_container(dev);
> +
> +	data = fc->data;
> +
> +	if (data->button_query.configurable == 0) {
> +		dev_err(dev,
> +			"%s: Error - sensitivity_adjust is not"
> +			" configuralbe at run-time", __func__);
> +		return -EINVAL;
> +	}
> +
> +	len = sscanf(buf, "%u", &new_value);
> +	if (new_value < 0 || new_value > 31)
> +		return -EINVAL;
> +
> +	data->button_control->all_button_sensitivity_adj->sensitivity_adj =
> +		 new_value;
> +	/* write back to the control register */
> +	button_reg = (data->button_count / 7) + 1;
> +	ctrl_bass_addr = fc->fd.control_base_addr +
> +		sizeof(struct f19_0d_control_0) +
> +		sizeof(struct f19_0d_control_1)*button_reg +
> +		sizeof(struct f19_0d_control_2)*button_reg +
> +		sizeof(struct f19_0d_control_3_4)*data->button_count;
> +	len = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
> +		(u8 *)data->button_control->all_button_sensitivity_adj,
> +			sizeof(struct f19_0d_control_5));
> +	if (len < 0) {
> +		dev_err(dev, "%s : Could not sensitivity_adjust_store to"
> +			" 0x%x\n", __func__, ctrl_bass_addr);
> +		return len;
> +	}
> +
> +	return len;
> +}
> +
> +static ssize_t rmi_f19_hysteresis_threshold_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n", data->button_control->
> +		all_button_hysteresis_threshold->hysteresis_threshold);
> +
> +}
> +static ssize_t rmi_f19_hysteresis_threshold_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t count)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +	unsigned int new_value;
> +	int len;
> +	int ctrl_bass_addr;
> +	int button_reg;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +	len = sscanf(buf, "%u", &new_value);
> +	if (new_value < 0 || new_value > 15) {
> +		dev_err(dev, "%s: Error - hysteresis_threshold_store has an "
> +		"invalid value %d.\n",
> +		__func__, new_value);
> +		return -EINVAL;
> +	}
> +	data->button_control->all_button_hysteresis_threshold->
> +		hysteresis_threshold = new_value;
> +	/* write back to the control register */
> +	button_reg = (data->button_count / 7) + 1;
> +	ctrl_bass_addr = fc->fd.control_base_addr +
> +		sizeof(struct f19_0d_control_0) +
> +		sizeof(struct f19_0d_control_1)*button_reg +
> +		sizeof(struct f19_0d_control_2)*button_reg +
> +		sizeof(struct f19_0d_control_3_4)*data->button_count+
> +		sizeof(struct f19_0d_control_5);
> +	len = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
> +		(u8 *)data->button_control->all_button_sensitivity_adj,
> +			sizeof(struct f19_0d_control_6));
> +	if (len < 0) {
> +		dev_err(dev, "%s : Could not write all_button hysteresis "
> +			"threshold to 0x%x\n", __func__, ctrl_bass_addr);
> +		return -EINVAL;
> +	}
> +
> +	return count;
> +}
> +
> +static ssize_t rmi_f19_has_hysteresis_threshold_show(struct device *dev,
> +				      struct device_attribute *attr, char *buf)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n",
> +			data->button_query.has_hysteresis_threshold);
> +}
> +
> +static ssize_t rmi_f19_has_sensitivity_adjust_show(struct device *dev,
> +				      struct device_attribute *attr, char *buf)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n",
> +			data->button_query.has_sensitivity_adjust);
> +}
> +
> +static ssize_t rmi_f19_configurable_show(struct device *dev,
> +				      struct device_attribute *attr, char *buf)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n",
> +			data->button_query.configurable);
> +}
> +
> +static ssize_t rmi_f19_rezero_show(struct device *dev,
> +				struct device_attribute *attr,
> +				char *buf)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n",
> +			data->button_rezero);
> +
> +}
> +
> +static ssize_t rmi_f19_rezero_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf,
> +					 size_t count)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +	unsigned int new_value;
> +	int len;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +	len = sscanf(buf, "%u", &new_value);
> +	if (new_value != 0 && new_value != 1) {
> +		dev_err(dev,
> +			"%s: Error - rezero is not a "
> +			"valid value 0x%x.\n",
> +			__func__, new_value);
> +		return -EINVAL;
> +	}
> +	data->button_rezero = new_value & 1;
> +	len = rmi_write(fc->rmi_dev, fc->fd.command_base_addr,
> +		data->button_rezero);
> +
> +	if (len < 0) {
> +		dev_err(dev, "%s : Could not write rezero to 0x%x\n",
> +				__func__, fc->fd.command_base_addr);
> +		return -EINVAL;
> +	}
> +	return count;
> +}
> +
> +static ssize_t rmi_f19_button_count_show(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +	return snprintf(buf, PAGE_SIZE, "%u\n",
> +			data->button_count);
> +}
> +
> +static ssize_t rmi_f19_button_map_show(struct device *dev,
> +				struct device_attribute *attr,
> +				char *buf)
> +{
> +
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +	int i, len, total_len = 0;
> +	char *current_buf = buf;
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +	/* loop through each button map value and copy its
> +	 * string representation into buf */
> +	for (i = 0; i < data->button_count; i++) {
> +		/* get next button mapping value and write it to buf */
> +		len = snprintf(current_buf, PAGE_SIZE - total_len,
> +			"%u ", data->button_map[i]);
> +		/* bump up ptr to next location in buf if the
> +		 * snprintf was valid.  Otherwise issue an error
> +		 * and return. */
> +		if (len > 0) {
> +			current_buf += len;
> +			total_len += len;
> +		} else {
> +			dev_err(dev, "%s: Failed to build button map buffer, "
> +				"code = %d.\n", __func__, len);
> +			return snprintf(buf, PAGE_SIZE, "unknown\n");
> +		}
> +	}
> +	len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
> +	if (len > 0)
> +		total_len += len;
> +	else
> +		dev_warn(dev, "%s: Failed to append carriage return.\n",
> +			 __func__);
> +	return total_len;
> +}
> +
> +static ssize_t rmi_f19_button_map_store(struct device *dev,
> +				struct device_attribute *attr,
> +				const char *buf,
> +				size_t count)
> +{
> +	struct rmi_function_container *fc;
> +	struct f19_data *data;
> +	unsigned int button;
> +	int i;
> +	int retval = count;
> +	int button_count = 0;
> +	unsigned char temp_button_map[KEY_MAX];
> +
> +	fc = to_rmi_function_container(dev);
> +	data = fc->data;
> +
> +	/* Do validation on the button map data passed in.  Store button
> +	 * mappings into a temp buffer and then verify button count and
> +	 * data prior to clearing out old button mappings and storing the
> +	 * new ones. */
> +	for (i = 0; i < data->button_count && *buf != 0;
> +	     i++) {
> +		/* get next button mapping value and store and bump up to
> +		 * point to next item in buf */
> +		sscanf(buf, "%u", &button);
> +
> +		/* Make sure the key is a valid key */
> +		if (button > KEY_MAX) {
> +			dev_err(dev,
> +				"%s: Error - button map for button %d is not a"
> +				" valid value 0x%x.\n", __func__, i, button);
> +			retval = -EINVAL;
> +			goto err_ret;
> +		}
> +
> +		temp_button_map[i] = button;
> +		button_count++;
> +
> +		/* bump up buf to point to next item to read */
> +		while (*buf != 0) {
> +			buf++;
> +			if (*(buf - 1) == ' ')
> +				break;
> +		}
> +	}
> +
> +	/* Make sure the button count matches */
> +	if (button_count != data->button_count) {
> +		dev_err(dev,
> +		    "%s: Error - button map count of %d doesn't match device "
> +		     "button count of %d.\n", __func__, button_count,
> +		     data->button_count);
> +		retval = -EINVAL;
> +		goto err_ret;
> +	}
> +
> +	/* Clear the key bits for the old button map. */
> +	for (i = 0; i < button_count; i++)
> +		clear_bit(data->button_map[i], data->input->keybit);
> +
> +	/* Switch to the new map. */
> +	memcpy(data->button_map, temp_button_map,
> +	       data->button_count);
> +
> +	/* Loop through the key map and set the key bit for the new mapping. */
> +	for (i = 0; i < button_count; i++)
> +		set_bit(data->button_map[i], data->input->keybit);
> +
> +err_ret:
> +	return retval;
> +}

Button map (keymap) should be manipulated with EVIOCGKEYCODE and
EVIOCSKEYCODE ioctls, no need to invent driver-specific way of doing
this.

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

Thanks.

-- 
Dmitry

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

* Re: [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
  2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
                   ` (11 preceding siblings ...)
  2012-01-01 13:51 ` [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Linus Walleij
@ 2012-01-05  7:58 ` Dmitry Torokhov
  2012-01-05 20:09   ` Christopher Heiny
  12 siblings, 1 reply; 34+ messages in thread
From: Dmitry Torokhov @ 2012-01-05  7:58 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Jean Delvare, Linux Kernel, Linux Input, Joerie de Gram,
	Linus Walleij, Naveen Kumar Gaddipati

On Wed, Dec 21, 2011 at 06:09:51PM -0800, Christopher Heiny wrote:
> 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 represents a complete refactoring and rewrite of the previously
> submitted patches, and supersedes all those patches.  We believe that all
> outstanding issues arising from the previous submissions have been addressed.
> However, because of the quantity of new code, we are submitting this patch
> as an RFC and requesting feedback from the kernel community.
> 
> 
> This patch is against the v2.6.38 tag of Linus' kernel tree, commit
> 521cb40b0c44418a4fd36dc633f575813d59a43d.
> 
> 
> 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
>         * F19 capacitive buttons
>         * F34 device reflash
>         * F54 analog data reporting
> 
>     - character device access to the RMI4 register map via rmidev

Could you please tell me why this facility is needed?

-- 
Dmitry

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

* Re: [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
  2012-01-05  7:58 ` Dmitry Torokhov
@ 2012-01-05 20:09   ` Christopher Heiny
  0 siblings, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2012-01-05 20:09 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Joerie de Gram,
	Linus Walleij, Naveen Kumar Gaddipati

On 01/04/2012 11:58 PM, Dmitry Torokhov wrote:
> On Wed, Dec 21, 2011 at 06:09:51PM -0800, Christopher Heiny wrote:
>> 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.

[snip]


>>      - character device access to the RMI4 register map via rmidev
>
> Could you please tell me why this facility is needed?

In conjunction with an Android application (Synaptics RED), this allows 
the PC-based Design Studio 4 configuration and diagnostic tool to access 
the RMI4 sensor via USB, Wifi, Bluetooth, etc.  We figured it might also 
be useful to the general development community, so we included it here.

					Chris

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

* Re: [RFC PATCH 2/11] input: RMI4 core bus and sensor drivers.
  2012-01-02  6:38   ` Shubhrajyoti
@ 2012-01-05 20:49     ` Christopher Heiny
  2012-01-05 21:58       ` Lars-Peter Clausen
  2012-01-06  1:56     ` Christopher Heiny
  1 sibling, 1 reply; 34+ messages in thread
From: Christopher Heiny @ 2012-01-05 20:49 UTC (permalink / raw)
  To: Shubhrajyoti
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Joerie de Gram, Linus Walleij, Naveen Kumar Gaddipati

On 01/01/2012 10:38 PM, Shubhrajyoti wrote:
> Hi Christopher,
>
> On Thursday 22 December 2011 07:39 AM, Christopher Heiny wrote:
>> Signed-off-by: Christopher Heiny<cheiny@synaptics.com>
>>
[snip]

>> +#ifdef CONFIG_PM
>> +static int rmi_bus_suspend(struct device *dev)
>> +{
>> +#ifdef GENERIC_SUBSYS_PM_OPS
>> +	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
>> +
>> +	if (pm&&  pm->suspend)
>> +		return pm->suspend(dev);
> If driver-pm- suspend is not there should you not fall back to . suspend ?

You're thinking of dev->driver->suspend here, right?  If so, that sounds 
good to me.


>
>> +#endif
>> +
>> +	return 0;
>> +}
>> +
>> +static int rmi_bus_resume(struct device *dev)
>> +{
>> +#ifdef GENERIC_SUBSYS_PM_OPS
>> +	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
>> +
>> +	if (pm&&  pm->resume)
>> +		return pm->resume(dev);
> same here?
>> +#endif
>> +
>> +	return 0;
>> +}
>> +#endif

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

* Re: [RFC PATCH 2/11] input: RMI4 core bus and sensor drivers.
  2012-01-05 20:49     ` Christopher Heiny
@ 2012-01-05 21:58       ` Lars-Peter Clausen
  0 siblings, 0 replies; 34+ messages in thread
From: Lars-Peter Clausen @ 2012-01-05 21:58 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Shubhrajyoti, Dmitry Torokhov, Jean Delvare, Linux Kernel,
	Linux Input, Joerie de Gram, Linus Walleij,
	Naveen Kumar Gaddipati

On 01/05/2012 09:49 PM, Christopher Heiny wrote:
> On 01/01/2012 10:38 PM, Shubhrajyoti wrote:
>> Hi Christopher,
>>
>> On Thursday 22 December 2011 07:39 AM, Christopher Heiny wrote:
>>> Signed-off-by: Christopher Heiny<cheiny@synaptics.com>
>>>
> [snip]
> 
>>> +#ifdef CONFIG_PM
>>> +static int rmi_bus_suspend(struct device *dev)
>>> +{
>>> +#ifdef GENERIC_SUBSYS_PM_OPS
>>> +    const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
>>> +
>>> +    if (pm&&  pm->suspend)
>>> +        return pm->suspend(dev);
>> If driver-pm- suspend is not there should you not fall back to .
>> suspend ?
> 
> You're thinking of dev->driver->suspend here, right?  If so, that sounds
> good to me.
> 

This will add a ambiguity as to how to specify to suspend/resume callbacks.
Just use generic_subsys_pm_ops for your bus' pm ops. It should work and will
also allow drivers to implement other pm callbacks than just suspend and resume.

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

* Re: [RFC PATCH 10/11] input: RMI4 F19 - capacitive buttons
  2012-01-05  7:53   ` Dmitry Torokhov
@ 2012-01-06  0:05     ` Christopher Heiny
  2012-01-06  2:50       ` Dmitry Torokhov
  0 siblings, 1 reply; 34+ messages in thread
From: Christopher Heiny @ 2012-01-06  0:05 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Joerie de Gram,
	Linus Walleij, Naveen Kumar Gaddipati, Vivian Ly

On 01/04/2012 11:53 PM, Dmitry Torokhov wrote:
> Hi Christopher,
>
> On Wed, Dec 21, 2011 at 06:10:01PM -0800, Christopher Heiny wrote:
>> Signed-off-by: Christopher Heiny<cheiny@synaptics.com>
>
> A bunch of comments generally applicable to all patches.
>
> I have not looked at the core thoroughly yet.

Thanks for the feedback.  I've included our responses below (along with 
snippage to remove areas out of context.


>> ---
>>
>>   drivers/input/rmi4/rmi_f19.c | 1571 ++++++++++++++++++++++++++++++++++++++++++
>>   1 files changed, 1571 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..a678f30
>> --- /dev/null
>> +++ b/drivers/input/rmi4/rmi_f19.c
>> @@ -0,0 +1,1571 @@
>> +/*

[snip]

>> +
>> +struct f19_0d_control_0 {
>> +	union {
>> +		struct {
>> +			u8 button_usage:2;
>> +			u8 filter_mode:2;
>> +		};
>> +		u8 f19_0d_control0;
>> +	};
>
> Anonymous union in a struct is kind of pointless...

Agreed.


>> +};
>> +
>> +struct f19_0d_control_1 {
>> +	u8 int_enabled_button;
>> +};
>> +
>> +struct f19_0d_control_2 {
>> +	u8 single_button;
>> +};
>> +
>> +struct f19_0d_control_3_4 {
>> +	u8 sensor_map_button:7;
>> +	/*u8 sensitivity_button;*/
>> +};
>> +
>> +struct f19_0d_control_5 {
>> +	u8 sensitivity_adj;
>> +};
>> +struct f19_0d_control_6 {
>> +	u8 hysteresis_threshold;
>> +};
>> +
>> +struct f19_0d_control {
>> +	struct f19_0d_control_0 *general_control;
>
> Single instance, does not need to be allocated separately.
>
>> +	struct f19_0d_control_1 *button_int_enable;
>> +	struct f19_0d_control_2 *single_button_participation;
>> +	struct f19_0d_control_3_4 *sensor_map;
>
> This should probably be an array of
>
> struct f19_button_ctrl {
> 	struct f19_0d_control_1 int_enable;
> 	struct f19_0d_control_2 participation;
> 	struct f19_0d_control_3_4 sensor_map;
> };
>
> located at the end of f19_0d_control structure so it can be all
> allocated in one shot.

We organized it this way because of how the controls are organized in 
the register map: first the interrupt enables for all buttons, then the 
participation for all buttons, and then the sensor map for all buttons. 
  Typical client interactions are to adjust these in an "all at once" 
approach - that is, change as a single group all the interrupt enables, 
all the participation settings, or all the sensor map.  By organizing 
them the way we did, it makes it very easy to read/write the data to the 
RMI4 sensor's register map.  Using an array of structs would require a 
building buffers (on write) or tedious extraction from buffers (on read).

However, the first two of these are bitmasks, and don't really need 
their own structs - they can conveniently be u8 * instead.


>> +	struct f19_0d_control_5 *all_button_sensitivity_adj;
>
> Single instance, does not need to be allocated separately.

Agreed.

>
>> +	struct f19_0d_control_6 *all_button_hysteresis_threshold;
>
> Single instance, does not need to be allocated separately.
>
>> +};
>> +/* data specific to fn $19 that needs to be kept around */
>> +struct f19_data {
>> +	struct f19_0d_control *button_control;
>> +	struct f19_0d_query button_query;
>> +	u8 button_rezero;
>> +	bool *button_down;
>
> Just let input core track this for you.

That would work.  We were trying to be efficient by only generating 
events of interest, but I'm quite happy to let another subsystem do the 
heavy lifting for us. We'll follow your later recommendation and just 
pass button state info along.


>> +	unsigned char button_count;
>> +	unsigned char button_data_buffer_size;
>
> Your fingers must be hurting by the time you finished writing this
> driver...

I'm a little obsessive about descriptive variable names.  As a result, I 
have digits the size of Arnold Schwarzenegger's biceps. :-)

>
>> +	unsigned char *button_data_buffer;
>> +	unsigned char *button_map;
>
> Linux keycodes are wider than 8 bits, should be unsigned short.

Agreed.


>> +	char input_name[MAX_LEN];
>> +	char input_phys[MAX_LEN];
>> +	struct input_dev *input;
>> +};
>> +
>> +static ssize_t rmi_f19_button_count_show(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 char *buf);
>> +
>> +static ssize_t rmi_f19_button_map_show(struct device *dev,
>> +				      struct device_attribute *attr, char *buf);
>> +
>> +static ssize_t rmi_f19_button_map_store(struct device *dev,
>> +				       struct device_attribute *attr,
>> +				       const char *buf, size_t count);
>> +static ssize_t rmi_f19_rezero_show(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 char *buf);
>> +static ssize_t rmi_f19_rezero_store(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 const char *buf, size_t count);
>> +static ssize_t rmi_f19_has_hysteresis_threshold_show(struct device *dev,
>> +				      struct device_attribute *attr, char *buf);
>> +static ssize_t rmi_f19_has_sensitivity_adjust_show(struct device *dev,
>> +				      struct device_attribute *attr, char *buf);
>> +static ssize_t rmi_f19_configurable_show(struct device *dev,
>> +				      struct device_attribute *attr, char *buf);
>> +static ssize_t rmi_f19_filter_mode_show(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 char *buf);
>> +static ssize_t rmi_f19_filter_mode_store(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 const char *buf, size_t count);
>> +static ssize_t rmi_f19_button_usage_show(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 char *buf);
>> +static ssize_t rmi_f19_button_usage_store(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 const char *buf, size_t count);
>> +static ssize_t rmi_f19_interrupt_enable_button_show(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 char *buf);
>> +static ssize_t rmi_f19_interrupt_enable_button_store(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 const char *buf, size_t count);
>> +static ssize_t rmi_f19_single_button_show(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 char *buf);
>> +static ssize_t rmi_f19_single_button_store(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 const char *buf, size_t count);
>> +static ssize_t rmi_f19_sensor_map_show(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 char *buf);
>> +static ssize_t rmi_f19_sensor_map_store(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 const char *buf, size_t count);
>> +static ssize_t rmi_f19_sensitivity_adjust_show(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 char *buf);
>> +static ssize_t rmi_f19_sensitivity_adjust_store(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 const char *buf, size_t count);
>> +static ssize_t rmi_f19_hysteresis_threshold_show(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 char *buf);
>> +static ssize_t rmi_f19_hysteresis_threshold_store(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 const char *buf, size_t count);
>> +
>> +
>> +
>> +static inline int rmi_f19_alloc_memory(struct rmi_function_container *fc);
>> +
>> +static inline void rmi_f19_free_memory(struct rmi_function_container *fc);
>> +
>> +static inline int rmi_f19_initialize(struct rmi_function_container *fc);
>> +
>> +static inline int rmi_f19_register_device(struct rmi_function_container *fc);
>> +
>> +static inline 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);
>> +
>> +static struct device_attribute attrs[] = {
>> +	__ATTR(button_count, RMI_RO_ATTR,
>> +		rmi_f19_button_count_show, rmi_store_error),
>
> Why not NULL instead of rmi_store_error?

We found that customers picking up our driver would try to write RO 
sysfs attributes (or read WO attrs) by invoking echo from the command 
line.  The operation would fail silently (I'm looking at you, Android 
shell), leaving the engineer baffled as to why the sensor behavior 
didn't change.  So we adopted this approach so as to give some clue as 
to the fact that the operation failed.

>> +	__ATTR(button_map, RMI_RW_ATTR,
>> +		rmi_f19_button_map_show, rmi_f19_button_map_store),
>> +	__ATTR(rezero, RMI_RW_ATTR,
>> +		rmi_f19_rezero_show, rmi_f19_rezero_store),
>> +	__ATTR(has_hysteresis_threshold, RMI_RO_ATTR,
>> +		rmi_f19_has_hysteresis_threshold_show, rmi_store_error),
>> +	__ATTR(has_sensitivity_adjust, RMI_RO_ATTR,
>> +		rmi_f19_has_sensitivity_adjust_show, rmi_store_error),
>> +	__ATTR(configurable, RMI_RO_ATTR,
>> +		rmi_f19_configurable_show, rmi_store_error),
>> +	__ATTR(filter_mode, RMI_RW_ATTR,
>> +		rmi_f19_filter_mode_show, rmi_f19_filter_mode_store),
>> +	__ATTR(button_usage, RMI_RW_ATTR,
>> +		rmi_f19_button_usage_show, rmi_f19_button_usage_store),
>> +	__ATTR(interrupt_enable_button, RMI_RW_ATTR,
>> +		rmi_f19_interrupt_enable_button_show,
>> +		rmi_f19_interrupt_enable_button_store),
>> +	__ATTR(single_button, RMI_RW_ATTR,
>> +		rmi_f19_single_button_show, rmi_f19_single_button_store),
>> +	__ATTR(sensor_map, RMI_RW_ATTR,
>> +		rmi_f19_sensor_map_show, rmi_f19_sensor_map_store),
>> +	__ATTR(sensitivity_adjust, RMI_RW_ATTR,
>> +		rmi_f19_sensitivity_adjust_show,
>> +		rmi_f19_sensitivity_adjust_store),
>> +	__ATTR(hysteresis_threshold, RMI_RW_ATTR,
>> +		rmi_f19_hysteresis_threshold_show,
>> +		rmi_f19_hysteresis_threshold_store)
>> +};
>> +
>> +
>> +int rmi_f19_read_control_parameters(struct rmi_device *rmi_dev,
>> +	struct f19_0d_control *button_control,
>> +	unsigned char button_count,
>> +	unsigned char int_button_enabled_count,
>> +	u8 ctrl_base_addr)
>> +{
>> +	int error = 0;
>> +	int i;
>> +
>> +	if (button_control->general_control) {
>
> It can't be NULL.

OK.

>> +		error = rmi_read_block(rmi_dev, ctrl_base_addr,
>> +				(u8 *)button_control->general_control,
>> +				sizeof(struct f19_0d_control_0));
>> +		if (error<  0) {
>> +			dev_err(&rmi_dev->dev,
>> +				"Failed to read f19_0d_control_0, code:"
>> +				" %d.\n", error);
>> +			return error;
>> +		}
>> +		ctrl_base_addr = ctrl_base_addr +
>> +				sizeof(struct f19_0d_control_0);
>> +	}
>> +
>> +	if (button_control->button_int_enable) {
>
> Neither can this.

OK.


>> +		for (i = 0; i<  int_button_enabled_count; i++) {
>> +			error = rmi_read_block(rmi_dev, ctrl_base_addr,
>> +				(u8 *)&button_control->button_int_enable[i],
>> +				sizeof(struct f19_0d_control_1));
>> +			if (error<  0) {
>> +				dev_err(&rmi_dev->dev,
>> +					"Failed to read f19_0d_control_2,"
>> +					" code: %d.\n", error);
>> +				return error;
>> +			}
>> +			ctrl_base_addr = ctrl_base_addr +
>> +				sizeof(struct f19_0d_control_1);
>> +		}
>> +	}
>> +
>> +	if (button_control->single_button_participation) {
>
> Nor this.

OK

>
>> +		for (i = 0; i<  int_button_enabled_count; i++) {
>> +			error = rmi_read_block(rmi_dev, ctrl_base_addr,
>> +					(u8 *)&button_control->
>> +						single_button_participation[i],
>> +					sizeof(struct f19_0d_control_2));
>> +			if (error<  0) {
>> +				dev_err(&rmi_dev->dev,
>> +					"Failed to read f19_0d_control_2,"
>> +					" code: %d.\n", error);
>> +				return error;
>> +			}
>> +			ctrl_base_addr = ctrl_base_addr +
>> +				sizeof(struct f19_0d_control_2);
>> +		}
>> +	}
>> +
>> +	if (button_control->sensor_map) {
>
> Nor this...

OK

>
>> +		for (i = 0; i<  button_count; i++) {
>> +			error = rmi_read_block(rmi_dev, ctrl_base_addr,
>> +					(u8 *)&button_control->sensor_map[i],
>> +					sizeof(struct f19_0d_control_3_4));
>> +			if (error<  0) {
>> +				dev_err(&rmi_dev->dev,
>> +				"Failed to read f19_0d_control_3_4,"
>> +				" code: %d.\n", error);
>> +				return error;
>> +			}
>> +			ctrl_base_addr = ctrl_base_addr +
>> +				sizeof(struct f19_0d_control_3_4);
>> +		}
>> +	}
>> +
>> +	if (button_control->all_button_sensitivity_adj) {
>
> Nor this.

OK.  And it probably shouldn't even be a pointer.


>> +		error = rmi_read_block(rmi_dev, ctrl_base_addr,
>> +				(u8 *)button_control->
>> +					all_button_sensitivity_adj,
>> +				sizeof(struct f19_0d_control_5));
>> +		if (error<  0) {
>> +			dev_err(&rmi_dev->dev,
>> +				"Failed to read f19_0d_control_5,"
>> +				" code: %d.\n", error);
>> +			return error;
>> +		}
>> +		ctrl_base_addr = ctrl_base_addr +
>> +			sizeof(struct f19_0d_control_5);
>> +	}
>> +
>> +	if (button_control->all_button_hysteresis_threshold) {
>
> ...

OK.

>
>> +		error = rmi_read_block(rmi_dev, ctrl_base_addr,
>> +				(u8 *)button_control->
>> +					all_button_hysteresis_threshold,
>> +				sizeof(struct f19_0d_control_6));
>> +		if (error<  0) {
>> +			dev_err(&rmi_dev->dev,
>> +				"Failed to read f19_0d_control_6,"
>> +				" code: %d.\n", error);
>> +			return error;
>> +		}
>> +		ctrl_base_addr = ctrl_base_addr +
>> +			sizeof(struct f19_0d_control_6);
>> +	}
>> +	return 0;
>> +}

[snip]

>> +
>> +
>> +static inline int rmi_f19_alloc_memory(struct rmi_function_container *fc)
>
> Please drop inline throughout, let compiler do its job.

Will do through out the driver.

>> +
>> +	f19->button_data_buffer_size = (f19->button_count + 7) / 8;
>
> 	DIV_ROUND_UP(f19->button_count, 8);

I think we tried this once, and found that some compilers/toolkits 
barfed on it.  I'll look into it.


>> +	f19->button_data_buffer =
>> +	    kcalloc(f19->button_data_buffer_size,
>> +		    sizeof(unsigned char), GFP_KERNEL);
>> +	if (!f19->button_data_buffer) {
>> +		dev_err(&fc->dev, "Failed to allocate button data buffer.\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	f19->button_map = kcalloc(f19->button_count,
>> +				sizeof(unsigned char), GFP_KERNEL);
>> +	if (!f19->button_map) {
>> +		dev_err(&fc->dev, "Failed to allocate button map.\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	f19->button_control = kzalloc(sizeof(struct f19_0d_control),
>> +				GFP_KERNEL);
>
> If you allocate a single copy of this structure why isn't it a member of
> struct f19_data to begin with?

Don't ask me questions I can't answer.  :-)  That's good point - we'll 
move it.

>> +	if (!f19->button_control) {
>> +		dev_err(&fc->dev, "Failed to allocate button_control.\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	f19->button_control->general_control =
>> +		kzalloc(sizeof(struct f19_0d_control_0), GFP_KERNEL);
>
> This a single byte, why do you need to allocate it separately?

See above.

>
>> +	if (!f19->button_control->general_control) {
>> +		dev_err(&fc->dev, "Failed to allocate"
>> +				" f19_0d_control_0.\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	f19->button_control->button_int_enable =
>> +		kzalloc(f19->button_data_buffer_size *
>> +			sizeof(struct f19_0d_control_2), GFP_KERNEL);
>> +	if (!f19->button_control->button_int_enable) {
>> +		dev_err(&fc->dev, "Failed to allocate f19_0d_control_1.\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	f19->button_control->single_button_participation =
>> +		kzalloc(f19->button_data_buffer_size *
>> +			sizeof(struct f19_0d_control_2), GFP_KERNEL);
>> +	if (!f19->button_control->single_button_participation) {
>> +		dev_err(&fc->dev, "Failed to allocate"
>> +			" f19_0d_control_2.\n");
>
> Do not split error messages; it's OK if they exceed 80 column limit.

We have one customer who refuses to accept any code if any line exceeds 
80 columns, so we wind up with ugliness like this.

>> +		return -ENOMEM;
>> +	}
>> +
>> +	f19->button_control->sensor_map =
>> +		kzalloc(f19->button_count *
>> +			sizeof(struct f19_0d_control_3_4), GFP_KERNEL);
>> +	if (!f19->button_control->sensor_map) {
>> +		dev_err(&fc->dev, "Failed to allocate"
>> +			" f19_0d_control_3_4.\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	f19->button_control->all_button_sensitivity_adj =
>> +		kzalloc(sizeof(struct f19_0d_control_5), GFP_KERNEL);
>> +	if (!f19->button_control->all_button_sensitivity_adj) {
>> +		dev_err(&fc->dev, "Failed to allocate"
>> +			" f19_0d_control_5.\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	f19->button_control->all_button_hysteresis_threshold =
>> +		kzalloc(sizeof(struct f19_0d_control_6), GFP_KERNEL);
>> +	if (!f19->button_control->all_button_hysteresis_threshold) {
>> +		dev_err(&fc->dev, "Failed to allocate"
>> +			" f19_0d_control_6.\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +
>> +
>> +static inline void rmi_f19_free_memory(struct rmi_function_container *fc)
>> +{
>> +	struct f19_data *f19 = fc->data;
>> +
>> +	if (f19) {
>
> Can it be NULL? How?

This is just defensive, in case someone calls this at the wrong place or 
time.

>> +		kfree(f19->button_down);
>> +		kfree(f19->button_data_buffer);
>> +		kfree(f19->button_map);
>> +		if (f19->button_control) {
>> +			kfree(f19->button_control->general_control);
>> +			kfree(f19->button_control->button_int_enable);
>> +			kfree(f19->button_control->single_button_participation);
>> +			kfree(f19->button_control->sensor_map);
>> +			kfree(f19->button_control->all_button_sensitivity_adj);
>> +			kfree(f19->button_control->
>> +				  all_button_hysteresis_threshold);
>> +			kfree(f19->button_control);
>> +		}
>> +		kfree(f19);
>> +		fc->data = NULL;
>> +	}
>> +}
>> +
>> +
>> +static inline int rmi_f19_initialize(struct rmi_function_container *fc)
>> +{
>> +	struct rmi_device *rmi_dev = fc->rmi_dev;
>> +	struct rmi_device_platform_data *pdata;
>> +	struct f19_data *f19 = fc->data;
>> +	int i;
>> +	int rc;
>> +
>> +	dev_info(&fc->dev, "Intializing F19 values.");
>
> dev_dbg()

OK.

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

[snip]

>> +
>> +static inline int rmi_f19_register_device(struct rmi_function_container *fc)
>> +{
>> +	struct rmi_device *rmi_dev = fc->rmi_dev;
>> +	struct input_dev *input_dev;
>> +	struct f19_data *f19 = 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;
>> +	}
>> +
>> +	f19->input = input_dev;
>> +	snprintf(f19->input_name, MAX_LEN, "%sfn%02x", dev_name(&rmi_dev->dev),
>> +		fc->fd.function_number);
>> +	input_dev->name = f19->input_name;
>> +	snprintf(f19->input_phys, MAX_LEN, "%s/input0", input_dev->name);
>> +	input_dev->phys = f19->input_phys;
>> +	input_dev->dev.parent =&rmi_dev->dev;
>> +	input_set_drvdata(input_dev, f19);
>> +
>> +	/* Set up any input events. */
>> +	set_bit(EV_SYN, input_dev->evbit);
>> +	set_bit(EV_KEY, input_dev->evbit);
>
> __set_bit(), no need to lock the bus.

OK.

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

[snip]

>> +static inline int rmi_f19_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;
>> +		}
>> +	}
>
> This is called attribute group, please use it.

Good point.  Will do that here and in the rest of the driver.



>> +
>> +	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_f19_config(struct rmi_function_container *fc)
>> +{
>> +	struct f19_data *data;
>> +	int retval;
>> +	int ctrl_base_addr;
>> +	int button_reg;
>> +
>> +	data = fc->data;
>> +
>> +	ctrl_base_addr = fc->fd.control_base_addr;
>> +
>> +	button_reg = (data->button_count / 7) + 1;
>
>
> Please combine variable declarations and initializations - this makes
> code a bit less verbose and easier to read.

Agree.

[snip]

>> +
>> +int rmi_f19_attention(struct rmi_function_container *fc, u8 *irq_bits)
>
> static.

Agree.

>> +{
>> +	struct rmi_device *rmi_dev = fc->rmi_dev;
>> +	struct f19_data *f19 = fc->data;
>> +	u8 data_base_addr = fc->fd.data_base_addr;
>> +	int error;
>> +	int button;
>> +
>> +	/* Read the button data. */
>> +
>> +	error = rmi_read_block(rmi_dev, data_base_addr, f19->button_data_buffer,
>> +			f19->button_data_buffer_size);
>> +	if (error<  0) {
>> +		dev_err(&fc->dev, "%s: Failed to read button data registers.\n",
>> +			__func__);
>> +		return error;
>> +	}
>> +
>> +	/* Generate events for buttons that change state. */
>> +	for (button = 0; button<  f19->button_count;
>> +	     button++) {
>> +		int button_reg;
>> +		int button_shift;
>> +		bool button_status;
>> +
>> +		/* determine which data byte the button status is in */
>> +		button_reg = button / 7;
>> +		/* bit shift to get button's status */
>> +		button_shift = button % 8;
>> +		button_status =
>> +		    ((f19->button_data_buffer[button_reg]>>  button_shift)
>> +			&  0x01) != 0;
>> +
>> +		/* if the button state changed from the last time report it
>> +		 * and store the new state */
>> +		if (button_status != f19->button_down[button]) {
>> +			dev_dbg(&fc->dev, "%s: Button %d (code %d) ->  %d.\n",
>> +				__func__, button, f19->button_map[button],
>> +				 button_status);
>> +			/* Generate an event here. */
>> +			input_report_key(f19->input, f19->button_map[button],
>> +					 button_status);
>> +			f19->button_down[button] = button_status;
>> +		}
>> +	}
>
> All of the above could be reduced to:
>
> 	for (button = 0; button<  f19->button_count; button++)
> 		input_report_key(f19->input, f19->button_map[button],
> 				 test_bit(button, f19->button_data_buffer);

I'd like to, but I'm not sure - can we count on the endian-ness of the 
host processor being the same as the RMI4 register endian-ness?

[snip]

>> +
>> +static ssize_t rmi_f19_sensor_map_store(struct device *dev,
>> +					 struct device_attribute *attr,
>> +					 const char *buf, size_t count)
>> +{
>> +	struct rmi_function_container *fc;
>> +	struct f19_data *data;
>> +	int sensor_map;
>> +	int i;
>> +	int retval = count;
>> +	int button_count = 0;
>> +	int ctrl_bass_addr;
>> +	int button_reg;
>> +	fc = to_rmi_function_container(dev);
>> +	data = fc->data;
>> +
>> +	if (data->button_query.configurable == 0) {
>> +		dev_err(dev,
>> +			"%s: Error - sensor map is not configuralbe at"
>> +			" run-time", __func__);
>
> This is not driver error, return error silently.

I don't like failing silently, for reasons outlined above.

>
>> +		return -EINVAL;
>> +	}
>
> If sensor is not cinfigurable maybe attributes mode should be adjusted
> to be read-only?

Good point.  Then we could eliminate the dev_err, above.  We'll look 
into this at the same time as the attribute groups.

[snip]

>> +
>> +static ssize_t rmi_f19_button_map_store(struct device *dev,
>> +				struct device_attribute *attr,
>> +				const char *buf,
>> +				size_t count)
>> +{
>> +	struct rmi_function_container *fc;
>> +	struct f19_data *data;
>> +	unsigned int button;
>> +	int i;
>> +	int retval = count;
>> +	int button_count = 0;
>> +	unsigned char temp_button_map[KEY_MAX];
>> +
>> +	fc = to_rmi_function_container(dev);
>> +	data = fc->data;
>> +
>> +	/* Do validation on the button map data passed in.  Store button
>> +	 * mappings into a temp buffer and then verify button count and
>> +	 * data prior to clearing out old button mappings and storing the
>> +	 * new ones. */
>> +	for (i = 0; i<  data->button_count&&  *buf != 0;
>> +	     i++) {
>> +		/* get next button mapping value and store and bump up to
>> +		 * point to next item in buf */
>> +		sscanf(buf, "%u",&button);
>> +
>> +		/* Make sure the key is a valid key */
>> +		if (button>  KEY_MAX) {
>> +			dev_err(dev,
>> +				"%s: Error - button map for button %d is not a"
>> +				" valid value 0x%x.\n", __func__, i, button);
>> +			retval = -EINVAL;
>> +			goto err_ret;
>> +		}
>> +
>> +		temp_button_map[i] = button;
>> +		button_count++;
>> +
>> +		/* bump up buf to point to next item to read */
>> +		while (*buf != 0) {
>> +			buf++;
>> +			if (*(buf - 1) == ' ')
>> +				break;
>> +		}
>> +	}
>> +
>> +	/* Make sure the button count matches */
>> +	if (button_count != data->button_count) {
>> +		dev_err(dev,
>> +		    "%s: Error - button map count of %d doesn't match device "
>> +		     "button count of %d.\n", __func__, button_count,
>> +		     data->button_count);
>> +		retval = -EINVAL;
>> +		goto err_ret;
>> +	}
>> +
>> +	/* Clear the key bits for the old button map. */
>> +	for (i = 0; i<  button_count; i++)
>> +		clear_bit(data->button_map[i], data->input->keybit);
>> +
>> +	/* Switch to the new map. */
>> +	memcpy(data->button_map, temp_button_map,
>> +	       data->button_count);
>> +
>> +	/* Loop through the key map and set the key bit for the new mapping. */
>> +	for (i = 0; i<  button_count; i++)
>> +		set_bit(data->button_map[i], data->input->keybit);
>> +
>> +err_ret:
>> +	return retval;
>> +}
>
> Button map (keymap) should be manipulated with EVIOCGKEYCODE and
> EVIOCSKEYCODE ioctls, no need to invent driver-specific way of doing
> this.

Makes sense, but... we had a customer request to specify the boot-time 
keymap through the RMI4 driver's platform data.  Is it legal to call 
setkeycode() to do that instead?  If so, we'll do that and get rid of 
the button_map entirely.

Thanks again for the review and input,

					Chris

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

* Re: [RFC PATCH 2/11] input: RMI4 core bus and sensor drivers.
  2012-01-02  6:38   ` Shubhrajyoti
  2012-01-05 20:49     ` Christopher Heiny
@ 2012-01-06  1:56     ` Christopher Heiny
  1 sibling, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2012-01-06  1:56 UTC (permalink / raw)
  To: Shubhrajyoti
  Cc: Dmitry Torokhov, Jean Delvare, Linux Kernel, Linux Input,
	Joerie de Gram, Linus Walleij, Naveen Kumar Gaddipati

Hi Shubhrajyoti,

Thanks for the feedback.  Please see my comments below.


On 01/01/2012 10:38 PM, Shubhrajyoti wrote:
> Hi Christopher,
>
> On Thursday 22 December 2011 07:39 AM, Christopher Heiny wrote:
>> Signed-off-by: Christopher Heiny<cheiny@synaptics.com>
>>
>> Cc: Dmitry Torokhov<dmitry.torokhov@gmail.com>
>> Cc: Linus Walleij<linus.walleij@stericsson.com>
>> Cc: Naveen Kumar Gaddipati<naveen.gaddipati@stericsson.com>
>> Cc: Joeri de Gram<j.de.gram@gmail.com>
>>
>> ---
>>
>>   drivers/input/rmi4/rmi_bus.c    |  436 ++++++++++++
>>   drivers/input/rmi4/rmi_driver.c | 1488 +++++++++++++++++++++++++++++++++++++++
>>   drivers/input/rmi4/rmi_driver.h |   97 +++
>>   3 files changed, 2021 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c

[snip]

>> +#ifdef CONFIG_PM
>> +static int rmi_bus_suspend(struct device *dev)
>> +{
>> +#ifdef GENERIC_SUBSYS_PM_OPS
>> +	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
>> +
>> +	if (pm&&  pm->suspend)
>> +		return pm->suspend(dev);
> If driver-pm- suspend is not there should you not fall back to . suspend ?

I'm looking into using dev->driver->suspend() here.  It wants a 
pm_message_t for the pm state.  We don't have that on entry to 
bus_suspend, though.  Any suggestion as to what we should pass?

				Thanks,
					Chris

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

* Re: [RFC PATCH 2/11] input: RMI4 core bus and sensor drivers.
  2011-12-22  2:09 ` [RFC PATCH 2/11] input: RMI4 core bus and sensor drivers Christopher Heiny
  2012-01-02  6:38   ` Shubhrajyoti
@ 2012-01-06  2:34   ` Dmitry Torokhov
  2012-01-07  3:26     ` Christopher Heiny
  1 sibling, 1 reply; 34+ messages in thread
From: Dmitry Torokhov @ 2012-01-06  2:34 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Jean Delvare, Linux Kernel, Linux Input, Joerie de Gram,
	Linus Walleij, Naveen Kumar Gaddipati

Hi Christopher,

Please find my [incomplete] comments below.

On Wed, Dec 21, 2011 at 06:09:53PM -0800, Christopher Heiny wrote:
> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
> 
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Linus Walleij <linus.walleij@stericsson.com>
> Cc: Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
> Cc: Joeri de Gram <j.de.gram@gmail.com>
> 
> ---
> 
>  drivers/input/rmi4/rmi_bus.c    |  436 ++++++++++++
>  drivers/input/rmi4/rmi_driver.c | 1488 +++++++++++++++++++++++++++++++++++++++
>  drivers/input/rmi4/rmi_driver.h |   97 +++
>  3 files changed, 2021 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..e32d4ad
> --- /dev/null
> +++ b/drivers/input/rmi4/rmi_bus.c
> @@ -0,0 +1,436 @@
> +/*
> + * Copyright (c) 2011 Synaptics Incorporated
> + * Copyright (c) 2011 Unixphere
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/pm.h>
> +#include <linux/slab.h>
> +#include <linux/list.h>
> +#include <linux/rmi.h>
> +
> +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 int physical_device_count;
> +
> +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, "Matching %s.\n", 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_err(dev, "%s: Match %s to %s failed.\n", __func__,
> +		pdata->driver_name, rmi_driver->driver.name);

Why is this an error? dev_vdbg() at most, better yet just remove it.

> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int rmi_bus_suspend(struct device *dev)
> +{
> +#ifdef GENERIC_SUBSYS_PM_OPS
> +	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
> +
> +	if (pm && pm->suspend)
> +		return pm->suspend(dev);
> +#endif
> +
> +	return 0;
> +}
> +
> +static int rmi_bus_resume(struct device *dev)
> +{
> +#ifdef GENERIC_SUBSYS_PM_OPS
> +	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
> +
> +	if (pm && pm->resume)
> +		return pm->resume(dev);
> +#endif
> +
> +	return 0;
> +}
> +#endif

These are not needed if you switch to generic_subsys_pm_ops.

> +
> +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
> +};
> +
> +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;
> +
> +	mutex_lock(&rmi_bus_mutex);
> +	rmi_dev->number = physical_device_count;
> +	physical_device_count++;
> +	mutex_unlock(&rmi_bus_mutex);

Do
	rmi_dev->number = atomic_inc_return(&rmi_no) - 1;

and  stick "static atomic_t rmi_no = ATOMIC_INIT(0)"; at the beginning
of the function. Then you don't need to take mutex here. Do you need
rmi_dev->number?

> +
> +	dev_set_name(&rmi_dev->dev, "sensor%02d", rmi_dev->number);
> +	pr_debug("%s: Registered %s as %s.\n", __func__, pdata->sensor_name,
> +		dev_name(&rmi_dev->dev));
> +
> +	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;
> +
> +	device_unregister(&rmi_dev->dev);
> +	kfree(rmi_dev);

This is lifetime rules violation; rmi_dev->dev might still be referenced
and you are freeing it. Please provide proper release function.

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

No cleanup whatsoever?

> +
> +	return 0;
> +}
> +
> +void rmi_unregister_driver(struct rmi_driver *driver)
> +{
> +	bus_for_each_dev(&rmi_bus_type, NULL, driver, __rmi_driver_remove);

Why don't you rely on driver core to unbind devices upon driver removal
instead of rolling your own (and highly likely broken) implementation.

> +	driver_unregister(&driver->driver);
> +}
> +EXPORT_SYMBOL(rmi_unregister_driver);
> +
> +static int __rmi_bus_fh_add(struct device *dev, void *data)
> +{
> +	struct rmi_driver *driver;
> +	struct rmi_device *rmi_dev = to_rmi_device(dev);
> +
> +	driver = rmi_dev->driver;
> +	if (driver && driver->fh_add)
> +		driver->fh_add(rmi_dev, data);
> +
> +	return 0;
> +}
> +
> +int rmi_register_function_driver(struct rmi_function_handler *fh)
> +{
> +	struct rmi_function_list *entry;
> +	struct rmi_function_handler *fh_dup;
> +
> +	fh_dup = rmi_get_function_handler(fh->func);
> +	if (fh_dup) {
> +		pr_err("%s: function f%.2x already registered!\n", __func__,
> +			fh->func);
> +		return -EINVAL;
> +	}
> +
> +	entry = kzalloc(sizeof(struct rmi_function_list), GFP_KERNEL);
> +	if (!entry)
> +		return -ENOMEM;
> +
> +	entry->fh = fh;
> +	list_add_tail(&entry->list, &rmi_supported_functions.list);
> +
> +	/* notify devices of the new function handler */
> +	bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_add);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(rmi_register_function_driver);
> +
> +static int __rmi_bus_fh_remove(struct device *dev, void *data)
> +{
> +	struct rmi_driver *driver;
> +	struct rmi_device *rmi_dev = to_rmi_device(dev);
> +
> +	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);
> +
> +	list_for_each_entry_safe(entry, n, &rmi_supported_functions.list,
> +									list) {
> +		if (entry->fh->func == fh->func) {
> +			list_del(&entry->list);
> +			kfree(entry);
> +		}
> +	}

You are still rolling partly your own infrastructure. It looks like you
need 2 types of devices on rmi bus - composite RMI device and sensor
device, see struct device_type. Make 2 of those and match drivers
depending on the device type. Then driver core will maintain all the
lists for you.

> +
> +}
> +EXPORT_SYMBOL(rmi_unregister_function_driver);
> +
> +struct rmi_function_handler *rmi_get_function_handler(int id)
> +{
> +	struct rmi_function_list *entry;
> +
> +	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;

No, no, no. Do not try to shut up warnings from device core; they are
there for a reason.

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

Hmm, thisi is very roundabout way of attaching RMI chardevice... Does it
even work if driver [re]appears after rmi_dev module was loaded?

IFF we agree on keeping rmi_dev interface then I think something more
elegant could be cooked via bus's blocking notifier.

> +
> +	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);
> +
> +	error = bus_register(&rmi_bus_type);
> +	if (error < 0) {
> +		pr_err("%s: error registering the RMI bus: %d\n", __func__,
> +		       error);
> +		return error;
> +	}
> +	pr_info("%s: successfully registered RMI bus.\n", __func__);

This is all useless noise. Just do:

	return bus_register(&rmi_bus_type);

> +
> +	return 0;
> +}
> +
> +static void __exit rmi_bus_exit(void)
> +{
> +	struct rmi_function_list *entry, *n;
> +
> +	list_for_each_entry_safe(entry, n, &rmi_supported_functions.list,
> +				 list) {
> +		list_del(&entry->list);
> +		kfree(entry);
> +	}

How can this list be non-free? Your bus code is reference by function
drivers so module count is non zero until all such drivers are unloaded,
and therefore rmi_bus_exit() can not be called.

> +
> +	bus_unregister(&rmi_bus_type);
> +}
> +
> +module_init(rmi_bus_init);
> +module_exit(rmi_bus_exit);
> +
> +MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
> +MODULE_DESCRIPTION("RMI bus");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
> new file mode 100644
> index 0000000..07097bb
> --- /dev/null
> +++ b/drivers/input/rmi4/rmi_driver.c
> @@ -0,0 +1,1488 @@
> +/*
> + * Copyright (c) 2011 Synaptics Incorporated
> + * Copyright (c) 2011 Unixphere
> + *
> + * This driver adds support for generic RMI4 devices from Synpatics. It
> + * implements the mandatory f01 RMI register and depends on the presence of
> + * other required RMI functions.
> + *
> + * The RMI4 specification can be found here (URL split after files/ for
> + * style reasons):
> + * http://www.synaptics.com/sites/default/files/
> + *           511-000136-01-Rev-E-RMI4%20Intrfacing%20Guide.pdf
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/slab.h>
> +#include <linux/list.h>
> +#include <linux/pm.h>
> +#include <linux/rmi.h>
> +#include "rmi_driver.h"
> +
> +#define DELAY_DEBUG 0
> +#define REGISTER_DEBUG 0
> +
> +#define PDT_END_SCAN_LOCATION	0x0005
> +#define PDT_PROPERTIES_LOCATION 0x00EF
> +#define BSR_LOCATION 0x00FE
> +#define HAS_BSR_MASK 0x20
> +#define HAS_NONSTANDARD_PDT_MASK 0x40
> +#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
> +#define RMI4_MAX_PAGE 0xff
> +#define RMI4_PAGE_SIZE 0x100
> +
> +#define RMI_DEVICE_RESET_CMD	0x01
> +#define DEFAULT_RESET_DELAY_MS	20
> +
> +#ifdef CONFIG_HAS_EARLYSUSPEND
> +static void rmi_driver_early_suspend(struct early_suspend *h);
> +static void rmi_driver_late_resume(struct early_suspend *h);
> +#endif

Does not appear to be in mainline; please trim.

> +
> +/* sysfs files for attributes for driver values. */
> +static ssize_t rmi_driver_hasbsr_show(struct device *dev,
> +				      struct device_attribute *attr, char *buf);

Well, if it does not have bsr why do you even create bsr attribute?

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

Should rely on load/unload or bind/unbind; no need for yet another
mechanism.

> +
> +static ssize_t rmi_driver_phys_show(struct device *dev,
> +				       struct device_attribute *attr,
> +				       char *buf);
> +
> +static ssize_t rmi_driver_version_show(struct device *dev,
> +				       struct device_attribute *attr,
> +				       char *buf);

Should be /sys/module/xxx/version already.

> +
> +#if REGISTER_DEBUG
> +static ssize_t rmi_driver_reg_store(struct device *dev,
> +					struct device_attribute *attr,
> +
					const char *buf, size_t count);
debugfs


> +#endif
> +
> +#if DELAY_DEBUG
> +static ssize_t rmi_delay_show(struct device *dev,
> +				       struct device_attribute *attr,
> +				       char *buf);
> +
> +static ssize_t rmi_delay_store(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t count);

debugfs

> +#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(hasbsr, RMI_RO_ATTR,
> +	       rmi_driver_hasbsr_show, rmi_store_error),
> +	__ATTR(bsr, RMI_RW_ATTR,
> +	       rmi_driver_bsr_show, rmi_driver_bsr_store),
> +	__ATTR(enabled, RMI_RW_ATTR,
> +	       rmi_driver_enabled_show, rmi_driver_enabled_store),
> +	__ATTR(phys, RMI_RO_ATTR,
> +	       rmi_driver_phys_show, rmi_store_error),
> +#if REGISTER_DEBUG
> +	__ATTR(reg, RMI_WO_ATTR,
> +	       rmi_show_error, rmi_driver_reg_store),
> +#endif
> +#if DELAY_DEBUG
> +	__ATTR(delay, RMI_RW_ATTR,
> +	       rmi_delay_show, rmi_delay_store),
> +#endif
> +	__ATTR(version, RMI_RO_ATTR,
> +	       rmi_driver_version_show, rmi_store_error),
> +};
> +
> +/* Useful helper functions for u8* */

No, they are not. We have *_bit and bitmaps infrastructure in place
already.

> +
> +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];
> +}
> +
> +static bool has_bsr(struct rmi_driver_data *data)
> +{
> +	return (data->pdt_props & HAS_BSR_MASK) != 0;
> +}
> +
> +/* Utility routine to set bits in a register. */
> +int rmi_set_bits(struct rmi_device *rmi_dev, unsigned short address,
> +		 unsigned char bits)
> +{
> +	unsigned char reg_contents;
> +	int retval;
> +
> +	retval = rmi_read_block(rmi_dev, address, &reg_contents, 1);
> +	if (retval)
> +		return retval;
> +	reg_contents = reg_contents | bits;
> +	retval = rmi_write_block(rmi_dev, address, &reg_contents, 1);
> +	if (retval == 1)
> +		return 0;
> +	else if (retval == 0)
> +		return -EIO;
> +	return retval;
> +}
> +EXPORT_SYMBOL(rmi_set_bits);
> +
> +/* Utility routine to clear bits in a register. */
> +int rmi_clear_bits(struct rmi_device *rmi_dev, unsigned short address,
> +		   unsigned char bits)
> +{
> +	unsigned char reg_contents;
> +	int retval;
> +
> +	retval = rmi_read_block(rmi_dev, address, &reg_contents, 1);
> +	if (retval)
> +		return retval;
> +	reg_contents = reg_contents & ~bits;
> +	retval = rmi_write_block(rmi_dev, address, &reg_contents, 1);
> +	if (retval == 1)
> +		return 0;
> +	else if (retval == 0)
> +		return -EIO;
> +	return retval;
> +}
> +EXPORT_SYMBOL(rmi_clear_bits);
> +
> +static void rmi_free_function_list(struct rmi_device *rmi_dev)
> +{
> +	struct rmi_function_container *entry, *n;
> +	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
> +
> +	if (!data) {
> +		dev_err(&rmi_dev->dev, "WTF: No driver data in %s\n", __func__);
> +		return;
> +	}
> +
> +	if (list_empty(&data->rmi_functions.list))
> +		return;
> +
> +	list_for_each_entry_safe(entry, n, &data->rmi_functions.list, list) {
> +		kfree(entry->irq_mask);
> +		list_del(&entry->list);
> +	}
> +}
> +
> +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, "%s: Register F%02X.\n", __func__,
> +			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;
> +	}
> +
> +	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;
> +				init_one_function(rmi_dev, entry);
> +			}
> +		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);
> +
> +	list_for_each_entry_safe(entry, temp, &data->rmi_functions.list,
> +									list) {
> +		if (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 error;
> +
> +	/* Device control (F01) is handled before anything else. */
> +
> +	if (data->f01_container->fh->reset) {
> +		error = data->f01_container->fh->reset(data->f01_container);
> +		if (error < 0) {
> +			dev_err(dev, "%s: f%.2x"
> +					" reset handler failed:"
> +					" %d\n", __func__,
> +					data->f01_container->fh->func, error);
> +			return error;
> +		}
> +	}
> +
> +	list_for_each_entry(entry, &data->rmi_functions.list, list) {
> +		if (entry->fh->reset) {
> +			error = entry->fh->reset(entry);
> +			if (error < 0) {
> +				dev_err(dev, "%s: f%.2x"
> +						" reset handler failed:"
> +						" %d\n", __func__,
> +						entry->fh->func, error);
> +				return error;
> +			}
> +		}
> +
> +	}
> +
> +	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 error;
> +
> +	/* Device control (F01) is handled before anything else. */
> +
> +	if (data->f01_container->fh->config) {
> +		error = data->f01_container->fh->config(data->f01_container);
> +		if (error < 0) {
> +			dev_err(dev, "%s: f%.2x"
> +					" config handler failed:"
> +					" %d\n", __func__,
> +					data->f01_container->fh->func, error);
> +			return error;
> +		}
> +	}
> +
> +	list_for_each_entry(entry, &data->rmi_functions.list, list) {
> +		if (entry->fh->config) {
> +			error = entry->fh->config(entry);
> +			if (error < 0) {
> +				dev_err(dev, "%s: f%.2x"
> +						" config handler failed:"
> +						" %d\n", __func__,
> +						entry->fh->func, error);
> +				return error;
> +			}
> +		}
> +	}
> +
> +	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, "%s: failed to read irqs.", __func__);
> +		return error;
> +	}
> +	/* Device control (F01) is handled before anything else. */
> +	u8_and(irq_bits, irq_status, data->f01_container->irq_mask,
> +			data->num_of_irq_regs);
> +	if (u8_is_any_set(irq_bits, data->num_of_irq_regs))
> +		data->f01_container->fh->attention(
> +				data->f01_container, irq_bits);
> +
> +	u8_and(irq_status, irq_status, data->current_irq_mask,
> +	       data->num_of_irq_regs);
> +	/* At this point, irq_status has all bits that are set in the
> +	 * interrupt status register and are enabled.
> +	 */
> +
> +	list_for_each_entry(entry, &data->rmi_functions.list, list)
> +		if (entry->irq_mask && entry->fh && entry->fh->attention) {
> +			u8_and(irq_bits, irq_status, entry->irq_mask,
> +			       data->num_of_irq_regs);
> +			if (u8_is_any_set(irq_bits, data->num_of_irq_regs)) {
> +				error = entry->fh->attention(entry, irq_bits);
> +				if (error < 0)
> +					dev_err(dev, "%s: f%.2x"
> +						" attention handler failed:"
> +						" %d\n", __func__,
> +						entry->fh->func, error);
> +			}
> +		}
> +
> +	return 0;
> +}
> +
> +static int rmi_driver_irq_handler(struct rmi_device *rmi_dev, int irq)
> +{
> +	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
> +
> +	/* Can get called before the driver is fully ready to deal with
> +	 * interrupts.
> +	 */
> +	if (!data || !data->f01_container || !data->f01_container->fh) {
> +		dev_warn(&rmi_dev->dev,
> +			 "Not ready to handle interrupts yet!\n");
> +		return 0;
> +	}
> +
> +	return process_interrupt_requests(rmi_dev);
> +}
> +
> +
> +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;
> +	}
> +
> +	dev_err(&rmi_dev->dev, "DEBUG 5!\n");
> +
> +	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 = kzalloc(sizeof(u8)*data->num_of_irq_regs, GFP_KERNEL);
> +	if (irq_mask)
> +		construct_mask(irq_mask, fc->num_of_irqs, fc->irq_pos);
> +
> +	return irq_mask;
> +}
> +
> +/*
> + * This pair of functions allows functions like function 54 to request to have
> + * other interupts disabled until the restore function is called. Only one store
> + * happens at a time.
> + */
> +static int rmi_driver_irq_save(struct rmi_device *rmi_dev, u8 * new_ints)
> +{
> +	int retval = 0;
> +	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
> +	struct device *dev = &rmi_dev->dev;
> +
> +	mutex_lock(&data->irq_mutex);
> +	if (!data->irq_stored) {
> +		/* Save current enabled interupts */
> +		retval = rmi_read_block(rmi_dev,
> +				data->f01_container->fd.control_base_addr+1,
> +				data->irq_mask_store, data->num_of_irq_regs);
> +		if (retval < 0) {
> +			dev_err(dev, "%s: Failed to read enabled interrupts!",
> +								__func__);
> +			goto error_unlock;
> +		}
> +		/*
> +		 * Disable every interupt except for function 54
> +		 * TODO:Will also want to not disable function 1-like functions.
> +		 * No need to take care of this now, since there's no good way
> +		 * to identify them.
> +		 */
> +		retval = rmi_write_block(rmi_dev,
> +				data->f01_container->fd.control_base_addr+1,
> +				new_ints, data->num_of_irq_regs);
> +		if (retval < 0) {
> +			dev_err(dev, "%s: Failed to change enabled interrupts!",
> +								__func__);
> +			goto error_unlock;
> +		}
> +		memcpy(data->current_irq_mask, new_ints,
> +					data->num_of_irq_regs * sizeof(u8));
> +		data->irq_stored = true;
> +	} else {
> +		retval = -ENOSPC; /* No space to store IRQs.*/
> +		dev_err(dev, "%s: Attempted to save values when"
> +						" already stored!", __func__);
> +	}
> +
> +error_unlock:
> +	mutex_unlock(&data->irq_mutex);
> +	return retval;
> +}
> +
> +static int rmi_driver_irq_restore(struct rmi_device *rmi_dev)
> +{
> +	int retval = 0;
> +	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
> +	struct device *dev = &rmi_dev->dev;
> +	mutex_lock(&data->irq_mutex);
> +
> +	if (data->irq_stored) {
> +		retval = rmi_write_block(rmi_dev,
> +				data->f01_container->fd.control_base_addr+1,
> +				data->irq_mask_store, data->num_of_irq_regs);
> +		if (retval < 0) {
> +			dev_err(dev, "%s: Failed to write enabled interupts!",
> +								__func__);
> +			goto error_unlock;
> +		}
> +		memcpy(data->current_irq_mask, data->irq_mask_store,
> +					data->num_of_irq_regs * sizeof(u8));
> +		data->irq_stored = false;
> +	} else {
> +		retval = -EINVAL;
> +		dev_err(dev, "%s: Attempted to restore values when not stored!",
> +			__func__);
> +	}
> +
> +error_unlock:
> +	mutex_unlock(&data->irq_mutex);
> +	return retval;
> +}
> +
> +static int rmi_driver_fn_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_info(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);
> +		kfree(fc);
> +		goto error_free_data;
> +	}
> +
> +	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;
> +	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);
> +
> +	dev_info(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, "%s: Register F01.\n", __func__);
> +	retval = device_register(&fc->dev);
> +	if (retval) {
> +		dev_err(dev, "%s: Failed device_register for F01.\n", __func__);
> +		goto error_free_data;
> +	}
> +
> +	data->f01_container = fc;
> +
> +	return retval;
> +
> +error_free_data:
> +	kfree(fc);
> +	return retval;
> +}
> +
> +/*
> + * Scan the PDT for F01 so we can force a reset before anything else
> + * is done.  This forces the sensor into a known state, and also
> + * forces application of any pending updates from reflashing the
> + * firmware or configuration.  We have to do this before actually
> + * building the PDT because the reflash might cause various registers
> + * to move around.
> + */
> +static int do_initial_reset(struct rmi_device *rmi_dev)
> +{
> +	struct pdt_entry pdt_entry;
> +	int page;
> +	struct device *dev = &rmi_dev->dev;
> +	bool done = false;
> +	int i;
> +	int retval;
> +	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);
> +				return 0;
> +			}
> +		}
> +	}
> +
> +	dev_warn(dev, "WARNING: Failed to find F01 for initial reset.\n");
> +	return -ENODEV;
> +}
> +
> +static int rmi_scan_pdt(struct rmi_device *rmi_dev)
> +{
> +	struct rmi_driver_data *data;
> +	struct pdt_entry pdt_entry;
> +	int page;
> +	struct device *dev = &rmi_dev->dev;
> +	int irq_count = 0;
> +	bool done = false;
> +	int i;
> +	int retval;
> +
> +	dev_info(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;
> +		}
> +	}
> +	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 error = 0;
> +	struct device *dev = &rmi_dev->dev;
> +	int attr_count = 0;
> +
> +	dev_dbg(dev, "%s: Starting probe.\n", __func__);
> +
> +	pdata = to_rmi_platform_data(rmi_dev);
> +
> +	data = kzalloc(sizeof(struct rmi_driver_data), GFP_KERNEL);
> +	if (!data) {
> +		dev_err(dev, "%s: Failed to allocate driver data.\n", __func__);
> +		return -ENOMEM;
> +	}
> +	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;
> +	error = do_initial_reset(rmi_dev);
> +	if (error)
> +		dev_warn(dev, "RMI initial reset failed! Soldiering on.\n");
> +
> +
> +	error = rmi_scan_pdt(rmi_dev);
> +	if (error) {
> +		dev_err(dev, "PDT scan for %s failed with code %d.\n",
> +			pdata->sensor_name, error);
> +		goto err_free_data;
> +	}
> +
> +	if (!data->f01_container) {
> +		dev_err(dev, "missing F01 container!\n");
> +		error = -EINVAL;
> +		goto err_free_data;
> +	}
> +
> +	data->f01_container->irq_mask = kzalloc(
> +			sizeof(u8)*data->num_of_irq_regs, GFP_KERNEL);
> +	if (!data->f01_container->irq_mask) {
> +		dev_err(dev, "Failed to allocate F01 IRQ mask.\n");
> +		error = -ENOMEM;
> +		goto err_free_data;
> +	}
> +	construct_mask(data->f01_container->irq_mask,
> +		       data->f01_container->num_of_irqs,
> +		       data->f01_container->irq_pos);
> +	list_for_each_entry(fc, &data->rmi_functions.list, list)
> +		fc->irq_mask = rmi_driver_irq_get_mask(rmi_dev, fc);
> +
> +	error = rmi_driver_f01_init(rmi_dev);
> +	if (error < 0) {
> +		dev_err(dev, "Failed to initialize F01.\n");
> +		goto err_free_data;
> +	}
> +
> +	error = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION,
> +			 (char *) &data->pdt_props);
> +	if (error < 0) {
> +		/* we'll print out a warning and continue since
> +		 * failure to get the PDT properties is not a cause to fail
> +		 */
> +		dev_warn(dev, "Could not read PDT properties from 0x%04x. "
> +			 "Assuming 0x00.\n", PDT_PROPERTIES_LOCATION);
> +	}
> +
> +	dev_dbg(dev, "%s: Creating sysfs files.", __func__);
> +	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
> +		error = device_create_file(dev, &attrs[attr_count]);
> +		if (error < 0) {
> +			dev_err(dev, "%s: Failed to create sysfs file %s.\n",
> +				__func__, attrs[attr_count].attr.name);
> +			goto err_free_data;
> +		}
> +	}

Use attribute group or driver infrastructure for registering common
attributes.

> +
> +	__mutex_init(&data->irq_mutex, "irq_mutex", &data->irq_key);

Why not standard mutex_init()?


> +	data->current_irq_mask = kzalloc(sizeof(u8)*data->num_of_irq_regs,

Spaces around arithmetic and other operations are appreciated.

> +					 GFP_KERNEL);
> +	if (!data->current_irq_mask) {
> +		dev_err(dev, "Failed to allocate current_irq_mask.\n");
> +		error = -ENOMEM;
> +		goto err_free_data;
> +	}
> +	error = rmi_read_block(rmi_dev,
> +				data->f01_container->fd.control_base_addr+1,
> +				data->current_irq_mask, data->num_of_irq_regs);
> +	if (error < 0) {
> +		dev_err(dev, "%s: Failed to read current IRQ mask.\n",
> +			__func__);
> +		goto err_free_data;
> +	}
> +	data->irq_mask_store = kzalloc(sizeof(u8)*data->num_of_irq_regs,
> +				       GFP_KERNEL);
> +	if (!data->irq_mask_store) {
> +		dev_err(dev, "Failed to allocate mask store.\n");
> +		error = -ENOMEM;
> +		goto err_free_data;
> +	}
> +
> +#ifdef	CONFIG_PM
> +	data->pm_data = pdata->pm_data;
> +	data->pre_suspend = pdata->pre_suspend;
> +	data->post_resume = pdata->post_resume;

Is it really platform dependent?

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

Not in mainline.

> +#endif /* CONFIG_HAS_EARLYSUSPEND */
> +#endif /* CONFIG_PM */
> +	data->enabled = true;
> +
> +	dev_info(dev, "connected RMI device manufacturer: %s product: %s\n",
> +		 data->manufacturer_id == 1 ? "synaptics" : "unknown",
> +		 data->product_id);
> +
> +	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) {

You exit earlier if data is NULL.

> +		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 error;
> +}
> +
> +#ifdef CONFIG_PM
> +static int rmi_driver_suspend(struct device *dev)
> +{
> +	struct rmi_device *rmi_dev;
> +	struct rmi_driver_data *data;
> +	struct rmi_function_container *entry;
> +	int retval = 0;
> +
> +	rmi_dev = to_rmi_device(dev);
> +	data = rmi_get_driverdata(rmi_dev);
> +
> +	mutex_lock(&data->suspend_mutex);
> +	if (data->suspended)
> +		goto exit;
> +
> +#ifndef	CONFIG_HAS_EARLYSUSPEND
> +	if (data->pre_suspend) {
> +		retval = data->pre_suspend(data->pm_data);
> +		if (retval)
> +			goto exit;
> +	}
> +#endif  /* !CONFIG_HAS_EARLYSUSPEND */
> +
> +	list_for_each_entry(entry, &data->rmi_functions.list, list)
> +		if (entry->fh && entry->fh->suspend) {
> +			retval = entry->fh->suspend(entry);
> +			if (retval < 0)
> +				goto exit;
> +		}
> +
> +	if (data->f01_container && data->f01_container->fh
> +				&& data->f01_container->fh->suspend) {
> +		retval = data->f01_container->fh->suspend(data->f01_container);
> +		if (retval < 0)
> +			goto exit;
> +	}
> +	data->suspended = true;
> +
> +exit:
> +	mutex_unlock(&data->suspend_mutex);
> +	return retval;

Once it is better integrated in driver core this will be much simpler.

> +}
> +
> +static int rmi_driver_resume(struct device *dev)
> +{
> +	struct rmi_device *rmi_dev;
> +	struct rmi_driver_data *data;
> +	struct rmi_function_container *entry;
> +	int retval = 0;
> +
> +	rmi_dev = to_rmi_device(dev);
> +	data = rmi_get_driverdata(rmi_dev);
> +
> +	mutex_lock(&data->suspend_mutex);
> +	if (!data->suspended)
> +		goto exit;
> +
> +	if (data->f01_container && data->f01_container->fh
> +				&& data->f01_container->fh->resume) {
> +		retval = data->f01_container->fh->resume(data->f01_container);
> +		if (retval < 0)
> +			goto exit;
> +	}
> +
> +	list_for_each_entry(entry, &data->rmi_functions.list, list)
> +		if (entry->fh && entry->fh->resume) {
> +			retval = entry->fh->resume(entry);
> +			if (retval < 0)
> +				goto exit;
> +		}
> +
> +#ifndef	CONFIG_HAS_EARLYSUSPEND
> +	if (data->post_resume) {
> +		retval = data->post_resume(data->pm_data);
> +		if (retval)
> +			goto exit;
> +	}
> +#endif  /* !CONFIG_HAS_EARLYSUSPEND */
> +
> +	data->suspended = false;
> +
> +exit:
> +	mutex_unlock(&data->suspend_mutex);
> +	return retval;

This one too.

> +}
> +
> +#ifdef CONFIG_HAS_EARLYSUSPEND
> +static void rmi_driver_early_suspend(struct early_suspend *h)
> +{
> +	struct rmi_device *rmi_dev =
> +	    container_of(h, struct rmi_device, early_suspend_handler);
> +	struct rmi_driver_data *data;
> +	struct rmi_function_container *entry;
> +	int retval = 0;
> +
> +	data = rmi_get_driverdata(rmi_dev);
> +	dev_dbg(&rmi_dev->dev, "Early suspend.\n");
> +
> +	mutex_lock(&data->suspend_mutex);
> +	if (data->suspended)
> +		goto exit;
> +
> +	if (data->pre_suspend) {
> +		retval = data->pre_suspend(data->pm_data);
> +		if (retval)
> +			goto exit;
> +	}
> +
> +	list_for_each_entry(entry, &data->rmi_functions.list, list)
> +		if (entry->fh && entry->fh->early_suspend) {
> +			retval = entry->fh->early_suspend(entry);
> +			if (retval < 0)
> +				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)
> +			goto exit;
> +	}
> +	data->suspended = true;
> +
> +exit:
> +	mutex_unlock(&data->suspend_mutex);
> +}
> +
> +static void rmi_driver_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);
> +	dev_dbg(&rmi_dev->dev, "Late resume.\n");
> +
> +	mutex_lock(&data->suspend_mutex);
> +	if (!data->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)
> +			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)
> +				goto exit;
> +		}
> +
> +	if (data->post_resume) {
> +		retval = data->post_resume(data->pm_data);
> +		if (retval)
> +			goto exit;
> +	}
> +
> +	data->suspended = false;
> +
> +exit:
> +	mutex_unlock(&data->suspend_mutex);
> +}
> +#endif /* CONFIG_HAS_EARLYSUSPEND */
> +#endif /* CONFIG_PM */
> +
> +static int __devexit rmi_driver_remove(struct rmi_device *rmi_dev)
> +{
> +	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
> +	struct rmi_function_container *entry;
> +	int i;
> +
> +	list_for_each_entry(entry, &data->rmi_functions.list, list)
> +		if (entry->fh && entry->fh->remove)
> +			entry->fh->remove(entry);
> +
> +	rmi_free_function_list(rmi_dev);
> +	for (i = 0; i < ARRAY_SIZE(attrs); i++)
> +		device_remove_file(&rmi_dev->dev, &attrs[i]);
> +	kfree(data->f01_container->irq_mask);
> +	kfree(data->irq_mask_store);
> +	kfree(data->current_irq_mask);
> +	kfree(data);
> +
> +	return 0;
> +}
> +
> +#ifdef UNIVERSAL_DEV_PM_OPS
> +static UNIVERSAL_DEV_PM_OPS(rmi_driver_pm, rmi_driver_suspend,
> +			    rmi_driver_resume, NULL);
> +#endif
> +
> +static struct rmi_driver sensor_driver = {
> +	.driver = {
> +		.owner = THIS_MODULE,
> +		.name = "rmi-generic",
> +#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_hasbsr_show(struct device *dev,
> +				      struct device_attribute *attr,
> +				      char *buf)
> +{
> +	struct rmi_device *rmi_dev;
> +	struct rmi_driver_data *data;
> +	rmi_dev = to_rmi_device(dev);
> +	data = rmi_get_driverdata(rmi_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n", has_bsr(data));
> +}
> +
> +static ssize_t rmi_driver_bsr_show(struct device *dev,
> +				   struct device_attribute *attr, char *buf)
> +{
> +	struct rmi_device *rmi_dev;
> +	struct rmi_driver_data *data;
> +	rmi_dev = to_rmi_device(dev);
> +	data = rmi_get_driverdata(rmi_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n", data->bsr);
> +}
> +
> +static ssize_t rmi_driver_bsr_store(struct device *dev,
> +				    struct device_attribute *attr,
> +				    const char *buf, size_t count)
> +{
> +	int retval;
> +	unsigned long val;
> +	struct rmi_device *rmi_dev;
> +	struct rmi_driver_data *data;
> +
> +	rmi_dev = to_rmi_device(dev);
> +	data = rmi_get_driverdata(rmi_dev);
> +
> +	/* need to convert the string data to an actual value */
> +	retval = strict_strtoul(buf, 10, &val);
> +	if (retval < 0) {
> +		dev_err(dev, "Invalid value '%s' written to BSR.\n", buf);
> +		return -EINVAL;
> +	}
> +
> +	retval = rmi_write(rmi_dev, BSR_LOCATION, (unsigned char)val);
> +	if (retval) {
> +		dev_err(dev, "%s : failed to write bsr %u to 0x%x\n",
> +			__func__, (unsigned int)val, BSR_LOCATION);
> +		return retval;
> +	}
> +
> +	data->bsr = val;
> +
> +	return count;
> +}
> +
> +static void disable_sensor(struct rmi_device *rmi_dev)
> +{
> +	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
> +
> +	rmi_dev->phys->disable_device(rmi_dev->phys);
> +
> +	data->enabled = false;
> +}
> +
> +static int enable_sensor(struct rmi_device *rmi_dev)
> +{
> +	struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
> +	int retval = 0;
> +
> +	retval = rmi_dev->phys->enable_device(rmi_dev->phys);
> +	/* non-zero means error occurred */
> +	if (retval)
> +		return retval;
> +
> +	data->enabled = true;
> +
> +	return 0;
> +}
> +
> +static ssize_t rmi_driver_enabled_show(struct device *dev,
> +				       struct device_attribute *attr, char *buf)
> +{
> +	struct rmi_device *rmi_dev;
> +	struct rmi_driver_data *data;
> +
> +	rmi_dev = to_rmi_device(dev);
> +	data = rmi_get_driverdata(rmi_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n", data->enabled);
> +}
> +
> +static ssize_t rmi_driver_enabled_store(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t count)
> +{
> +	int retval;
> +	int new_value;
> +	struct rmi_device *rmi_dev;
> +	struct rmi_driver_data *data;
> +
> +	rmi_dev = to_rmi_device(dev);
> +	data = rmi_get_driverdata(rmi_dev);
> +
> +	if (sysfs_streq(buf, "0"))
> +		new_value = false;
> +	else if (sysfs_streq(buf, "1"))
> +		new_value = true;
> +	else
> +		return -EINVAL;
> +
> +	if (new_value) {
> +		retval = enable_sensor(rmi_dev);
> +		if (retval) {
> +			dev_err(dev, "Failed to enable sensor, code=%d.\n",
> +				retval);
> +			return -EIO;
> +		}
> +	} else {
> +		disable_sensor(rmi_dev);
> +	}
> +
> +	return count;
> +}
> +
> +#if REGISTER_DEBUG
> +static ssize_t rmi_driver_reg_store(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t count)
> +{
> +	int retval;
> +	unsigned int address;
> +	unsigned int bytes;
> +	struct rmi_device *rmi_dev;
> +	struct rmi_driver_data *data;
> +	u8 readbuf[128];
> +	unsigned char outbuf[512];
> +	unsigned char *bufptr = outbuf;
> +	int i;
> +
> +	rmi_dev = to_rmi_device(dev);
> +	data = rmi_get_driverdata(rmi_dev);
> +
> +	retval = sscanf(buf, "%x %u", &address, &bytes);
> +	if (retval != 2) {
> +		dev_err(dev, "Invalid input (code %d) for reg store: %s",
> +			retval, buf);
> +		return -EINVAL;
> +	}
> +	if (address < 0 || address > 0xFFFF) {
> +		dev_err(dev, "Invalid address for reg store '%#06x'.\n",
> +			address);
> +		return -EINVAL;
> +	}
> +	if (bytes < 0 || bytes >= sizeof(readbuf) || address+bytes > 65535) {
> +		dev_err(dev, "Invalid byte count for reg store '%d'.\n",
> +			bytes);
> +		return -EINVAL;
> +	}
> +
> +	retval = rmi_read_block(rmi_dev, address, readbuf, bytes);
> +	if (retval != bytes) {
> +		dev_err(dev, "Failed to read %d registers at %#06x, code %d.\n",
> +			bytes, address, retval);
> +		return retval;
> +	}
> +
> +	dev_info(dev, "Reading %d bytes from %#06x.\n", bytes, address);
> +	for (i = 0; i < bytes; i++) {
> +		retval = snprintf(bufptr, 4, "%02X ", readbuf[i]);
> +		if (retval < 0) {
> +			dev_err(dev, "Failed to format string. Code: %d",
> +				retval);
> +			return retval;
> +		}
> +		bufptr += retval;
> +	}
> +	dev_info(dev, "%s\n", outbuf);
> +
> +	return count;
> +}
> +#endif
> +
> +#if DELAY_DEBUG
> +static ssize_t rmi_delay_store(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t count)
> +{
> +	int retval;
> +	struct rmi_device *rmi_dev;
> +	struct rmi_device_platform_data *pdata;
> +	unsigned int new_read_delay;
> +	unsigned int new_write_delay;
> +	unsigned int new_block_delay;
> +	unsigned int new_pre_delay;
> +	unsigned int new_post_delay;
> +
> +	retval = sscanf(buf, "%u %u %u %u %u", &new_read_delay,
> +			&new_write_delay, &new_block_delay,
> +			&new_pre_delay, &new_post_delay);
> +	if (retval != 5) {
> +		dev_err(dev, "Incorrect number of values provided for delay.");
> +		return -EINVAL;
> +	}
> +	if (new_read_delay < 0) {
> +		dev_err(dev, "Byte delay must be positive microseconds.\n");
> +		return -EINVAL;
> +	}
> +	if (new_write_delay < 0) {
> +		dev_err(dev, "Write delay must be positive microseconds.\n");
> +		return -EINVAL;
> +	}
> +	if (new_block_delay < 0) {
> +		dev_err(dev, "Block delay must be positive microseconds.\n");
> +		return -EINVAL;
> +	}
> +	if (new_pre_delay < 0) {
> +		dev_err(dev,
> +			"Pre-transfer delay must be positive microseconds.\n");
> +		return -EINVAL;
> +	}
> +	if (new_post_delay < 0) {
> +		dev_err(dev,
> +			"Post-transfer delay must be positive microseconds.\n");
> +		return -EINVAL;
> +	}
> +
> +	rmi_dev = to_rmi_device(dev);
> +	pdata = rmi_dev->phys->dev->platform_data;
> +
> +	dev_info(dev, "Setting delays to %u %u %u %u %u.\n", new_read_delay,
> +		 new_write_delay, new_block_delay, new_pre_delay,
> +		 new_post_delay);
> +	pdata->spi_data.read_delay_us = new_read_delay;
> +	pdata->spi_data.write_delay_us = new_write_delay;
> +	pdata->spi_data.block_delay_us = new_block_delay;
> +	pdata->spi_data.pre_delay_us = new_pre_delay;
> +	pdata->spi_data.post_delay_us = new_post_delay;
> +
> +	return count;
> +}
> +
> +static ssize_t rmi_delay_show(struct device *dev,
> +				       struct device_attribute *attr, char *buf)
> +{
> +	struct rmi_device *rmi_dev;
> +	struct rmi_device_platform_data *pdata;
> +
> +	rmi_dev = to_rmi_device(dev);
> +	pdata = rmi_dev->phys->dev->platform_data;
> +
> +	return snprintf(buf, PAGE_SIZE, "%d %d %d %d %d\n",
> +		pdata->spi_data.read_delay_us, pdata->spi_data.write_delay_us,
> +		pdata->spi_data.block_delay_us,
> +		pdata->spi_data.pre_delay_us, pdata->spi_data.post_delay_us);

This violates "one value per attribute" principle. Also it does not look
like it is essential for device operation but rather diagnostic
facility. Switch to debugfs?

> +}
> +#endif
> +
> +static ssize_t rmi_driver_phys_show(struct device *dev,
> +				       struct device_attribute *attr, char *buf)
> +{
> +	struct rmi_device *rmi_dev;
> +	struct rmi_phys_info *info;
> +
> +	rmi_dev = to_rmi_device(dev);
> +	info = &rmi_dev->phys->info;
> +
> +	return snprintf(buf, PAGE_SIZE, "%-5s %ld %ld %ld %ld %ld %ld %ld\n",
> +		 info->proto ? info->proto : "unk",
> +		 info->tx_count, info->tx_bytes, info->tx_errs,
> +		 info->rx_count, info->rx_bytes, info->rx_errs,
> +		 info->attn_count);

Same as delay.

> +}
> +
> +static ssize_t rmi_driver_version_show(struct device *dev,
> +				       struct device_attribute *attr, char *buf)
> +{
> +	return snprintf(buf, PAGE_SIZE, "%s\n",
> +		 RMI_DRIVER_VERSION_STRING);
> +}
> +
> +static int __init rmi_driver_init(void)
> +{
> +	return rmi_register_driver(&sensor_driver);
> +}
> +
> +static void __exit rmi_driver_exit(void)
> +{
> +	rmi_unregister_driver(&sensor_driver);
> +}
> +
> +module_init(rmi_driver_init);
> +module_exit(rmi_driver_exit);
> +
> +MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com");
> +MODULE_DESCRIPTION("RMI generic driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
> new file mode 100644
> index 0000000..dc0d9c5
> --- /dev/null
> +++ b/drivers/input/rmi4/rmi_driver.h
> @@ -0,0 +1,97 @@
> +/*
> + * Copyright (c) 2011 Synaptics Incorporated
> + * Copyright (c) 2011 Unixphere
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +#ifndef _RMI_DRIVER_H
> +#define _RMI_DRIVER_H
> +
> +#define RMI_DRIVER_MAJOR_VERSION     1
> +#define RMI_DRIVER_MINOR_VERSION     3
> +#define RMI_DRIVER_SUB_MINOR_VERSION 0
> +
> +/* TODO: Figure out some way to construct this string in the define macro
> + * using the values defined above.
> + */
> +#define RMI_DRIVER_VERSION_STRING "1.3.0"
> +
> +
> +#define RMI_PRODUCT_ID_LENGTH    10
> +#define RMI_PRODUCT_INFO_LENGTH   2
> +#define RMI_DATE_CODE_LENGTH      3
> +
> +struct rmi_driver_data {
> +	struct rmi_function_container rmi_functions;
> +
> +	struct rmi_function_container *f01_container;
> +
> +	int num_of_irq_regs;
> +	u8 *current_irq_mask;
> +	u8 *irq_mask_store;
> +	bool irq_stored;
> +	struct mutex irq_mutex;
> +	struct lock_class_key irq_key;
> +	struct mutex pdt_mutex;
> +
> +	unsigned char pdt_props;
> +	unsigned char bsr;
> +	bool enabled;
> +
> +	u8 manufacturer_id;
> +	/* product id + null termination */
> +	u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
> +
> +#ifdef CONFIG_PM
> +	bool suspended;
> +	struct mutex suspend_mutex;
> +
> +	void *pm_data;
> +	int (*pre_suspend) (const void *pm_data);
> +	int (*post_resume) (const void *pm_data);
> +#endif
> +
> +	void *data;
> +};
> +
> +struct pdt_entry {
> +	u8 query_base_addr:8;
> +	u8 command_base_addr:8;
> +	u8 control_base_addr:8;
> +	u8 data_base_addr:8;
> +	u8 interrupt_source_count:3;
> +	u8 bits3and4:2;
> +	u8 function_version:2;
> +	u8 bit7:1;
> +	u8 function_number:8;
> +};
> +
> +int rmi_driver_f01_init(struct rmi_device *rmi_dev);
> +
> +static inline void copy_pdt_entry_to_fd(struct pdt_entry *pdt,
> +				 struct rmi_function_descriptor *fd,
> +				 u16 page_start)
> +{
> +	fd->query_base_addr = pdt->query_base_addr + page_start;
> +	fd->command_base_addr = pdt->command_base_addr + page_start;
> +	fd->control_base_addr = pdt->control_base_addr + page_start;
> +	fd->data_base_addr = pdt->data_base_addr + page_start;
> +	fd->function_number = pdt->function_number;
> +	fd->interrupt_source_count = pdt->interrupt_source_count;
> +	fd->function_version = pdt->function_version;
> +}
> +
> +#endif
> +

Thanks.

-- 
Dmitry

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

* Re: [RFC PATCH 10/11] input: RMI4 F19 - capacitive buttons
  2012-01-06  0:05     ` Christopher Heiny
@ 2012-01-06  2:50       ` Dmitry Torokhov
  2012-01-09 21:02         ` Christopher Heiny
  0 siblings, 1 reply; 34+ messages in thread
From: Dmitry Torokhov @ 2012-01-06  2:50 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Jean Delvare, Linux Kernel, Linux Input, Joerie de Gram,
	Linus Walleij, Naveen Kumar Gaddipati, Vivian Ly

On Thu, Jan 05, 2012 at 04:05:43PM -0800, Christopher Heiny wrote:
> On 01/04/2012 11:53 PM, Dmitry Torokhov wrote:
> >Hi Christopher,
> >
> >On Wed, Dec 21, 2011 at 06:10:01PM -0800, Christopher Heiny wrote:
> >>+
> >>+struct f19_0d_control {
> >>+	struct f19_0d_control_0 *general_control;
> >
> >Single instance, does not need to be allocated separately.
> >
> >>+	struct f19_0d_control_1 *button_int_enable;
> >>+	struct f19_0d_control_2 *single_button_participation;
> >>+	struct f19_0d_control_3_4 *sensor_map;
> >
> >This should probably be an array of
> >
> >struct f19_button_ctrl {
> >	struct f19_0d_control_1 int_enable;
> >	struct f19_0d_control_2 participation;
> >	struct f19_0d_control_3_4 sensor_map;
> >};
> >
> >located at the end of f19_0d_control structure so it can be all
> >allocated in one shot.
> 
> We organized it this way because of how the controls are organized
> in the register map: first the interrupt enables for all buttons,
> then the participation for all buttons, and then the sensor map for
> all buttons.  Typical client interactions are to adjust these in an
> "all at once" approach - that is, change as a single group all the
> interrupt enables, all the participation settings, or all the sensor
> map.  By organizing them the way we did, it makes it very easy to
> read/write the data to the RMI4 sensor's register map.  Using an
> array of structs would require a building buffers (on write) or
> tedious extraction from buffers (on read).
> 
> However, the first two of these are bitmasks, and don't really need
> their own structs - they can conveniently be u8 * instead.

I'll try coding something to show that it is not as bad as it seems...

> 
> 
> >>+	struct f19_0d_control_5 *all_button_sensitivity_adj;
> >
> >Single instance, does not need to be allocated separately.
> 
> Agreed.
> 
> >
> >>+	struct f19_0d_control_6 *all_button_hysteresis_threshold;
> >
> >Single instance, does not need to be allocated separately.
> >
> >>+};
> >>+/* data specific to fn $19 that needs to be kept around */
> >>+struct f19_data {
> >>+	struct f19_0d_control *button_control;
> >>+	struct f19_0d_query button_query;
> >>+	u8 button_rezero;
> >>+	bool *button_down;
> >
> >Just let input core track this for you.
> 
> That would work.  We were trying to be efficient by only generating
> events of interest, but I'm quite happy to let another subsystem do
> the heavy lifting for us. We'll follow your later recommendation and
> just pass button state info along.
> 
> 
> >>+	unsigned char button_count;
> >>+	unsigned char button_data_buffer_size;
> >
> >Your fingers must be hurting by the time you finished writing this
> >driver...
> 
> I'm a little obsessive about descriptive variable names.  As a
> result, I have digits the size of Arnold Schwarzenegger's biceps.
> :-)

;)

> >>+
> >>+static struct device_attribute attrs[] = {
> >>+	__ATTR(button_count, RMI_RO_ATTR,
> >>+		rmi_f19_button_count_show, rmi_store_error),
> >
> >Why not NULL instead of rmi_store_error?
> 
> We found that customers picking up our driver would try to write RO
> sysfs attributes (or read WO attrs) by invoking echo from the
> command line.  The operation would fail silently (I'm looking at
> you, Android shell), leaving the engineer baffled as to why the
> sensor behavior didn't change.  So we adopted this approach so as to
> give some clue as to the fact that the operation failed.

But this ends up in various logs (dmesg, /var/log/messages, etc) making
it potentially DOS scenario. Please help fixing tools instead.

> >>+
> >>+	f19->button_control->single_button_participation =
> >>+		kzalloc(f19->button_data_buffer_size *
> >>+			sizeof(struct f19_0d_control_2), GFP_KERNEL);
> >>+	if (!f19->button_control->single_button_participation) {
> >>+		dev_err(&fc->dev, "Failed to allocate"
> >>+			" f19_0d_control_2.\n");
> >
> >Do not split error messages; it's OK if they exceed 80 column limit.
> 
> We have one customer who refuses to accept any code if any line
> exceeds 80 columns, so we wind up with ugliness like this.

This is contrary to current kernel policy though
(Documentation/CodingStyle, ch 2):

"However, never break user-visible strings such as
printk messages, because that breaks the ability to grep for them."

> >>+
> >>+static inline void rmi_f19_free_memory(struct rmi_function_container *fc)
> >>+{
> >>+	struct f19_data *f19 = fc->data;
> >>+
> >>+	if (f19) {
> >
> >Can it be NULL? How?
> 
> This is just defensive, in case someone calls this at the wrong
> place or time.

Just make sure you call it at right time ;)

> >>+
> >>+	/* Generate events for buttons that change state. */
> >>+	for (button = 0; button<  f19->button_count;
> >>+	     button++) {
> >>+		int button_reg;
> >>+		int button_shift;
> >>+		bool button_status;
> >>+
> >>+		/* determine which data byte the button status is in */
> >>+		button_reg = button / 7;
> >>+		/* bit shift to get button's status */
> >>+		button_shift = button % 8;
> >>+		button_status =
> >>+		    ((f19->button_data_buffer[button_reg]>>  button_shift)
> >>+			&  0x01) != 0;
> >>+
> >>+		/* if the button state changed from the last time report it
> >>+		 * and store the new state */
> >>+		if (button_status != f19->button_down[button]) {
> >>+			dev_dbg(&fc->dev, "%s: Button %d (code %d) ->  %d.\n",
> >>+				__func__, button, f19->button_map[button],
> >>+				 button_status);
> >>+			/* Generate an event here. */
> >>+			input_report_key(f19->input, f19->button_map[button],
> >>+					 button_status);
> >>+			f19->button_down[button] = button_status;
> >>+		}
> >>+	}
> >
> >All of the above could be reduced to:
> >
> >	for (button = 0; button<  f19->button_count; button++)
> >		input_report_key(f19->input, f19->button_map[button],
> >				 test_bit(button, f19->button_data_buffer);
> 
> I'd like to, but I'm not sure - can we count on the endian-ness of
> the host processor being the same as the RMI4 register endian-ness?

Hmm, I'd expect RMI transport functions take care of converting into
proper endianness.

> 
> [snip]
> 
> >>+
> >>+static ssize_t rmi_f19_sensor_map_store(struct device *dev,
> >>+					 struct device_attribute *attr,
> >>+					 const char *buf, size_t count)
> >>+{
> >>+	struct rmi_function_container *fc;
> >>+	struct f19_data *data;
> >>+	int sensor_map;
> >>+	int i;
> >>+	int retval = count;
> >>+	int button_count = 0;
> >>+	int ctrl_bass_addr;
> >>+	int button_reg;
> >>+	fc = to_rmi_function_container(dev);
> >>+	data = fc->data;
> >>+
> >>+	if (data->button_query.configurable == 0) {
> >>+		dev_err(dev,
> >>+			"%s: Error - sensor map is not configuralbe at"
> >>+			" run-time", __func__);
> >
> >This is not driver error, return error silently.
> 
> I don't like failing silently, for reasons outlined above.

And for the reason also outlined above I disagree. Driver errors
(especially KERNEL_ERR level) should be used to signal conditions when
driver either can't continue or its functionality is seriously impaired.
Bad user input does not qualify here.

> 
> >
> >>+		return -EINVAL;
> >>+	}
> >
> >If sensor is not cinfigurable maybe attributes mode should be adjusted
> >to be read-only?
> 
> Good point.  Then we could eliminate the dev_err, above.  We'll look
> into this at the same time as the attribute groups.

Thanks.

> 
> [snip]
> 
> >>+
> >>+static ssize_t rmi_f19_button_map_store(struct device *dev,
> >>+				struct device_attribute *attr,
> >>+				const char *buf,
> >>+				size_t count)
> >>+{
> >>+	struct rmi_function_container *fc;
> >>+	struct f19_data *data;
> >>+	unsigned int button;
> >>+	int i;
> >>+	int retval = count;
> >>+	int button_count = 0;
> >>+	unsigned char temp_button_map[KEY_MAX];
> >>+
> >>+	fc = to_rmi_function_container(dev);
> >>+	data = fc->data;
> >>+
> >>+	/* Do validation on the button map data passed in.  Store button
> >>+	 * mappings into a temp buffer and then verify button count and
> >>+	 * data prior to clearing out old button mappings and storing the
> >>+	 * new ones. */
> >>+	for (i = 0; i<  data->button_count&&  *buf != 0;
> >>+	     i++) {
> >>+		/* get next button mapping value and store and bump up to
> >>+		 * point to next item in buf */
> >>+		sscanf(buf, "%u",&button);
> >>+
> >>+		/* Make sure the key is a valid key */
> >>+		if (button>  KEY_MAX) {
> >>+			dev_err(dev,
> >>+				"%s: Error - button map for button %d is not a"
> >>+				" valid value 0x%x.\n", __func__, i, button);
> >>+			retval = -EINVAL;
> >>+			goto err_ret;
> >>+		}
> >>+
> >>+		temp_button_map[i] = button;
> >>+		button_count++;
> >>+
> >>+		/* bump up buf to point to next item to read */
> >>+		while (*buf != 0) {
> >>+			buf++;
> >>+			if (*(buf - 1) == ' ')
> >>+				break;
> >>+		}
> >>+	}
> >>+
> >>+	/* Make sure the button count matches */
> >>+	if (button_count != data->button_count) {
> >>+		dev_err(dev,
> >>+		    "%s: Error - button map count of %d doesn't match device "
> >>+		     "button count of %d.\n", __func__, button_count,
> >>+		     data->button_count);
> >>+		retval = -EINVAL;
> >>+		goto err_ret;
> >>+	}
> >>+
> >>+	/* Clear the key bits for the old button map. */
> >>+	for (i = 0; i<  button_count; i++)
> >>+		clear_bit(data->button_map[i], data->input->keybit);
> >>+
> >>+	/* Switch to the new map. */
> >>+	memcpy(data->button_map, temp_button_map,
> >>+	       data->button_count);
> >>+
> >>+	/* Loop through the key map and set the key bit for the new mapping. */
> >>+	for (i = 0; i<  button_count; i++)
> >>+		set_bit(data->button_map[i], data->input->keybit);
> >>+
> >>+err_ret:
> >>+	return retval;
> >>+}
> >
> >Button map (keymap) should be manipulated with EVIOCGKEYCODE and
> >EVIOCSKEYCODE ioctls, no need to invent driver-specific way of doing
> >this.
> 
> Makes sense, but... we had a customer request to specify the
> boot-time keymap through the RMI4 driver's platform data.  Is it
> legal to call setkeycode() to do that instead?

No, the input device should be fully registered for that... But you can
supply initial keymap in platform data, almost all drivers do that. It
is new sysfs interface I object to here.

>  If so, we'll do that
> and get rid of the button_map entirely.
> 
> Thanks again for the review and input,

Thanks for your patience.

-- 
Dmitry

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

* Re: [RFC PATCH 1/11] input: RMI4 public header file and documentation.
  2011-12-22  2:09 ` [RFC PATCH 1/11] input: RMI4 public header file and documentation Christopher Heiny
@ 2012-01-06  6:35   ` Dmitry Torokhov
  2012-01-09 20:31     ` Christopher Heiny
  0 siblings, 1 reply; 34+ messages in thread
From: Dmitry Torokhov @ 2012-01-06  6:35 UTC (permalink / raw)
  To: Christopher Heiny
  Cc: Jean Delvare, Linux Kernel, Linux Input, Joerie de Gram,
	Linus Walleij, Naveen Kumar Gaddipati

On Wed, Dec 21, 2011 at 06:09:52PM -0800, Christopher Heiny 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.
> + * 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;
> +}

We have nice set of be/le16_to_cpu and cpu_to_be/le16 helpers that do
just that and in much more efficient way.

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

Although these methods are not needed, a general comment nonetheless: do
not put in header files and mark inline functions which address is taken
and used elsewhere.

Thanks.

-- 
Dmitry

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

* Re: [RFC PATCH 2/11] input: RMI4 core bus and sensor drivers.
  2012-01-06  2:34   ` Dmitry Torokhov
@ 2012-01-07  3:26     ` Christopher Heiny
  0 siblings, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2012-01-07  3:26 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Joerie de Gram,
	Linus Walleij, Naveen Kumar Gaddipati

On 01/05/2012 06:34 PM, Dmitry Torokhov wrote:
> Hi Christopher,
>
> Please find my [incomplete] comments below.

And my responses.  Most of them are just ACKing your input, but there's 
a few point of disagreement, which I've called out.

>
> On Wed, Dec 21, 2011 at 06:09:53PM -0800, Christopher Heiny wrote:
>> Signed-off-by: Christopher Heiny<cheiny@synaptics.com>
>>
>> Cc: Dmitry Torokhov<dmitry.torokhov@gmail.com>
>> Cc: Linus Walleij<linus.walleij@stericsson.com>
>> Cc: Naveen Kumar Gaddipati<naveen.gaddipati@stericsson.com>
>> Cc: Joeri de Gram<j.de.gram@gmail.com>
>>
>> ---
>>
>>   drivers/input/rmi4/rmi_bus.c    |  436 ++++++++++++
>>   drivers/input/rmi4/rmi_driver.c | 1488 +++++++++++++++++++++++++++++++++++++++
>>   drivers/input/rmi4/rmi_driver.h |   97 +++
>>   3 files changed, 2021 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c

[snip]

>> +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, "Matching %s.\n", 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_err(dev, "%s: Match %s to %s failed.\n", __func__,
>> +		pdata->driver_name, rmi_driver->driver.name);
>
> Why is this an error? dev_vdbg() at most, better yet just remove it.

It's useful when helping new customers bring up the driver on their 
product.  However, dev_dbg or dev_vdbg is a better choice for 
production, so we'll change to that.

>
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +static int rmi_bus_suspend(struct device *dev)
>> +{
>> +#ifdef GENERIC_SUBSYS_PM_OPS
>> +	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
>> +
>> +	if (pm&&  pm->suspend)
>> +		return pm->suspend(dev);
>> +#endif
>> +
>> +	return 0;
>> +}
>> +
>> +static int rmi_bus_resume(struct device *dev)
>> +{
>> +#ifdef GENERIC_SUBSYS_PM_OPS
>> +	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
>> +
>> +	if (pm&&  pm->resume)
>> +		return pm->resume(dev);
>> +#endif
>> +
>> +	return 0;
>> +}
>> +#endif
>
> These are not needed if you switch to generic_subsys_pm_ops.

Unfortunately, we've got some customers on kernels that don't support 
generic_subsys_pm_ops.  We'll probably drop support for that later this 
year, but in the meantime we'd like to retain it.


[snip]

>> +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;
>> +
>> +	mutex_lock(&rmi_bus_mutex);
>> +	rmi_dev->number = physical_device_count;
>> +	physical_device_count++;
>> +	mutex_unlock(&rmi_bus_mutex);
>
> Do
> 	rmi_dev->number = atomic_inc_return(&rmi_no) - 1;
>
> and  stick "static atomic_t rmi_no = ATOMIC_INIT(0)"; at the beginning
> of the function. Then you don't need to take mutex here. Do you need
> rmi_dev->number?

Yes, it's used elsewhere.  atomic_inc is a tidier way to manage this, so 
we'll switch to that.

>
>> +
>> +	dev_set_name(&rmi_dev->dev, "sensor%02d", rmi_dev->number);
>> +	pr_debug("%s: Registered %s as %s.\n", __func__, pdata->sensor_name,
>> +		dev_name(&rmi_dev->dev));
>> +
>> +	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;
>> +
>> +	device_unregister(&rmi_dev->dev);
>> +	kfree(rmi_dev);
>
> This is lifetime rules violation; rmi_dev->dev might still be referenced
> and you are freeing it. Please provide proper release function.

Agreed.  Thanks for catching this.

>
>> +}
>> +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;
>
> No cleanup whatsoever?

That certainly looks like a bug.

>
>> +
>> +	return 0;
>> +}
>> +
>> +void rmi_unregister_driver(struct rmi_driver *driver)
>> +{
>> +	bus_for_each_dev(&rmi_bus_type, NULL, driver, __rmi_driver_remove);
>
> Why don't you rely on driver core to unbind devices upon driver removal
> instead of rolling your own (and highly likely broken) implementation.

We'll look into that.

>
>> +	driver_unregister(&driver->driver);
>> +}
>> +EXPORT_SYMBOL(rmi_unregister_driver);
>> +
[snip]

>> +
>> +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);
>> +
>> +	list_for_each_entry_safe(entry, n,&rmi_supported_functions.list,
>> +									list) {
>> +		if (entry->fh->func == fh->func) {
>> +			list_del(&entry->list);
>> +			kfree(entry);
>> +		}
>> +	}
>
> You are still rolling partly your own infrastructure. It looks like you
> need 2 types of devices on rmi bus - composite RMI device and sensor
> device, see struct device_type. Make 2 of those and match drivers
> depending on the device type. Then driver core will maintain all the
> lists for you.

Much better.  We'll look into that.

>
>> +
>> +}
>> +EXPORT_SYMBOL(rmi_unregister_function_driver);
>> +
>> +struct rmi_function_handler *rmi_get_function_handler(int id)
>> +{
>> +	struct rmi_function_list *entry;
>> +
>> +	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;
>
> No, no, no. Do not try to shut up warnings from device core; they are
> there for a reason.

I don't think that's what this is trying to do.  But certainly it should 
be doing something other than quietly saying "here".

>
>> +}

[snip]

>> +
>> +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);
>
> Hmm, thisi is very roundabout way of attaching RMI chardevice... Does it
> even work if driver [re]appears after rmi_dev module was loaded?
>
> IFF we agree on keeping rmi_dev interface then I think something more
> elegant could be cooked via bus's blocking notifier.

It does appear to work in that situation, at least on the bench.  We'll 
look into the blocking notifier, though - like you say, better to avoid 
re-inventing the functionality.

[snip]

>> +
>> +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);
>> +
>> +	error = bus_register(&rmi_bus_type);
>> +	if (error<  0) {
>> +		pr_err("%s: error registering the RMI bus: %d\n", __func__,
>> +		       error);
>> +		return error;
>> +	}
>> +	pr_info("%s: successfully registered RMI bus.\n", __func__);
>
> This is all useless noise. Just do:
>
> 	return bus_register(&rmi_bus_type);

Not entirely useless, as it helps customers debug failures when first 
integrating the driver.  However, we'll switch pr_info to pr_debug.

>
>> +
>> +	return 0;
>> +}
>> +
>> +static void __exit rmi_bus_exit(void)
>> +{
>> +	struct rmi_function_list *entry, *n;
>> +
>> +	list_for_each_entry_safe(entry, n,&rmi_supported_functions.list,
>> +				 list) {
>> +		list_del(&entry->list);
>> +		kfree(entry);
>> +	}
>
> How can this list be non-free? Your bus code is reference by function
> drivers so module count is non zero until all such drivers are unloaded,
> and therefore rmi_bus_exit() can not be called.

Agreed.  Will address this.

>
>> +
>> +	bus_unregister(&rmi_bus_type);
>> +}
>> +
>> +module_init(rmi_bus_init);
>> +module_exit(rmi_bus_exit);
>> +
>> +MODULE_AUTHOR("Eric Andersson<eric.andersson@unixphere.com>");
>> +MODULE_DESCRIPTION("RMI bus");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
>> new file mode 100644
>> index 0000000..07097bb
>> --- /dev/null
>> +++ b/drivers/input/rmi4/rmi_driver.c
>> @@ -0,0 +1,1488 @@
>> +/*
>> + * Copyright (c) 2011 Synaptics Incorporated
>> + * Copyright (c) 2011 Unixphere
>> + *
>> + * This driver adds support for generic RMI4 devices from Synpatics. It
>> + * implements the mandatory f01 RMI register and depends on the presence of
>> + * other required RMI functions.
>> + *
>> + * The RMI4 specification can be found here (URL split after files/ for
>> + * style reasons):
>> + * http://www.synaptics.com/sites/default/files/
>> + *           511-000136-01-Rev-E-RMI4%20Intrfacing%20Guide.pdf
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
>> + */
>> +
>> +#include<linux/kernel.h>
>> +#include<linux/delay.h>
>> +#include<linux/device.h>
>> +#include<linux/module.h>
>> +#include<linux/device.h>
>> +#include<linux/slab.h>
>> +#include<linux/list.h>
>> +#include<linux/pm.h>
>> +#include<linux/rmi.h>
>> +#include "rmi_driver.h"
>> +
>> +#define DELAY_DEBUG 0
>> +#define REGISTER_DEBUG 0
>> +
>> +#define PDT_END_SCAN_LOCATION	0x0005
>> +#define PDT_PROPERTIES_LOCATION 0x00EF
>> +#define BSR_LOCATION 0x00FE
>> +#define HAS_BSR_MASK 0x20
>> +#define HAS_NONSTANDARD_PDT_MASK 0x40
>> +#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
>> +#define RMI4_MAX_PAGE 0xff
>> +#define RMI4_PAGE_SIZE 0x100
>> +
>> +#define RMI_DEVICE_RESET_CMD	0x01
>> +#define DEFAULT_RESET_DELAY_MS	20
>> +
>> +#ifdef CONFIG_HAS_EARLYSUSPEND
>> +static void rmi_driver_early_suspend(struct early_suspend *h);
>> +static void rmi_driver_late_resume(struct early_suspend *h);
>> +#endif
>
> Does not appear to be in mainline; please trim.

Understood, but we're trying to support Android kernels without forking 
the heck out of our codebase.

>
>> +
>> +/* sysfs files for attributes for driver values. */
>> +static ssize_t rmi_driver_hasbsr_show(struct device *dev,
>> +				      struct device_attribute *attr, char *buf);
>
> Well, if it does not have bsr why do you even create bsr attribute?

We've been asking that very same question, and plan to change that.

>
>> +
>> +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);
>
> Should rely on load/unload or bind/unbind; no need for yet another
> mechanism.

This is needed as part of the support for the RED/Design Studio tools. 
The variable name is misleading, though - it doesn't enable/disable the 
driver, but enables/disables touch data processing for a single sensor. 
  We'll change the name of this.

>
>> +
>> +static ssize_t rmi_driver_phys_show(struct device *dev,
>> +				       struct device_attribute *attr,
>> +				       char *buf);
>> +
>> +static ssize_t rmi_driver_version_show(struct device *dev,
>> +				       struct device_attribute *attr,
>> +				       char *buf);
>
> Should be /sys/module/xxx/version already.

I don't find that.  I'll look into why not.



>> +
>> +#if REGISTER_DEBUG
>> +static ssize_t rmi_driver_reg_store(struct device *dev,
>> +					struct device_attribute *attr,
>> +
> 					const char *buf, size_t count);
> debugfs

Agree.

>> +#endif
>> +
>> +#if DELAY_DEBUG
>> +static ssize_t rmi_delay_show(struct device *dev,
>> +				       struct device_attribute *attr,
>> +				       char *buf);
>> +
>> +static ssize_t rmi_delay_store(struct device *dev,
>> +					struct device_attribute *attr,
>> +					const char *buf, size_t count);
>
> debugfs

Agree.

[snip]
[snip]

>> +	dev_dbg(dev, "%s: Creating sysfs files.", __func__);
>> +	for (attr_count = 0; attr_count<  ARRAY_SIZE(attrs); attr_count++) {
>> +		error = device_create_file(dev,&attrs[attr_count]);
>> +		if (error<  0) {
>> +			dev_err(dev, "%s: Failed to create sysfs file %s.\n",
>> +				__func__, attrs[attr_count].attr.name);
>> +			goto err_free_data;
>> +		}
>> +	}
>
> Use attribute group or driver infrastructure for registering common
> attributes.

Agree.  We're looking into this.

>
>> +
>> +	__mutex_init(&data->irq_mutex, "irq_mutex",&data->irq_key);
>
> Why not standard mutex_init()?

Not sure.  Will correct this.

>
>
>> +	data->current_irq_mask = kzalloc(sizeof(u8)*data->num_of_irq_regs,
>
> Spaces around arithmetic and other operations are appreciated.

Agree.


>
>> +					 GFP_KERNEL);
>> +	if (!data->current_irq_mask) {
>> +		dev_err(dev, "Failed to allocate current_irq_mask.\n");
>> +		error = -ENOMEM;
>> +		goto err_free_data;
>> +	}
>> +	error = rmi_read_block(rmi_dev,
>> +				data->f01_container->fd.control_base_addr+1,
>> +				data->current_irq_mask, data->num_of_irq_regs);
>> +	if (error<  0) {
>> +		dev_err(dev, "%s: Failed to read current IRQ mask.\n",
>> +			__func__);
>> +		goto err_free_data;
>> +	}
>> +	data->irq_mask_store = kzalloc(sizeof(u8)*data->num_of_irq_regs,
>> +				       GFP_KERNEL);
>> +	if (!data->irq_mask_store) {
>> +		dev_err(dev, "Failed to allocate mask store.\n");
>> +		error = -ENOMEM;
>> +		goto err_free_data;
>> +	}
>> +
>> +#ifdef	CONFIG_PM
>> +	data->pm_data = pdata->pm_data;
>> +	data->pre_suspend = pdata->pre_suspend;
>> +	data->post_resume = pdata->post_resume;
>
> Is it really platform dependent?

Yes.  Some platforms have special things they want to do before/after 
the touchpad suspends/resumes.

>
>> +
>> +	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);
>
> Not in mainline.

Yes, but as noted before we need to support Android without forking our 
driver codebase.

>
>> +#endif /* CONFIG_HAS_EARLYSUSPEND */
>> +#endif /* CONFIG_PM */
>> +	data->enabled = true;
>> +
>> +	dev_info(dev, "connected RMI device manufacturer: %s product: %s\n",
>> +		 data->manufacturer_id == 1 ? "synaptics" : "unknown",
>> +		 data->product_id);
>> +
>> +	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) {
>
> You exit earlier if data is NULL.

Agree.

>
>> +		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 error;
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +static int rmi_driver_suspend(struct device *dev)
>> +{
>> +	struct rmi_device *rmi_dev;
>> +	struct rmi_driver_data *data;
>> +	struct rmi_function_container *entry;
>> +	int retval = 0;
>> +
>> +	rmi_dev = to_rmi_device(dev);
>> +	data = rmi_get_driverdata(rmi_dev);
>> +
>> +	mutex_lock(&data->suspend_mutex);
>> +	if (data->suspended)
>> +		goto exit;
>> +
>> +#ifndef	CONFIG_HAS_EARLYSUSPEND
>> +	if (data->pre_suspend) {
>> +		retval = data->pre_suspend(data->pm_data);
>> +		if (retval)
>> +			goto exit;
>> +	}
>> +#endif  /* !CONFIG_HAS_EARLYSUSPEND */
>> +
>> +	list_for_each_entry(entry,&data->rmi_functions.list, list)
>> +		if (entry->fh&&  entry->fh->suspend) {
>> +			retval = entry->fh->suspend(entry);
>> +			if (retval<  0)
>> +				goto exit;
>> +		}
>> +
>> +	if (data->f01_container&&  data->f01_container->fh
>> +				&&  data->f01_container->fh->suspend) {
>> +		retval = data->f01_container->fh->suspend(data->f01_container);
>> +		if (retval<  0)
>> +			goto exit;
>> +	}
>> +	data->suspended = true;
>> +
>> +exit:
>> +	mutex_unlock(&data->suspend_mutex);
>> +	return retval;
>
> Once it is better integrated in driver core this will be much simpler.

OK.

>
>> +}
>> +
>> +static int rmi_driver_resume(struct device *dev)
>> +{
>> +	struct rmi_device *rmi_dev;
>> +	struct rmi_driver_data *data;
>> +	struct rmi_function_container *entry;
>> +	int retval = 0;
>> +
>> +	rmi_dev = to_rmi_device(dev);
>> +	data = rmi_get_driverdata(rmi_dev);
>> +
>> +	mutex_lock(&data->suspend_mutex);
>> +	if (!data->suspended)
>> +		goto exit;
>> +
>> +	if (data->f01_container&&  data->f01_container->fh
>> +				&&  data->f01_container->fh->resume) {
>> +		retval = data->f01_container->fh->resume(data->f01_container);
>> +		if (retval<  0)
>> +			goto exit;
>> +	}
>> +
>> +	list_for_each_entry(entry,&data->rmi_functions.list, list)
>> +		if (entry->fh&&  entry->fh->resume) {
>> +			retval = entry->fh->resume(entry);
>> +			if (retval<  0)
>> +				goto exit;
>> +		}
>> +
>> +#ifndef	CONFIG_HAS_EARLYSUSPEND
>> +	if (data->post_resume) {
>> +		retval = data->post_resume(data->pm_data);
>> +		if (retval)
>> +			goto exit;
>> +	}
>> +#endif  /* !CONFIG_HAS_EARLYSUSPEND */
>> +
>> +	data->suspended = false;
>> +
>> +exit:
>> +	mutex_unlock(&data->suspend_mutex);
>> +	return retval;
>
> This one too.

OK

[snip]

>> +
>> +static ssize_t rmi_delay_show(struct device *dev,
>> +				       struct device_attribute *attr, char *buf)
>> +{
>> +	struct rmi_device *rmi_dev;
>> +	struct rmi_device_platform_data *pdata;
>> +
>> +	rmi_dev = to_rmi_device(dev);
>> +	pdata = rmi_dev->phys->dev->platform_data;
>> +
>> +	return snprintf(buf, PAGE_SIZE, "%d %d %d %d %d\n",
>> +		pdata->spi_data.read_delay_us, pdata->spi_data.write_delay_us,
>> +		pdata->spi_data.block_delay_us,
>> +		pdata->spi_data.pre_delay_us, pdata->spi_data.post_delay_us);
>
> This violates "one value per attribute" principle. Also it does not look
> like it is essential for device operation but rather diagnostic
> facility. Switch to debugfs?

Agree with debugfs. However, these are values that really ought to be 
managed as a group, rather than one at a time.

>
>> +}
>> +#endif
>> +
>> +static ssize_t rmi_driver_phys_show(struct device *dev,
>> +				       struct device_attribute *attr, char *buf)
>> +{
>> +	struct rmi_device *rmi_dev;
>> +	struct rmi_phys_info *info;
>> +
>> +	rmi_dev = to_rmi_device(dev);
>> +	info =&rmi_dev->phys->info;
>> +
>> +	return snprintf(buf, PAGE_SIZE, "%-5s %ld %ld %ld %ld %ld %ld %ld\n",
>> +		 info->proto ? info->proto : "unk",
>> +		 info->tx_count, info->tx_bytes, info->tx_errs,
>> +		 info->rx_count, info->rx_bytes, info->rx_errs,
>> +		 info->attn_count);
>
> Same as delay.

Agree with the debugfs part.  However, this is a bunch of related 
numbers that capture the state of the system at particular point in 
time, and it doesn't make sense to me to spread it across a bunch of 
different files.  We used things like /proc/loadavg for the model here, 
for good or ill.

[snip]

Thanks for all the input!

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

* Re: [RFC PATCH 1/11] input: RMI4 public header file and documentation.
  2012-01-06  6:35   ` Dmitry Torokhov
@ 2012-01-09 20:31     ` Christopher Heiny
  0 siblings, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2012-01-09 20:31 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Joerie de Gram,
	Linus Walleij, Naveen Kumar Gaddipati

On 01/05/2012 10:35 PM, Dmitry Torokhov wrote:
> On Wed, Dec 21, 2011 at 06:09:52PM -0800, Christopher Heiny 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.
>> + * 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;
>> +}
>
> We have nice set of be/le16_to_cpu and cpu_to_be/le16 helpers that do
> just that and in much more efficient way.

Yay!  We'll switch to those.

>
>> +
>> +/* 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;
>> +}
>
> Although these methods are not needed, a general comment nonetheless: do
> not put in header files and mark inline functions which address is taken
> and used elsewhere.

OK.

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

* Re: [RFC PATCH 10/11] input: RMI4 F19 - capacitive buttons
  2012-01-06  2:50       ` Dmitry Torokhov
@ 2012-01-09 21:02         ` Christopher Heiny
  0 siblings, 0 replies; 34+ messages in thread
From: Christopher Heiny @ 2012-01-09 21:02 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Jean Delvare, Linux Kernel, Linux Input, Joerie de Gram,
	Linus Walleij, Naveen Kumar Gaddipati, Vivian Ly

On 01/05/2012 06:50 PM, Dmitry Torokhov wrote:
> On Thu, Jan 05, 2012 at 04:05:43PM -0800, Christopher Heiny wrote:
>> On 01/04/2012 11:53 PM, Dmitry Torokhov wrote:
>>> Hi Christopher,
>>>
>>> On Wed, Dec 21, 2011 at 06:10:01PM -0800, Christopher Heiny wrote:
>>>> +
>>>> +struct f19_0d_control {
>>>> +	struct f19_0d_control_0 *general_control;
>>>
>>> Single instance, does not need to be allocated separately.
>>>
>>>> +	struct f19_0d_control_1 *button_int_enable;
>>>> +	struct f19_0d_control_2 *single_button_participation;
>>>> +	struct f19_0d_control_3_4 *sensor_map;
>>>
>>> This should probably be an array of
>>>
>>> struct f19_button_ctrl {
>>> 	struct f19_0d_control_1 int_enable;
>>> 	struct f19_0d_control_2 participation;
>>> 	struct f19_0d_control_3_4 sensor_map;
>>> };
>>>
>>> located at the end of f19_0d_control structure so it can be all
>>> allocated in one shot.
>>
>> We organized it this way because of how the controls are organized
>> in the register map: first the interrupt enables for all buttons,
>> then the participation for all buttons, and then the sensor map for
>> all buttons.  Typical client interactions are to adjust these in an
>> "all at once" approach - that is, change as a single group all the
>> interrupt enables, all the participation settings, or all the sensor
>> map.  By organizing them the way we did, it makes it very easy to
>> read/write the data to the RMI4 sensor's register map.  Using an
>> array of structs would require a building buffers (on write) or
>> tedious extraction from buffers (on read).
>>
>> However, the first two of these are bitmasks, and don't really need
>> their own structs - they can conveniently be u8 * instead.
>
> I'll try coding something to show that it is not as bad as it seems...

OK, we'll look forward to that.

[snip]

>
>>>> +
>>>> +static struct device_attribute attrs[] = {
>>>> +	__ATTR(button_count, RMI_RO_ATTR,
>>>> +		rmi_f19_button_count_show, rmi_store_error),
>>>
>>> Why not NULL instead of rmi_store_error?
>>
>> We found that customers picking up our driver would try to write RO
>> sysfs attributes (or read WO attrs) by invoking echo from the
>> command line.  The operation would fail silently (I'm looking at
>> you, Android shell), leaving the engineer baffled as to why the
>> sensor behavior didn't change.  So we adopted this approach so as to
>> give some clue as to the fact that the operation failed.
>
> But this ends up in various logs (dmesg, /var/log/messages, etc) making
> it potentially DOS scenario. Please help fixing tools instead.

I see your point about the DOS scenario.  How about we do this: make the 
rmi_store/show_error routines a #define, that normally is set to NULL, 
but have an optional debug flag (RMI4_SYSFS_DEBUG) that uses these 
verbose functions.

>
>>>> +
>>>> +	f19->button_control->single_button_participation =
>>>> +		kzalloc(f19->button_data_buffer_size *
>>>> +			sizeof(struct f19_0d_control_2), GFP_KERNEL);
>>>> +	if (!f19->button_control->single_button_participation) {
>>>> +		dev_err(&fc->dev, "Failed to allocate"
>>>> +			" f19_0d_control_2.\n");
>>>
>>> Do not split error messages; it's OK if they exceed 80 column limit.
>>
>> We have one customer who refuses to accept any code if any line
>> exceeds 80 columns, so we wind up with ugliness like this.
>
> This is contrary to current kernel policy though
> (Documentation/CodingStyle, ch 2):
>
> "However, never break user-visible strings such as
> printk messages, because that breaks the ability to grep for them."

Yes, but then checkpatch.pl whines about them, which is also 
unacceptable to that customer.  However, at this point I think 
usefulness takes priority, so we'll unbreak those strings.

[snip]

>>>> +
>>>> +	/* Generate events for buttons that change state. */
>>>> +	for (button = 0; button<   f19->button_count;
>>>> +	     button++) {
>>>> +		int button_reg;
>>>> +		int button_shift;
>>>> +		bool button_status;
>>>> +
>>>> +		/* determine which data byte the button status is in */
>>>> +		button_reg = button / 7;
>>>> +		/* bit shift to get button's status */
>>>> +		button_shift = button % 8;
>>>> +		button_status =
>>>> +		    ((f19->button_data_buffer[button_reg]>>   button_shift)
>>>> +			&   0x01) != 0;
>>>> +
>>>> +		/* if the button state changed from the last time report it
>>>> +		 * and store the new state */
>>>> +		if (button_status != f19->button_down[button]) {
>>>> +			dev_dbg(&fc->dev, "%s: Button %d (code %d) ->   %d.\n",
>>>> +				__func__, button, f19->button_map[button],
>>>> +				 button_status);
>>>> +			/* Generate an event here. */
>>>> +			input_report_key(f19->input, f19->button_map[button],
>>>> +					 button_status);
>>>> +			f19->button_down[button] = button_status;
>>>> +		}
>>>> +	}
>>>
>>> All of the above could be reduced to:
>>>
>>> 	for (button = 0; button<   f19->button_count; button++)
>>> 		input_report_key(f19->input, f19->button_map[button],
>>> 				 test_bit(button, f19->button_data_buffer);
>>
>> I'd like to, but I'm not sure - can we count on the endian-ness of
>> the host processor being the same as the RMI4 register endian-ness?
>
> Hmm, I'd expect RMI transport functions take care of converting into
> proper endianness.

If I can convince myself that it will never do something surprising, 
we'll implement that.

>
>>
>> [snip]
>>
>>>> +
>>>> +static ssize_t rmi_f19_sensor_map_store(struct device *dev,
>>>> +					 struct device_attribute *attr,
>>>> +					 const char *buf, size_t count)
>>>> +{
>>>> +	struct rmi_function_container *fc;
>>>> +	struct f19_data *data;
>>>> +	int sensor_map;
>>>> +	int i;
>>>> +	int retval = count;
>>>> +	int button_count = 0;
>>>> +	int ctrl_bass_addr;
>>>> +	int button_reg;
>>>> +	fc = to_rmi_function_container(dev);
>>>> +	data = fc->data;
>>>> +
>>>> +	if (data->button_query.configurable == 0) {
>>>> +		dev_err(dev,
>>>> +			"%s: Error - sensor map is not configuralbe at"
>>>> +			" run-time", __func__);
>>>
>>> This is not driver error, return error silently.
>>
>> I don't like failing silently, for reasons outlined above.
>
> And for the reason also outlined above I disagree. Driver errors
> (especially KERNEL_ERR level) should be used to signal conditions when
> driver either can't continue or its functionality is seriously impaired.
> Bad user input does not qualify here.

I think switching to the attribute groups will eliminate this particular 
case, along with a few others.  We'll switch others to warning, which is 
a more appropriate level, and look at making them conditional (default 
to silent).

[snip]

>>>> +static ssize_t rmi_f19_button_map_store(struct device *dev,
>>>> +				struct device_attribute *attr,
>>>> +				const char *buf,
>>>> +				size_t count)
>>>> +{
>>>> +	struct rmi_function_container *fc;
>>>> +	struct f19_data *data;
>>>> +	unsigned int button;
>>>> +	int i;
>>>> +	int retval = count;
>>>> +	int button_count = 0;
>>>> +	unsigned char temp_button_map[KEY_MAX];
>>>> +
>>>> +	fc = to_rmi_function_container(dev);
>>>> +	data = fc->data;
>>>> +
>>>> +	/* Do validation on the button map data passed in.  Store button
>>>> +	 * mappings into a temp buffer and then verify button count and
>>>> +	 * data prior to clearing out old button mappings and storing the
>>>> +	 * new ones. */
>>>> +	for (i = 0; i<   data->button_count&&   *buf != 0;
>>>> +	     i++) {
>>>> +		/* get next button mapping value and store and bump up to
>>>> +		 * point to next item in buf */
>>>> +		sscanf(buf, "%u",&button);
>>>> +
>>>> +		/* Make sure the key is a valid key */
>>>> +		if (button>   KEY_MAX) {
>>>> +			dev_err(dev,
>>>> +				"%s: Error - button map for button %d is not a"
>>>> +				" valid value 0x%x.\n", __func__, i, button);
>>>> +			retval = -EINVAL;
>>>> +			goto err_ret;
>>>> +		}
>>>> +
>>>> +		temp_button_map[i] = button;
>>>> +		button_count++;
>>>> +
>>>> +		/* bump up buf to point to next item to read */
>>>> +		while (*buf != 0) {
>>>> +			buf++;
>>>> +			if (*(buf - 1) == ' ')
>>>> +				break;
>>>> +		}
>>>> +	}
>>>> +
>>>> +	/* Make sure the button count matches */
>>>> +	if (button_count != data->button_count) {
>>>> +		dev_err(dev,
>>>> +		    "%s: Error - button map count of %d doesn't match device "
>>>> +		     "button count of %d.\n", __func__, button_count,
>>>> +		     data->button_count);
>>>> +		retval = -EINVAL;
>>>> +		goto err_ret;
>>>> +	}
>>>> +
>>>> +	/* Clear the key bits for the old button map. */
>>>> +	for (i = 0; i<   button_count; i++)
>>>> +		clear_bit(data->button_map[i], data->input->keybit);
>>>> +
>>>> +	/* Switch to the new map. */
>>>> +	memcpy(data->button_map, temp_button_map,
>>>> +	       data->button_count);
>>>> +
>>>> +	/* Loop through the key map and set the key bit for the new mapping. */
>>>> +	for (i = 0; i<   button_count; i++)
>>>> +		set_bit(data->button_map[i], data->input->keybit);
>>>> +
>>>> +err_ret:
>>>> +	return retval;
>>>> +}
>>>
>>> Button map (keymap) should be manipulated with EVIOCGKEYCODE and
>>> EVIOCSKEYCODE ioctls, no need to invent driver-specific way of doing
>>> this.
>>
>> Makes sense, but... we had a customer request to specify the
>> boot-time keymap through the RMI4 driver's platform data.  Is it
>> legal to call setkeycode() to do that instead?
>
> No, the input device should be fully registered for that... But you can
> supply initial keymap in platform data, almost all drivers do that. It
> is new sysfs interface I object to here.

Thanks - we'll study this more closely.  I agree with not having another 
sysfs interface if the functionality is already there.  We just don't 
always realize that there is already an existing interface.

^ permalink raw reply	[flat|nested] 34+ 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; 34+ 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] 34+ messages in thread

* Re: [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
  2012-08-22 12:50 ` 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; 34+ 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] 34+ messages in thread

* Re: [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
  2012-08-17 22:17 Christopher Heiny
  2012-08-22 12:50 ` Linus Walleij
@ 2012-08-27 23:05 ` Linus Walleij
  1 sibling, 0 replies; 34+ 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] 34+ messages in thread

* Re: [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
  2012-08-22 12:50 ` Linus Walleij
@ 2012-08-22 21:29   ` Christopher Heiny
  2012-08-27 23:20   ` Christopher Heiny
  1 sibling, 0 replies; 34+ 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] 34+ messages in thread

* Re: [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
  2012-08-17 22:17 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
  1 sibling, 2 replies; 34+ 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] 34+ messages in thread

* [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver
@ 2012-08-17 22:17 Christopher Heiny
  2012-08-22 12:50 ` Linus Walleij
  2012-08-27 23:05 ` Linus Walleij
  0 siblings, 2 replies; 34+ 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] 34+ messages in thread

end of thread, other threads:[~2012-08-28  0:12 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-22  2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
2011-12-22  2:09 ` [RFC PATCH 1/11] input: RMI4 public header file and documentation Christopher Heiny
2012-01-06  6:35   ` Dmitry Torokhov
2012-01-09 20:31     ` Christopher Heiny
2011-12-22  2:09 ` [RFC PATCH 2/11] input: RMI4 core bus and sensor drivers Christopher Heiny
2012-01-02  6:38   ` Shubhrajyoti
2012-01-05 20:49     ` Christopher Heiny
2012-01-05 21:58       ` Lars-Peter Clausen
2012-01-06  1:56     ` Christopher Heiny
2012-01-06  2:34   ` Dmitry Torokhov
2012-01-07  3:26     ` Christopher Heiny
2011-12-22  2:09 ` [RFC PATCH 3/11] input: RMI4 physical layer drivers for I2C and SPI Christopher Heiny
2011-12-22  2:09 ` [RFC PATCH 4/11] input: RMI4 KConfigs and Makefiles Christopher Heiny
2011-12-22  2:09 ` [RFC PATCH 5/11] input: rmidev character driver for RMI4 sensors Christopher Heiny
2011-12-22  2:09 ` [RFC PATCH 6/11] input: RMI4 F09 - self test Christopher Heiny
2011-12-22  2:09 ` [RFC PATCH 7/11] input: RMI4 F01 - device control Christopher Heiny
2011-12-22  2:09 ` [RFC PATCH 8/11] input: RMI4 F54 - analog data reporting Christopher Heiny
2011-12-22  2:10 ` [RFC PATCH 9/11] input: RMI F34 - firmware reflash Christopher Heiny
2011-12-22  2:10 ` [RFC PATCH 10/11] input: RMI4 F19 - capacitive buttons Christopher Heiny
2012-01-05  7:53   ` Dmitry Torokhov
2012-01-06  0:05     ` Christopher Heiny
2012-01-06  2:50       ` Dmitry Torokhov
2012-01-09 21:02         ` Christopher Heiny
2011-12-22  2:10 ` [RFC PATCH 11/11] input: RMI4 F11 - multifinger pointing Christopher Heiny
2012-01-01 13:51 ` [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Linus Walleij
2012-01-04  1:51   ` Christopher Heiny
2012-01-05  7:58 ` Dmitry Torokhov
2012-01-05 20:09   ` Christopher Heiny
2012-08-17 22:17 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-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).