2012-11-17 03:59:58

by Christopher Heiny

[permalink] [raw]
Subject: [RFC PATCH 00/06] input: Synaptics RMI4 Touchscreen Driver

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 2012-10-05. For all files
included in this patch, we believe that all outstanding issues arising
from the previous submissions have been addressed, except as follows:

* we've investigated using irq_chip to manage chip interrupt dispatch, and
that certainly is a good idea. However, we need to support kernels back to
3.0.x, and the required functionality is not yet present in those older kernels.
Once we no longer need to support 3.0.x, we'll jump onto irq_chip right
away.

* some of the requested changes to rmi_f11.c are simply not possible while
still retaining general driver functionality. We've clarified existing comments
and added new ones to explain why that is the case.


This patch is against the v3.7-rc5 tag of Linus' kernel tree, object
77b67063bb6bce6d475e910d3b886a606d0d91f7. It should work fine with that kernel,
but will not work with earlier kernels due to changes in the input subsystem.



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

- physical layer implementation for I2C

- device driver for general RMI4 sensor functionality

- function implementations for the following RMI4 functions:
* F01 device control
* F11 multifinger pointing

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 or debugfs (depending
on whether the parameters are used during normal operation or system
development/prototyping).



The amount of feedback received on previous patches precludes addressing each
item individually. However, major changes for this patch are:
- elimination of sysfs management macros
- elimination of roll-your-own bitmask management
- moved potentially large arrays and structs from the stack to the heap
- elimination of the union/struct idiom for mapping register groups
- corrected identification of input devices, including adding a BUS_RMI
bus type.


We've broken this patch into 6 parts, as follows:
01 - public header files and documentation
02 - core sensor and bus implementation
03 - I2C physical layer driver
04 - Kconfigs and Makefiles
05..06 - 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 <[email protected]>

Cc: Jean Delvare <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>

---


2012-11-17 04:00:17

by Christopher Heiny

[permalink] [raw]
Subject: [RFC PATCH 01/06] input/rmi4: Public header and documentation

rmi.h provides public definitions required by the RMI bus implementation and
modules that interact with it.

debugfs and sysfs attributes are documented in files in
Documentation/ABI/testing. There's two files, one for debugfs and one for
sysfs.


Signed-off-by: Christopher Heiny <[email protected]>

Cc: Dmitry Torokhov <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>

---

Documentation/ABI/testing/debugfs-rmi4 | 99 ++++++
Documentation/ABI/testing/sysfs-rmi4 | 103 ++++++
include/linux/rmi.h | 596 ++++++++++++++++++++++++++++++++
3 files changed, 798 insertions(+), 0 deletions(-)

diff --git a/Documentation/ABI/testing/debugfs-rmi4 b/Documentation/ABI/testing/debugfs-rmi4
new file mode 100644
index 0000000..ef0739d
--- /dev/null
+++ b/Documentation/ABI/testing/debugfs-rmi4
@@ -0,0 +1,99 @@
+What: /sys/kernel/debug/rmi/devices
+Date: October 2012
+KernelVersion: 3.x
+Contact: Christopher Heiny <[email protected]>
+Description:
+
+ The RMI4 driver implementation exposes a set of informational and control
+ parameters via debugfs. These parameters are those that typically are only
+ viewed or adjusted during product development, tuning, and debug.
+ For parameters that are referenced and/or adjusted during normal operation,
+ please see sysfs-rmi4 in this directory.
+
+ General debugging parameters for a particular RMI4 sensor are found in
+ /sys/kernel/debug/rmi/sensorXX/, where XX is a the device's ID as a two
+ digit number (padded with leading zeros). Function specific parameters
+ for an RMI4 sensor are found in /sys/kernel/debug/rmi/devices/FYY/, 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, F11 for RMI function
+ F11).
+
+ For RMI4 functions that support multiple sensor instances (such as F11),
+ the parameters for individual sensors have .Z appended to them, where Z is
+ the index of the sensor instance (for example, clip.0, clip.1, clip.2, and
+ so on).
+
+ Some of the parameters exposed here are described in detail in the
+ RMI4 Specification, which is found here:
+ http://www.synaptics.com/sites/default/files/511-000136-01_revD.pdf
+ For such parameters, we'll reference you to that document, rather than
+ copying the contents here.
+
+ /sys/kernel/debug/rmi/
+ /sensorXX/
+ attn_count - (ro) Shows the number of ATTN interrupts handled so far.
+ comms_debug - (rw) Write 1 to this dump information about register
+ reads and writes to the console. Write 0 to this to turn
+ this feature off. WARNING: Imposes a major performance
+ penalty when turned on.
+ irq_debug - (rw) Write 1 to this dump information about interrupts
+ to the console. Write 0 to this to turn this feature off.
+ WARNIG: Imposes a major performance penalty when turned on.
+ phys - (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
+
+ All counts are 64-bit unsigned values, and are set to zero
+ when the physical layer driver is initialized.
+
+ /sensorXX/F01/
+ interrupt_enable - (rw) allows you to read or modify the F01
+ interrupt enable mask (the F01_RMI_Ctrl1 register(s)).
+
+ /sensorXX/F11/
+ clip.Z - (rw) Controls in-driver coordinate clipping for the 2D
+ sensor Z. 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.
+ delta_threshold.Z - (rw) Controls the F11 distance thresholds. This
+ contains two values, corresponding to F11_2D_Ctrl2 and
+ F11_2D_Ctrl3. Se the spec for more details.
+ flip.Z - (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.Z - (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.
+ rezero_wait - (rw) If non-zero, F11 will wait this many milliseconds
+ after exiting suspend mode before recalibrating the sensor(s).
+ This is useful in systems were there may be unusual
+ electrical conditions during the resume process, allowing
+ you to delay recalibration until the electrical environment
+ has stabilized.
+ swap.Z - (rw) Writing 1 to this parameter swaps the X and Y axis as
+ reported by the sensor instance Z, 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.
+ type_a - (rw) Most RMI4 F11 implementations support MT-B reporting.
+ You can write 1 to this parameter to force MT-A reporting.
diff --git a/Documentation/ABI/testing/sysfs-rmi4 b/Documentation/ABI/testing/sysfs-rmi4
new file mode 100644
index 0000000..3354d10
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-rmi4
@@ -0,0 +1,103 @@
+What: /sys/bus/rmi/devices
+Date: October 2012
+KernelVersion: 3.x
+Contact: Christopher Heiny <[email protected]>
+Description:
+
+ The RMI4 driver implementation exposes a set of informational and control
+ parameters via sysfs. These parameters are those that typically will be
+ referenced and/or adjusted during normal operation of products containing
+ RMI sensors. For paramters that are only viewed or adjusted during product
+ development and debug, please see debugfs-rmi4 in this directory.
+
+ General parameters for a particular RMI4 sensor 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
+ for an RMI4 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).
+
+ Many of the parameters exposed here are described in detail in the
+ RMI4 Specification, which is found here:
+ http://www.synaptics.com/sites/default/files/511-000136-01_revD.pdf
+ For such parameters, we'll reference you to that document, rather than
+ copying the contents here.
+
+ /sys/bus/rmi/devices
+ /sensorXX/
+ bsr ... (rw) bus select register, if supported (see spec)
+ enabled ... (rw) enable/disable interrupt management [deprecated]
+
+ /sensor00.fn01/
+ 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. See spec for full details.
+ 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.
+ See spec for full details.
+ datecode ... (ro) The date on which the module was manufactured.
+ See spec for full details.
+ doze_holdoff ... (rw) Controls how long the sensor will wait before
+ entering the doze state when no fingers are present on the
+ device. The time is in terms of 10 milliseconds - a
+ doze_holdoff value of 3 corresponds to a time period of 30
+ milliseconds. See spec for full details.
+ 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. See spec for full details.
+ interrupt_enable ... (ro) This represents the current RMI4 interrupt
+ mask (F01_RMI_Ctrl1 registers). See spec for full details.
+ manufacturer ... (ro) This is the identity of the manufacturer of
+ the device, as obtained from F01_RMI_Query0. See spec for
+ full details.
+ nosleep ... (rw) Writing 1 to this parameter disables all normal
+ firmware powersaving behaviors and forces the device to run
+ at full power without sleeping. See spec for full details.
+ productid ... (ro) The product info bytes, as determined from
+ F01_RMI_Query2 and F01_RMI_Query3 registers. See spec for
+ full details.
+ productinfo ... (ro) A string of up to 10 characters, identifying
+ the product. See spec for full details.
+ 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 both the RMI4 spec and the
+ sensor spec sheet for details.
+ reset ... (wo) Writing a 1 to this write only register forces the
+ device to reset.
+ sleepmode ... (rw) Controls power management on the device. Writing
+ 0 to this parameter puts the device into its normal operating
+ mode. Writing 1 to this parameter fully disables touch
+ sensors and similar inputs - no touch data will be reported
+ from the device in this mode. Writing 2 or 3 to this device
+ may or may not have an effect, depending on the particular
+ device - see the product specification for your sensor for
+ details.
+ statuscode ... (ro) Reports the most recent device status, such as
+ invalid configuration, device reset, CRC failure, and so on.
+ Please se the RMI4 specification for details.
+ unconfigured ... (ro) This is the opposite of the configured bit,
+ described above.
+ wakeup_threshold ... (rw) This controls the change in capacitive
+ signal needed to wake the device from the doze state. Please
+ see the RMI4 specification for the F01_RMI_Ctrl3 register
+ for more details.
+
+ /sensor00.fn11/
+ abs_pos_filt ... (rw) Enables or disables the absolute position
+ filter feature. See spec for full details.
+ maxPos ... (rw) Adjusts the maximum X and Y position control
+ registers (F11_2D_Ctrl6 through F11_2D_Ctrl9). See spec for
+ full details.
+ relreport ... (rw) Enables or disabled relative finger motion
+ reporting. See spec for full details.
+ rezero ... (wo) Force recalibration of the F11 2D sensor(s). See
+ spec for full details.
+
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
new file mode 100644
index 0000000..b7d0a34
--- /dev/null
+++ b/include/linux/rmi.h
@@ -0,0 +1,596 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _RMI_H
+#define _RMI_H
+#include <linux/kernel.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/list.h>
+#include <linux/lockdep.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/stat.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/debugfs.h>
+
+extern struct bus_type rmi_bus_type;
+
+extern struct device_type rmi_function_type;
+extern struct device_type rmi_sensor_type;
+
+
+/* Permissions for sysfs attributes. Since the permissions policy will change
+ * on a global basis in the future, rather than edit all sysfs attrs everywhere
+ * in the driver (and risk screwing that up in the process), we use this handy
+ * set of #defines. That way when we change the policy for sysfs permissions,
+ * we only need to change them here.
+ */
+#define RMI_RO_ATTR S_IRUGO
+#define RMI_RW_ATTR (S_IRUGO | S_IWUGO)
+#define RMI_WO_ATTR S_IWUGO
+
+enum rmi_attn_polarity {
+ RMI_ATTN_ACTIVE_LOW = 0,
+ RMI_ATTN_ACTIVE_HIGH = 1
+};
+
+/**
+ * struct rmi_f11_axis_alignment - target axis alignment
+ * @swap_axes: set to TRUE if desired to swap x- and y-axis
+ * @flip_x: set to TRUE if desired to flip direction on x-axis
+ * @flip_y: set to TRUE if desired to flip direction on y-axis
+ * @clip_X_low - reported X coordinates below this setting will be clipped to
+ * the specified value
+ * @clip_X_high - reported X coordinates above this setting will be clipped to
+ * the specified value
+ * @clip_Y_low - reported Y coordinates below this setting will be clipped to
+ * the specified value
+ * @clip_Y_high - reported Y coordinates above this setting will be clipped to
+ * the specified value
+ * @offset_X - this value will be added to all reported X coordinates
+ * @offset_Y - this value will be added to all reported Y coordinates
+ * @rel_report_enabled - if set to true, the relative reporting will be
+ * automatically enabled for this sensor.
+ */
+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;
+ u8 delta_x_threshold;
+ u8 delta_y_threshold;
+};
+
+/** This is used to override any hints an F11 2D sensor might have provided
+ * as to what type of sensor it is.
+ *
+ * @rmi_f11_sensor_default - do not override, determine from F11_2D_QUERY14 if
+ * available.
+ * @rmi_f11_sensor_touchscreen - treat the sensor as a touchscreen (direct
+ * pointing).
+ * @rmi_f11_sensor_touchpad - thread the sensor as a touchpad (indirect
+ * pointing).
+ */
+enum rmi_f11_sensor_type {
+ rmi_f11_sensor_default = 0,
+ rmi_f11_sensor_touchscreen,
+ rmi_f11_sensor_touchpad
+};
+
+/**
+ * struct rmi_f11_sensor_data - overrides defaults for a single F11 2D sensor.
+ * @axis_align - provides axis alignment overrides (see above).
+ * @type_a - all modern RMI F11 firmwares implement Multifinger Type B
+ * protocol. Set this to true to force MF Type A behavior, in case you find
+ * an older sensor.
+ * @sensor_type - Forces the driver to treat the sensor as an indirect
+ * pointing device (touchpad) rather than a direct pointing device
+ * (touchscreen). This is useful when F11_2D_QUERY14 register is not
+ * available.
+ */
+struct rmi_f11_sensor_data {
+ struct rmi_f11_2d_axis_alignment axis_align;
+ bool type_a;
+ enum rmi_f11_sensor_type sensor_type;
+};
+
+/**
+ * struct rmi_f01_power - override default power management settings.
+ *
+ */
+enum rmi_f01_nosleep {
+ RMI_F01_NOSLEEP_DEFAULT = 0,
+ RMI_F01_NOSLEEP_OFF = 1,
+ RMI_F01_NOSLEEP_ON = 2
+};
+
+/**
+ * struct rmi_f01_power_management -When non-zero, these values will be written
+ * to the touch sensor to override the default firmware settigns. For a
+ * detailed explanation of what each field does, see the corresponding
+ * documention in the RMI4 specification.
+ *
+ * @nosleep - specifies whether the device is permitted to sleep or doze (that
+ * is, enter a temporary low power state) when no fingers are touching the
+ * sensor.
+ * @wakeup_threshold - controls the capacitance threshold at which the touch
+ * sensor will decide to wake up from that low power state.
+ * @doze_holdoff - controls how long the touch sensor waits after the last
+ * finger lifts before entering the doze state, in units of 100ms.
+ * @doze_interval - controls the interval between checks for finger presence
+ * when the touch sensor is in doze mode, in units of 10ms.
+ */
+struct rmi_f01_power_management {
+ enum rmi_f01_nosleep nosleep;
+ u8 wakeup_threshold;
+ u8 doze_holdoff;
+ u8 doze_interval;
+};
+
+/**
+ * struct rmi_button_map - used to specify the initial input subsystem key
+ * event codes to be generated by buttons (or button like entities) on the
+ * touch sensor.
+ * @nbuttons - length of the button map.
+ * @map - the key event codes for the corresponding buttons on the touch
+ * sensor.
+ */
+struct rmi_button_map {
+ u8 nbuttons;
+ u8 *map;
+};
+
+struct rmi_f30_gpioled_map {
+ u8 ngpioleds;
+ u8 *map;
+};
+
+/**
+ * struct rmi_device_platform_data_spi - provides parameters used in SPI
+ * communications. All Synaptics SPI products support a standard SPI
+ * interface; some also support what is called SPI V2 mode, depending on
+ * firmware and/or ASIC limitations. In V2 mode, the touch sensor can
+ * support shorter delays during certain operations, and these are specified
+ * separately from the standard mode delays.
+ *
+ * @block_delay - for standard SPI transactions consisting of both a read and
+ * write operation, the delay (in microseconds) between the read and write
+ * operations.
+ * @split_read_block_delay_us - for V2 SPI transactions consisting of both a
+ * read and write operation, the delay (in microseconds) between the read and
+ * write operations.
+ * @read_delay_us - the delay between each byte of a read operation in normal
+ * SPI mode.
+ * @write_delay_us - the delay between each byte of a write operation in normal
+ * SPI mode.
+ * @split_read_byte_delay_us - the delay between each byte of a read operation
+ * in V2 mode.
+ * @pre_delay_us - the delay before the start of a SPI transaction. This is
+ * typically useful in conjunction with custom chip select assertions (see
+ * below).
+ * @post_delay_us - the delay after the completion of an SPI transaction. This
+ * is typically useful in conjunction with custom chip select assertions (see
+ * below).
+ * @cs_assert - For systems where the SPI subsystem does not control the CS/SSB
+ * line, or where such control is broken, you can provide a custom routine to
+ * handle a GPIO as CS/SSB. This routine will be called at the beginning and
+ * end of each SPI transaction. The RMI SPI implementation will wait
+ * pre_delay_us after this routine returns before starting the SPI transfer;
+ * and post_delay_us after completion of the SPI transfer(s) before calling it
+ * with assert==FALSE.
+ */
+struct rmi_device_platform_data_spi {
+ int block_delay_us;
+ int split_read_block_delay_us;
+ int read_delay_us;
+ int write_delay_us;
+ int split_read_byte_delay_us;
+ int pre_delay_us;
+ int post_delay_us;
+
+ void *cs_assert_data;
+ int (*cs_assert) (const void *cs_assert_data, const bool assert);
+};
+
+/**
+ * struct rmi_device_platform_data - system specific configuration info.
+ *
+ * @sensor_name - this is used for various diagnostic messages.
+ *
+ * @firmware_name - if specified will override default firmware name,
+ * for reflashing.
+ *
+ * @attn_gpio - the index of a GPIO that will be used to provide the ATTN
+ * interrupt from the touch sensor.
+ * @attn_polarity - indicates whether ATTN is active high or low.
+ * @level_triggered - by default, the driver uses edge triggered interrupts.
+ * However, this can cause problems with suspend/resume on some platforms. In
+ * that case, set this to 1 to use level triggered interrupts.
+ * @gpio_config - a routine that will be called when the driver is loaded to
+ * perform any platform specific GPIO configuration, and when it is unloaded
+ * for GPIO de-configuration. This is typically used to configure the ATTN
+ * GPIO and the I2C or SPI pins, if necessary.
+ * @gpio_data - platform specific data to be passed to the GPIO configuration
+ * function.
+ *
+ * @poll_interval_ms - the time in milliseconds between reads of the interrupt
+ * status register. This is ignored if attn_gpio is non-zero.
+ *
+ * @reset_delay_ms - after issuing a reset command to the touch sensor, the
+ * driver waits a few milliseconds to give the firmware a chance to
+ * to re-initialize. You can override the default wait period here.
+ *
+ * @spi_data - override default settings for SPI delays and SSB management (see
+ * above).
+ *
+ * @f11_sensor_data - an array of platform data for individual F11 2D sensors.
+ * @f11_sensor_count - the length of f11_sensor_data array. Extra entries will
+ * be ignored; if there are too few entries, all settings for the additional
+ * sensors will be defaulted.
+ * @f11_rezero_wait - if non-zero, this is how may milliseconds the F11 2D
+ * sensor(s) will wait before being be rezeroed on exit from suspend. If
+ * this value is zero, the F11 2D sensor(s) will not be rezeroed on resume.
+ * @pre_suspend - this will be called before any other suspend operations are
+ * done.
+ * @power_management - overrides default touch sensor doze mode settings (see
+ * above)
+ * @f19_button_map - provide initial input subsystem key mappings for F19.
+ * @f1a_button_map - provide initial input subsystem key mappings for F1A.
+ * @gpioled_map - provides initial settings for GPIOs and LEDs controlled by
+ * F30.
+ * @f41_button_map - provide initial input subsystem key mappings for F41.
+ *
+ * @post_suspend - this will be called after all suspend operations are
+ * completed. This is the ONLY safe place to power off an RMI sensor
+ * during the suspend process.
+ * @pre_resume - this is called before any other resume operations. If you
+ * powered off the RMI4 sensor in post_suspend(), then you MUST power it back
+ * here, and you MUST wait an appropriate time for the ASIC to come up
+ * (100ms to 200ms, depending on the sensor) before returning.
+ * @pm_data - this will be passed to the various (pre|post)_(suspend/resume)
+ * functions.
+ */
+struct rmi_device_platform_data {
+ char *sensor_name; /* Used for diagnostics. */
+
+ int attn_gpio;
+ enum rmi_attn_polarity attn_polarity;
+ bool level_triggered;
+ void *gpio_data;
+ int (*gpio_config)(void *gpio_data, bool configure);
+
+ int poll_interval_ms;
+
+ int reset_delay_ms;
+
+ struct rmi_device_platform_data_spi spi_data;
+
+ /* function handler pdata */
+ struct rmi_f11_sensor_data *f11_sensor_data;
+ u8 f11_sensor_count;
+ u16 f11_rezero_wait;
+ struct rmi_f01_power_management power_management;
+ struct rmi_button_map *f19_button_map;
+ struct rmi_button_map *f1a_button_map;
+ struct rmi_f30_gpioled_map *gpioled_map;
+ struct rmi_button_map *f41_button_map;
+
+#ifdef CONFIG_RMI4_FWLIB
+ char *firmware_name;
+#endif
+
+#ifdef CONFIG_PM
+ void *pm_data;
+ int (*pre_suspend) (const void *pm_data);
+ int (*post_suspend) (const void *pm_data);
+ int (*pre_resume) (const void *pm_data);
+ int (*post_resume) (const void *pm_data);
+#endif
+};
+
+/**
+ * struct rmi_function_descriptor - RMI function base addresses
+ *
+ * @query_base_addr: The RMI Query base address
+ * @command_base_addr: The RMI Command base address
+ * @control_base_addr: The RMI Control base address
+ * @data_base_addr: The RMI Data base address
+ * @interrupt_source_count: The number of irqs this RMI function needs
+ * @function_number: The RMI function number
+ *
+ * This struct is used when iterating the Page Description Table. The addresses
+ * are 16-bit values to include the current page address.
+ *
+ */
+struct rmi_function_descriptor {
+ u16 query_base_addr;
+ u16 command_base_addr;
+ u16 control_base_addr;
+ u16 data_base_addr;
+ u8 interrupt_source_count;
+ u8 function_number;
+ u8 function_version;
+};
+
+struct rmi_function_container;
+struct rmi_device;
+
+/**
+ * struct rmi_function_handler - driver routines for a particular RMI function.
+ *
+ * @func: The RMI function number
+ * @reset: Called when a reset of the touch sensor is detected. The routine
+ * should perform any out-of-the-ordinary reset handling that might be
+ * necessary. Restoring of touch sensor configuration registers should be
+ * handled in the config() callback, below.
+ * @config: Called when the function container is first initialized, and
+ * after a reset is detected. This routine should write any necessary
+ * configuration settings to the device.
+ * @attention: Called when the IRQ(s) for the function are set by the touch
+ * sensor.
+ * @suspend: Should perform any required operations to suspend the particular
+ * function.
+ * @resume: Should perform any required operations to resume the particular
+ * function.
+ *
+ * All callbacks are expected to return 0 on success, error code on failure.
+ */
+struct rmi_function_handler {
+ struct device_driver driver;
+
+ u8 func;
+ int (*config)(struct rmi_function_container *fc);
+ int (*reset)(struct rmi_function_container *fc);
+ int (*attention)(struct rmi_function_container *fc,
+ unsigned long *irq_bits);
+#ifdef CONFIG_PM
+ int (*suspend)(struct rmi_function_container *fc);
+ int (*resume)(struct rmi_function_container *fc);
+#endif
+};
+
+#define to_rmi_function_handler(d) \
+ container_of(d, struct rmi_function_handler, driver);
+
+/**
+ * struct rmi_function_container - represents the implementation of an RMI4
+ * function for a particular device (basically, a driver for that RMI4 function)
+ *
+ * @fd: The function descriptor of the RMI function
+ * @rmi_dev: Pointer to the RMI device associated with this function container
+ * @dev: The device associated with this particular function.
+ *
+ * @num_of_irqs: The number of irqs needed by this function
+ * @irq_pos: The position in the irq bitfield this function holds
+ * @irq_mask: For convience, can be used to mask IRQ bits off during ATTN
+ * interrupt handling.
+ * @data: Private data pointer
+ *
+ * @list: Used to create a list of function containers.
+ * @debugfs_root: used during debugging
+ *
+ */
+struct rmi_function_container {
+
+ struct rmi_function_descriptor fd;
+ struct rmi_device *rmi_dev;
+ struct device dev;
+ int num_of_irqs;
+ int irq_pos;
+ unsigned long *irq_mask;
+ void *data;
+ struct list_head list;
+
+#ifdef CONFIG_RMI4_DEBUG
+ struct dentry *debugfs_root;
+#endif
+};
+
+#define to_rmi_function_container(d) \
+ container_of(d, struct rmi_function_container, dev);
+
+/**
+ * struct rmi_driver - driver for an RMI4 sensor on the RMI bus.
+ *
+ * @driver: Device driver model driver
+ * @irq_handler: Callback for handling irqs
+ * @reset_handler: Called when a reset is detected.
+ * @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
+ * @store_productid: Callback for cache product id from function 01
+ * @data: Private data pointer
+ *
+ */
+struct rmi_driver {
+ struct device_driver driver;
+
+ int (*irq_handler)(struct rmi_device *rmi_dev, int irq);
+ int (*reset_handler)(struct rmi_device *rmi_dev);
+ int (*store_irq_mask)(struct rmi_device *rmi_dev,
+ unsigned long *new_interupts);
+ int (*restore_irq_mask)(struct rmi_device *rmi_dev);
+ int (*store_productid)(struct rmi_device *rmi_dev);
+ int (*set_input_params)(struct rmi_device *rmi_dev,
+ struct input_dev *input);
+ 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 debugfs 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;
+};
+
+/**
+ * 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_block: Writing a block of data to the specified address
+ * @read_block: Read a block of data from the specified address.
+ * @irq_thread: if not NULL, the sensor driver will use this instead of the
+ * default irq_thread implementation.
+ * @hard_irq: if not NULL, the sensor driver will use this for the hard IRQ
+ * handling
+ * @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_block)(struct rmi_phys_device *phys, u16 addr,
+ const void *buf, const int len);
+ int (*read_block)(struct rmi_phys_device *phys, u16 addr,
+ void *buf, const int len);
+
+ int (*enable_device) (struct rmi_phys_device *phys);
+ void (*disable_device) (struct rmi_phys_device *phys);
+
+ irqreturn_t (*irq_thread)(int irq, void *p);
+ irqreturn_t (*hard_irq)(int irq, void *p);
+
+ void *data;
+
+ struct rmi_phys_info info;
+};
+
+/**
+ * struct rmi_device - represents an RMI4 sensor device on the RMI bus.
+ *
+ * @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
+ * @debugfs_root: base for this particular sensor device.
+ *
+ */
+struct rmi_device {
+ struct device dev;
+ int number;
+
+ struct rmi_driver *driver;
+ struct rmi_phys_device *phys;
+
+#ifdef CONFIG_RMI4_DEBUG
+ struct dentry *debugfs_root;
+#endif
+};
+
+#define to_rmi_device(d) container_of(d, struct rmi_device, dev);
+#define to_rmi_platform_data(d) ((d)->phys->dev->platform_data);
+
+/**
+ * rmi_read - read a single 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, void *buf)
+{
+ return d->phys->read_block(d->phys, addr, buf, 1);
+}
+
+/**
+ * rmi_read_block - read a block of bytes
+ * @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, void *buf,
+ const int len)
+{
+ return d->phys->read_block(d->phys, addr, buf, len);
+}
+
+/**
+ * rmi_write - write a single 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, const u8 data)
+{
+ return d->phys->write_block(d->phys, addr, &data, 1);
+}
+
+/**
+ * rmi_write_block - write a block of bytes
+ * @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,
+ const void *buf, const int len)
+{
+ return d->phys->write_block(d->phys, addr, buf, len);
+}
+
+int rmi_register_phys_device(struct rmi_phys_device *phys);
+void rmi_unregister_phys_device(struct rmi_phys_device *phys);
+int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data));
+
+#endif

2012-11-17 04:00:30

by Christopher Heiny

[permalink] [raw]
Subject: [RFC PATCH 05/06] input/rmi4: F01 - device control

RMI Function 01 implements basic device control and power management
behaviors for the RMI4 sensor.

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


Signed-off-by: Christopher Heiny <[email protected]>

Cc: Dmitry Torokhov <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>


---

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

2012-11-17 04:00:33

by Christopher Heiny

[permalink] [raw]
Subject: [RFC PATCH 02/06] input/rmi4: Core files

rmi_bus.c implements the basic functionality of the RMI bus. This file is
greatly simplified compared to the previous patch - we've switched from
"do it yourself" device/driver binding to using device_type to distinguish
between the two kinds of devices on the bus (sensor devices and function
specific devices) and using the standard bus implementation to manage devices
and drivers.


rmi_driver.c is a driver for the general functionality of the RMI sensor as a
whole, managing those behaviors (including IRQ handling) that are not specific
to any RMI4 function. It has some unavoidable dependencies on F01 behavior,
though we have worked to minimize those as far as possible.


The header file rmi_driver.h provides definitions that are shared among
the modules of the RMI implementation, but not thought to be necessary
outside it.


Greg KH - Linus Walleij recommended that we seek your input on these core
files, particularly the bus implementation.


Signed-off-by: Christopher Heiny <[email protected]>

Cc: Greg Kroah-Hartman <[email protected]>
Cc: Dmitry Torokhov <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>

---

drivers/input/rmi4/rmi_bus.c | 248 ++++++
drivers/input/rmi4/rmi_driver.c | 1663 +++++++++++++++++++++++++++++++++++++++
drivers/input/rmi4/rmi_driver.h | 139 ++++
include/uapi/linux/input.h | 1 +
4 files changed, 2051 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..a912349
--- /dev/null
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/kconfig.h>
+#include <linux/list.h>
+#include <linux/pm.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/debugfs.h>
+#include "rmi_driver.h"
+
+DEFINE_MUTEX(rmi_bus_mutex);
+
+static struct attribute *function_dev_attrs[] = {
+ NULL,
+};
+
+static struct attribute_group function_dev_attr_group = {
+ .attrs = function_dev_attrs,
+};
+
+static const struct attribute_group *function_dev_attr_groups[] = {
+ &function_dev_attr_group,
+ NULL,
+};
+
+struct device_type rmi_function_type = {
+ .name = "rmi_function",
+ .groups = function_dev_attr_groups,
+};
+EXPORT_SYMBOL_GPL(rmi_function_type);
+
+static struct attribute *sensor_dev_attrs[] = {
+ NULL,
+};
+static struct attribute_group sensor_dev_attr_group = {
+ .attrs = sensor_dev_attrs,
+};
+
+static const struct attribute_group *sensor_dev_attr_groups[] = {
+ &sensor_dev_attr_group,
+ NULL,
+};
+
+struct device_type rmi_sensor_type = {
+ .name = "rmi_sensor",
+ .groups = sensor_dev_attr_groups,
+};
+EXPORT_SYMBOL_GPL(rmi_sensor_type);
+
+static atomic_t physical_device_count = ATOMIC_INIT(0);
+
+#ifdef CONFIG_RMI4_DEBUG
+static struct dentry *rmi_debugfs_root;
+#endif
+
+#ifdef CONFIG_PM
+static int rmi_bus_suspend(struct device *dev)
+{
+ struct device_driver *driver = dev->driver;
+ const struct dev_pm_ops *pm;
+
+ if (!driver)
+ return 0;
+
+ pm = driver->pm;
+ if (pm && pm->suspend)
+ return pm->suspend(dev);
+ if (driver->suspend)
+ return driver->suspend(dev, PMSG_SUSPEND);
+
+ return 0;
+}
+
+static int rmi_bus_resume(struct device *dev)
+{
+ struct device_driver *driver = dev->driver;
+ const struct dev_pm_ops *pm;
+
+ if (!driver)
+ return 0;
+
+ pm = driver->pm;
+ if (pm && pm->resume)
+ return pm->resume(dev);
+ if (driver->resume)
+ return driver->resume(dev);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rmi_bus_pm_ops,
+ rmi_bus_suspend, rmi_bus_resume);
+
+struct bus_type rmi_bus_type = {
+ .name = "rmi",
+ .pm = &rmi_bus_pm_ops
+};
+EXPORT_SYMBOL_GPL(rmi_bus_type);
+
+static void release_rmidev_device(struct device *dev)
+{
+ device_unregister(dev);
+}
+
+/**
+ * rmi_register_phys_device - register a physical device connection on the RMI
+ * bus. Physical drivers provide communication from the devices on the bus to
+ * the RMI4 sensor on a bus such as SPI, I2C, and so on.
+ *
+ * @phys: the physical device to register
+ */
+int rmi_register_phys_device(struct rmi_phys_device *phys)
+{
+ struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+ struct rmi_device *rmi_dev;
+
+ if (!pdata) {
+ dev_err(phys->dev, "no platform data!\n");
+ return -EINVAL;
+ }
+
+ rmi_dev = kzalloc(sizeof(struct rmi_device), GFP_KERNEL);
+ if (!rmi_dev)
+ return -ENOMEM;
+
+ rmi_dev->phys = phys;
+ rmi_dev->dev.bus = &rmi_bus_type;
+ rmi_dev->dev.type = &rmi_sensor_type;
+
+ rmi_dev->number = atomic_inc_return(&physical_device_count) - 1;
+ rmi_dev->dev.release = release_rmidev_device;
+
+ dev_set_name(&rmi_dev->dev, "sensor%02d", rmi_dev->number);
+ dev_dbg(phys->dev, "%s: Registered %s as %s.\n", __func__,
+ pdata->sensor_name, dev_name(&rmi_dev->dev));
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG) && rmi_debugfs_root) {
+ rmi_dev->debugfs_root = debugfs_create_dir(
+ dev_name(&rmi_dev->dev), rmi_debugfs_root);
+ if (!rmi_dev->debugfs_root)
+ dev_err(&rmi_dev->dev, "Failed to create debugfs root.\n");
+ }
+
+ phys->rmi_dev = rmi_dev;
+ return device_register(&rmi_dev->dev);
+}
+EXPORT_SYMBOL(rmi_register_phys_device);
+
+/**
+ * rmi_unregister_phys_device - unregister a physical device connection
+ * @phys: the physical driver to unregister
+ *
+ */
+void rmi_unregister_phys_device(struct rmi_phys_device *phys)
+{
+ struct rmi_device *rmi_dev = phys->rmi_dev;
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG) && rmi_dev->debugfs_root)
+ debugfs_remove(rmi_dev->debugfs_root);
+
+ kfree(rmi_dev);
+}
+EXPORT_SYMBOL(rmi_unregister_phys_device);
+
+/**
+ * rmi_for_each_dev - provides a way for other parts of the system to enumerate
+ * the devices on the RMI bus.
+ *
+ * @data - will be passed into the callback function.
+ * @func - will be called for each device.
+ */
+int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data))
+{
+ int retval;
+ mutex_lock(&rmi_bus_mutex);
+ retval = bus_for_each_dev(&rmi_bus_type, NULL, data, func);
+ mutex_unlock(&rmi_bus_mutex);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(rmi_for_each_dev);
+
+static int __init rmi_bus_init(void)
+{
+ int error;
+
+ mutex_init(&rmi_bus_mutex);
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+ rmi_debugfs_root = debugfs_create_dir(rmi_bus_type.name, NULL);
+ if (!rmi_debugfs_root)
+ pr_err("%s: Failed to create debugfs root.\n",
+ __func__);
+ else if (IS_ERR(rmi_debugfs_root)) {
+ pr_err("%s: Kernel may not contain debugfs support, code=%ld\n",
+ __func__, PTR_ERR(rmi_debugfs_root));
+ rmi_debugfs_root = NULL;
+ }
+ }
+
+ error = bus_register(&rmi_bus_type);
+ if (error < 0) {
+ pr_err("%s: error registering the RMI bus: %d\n", __func__,
+ error);
+ return error;
+ }
+ pr_debug("%s: successfully registered RMI bus.\n", __func__);
+
+ return 0;
+}
+
+static void __exit rmi_bus_exit(void)
+{
+ /* We should only ever get here if all drivers are unloaded, so
+ * all we have to do at this point is unregister ourselves.
+ */
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG) && rmi_debugfs_root)
+ debugfs_remove(rmi_debugfs_root);
+ bus_unregister(&rmi_bus_type);
+}
+
+module_init(rmi_bus_init);
+module_exit(rmi_bus_exit);
+
+MODULE_AUTHOR("Christopher Heiny <[email protected]");
+MODULE_DESCRIPTION("RMI bus");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
new file mode 100644
index 0000000..05a73ae
--- /dev/null
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -0,0 +1,1663 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This driver adds support for generic RMI4 devices from Synpatics. It
+ * implements the mandatory f01 RMI register and depends on the presence of
+ * other required RMI functions.
+ *
+ * The RMI4 specification can be found here (URL split after files/ for
+ * style reasons):
+ * http://www.synaptics.com/sites/default/files/
+ * 511-000136-01-Rev-E-RMI4%20Intrfacing%20Guide.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/gpio.h>
+#include <linux/kconfig.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/input.h>
+#include "rmi_driver.h"
+#include "rmi_f01.h"
+
+#define HAS_NONSTANDARD_PDT_MASK 0x40
+#define RMI4_MAX_PAGE 0xff
+#define RMI4_PAGE_SIZE 0x100
+
+#define RMI_DEVICE_RESET_CMD 0x01
+#define DEFAULT_RESET_DELAY_MS 100
+
+#define DEFAULT_POLL_INTERVAL_MS 13
+
+#define IRQ_DEBUG(data) (IS_ENABLED(CONFIG_RMI4_DEBUG) && data->irq_debug)
+
+#ifdef CONFIG_RMI4_DEBUG
+struct driver_debugfs_data {
+ bool done;
+ struct rmi_device *rmi_dev;
+};
+
+static int debug_open(struct inode *inodep, struct file *filp)
+{
+ struct driver_debugfs_data *data;
+ struct rmi_device *rmi_dev;
+
+ rmi_dev = inodep->i_private;
+ data = kzalloc(sizeof(struct driver_debugfs_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->rmi_dev = inodep->i_private;
+ filp->private_data = data;
+ return 0;
+}
+
+static int debug_release(struct inode *inodep, struct file *filp)
+{
+ kfree(filp->private_data);
+ return 0;
+}
+
+static ssize_t delay_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ struct driver_debugfs_data *data = filp->private_data;
+ struct rmi_device_platform_data *pdata =
+ data->rmi_dev->phys->dev->platform_data;
+ int retval;
+ char *local_buf;
+
+ if (data->done)
+ return 0;
+
+ data->done = 1;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ retval = snprintf(local_buf, size, "%d %d %d %d %d\n",
+ pdata->spi_data.read_delay_us, pdata->spi_data.write_delay_us,
+ pdata->spi_data.block_delay_us,
+ pdata->spi_data.pre_delay_us, pdata->spi_data.post_delay_us);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ retval = -EFAULT;
+ kfree(local_buf);
+
+ return retval;
+}
+
+static ssize_t delay_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset) {
+ struct driver_debugfs_data *data = filp->private_data;
+ struct rmi_device_platform_data *pdata =
+ data->rmi_dev->phys->dev->platform_data;
+ int retval;
+ char *local_buf;
+ 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;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval) {
+ kfree(local_buf);
+ return -EFAULT;
+ }
+
+ retval = sscanf(local_buf, "%u %u %u %u %u", &new_read_delay,
+ &new_write_delay, &new_block_delay,
+ &new_pre_delay, &new_post_delay);
+ kfree(local_buf);
+
+ if (retval != 5) {
+ dev_err(&data->rmi_dev->dev,
+ "Incorrect number of values provided for delay.");
+ return -EINVAL;
+ }
+ dev_dbg(&data->rmi_dev->dev,
+ "Setting delays to %u %u %u %u %u.\n", new_read_delay,
+ new_write_delay, new_block_delay, new_pre_delay,
+ new_post_delay);
+ pdata->spi_data.read_delay_us = new_read_delay;
+ pdata->spi_data.write_delay_us = new_write_delay;
+ pdata->spi_data.block_delay_us = new_block_delay;
+ pdata->spi_data.pre_delay_us = new_pre_delay;
+ pdata->spi_data.post_delay_us = new_post_delay;
+
+ return size;
+}
+
+static const struct file_operations delay_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_open,
+ .release = debug_release,
+ .read = delay_read,
+ .write = delay_write,
+};
+
+#define PHYS_NAME "phys"
+
+static ssize_t phys_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ struct driver_debugfs_data *data = filp->private_data;
+ struct rmi_phys_info *info = &data->rmi_dev->phys->info;
+ int retval;
+ char *local_buf;
+
+ if (data->done)
+ return 0;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size,
+ "%-5s %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);
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ retval = -EFAULT;
+ kfree(local_buf);
+
+ return retval;
+}
+
+static const struct file_operations phys_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_open,
+ .release = debug_release,
+ .read = phys_read,
+};
+
+static ssize_t attn_count_read(struct file *filp, char __user *buffer,
+ size_t size, loff_t *offset) {
+ struct driver_debugfs_data *data = filp->private_data;
+ struct rmi_driver_data *rmi_data = dev_get_drvdata(&data->rmi_dev->dev);
+ int retval;
+ char *local_buf;
+
+ if (data->done)
+ return 0;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%d\n",
+ rmi_data->attn_count.counter);
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ retval = -EFAULT;
+ kfree(local_buf);
+
+ return retval;
+}
+
+static const struct file_operations attn_count_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_open,
+ .release = debug_release,
+ .read = attn_count_read,
+};
+
+static ssize_t irq_debug_read(struct file *filp, char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char *local_buf;
+ struct driver_debugfs_data *data = filp->private_data;
+ struct rmi_driver_data *rmi_data = dev_get_drvdata(&data->rmi_dev->dev);
+
+ if (data->done)
+ return 0;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u\n", rmi_data->irq_debug);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ retval = -EFAULT;
+ kfree(local_buf);
+
+ return retval;
+}
+
+static ssize_t irq_debug_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char *local_buf;
+ unsigned int new_value;
+ struct driver_debugfs_data *data = filp->private_data;
+ struct rmi_driver_data *rmi_data = dev_get_drvdata(&data->rmi_dev->dev);
+
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval) {
+ kfree(local_buf);
+ return -EFAULT;
+ }
+
+ retval = sscanf(local_buf, "%u", &new_value);
+ if (retval != 1 || new_value > 1)
+ retval = -EINVAL;
+ kfree(local_buf);
+ rmi_data->irq_debug = new_value;
+
+ return size;
+}
+
+static const struct file_operations irq_debug_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_open,
+ .release = debug_release,
+ .read = irq_debug_read,
+ .write = irq_debug_write,
+};
+
+static int setup_debugfs(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_phys_info *info = &rmi_dev->phys->info;
+ int retval = 0;
+
+ if (!rmi_dev->debugfs_root)
+ return -ENODEV;
+
+ if (IS_ENABLED(CONFIG_RMI4_SPI) && !strncmp("spi", info->proto, 3)) {
+ data->debugfs_delay = debugfs_create_file("delay",
+ RMI_RW_ATTR, rmi_dev->debugfs_root, rmi_dev,
+ &delay_fops);
+ if (!data->debugfs_delay || IS_ERR(data->debugfs_delay)) {
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs delay.\n");
+ data->debugfs_delay = NULL;
+ }
+ }
+
+ data->debugfs_phys = debugfs_create_file(PHYS_NAME, RMI_RO_ATTR,
+ rmi_dev->debugfs_root, rmi_dev, &phys_fops);
+ if (!data->debugfs_phys || IS_ERR(data->debugfs_phys)) {
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs phys.\n");
+ data->debugfs_phys = NULL;
+ }
+
+ data->debugfs_irq = debugfs_create_file("irq_debug",
+ RMI_RW_ATTR,
+ rmi_dev->debugfs_root,
+ rmi_dev, &irq_debug_fops);
+ if (!data->debugfs_irq || IS_ERR(data->debugfs_irq)) {
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs irq_debug.\n");
+ data->debugfs_irq = NULL;
+ }
+
+ data->debugfs_attn_count = debugfs_create_file("attn_count",
+ RMI_RO_ATTR,
+ rmi_dev->debugfs_root,
+ rmi_dev, &attn_count_fops);
+ if (!data->debugfs_phys || IS_ERR(data->debugfs_attn_count)) {
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs attn_count.\n");
+ data->debugfs_attn_count = NULL;
+ }
+
+ return retval;
+}
+
+static void teardown_debugfs(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ if (IS_ENABLED(CONFIG_RMI4_SPI) && data->debugfs_delay)
+ debugfs_remove(data->debugfs_delay);
+ if (data->debugfs_phys)
+ debugfs_remove(data->debugfs_phys);
+ if (data->debugfs_irq)
+ debugfs_remove(data->debugfs_irq);
+ if (data->debugfs_attn_count)
+ debugfs_remove(data->debugfs_attn_count);
+}
+#else
+#define teardown_debugfs(rmi_dev)
+#define setup_debugfs(rmi_dev) 0
+#endif
+
+static irqreturn_t rmi_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;
+ struct rmi_driver_data *data;
+
+ data = dev_get_drvdata(&rmi_dev->dev);
+
+ if IRQ_DEBUG(data)
+ dev_dbg(phys->dev, "ATTN gpio, value: %d.\n",
+ gpio_get_value(pdata->attn_gpio));
+
+ if (gpio_get_value(pdata->attn_gpio) == pdata->attn_polarity) {
+ atomic_inc(&data->attn_count);
+ if (driver && driver->irq_handler && rmi_dev)
+ driver->irq_handler(rmi_dev, irq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int process_interrupt_requests(struct rmi_device *rmi_dev);
+
+static void rmi_poll_work(struct work_struct *work)
+{
+ struct rmi_driver_data *data =
+ container_of(work, struct rmi_driver_data, poll_work);
+ struct rmi_device *rmi_dev = data->rmi_dev;
+
+ process_interrupt_requests(rmi_dev);
+}
+
+/* This is the timer function for polling - it simply has to schedule work
+ * and restart the timer. */
+static enum hrtimer_restart rmi_poll_timer(struct hrtimer *timer)
+{
+ struct rmi_driver_data *data =
+ container_of(timer, struct rmi_driver_data, poll_timer);
+
+ if (!data->enabled)
+ return HRTIMER_NORESTART;
+ if (!work_pending(&data->poll_work))
+ schedule_work(&data->poll_work);
+ hrtimer_start(&data->poll_timer, data->poll_interval, HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+}
+
+static int enable_polling(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ dev_dbg(&rmi_dev->dev, "Polling enabled.\n");
+ INIT_WORK(&data->poll_work, rmi_poll_work);
+ hrtimer_init(&data->poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ data->poll_timer.function = rmi_poll_timer;
+ hrtimer_start(&data->poll_timer, data->poll_interval, HRTIMER_MODE_REL);
+
+ return 0;
+}
+
+static void disable_polling(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ dev_dbg(&rmi_dev->dev, "Polling disabled.\n");
+ hrtimer_cancel(&data->poll_timer);
+ cancel_work_sync(&data->poll_work);
+}
+
+static void disable_sensor(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ if (!data->enabled)
+ return;
+
+ if (!data->irq)
+ disable_polling(rmi_dev);
+
+ if (rmi_dev->phys->disable_device)
+ rmi_dev->phys->disable_device(rmi_dev->phys);
+
+ if (data->irq) {
+ disable_irq(data->irq);
+ free_irq(data->irq, rmi_dev->phys);
+ }
+
+ data->enabled = false;
+}
+
+static int enable_sensor(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_phys_device *rmi_phys;
+ int retval = 0;
+ struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
+
+ if (data->enabled)
+ return 0;
+
+ if (rmi_dev->phys->enable_device) {
+ retval = rmi_dev->phys->enable_device(rmi_dev->phys);
+ if (retval)
+ return retval;
+ }
+
+ rmi_phys = rmi_dev->phys;
+ if (data->irq) {
+ retval = request_threaded_irq(data->irq,
+ rmi_phys->hard_irq ? rmi_phys->hard_irq : NULL,
+ rmi_phys->irq_thread ?
+ rmi_phys->irq_thread : rmi_irq_thread,
+ data->irq_flags,
+ dev_name(&rmi_dev->dev), rmi_phys);
+ if (retval)
+ return retval;
+ } else {
+ retval = enable_polling(rmi_dev);
+ if (retval < 0)
+ return retval;
+ }
+
+ data->enabled = true;
+
+ if (!pdata->level_triggered &&
+ gpio_get_value(pdata->attn_gpio) == pdata->attn_polarity)
+ retval = process_interrupt_requests(rmi_dev);
+
+ return retval;
+}
+
+/* sysfs show and store fns for driver attributes */
+
+static ssize_t rmi_driver_bsr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+ rmi_dev = to_rmi_device(dev);
+ data = dev_get_drvdata(&rmi_dev->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 = dev_get_drvdata(&rmi_dev->dev);
+
+ /* need to convert the string data to an actual value */
+ retval = strict_strtoul(buf, 10, &val);
+ if (retval < 0 || val > 255) {
+ dev_err(dev, "Invalid value '%s' written to BSR.\n", buf);
+ return -EINVAL;
+ }
+
+ retval = rmi_write(rmi_dev, BSR_LOCATION, (u8)val);
+ if (retval < 0) {
+ dev_err(dev, "%s : failed to write bsr %lu to %#06x\n",
+ __func__, val, BSR_LOCATION);
+ return retval;
+ }
+
+ data->bsr = val;
+
+ return count;
+}
+
+static ssize_t rmi_driver_enabled_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+
+ rmi_dev = to_rmi_device(dev);
+ data = dev_get_drvdata(&rmi_dev->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 = dev_get_drvdata(&rmi_dev->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;
+}
+
+/** This sysfs attribute is deprecated, and will be removed in a future release.
+ */
+static struct device_attribute attrs[] = {
+ __ATTR(enabled, RMI_RW_ATTR,
+ rmi_driver_enabled_show, rmi_driver_enabled_store),
+};
+
+static struct device_attribute bsr_attribute = __ATTR(bsr, RMI_RW_ATTR,
+ rmi_driver_bsr_show, rmi_driver_bsr_store);
+
+static void rmi_free_function_list(struct rmi_device *rmi_dev)
+{
+ struct rmi_function_container *entry, *n;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ if (!data) {
+ dev_err(&rmi_dev->dev, "WTF: No driver data in %s\n", __func__);
+ return;
+ }
+
+ data->f01_container = NULL;
+
+ if (list_empty(&data->rmi_functions.list))
+ return;
+
+ list_for_each_entry_safe(entry, n, &data->rmi_functions.list, list) {
+ device_unregister(&entry->dev);
+ list_del(&entry->list);
+ }
+}
+
+static void release_fc_device(struct device *dev)
+{
+ dev_dbg(dev, "REMOVING KOBJ!");
+ kobject_put(&dev->kobj);
+}
+
+static int reset_one_function(struct rmi_function_container *fc)
+{
+ struct rmi_function_handler *fh;
+ int retval = 0;
+
+ if (!fc || !fc->dev.driver)
+ return 0;
+
+ fh = to_rmi_function_handler(fc->dev.driver);
+ if (fh->reset) {
+ retval = fh->reset(fc);
+ if (retval < 0)
+ dev_err(&fc->dev, "Reset failed with code %d.\n",
+ retval);
+ }
+
+ return retval;
+}
+
+static int configure_one_function(struct rmi_function_container *fc)
+{
+ struct rmi_function_handler *fh;
+ int retval = 0;
+
+ if (!fc || !fc->dev.driver)
+ return 0;
+
+ fh = to_rmi_function_handler(fc->dev.driver);
+ if (fh->config) {
+ retval = fh->config(fc);
+ if (retval < 0)
+ dev_err(&fc->dev, "Config failed with code %d.\n",
+ retval);
+ }
+
+ return retval;
+}
+
+static int rmi_driver_process_reset_requests(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_function_container *entry;
+ int retval;
+
+ if (list_empty(&data->rmi_functions.list))
+ return 0;
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list) {
+ retval = reset_one_function(entry);
+ if (retval < 0)
+ return retval;
+ }
+
+ return 0;
+}
+
+static int rmi_driver_process_config_requests(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_function_container *entry;
+ int retval;
+
+ if (list_empty(&data->rmi_functions.list))
+ return 0;
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list) {
+ retval = configure_one_function(entry);
+ if (retval < 0)
+ return retval;
+ }
+
+ return 0;
+}
+
+static void process_one_interrupt(struct rmi_function_container *fc,
+ unsigned long *irq_status, struct rmi_driver_data *data)
+{
+ struct rmi_function_handler *fh;
+ DECLARE_BITMAP(irq_bits, data->num_of_irq_regs);
+
+ if (!fc || !fc->dev.driver)
+ return;
+
+ fh = to_rmi_function_handler(fc->dev.driver);
+ if (fc->irq_mask && fh->attention) {
+ bitmap_and(irq_bits, irq_status, fc->irq_mask,
+ data->irq_count);
+ if (!bitmap_empty(irq_bits, data->irq_count))
+ fh->attention(fc, irq_bits);
+ }
+
+}
+
+static int process_interrupt_requests(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct device *dev = &rmi_dev->dev;
+ struct rmi_function_container *entry;
+ int error;
+
+ error = rmi_read_block(rmi_dev,
+ data->f01_container->fd.data_base_addr + 1,
+ data->irq_status, data->num_of_irq_regs);
+ if (error < 0) {
+ dev_err(dev, "Failed to read irqs, code=%d\n", error);
+ return error;
+ }
+
+ mutex_lock(&data->irq_mutex);
+ bitmap_and(data->irq_status, data->irq_status, data->current_irq_mask,
+ data->irq_count);
+ /* At this point, irq_status has all bits that are set in the
+ * interrupt status register and are enabled.
+ */
+ mutex_unlock(&data->irq_mutex);
+
+ /* It would be nice to be able to use irq_chip to handle these
+ * nested IRQs. Unfortunately, most of the current customers for
+ * this driver are using older kernels (3.0.x) that don't support
+ * the features required for that. Once they've shifted to more
+ * recent kernels (say, 3.3 and higher), this should be switched to
+ * use irq_chip.
+ */
+ list_for_each_entry(entry, &data->rmi_functions.list, list) {
+ if (entry->irq_mask)
+ process_one_interrupt(entry, data->irq_status,
+ data);
+ }
+
+ return 0;
+}
+
+/**
+ * rmi_driver_set_input_params - set input device id and other data.
+ *
+ * @rmi_dev: Pointer to an RMI device
+ * @input: Pointer to input device
+ *
+ */
+static int rmi_driver_set_input_params(struct rmi_device *rmi_dev,
+ struct input_dev *input)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ input->name = SYNAPTICS_INPUT_DEVICE_NAME;
+ input->id.vendor = SYNAPTICS_VENDOR_ID;
+ input->id.product = data->board;
+ input->id.version = data->rev;
+ input->id.bustype = BUS_RMI;
+ return 0;
+}
+
+/**
+ * This pair of functions allows functions like function 54 to request to have
+ * other interrupts 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,
+ unsigned long *new_ints)
+{
+ int retval = 0;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct device *dev = &rmi_dev->dev;
+
+ mutex_lock(&data->irq_mutex);
+ if (!data->irq_stored) {
+ /* Save current enabled interrupts */
+ retval = rmi_read_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ data->irq_mask_store, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to read enabled interrupts!",
+ __func__);
+ goto error_unlock;
+ }
+ /*
+ * Disable every interrupt except for function 54
+ * TODO:Will also want to not disable function 1-like functions.
+ * No need to take care of this now, since there's no good way
+ * to identify them.
+ */
+ retval = rmi_write_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ new_ints, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to change enabled interrupts!",
+ __func__);
+ goto error_unlock;
+ }
+ bitmap_copy(data->current_irq_mask, new_ints, data->irq_count);
+ data->irq_stored = true;
+ } else {
+ retval = -ENOSPC; /* No space to store IRQs.*/
+ dev_err(dev, "Attempted to save IRQs when already stored!");
+ }
+
+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 = dev_get_drvdata(&rmi_dev->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_irq_handler(struct rmi_device *rmi_dev, int irq)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ might_sleep();
+ /* Can get called before the driver is fully ready to deal with
+ * interrupts.
+ */
+ if (!data || !data->f01_container) {
+ dev_dbg(&rmi_dev->dev,
+ "Not ready to handle interrupts yet!\n");
+ return 0;
+ }
+
+ return process_interrupt_requests(rmi_dev);
+}
+
+static int rmi_driver_reset_handler(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ int error;
+
+ /* Can get called before the driver is fully ready to deal with
+ * this situation.
+ */
+ if (!data || !data->f01_container) {
+ dev_warn(&rmi_dev->dev,
+ "Not ready to handle reset yet!\n");
+ return 0;
+ }
+
+ error = rmi_driver_process_reset_requests(rmi_dev);
+ if (error < 0)
+ return error;
+
+
+ error = rmi_driver_process_config_requests(rmi_dev);
+ if (error < 0)
+ return error;
+
+ if (data->irq_stored) {
+ error = rmi_driver_irq_restore(rmi_dev);
+ if (error < 0)
+ return error;
+ }
+
+ return 0;
+}
+
+/*
+ * Construct a function's IRQ mask. This should be called once and stored.
+ */
+int rmi_driver_irq_get_mask(struct rmi_device *rmi_dev,
+ struct rmi_function_container *fc) {
+ int i;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ /* call devm_kcalloc when it will be defined in kernel in future */
+ fc->irq_mask = devm_kzalloc(&rmi_dev->dev,
+ BITS_TO_LONGS(data->irq_count)*sizeof(unsigned long),
+ GFP_KERNEL);
+
+ if (fc->irq_mask) {
+ for (i = 0; i < fc->num_of_irqs; i++)
+ set_bit(fc->irq_pos+i, fc->irq_mask);
+ return 0;
+ } else
+ return -ENOMEM;
+}
+
+static int init_function_device(struct rmi_device *rmi_dev,
+ struct rmi_function_container *fc)
+{
+ int retval;
+
+ /* This memset might not be what we want to do... */
+ memset(&(fc->dev), 0, sizeof(struct device));
+ dev_set_name(&(fc->dev), "%s.fn%02x", dev_name(&rmi_dev->dev),
+ fc->fd.function_number);
+ fc->dev.release = release_fc_device;
+
+ fc->dev.parent = &rmi_dev->dev;
+ fc->dev.type = &rmi_function_type;
+ fc->dev.bus = &rmi_bus_type;
+ dev_dbg(&rmi_dev->dev, "Register F%02X.\n", fc->fd.function_number);
+ retval = device_register(&fc->dev);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "Failed device_register for F%02X.\n",
+ fc->fd.function_number);
+ return retval;
+ }
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+ char dirname[12];
+
+ snprintf(dirname, 12, "F%02X", fc->fd.function_number);
+ fc->debugfs_root = debugfs_create_dir(dirname,
+ rmi_dev->debugfs_root);
+ if (!fc->debugfs_root)
+ dev_warn(&fc->dev, "Failed to create debugfs dir.\n");
+ }
+
+ return 0;
+}
+
+static int create_function_container(struct rmi_device *rmi_dev,
+ struct pdt_entry *pdt_ptr,
+ int *current_irq_count,
+ u16 page_start)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_function_container *fc = NULL;
+ int retval = 0;
+ struct device *dev = &rmi_dev->dev;
+ struct rmi_device_platform_data *pdata;
+
+ pdata = to_rmi_platform_data(rmi_dev);
+
+ dev_dbg(dev, "Initializing F%02X for %s.\n", pdt_ptr->function_number,
+ pdata->sensor_name);
+
+ fc = devm_kzalloc(dev, sizeof(struct rmi_function_container),
+ GFP_KERNEL);
+ if (!fc) {
+ dev_err(dev, "Failed to allocate F%02X container.\n",
+ pdt_ptr->function_number);
+ return -ENOMEM;
+ }
+
+ 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_function_device(rmi_dev, fc);
+ if (retval < 0) {
+ dev_err(dev, "Failed to initialize F%02X device.\n",
+ pdt_ptr->function_number);
+ goto error_free_data;
+ }
+
+ INIT_LIST_HEAD(&fc->list);
+ /* we need to ensure that F01 is at the head of the list.
+ */
+ if (pdt_ptr->function_number == 0x01) {
+ list_add(&fc->list, &data->rmi_functions.list);
+ data->f01_container = fc;
+ } else
+ list_add_tail(&fc->list, &data->rmi_functions.list);
+ return 0;
+
+error_free_data:
+ return retval;
+}
+
+/*
+ * Once we find F01, we need to see if we're in bootloader mode. If we are,
+ * we'll stop scanning the PDT with the current page (usually 0x00 in that
+ * case).
+ */
+static void check_bootloader_mode(struct rmi_device *rmi_dev,
+ struct pdt_entry *pdt_ptr,
+ u16 page_start)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct f01_device_status device_status;
+ int retval = 0;
+
+ retval = rmi_read(rmi_dev, pdt_ptr->data_base_addr+page_start,
+ &device_status);
+ if (retval < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read device status.\n");
+ return;
+ }
+ data->f01_bootloader_mode = device_status.flash_prog;
+ if (device_status.flash_prog)
+ dev_warn(&rmi_dev->dev,
+ "WARNING: RMI4 device is in bootloader mode!\n");
+
+}
+
+/*
+ * 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.
+ *
+ * At this time, we also reflash the device if (a) in kernel reflashing is
+ * enabled, and (b) the reflash module decides it requires reflashing.
+ *
+ * We have to do this before actually building the PDT because the reflash
+ * might cause various registers to move around.
+ */
+static int reset_and_reflash(struct rmi_device *rmi_dev)
+{
+ struct pdt_entry pdt_entry;
+ int page;
+ struct device *dev = &rmi_dev->dev;
+ bool done = false;
+ bool has_f01 = false;
+ bool has_f34 = false;
+ struct pdt_entry f34_pdt, f01_pdt;
+ 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, &pdt_entry,
+ sizeof(pdt_entry));
+ if (retval != sizeof(pdt_entry)) {
+ dev_err(dev, "Read PDT entry at %#06x 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);
+ if (IS_ENABLED(CONFIG_RMI4_FWLIB))
+ memcpy(&f01_pdt, &pdt_entry,
+ sizeof(pdt_entry));
+ else
+ done = true;
+ has_f01 = true;
+ break;
+ } else if (IS_ENABLED(CONFIG_RMI4_FWLIB) &&
+ pdt_entry.function_number == 0x34) {
+ memcpy(&f34_pdt, &pdt_entry, sizeof(pdt_entry));
+ has_f34 = true;
+ }
+ }
+ }
+
+ if (!has_f01) {
+ dev_warn(dev, "WARNING: Failed to find F01 for initial reset.\n");
+ return -ENODEV;
+ }
+
+ if (IS_ENABLED(CONFIG_RMI4_FWLIB)) {
+ if (has_f34)
+ rmi4_fw_update(rmi_dev, &f01_pdt, &f34_pdt);
+ else
+ dev_warn(dev, "WARNING: No F34, firmware update will not be done.\n");
+ }
+
+ return 0;
+}
+
+
+/* extract product ID */
+void get_prod_id(struct rmi_device *rmi_dev, struct rmi_driver_data *drvdata)
+{
+ struct device *dev = &rmi_dev->dev;
+ int retval;
+ int board = 0, rev = 0;
+ int i;
+ static const char * const pattern[] = {
+ "tm%4d-%d", "s%4d-%d", "s%4d-ver%1d"};
+ u8 product_id[RMI_PRODUCT_ID_LENGTH+1];
+
+ retval = rmi_read_block(rmi_dev,
+ drvdata->f01_container->fd.query_base_addr+
+ sizeof(struct f01_basic_queries),
+ product_id, RMI_PRODUCT_ID_LENGTH);
+ if (retval < 0) {
+ dev_err(dev, "Failed to read product id, code=%d!", retval);
+ return;
+ }
+ product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
+
+ for (i = 0; i < sizeof(product_id); i++)
+ product_id[i] = tolower(product_id[i]);
+
+ for (i = 0; i < sizeof(pattern); i++) {
+ retval = sscanf(product_id, pattern[i], &board, &rev);
+ if (retval)
+ break;
+ }
+ /* save board and rev data in the rmi_driver_data */
+ drvdata->board = board;
+ drvdata->rev = rev;
+ dev_dbg(dev, "Rmi_driver getProdID, set board: %d rev: %d\n",
+ drvdata->board, drvdata->rev);
+}
+
+static int rmi_scan_pdt(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data;
+ struct pdt_entry pdt_entry;
+ int page;
+ struct device *dev = &rmi_dev->dev;
+ int irq_count = 0;
+ bool done = false;
+ int i;
+ int retval;
+
+ dev_dbg(dev, "Scanning PDT...\n");
+
+ data = dev_get_drvdata(&rmi_dev->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, &pdt_entry,
+ sizeof(pdt_entry));
+ if (retval != sizeof(pdt_entry)) {
+ dev_err(dev, "Read of PDT entry at %#06x failed.\n",
+ i);
+ goto error_exit;
+ }
+
+ if (RMI4_END_OF_PDT(pdt_entry.function_number))
+ break;
+
+ dev_dbg(dev, "Found F%02X on page %#04x\n",
+ pdt_entry.function_number, page);
+ done = false;
+
+ if (pdt_entry.function_number == 0x01)
+ check_bootloader_mode(rmi_dev, &pdt_entry,
+ page_start);
+
+
+ retval = create_function_container(rmi_dev,
+ &pdt_entry, &irq_count, page_start);
+
+ if (retval)
+ goto error_exit;
+
+ if (pdt_entry.function_number == 0x01)
+ get_prod_id(rmi_dev, data);
+ }
+ done = done || data->f01_bootloader_mode;
+ }
+ data->irq_count = irq_count;
+ data->num_of_irq_regs = (irq_count + 7) / 8;
+ dev_dbg(dev, "%s: Done with PDT scan.\n", __func__);
+ retval = 0;
+
+error_exit:
+ mutex_unlock(&data->pdt_mutex);
+ return retval;
+}
+
+static int f01_notifier_call(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+ struct rmi_function_container *fc;
+
+ if (dev->type != &rmi_function_type)
+ return 0;
+
+ fc = to_rmi_function_container(dev);
+ if (fc->fd.function_number != 0x01)
+ return 0;
+
+ switch (action) {
+ case BUS_NOTIFY_BOUND_DRIVER:
+ dev_dbg(dev, "%s: F01 driver bound.\n", __func__);
+ enable_sensor(fc->rmi_dev);
+ break;
+ case BUS_NOTIFY_UNBIND_DRIVER:
+ dev_dbg(dev, "%s: F01 driver going away.\n", __func__);
+ disable_sensor(fc->rmi_dev);
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block rmi_bus_notifier = {
+ .notifier_call = f01_notifier_call,
+};
+
+#ifdef CONFIG_PM
+static int suspend_one_device(struct rmi_function_container *fc)
+{
+ struct rmi_function_handler *fh;
+ int retval = 0;
+
+ if (!fc->dev.driver)
+ return 0;
+
+ fh = to_rmi_function_handler(fc->dev.driver);
+
+ if (fh->suspend) {
+ retval = fh->suspend(fc);
+ if (retval < 0)
+ dev_err(&fc->dev, "Suspend failed, code: %d",
+ retval);
+ }
+
+ return retval;
+}
+
+static int rmi_driver_suspend(struct device *dev)
+{
+ struct rmi_driver_data *data;
+ struct rmi_function_container *entry;
+ int retval = 0;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ data = dev_get_drvdata(&rmi_dev->dev);
+
+ mutex_lock(&data->suspend_mutex);
+ if (data->suspended)
+ goto exit;
+
+ if (data->pre_suspend) {
+ retval = data->pre_suspend(data->pm_data);
+ if (retval)
+ goto exit;
+ }
+
+ disable_sensor(rmi_dev);
+
+ /** Do it backwards so F01 comes last. */
+ list_for_each_entry_reverse(entry, &data->rmi_functions.list, list)
+ if (suspend_one_device(entry) < 0)
+ goto exit;
+
+ data->suspended = true;
+
+ if (data->post_suspend)
+ retval = data->post_suspend(data->pm_data);
+
+exit:
+ mutex_unlock(&data->suspend_mutex);
+ return retval;
+}
+
+static int resume_one_device(struct rmi_function_container *fc)
+{
+ struct rmi_function_handler *fh;
+ int retval = 0;
+
+ if (!fc->dev.driver)
+ return 0;
+
+ fh = to_rmi_function_handler(fc->dev.driver);
+
+ if (fh->resume) {
+ retval = fh->resume(fc);
+ if (retval < 0)
+ dev_err(&fc->dev, "Resume failed, code: %d",
+ retval);
+ }
+
+ return retval;
+}
+
+static int rmi_driver_resume(struct device *dev)
+{
+ struct rmi_driver_data *data;
+ struct rmi_function_container *entry;
+ int retval = 0;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ data = dev_get_drvdata(&rmi_dev->dev);
+ mutex_lock(&data->suspend_mutex);
+ if (!data->suspended)
+ goto exit;
+
+ if (data->pre_resume) {
+ retval = data->pre_resume(data->pm_data);
+ if (retval)
+ goto exit;
+ }
+
+ /** Do it forwards, so F01 comes first. */
+ list_for_each_entry(entry, &data->rmi_functions.list, list) {
+ if (resume_one_device(entry) < 0)
+ goto exit;
+ }
+
+ retval = enable_sensor(rmi_dev);
+ if (retval)
+ goto exit;
+
+
+ if (data->post_resume) {
+ retval = data->post_resume(data->pm_data);
+ if (retval)
+ goto exit;
+ }
+
+ data->suspended = false;
+exit:
+ mutex_unlock(&data->suspend_mutex);
+ return retval;
+}
+
+#endif /* CONFIG_PM */
+
+static int __devexit rmi_driver_remove(struct device *dev)
+{
+ struct rmi_driver_data *data;
+ int i;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ data = dev_get_drvdata(&rmi_dev->dev);
+
+ disable_sensor(rmi_dev);
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG))
+ teardown_debugfs(rmi_dev);
+
+ rmi_free_function_list(rmi_dev);
+ for (i = 0; i < ARRAY_SIZE(attrs); i++)
+ device_remove_file(&rmi_dev->dev, &attrs[i]);
+ if (data->pdt_props.has_bsr)
+ device_remove_file(&rmi_dev->dev, &bsr_attribute);
+ return 0;
+}
+
+static int __devinit rmi_driver_probe(struct device *dev)
+{
+ struct rmi_driver *rmi_driver;
+ struct rmi_driver_data *data = NULL;
+ struct rmi_function_container *fc;
+ struct rmi_device_platform_data *pdata;
+ int retval = 0;
+ int attr_count = 0;
+ struct rmi_device *rmi_dev;
+
+ dev_dbg(dev, "%s: Starting probe.\n", __func__);
+ if (!dev->driver) {
+ dev_err(dev, "No driver for RMI4 device during probe!\n");
+ return -ENODEV;
+ }
+
+ if (dev->type != &rmi_sensor_type) {
+ dev_dbg(dev, "Not a sensor device.\n");
+ return 1;
+ }
+
+ rmi_dev = to_rmi_device(dev);
+ rmi_driver = to_rmi_driver(dev->driver);
+ rmi_dev->driver = rmi_driver;
+
+ pdata = to_rmi_platform_data(rmi_dev);
+
+ data = devm_kzalloc(dev, 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);
+ data->rmi_dev = rmi_dev;
+ dev_set_drvdata(&rmi_dev->dev, data);
+ mutex_init(&data->pdt_mutex);
+
+ /* Right before a warm boot, the sensor might be in some unusual state,
+ * such as F54 diagnostics, or F34 bootloader mode. In order to clear
+ * the sensor to a known state, we issue a initial reset to clear any
+ * previous settings and force it into normal operation.
+ *
+ * For a number of reasons, this initial reset may fail to return
+ * within the specified time, but we'll still be able to bring up the
+ * driver normally after that failure. This occurs most commonly in
+ * a cold boot situation (where then firmware takes longer to come up
+ * than from a warm boot) and the reset_delay_ms in the platform data
+ * has been set too short to accomodate that. Since the sensor will
+ * eventually come up and be usable, we don't want to just fail here
+ * and leave the customer's device unusable. So we warn them, and
+ * continue processing.
+ */
+ if (!pdata->reset_delay_ms)
+ pdata->reset_delay_ms = DEFAULT_RESET_DELAY_MS;
+ retval = reset_and_reflash(rmi_dev);
+ if (retval)
+ dev_warn(dev, "RMI initial reset failed! Continuing in spite of this.\n");
+
+
+ retval = rmi_scan_pdt(rmi_dev);
+ if (retval) {
+ dev_err(dev, "PDT scan for %s failed with code %d.\n",
+ pdata->sensor_name, retval);
+ goto err_free_data;
+ }
+
+ if (!data->f01_container) {
+ dev_err(dev, "missing F01 container!\n");
+ retval = -EINVAL;
+ goto err_free_data;
+ }
+
+ list_for_each_entry(fc, &data->rmi_functions.list, list) {
+ retval = rmi_driver_irq_get_mask(rmi_dev, fc);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to create irq_mask.\n",
+ __func__);
+ goto err_free_data;
+ }
+ }
+
+ retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, &data->pdt_props);
+ if (retval < 0) {
+ /* we'll print out a warning and continue since
+ * failure to get the PDT properties is not a cause to fail
+ */
+ dev_warn(dev, "Could not read PDT properties from %#06x. Assuming 0x00.\n",
+ PDT_PROPERTIES_LOCATION);
+ }
+
+ dev_dbg(dev, "%s: Creating sysfs files.", __func__);
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ retval = device_create_file(dev, &attrs[attr_count]);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to create sysfs file %s.\n",
+ __func__, attrs[attr_count].attr.name);
+ goto err_free_data;
+ }
+ }
+ if (data->pdt_props.has_bsr) {
+ retval = device_create_file(dev, &bsr_attribute);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to create sysfs file bsr.\n",
+ __func__);
+ goto err_free_data;
+ }
+ }
+
+ mutex_init(&data->irq_mutex);
+ data->irq_status = devm_kzalloc(dev,
+ BITS_TO_LONGS(data->irq_count)*sizeof(unsigned long),
+ GFP_KERNEL);
+ if (!data->irq_status) {
+ dev_err(dev, "Failed to allocate irq_status.\n");
+ retval = -ENOMEM;
+ goto err_free_data;
+ }
+
+ data->current_irq_mask = devm_kzalloc(dev,
+ data->num_of_irq_regs,
+ GFP_KERNEL);
+ if (!data->current_irq_mask) {
+ dev_err(dev, "Failed to allocate current_irq_mask.\n");
+ retval = -ENOMEM;
+ goto err_free_data;
+ }
+
+ retval = rmi_read_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ data->current_irq_mask, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to read current IRQ mask.\n",
+ __func__);
+ goto err_free_data;
+ }
+
+ data->irq_mask_store = devm_kzalloc(dev,
+ BITS_TO_LONGS(data->irq_count)*sizeof(unsigned long),
+ GFP_KERNEL);
+ if (!data->irq_mask_store) {
+ dev_err(dev, "Failed to allocate mask store.\n");
+ retval = -ENOMEM;
+ goto err_free_data;
+ }
+ if (IS_ENABLED(CONFIG_PM)) {
+ data->pm_data = pdata->pm_data;
+ data->pre_suspend = pdata->pre_suspend;
+ data->post_suspend = pdata->post_suspend;
+ data->pre_resume = pdata->pre_resume;
+ data->post_resume = pdata->post_resume;
+
+ mutex_init(&data->suspend_mutex);
+ }
+
+ if (pdata->attn_gpio) {
+ data->irq = gpio_to_irq(pdata->attn_gpio);
+ if (pdata->level_triggered) {
+ data->irq_flags = IRQF_ONESHOT |
+ ((pdata->attn_polarity == RMI_ATTN_ACTIVE_HIGH)
+ ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW);
+ } else {
+ data->irq_flags =
+ (pdata->attn_polarity == RMI_ATTN_ACTIVE_HIGH)
+ ? IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+ }
+ } else
+ data->poll_interval = ktime_set(0,
+ (pdata->poll_interval_ms ? pdata->poll_interval_ms :
+ DEFAULT_POLL_INTERVAL_MS) * 1000);
+
+ if (data->f01_container->dev.driver) {
+ /* Driver already bound, so enable ATTN now. */
+ enable_sensor(rmi_dev);
+ }
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+ retval = setup_debugfs(rmi_dev);
+ if (retval < 0)
+ dev_warn(&fc->dev, "Failed to setup debugfs. Code: %d.\n",
+ retval);
+ }
+
+ if (IS_ENABLED(CONFIG_RMI4_DEV) && pdata->attn_gpio) {
+ retval = gpio_export(pdata->attn_gpio, false);
+ if (retval) {
+ dev_warn(dev, "WARNING: Failed to export ATTN gpio!\n");
+ retval = 0;
+ } else {
+ retval = gpio_export_link(dev,
+ "attn", pdata->attn_gpio);
+ if (retval) {
+ dev_warn(dev,
+ "WARNING: Failed to symlink ATTN gpio!\n");
+ retval = 0;
+ } else {
+ dev_info(dev, "Exported ATTN GPIO %d.",
+ pdata->attn_gpio);
+ }
+ }
+ }
+
+ return 0;
+
+ err_free_data:
+ rmi_free_function_list(rmi_dev);
+ for (attr_count--; attr_count >= 0; attr_count--)
+ device_remove_file(dev, &attrs[attr_count]);
+ if (data->pdt_props.has_bsr)
+ device_remove_file(dev, &bsr_attribute);
+ return retval;
+}
+
+static UNIVERSAL_DEV_PM_OPS(rmi_driver_pm, rmi_driver_suspend,
+ rmi_driver_resume, NULL);
+
+static struct rmi_driver sensor_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rmi_generic",
+ .bus = &rmi_bus_type,
+ .pm = &rmi_driver_pm,
+ .probe = rmi_driver_probe,
+ .remove = __devexit_p(rmi_driver_remove),
+ },
+ .irq_handler = rmi_driver_irq_handler,
+ .reset_handler = rmi_driver_reset_handler,
+ .store_irq_mask = rmi_driver_irq_save,
+ .restore_irq_mask = rmi_driver_irq_restore,
+ .set_input_params = rmi_driver_set_input_params,
+};
+
+static int __init rmi_driver_init(void)
+{
+ int retval;
+
+ retval = driver_register(&sensor_driver.driver);
+ if (retval) {
+ pr_err("%s: driver register failed, code=%d.\n", __func__,
+ retval);
+ return retval;
+ }
+
+ /* Ask the bus to let us know when drivers are bound to devices. */
+ retval = bus_register_notifier(&rmi_bus_type, &rmi_bus_notifier);
+ if (retval) {
+ pr_err("%s: failed to register bus notifier, code=%d.\n",
+ __func__, retval);
+ return retval;
+ }
+
+ return retval;
+}
+
+static void __exit rmi_driver_exit(void)
+{
+ bus_unregister_notifier(&rmi_bus_type, &rmi_bus_notifier);
+ driver_unregister(&sensor_driver.driver);
+}
+
+module_init(rmi_driver_init);
+module_exit(rmi_driver_exit);
+
+MODULE_AUTHOR("Christopher Heiny <[email protected]");
+MODULE_DESCRIPTION("RMI generic driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
new file mode 100644
index 0000000..f5b221a
--- /dev/null
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _RMI_DRIVER_H
+#define _RMI_DRIVER_H
+
+#include <linux/ctype.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
+
+#define RMI_DRIVER_VERSION "1.6"
+
+#define SYNAPTICS_INPUT_DEVICE_NAME "Synaptics RMI4 Touch Sensor"
+#define SYNAPTICS_VENDOR_ID 0x06cb
+
+#define GROUP(_attrs) { \
+ .attrs = _attrs, \
+}
+
+#define attrify(nm) (&dev_attr_##nm.attr)
+
+#define PDT_PROPERTIES_LOCATION 0x00EF
+#define BSR_LOCATION 0x00FE
+
+struct pdt_properties {
+ u8 reserved_1:6;
+ u8 has_bsr:1;
+ u8 reserved_2:1;
+} __attribute__((__packed__));
+
+struct rmi_driver_data {
+ struct rmi_function_container rmi_functions;
+ struct rmi_device *rmi_dev;
+
+ struct rmi_function_container *f01_container;
+ bool f01_bootloader_mode;
+
+ atomic_t attn_count;
+ bool irq_debug;
+ int irq;
+ int irq_flags;
+ int num_of_irq_regs;
+ int irq_count;
+ unsigned long *irq_status;
+ unsigned long *current_irq_mask;
+ unsigned long *irq_mask_store;
+ bool irq_stored;
+ struct mutex irq_mutex;
+
+ /* Following are used when polling. */
+ struct hrtimer poll_timer;
+ struct work_struct poll_work;
+ ktime_t poll_interval;
+
+ struct mutex pdt_mutex;
+ struct pdt_properties pdt_props;
+ u8 bsr;
+
+ int board;
+ int rev;
+
+ bool enabled;
+#ifdef CONFIG_PM
+ bool suspended;
+ struct mutex suspend_mutex;
+
+ void *pm_data;
+ int (*pre_suspend) (const void *pm_data);
+ int (*post_suspend) (const void *pm_data);
+ int (*pre_resume) (const void *pm_data);
+ int (*post_resume) (const void *pm_data);
+#endif
+
+#ifdef CONFIG_RMI4_DEBUG
+ struct dentry *debugfs_delay;
+ struct dentry *debugfs_phys;
+ struct dentry *debugfs_reg_ctl;
+ struct dentry *debugfs_reg;
+ struct dentry *debugfs_irq;
+ struct dentry *debugfs_attn_count;
+ u16 reg_debug_addr;
+ u8 reg_debug_size;
+#endif
+
+ void *data;
+};
+
+#define PDT_START_SCAN_LOCATION 0x00e9
+#define PDT_END_SCAN_LOCATION 0x0005
+#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
+
+struct pdt_entry {
+ u8 query_base_addr:8;
+ u8 command_base_addr:8;
+ u8 control_base_addr:8;
+ u8 data_base_addr:8;
+ u8 interrupt_source_count:3;
+ u8 bits3and4:2;
+ u8 function_version:2;
+ u8 bit7:1;
+ u8 function_number:8;
+} __attribute__((__packed__));
+
+static inline void copy_pdt_entry_to_fd(struct pdt_entry *pdt,
+ struct rmi_function_descriptor *fd,
+ u16 page_start)
+{
+ fd->query_base_addr = pdt->query_base_addr + page_start;
+ fd->command_base_addr = pdt->command_base_addr + page_start;
+ fd->control_base_addr = pdt->control_base_addr + page_start;
+ fd->data_base_addr = pdt->data_base_addr + page_start;
+ fd->function_number = pdt->function_number;
+ fd->interrupt_source_count = pdt->interrupt_source_count;
+ fd->function_version = pdt->function_version;
+}
+
+#ifdef CONFIG_RMI4_FWLIB
+extern void rmi4_fw_update(struct rmi_device *rmi_dev,
+ struct pdt_entry *f01_pdt, struct pdt_entry *f34_pdt);
+#else
+#define rmi4_fw_update(rmi_dev, f01_pdt, f34_pdt)
+#endif
+
+#endif
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 5588285..5019cb0 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -920,6 +920,7 @@ struct input_keymap_entry {
#define BUS_GSC 0x1A
#define BUS_ATARI 0x1B
#define BUS_SPI 0x1C
+#define BUS_RMI 0x1D

/*
* MT_TOOL types

2012-11-17 04:00:46

by Christopher Heiny

[permalink] [raw]
Subject: [RFC PATCH 06/06] input/rmi4: F11 - 2D touch interface

rmi_f11.c is a driver for 2D touch sensors using the RMI4 protocol. It supports
both touchscreen and touchpad input, in both absolute and relative formats.
Support for Type-B multitouch is the default, Type-A support is included for
certain legacy sensors.


Signed-off-by: Christopher Heiny <[email protected]>

To: Henrik Rydberg <[email protected]>
Cc: Dmitry Torokhov <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>

---

drivers/input/rmi4/rmi_f11.c | 2813 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 2813 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..b9a84bc
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f11.c
@@ -0,0 +1,2813 @@
+/*
+ * Copyright (c) 2011,2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define FUNCTION_DATA f11_data
+#define FNUM 11
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/kconfig.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+#ifdef CONFIG_RMI4_DEBUG
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#endif
+
+#define F11_MAX_NUM_OF_SENSORS 8
+#define F11_MAX_NUM_OF_FINGERS 10
+#define F11_MAX_NUM_OF_TOUCH_SHAPES 16
+
+#define F11_REL_POS_MIN -128
+#define F11_REL_POS_MAX 127
+
+#define FINGER_STATE_MASK 0x03
+
+#define F11_CTRL_SENSOR_MAX_X_POS_OFFSET 6
+#define F11_CTRL_SENSOR_MAX_Y_POS_OFFSET 8
+
+#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 NAME_BUFFER_SIZE 256
+#define FUNCTION_NUMBER 0x11
+
+/** A note about RMI4 F11 register structure.
+ *
+ * There may be one or more individual 2D touch surfaces associated with an
+ * instance for F11. For example, a handheld device might have a touchscreen
+ * display on the front, and a touchpad on the back. F11 represents these touch
+ * surfaces as individual sensors, up to 7 on a given RMI4 device.
+ *
+ * The properties for
+ * a given sensor are described by its query registers. The number of query
+ * registers and the layout of their contents are described by the F11 device
+ * queries as well as the per-sensor query information. The query registers
+ * for sensor[n+1] immediately follow those for sensor[n], so the start address
+ * of the sensor[n+1] queries can only be computed if you know the size of the
+ * sensor[n] queries. Because each of the sensors may have different
+ * properties, the size of the query registers for each sensor must be
+ * calculated on a sensor by sensor basis.
+ *
+ * Similarly, each sensor has control registers that govern its behavior. The
+ * size and layout of the control registers for a given sensor can be determined
+ * by parsing that sensors query registers. The control registers for
+ * sensor[n+1] immediately follow those for sensor[n], so you can only know
+ * the start address for the sensor[n+1] controls if you know the size (and
+ * location) of the sensor[n] controls.
+ *
+ * And in a likewise fashion, each sensor has data registers where it reports
+ * its touch data and other interesting stuff. The size and layout of a
+ * sensors data registers must be determined by parsing its query registers.
+ * The data registers for sensor[n+1] immediately follow those for sensor[n],
+ * so you can only know the start address for the sensor[n+1] controls if you
+ * know the size (and location) of the sensor[n] controls.
+ *
+ * The short story is that we need to read and parse a lot of query
+ * registers in order to determine the attributes of a sensor[0]. Then
+ * we need to use that data to compute the size of the control and data
+ * registers for sensor[0]. Once we have that figured out, we can then do
+ * the same thing for each subsequent sensor.
+ *
+ * The end result is that we have a number of structs that aren't used to
+ * directly generate the input events, but their size, location and contents
+ * are critical to determining where the data we are interested in lives.
+ *
+ * At this time, the driver does not yet comprehend all possible F11
+ * configuration options, but it should be sufficient to cover 99% of RMI4 F11
+ * devices currently in the field.
+ */
+
+/**
+ * @rezero - writing 1 to this will cause the sensor to calibrate to the
+ * current capacitive state.
+ */
+struct f11_2d_commands {
+ bool rezero:1;
+ u8 reserved:7;
+} __attribute__((__packed__));
+
+/** This query is always present, and is on a per device basis. All other
+ * queries are on a per-sensor basis.
+ *
+ * @nbr_of_sensors - the number of 2D sensors on the touch device.
+ * @has_query9 - indicates the F11_2D_Query9 register exists.
+ * @has_query11 - indicates the F11_2D_Query11 register exists.
+ * @has_query12 - indicates the F11_2D_Query12 register exists.
+ */
+struct f11_2d_device_query {
+ u8 nbr_of_sensors:3;
+ bool has_query9:1;
+ bool has_query11:1;
+ bool has_query12:1;
+ bool has_query27:1;
+ bool has_query28:1;
+} __attribute__((__packed__));
+
+/** Query registers 1 through 4 are always present.
+ * @number_of_fingers - describes the maximum number of fingers the 2-D sensor
+ * supports.
+ * @has_rel - the sensor supports relative motion reporting.
+ * @has_abs - the sensor supports absolute poition reporting.
+ * @has_gestures - the sensor supports gesture reporting.
+ * @has_sensitivity_adjust - the sensor supports a global sensitivity
+ * adjustment.
+ * @configurable - the sensor supports various configuration options.
+ * @num_of_x_electrodes - the maximum number of electrodes the 2-D sensor
+ * supports on the X axis.
+ * @num_of_y_electrodes - the maximum number of electrodes the 2-D sensor
+ * supports on the Y axis.
+ * @max_electrodes - the total number of X and Y electrodes that may be
+ * configured.
+ */
+struct f11_2d_sensor_info {
+ /* query1 */
+ u8 number_of_fingers:3;
+ bool has_rel:1;
+ bool has_abs:1;
+ bool has_gestures:1;
+ bool has_sensitivity_adjust:1;
+ bool configurable:1;
+ /* query2 */
+ u8 num_of_x_electrodes:7;
+ u8 reserved_1:1;
+ /* query3 */
+ u8 num_of_y_electrodes:7;
+ u8 reserved_2:1;
+ /* query4 */
+ u8 max_electrodes:7;
+ u8 reserved_3:1;
+} __attribute__((__packed__));
+
+/** Query 5 - this is present if the has_abs bit is set.
+ *
+ * @abs_data_size - describes the format of data reported by the absolute
+ * data source. Only one format (the kind used here) is supported at this
+ * time.
+ * @has_anchored_finger - then the sensor supports the high-precision second
+ * finger tracking provided by the manual tracking and motion sensitivity
+ * options.
+ * @has_adjust_hyst - the difference between the finger release threshold and
+ * the touch threshold.
+ * @has_dribble - the sensor supports the generation of dribble interrupts,
+ * which may be enabled or disabled with the dribble control bit.
+ * @has_bending_correction - Bending related data registers 28 and 36, and
+ * control register 52..57 are present.
+ * @has_large_object_suppression - control register 58 and data register 28
+ * exist.
+ * @has_jitter_filter - query 13 and control 73..76 exist.
+ */
+struct f11_2d_abs_info {
+ u8 abs_data_size:2;
+ bool has_anchored_finger:1;
+ bool has_adj_hyst:1;
+ bool has_dribble:1;
+ bool has_bending_correction:1;
+ bool has_large_object_suppression:1;
+ bool has_jitter_filter:1;
+} __attribute__((__packed__));
+
+/** Gesture information queries 7 and 8 are present if has_gestures bit is set.
+ *
+ * @has_single_tap - a basic single-tap gesture is supported.
+ * @has_tap_n_hold - tap-and-hold gesture is supported.
+ * @has_double_tap - double-tap gesture is supported.
+ * @has_early_tap - early tap is supported and reported as soon as the finger
+ * lifts for any tap event that could be interpreted as either a single tap
+ * or as the first tap of a double-tap or tap-and-hold gesture.
+ * @has_flick - flick detection is supported.
+ * @has_press - press gesture reporting is supported.
+ * @has_pinch - pinch gesture detection is supported.
+ * @has_palm_det - the 2-D sensor notifies the host whenever a large conductive
+ * object such as a palm or a cheek touches the 2-D sensor.
+ * @has_rotate - rotation gesture detection is supported.
+ * @has_touch_shapes - TouchShapes are supported. A TouchShape is a fixed
+ * rectangular area on the sensor that behaves like a capacitive button.
+ * @has_scroll_zones - scrolling areas near the sensor edges are supported.
+ * @has_individual_scroll_zones - if 1, then 4 scroll zones are supported;
+ * if 0, then only two are supported.
+ * @has_multi_finger_scroll - the multifinger_scrolling bit will be set when
+ * more than one finger is involved in a scrolling action.
+ */
+struct f11_2d_gesture_info {
+ bool has_single_tap:1;
+ bool has_tap_n_hold:1;
+ bool has_double_tap:1;
+ bool has_early_tap:1;
+ bool has_flick:1;
+ bool has_press:1;
+ bool has_pinch:1;
+ bool has_chiral:1;
+
+ bool has_palm_det:1;
+ bool has_rotate:1;
+ bool has_touch_shapes:1;
+ bool has_scroll_zones:1;
+ bool has_individual_scroll_zones:1;
+ bool has_multi_finger_scroll:1;
+ bool has_mf_edge_motion:1;
+ bool has_mf_scroll_inertia:1;
+} __attribute__((__packed__));
+
+/** Utility for checking bytes in the gesture info registers. This is done
+ * often enough that we put it here to declutter the conditionals.
+ */
+static bool has_gesture_bits(const struct f11_2d_gesture_info *info,
+ const u8 byte) {
+ return ((u8 *) info)[byte] != 0;
+}
+
+/**
+ * @has_pen - detection of a stylus is supported and registers F11_2D_Ctrl20
+ * and F11_2D_Ctrl21 exist.
+ * @has_proximity - detection of fingers near the sensor is supported and
+ * registers F11_2D_Ctrl22 through F11_2D_Ctrl26 exist.
+ * @has_palm_det_sensitivity - the sensor supports the palm detect sensitivity
+ * feature and register F11_2D_Ctrl27 exists.
+ * @has_two_pen_thresholds - is has_pen is also set, then F11_2D_Ctrl35 exists.
+ * @has_contact_geometry - the sensor supports the use of contact geometry to
+ * map absolute X and Y target positions and registers F11_2D_Data18.* through
+ * F11_2D_Data27 exist.
+ */
+struct f11_2d_query9 {
+ bool has_pen:1;
+ bool has_proximity:1;
+ bool has_palm_det_sensitivity:1;
+ bool has_suppress_on_palm_detect:1;
+ bool has_two_pen_thresholds:1;
+ bool has_contact_geometry:1;
+ bool has_pen_hover_discrimination:1;
+ bool has_pen_filters:1;
+} __attribute__((__packed__));
+
+/** Touch shape info (query 10) is present if has_touch_shapes is set.
+ *
+ * @nbr_touch_shapes - the total number of touch shapes supported.
+ */
+struct f11_2d_ts_info {
+ u8 nbr_touch_shapes:5;
+ u8 reserved:3;
+} __attribute__((__packed__));
+
+/** Query 11 is present if the has_query11 bit is set in query 0.
+ *
+ * @has_z_tuning - if set, the sensor supports Z tuning and registers
+ * F11_2D_Ctrl29 through F11_2D_Ctrl33 exist.
+ * @has_algorithm_selection - controls choice of noise suppression algorithm
+ * @has_w_tuning - the sensor supports Wx and Wy scaling and registers
+ * F11_2D_Ctrl36 through F11_2D_Ctrl39 exist.
+ * @has_pitch_info - the X and Y pitches of the sensor electrodes can be
+ * configured and registers F11_2D_Ctrl40 and F11_2D_Ctrl41 exist.
+ * @has_finger_size - the default finger width settings for the
+ * sensor can be configured and registers F11_2D_Ctrl42 through F11_2D_Ctrl44
+ * exist.
+ * @has_segmentation_aggressiveness - the sensor’s ability to distinguish
+ * multiple objects close together can be configured and register F11_2D_Ctrl45
+ * exists.
+ * @has_XY_clip - the inactive outside borders of the sensor can be
+ * configured and registers F11_2D_Ctrl46 through F11_2D_Ctrl49 exist.
+ * @has_drumming_filter - the sensor can be configured to distinguish
+ * between a fast flick and a quick drumming movement and registers
+ * F11_2D_Ctrl50 and F11_2D_Ctrl51 exist.
+ */
+struct f11_2d_query11 {
+ bool has_z_tuning:1;
+ bool has_algorithm_selection:1;
+ bool has_w_tuning:1;
+ bool has_pitch_info:1;
+ bool has_finger_size:1;
+ bool has_segmentation_aggressiveness:1;
+ bool has_XY_clip:1;
+ bool has_drumming_filter:1;
+} __attribute__((__packed__));
+
+/**
+ * @has_gapless_finger - control registers relating to gapless finger are
+ * present.
+ * @has_gapless_finger_tuning - additional control and data registers relating
+ * to gapless finger are present.
+ * @has_8bit_w - larger W value reporting is supported.
+ * @has_adjustable_mapping - TBD
+ * @has_info2 - the general info query14 is present
+ * @has_physical_props - additional queries describing the physical properties
+ * of the sensor are present.
+ * @has_finger_limit - indicates that F11 Ctrl 80 exists.
+ * @has_linear_coeff - indicates that F11 Ctrl 81 exists.
+ */
+struct f11_2d_query12 {
+ bool has_gapless_finger:1;
+ bool has_gapless_finger_tuning:1;
+ bool has_8bit_w:1;
+ bool has_adjustable_mapping:1;
+ bool has_info2:1;
+ bool has_physical_props:1;
+ bool has_finger_limit:1;
+ bool has_linear_coeff_2:1;
+} __attribute__((__packed__));
+
+/** This register is present if Query 5's has_jitter_filter bit is set.
+ * @jitter_window_size - used by Design Studio 4.
+ * @jitter_filter_type - used by Design Studio 4.
+ */
+struct f11_2d_query13 {
+ u8 jtter_window_size:5;
+ u8 jitter_filter_type:2;
+ u8 reserved:1;
+} __attribute__((__packed__));
+
+/** This register is present if query 12's has_general_info2 flag is set.
+ *
+ * @light_control - Indicates what light/led control features are present, if
+ * any.
+ * @is_clear - if set, this is a clear sensor (indicating direct pointing
+ * application), otherwise it's opaque (indicating indirect pointing).
+ * @clickpad_props - specifies if this is a clickpad, and if so what sort of
+ * mechanism it uses
+ * @mouse_buttons - specifies the number of mouse buttons present (if any).
+ * @has_advanced_gestures - advanced driver gestures are supported.
+ */
+struct f11_2d_query14 {
+ u8 light_control:2;
+ bool is_clear:1;
+ u8 clickpad_props:2;
+ u8 mouse_buttons:2;
+ bool has_advanced_gestures:1;
+} __attribute__((__packed__));
+
+#define F11_LIGHT_CTL_NONE 0x00
+#define F11_LUXPAD 0x01
+#define F11_DUAL_MODE 0x02
+
+#define F11_NOT_CLICKPAD 0x00
+#define F11_HINGED_CLICKPAD 0x01
+#define F11_UNIFORM_CLICKPAD 0x02
+
+/** See notes above for information about specific query register sets.
+ */
+struct f11_2d_sensor_queries {
+ struct f11_2d_sensor_info info;
+
+ struct f11_2d_abs_info abs_info;
+
+ u8 f11_2d_query6;
+
+ struct f11_2d_gesture_info gesture_info;
+
+ struct f11_2d_query9 query9;
+
+ struct f11_2d_ts_info ts_info;
+
+ struct f11_2d_query11 features_1;
+
+ struct f11_2d_query12 features_2;
+
+ struct f11_2d_query13 jitter_filter;
+
+ struct f11_2d_query14 info_2;
+};
+
+/**
+ * @reporting_mode - controls how often finger position data is reported.
+ * @abs_pos_filt - when set, enables various noise and jitter filtering
+ * algorithms for absolute reports.
+ * @rel_pos_filt - when set, enables various noise and jitter filtering
+ * algorithms for relative reports.
+ * @rel_ballistics - enables ballistics processing for the relative finger
+ * motion on the 2-D sensor.
+ * @dribble - enables the dribbling feature.
+ * @report_beyond_clip - when this is set, fingers outside the active area
+ * specified by the x_clip and y_clip registers will be reported, but with
+ * reported finger position clipped to the edge of the active area.
+ * @palm_detect_thresh - the threshold at which a wide finger is considered a
+ * palm. A value of 0 inhibits palm detection.
+ * @motion_sensitivity - specifies the threshold an anchored finger must move
+ * before it is considered no longer anchored. High values mean more
+ * sensitivity.
+ * @man_track_en - for anchored finger tracking, whether the host (1) or the
+ * device (0) determines which finger is the tracked finger.
+ * @man_tracked_finger - when man_track_en is 1, specifies whether finger 0 or
+ * finger 1 is the tracked finger.
+ * @delta_x_threshold - 2-D position update interrupts are inhibited unless
+ * the finger moves more than a certain threshold distance along the X axis.
+ * @delta_y_threshold - 2-D position update interrupts are inhibited unless
+ * the finger moves more than a certain threshold distance along the Y axis.
+ * @velocity - When rel_ballistics is set, this register defines the
+ * velocity ballistic parameter applied to all relative motion events.
+ * @acceleration - When rel_ballistics is set, this register defines the
+ * acceleration ballistic parameter applied to all relative motion events.
+ * @sensor_max_x_pos - the maximum X coordinate reported by the sensor.
+ * @sensor_max_y_pos - the maximum Y coordinate reported by the sensor.
+ */
+struct f11_2d_ctrl0_9 {
+ /* F11_2D_Ctrl0 */
+ u8 reporting_mode:3;
+ bool abs_pos_filt:1;
+ bool rel_pos_filt:1;
+ bool rel_ballistics:1;
+ bool dribble:1;
+ bool report_beyond_clip:1;
+ /* F11_2D_Ctrl1 */
+ u8 palm_detect_thres:4;
+ u8 motion_sensitivity:2;
+ bool man_track_en:1;
+ bool 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;
+} __attribute__((__packed__));
+
+/**
+ * @single_tap_int_enable - enable tap gesture recognition.
+ * @tap_n_hold_int_enable - enable tap-and-hold gesture recognition.
+ * @double_tap_int_enable - enable double-tap gesture recognition.
+ * @early_tap_int_enable - enable early tap notification.
+ * @flick_int_enable - enable flick detection.
+ * @press_int_enable - enable press gesture recognition.
+ * @pinch_int_enable - enable pinch detection.
+ */
+struct f11_2d_ctrl10 {
+ bool single_tap_int_enable:1;
+ bool tap_n_hold_int_enable:1;
+ bool double_tap_int_enable:1;
+ bool early_tap_int_enable:1;
+ bool flick_int_enable:1;
+ bool press_int_enable:1;
+ bool pinch_int_enable:1;
+ u8 reserved:1;
+} __attribute__((__packed__));
+
+/**
+ * @palm_detect_int_enable - enable palm detection feature.
+ * @rotate_int_enable - enable rotate gesture detection.
+ * @touch_shape_int_enable - enable the TouchShape feature.
+ * @scroll_zone_int_enable - enable scroll zone reporting.
+ * @multi_finger_scroll_int_enable - enable the multfinger scroll feature.
+ */
+struct f11_2d_ctrl11 {
+ bool palm_detect_int_enable:1;
+ bool rotate_int_enable:1;
+ bool touch_shape_int_enable:1;
+ bool scroll_zone_int_enable:1;
+ bool multi_finger_scroll_int_enable:1;
+ u8 reserved:3;
+} __attribute__((__packed__));
+
+/**
+ * @sens_adjustment - allows a host to alter the overall sensitivity of a
+ * 2-D sensor. A positive value in this register will make the sensor more
+ * sensitive than the factory defaults, and a negative value will make it
+ * less sensitive.
+ * @hyst_adjustment - increase the touch/no-touch hysteresis by 2 Z-units for
+ * each one unit increment in this setting.
+ */
+struct f11_2d_ctrl14 {
+ s8 sens_adjustment:5;
+ u8 hyst_adjustment:3;
+} __attribute__((__packed__));
+
+/**
+ * @max_tap_time - the maximum duration of a tap, in 10-millisecond units.
+ */
+struct f11_2d_ctrl15 {
+ u8 max_tap_time:8;
+} __attribute__((__packed__));
+
+/**
+ * @min_press_time - The minimum duration required for stationary finger(s) to
+ * generate a press gesture, in 10-millisecond units.
+ */
+struct f11_2d_ctrl16 {
+ u8 min_press_time:8;
+} __attribute__((__packed__));
+
+/**
+ * @max_tap_distance - Determines the maximum finger movement allowed during
+ * a tap, in 0.1-millimeter units.
+ */
+struct f11_2d_ctrl17 {
+ u8 max_tap_distance:8;
+} __attribute__((__packed__));
+
+/**
+ * @min_flick_distance - the minimum finger movement for a flick gesture,
+ * in 1-millimeter units.
+ * @min_flick_speed - the minimum finger speed for a flick gesture, in
+ * 10-millimeter/second units.
+ */
+struct f11_2d_ctrl18_19 {
+ u8 min_flick_distance:8;
+ u8 min_flick_speed:8;
+} __attribute__((__packed__));
+
+/**
+ * @pen_detect_enable - enable reporting of stylus activity.
+ * @pen_jitter_filter_enable - Setting this enables the stylus anti-jitter
+ * filter.
+ * @pen_z_threshold - This is the stylus-detection lower threshold. Smaller
+ * values result in higher sensitivity.
+ */
+struct f11_2d_ctrl20_21 {
+ bool pen_detect_enable:1;
+ bool pen_jitter_filter_enable:1;
+ u8 ctrl20_reserved:6;
+ u8 pen_z_threshold:8;
+} __attribute__((__packed__));
+
+/**
+ * These are not accessible through sysfs yet.
+ *
+ * @proximity_detect_int_en - enable proximity detection feature.
+ * @proximity_jitter_filter_en - enables an anti-jitter filter on proximity
+ * data.
+ * @proximity_detection_z_threshold - the threshold for finger-proximity
+ * detection.
+ * @proximity_delta_x_threshold - In reduced-reporting modes, this is the
+ * threshold for proximate-finger movement in the direction parallel to the
+ * X-axis.
+ * @proximity_delta_y_threshold - In reduced-reporting modes, this is the
+ * threshold for proximate-finger movement in the direction parallel to the
+ * Y-axis.
+ * * @proximity_delta_Z_threshold - In reduced-reporting modes, this is the
+ * threshold for proximate-finger movement in the direction parallel to the
+ * Z-axis.
+ */
+struct f11_2d_ctrl22_26 {
+ /* control 22 */
+ bool proximity_detect_int_en:1;
+ bool proximity_jitter_filter_en:1;
+ u8 f11_2d_ctrl6_b3__7:6;
+
+ /* control 23 */
+ u8 proximity_detection_z_threshold;
+
+ /* control 24 */
+ u8 proximity_delta_x_threshold;
+
+ /* control 25 */
+ u8 proximity_delta_y_threshold;
+
+ /* control 26 */
+ u8 proximity_delta_z_threshold;
+} __attribute__((__packed__));
+
+/**
+ * @palm_detecy_sensitivity - When this value is small, smaller objects will
+ * be identified as palms; when this value is large, only larger objects will
+ * be identified as palms. 0 represents the factory default.
+ * @suppress_on_palm_detect - when set, all F11 interrupts except palm_detect
+ * are suppressed while a palm is detected.
+ */
+struct f11_2d_ctrl27 {
+ s8 palm_detect_sensitivity:4;
+ bool suppress_on_palm_detect:1;
+ u8 f11_2d_ctrl27_b5__7:3;
+} __attribute__((__packed__));
+
+/**
+ * @multi_finger_scroll_mode - allows choice of multi-finger scroll mode and
+ * determines whether and how X or Y displacements are reported.
+ * @edge_motion_en - enables the edge_motion feature.
+ * @multi_finger_scroll_momentum - controls the length of time that scrolling
+ * continues after fingers have been lifted.
+ */
+struct f11_2d_ctrl28 {
+ u8 multi_finger_scroll_mode:2;
+ bool edge_motion_en:1;
+ bool f11_2d_ctrl28b_3:1;
+ u8 multi_finger_scroll_momentum:4;
+} __attribute__((__packed__));
+
+/**
+ * @z_touch_threshold - Specifies the finger-arrival Z threshold. Large values
+ * may cause smaller fingers to be rejected.
+ * @z_touch_hysteresis - Specifies the difference between the finger-arrival
+ * Z threshold and the finger-departure Z threshold.
+ */
+struct f11_2d_ctrl29_30 {
+ u8 z_touch_threshold;
+ u8 z_touch_hysteresis;
+} __attribute__((__packed__));
+
+
+struct f11_2d_ctrl {
+ struct f11_2d_ctrl0_9 *ctrl0_9;
+ u16 ctrl0_9_address;
+ struct f11_2d_ctrl10 *ctrl10;
+ struct f11_2d_ctrl11 *ctrl11;
+ u8 ctrl12_size;
+ struct f11_2d_ctrl14 *ctrl14;
+ struct f11_2d_ctrl15 *ctrl15;
+ struct f11_2d_ctrl16 *ctrl16;
+ struct f11_2d_ctrl17 *ctrl17;
+ struct f11_2d_ctrl18_19 *ctrl18_19;
+ struct f11_2d_ctrl20_21 *ctrl20_21;
+ struct f11_2d_ctrl22_26 *ctrl22_26;
+ struct f11_2d_ctrl27 *ctrl27;
+ struct f11_2d_ctrl28 *ctrl28;
+ struct f11_2d_ctrl29_30 *ctrl29_30;
+};
+
+/**
+ * @x_msb - top 8 bits of X finger position.
+ * @y_msb - top 8 bits of Y finger position.
+ * @x_lsb - bottom 4 bits of X finger position.
+ * @y_lsb - bottom 4 bits of Y finger position.
+ * @w_y - contact patch width along Y axis.
+ * @w_x - contact patch width along X axis.
+ * @z - finger Z value (proxy for pressure).
+ */
+struct f11_2d_data_1_5 {
+ u8 x_msb;
+ u8 y_msb;
+ u8 x_lsb:4;
+ u8 y_lsb:4;
+ u8 w_y:4;
+ u8 w_x:4;
+ u8 z;
+} __attribute__((__packed__));
+
+/**
+ * @delta_x - relative motion along X axis.
+ * @delta_y - relative motion along Y axis.
+ */
+struct f11_2d_data_6_7 {
+ s8 delta_x;
+ s8 delta_y;
+} __attribute__((__packed__));
+
+/**
+ * @single_tap - a single tap was recognized.
+ * @tap_and_hold - a tap-and-hold gesture was recognized.
+ * @double_tap - a double tap gesture was recognized.
+ * @early_tap - a tap gesture might be happening.
+ * @flick - a flick gesture was detected.
+ * @press - a press gesture was recognized.
+ * @pinch - a pinch gesture was detected.
+ */
+struct f11_2d_data_8 {
+ bool single_tap:1;
+ bool tap_and_hold:1;
+ bool double_tap:1;
+ bool early_tap:1;
+ bool flick:1;
+ bool press:1;
+ bool pinch:1;
+} __attribute__((__packed__));
+
+/**
+ * @palm_detect - a palm or other large object is in contact with the sensor.
+ * @rotate - a rotate gesture was detected.
+ * @shape - a TouchShape has been activated.
+ * @scrollzone - scrolling data is available.
+ * @finger_count - number of fingers involved in the reported gesture.
+ */
+struct f11_2d_data_9 {
+ bool palm_detect:1;
+ bool rotate:1;
+ bool shape:1;
+ bool scrollzone:1;
+ u8 finger_count:3;
+} __attribute__((__packed__));
+
+/**
+ * @pinch_motion - when a pinch gesture is detected, this is the change in
+ * distance between the two fingers since this register was last read.
+ */
+struct f11_2d_data_10 {
+ s8 pinch_motion;
+} __attribute__((__packed__));
+
+/**
+ * @x_flick_dist - when a flick gesture is detected, the distance of flick
+ * gesture in X direction.
+ * @y_flick_dist - when a flick gesture is detected, the distance of flick
+ * gesture in Y direction.
+ * @flick_time - the total time of the flick gesture, in 10ms units.
+ */
+struct f11_2d_data_10_12 {
+ s8 x_flick_dist;
+ s8 y_flick_dist;
+ u8 flick_time;
+} __attribute__((__packed__));
+
+/**
+ * @motion - when a rotate gesture is detected, the accumulated distance
+ * of the rotate motion. Clockwise motion is positive and counterclockwise
+ * motion is negative.
+ * @finger_separation - when a rotate gesture is detected, the distance
+ * between the fingers.
+ */
+struct f11_2d_data_11_12 {
+ s8 motion;
+ u8 finger_separation;
+} __attribute__((__packed__));
+
+/**
+ * @shape_n - a bitmask of the currently activate TouchShapes (if any).
+ */
+struct f11_2d_data_13 {
+ u8 shape_n;
+} __attribute__((__packed__));
+
+/**
+ * @horizontal - chiral scrolling distance in the X direction.
+ * @vertical - chiral scrolling distance in the Y direction.
+ */
+struct f11_2d_data_14_15 {
+ s8 horizontal;
+ s8 vertical;
+} __attribute__((__packed__));
+
+/**
+ * @x_low - scroll zone motion along the lower edge of the sensor.
+ * @y_right - scroll zone motion along the right edge of the sensor.
+ * @x_upper - scroll zone motion along the upper edge of the sensor.
+ * @y_left - scroll zone motion along the left edge of the sensor.
+ */
+struct f11_2d_data_14_17 {
+ s8 x_low;
+ s8 y_right;
+ s8 x_upper;
+ s8 y_left;
+} __attribute__((__packed__));
+
+struct f11_2d_data {
+ u8 *f_state;
+ const struct f11_2d_data_1_5 *abs_pos;
+ const struct f11_2d_data_6_7 *rel_pos;
+ const struct f11_2d_data_8 *gest_1;
+ const struct f11_2d_data_9 *gest_2;
+ const struct f11_2d_data_10 *pinch;
+ const struct f11_2d_data_10_12 *flick;
+ const struct f11_2d_data_11_12 *rotate;
+ const struct f11_2d_data_13 *shapes;
+ const struct f11_2d_data_14_15 *multi_scroll;
+ const struct f11_2d_data_14_17 *scroll_zones;
+};
+
+/**
+ * @axis_align - controls parameters that are useful in system prototyping
+ * and bring up.
+ * @sens_query - query registers for this particular sensor.
+ * @data - the data reported by this sensor, mapped into a collection of
+ * structs.
+ * @max_x - The maximum X coordinate that will be reported by this sensor.
+ * @max_y - The maximum Y coordinate that will be reported by this sensor.
+ * @nbr_fingers - How many fingers can this sensor report?
+ * @data_pkt - buffer for data reported by this sensor.
+ * @pkt_size - number of bytes in that buffer.
+ * @sensor_index - identifies this particular 2D touch sensor
+ * @type_a - some early RMI4 2D sensors do not reliably track the finger
+ * position when two fingers are on the device. When this is true, we
+ * assume we have one of those sensors and report events appropriately.
+ * @sensor_type - indicates whether we're touchscreen or touchpad.
+ * @input - input device for absolute pointing stream
+ * @mouse_input - input device for relative pointing stream.
+ * @input_phys - buffer for the absolute phys name for this sensor.
+ * @input_mouse_phys - buffer for the relative phys name for this sensor.
+ * @debugfs_flip - inverts one or both axes. Useful in prototyping new
+ * systems.
+ * @debugfs_flip - coordinate clipping range for one or both axes. Useful in
+ * prototyping new systems.
+ * @debugfs_delta_threshold - adjusts motion sensitivity for relative reports
+ * and (in reduced reporting mode) absolute reports. Useful in prototyping new
+ * systems.
+ * @debugfs_offset - offsets one or both axes. Useful in prototyping new
+ * systems.
+ * @debugfs_swap - swaps X and Y axes. Useful in prototyping new systems.
+ * @debugfs_type_a - forces type A behavior. Useful in bringing up old systems
+ * when you're not sure if you've got a Type A or Type B sensor.
+ */
+struct f11_2d_sensor {
+ struct rmi_f11_2d_axis_alignment axis_align;
+ struct f11_2d_sensor_queries sens_query;
+ struct f11_2d_data data;
+ u16 max_x;
+ u16 max_y;
+ u8 nbr_fingers;
+ u8 *data_pkt;
+ int pkt_size;
+ u8 sensor_index;
+ bool type_a;
+ enum rmi_f11_sensor_type sensor_type;
+ struct input_dev *input;
+ struct input_dev *mouse_input;
+ struct rmi_function_container *fc;
+ char input_phys[NAME_BUFFER_SIZE];
+ char input_phys_mouse[NAME_BUFFER_SIZE];
+
+#ifdef CONFIG_RMI4_DEBUG
+ struct dentry *debugfs_flip;
+ struct dentry *debugfs_clip;
+ struct dentry *debugfs_delta_threshold;
+ struct dentry *debugfs_offset;
+ struct dentry *debugfs_swap;
+ struct dentry *debugfs_type_a;
+#endif
+};
+
+/** Data pertaining to F11 in general. For per-sensor data, see struct
+ * f11_2d_sensor.
+ *
+ * @dev_query - F11 device specific query registers.
+ * @dev_controls - F11 device specific control registers.
+ * @dev_controls_mutex - lock for the control registers.
+ * @rezero_wait_ms - if nonzero, upon resume we will wait this many
+ * milliseconds before rezeroing the sensor(s). This is useful in systems with
+ * poor electrical behavior on resume, where the initial calibration of the
+ * sensor(s) coming out of sleep state may be bogus.
+ * @sensors - per sensor data structures.
+ * @debugfs_rezero_wait - allows control of the rezero_wait value. Useful
+ * during system prototyping.
+ */
+struct f11_data {
+ struct f11_2d_device_query dev_query;
+ struct f11_2d_ctrl dev_controls;
+ struct mutex dev_controls_mutex;
+ u16 rezero_wait_ms;
+ struct f11_2d_sensor sensors[F11_MAX_NUM_OF_SENSORS];
+
+#ifdef CONFIG_RMI4_DEBUG
+ struct dentry *debugfs_rezero_wait;
+#endif
+};
+
+enum finger_state_values {
+ F11_NO_FINGER = 0x00,
+ F11_PRESENT = 0x01,
+ F11_INACCURATE = 0x02,
+ F11_RESERVED = 0x03
+};
+
+static ssize_t f11_maxPos_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u %u\n",
+ data->sensors[0].max_x, data->sensors[0].max_y);
+}
+
+static ssize_t f11_relreport_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->
+ sensors[0].axis_align.rel_report_enabled);
+}
+
+static ssize_t f11_relreport_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+ unsigned int new_value;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+
+ if (sscanf(buf, "%u", &new_value) != 1)
+ return -EINVAL;
+ if (new_value > 1)
+ return -EINVAL;
+ instance_data->sensors[0].axis_align.rel_report_enabled = new_value;
+
+ return count;
+}
+
+static ssize_t f11_rezero_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc = NULL;
+ unsigned int rezero;
+ int retval = 0;
+
+ fc = to_rmi_function_container(dev);
+
+ if (sscanf(buf, "%u", &rezero) != 1)
+ return -EINVAL;
+ if (rezero > 1)
+ return -EINVAL;
+
+ /* Per spec, 0 has no effect, so we skip it entirely. */
+ if (rezero) {
+ /* Command register always reads as 0, so just use a local. */
+ struct f11_2d_commands commands = {
+ .rezero = true,
+ };
+
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+ &commands, sizeof(commands));
+ if (retval < 0) {
+ dev_err(dev, "%s: failed to issue rezero command, error = %d.",
+ __func__, retval);
+ return retval;
+ }
+ }
+
+ return count;
+}
+
+static struct device_attribute attrs[] = {
+ __ATTR(relreport, RMI_RW_ATTR, f11_relreport_show, f11_relreport_store),
+ __ATTR(maxPos, RMI_RO_ATTR, f11_maxPos_show, NULL),
+ __ATTR(rezero, RMI_WO_ATTR, NULL, f11_rezero_store)
+};
+
+#ifdef CONFIG_RMI4_DEBUG
+
+struct sensor_debugfs_data {
+ bool done;
+ struct f11_2d_sensor *sensor;
+};
+
+static int sensor_debug_open(struct inode *inodep, struct file *filp)
+{
+ struct sensor_debugfs_data *data;
+ struct f11_2d_sensor *sensor = inodep->i_private;
+
+ data = kzalloc(sizeof(struct sensor_debugfs_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->sensor = sensor;
+ filp->private_data = data;
+ return 0;
+}
+
+static int sensor_debug_release(struct inode *inodep, struct file *filp)
+{
+ kfree(filp->private_data);
+ return 0;
+}
+
+static ssize_t flip_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ int retval;
+ char *local_buf;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ if (data->done)
+ return 0;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u %u\n",
+ data->sensor->axis_align.flip_x,
+ data->sensor->axis_align.flip_y);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ retval = -EFAULT;
+ kfree(local_buf);
+
+ return retval;
+}
+
+static ssize_t flip_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char *local_buf;
+ unsigned int new_X;
+ unsigned int new_Y;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval) {
+ kfree(local_buf);
+ return -EFAULT;
+ }
+
+ retval = sscanf(local_buf, "%u %u", &new_X, &new_Y);
+ kfree(local_buf);
+ if (retval != 2 || new_X > 1 || new_Y > 1)
+ return -EINVAL;
+
+ data->sensor->axis_align.flip_x = new_X;
+ data->sensor->axis_align.flip_y = new_Y;
+
+ return size;
+}
+
+static const struct file_operations flip_fops = {
+ .owner = THIS_MODULE,
+ .open = sensor_debug_open,
+ .release = sensor_debug_release,
+ .read = flip_read,
+ .write = flip_write,
+};
+
+static ssize_t delta_threshold_read(struct file *filp, char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char *local_buf;
+ struct sensor_debugfs_data *data = filp->private_data;
+ struct f11_data *f11 = data->sensor->fc->data;
+ struct f11_2d_ctrl *ctrl = &f11->dev_controls;
+
+ if (data->done)
+ return 0;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u %u\n",
+ ctrl->ctrl0_9->delta_x_threshold,
+ ctrl->ctrl0_9->delta_y_threshold);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ retval = -EFAULT;
+ kfree(local_buf);
+
+ return retval;
+
+}
+
+static ssize_t delta_threshold_write(struct file *filp,
+ const char __user *buffer, size_t size, loff_t *offset) {
+ int retval;
+ char *local_buf;
+ unsigned int new_X, new_Y;
+ u8 save_X, save_Y;
+ int rc;
+ struct sensor_debugfs_data *data = filp->private_data;
+ struct f11_data *f11 = data->sensor->fc->data;
+ struct f11_2d_ctrl *ctrl = &f11->dev_controls;
+ struct rmi_device *rmi_dev = data->sensor->fc->rmi_dev;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval) {
+ kfree(local_buf);
+ return -EFAULT;
+ }
+
+ retval = sscanf(local_buf, "%u %u", &new_X, &new_Y);
+ kfree(local_buf);
+ if (retval != 2 || new_X > 1 || new_Y > 1)
+ return -EINVAL;
+
+ save_X = ctrl->ctrl0_9->delta_x_threshold;
+ save_Y = ctrl->ctrl0_9->delta_y_threshold;
+
+ ctrl->ctrl0_9->delta_x_threshold = new_X;
+ ctrl->ctrl0_9->delta_y_threshold = new_Y;
+ rc = rmi_write_block(rmi_dev, ctrl->ctrl0_9_address,
+ ctrl->ctrl0_9, sizeof(*ctrl->ctrl0_9));
+ if (rc < 0) {
+ dev_warn(&data->sensor->fc->dev,
+ "Failed to write to delta_threshold. Code: %d.\n",
+ rc);
+ ctrl->ctrl0_9->delta_x_threshold = save_X;
+ ctrl->ctrl0_9->delta_y_threshold = save_Y;
+ }
+
+ return size;
+}
+
+static const struct file_operations delta_threshold_fops = {
+ .owner = THIS_MODULE,
+ .open = sensor_debug_open,
+ .release = sensor_debug_release,
+ .read = delta_threshold_read,
+ .write = delta_threshold_write,
+};
+
+static ssize_t offset_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ int retval;
+ char *local_buf;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ if (data->done)
+ return 0;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u %u\n",
+ data->sensor->axis_align.offset_X,
+ data->sensor->axis_align.offset_Y);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ retval = -EFAULT;
+ kfree(local_buf);
+
+ return retval;
+}
+
+static ssize_t offset_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset)
+{
+ int retval;
+ char *local_buf;
+ int new_X;
+ int new_Y;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval) {
+ kfree(local_buf);
+ return -EFAULT;
+ }
+ retval = sscanf(local_buf, "%u %u", &new_X, &new_Y);
+ kfree(local_buf);
+ if (retval != 2)
+ return -EINVAL;
+
+ data->sensor->axis_align.offset_X = new_X;
+ data->sensor->axis_align.offset_Y = new_Y;
+
+ return size;
+}
+
+static const struct file_operations offset_fops = {
+ .owner = THIS_MODULE,
+ .open = sensor_debug_open,
+ .release = sensor_debug_release,
+ .read = offset_read,
+ .write = offset_write,
+};
+
+static ssize_t clip_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ int retval;
+ char *local_buf;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ if (data->done)
+ return 0;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u %u %u %u\n",
+ data->sensor->axis_align.clip_X_low,
+ data->sensor->axis_align.clip_X_high,
+ data->sensor->axis_align.clip_Y_low,
+ data->sensor->axis_align.clip_Y_high);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ retval = -EFAULT;
+ kfree(local_buf);
+
+ return retval;
+}
+
+static ssize_t clip_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset)
+{
+ int retval;
+ char *local_buf;
+ unsigned int new_X_low, new_X_high, new_Y_low, new_Y_high;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval) {
+ kfree(local_buf);
+ return -EFAULT;
+ }
+
+ retval = sscanf(local_buf, "%u %u %u %u",
+ &new_X_low, &new_X_high, &new_Y_low, &new_Y_high);
+ kfree(local_buf);
+ if (retval != 4)
+ return -EINVAL;
+
+ if (new_X_low >= new_X_high || new_Y_low >= new_Y_high)
+ return -EINVAL;
+
+ data->sensor->axis_align.clip_X_low = new_X_low;
+ data->sensor->axis_align.clip_X_high = new_X_high;
+ data->sensor->axis_align.clip_Y_low = new_Y_low;
+ data->sensor->axis_align.clip_Y_high = new_Y_high;
+
+ return size;
+}
+
+static const struct file_operations clip_fops = {
+ .owner = THIS_MODULE,
+ .open = sensor_debug_open,
+ .release = sensor_debug_release,
+ .read = clip_read,
+ .write = clip_write,
+};
+
+static ssize_t swap_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ int retval;
+ char *local_buf;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ if (data->done)
+ return 0;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u\n",
+ data->sensor->axis_align.swap_axes);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ retval = -EFAULT;
+ kfree(local_buf);
+
+ return retval;
+}
+
+static ssize_t swap_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset)
+{
+ int retval;
+ char *local_buf;
+ int new_value;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval) {
+ kfree(local_buf);
+ return -EFAULT;
+ }
+ retval = sscanf(local_buf, "%u", &new_value);
+ kfree(local_buf);
+ if (retval != 1 || new_value > 1)
+ return -EINVAL;
+
+ data->sensor->axis_align.swap_axes = new_value;
+ return size;
+}
+
+static const struct file_operations swap_fops = {
+ .owner = THIS_MODULE,
+ .open = sensor_debug_open,
+ .release = sensor_debug_release,
+ .read = swap_read,
+ .write = swap_write,
+};
+
+static ssize_t type_a_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ int retval;
+ char *local_buf;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ if (data->done)
+ return 0;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u\n",
+ data->sensor->type_a);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ retval = -EFAULT;
+ kfree(local_buf);
+
+ return retval;
+}
+
+static ssize_t type_a_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset)
+{
+ int retval;
+ char *local_buf;
+ int new_value;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval) {
+ kfree(local_buf);
+ return -EFAULT;
+ }
+
+ retval = sscanf(local_buf, "%u", &new_value);
+ kfree(local_buf);
+ if (retval != 1 || new_value > 1)
+ return -EINVAL;
+
+ data->sensor->type_a = new_value;
+ return size;
+}
+
+static const struct file_operations type_a_fops = {
+ .owner = THIS_MODULE,
+ .open = sensor_debug_open,
+ .release = sensor_debug_release,
+ .read = type_a_read,
+ .write = type_a_write,
+};
+
+static int setup_sensor_debugfs(struct f11_2d_sensor *sensor)
+{
+ int retval = 0;
+ char fname[NAME_BUFFER_SIZE];
+ struct rmi_function_container *fc = sensor->fc;
+
+ if (!fc->debugfs_root)
+ return -ENODEV;
+
+ retval = snprintf(fname, NAME_BUFFER_SIZE, "flip.%d",
+ sensor->sensor_index);
+ sensor->debugfs_flip = debugfs_create_file(fname, RMI_RW_ATTR,
+ fc->debugfs_root, sensor, &flip_fops);
+ if (!sensor->debugfs_flip)
+ dev_warn(&fc->dev, "Failed to create debugfs %s.\n",
+ fname);
+
+ retval = snprintf(fname, NAME_BUFFER_SIZE, "clip.%d",
+ sensor->sensor_index);
+ sensor->debugfs_clip = debugfs_create_file(fname, RMI_RW_ATTR,
+ fc->debugfs_root, sensor, &clip_fops);
+ if (!sensor->debugfs_clip)
+ dev_warn(&fc->dev, "Failed to create debugfs %s.\n",
+ fname);
+
+ retval = snprintf(fname, NAME_BUFFER_SIZE, "delta_threshold.%d",
+ sensor->sensor_index);
+ sensor->debugfs_clip = debugfs_create_file(fname, RMI_RW_ATTR,
+ fc->debugfs_root, sensor,
+ &delta_threshold_fops);
+ if (!sensor->debugfs_delta_threshold)
+ dev_warn(&fc->dev, "Failed to create debugfs %s.\n",
+ fname);
+
+ retval = snprintf(fname, NAME_BUFFER_SIZE, "offset.%d",
+ sensor->sensor_index);
+ sensor->debugfs_offset = debugfs_create_file(fname, RMI_RW_ATTR,
+ fc->debugfs_root, sensor, &offset_fops);
+ if (!sensor->debugfs_offset)
+ dev_warn(&fc->dev, "Failed to create debugfs %s.\n",
+ fname);
+
+ retval = snprintf(fname, NAME_BUFFER_SIZE, "swap.%d",
+ sensor->sensor_index);
+ sensor->debugfs_swap = debugfs_create_file(fname, RMI_RW_ATTR,
+ fc->debugfs_root, sensor, &swap_fops);
+ if (!sensor->debugfs_swap)
+ dev_warn(&fc->dev, "Failed to create debugfs %s.\n",
+ fname);
+
+ retval = snprintf(fname, NAME_BUFFER_SIZE, "type_a.%d",
+ sensor->sensor_index);
+ sensor->debugfs_type_a = debugfs_create_file(fname, RMI_RW_ATTR,
+ fc->debugfs_root, sensor, &type_a_fops);
+ if (!sensor->debugfs_type_a)
+ dev_warn(&fc->dev, "Failed to create debugfs %s.\n",
+ fname);
+
+ return retval;
+}
+
+static void teardown_sensor_debugfs(struct f11_2d_sensor *sensor)
+{
+ if (sensor->debugfs_flip)
+ debugfs_remove(sensor->debugfs_flip);
+
+ if (sensor->debugfs_clip)
+ debugfs_remove(sensor->debugfs_clip);
+
+ if (sensor->debugfs_offset)
+ debugfs_remove(sensor->debugfs_offset);
+
+ if (sensor->debugfs_swap)
+ debugfs_remove(sensor->debugfs_swap);
+
+ if (sensor->debugfs_type_a)
+ debugfs_remove(sensor->debugfs_type_a);
+}
+
+struct f11_debugfs_data {
+ bool done;
+ struct rmi_function_container *fc;
+};
+
+static int f11_debug_open(struct inode *inodep, struct file *filp)
+{
+ struct f11_debugfs_data *data;
+ struct rmi_function_container *fc = inodep->i_private;
+
+ data = devm_kzalloc(&fc->dev, sizeof(struct f11_debugfs_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->fc = fc;
+ filp->private_data = data;
+ return 0;
+}
+
+static ssize_t rezero_wait_read(struct file *filp, char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char *local_buf;
+ struct f11_debugfs_data *data = filp->private_data;
+ struct f11_data *f11 = data->fc->data;
+
+ if (data->done)
+ return 0;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u\n", f11->rezero_wait_ms);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ retval = -EFAULT;
+ kfree(local_buf);
+
+ return retval;
+}
+
+static ssize_t rezero_wait_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset)
+{
+ int retval;
+ char *local_buf;
+ int new_value;
+ struct f11_debugfs_data *data = filp->private_data;
+ struct f11_data *f11 = data->fc->data;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval) {
+ kfree(local_buf);
+ return -EFAULT;
+ }
+
+ retval = sscanf(local_buf, "%u", &new_value);
+ kfree(local_buf);
+ if (retval != 1 || new_value > 65535)
+ return -EINVAL;
+
+ f11->rezero_wait_ms = new_value;
+ return size;
+}
+
+static const struct file_operations rezero_wait_fops = {
+ .owner = THIS_MODULE,
+ .open = f11_debug_open,
+ .read = rezero_wait_read,
+ .write = rezero_wait_write,
+};
+
+static int setup_f11_debugfs(struct rmi_function_container *fc)
+{
+ struct f11_data *f11 = fc->data;
+
+ if (!fc->debugfs_root)
+ return -ENODEV;
+
+ f11->debugfs_rezero_wait = debugfs_create_file("rezero_wait",
+ RMI_RW_ATTR, fc->debugfs_root, fc, &rezero_wait_fops);
+ if (!f11->debugfs_rezero_wait)
+ dev_warn(&fc->dev,
+ "Failed to create debugfs rezero_wait.\n");
+
+ return 0;
+}
+
+static void teardown_f11_debugfs(struct f11_data *f11)
+{
+ if (f11->debugfs_rezero_wait)
+ debugfs_remove(f11->debugfs_rezero_wait);
+}
+#endif
+/* End adding debugfs */
+
+/** F11_INACCURATE state is overloaded to indicate pen present. */
+#define F11_PEN F11_INACCURATE
+
+static int get_tool_type(struct f11_2d_sensor *sensor, u8 finger_state)
+{
+ if (IS_ENABLED(CONFIG_RMI4_F11_PEN) &&
+ sensor->sens_query.query9.has_pen &&
+ finger_state == F11_PEN)
+ return MT_TOOL_PEN;
+ return MT_TOOL_FINGER;
+}
+
+static void rmi_f11_rel_pos_report(struct f11_2d_sensor *sensor, u8 n_finger)
+{
+ struct f11_2d_data *data = &sensor->data;
+ struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align;
+ s8 x, y;
+ s8 temp;
+
+ x = data->rel_pos[n_finger].delta_x;
+ y = data->rel_pos[n_finger].delta_y;
+
+ x = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)x));
+ y = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)y));
+
+ if (axis_align->swap_axes) {
+ temp = x;
+ x = y;
+ y = temp;
+ }
+ if (axis_align->flip_x)
+ x = min(F11_REL_POS_MAX, -x);
+ if (axis_align->flip_y)
+ y = min(F11_REL_POS_MAX, -y);
+
+ if (x || y) {
+ input_report_rel(sensor->input, REL_X, x);
+ input_report_rel(sensor->input, REL_Y, y);
+ input_report_rel(sensor->mouse_input, REL_X, x);
+ input_report_rel(sensor->mouse_input, REL_Y, y);
+ }
+ input_sync(sensor->mouse_input);
+}
+
+static void rmi_f11_abs_pos_report(struct f11_data *f11,
+ struct f11_2d_sensor *sensor,
+ u8 finger_state, u8 n_finger)
+{
+ struct f11_2d_data *data = &sensor->data;
+ struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align;
+ int x, y, z;
+ int w_x, w_y, w_max, w_min, orient;
+ int temp;
+
+ if (finger_state) {
+ 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);
+
+ }
+
+ /* Some UIs ignore W of zero, so we fudge it to 1 for pens. This
+ * only appears to be an issue when reporting pens, not plain old
+ * fingers. */
+ if (IS_ENABLED(CONFIG_RMI4_F11_PEN) &&
+ get_tool_type(sensor, finger_state) == MT_TOOL_PEN) {
+ w_max = max(1, w_max);
+ w_min = max(1, w_min);
+ }
+
+ if (sensor->type_a) {
+ input_report_abs(sensor->input, ABS_MT_TRACKING_ID, n_finger);
+ input_report_abs(sensor->input, ABS_MT_TOOL_TYPE,
+ get_tool_type(sensor, finger_state));
+ } else {
+ input_mt_slot(sensor->input, n_finger);
+ input_mt_report_slot_state(sensor->input,
+ get_tool_type(sensor, finger_state), finger_state);
+ }
+
+ if (finger_state) {
+ input_report_abs(sensor->input, ABS_MT_PRESSURE, z);
+ 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);
+ dev_dbg(&sensor->fc->dev,
+ "finger[%d]:%d - x:%d y:%d z:%d w_max:%d w_min:%d\n",
+ n_finger, finger_state, x, y, z, w_max, w_min);
+ }
+ /* MT sync between fingers */
+ if (sensor->type_a)
+ input_mt_sync(sensor->input);
+}
+
+static void rmi_f11_finger_handler(struct f11_data *f11,
+ struct f11_2d_sensor *sensor)
+{
+ const u8 *f_state = sensor->data.f_state;
+ u8 finger_state;
+ u8 finger_pressed_count;
+ u8 i;
+
+ for (i = 0, finger_pressed_count = 0; i < sensor->nbr_fingers; i++) {
+ /* Possible of having 4 fingers per f_statet register */
+ finger_state = (f_state[i / 4] >> (2 * (i % 4))) &
+ FINGER_STATE_MASK;
+ if (finger_state == F11_RESERVED) {
+ pr_err("%s: Invalid finger state[%d]:0x%02x.", __func__,
+ i, finger_state);
+ continue;
+ } else if ((finger_state == F11_PRESENT) ||
+ (finger_state == F11_INACCURATE)) {
+ finger_pressed_count++;
+ }
+
+ if (sensor->data.abs_pos)
+ rmi_f11_abs_pos_report(f11, sensor, finger_state, i);
+
+ if (sensor->data.rel_pos)
+ rmi_f11_rel_pos_report(sensor, i);
+ }
+ input_mt_sync(sensor->input);
+ input_sync(sensor->input);
+}
+
+static int f11_2d_construct_data(struct f11_2d_sensor *sensor)
+{
+ struct f11_2d_sensor_queries *query = &sensor->sens_query;
+ struct f11_2d_data *data = &sensor->data;
+ int i;
+
+ sensor->nbr_fingers = (query->info.number_of_fingers == 5 ? 10 :
+ query->info.number_of_fingers + 1);
+
+ sensor->pkt_size = DIV_ROUND_UP(sensor->nbr_fingers, 4);
+
+ if (query->info.has_abs)
+ sensor->pkt_size += (sensor->nbr_fingers * 5);
+
+ if (query->info.has_rel)
+ sensor->pkt_size += (sensor->nbr_fingers * 2);
+
+ /* Check if F11_2D_Query7 is non-zero */
+ if (has_gesture_bits(&query->gesture_info, 0))
+ sensor->pkt_size += sizeof(u8);
+
+ /* Check if F11_2D_Query7 or F11_2D_Query8 is non-zero */
+ if (has_gesture_bits(&query->gesture_info, 0) ||
+ has_gesture_bits(&query->gesture_info, 1))
+ sensor->pkt_size += sizeof(u8);
+
+ if (query->gesture_info.has_pinch || query->gesture_info.has_flick
+ || query->gesture_info.has_rotate) {
+ sensor->pkt_size += 3;
+ if (!query->gesture_info.has_flick)
+ sensor->pkt_size--;
+ if (!query->gesture_info.has_rotate)
+ sensor->pkt_size--;
+ }
+
+ if (query->gesture_info.has_touch_shapes)
+ sensor->pkt_size +=
+ DIV_ROUND_UP(query->ts_info.nbr_touch_shapes + 1, 8);
+
+ sensor->data_pkt = kzalloc(sensor->pkt_size, GFP_KERNEL);
+ if (!sensor->data_pkt)
+ return -ENOMEM;
+
+ data->f_state = sensor->data_pkt;
+ i = DIV_ROUND_UP(sensor->nbr_fingers, 4);
+
+ if (query->info.has_abs) {
+ data->abs_pos = (struct f11_2d_data_1_5 *)
+ &sensor->data_pkt[i];
+ i += (sensor->nbr_fingers * 5);
+ }
+
+ if (query->info.has_rel) {
+ data->rel_pos = (struct f11_2d_data_6_7 *)
+ &sensor->data_pkt[i];
+ i += (sensor->nbr_fingers * 2);
+ }
+
+ if (has_gesture_bits(&query->gesture_info, 0)) {
+ data->gest_1 = (struct f11_2d_data_8 *)&sensor->data_pkt[i];
+ i++;
+ }
+
+ if (has_gesture_bits(&query->gesture_info, 0) ||
+ has_gesture_bits(&query->gesture_info, 1)) {
+ data->gest_2 = (struct f11_2d_data_9 *)&sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->gesture_info.has_pinch) {
+ data->pinch = (struct f11_2d_data_10 *)&sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->gesture_info.has_flick) {
+ if (query->gesture_info.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->gesture_info.has_rotate) {
+ if (query->gesture_info.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->gesture_info.has_touch_shapes)
+ data->shapes = (struct f11_2d_data_13 *)&sensor->data_pkt[i];
+
+ return 0;
+}
+
+static int f11_read_control_regs(struct rmi_function_container *fc,
+ struct f11_2d_ctrl *ctrl, u16 ctrl_base_addr) {
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ u16 read_address = ctrl_base_addr;
+ int error = 0;
+
+ ctrl->ctrl0_9_address = read_address;
+ error = rmi_read_block(rmi_dev, read_address, ctrl->ctrl0_9,
+ sizeof(*ctrl->ctrl0_9));
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to read ctrl0, code: %d.\n", error);
+ return error;
+ }
+ read_address += sizeof(*ctrl->ctrl0_9);
+
+ if (ctrl->ctrl10) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl10, sizeof(*ctrl->ctrl10));
+ if (error < 0) {
+ dev_err(&fc->dev,
+ "Failed to read ctrl10, code: %d.\n", error);
+ return error;
+ }
+ read_address += sizeof(*ctrl->ctrl10);
+ }
+
+ if (ctrl->ctrl11) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl11, sizeof(*ctrl->ctrl11));
+ if (error < 0) {
+ dev_err(&fc->dev,
+ "Failed to read ctrl11, code: %d.\n", error);
+ return error;
+ }
+ read_address += sizeof(*ctrl->ctrl11);
+ }
+
+ if (ctrl->ctrl14) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl14, sizeof(*ctrl->ctrl14));
+ if (error < 0) {
+ dev_err(&fc->dev,
+ "Failed to read ctrl14, code: %d.\n", error);
+ return error;
+ }
+ read_address += sizeof(*ctrl->ctrl14);
+ }
+
+ if (ctrl->ctrl15) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl15, sizeof(*ctrl->ctrl15));
+ if (error < 0) {
+ dev_err(&fc->dev,
+ "Failed to read ctrl15, code: %d.\n", error);
+ return error;
+ }
+ read_address += sizeof(*ctrl->ctrl15);
+ }
+
+ if (ctrl->ctrl16) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl16, sizeof(*ctrl->ctrl16));
+ if (error < 0) {
+ dev_err(&fc->dev,
+ "Failed to read ctrl16, code: %d.\n", error);
+ return error;
+ }
+ read_address += sizeof(*ctrl->ctrl16);
+ }
+
+ if (ctrl->ctrl17) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl17, sizeof(*ctrl->ctrl17));
+ if (error < 0) {
+ dev_err(&fc->dev,
+ "Failed to read ctrl17, code: %d.\n", error);
+ return error;
+ }
+ read_address += sizeof(*ctrl->ctrl17);
+ }
+
+ if (ctrl->ctrl18_19) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl18_19, sizeof(*ctrl->ctrl18_19));
+ if (error < 0) {
+ dev_err(&fc->dev,
+ "Failed to read ctrl18_19, code: %d.\n", error);
+ return error;
+ }
+ read_address += sizeof(*ctrl->ctrl18_19);
+ }
+
+ if (ctrl->ctrl20_21) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl20_21, sizeof(*ctrl->ctrl20_21));
+ if (error < 0) {
+ dev_err(&fc->dev,
+ "Failed to read ctrl20_21, code: %d.\n", error);
+ return error;
+ }
+ read_address += sizeof(*ctrl->ctrl20_21);
+ }
+
+ if (ctrl->ctrl22_26) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl22_26, sizeof(*ctrl->ctrl22_26));
+ if (error < 0) {
+ dev_err(&fc->dev,
+ "Failed to read ctrl22_26, code: %d.\n", error);
+ return error;
+ }
+ read_address += sizeof(*ctrl->ctrl22_26);
+ }
+
+ if (ctrl->ctrl27) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl27, sizeof(*ctrl->ctrl27));
+ if (error < 0) {
+ dev_err(&fc->dev,
+ "Failed to read ctrl27, code: %d.\n", error);
+ return error;
+ }
+ read_address += sizeof(*ctrl->ctrl27);
+ }
+
+ if (ctrl->ctrl28) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl28, sizeof(*ctrl->ctrl28));
+ if (error < 0) {
+ dev_err(&fc->dev,
+ "Failed to read ctrl28, code: %d.\n", error);
+ return error;
+ }
+ read_address += sizeof(*ctrl->ctrl28);
+ }
+
+ if (ctrl->ctrl29_30) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl29_30, sizeof(*ctrl->ctrl29_30));
+ if (error < 0) {
+ dev_err(&fc->dev,
+ "Failed to read ctrl29_30, code: %d.\n", error);
+ return error;
+ }
+ read_address += sizeof(*ctrl->ctrl29_30);
+ }
+ return 0;
+}
+
+static int f11_allocate_control_regs(struct rmi_function_container *fc,
+ struct f11_2d_device_query *device_query,
+ struct f11_2d_sensor_queries *sensor_query,
+ struct f11_2d_ctrl *ctrl,
+ u16 ctrl_base_addr) {
+
+ ctrl->ctrl0_9 = devm_kzalloc(&fc->dev, sizeof(struct f11_2d_ctrl0_9),
+ GFP_KERNEL);
+ if (!ctrl->ctrl0_9)
+ return -ENOMEM;
+ if (has_gesture_bits(&sensor_query->gesture_info, 0)) {
+ ctrl->ctrl10 = devm_kzalloc(&fc->dev,
+ sizeof(struct f11_2d_ctrl10), GFP_KERNEL);
+ if (!ctrl->ctrl10)
+ return -ENOMEM;
+ }
+
+ if (has_gesture_bits(&sensor_query->gesture_info, 1)) {
+ ctrl->ctrl11 = devm_kzalloc(&fc->dev,
+ sizeof(struct f11_2d_ctrl11), GFP_KERNEL);
+ if (!ctrl->ctrl11)
+ return -ENOMEM;
+ }
+
+ if (device_query->has_query9 && sensor_query->query9.has_pen) {
+ ctrl->ctrl20_21 = devm_kzalloc(&fc->dev,
+ sizeof(struct f11_2d_ctrl20_21), GFP_KERNEL);
+ if (!ctrl->ctrl20_21)
+ return -ENOMEM;
+ }
+
+ if (device_query->has_query9 && sensor_query->query9.has_proximity) {
+ ctrl->ctrl22_26 = devm_kzalloc(&fc->dev,
+ sizeof(struct f11_2d_ctrl22_26), GFP_KERNEL);
+ if (!ctrl->ctrl22_26)
+ return -ENOMEM;
+ }
+
+ if (device_query->has_query9 &&
+ (sensor_query->query9.has_palm_det_sensitivity ||
+ sensor_query->query9.has_suppress_on_palm_detect)) {
+ ctrl->ctrl27 = devm_kzalloc(&fc->dev,
+ sizeof(struct f11_2d_ctrl27), GFP_KERNEL);
+ if (!ctrl->ctrl27)
+ return -ENOMEM;
+ }
+
+ if (sensor_query->gesture_info.has_multi_finger_scroll) {
+ ctrl->ctrl28 = devm_kzalloc(&fc->dev,
+ sizeof(struct f11_2d_ctrl28), GFP_KERNEL);
+ if (!ctrl->ctrl28)
+ return -ENOMEM;
+ }
+
+ if (device_query->has_query11 &&
+ sensor_query->features_1.has_z_tuning) {
+ ctrl->ctrl29_30 = devm_kzalloc(&fc->dev,
+ sizeof(struct f11_2d_ctrl29_30), GFP_KERNEL);
+ if (!ctrl->ctrl29_30)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int f11_write_control_regs(struct rmi_function_container *fc,
+ struct f11_2d_sensor_queries *query,
+ struct f11_2d_ctrl *ctrl,
+ u16 ctrl_base_addr)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ u16 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, sizeof(*ctrl->ctrl10));
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl11) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl11, sizeof(*ctrl->ctrl11));
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl14) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl14, sizeof(ctrl->ctrl14));
+ if (error < 0)
+ return error;
+ write_address += sizeof(*ctrl->ctrl15);
+ }
+
+ if (ctrl->ctrl15) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl15, sizeof(*ctrl->ctrl15));
+ if (error < 0)
+ return error;
+ write_address += sizeof(*ctrl->ctrl15);
+ }
+
+ if (ctrl->ctrl16) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl16, sizeof(*ctrl->ctrl16));
+ if (error < 0)
+ return error;
+ write_address += sizeof(*ctrl->ctrl16);
+ }
+
+ if (ctrl->ctrl17) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl17, sizeof(*ctrl->ctrl17));
+ if (error < 0)
+ return error;
+ write_address += sizeof(*ctrl->ctrl17);
+ }
+
+ if (ctrl->ctrl18_19) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl18_19, sizeof(*ctrl->ctrl18_19));
+ if (error < 0)
+ return error;
+ write_address += sizeof(*ctrl->ctrl18_19);
+ }
+
+ if (ctrl->ctrl20_21) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl20_21, sizeof(*ctrl->ctrl20_21));
+ if (error < 0)
+ return error;
+ write_address += sizeof(*ctrl->ctrl20_21);
+ }
+
+ if (ctrl->ctrl22_26) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl22_26, sizeof(*ctrl->ctrl22_26));
+ if (error < 0)
+ return error;
+ write_address += sizeof(*ctrl->ctrl22_26);
+ }
+
+ if (ctrl->ctrl27) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl27, sizeof(*ctrl->ctrl27));
+ if (error < 0)
+ return error;
+ write_address += sizeof(*ctrl->ctrl27);
+ }
+
+ if (ctrl->ctrl28) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl28, sizeof(*ctrl->ctrl28));
+ if (error < 0)
+ return error;
+ write_address += sizeof(*ctrl->ctrl28);
+ }
+
+ if (ctrl->ctrl29_30) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl29_30,
+ sizeof(struct f11_2d_ctrl29_30));
+ if (error < 0)
+ return error;
+ write_address += sizeof(struct f11_2d_ctrl29_30);
+ }
+
+ return 0;
+}
+
+static int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev,
+ struct f11_2d_device_query *dev_query,
+ struct f11_2d_sensor_queries *sensor_query,
+ u16 query_base_addr)
+{
+ int query_size;
+ int rc;
+
+ rc = rmi_read_block(rmi_dev, query_base_addr,
+ &sensor_query->info, sizeof(sensor_query->info));
+ if (rc < 0)
+ return rc;
+ query_size = sizeof(sensor_query->info);
+
+ if (sensor_query->info.has_abs) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ &sensor_query->abs_info);
+ if (rc < 0)
+ return rc;
+ query_size++;
+ }
+
+ if (sensor_query->info.has_rel) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ &sensor_query->f11_2d_query6);
+ if (rc < 0)
+ return rc;
+ query_size++;
+ }
+
+ if (sensor_query->info.has_gestures) {
+ rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
+ &sensor_query->gesture_info,
+ sizeof(sensor_query->gesture_info));
+ if (rc < 0)
+ return rc;
+ query_size += sizeof(sensor_query->gesture_info);
+ }
+
+ if (dev_query->has_query9) {
+ rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
+ &sensor_query->query9,
+ sizeof(sensor_query->query9));
+ if (rc < 0)
+ return rc;
+ query_size += sizeof(sensor_query->query9);
+ }
+
+ if (sensor_query->gesture_info.has_touch_shapes) {
+ rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
+ &sensor_query->ts_info,
+ sizeof(sensor_query->ts_info));
+ if (rc < 0)
+ return rc;
+ query_size += sizeof(sensor_query->ts_info);
+ }
+
+ if (dev_query->has_query11) {
+ rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
+ &sensor_query->features_1,
+ sizeof(sensor_query->features_1));
+ if (rc < 0)
+ return rc;
+ query_size += sizeof(sensor_query->features_1);
+ }
+
+ if (dev_query->has_query12) {
+ rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
+ &sensor_query->features_2,
+ sizeof(sensor_query->features_2));
+ if (rc < 0)
+ return rc;
+ query_size += sizeof(sensor_query->features_2);
+ }
+
+ if (sensor_query->abs_info.has_jitter_filter) {
+ rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
+ &sensor_query->jitter_filter,
+ sizeof(sensor_query->jitter_filter));
+ if (rc < 0)
+ return rc;
+ query_size += sizeof(sensor_query->jitter_filter);
+ }
+
+ if (dev_query->has_query12 && sensor_query->features_2.has_info2) {
+ rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
+ &sensor_query->info_2,
+ sizeof(sensor_query->info_2));
+ if (rc < 0)
+ return rc;
+ query_size += sizeof(sensor_query->info_2);
+ }
+
+ return query_size;
+}
+
+/* This operation is done in a number of places, so we have a handy routine
+ * for it.
+ */
+static void f11_set_abs_params(struct rmi_function_container *fc, int index)
+{
+ struct f11_data *f11 = fc->data;
+ struct f11_2d_sensor *sensor = &f11->sensors[index];
+ struct input_dev *input = sensor->input;
+ int device_x_max =
+ f11->dev_controls.ctrl0_9->sensor_max_x_pos;
+ int device_y_max =
+ f11->dev_controls.ctrl0_9->sensor_max_y_pos;
+ int x_min, x_max, y_min, y_max;
+ unsigned int input_flags;
+
+ /* We assume touchscreen unless demonstrably a touchpad or specified
+ * as a touchpad in the platform data
+ */
+ if (sensor->sensor_type == rmi_f11_sensor_touchpad ||
+ (sensor->sens_query.features_2.has_info2 &&
+ !sensor->sens_query.info_2.is_clear))
+ input_flags = INPUT_PROP_POINTER;
+ else
+ input_flags = INPUT_PROP_DIRECT;
+ set_bit(input_flags, input->propbit);
+
+ if (sensor->axis_align.swap_axes) {
+ int temp = device_x_max;
+ device_x_max = device_y_max;
+ device_y_max = temp;
+ }
+ /* Use the max X and max Y read from the device, or the clip values,
+ * whichever is stricter.
+ */
+ x_min = sensor->axis_align.clip_X_low;
+ if (sensor->axis_align.clip_X_high)
+ x_max = min((int) device_x_max,
+ sensor->axis_align.clip_X_high);
+ else
+ x_max = device_x_max;
+
+ y_min = sensor->axis_align.clip_Y_low;
+ if (sensor->axis_align.clip_Y_high)
+ y_max = min((int) device_y_max,
+ sensor->axis_align.clip_Y_high);
+ else
+ y_max = device_y_max;
+
+ dev_dbg(&fc->dev, "Set ranges X=[%d..%d] Y=[%d..%d].",
+ x_min, x_max, y_min, y_max);
+
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0,
+ DEFAULT_MAX_ABS_MT_PRESSURE, 0, 0);
+ 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);
+ if (!sensor->type_a)
+ input_mt_init_slots(input, sensor->nbr_fingers, input_flags);
+ if (IS_ENABLED(CONFIG_RMI4_F11_PEN) &&
+ sensor->sens_query.query9.has_pen)
+ input_set_abs_params(input, ABS_MT_TOOL_TYPE,
+ 0, MT_TOOL_MAX, 0, 0);
+ else
+ input_set_abs_params(input, ABS_MT_TOOL_TYPE,
+ 0, MT_TOOL_FINGER, 0, 0);
+}
+
+static int rmi_f11_initialize(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct f11_data *f11;
+ struct f11_2d_ctrl *ctrl;
+ u8 query_offset;
+ u16 query_base_addr;
+ u16 control_base_addr;
+ u16 max_x_pos, max_y_pos, temp;
+ int rc;
+ int i;
+ struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
+
+ dev_dbg(&fc->dev, "Initializing F11 values for %s.\n",
+ pdata->sensor_name);
+
+ /*
+ ** init instance data, fill in values and create any sysfs files
+ */
+ f11 = devm_kzalloc(&fc->dev, sizeof(struct f11_data), GFP_KERNEL);
+ if (!f11)
+ return -ENOMEM;
+
+ fc->data = f11;
+ f11->rezero_wait_ms = pdata->f11_rezero_wait;
+
+ 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);
+ 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++) {
+ struct f11_2d_sensor *sensor = &f11->sensors[i];
+ sensor->sensor_index = i;
+ sensor->fc = fc;
+
+ rc = rmi_f11_get_query_parameters(rmi_dev, &f11->dev_query,
+ &sensor->sens_query, query_offset);
+ if (rc < 0)
+ return rc;
+ query_offset += rc;
+
+ rc = f11_allocate_control_regs(fc,
+ &f11->dev_query, &sensor->sens_query,
+ &f11->dev_controls, control_base_addr);
+ if (rc < 0) {
+ dev_err(&fc->dev,
+ "Failed to allocate F11 control params.\n");
+ return rc;
+ }
+
+ rc = f11_read_control_regs(fc, &f11->dev_controls,
+ control_base_addr);
+ if (rc < 0) {
+ dev_err(&fc->dev,
+ "Failed to read F11 control params.\n");
+ return rc;
+ }
+
+ if (i < pdata->f11_sensor_count) {
+ sensor->axis_align =
+ pdata->f11_sensor_data[i].axis_align;
+ sensor->type_a = pdata->f11_sensor_data[i].type_a;
+ sensor->sensor_type =
+ pdata->f11_sensor_data[i].sensor_type;
+ }
+
+ 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 (sensor->axis_align.swap_axes) {
+ temp = max_x_pos;
+ max_x_pos = max_y_pos;
+ max_y_pos = temp;
+ }
+ sensor->max_x = max_x_pos;
+ sensor->max_y = max_y_pos;
+
+ rc = f11_2d_construct_data(sensor);
+ if (rc < 0)
+ return rc;
+
+ ctrl = &f11->dev_controls;
+ if (sensor->axis_align.delta_x_threshold) {
+ ctrl->ctrl0_9->delta_x_threshold =
+ sensor->axis_align.delta_x_threshold;
+ rc = rmi_write_block(rmi_dev,
+ ctrl->ctrl0_9_address,
+ ctrl->ctrl0_9,
+ sizeof(*ctrl->ctrl0_9));
+ if (rc < 0)
+ dev_warn(&fc->dev, "Failed to write to delta_x_threshold %d. Code: %d.\n",
+ i, rc);
+
+ }
+
+ if (sensor->axis_align.delta_y_threshold) {
+ ctrl->ctrl0_9->delta_y_threshold =
+ sensor->axis_align.delta_y_threshold;
+ rc = rmi_write_block(rmi_dev,
+ ctrl->ctrl0_9_address,
+ ctrl->ctrl0_9,
+ sizeof(*ctrl->ctrl0_9));
+ if (rc < 0)
+ dev_warn(&fc->dev, "Failed to write to delta_y_threshold %d. Code: %d.\n",
+ i, rc);
+ }
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+ rc = setup_sensor_debugfs(sensor);
+ if (rc < 0)
+ dev_warn(&fc->dev, "Failed to setup debugfs for F11 sensor %d. Code: %d.\n",
+ i, rc);
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+ rc = setup_f11_debugfs(fc);
+ if (rc < 0)
+ dev_warn(&fc->dev, "Failed to setup debugfs for F11. Code: %d.\n",
+ rc);
+ }
+
+ mutex_init(&f11->dev_controls_mutex);
+ return 0;
+}
+
+static int rmi_f11_register_devices(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct f11_data *f11 = fc->data;
+ struct input_dev *input_dev;
+ struct input_dev *input_dev_mouse;
+ struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_driver *driver = rmi_dev->driver;
+ int sensors_itertd = 0;
+ int i;
+ int rc;
+ int board, version;
+
+ board = driver_data->board;
+ version = driver_data->rev;
+
+ for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) {
+ struct f11_2d_sensor *sensor = &f11->sensors[i];
+ sensors_itertd = i;
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ rc = -ENOMEM;
+ goto error_unregister;
+ }
+
+ sensor->input = input_dev;
+ if (driver->set_input_params) {
+ rc = driver->set_input_params(rmi_dev, input_dev);
+ if (rc < 0) {
+ dev_err(&fc->dev,
+ "%s: Error in setting input device.\n",
+ __func__);
+ goto error_unregister;
+ }
+ }
+ sprintf(sensor->input_phys, "%s.abs%d/input0",
+ dev_name(&fc->dev), i);
+ input_dev->phys = sensor->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_ABS, input_dev->evbit);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+
+ f11_set_abs_params(fc, i);
+
+ if (sensor->sens_query.info.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);
+ sensor->input = NULL;
+ goto error_unregister;
+ }
+
+ if (sensor->sens_query.info.has_rel) {
+ /*create input device for mouse events */
+ input_dev_mouse = input_allocate_device();
+ if (!input_dev_mouse) {
+ rc = -ENOMEM;
+ goto error_unregister;
+ }
+
+ sensor->mouse_input = input_dev_mouse;
+ if (driver->set_input_params) {
+ rc = driver->set_input_params(rmi_dev,
+ input_dev_mouse);
+ if (rc < 0) {
+ dev_err(&fc->dev,
+ "%s: Error in setting input device.\n",
+ __func__);
+ goto error_unregister;
+ }
+ }
+ sprintf(sensor->input_phys_mouse, "%s.rel%d/input0",
+ dev_name(&fc->dev), i);
+ 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);
+ sensor->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].mouse_input)
+ input_unregister_device(f11->sensors[i].mouse_input);
+ }
+}
+
+static int rmi_f11_create_sysfs(struct rmi_function_container *fc)
+{
+ int attr_count = 0;
+ int rc;
+
+ 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, &f11->sensors[i].sens_query,
+ &f11->dev_controls, fc->fd.query_base_addr);
+ if (rc < 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+int rmi_f11_attention(struct rmi_function_container *fc,
+ unsigned long *irq_bits)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct f11_data *f11 = fc->data;
+ u16 data_base_addr = fc->fd.data_base_addr;
+ u16 data_base_addr_offset = 0;
+ int error;
+ int i;
+
+ for (i = 0; i < f11->dev_query.nbr_of_sensors + 1; i++) {
+ error = rmi_read_block(rmi_dev,
+ data_base_addr + data_base_addr_offset,
+ f11->sensors[i].data_pkt,
+ f11->sensors[i].pkt_size);
+ if (error < 0)
+ return error;
+
+ rmi_f11_finger_handler(f11, &f11->sensors[i]);
+ data_base_addr_offset += f11->sensors[i].pkt_size;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+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. */
+ struct f11_2d_commands commands = {
+ .rezero = true,
+ };
+ int retval = 0;
+
+ dev_dbg(&fc->dev, "Resuming...\n");
+ if (!data->rezero_wait_ms)
+ return 0;
+
+ mdelay(data->rezero_wait_ms);
+
+ retval = rmi_write_block(rmi_dev, fc->fd.command_base_addr,
+ &commands, sizeof(commands));
+ if (retval < 0) {
+ dev_err(&fc->dev, "%s: failed to issue rezero command, error = %d.",
+ __func__, retval);
+ return retval;
+ }
+
+ return retval;
+}
+#endif /* CONFIG_PM */
+
+static int f11_remove_device(struct device *dev)
+{
+ int attr_count = 0;
+ struct f11_data *f11;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ f11 = fc->data;
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+ int i;
+
+ for (i = 0; i < f11->dev_query.nbr_of_sensors + 1; i++)
+ teardown_sensor_debugfs(&f11->sensors[i]);
+ teardown_f11_debugfs(f11);
+ }
+
+ 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);
+ return 0;
+}
+
+static int f11_device_init(struct rmi_function_container *fc)
+{
+ int rc;
+
+ rc = rmi_f11_initialize(fc);
+ if (rc < 0)
+ return rc;
+
+ rc = rmi_f11_register_devices(fc);
+ if (rc < 0)
+ return rc;
+
+ rc = rmi_f11_create_sysfs(fc);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static __devinit int f11_probe(struct device *dev)
+{
+ struct rmi_function_container *fc;
+
+ if (dev->type != &rmi_function_type)
+ return 1;
+
+ fc = to_rmi_function_container(dev);
+ if (fc->fd.function_number != FUNCTION_NUMBER)
+ return 1;
+
+ return f11_device_init(fc);
+}
+
+
+static struct rmi_function_handler function_handler = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rmi_f11",
+ .bus = &rmi_bus_type,
+ .probe = f11_probe,
+ .remove = f11_remove_device,
+ },
+ .func = FUNCTION_NUMBER,
+ .config = rmi_f11_config,
+ .attention = rmi_f11_attention,
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ .late_resume = rmi_f11_resume
+#elif defined(CONFIG_PM)
+ .resume = rmi_f11_resume
+#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
+};
+
+static int __init rmi_f11_module_init(void)
+{
+ int error;
+
+ error = driver_register(&function_handler.driver);
+ if (error < 0) {
+ pr_err("%s: register driver failed!\n", __func__);
+ return error;
+ }
+
+ return 0;
+}
+
+static void __exit rmi_f11_module_exit(void)
+{
+ driver_unregister(&function_handler.driver);
+}
+
+module_init(rmi_f11_module_init);
+module_exit(rmi_f11_module_exit);
+
+MODULE_AUTHOR("Christopher Heiny <[email protected]");
+MODULE_DESCRIPTION("RMI F11 module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);

2012-11-17 04:00:16

by Christopher Heiny

[permalink] [raw]
Subject: [RFC PATCH 03/06] input/rmi4: I2C physical interface


rmi_i2c.c abstracts an RMI4 device on some arbitrary I2C bus as a logical
device in the RMI bus. It handles reads/writes from/to the RMI4 devices,
and manages the page select register setting (since the meaning of page
select is dependent on the physical layer used to communicate with the RMi4
device).


Signed-off-by: Christopher Heiny <[email protected]>

Cc: Dmitry Torokhov <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>

Acked-by: Jean Delvare <[email protected]>

---

drivers/input/rmi4/rmi_i2c.c | 490 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 490 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..ca32101
--- /dev/null
+++ b/drivers/input/rmi4/rmi_i2c.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kconfig.h>
+#include <linux/lockdep.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include "rmi_driver.h"
+
+#define BUFFER_SIZE_INCREMENT 32
+/**
+ * struct rmi_i2c_data - stores information for i2c communication
+ *
+ * @page_mutex: Locks current page to avoid changing pages in unexpected ways.
+ * @page: Keeps track of the current virtual page
+ * @phys: Pointer to the physical interface
+ *
+ * @tx_buf: Buffer used for transmitting data to the sensor over i2c.
+ * @tx_buf_size: Size of the buffer
+ * @debug_buf: Buffer used for exposing buffer contents using dev_dbg
+ * @debug_buf_size: Size of the debug buffer.
+ *
+ * @comms_debug: Latest data read/written for debugging I2C communications
+ * @debugfs_comms: Debugfs file for debugging I2C communications
+ *
+ */
+struct rmi_i2c_data {
+ struct mutex page_mutex;
+ int page;
+ struct rmi_phys_device *phys;
+
+ u8 *tx_buf;
+ int tx_buf_size;
+ u8 *debug_buf;
+ int debug_buf_size;
+
+ bool comms_debug;
+#ifdef CONFIG_RMI4_DEBUG
+ struct dentry *debugfs_comms;
+#endif
+};
+
+#ifdef CONFIG_RMI4_DEBUG
+
+
+/**
+ * struct i2c_debugfs_data - stores information for debugfs
+ *
+ * @done: Indicates that we are done reading debug data. Subsequent reads
+ * will return EOF.
+ * @i2c_data: Pointer to the i2c data
+ *
+ */
+struct i2c_debugfs_data {
+ bool done;
+ struct rmi_i2c_data *i2c_data;
+};
+
+static int debug_open(struct inode *inodep, struct file *filp)
+{
+ struct i2c_debugfs_data *data;
+
+ data = kzalloc(sizeof(struct i2c_debugfs_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->i2c_data = inodep->i_private;
+ filp->private_data = data;
+ return 0;
+}
+
+static int debug_release(struct inode *inodep, struct file *filp)
+{
+ kfree(filp->private_data);
+ return 0;
+}
+
+static ssize_t comms_debug_read(struct file *filp, char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char *local_buf;
+ struct i2c_debugfs_data *dfs = filp->private_data;
+ struct rmi_i2c_data *data = dfs->i2c_data;
+
+ if (dfs->done)
+ return 0;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+
+ dfs->done = 1;
+
+ retval = snprintf(local_buf, PAGE_SIZE, "%u\n", data->comms_debug);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ retval = -EFAULT;
+ kfree(local_buf);
+
+ return retval;
+}
+
+static ssize_t comms_debug_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char *local_buf;
+ unsigned int new_value;
+ struct i2c_debugfs_data *dfs = filp->private_data;
+ struct rmi_i2c_data *data = dfs->i2c_data;
+
+ local_buf = kcalloc(size, sizeof(u8), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval) {
+ kfree(local_buf);
+ return -EFAULT;
+ }
+
+ retval = sscanf(local_buf, "%u", &new_value);
+ kfree(local_buf);
+ if (retval != 1 || new_value > 1)
+ return -EINVAL;
+
+ data->comms_debug = new_value;
+
+ return size;
+}
+
+
+static const struct file_operations comms_debug_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_open,
+ .release = debug_release,
+ .read = comms_debug_read,
+ .write = comms_debug_write,
+};
+
+static int setup_debugfs(struct rmi_device *rmi_dev, struct rmi_i2c_data *data)
+{
+ if (!rmi_dev->debugfs_root)
+ return -ENODEV;
+
+ data->debugfs_comms = debugfs_create_file("comms_debug", RMI_RW_ATTR,
+ rmi_dev->debugfs_root, data, &comms_debug_fops);
+ if (!data->debugfs_comms || IS_ERR(data->debugfs_comms)) {
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs comms_debug.\n");
+ data->debugfs_comms = NULL;
+ }
+
+ return 0;
+}
+
+static void teardown_debugfs(struct rmi_i2c_data *data)
+{
+ if (data->debugfs_comms)
+ debugfs_remove(data->debugfs_comms);
+}
+#endif
+
+#define COMMS_DEBUG(data) (IS_ENABLED(CONFIG_RMI4_DEBUG) && data->comms_debug)
+
+#define RMI_PAGE_SELECT_REGISTER 0xff
+#define RMI_I2C_PAGE(addr) (((addr) >> 8) & 0xff)
+
+static char *phys_proto_name = "i2c";
+
+/*
+ * 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, u8 page)
+{
+ struct i2c_client *client = to_i2c_client(phys->dev);
+ struct rmi_i2c_data *data = phys->data;
+ u8 txbuf[2] = {RMI_PAGE_SELECT_REGISTER, page};
+ int retval;
+
+ if (COMMS_DEBUG(data))
+ dev_dbg(&client->dev, "writes 3 bytes: %02x %02x\n",
+ txbuf[0], txbuf[1]);
+ 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 copy_to_debug_buf(struct device *dev, struct rmi_i2c_data *data,
+ const u8 *buf, const int len) {
+ int i;
+ int n = 0;
+ char *temp;
+ int dbg_size = 3 * len + 1;
+
+ if (!data->debug_buf || data->debug_buf_size < dbg_size) {
+ if (data->debug_buf)
+ devm_kfree(dev, data->debug_buf);
+ data->debug_buf_size = dbg_size + BUFFER_SIZE_INCREMENT;
+ data->debug_buf = devm_kzalloc(dev, data->debug_buf_size,
+ GFP_KERNEL);
+ if (!data->debug_buf) {
+ data->debug_buf_size = 0;
+ return -ENOMEM;
+ }
+ }
+ temp = data->debug_buf;
+
+ for (i = 0; i < len; i++) {
+ n = sprintf(temp, " %02x", buf[i]);
+ temp += n;
+ }
+
+ return 0;
+}
+
+static int rmi_i2c_write_block(struct rmi_phys_device *phys, u16 addr,
+ const void *buf, const int len)
+{
+ struct i2c_client *client = to_i2c_client(phys->dev);
+ struct rmi_i2c_data *data = phys->data;
+ int retval;
+ int tx_size = len + 1;
+
+ mutex_lock(&data->page_mutex);
+
+ if (!data->tx_buf || data->tx_buf_size < tx_size) {
+ if (data->tx_buf)
+ devm_kfree(&client->dev, data->tx_buf);
+ data->tx_buf_size = tx_size + BUFFER_SIZE_INCREMENT;
+ data->tx_buf = devm_kzalloc(&client->dev, data->tx_buf_size,
+ GFP_KERNEL);
+ if (!data->tx_buf) {
+ data->tx_buf_size = 0;
+ retval = -ENOMEM;
+ goto exit;
+ }
+ }
+ data->tx_buf[0] = addr & 0xff;
+ memcpy(data->tx_buf + 1, buf, len);
+
+ if (RMI_I2C_PAGE(addr) != data->page) {
+ retval = rmi_set_page(phys, RMI_I2C_PAGE(addr));
+ if (retval < 0)
+ goto exit;
+ }
+
+ if (COMMS_DEBUG(data)) {
+ retval = copy_to_debug_buf(&client->dev, data, (u8 *) buf, len);
+ if (!retval)
+ dev_dbg(&client->dev, "writes %d bytes at %#06x:%s\n",
+ len, addr, data->debug_buf);
+ }
+
+ phys->info.tx_count++;
+ phys->info.tx_bytes += tx_size;
+ retval = i2c_master_send(client, data->tx_buf, tx_size);
+ if (retval < 0)
+ phys->info.tx_errs++;
+ else
+ retval--; /* don't count the address byte */
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return retval;
+}
+
+
+static int rmi_i2c_read_block(struct rmi_phys_device *phys, u16 addr,
+ void *buf, const 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;
+
+ 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(data))
+ dev_dbg(&client->dev, "writes 1 bytes: %02x\n", txbuf[0]);
+
+ 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, (u8 *) buf, len);
+
+ phys->info.rx_count++;
+ phys->info.rx_bytes += len;
+ if (retval < 0)
+ phys->info.rx_errs++;
+ else if (COMMS_DEBUG(data)) {
+ retval = copy_to_debug_buf(&client->dev, data, (u8 *) buf, len);
+ if (!retval)
+ dev_dbg(&client->dev, "read %d bytes at %#06x:%s\n",
+ len, addr, data->debug_buf);
+ }
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return retval;
+}
+
+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 retval;
+
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
+ dev_info(&client->dev, "Probing %s at %#02x (IRQ %d).\n",
+ pdata->sensor_name ? pdata->sensor_name : "-no name-",
+ client->addr, pdata->attn_gpio);
+
+ if (pdata->gpio_config) {
+ dev_info(&client->dev, "Configuring GPIOs.\n");
+ retval = pdata->gpio_config(pdata->gpio_data, true);
+ if (retval < 0) {
+ dev_err(&client->dev, "Failed to configure GPIOs, code: %d.\n",
+ retval);
+ return retval;
+ }
+ dev_info(&client->dev, "Done with GPIO configuration.\n");
+ }
+
+ retval = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
+ if (!retval) {
+ dev_err(&client->dev, "i2c_check_functionality error %d.\n",
+ retval);
+ return retval;
+ }
+
+ rmi_phys = devm_kzalloc(&client->dev, sizeof(struct rmi_phys_device),
+ GFP_KERNEL);
+
+ if (!rmi_phys)
+ return -ENOMEM;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct rmi_i2c_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->phys = rmi_phys;
+
+ rmi_phys->data = data;
+ rmi_phys->dev = &client->dev;
+
+ rmi_phys->write_block = rmi_i2c_write_block;
+ rmi_phys->read_block = rmi_i2c_read_block;
+ 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.
+ */
+ retval = rmi_set_page(rmi_phys, 0);
+ if (retval) {
+ dev_err(&client->dev, "Failed to set page select to 0.\n");
+ return retval;
+ }
+
+ retval = rmi_register_phys_device(rmi_phys);
+ if (retval) {
+ dev_err(&client->dev,
+ "failed to register physical driver at 0x%.2X.\n",
+ client->addr);
+ goto err_gpio;
+ }
+ i2c_set_clientdata(client, rmi_phys);
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG))
+ retval = setup_debugfs(rmi_phys->rmi_dev, data);
+
+ dev_info(&client->dev, "registered rmi i2c driver at %#04x.\n",
+ client->addr);
+ return 0;
+
+err_gpio:
+ if (pdata->gpio_config)
+ pdata->gpio_config(pdata->gpio_data, false);
+ return retval;
+}
+
+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;
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG))
+ teardown_debugfs(phys->data);
+
+ rmi_unregister_phys_device(phys);
+
+ if (pd->gpio_config)
+ pd->gpio_config(&pd->gpio_data, false);
+
+ return 0;
+}
+
+static const struct i2c_device_id rmi_id[] = {
+ { "rmi_i2c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rmi_id);
+
+static struct i2c_driver rmi_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rmi_i2c"
+ },
+ .id_table = rmi_id,
+ .probe = rmi_i2c_probe,
+ .remove = __devexit_p(rmi_i2c_remove),
+};
+
+static int __init rmi_i2c_init(void)
+{
+ return i2c_add_driver(&rmi_i2c_driver);
+}
+
+static void __exit rmi_i2c_exit(void)
+{
+ i2c_del_driver(&rmi_i2c_driver);
+}
+
+module_init(rmi_i2c_init);
+module_exit(rmi_i2c_exit);
+
+MODULE_AUTHOR("Christopher Heiny <[email protected]>");
+MODULE_DESCRIPTION("RMI I2C driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);

2012-11-17 04:00:14

by Christopher Heiny

[permalink] [raw]
Subject: [RFC PATCH 04/06] input/rmi4: Config files and makefiles


Infrastructure files for configuration and building.


Signed-off-by: Christopher Heiny <[email protected]>

Cc: Dmitry Torokhov <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>


---

drivers/input/Kconfig | 2 +
drivers/input/Makefile | 3 ++
drivers/input/rmi4/Kconfig | 76 +++++++++++++++++++++++++++++++++++++++++++
drivers/input/rmi4/Makefile | 22 ++++++++++++
4 files changed, 103 insertions(+), 0 deletions(-)

diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 55f7e57..2c543c0 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -188,6 +188,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 5ca3f63..88354fc 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -24,4 +24,7 @@ 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
+
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
new file mode 100644
index 0000000..41cbbee
--- /dev/null
+++ b/drivers/input/rmi4/Kconfig
@@ -0,0 +1,76 @@
+#
+# RMI4 configuration
+#
+config RMI4_BUS
+ bool "Synaptics RMI4 bus support"
+ help
+ Say Y here if you want to support the Synaptics RMI4 bus. This is
+ required for all RMI4 device support.
+
+ If unsure, say Y.
+
+ This feature is not currently available as a loadable module.
+
+config RMI4_DEBUG
+ bool "RMI4 Debugging"
+ depends on RMI4_BUS
+ select DEBUG_FS
+ help
+ Say Y here to enable debug feature in the RMI4 driver.
+
+ Note that the RMI4 driver debug features can generate a lot of
+ output (potentially clogging up your dmesg output) and generally
+ slow down driver operation. It's recommended to enable them only
+ if you are actively developing/debugging RMI4 features.
+
+ If unsure, say N.
+
+config RMI4_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_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_F11
+ tristate "RMI4 Function 11 (2D pointing)"
+ depends on RMI4_BUS && RMI4_GENERIC
+ help
+ Say Y here if you want to add support for RMI4 function 11.
+
+ Function 11 provides 2D multifinger pointing for touchscreens and
+ touchpads. For sensors that support relative pointing, F11 also
+ provides mouse input.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rmi-f11.
+
+config RMI4_F11_PEN
+ bool "RMI4 F11 Pen Support"
+ depends on RMI4_F11
+ help
+ Say Y here to add support for pen input to RMI4 function 11.
+
+ If this feature is enabled, when pen inputs are detected they
+ will be reported to the input stream as MT_TOOL_PEN. Otherwise,
+ pens will be treated the same as fingers.
+
+ Not all UI implementations deal gracefully with pen discrimination.
+ If your system is not recognizing pen touches and you know your
+ sensor supports pen input, you probably want to turn this feature
+ off.
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
new file mode 100644
index 0000000..8882c3d
--- /dev/null
+++ b/drivers/input/rmi4/Makefile
@@ -0,0 +1,22 @@
+obj-$(CONFIG_RMI4_BUS) += rmi_bus.o
+obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
+obj-$(CONFIG_RMI4_GENERIC) += rmi_driver.o rmi_f01.o
+obj-$(CONFIG_RMI4_F11) += rmi_f11.o
+
+ccflags-$(CONFIG_RMI4_DEBUG) += -DDEBUG
+
+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
+
+endif
+

2012-11-17 21:54:22

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [RFC PATCH 02/06] input/rmi4: Core files

On Fri, Nov 16, 2012 at 07:58:50PM -0800, Christopher Heiny wrote:
> +static void release_rmidev_device(struct device *dev)
> +{
> + device_unregister(dev);
> +}

You just leaked memory here, right?

Also, you already unregistered the device, otherwise this function would
have never been called, so you just ended up in a loop?

Have you ever tried removing a device? Are you sure it's working
properly?

> +EXPORT_SYMBOL(rmi_register_phys_device);

Just curious, but why not EXPORT_SYMBOL_GPL() on all of these new
symbols you are creating?

thanks,

greg k-h

2012-11-17 22:41:22

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 01/06] input/rmi4: Public header and documentation

On Sat, Nov 17, 2012 at 4:58 AM, Christopher Heiny <[email protected]> wrote:

> rmi.h provides public definitions required by the RMI bus implementation and
> modules that interact with it.
>
> debugfs and sysfs attributes are documented in files in
> Documentation/ABI/testing. There's two files, one for debugfs and one for
> sysfs.
>
>
> Signed-off-by: Christopher Heiny <[email protected]>
>

Cut this white line.

> Cc: Dmitry Torokhov <[email protected]>
> Cc: Linus Walleij <[email protected]>
> Cc: Naveen Kumar Gaddipati <[email protected]>
> Cc: Joeri de Gram <[email protected]>

There is always room for improvement, but you've done a massive
job at fixing this driver to a state which is perfectly maintainable
in the kernel.
Reviewed-by: Linus Walleij <[email protected]>

Yours,
Linus Walleij

2012-11-17 22:45:30

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 02/06] input/rmi4: Core files

On Sat, Nov 17, 2012 at 4:58 AM, Christopher Heiny <[email protected]> wrote:

> rmi_bus.c implements the basic functionality of the RMI bus. This file is
> greatly simplified compared to the previous patch - we've switched from
> "do it yourself" device/driver binding to using device_type to distinguish
> between the two kinds of devices on the bus (sensor devices and function
> specific devices) and using the standard bus implementation to manage devices
> and drivers.

I have no further comments, I have reviewed up to my level of competence.
Next you need to fix comments from Greg & Dmitry, which will probably be
just as demanding :-)

But FWIW:
Reviewed-by: Linus Walleij <[email protected]>

Yours,
Linus Walleij

2012-11-17 22:47:23

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 03/06] input/rmi4: I2C physical interface

On Sat, Nov 17, 2012 at 4:58 AM, Christopher Heiny <[email protected]> wrote:

> rmi_i2c.c abstracts an RMI4 device on some arbitrary I2C bus as a logical
> device in the RMI bus. It handles reads/writes from/to the RMI4 devices,
> and manages the page select register setting (since the meaning of page
> select is dependent on the physical layer used to communicate with the RMi4
> device).
>
>
> Signed-off-by: Christopher Heiny <[email protected]>
>

Cut that blank line on all patches.

> Cc: Dmitry Torokhov <[email protected]>
> Cc: Linus Walleij <[email protected]>
> Cc: Naveen Kumar Gaddipati <[email protected]>
> Cc: Joeri de Gram <[email protected]>
>

And that one.

> Acked-by: Jean Delvare <[email protected]>

Looks good to me.
Reviewed-by: Linus Walleij <[email protected]>

Yours,
Linus Walleij

2012-11-17 22:50:39

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 04/06] input/rmi4: Config files and makefiles

On Sat, Nov 17, 2012 at 4:58 AM, Christopher Heiny <[email protected]> wrote:

> Infrastructure files for configuration and building.
>
>
> Signed-off-by: Christopher Heiny <[email protected]>
>

Cut blank line.

> Cc: Dmitry Torokhov <[email protected]>
> Cc: Linus Walleij <[email protected]>
> Cc: Naveen Kumar Gaddipati <[email protected]>
> Cc: Joeri de Gram <[email protected]>

(...)
> +++ b/drivers/input/rmi4/Makefile
> @@ -0,0 +1,22 @@
> +obj-$(CONFIG_RMI4_BUS) += rmi_bus.o
> +obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
> +obj-$(CONFIG_RMI4_GENERIC) += rmi_driver.o rmi_f01.o
> +obj-$(CONFIG_RMI4_F11) += rmi_f11.o
> +
> +ccflags-$(CONFIG_RMI4_DEBUG) += -DDEBUG
> +


>From here:

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

This stuff looks weird. Like some local build rule to just rebuild
this part, and the commented-out Android thing is really just
cruft. Just delete all this, keep it in your private delta
if you need it.

Yours,
Linus Walleij

2012-11-17 22:54:47

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 06/06] input/rmi4: F11 - 2D touch interface

On Sat, Nov 17, 2012 at 4:58 AM, Christopher Heiny <[email protected]> wrote:

> rmi_f11.c is a driver for 2D touch sensors using the RMI4 protocol. It supports
> both touchscreen and touchpad input, in both absolute and relative formats.
> Support for Type-B multitouch is the default, Type-A support is included for
> certain legacy sensors.
>
>
> Signed-off-by: Christopher Heiny <[email protected]>
>

Cut blank line.

> To: Henrik Rydberg <[email protected]>
> Cc: Dmitry Torokhov <[email protected]>
> Cc: Linus Walleij <[email protected]>
> Cc: Naveen Kumar Gaddipati <[email protected]>
> Cc: Joeri de Gram <[email protected]>

I'm happy. Now you only need to convince Dmitry and Henrik too ;-)

Reviewed-by: Linus Walleij <[email protected]>

Yours,
Linus Walleij

2012-11-17 22:56:18

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 05/06] input/rmi4: F01 - device control

On Sat, Nov 17, 2012 at 4:58 AM, Christopher Heiny <[email protected]> wrote:

> RMI Function 01 implements basic device control and power management
> behaviors for the RMI4 sensor.
>
> rmi_f01.h exports definitions that we expect to be used by other functionality
> in the future (such as firmware reflash).
>
>
> Signed-off-by: Christopher Heiny <[email protected]>
>

Cut blank line.

> Cc: Dmitry Torokhov <[email protected]>
> Cc: Linus Walleij <[email protected]>
> Cc: Naveen Kumar Gaddipati <[email protected]>
> Cc: Joeri de Gram <[email protected]>

All other comments I've had appear to be fixed.
Reviewed-by: Linus Walleij <[email protected]>

Yours,
Linus Walleij

2012-11-20 04:56:16

by Christopher Heiny

[permalink] [raw]
Subject: Re: [RFC PATCH 02/06] input/rmi4: Core files

On 11/17/2012 01:54 PM, Greg Kroah-Hartman wrote:
> On Fri, Nov 16, 2012 at 07:58:50PM -0800, Christopher Heiny wrote:
>> +static void release_rmidev_device(struct device *dev)
>> +{
>> + device_unregister(dev);
>> +}
>
> You just leaked memory here, right?
>
> Also, you already unregistered the device, otherwise this function would
> have never been called, so you just ended up in a loop?

Roger. We'll fix that.


> Have you ever tried removing a device? Are you sure it's working
> properly?

Hmmmm. If it leads to the loop you mention above, then the test I'm
using must not be doing what I thought it was. I'll fix that, too.


>> +EXPORT_SYMBOL(rmi_register_phys_device);
>
> Just curious, but why not EXPORT_SYMBOL_GPL() on all of these new
> symbols you are creating?

We'll change that.

Thanks very much!
Chris

2012-11-26 09:40:26

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC PATCH 05/06] input/rmi4: F01 - device control

Hi Christopher,

On Fri, Nov 16, 2012 at 07:58:53PM -0800, Christopher Heiny wrote:
> RMI Function 01 implements basic device control and power management
> behaviors for the RMI4 sensor.
>
> rmi_f01.h exports definitions that we expect to be used by other functionality
> in the future (such as firmware reflash).

Please see my comments below.

>
>
> Signed-off-by: Christopher Heiny <[email protected]>
>
> Cc: Dmitry Torokhov <[email protected]>
> Cc: Linus Walleij <[email protected]>
> Cc: Naveen Kumar Gaddipati <[email protected]>
> Cc: Joeri de Gram <[email protected]>
>
>
> ---
>
> drivers/input/rmi4/rmi_f01.c | 1348 ++++++++++++++++++++++++++++++++++++++++++
> drivers/input/rmi4/rmi_f01.h | 160 +++++
> 2 files changed, 1508 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..038266c
> --- /dev/null
> +++ b/drivers/input/rmi4/rmi_f01.c
> @@ -0,0 +1,1348 @@
> +/*
> + * Copyright (c) 2011-2012 Synaptics Incorporated
> + * Copyright (c) 2011 Unixphere
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Please strip FSF address from your files.

> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/debugfs.h>
> +#include <linux/kconfig.h>
> +#include <linux/rmi.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include "rmi_driver.h"
> +#include "rmi_f01.h"
> +
> +#define FUNCTION_NUMBER 0x01
> +
> +/**
> + * @reset - set this bit to force a firmware reset of the sensor.
> + */
> +struct f01_device_commands {
> + bool reset:1;
> + u8 reserved:7;

When specifying bitwise fields please use u8, u16, etc only.

> +};
> +
> +/**
> + * @ctrl0 - see documentation in rmi_f01.h.
> + * @interrupt_enable - A mask of per-function interrupts on the touch sensor.
> + * @doze_interval - controls the interval between checks for finger presence
> + * when the touch sensor is in doze mode, in units of 10ms.
> + * @wakeup_threshold - controls the capacitance threshold at which the touch
> + * sensor will decide to wake up from that low power state.
> + * @doze_holdoff - controls how long the touch sensor waits after the last
> + * finger lifts before entering the doze state, in units of 100ms.
> + */
> +struct f01_device_control {
> + struct f01_device_control_0 ctrl0;
> + u8 *interrupt_enable;
> + u8 doze_interval;
> + u8 wakeup_threshold;
> + u8 doze_holdoff;
> +};
> +
> +/**
> + * @has_ds4_queries - if true, the query registers relating to Design Studio 4
> + * features are present.
> + * @has_multi_phy - if true, multiple physical communications interfaces are
> + * supported.
> + * @has_guest - if true, a "guest" device is supported.
> + */
> +struct f01_query_42 {
> + bool has_ds4_queries:1;
> + bool has_multi_phy:1;
> + bool has_guest:1;
> + u8 reserved:5;
> +} __attribute__((__packed__));
> +
> +/**
> + * @length - the length of the remaining Query43.* register block, not
> + * including the first register.
> + * @has_package_id_query - the package ID query data will be accessible from
> + * inside the ProductID query registers.
> + * @has_packrat_query - the packrat query data will be accessible from inside
> + * the ProductID query registers.
> + * @has_reset_query - the reset pin related registers are valid.
> + * @has_maskrev_query - the silicon mask revision number will be reported.
> + * @has_i2c_control - the register F01_RMI_Ctrl6 will exist.
> + * @has_spi_control - the register F01_RMI_Ctrl7 will exist.
> + * @has_attn_control - the register F01_RMI_Ctrl8 will exist.
> + * @reset_enabled - the hardware reset pin functionality has been enabled
> + * for this device.
> + * @reset_polarity - If this bit reports as ‘0’, it means that the reset state
> + * is active low. A ‘1’ means that the reset state is active high.
> + * @pullup_enabled - If set, it indicates that a built-in weak pull up has
> + * been enabled on the Reset pin; clear means that no pull-up is present.
> + * @reset_pin_number - This field represents which GPIO pin number has been
> + * assigned the reset functionality.
> + */
> +struct f01_ds4_queries {
> + u8 length:4;
> + u8 reserved_1:4;
> +
> + bool has_package_id_query:1;
> + bool has_packrat_query:1;
> + bool has_reset_query:1;
> + bool has_maskrev_query:1;
> + u8 reserved_2:4;
> +
> + bool has_i2c_control:1;
> + bool has_spi_control:1;
> + bool has_attn_control:1;
> + u8 reserved_3:5;
> +
> + bool reset_enabled:1;
> + bool reset_polarity:1;
> + bool pullup_enabled:1;
> + u8 reserved_4:1;
> + u8 reset_pin_number:4;
> +} __attribute__((__packed__));
> +
> +struct f01_data {
> + struct f01_device_control device_control;
> + struct f01_basic_queries basic_queries;
> + struct f01_device_status dhevice_status;
> + u8 product_id[RMI_PRODUCT_ID_LENGTH+1];
> +
> + u16 interrupt_enable_addr;
> + u16 doze_interval_addr;
> + u16 wakeup_threshold_addr;
> + u16 doze_holdoff_addr;
> +
> + int irq_count;
> + int num_of_irq_regs;
> +
> +#ifdef CONFIG_PM
> + bool suspended;
> + bool old_nosleep;
> +#endif
> +
> +#ifdef CONFIG_RMI4_DEBUG
> + struct dentry *debugfs_interrupt_enable;
> +#endif
> +};
> +
> +#ifdef CONFIG_RMI4_DEBUG
> +struct f01_debugfs_data {
> + bool done;
> + struct rmi_function_container *fc;
> +};
> +
> +static int f01_debug_open(struct inode *inodep, struct file *filp)
> +{
> + struct f01_debugfs_data *data;
> + struct rmi_function_container *fc = inodep->i_private;
> +
> + data = kzalloc(sizeof(struct f01_debugfs_data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + data->fc = fc;
> + filp->private_data = data;
> + return 0;
> +}
> +
> +static int f01_debug_release(struct inode *inodep, struct file *filp)
> +{
> + kfree(filp->private_data);
> + return 0;
> +}
> +
> +static ssize_t interrupt_enable_read(struct file *filp, char __user *buffer,
> + size_t size, loff_t *offset) {
> + int i;
> + int len;
> + int total_len = 0;
> + char local_buf[size];
> + char *current_buf = local_buf;
> + struct f01_debugfs_data *data = filp->private_data;
> + struct f01_data *f01 = data->fc->data;
> +
> + if (data->done)
> + return 0;
> +
> + data->done = 1;

Why do we do this only once?

> +
> + /* loop through each irq value and copy its
> + * string representation into buf */
> + for (i = 0; i < f01->irq_count; i++) {
> + int irq_reg;
> + int irq_shift;
> + int interrupt_enable;
> +
> + irq_reg = i / 8;
> + irq_shift = i % 8;
> + interrupt_enable =
> + ((f01->device_control.interrupt_enable[irq_reg]
> + >> irq_shift) & 0x01);
> +
> + /* get next irq value and write it to buf */
> + len = snprintf(current_buf, size - total_len,
> + "%u ", interrupt_enable);
> + /* bump up ptr to next location in buf if the
> + * snprintf was valid. Otherwise issue an error
> + * and return. */
> + if (len > 0) {
> + current_buf += len;
> + total_len += len;
> + } else {
> + dev_err(&data->fc->dev, "Failed to build interrupt_enable buffer, code = %d.\n",
> + len);
> + return snprintf(local_buf, size, "unknown\n");
> + }
> + }
> + len = snprintf(current_buf, size - total_len, "\n");
> + if (len > 0)
> + total_len += len;
> + else
> + dev_warn(&data->fc->dev, "%s: Failed to append carriage return.\n",
> + __func__);
> +
> + if (copy_to_user(buffer, local_buf, total_len))
> + return -EFAULT;
> +
> + return total_len;
> +}
> +
> +static ssize_t interrupt_enable_write(struct file *filp,
> + const char __user *buffer, size_t size, loff_t *offset) {
> + int retval;
> + char buf[size];
> + char *local_buf = buf;
> + int i;
> + int irq_count = 0;
> + int irq_reg = 0;
> + struct f01_debugfs_data *data = filp->private_data;
> + struct f01_data *f01 = data->fc->data;
> +
> + retval = copy_from_user(buf, buffer, size);
> + if (retval)
> + return -EFAULT;
> +
> + for (i = 0; i < f01->irq_count && *local_buf != 0;
> + i++, local_buf += 2) {
> + int irq_shift;
> + int interrupt_enable;
> + int result;
> +
> + irq_reg = i / 8;
> + irq_shift = i % 8;
> +
> + /* get next interrupt mapping value and store and bump up to
> + * point to next item in local_buf */
> + result = sscanf(local_buf, "%u", &interrupt_enable);
> + if ((result != 1) ||
> + (interrupt_enable != 0 && interrupt_enable != 1)) {
> + dev_err(&data->fc->dev, "Interrupt enable[%d] is not a valid value 0x%x.\n",
> + i, interrupt_enable);
> + return -EINVAL;
> + }
> + if (interrupt_enable == 0) {
> + f01->device_control.interrupt_enable[irq_reg] &=
> + (1 << irq_shift) ^ 0xFF;
> + } else
> + f01->device_control.interrupt_enable[irq_reg] |=
> + (1 << irq_shift);
> + irq_count++;
> + }
> +
> + /* Make sure the irq count matches */
> + if (irq_count != f01->irq_count) {
> + dev_err(&data->fc->dev, "Interrupt enable count of %d doesn't match device count of %d.\n",
> + irq_count, f01->irq_count);
> + return -EINVAL;
> + }
> +
> + /* write back to the control register */
> + retval = rmi_write_block(data->fc->rmi_dev, f01->interrupt_enable_addr,
> + f01->device_control.interrupt_enable,
> + f01->num_of_irq_regs);
> + if (retval < 0) {
> + dev_err(&data->fc->dev, "Could not write interrupt_enable mask to %#06x\n",
> + f01->interrupt_enable_addr);
> + return retval;
> + }
> +
> + return size;
> +}
> +
> +static const struct file_operations interrupt_enable_fops = {
> + .owner = THIS_MODULE,
> + .open = f01_debug_open,
> + .release = f01_debug_release,
> + .read = interrupt_enable_read,
> + .write = interrupt_enable_write,
> +};
> +
> +static int setup_debugfs(struct rmi_function_container *fc)
> +{
> + struct f01_data *data = fc->data;
> +
> + if (!fc->debugfs_root)
> + return -ENODEV;
> +
> + data->debugfs_interrupt_enable = debugfs_create_file("interrupt_enable",
> + RMI_RW_ATTR, fc->debugfs_root, fc, &interrupt_enable_fops);
> + if (!data->debugfs_interrupt_enable)
> + dev_warn(&fc->dev,
> + "Failed to create debugfs interrupt_enable.\n");
> +
> + return 0;
> +}
> +
> +static void teardown_debugfs(struct f01_data *f01)
> +{
> + if (f01->debugfs_interrupt_enable)
> + debugfs_remove(f01->debugfs_interrupt_enable);
> +}

You also need dummy stubs in case CONFIG_RMI4_DEBUG is not
defined.

> +#endif
> +
> +/* Utility routine to set the value of a bit field in a register. */
> +int rmi_mask_and_set(struct rmi_device *rmi_dev,
> + u16 address,
> + u8 mask,
> + u8 set)

This functions does not seem to be used.

> +{
> + u8 reg_contents;
> + int retval;
> +
> + retval = rmi_read(rmi_dev, address, &reg_contents);
> + if (retval < 0)
> + return retval;
> + reg_contents = (reg_contents & ~mask) | set;
> + retval = rmi_write(rmi_dev, address, reg_contents);
> + if (retval == 1)
> + return 0;
> + else if (retval == 0)
> + return -EIO;
> + return retval;
> +}
> +
> +static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n",
> + data->basic_queries.productinfo_1,
> + data->basic_queries.productinfo_2);
> +}
> +
> +static ssize_t rmi_fn_01_productid_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "%s\n", data->product_id);
> +}
> +
> +static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "0x%02x\n",
> + data->basic_queries.manufacturer_id);
> +}
> +
> +static ssize_t rmi_fn_01_datecode_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "20%02u-%02u-%02u\n",
> + data->basic_queries.year,
> + data->basic_queries.month,
> + data->basic_queries.day);
> +}
> +
> +static ssize_t rmi_fn_01_reset_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct rmi_function_container *fc = NULL;
> + unsigned int reset;
> + int retval = 0;
> +
> + 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) {
> + /* Command register always reads as 0, so just use a local. */
> + struct f01_device_commands commands = {
> + .reset = 1
> + };
> + retval = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
> + &commands, sizeof(commands));
> + if (retval < 0) {
> + dev_err(dev, "Failed to issue reset command, code = %d.",
> + retval);
> + return retval;
> + }
> + }
> +
> + return count;
> +}
> +
> +static ssize_t rmi_fn_01_sleepmode_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE,
> + "%d\n", data->device_control.ctrl0.sleep_mode);
> +}
> +
> +static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct f01_data *data = NULL;
> + unsigned long new_value;
> + int retval;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + retval = strict_strtoul(buf, 10, &new_value);
> + if (retval < 0 || !RMI_IS_VALID_SLEEPMODE(new_value)) {
> + dev_err(dev, "%s: Invalid sleep mode %s.", __func__, buf);
> + return -EINVAL;
> + }
> +
> + dev_dbg(dev, "Setting sleep mode to %ld.", new_value);
> + data->device_control.ctrl0.sleep_mode = new_value;
> + retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
> + &data->device_control.ctrl0,
> + sizeof(data->device_control.ctrl0));
> + if (retval >= 0)
> + retval = count;
> + else
> + dev_err(dev, "Failed to write sleep mode, code %d.\n", retval);
> + return retval;
> +}
> +
> +static ssize_t rmi_fn_01_nosleep_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "%d\n",
> + data->device_control.ctrl0.nosleep);
> +}
> +
> +static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct f01_data *data = NULL;
> + unsigned long new_value;
> + int retval;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + retval = strict_strtoul(buf, 10, &new_value);
> + if (retval < 0 || new_value > 1) {
> + dev_err(dev, "%s: Invalid nosleep bit %s.", __func__, buf);
> + return -EINVAL;
> + }
> +
> + data->device_control.ctrl0.nosleep = new_value;
> + retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
> + &data->device_control.ctrl0,
> + sizeof(data->device_control.ctrl0));
> + if (retval >= 0)
> + retval = count;
> + else
> + dev_err(dev, "Failed to write nosleep bit.\n");
> + return retval;
> +}
> +
> +static ssize_t rmi_fn_01_chargerinput_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "%d\n",
> + data->device_control.ctrl0.charger_input);
> +}
> +
> +static ssize_t rmi_fn_01_chargerinput_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct f01_data *data = NULL;
> + unsigned long new_value;
> + int retval;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + retval = strict_strtoul(buf, 10, &new_value);
> + if (retval < 0 || new_value > 1) {
> + dev_err(dev, "%s: Invalid chargerinput bit %s.", __func__, buf);
> + return -EINVAL;
> + }
> +
> + data->device_control.ctrl0.charger_input = new_value;
> + retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
> + &data->device_control.ctrl0,
> + sizeof(data->device_control.ctrl0));
> + if (retval >= 0)
> + retval = count;
> + else
> + dev_err(dev, "Failed to write chargerinput bit.\n");
> + return retval;
> +}
> +
> +static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "%d\n",
> + data->device_control.ctrl0.report_rate);
> +}
> +
> +static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct f01_data *data = NULL;
> + unsigned long new_value;
> + int retval;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + retval = strict_strtoul(buf, 10, &new_value);
> + if (retval < 0 || new_value > 1) {
> + dev_err(dev, "%s: Invalid reportrate bit %s.", __func__, buf);
> + return -EINVAL;
> + }
> +
> + data->device_control.ctrl0.report_rate = new_value;
> + retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
> + &data->device_control.ctrl0,
> + sizeof(data->device_control.ctrl0));
> + if (retval >= 0)
> + retval = count;
> + else
> + dev_err(dev, "Failed to write reportrate bit.\n");
> + return retval;
> +}
> +
> +static ssize_t rmi_fn_01_interrupt_enable_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct rmi_function_container *fc;
> + struct f01_data *data;
> + int i, len, total_len = 0;
> + char *current_buf = buf;
> +
> + fc = to_rmi_function_container(dev);
> + data = fc->data;
> + /* loop through each irq value and copy its
> + * string representation into buf */
> + for (i = 0; i < data->irq_count; i++) {
> + int irq_reg;
> + int irq_shift;
> + int interrupt_enable;
> +
> + irq_reg = i / 8;
> + irq_shift = i % 8;
> + interrupt_enable =
> + ((data->device_control.interrupt_enable[irq_reg]
> + >> irq_shift) & 0x01);
> +
> + /* get next irq value and write it to buf */
> + len = snprintf(current_buf, PAGE_SIZE - total_len,
> + "%u ", interrupt_enable);
> + /* bump up ptr to next location in buf if the
> + * snprintf was valid. Otherwise issue an error
> + * and return. */
> + if (len > 0) {
> + current_buf += len;
> + total_len += len;
> + } else {
> + dev_err(dev, "Failed to build interrupt_enable buffer, code = %d.\n",
> + len);
> + return snprintf(buf, PAGE_SIZE, "unknown\n");
> + }
> + }
> + len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
> + if (len > 0)
> + total_len += len;
> + else
> + dev_warn(dev, "%s: Failed to append carriage return.\n",
> + __func__);
> + return total_len;
> +
> +}
> +
> +static ssize_t rmi_fn_01_doze_interval_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "%d\n",
> + data->device_control.doze_interval);
> +
> +}
> +
> +static ssize_t rmi_fn_01_doze_interval_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct f01_data *data = NULL;
> + unsigned long new_value;
> + int retval;
> + u16 ctrl_base_addr;
> +
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + retval = strict_strtoul(buf, 10, &new_value);
> + if (retval < 0 || new_value > 255) {
> + dev_err(dev, "%s: Invalid doze interval %s.", __func__, buf);
> + return -EINVAL;
> + }
> +
> + data->device_control.doze_interval = new_value;
> + ctrl_base_addr = fc->fd.control_base_addr + sizeof(u8) +
> + (sizeof(u8)*(data->num_of_irq_regs));
> + dev_dbg(dev, "doze_interval store address %x, value %d",
> + ctrl_base_addr, data->device_control.doze_interval);
> +
> + retval = rmi_write_block(fc->rmi_dev, data->doze_interval_addr,
> + &data->device_control.doze_interval,
> + sizeof(u8));
> + if (retval >= 0)
> + retval = count;
> + else
> + dev_err(dev, "Failed to write doze interval.\n");
> + return retval;
> +
> +}
> +
> +static ssize_t rmi_fn_01_wakeup_threshold_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "%d\n",
> + data->device_control.wakeup_threshold);
> +}
> +
> +static ssize_t rmi_fn_01_wakeup_threshold_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct f01_data *data = NULL;
> + unsigned long new_value;
> + int retval;
> +
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + retval = strict_strtoul(buf, 10, &new_value);
> + if (retval < 0 || new_value > 255) {
> + dev_err(dev, "%s: Invalid wakeup threshold %s.", __func__, buf);
> + return -EINVAL;
> + }
> +
> + data->device_control.doze_interval = new_value;
> + retval = rmi_write_block(fc->rmi_dev, data->wakeup_threshold_addr,
> + &data->device_control.wakeup_threshold,
> + sizeof(u8));
> + if (retval >= 0)
> + retval = count;
> + else
> + dev_err(dev, "Failed to write wakeup threshold.\n");
> + return retval;
> +
> +}
> +
> +static ssize_t rmi_fn_01_doze_holdoff_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "%d\n",
> + data->device_control.doze_holdoff);
> +
> +}
> +
> +
> +static ssize_t rmi_fn_01_doze_holdoff_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct f01_data *data = NULL;
> + unsigned long new_value;
> + int retval;
> +
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + retval = strict_strtoul(buf, 10, &new_value);
> + if (retval < 0 || new_value > 255) {
> + dev_err(dev, "%s: Invalid doze holdoff %s.", __func__, buf);
> + return -EINVAL;
> + }
> +
> + data->device_control.doze_interval = new_value;
> + retval = rmi_write_block(fc->rmi_dev, data->doze_holdoff_addr,
> + &data->device_control.doze_holdoff,
> + sizeof(u8));
> + if (retval >= 0)
> + retval = count;
> + else
> + dev_err(dev, "Failed to write doze holdoff.\n");
> + return retval;
> +
> +}
> +
> +static ssize_t rmi_fn_01_configured_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "%d\n",
> + data->device_control.ctrl0.configured);
> +}
> +
> +static ssize_t rmi_fn_01_unconfigured_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "%d\n",
> + data->device_status.unconfigured);
> +}
> +
> +static ssize_t rmi_fn_01_flashprog_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "%d\n",
> + data->device_status.flash_prog);
> +}
> +
> +static ssize_t rmi_fn_01_statuscode_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct f01_data *data = NULL;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + data = fc->data;
> +
> + return snprintf(buf, PAGE_SIZE, "0x%02x\n",
> + data->device_status.status_code);
> +}
> +
> +static struct device_attribute fn_01_attrs[] = {
> + __ATTR(productinfo, RMI_RO_ATTR,
> + rmi_fn_01_productinfo_show, NULL),
> + __ATTR(productid, RMI_RO_ATTR,
> + rmi_fn_01_productid_show, NULL),
> + __ATTR(manufacturer, RMI_RO_ATTR,
> + rmi_fn_01_manufacturer_show, NULL),
> + __ATTR(datecode, RMI_RO_ATTR,
> + rmi_fn_01_datecode_show, NULL),
> +
> + /* control register access */
> + __ATTR(sleepmode, RMI_RW_ATTR,
> + rmi_fn_01_sleepmode_show, rmi_fn_01_sleepmode_store),
> + __ATTR(nosleep, RMI_RW_ATTR,
> + rmi_fn_01_nosleep_show, rmi_fn_01_nosleep_store),
> + __ATTR(chargerinput, RMI_RW_ATTR,
> + rmi_fn_01_chargerinput_show, rmi_fn_01_chargerinput_store),
> + __ATTR(reportrate, RMI_RW_ATTR,
> + rmi_fn_01_reportrate_show, rmi_fn_01_reportrate_store),
> + /* We don't want arbitrary callers changing the interrupt enable mask,
> + * so it's read only.
> + */
> + __ATTR(interrupt_enable, RMI_RO_ATTR,
> + rmi_fn_01_interrupt_enable_show, NULL),
> + __ATTR(doze_interval, RMI_RW_ATTR,
> + rmi_fn_01_doze_interval_show, rmi_fn_01_doze_interval_store),
> + __ATTR(wakeup_threshold, RMI_RW_ATTR,
> + rmi_fn_01_wakeup_threshold_show,
> + rmi_fn_01_wakeup_threshold_store),
> + __ATTR(doze_holdoff, RMI_RW_ATTR,
> + rmi_fn_01_doze_holdoff_show, rmi_fn_01_doze_holdoff_store),
> +
> + /* We make report rate RO, since the driver uses that to look for
> + * resets. We don't want someone faking us out by changing that
> + * bit.
> + */
> + __ATTR(configured, RMI_RO_ATTR,
> + rmi_fn_01_configured_show, NULL),
> +
> + /* Command register access. */
> + __ATTR(reset, RMI_WO_ATTR,
> + NULL, rmi_fn_01_reset_store),
> +
> + /* STatus register access. */
> + __ATTR(unconfigured, RMI_RO_ATTR,
> + rmi_fn_01_unconfigured_show, NULL),
> + __ATTR(flashprog, RMI_RO_ATTR,
> + rmi_fn_01_flashprog_show, NULL),
> + __ATTR(statuscode, RMI_RO_ATTR,
> + rmi_fn_01_statuscode_show, NULL),
> +};

The attributes above should be wrapped into attribute group.

> +
> +static int rmi_f01_alloc_memory(struct rmi_function_container *fc,
> + int num_of_irq_regs)
> +{
> + struct f01_data *f01;
> +
> + f01 = devm_kzalloc(&fc->dev, sizeof(struct f01_data), GFP_KERNEL);
> + if (!f01) {
> + dev_err(&fc->dev, "Failed to allocate fn_01_data.\n");
> + return -ENOMEM;
> + }
> +
> + f01->device_control.interrupt_enable = devm_kzalloc(&fc->dev,
> + sizeof(u8)*(num_of_irq_regs),
> + GFP_KERNEL);
> + if (!f01->device_control.interrupt_enable) {
> + dev_err(&fc->dev, "Failed to allocate interrupt enable.\n");
> + return -ENOMEM;
> + }
> + fc->data = f01;
> +
> + return 0;
> +}
> +
> +static int rmi_f01_initialize(struct rmi_function_container *fc)
> +{
> + u8 temp;
> + int retval;
> + u16 ctrl_base_addr;
> + struct rmi_device *rmi_dev = fc->rmi_dev;
> + struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
> + struct f01_data *data = fc->data;
> + struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
> +
> + /* Set the configured bit and (optionally) other important stuff
> + * in the device control register. */
> + ctrl_base_addr = fc->fd.control_base_addr;
> + retval = rmi_read_block(rmi_dev, fc->fd.control_base_addr,
> + &data->device_control.ctrl0,
> + sizeof(data->device_control.ctrl0));
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to read F01 control.\n");
> + return retval;
> + }
> + switch (pdata->power_management.nosleep) {
> + case RMI_F01_NOSLEEP_DEFAULT:
> + break;
> + case RMI_F01_NOSLEEP_OFF:
> + data->device_control.ctrl0.nosleep = 0;
> + break;
> + case RMI_F01_NOSLEEP_ON:
> + data->device_control.ctrl0.nosleep = 1;
> + break;
> + }
> + /* Sleep mode might be set as a hangover from a system crash or
> + * reboot without power cycle. If so, clear it so the sensor
> + * is certain to function.
> + */
> + if (data->device_control.ctrl0.sleep_mode != RMI_SLEEP_MODE_NORMAL) {
> + dev_warn(&fc->dev,
> + "WARNING: Non-zero sleep mode found. Clearing...\n");
> + data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_NORMAL;
> + }
> +
> + data->device_control.ctrl0.configured = 1;
> + retval = rmi_write_block(rmi_dev, fc->fd.control_base_addr,
> + &data->device_control.ctrl0,
> + sizeof(data->device_control.ctrl0));
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to write F01 control.\n");
> + return retval;
> + }
> +
> + data->irq_count = driver_data->irq_count;
> + data->num_of_irq_regs = driver_data->num_of_irq_regs;
> + ctrl_base_addr += sizeof(struct f01_device_control_0);
> +
> + data->interrupt_enable_addr = ctrl_base_addr;
> + retval = rmi_read_block(rmi_dev, ctrl_base_addr,
> + data->device_control.interrupt_enable,
> + sizeof(u8)*(data->num_of_irq_regs));
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to read F01 control interrupt enable register.\n");
> + goto error_exit;
> + }
> + ctrl_base_addr += data->num_of_irq_regs;
> +
> + /* dummy read in order to clear irqs */
> + retval = rmi_read(rmi_dev, fc->fd.data_base_addr + 1, &temp);
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to read Interrupt Status.\n");
> + return retval;
> + }
> +
> + retval = rmi_read_block(rmi_dev, fc->fd.query_base_addr,
> + &data->basic_queries,
> + sizeof(data->basic_queries));
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to read device query registers.\n");
> + return retval;
> + }
> +
> + retval = rmi_read_block(rmi_dev,
> + fc->fd.query_base_addr + sizeof(data->basic_queries),
> + data->product_id, RMI_PRODUCT_ID_LENGTH);
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to read product ID.\n");
> + return retval;
> + }
> + data->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
> + dev_info(&fc->dev, "found RMI device, manufacturer: %s, product: %s\n",
> + data->basic_queries.manufacturer_id == 1 ?
> + "synaptics" : "unknown",
> + data->product_id);
> +
> + /* read control register */
> + if (data->basic_queries.has_adjustable_doze) {
> + data->doze_interval_addr = ctrl_base_addr;
> + ctrl_base_addr++;
> +
> + if (pdata->power_management.doze_interval) {
> + data->device_control.doze_interval =
> + pdata->power_management.doze_interval;
> + retval = rmi_write(rmi_dev, data->doze_interval_addr,
> + data->device_control.doze_interval);
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to configure F01 doze interval register.\n");
> + goto error_exit;
> + }
> + } else {
> + retval = rmi_read(rmi_dev, data->doze_interval_addr,
> + &data->device_control.doze_interval);
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to read F01 doze interval register.\n");
> + goto error_exit;
> + }
> + }
> +
> + data->wakeup_threshold_addr = ctrl_base_addr;
> + ctrl_base_addr++;
> +
> + if (pdata->power_management.wakeup_threshold) {
> + data->device_control.wakeup_threshold =
> + pdata->power_management.wakeup_threshold;
> + retval = rmi_write(rmi_dev, data->wakeup_threshold_addr,
> + data->device_control.wakeup_threshold);
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to configure F01 wakeup threshold register.\n");
> + goto error_exit;
> + }
> + } else {
> + retval = rmi_read(rmi_dev, data->wakeup_threshold_addr,
> + &data->device_control.wakeup_threshold);
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to read F01 wakeup threshold register.\n");
> + goto error_exit;
> + }
> + }
> + }
> +
> + if (data->basic_queries.has_adjustable_doze_holdoff) {
> + data->doze_holdoff_addr = ctrl_base_addr;
> + ctrl_base_addr++;
> +
> + if (pdata->power_management.doze_holdoff) {
> + data->device_control.doze_holdoff =
> + pdata->power_management.doze_holdoff;
> + retval = rmi_write(rmi_dev, data->doze_holdoff_addr,
> + data->device_control.doze_holdoff);
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to configure F01 doze holdoff register.\n");
> + goto error_exit;
> + }
> + } else {
> + retval = rmi_read(rmi_dev, data->doze_holdoff_addr,
> + &data->device_control.doze_holdoff);
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to read F01 doze holdoff register.\n");
> + goto error_exit;
> + }
> + }
> + }
> +
> + retval = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
> + &data->device_status, sizeof(data->device_status));
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to read device status.\n");
> + goto error_exit;
> + }
> +
> + if (data->device_status.unconfigured) {
> + dev_err(&fc->dev, "Device reset during configuration process, status: %#02x!\n",
> + data->device_status.status_code);
> + retval = -EINVAL;
> + goto error_exit;
> + }
> +
> + if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
> + retval = setup_debugfs(fc);
> + if (retval < 0)
> + dev_warn(&fc->dev, "Failed to setup debugfs. Code: %d.\n",
> + retval);
> + }
> +
> + return retval;
> +
> + error_exit:
> + kfree(data);
> + return retval;
> +}
> +
> +static int rmi_f01_create_sysfs(struct rmi_function_container *fc)
> +{
> + int attr_count = 0;
> + int retval = 0;
> + struct f01_data *data = fc->data;
> +
> + dev_dbg(&fc->dev, "Creating sysfs files.");
> + for (attr_count = 0; attr_count < ARRAY_SIZE(fn_01_attrs);
> + attr_count++) {
> + if (!strcmp("doze_interval", fn_01_attrs[attr_count].attr.name)
> + && !data->basic_queries.has_lts) {
> + continue;
> + }
> + if (!strcmp("wakeup_threshold",
> + fn_01_attrs[attr_count].attr.name)
> + && !data->basic_queries.has_adjustable_doze) {
> + continue;
> + }
> + if (!strcmp("doze_holdoff", fn_01_attrs[attr_count].attr.name)
> + && !data->basic_queries.has_adjustable_doze_holdoff) {
> + continue;
> + }
> + retval = sysfs_create_file(&fc->dev.kobj,
> + &fn_01_attrs[attr_count].attr);
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to create sysfs file for %s.",
> + fn_01_attrs[attr_count].attr.name);
> + goto err_remove_sysfs;
> + }
> + }
> +
> + return 0;
> +
> +err_remove_sysfs:
> + for (attr_count--; attr_count >= 0; attr_count--)
> + sysfs_remove_file(&fc->dev.kobj,
> + &fn_01_attrs[attr_count].attr);
> +
> + return retval;
> +}
> +
> +static int rmi_f01_config(struct rmi_function_container *fc)
> +{
> + struct f01_data *data = fc->data;
> + int retval;
> +
> + retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
> + &data->device_control.ctrl0,
> + sizeof(data->device_control.ctrl0));
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to write device_control.reg.\n");
> + return retval;
> + }
> +
> + retval = rmi_write_block(fc->rmi_dev, data->interrupt_enable_addr,
> + data->device_control.interrupt_enable,
> + sizeof(u8)*(data->num_of_irq_regs));
> +
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to write interrupt enable.\n");
> + return retval;
> + }
> + if (data->basic_queries.has_lts) {
> + retval = rmi_write_block(fc->rmi_dev, data->doze_interval_addr,
> + &data->device_control.doze_interval,
> + sizeof(u8));
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to write doze interval.\n");
> + return retval;
> + }
> + }
> +
> + if (data->basic_queries.has_adjustable_doze) {
> + retval = rmi_write_block(
> + fc->rmi_dev, data->wakeup_threshold_addr,
> + &data->device_control.wakeup_threshold,
> + sizeof(u8));
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to write wakeup threshold.\n");
> + return retval;
> + }
> + }
> +
> + if (data->basic_queries.has_adjustable_doze_holdoff) {
> + retval = rmi_write_block(fc->rmi_dev, data->doze_holdoff_addr,
> + &data->device_control.doze_holdoff,
> + sizeof(u8));
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to write doze holdoff.\n");
> + return retval;
> + }
> + }
> + return 0;
> +}
> +
> +static int f01_device_init(struct rmi_function_container *fc)
> +{
> + struct rmi_driver_data *driver_data =
> + dev_get_drvdata(&fc->rmi_dev->dev);
> + int error;
> +
> + error = rmi_f01_alloc_memory(fc, driver_data->num_of_irq_regs);
> + if (error < 0)
> + return error;
> +
> + error = rmi_f01_initialize(fc);
> + if (error < 0)
> + return error;
> +
> + error = rmi_f01_create_sysfs(fc);
> + if (error < 0)
> + return error;
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int rmi_f01_suspend(struct rmi_function_container *fc)
> +{
> + struct rmi_device *rmi_dev = fc->rmi_dev;
> + struct f01_data *data = fc->data;
> + int retval = 0;
> +
> + if (data->suspended)
> + return 0;
> +
> + data->old_nosleep = data->device_control.ctrl0.nosleep;
> + data->device_control.ctrl0.nosleep = 0;
> + data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_SENSOR_SLEEP;
> +
> + retval = rmi_write_block(rmi_dev,
> + fc->fd.control_base_addr,
> + &data->device_control.ctrl0,
> + sizeof(data->device_control.ctrl0));
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to write sleep mode. Code: %d.\n",
> + retval);
> + data->device_control.ctrl0.nosleep = data->old_nosleep;
> + data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_NORMAL;
> + } else {
> + data->suspended = true;
> + retval = 0;
> + }
> +
> + return retval;
> +}
> +
> +static int rmi_f01_resume(struct rmi_function_container *fc)
> +{
> + struct rmi_device *rmi_dev = fc->rmi_dev;
> + struct f01_data *data = fc->data;
> + int retval = 0;
> +
> + if (!data->suspended)
> + return 0;
> +
> + data->device_control.ctrl0.nosleep = data->old_nosleep;
> + data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_NORMAL;
> +
> + retval = rmi_write_block(rmi_dev, fc->fd.control_base_addr,
> + &data->device_control.ctrl0,
> + sizeof(data->device_control.ctrl0));
> + if (retval < 0)
> + dev_err(&fc->dev,
> + "Failed to restore normal operation. Code: %d.\n",
> + retval);
> + else {
> + data->suspended = false;
> + retval = 0;
> + }
> +
> + return retval;
> +}
> +#endif /* CONFIG_PM */
> +
> +static int f01_remove_device(struct device *dev)
> +{
> + int attr_count;
> + struct rmi_function_container *fc = to_rmi_function_container(dev);
> +
> + if (IS_ENABLED(CONFIG_RMI4_DEBUG))
> + teardown_debugfs(fc->data);
> +
> + for (attr_count = 0; attr_count < ARRAY_SIZE(fn_01_attrs);
> + attr_count++) {
> + sysfs_remove_file(&fc->dev.kobj, &fn_01_attrs[attr_count].attr);
> + }
> + return 0;
> +}
> +
> +static int rmi_f01_attention(struct rmi_function_container *fc,
> + unsigned long *irq_bits)
> +{
> + struct rmi_device *rmi_dev = fc->rmi_dev;
> + struct f01_data *data = fc->data;
> + int retval;
> +
> + retval = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
> + &data->device_status, sizeof(data->device_status));
> + if (retval < 0) {
> + dev_err(&fc->dev, "Failed to read device status, code: %d.\n",
> + retval);
> + return retval;
> + }
> + if (data->device_status.unconfigured) {
> + dev_warn(&fc->dev, "Device reset detected.\n");
> + retval = rmi_dev->driver->reset_handler(rmi_dev);
> + if (retval < 0)
> + return retval;
> + }
> + return 0;
> +}
> +
> +static __devinit int f01_probe(struct device *dev)
> +{
> + struct rmi_function_container *fc;
> +
> + if (dev->type != &rmi_function_type)
> + return 1;
> +
> + fc = to_rmi_function_container(dev);
> + if (fc->fd.function_number != FUNCTION_NUMBER)
> + return 1;
> +
> + return f01_device_init(fc);
> +}
> +
> +static struct rmi_function_handler function_handler = {
> + .driver = {
> + .owner = THIS_MODULE,
> + .name = "rmi_f01",
> + .bus = &rmi_bus_type,
> + .probe = f01_probe,
> + .remove = f01_remove_device,
> + },
> + .func = FUNCTION_NUMBER,
> + .config = rmi_f01_config,
> + .attention = rmi_f01_attention,
> +
> +#ifdef CONFIG_PM
> + .suspend = rmi_f01_suspend,
> + .resume = rmi_f01_resume,
> +#endif /* CONFIG_PM */
> +};
> +
> +
> +static int __init rmi_f01_module_init(void)
> +{
> + int error;
> +
> + error = driver_register(&function_handler.driver);
> + if (error < 0) {
> + pr_err("%s: register failed!\n", __func__);
> + return error;
> + }
> +
> + return 0;
> +}
> +
> +static void __exit rmi_f01_module_exit(void)
> +{
> + driver_unregister(&function_handler.driver);
> +}
> +
> +module_init(rmi_f01_module_init);
> +module_exit(rmi_f01_module_exit);
> +
> +MODULE_AUTHOR("Christopher Heiny <[email protected]>");
> +MODULE_DESCRIPTION("RMI F01 module");
> +MODULE_LICENSE("GPL");
> +MODULE_VERSION(RMI_DRIVER_VERSION);
> diff --git a/drivers/input/rmi4/rmi_f01.h b/drivers/input/rmi4/rmi_f01.h
> new file mode 100644
> index 0000000..6bc5724
> --- /dev/null
> +++ b/drivers/input/rmi4/rmi_f01.h
> @@ -0,0 +1,160 @@
> +/*
> + * Copyright (c) 2012 Synaptics Incorporated
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +
> +#ifndef _RMI_F01_H
> +#define _RMI_F01_H
> +
> +#define RMI_PRODUCT_ID_LENGTH 10
> +#define RMI_PRODUCT_INFO_LENGTH 2
> +
> +#define RMI_DATE_CODE_LENGTH 3
> +
> +#define PRODUCT_ID_OFFSET 0x10
> +#define PRODUCT_INFO_OFFSET 0x1E
> +
> +#define F01_RESET_MASK 0x01
> +
> +/**
> + * @manufacturer_id - reports the identity of the manufacturer of the RMI
> + * device. Synaptics RMI devices report a Manufacturer ID of $01.
> + * @custom_map - at least one custom, non
> + * RMI-compatible register exists in the register address map for this device.
> + * @non-compliant - the device implements a register map that is not compliant
> + * with the RMI specification.
> + * @has_lts - the device uses Synaptics' LTS hardware architecture.
> + * @has_sensor_id - the SensorID query register (F01_RMI_Query22) exists.
> + * @has_charger_input - the ChargerConnected bit (F01_RMI_Ctrl0, bit 5) is
> + * meaningful.
> + * @has_adjustable_doze - the doze (power management) control registers exist.
> + * @has_adjustable_doze_holdoff - the doze holdoff register exists.
> + * @has_product_properties - indicates the presence of F01_RMI_Query42,
> + * ProductProperties2.
> + * @productinfo_1 - meaning varies from product to product, consult your
> + * product spec sheet.
> + * @productinfo_2 - meaning varies from product to product, consult your
> + * product spec sheet.
> + * @year - year of manufacture MOD 2000.
> + * @month - month of manufacture
> + * @day - day of manufacture
> + * @wafer_id1_lsb - The wafer-lot ID registers record the lot number of the
> + * wafer from which the module’s touch controller was produced.
> + * @wafer_id1_msb - The wafer-lot ID registers record the lot number of the
> + * wafer from which the module’s touch controller was produced.
> + * @wafer_id2_lsb - The wafer-lot ID registers record the lot number of the
> + * wafer from which the module’s touch controller was produced.
> + * @wafer_id2_msb - The wafer-lot ID registers record the lot number of the
> + * wafer from which the module’s touch controller was produced.
> + * @wafer_id3_lsb - The wafer-lot ID registers record the lot number of the
> + * wafer from which the module’s touch controller was produced.
> + */
> +struct f01_basic_queries {
> + u8 manufacturer_id:8;
> +
> + bool custom_map:1;
> + bool non_compliant:1;
> + bool has_lts:1;
> + bool has_sensor_id:1;
> + bool has_charger_input:1;
> + bool has_adjustable_doze:1;
> + bool has_adjustable_doze_holdoff:1;
> + bool has_product_properties_2:1;
> +
> + u8 productinfo_1:7;
> + bool q2_bit_7:1;
> + u8 productinfo_2:7;
> + bool q3_bit_7:1;
> +
> + u8 year:5;
> + u8 month:4;

My GCC warns that alignment of "month" changes between GCC 4.4 and
earlier. It looks like we need to define this as u16 and convert form
bus to CPU endianess.

Below is a patch implementing some of the suggestions. Please see what
can be applied to your other files in RMI subsystem.

Thanks.

--
Dmitry


Input: RMI4 - misc changes

From: Dmitry Torokhov <[email protected]>

Signed-off-by: Dmitry Torokhov <[email protected]>
---
drivers/input/rmi4/rmi_f01.c | 482 +++++++++++++++++++-----------------------
drivers/input/rmi4/rmi_f01.h | 40 ++-
include/linux/rmi.h | 4
3 files changed, 236 insertions(+), 290 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index 038266c..b6352ba 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -32,7 +32,7 @@
* @reset - set this bit to force a firmware reset of the sensor.
*/
struct f01_device_commands {
- bool reset:1;
+ u8 reset:1;
u8 reserved:7;
};

@@ -62,10 +62,10 @@ struct f01_device_control {
* @has_guest - if true, a "guest" device is supported.
*/
struct f01_query_42 {
- bool has_ds4_queries:1;
- bool has_multi_phy:1;
- bool has_guest:1;
- u8 reserved:5;
+ u8 has_ds4_queries:1;
+ u8 has_multi_phy:1;
+ u8 has_guest:1;
+ u8 reserved:5;
} __attribute__((__packed__));

/**
@@ -93,20 +93,20 @@ struct f01_ds4_queries {
u8 length:4;
u8 reserved_1:4;

- bool has_package_id_query:1;
- bool has_packrat_query:1;
- bool has_reset_query:1;
- bool has_maskrev_query:1;
+ u8 has_package_id_query:1;
+ u8 has_packrat_query:1;
+ u8 has_reset_query:1;
+ u8 has_maskrev_query:1;
u8 reserved_2:4;

- bool has_i2c_control:1;
- bool has_spi_control:1;
- bool has_attn_control:1;
+ u8 has_i2c_control:1;
+ u8 has_spi_control:1;
+ u8 has_attn_control:1;
u8 reserved_3:5;

- bool reset_enabled:1;
- bool reset_polarity:1;
- bool pullup_enabled:1;
+ u8 reset_enabled:1;
+ u8 reset_polarity:1;
+ u8 pullup_enabled:1;
u8 reserved_4:1;
u8 reset_pin_number:4;
} __attribute__((__packed__));
@@ -115,7 +115,7 @@ struct f01_data {
struct f01_device_control device_control;
struct f01_basic_queries basic_queries;
struct f01_device_status device_status;
- u8 product_id[RMI_PRODUCT_ID_LENGTH+1];
+ u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];

u16 interrupt_enable_addr;
u16 doze_interval_addr;
@@ -125,17 +125,17 @@ struct f01_data {
int irq_count;
int num_of_irq_regs;

-#ifdef CONFIG_PM
+#ifdef CONFIG_PM
bool suspended;
bool old_nosleep;
#endif

-#ifdef CONFIG_RMI4_DEBUG
+#ifdef CONFIG_RMI4_DEBUG
struct dentry *debugfs_interrupt_enable;
#endif
};

-#ifdef CONFIG_RMI4_DEBUG
+#ifdef CONFIG_RMI4_DEBUG
struct f01_debugfs_data {
bool done;
struct rmi_function_container *fc;
@@ -308,37 +308,26 @@ static void teardown_debugfs(struct f01_data *f01)
if (f01->debugfs_interrupt_enable)
debugfs_remove(f01->debugfs_interrupt_enable);
}
-#endif

-/* Utility routine to set the value of a bit field in a register. */
-int rmi_mask_and_set(struct rmi_device *rmi_dev,
- u16 address,
- u8 mask,
- u8 set)
+#else
+
+static inline int setup_debugfs(struct rmi_function_container *fc)
{
- u8 reg_contents;
- int retval;
+ return 0;
+}

- retval = rmi_read(rmi_dev, address, &reg_contents);
- if (retval < 0)
- return retval;
- reg_contents = (reg_contents & ~mask) | set;
- retval = rmi_write(rmi_dev, address, reg_contents);
- if (retval == 1)
- return 0;
- else if (retval == 0)
- return -EIO;
- return retval;
+static inline void teardown_debugfs(struct f01_data *f01)
+{
}

+#endif
+
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;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n",
data->basic_queries.productinfo_1,
@@ -349,10 +338,8 @@ 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;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "%s\n", data->product_id);
}
@@ -361,10 +348,8 @@ 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;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "0x%02x\n",
data->basic_queries.manufacturer_id);
@@ -374,10 +359,8 @@ 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;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "20%02u-%02u-%02u\n",
data->basic_queries.year,
@@ -389,11 +372,10 @@ 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;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
unsigned int reset;
int retval = 0;

- fc = to_rmi_function_container(dev);

if (sscanf(buf, "%u", &reset) != 1)
return -EINVAL;
@@ -422,10 +404,8 @@ 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;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE,
"%d\n", data->device_control.ctrl0.sleep_mode);
@@ -435,12 +415,10 @@ 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;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+ struct f01_data *data = fc->data;
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)) {
@@ -463,25 +441,21 @@ static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
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;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "%d\n",
- data->device_control.ctrl0.nosleep);
+ data->device_control.ctrl0.nosleep);
}

static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+ struct f01_data *data = fc->data;
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 > 1) {
@@ -497,16 +471,15 @@ static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
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;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "%d\n",
data->device_control.ctrl0.charger_input);
@@ -516,12 +489,10 @@ 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;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+ struct f01_data *data = fc->data;
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 > 1) {
@@ -537,16 +508,15 @@ static ssize_t rmi_fn_01_chargerinput_store(struct device *dev,
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;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "%d\n",
data->device_control.ctrl0.report_rate);
@@ -556,12 +526,10 @@ 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;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+ struct f01_data *data = fc->data;
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 > 1) {
@@ -577,19 +545,18 @@ static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
retval = count;
else
dev_err(dev, "Failed to write reportrate bit.\n");
+
return retval;
}

static ssize_t rmi_fn_01_interrupt_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct rmi_function_container *fc;
- struct f01_data *data;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+ struct f01_data *data = fc->data;
int i, len, total_len = 0;
char *current_buf = buf;

- fc = to_rmi_function_container(dev);
- data = fc->data;
/* loop through each irq value and copy its
* string representation into buf */
for (i = 0; i < data->irq_count; i++) {
@@ -631,10 +598,8 @@ static ssize_t rmi_fn_01_interrupt_enable_show(struct device *dev,
static ssize_t rmi_fn_01_doze_interval_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct f01_data *data = NULL;
struct rmi_function_container *fc = to_rmi_function_container(dev);
-
- data = fc->data;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "%d\n",
data->device_control.doze_interval);
@@ -645,15 +610,12 @@ static ssize_t rmi_fn_01_doze_interval_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+ struct f01_data *data = fc->data;
unsigned long new_value;
int retval;
u16 ctrl_base_addr;

- struct rmi_function_container *fc = to_rmi_function_container(dev);
-
- data = fc->data;
-
retval = strict_strtoul(buf, 10, &new_value);
if (retval < 0 || new_value > 255) {
dev_err(dev, "%s: Invalid doze interval %s.", __func__, buf);
@@ -673,6 +635,7 @@ static ssize_t rmi_fn_01_doze_interval_store(struct device *dev,
retval = count;
else
dev_err(dev, "Failed to write doze interval.\n");
+
return retval;

}
@@ -681,10 +644,8 @@ static ssize_t rmi_fn_01_wakeup_threshold_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct f01_data *data = NULL;
struct rmi_function_container *fc = to_rmi_function_container(dev);
-
- data = fc->data;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "%d\n",
data->device_control.wakeup_threshold);
@@ -694,14 +655,11 @@ static ssize_t rmi_fn_01_wakeup_threshold_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+ struct f01_data *data = fc->data;
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 > 255) {
dev_err(dev, "%s: Invalid wakeup threshold %s.", __func__, buf);
@@ -724,10 +682,8 @@ static ssize_t rmi_fn_01_doze_holdoff_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct f01_data *data = NULL;
struct rmi_function_container *fc = to_rmi_function_container(dev);
-
- data = fc->data;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "%d\n",
data->device_control.doze_holdoff);
@@ -739,14 +695,11 @@ static ssize_t rmi_fn_01_doze_holdoff_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+ struct f01_data *data = fc->data;
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 > 255) {
dev_err(dev, "%s: Invalid doze holdoff %s.", __func__, buf);
@@ -761,6 +714,7 @@ static ssize_t rmi_fn_01_doze_holdoff_store(struct device *dev,
retval = count;
else
dev_err(dev, "Failed to write doze holdoff.\n");
+
return retval;

}
@@ -768,10 +722,8 @@ static ssize_t rmi_fn_01_doze_holdoff_store(struct device *dev,
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;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "%d\n",
data->device_control.ctrl0.configured);
@@ -780,10 +732,8 @@ static ssize_t rmi_fn_01_configured_show(struct device *dev,
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;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "%d\n",
data->device_status.unconfigured);
@@ -792,10 +742,8 @@ static ssize_t rmi_fn_01_unconfigured_show(struct device *dev,
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;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "%d\n",
data->device_status.flash_prog);
@@ -804,69 +752,114 @@ static ssize_t rmi_fn_01_flashprog_show(struct device *dev,
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;
+ struct f01_data *data = fc->data;

return snprintf(buf, PAGE_SIZE, "0x%02x\n",
data->device_status.status_code);
}

-static struct device_attribute fn_01_attrs[] = {
- __ATTR(productinfo, RMI_RO_ATTR,
- rmi_fn_01_productinfo_show, NULL),
- __ATTR(productid, RMI_RO_ATTR,
- rmi_fn_01_productid_show, NULL),
- __ATTR(manufacturer, RMI_RO_ATTR,
- rmi_fn_01_manufacturer_show, NULL),
- __ATTR(datecode, RMI_RO_ATTR,
- rmi_fn_01_datecode_show, NULL),
-
- /* control register access */
- __ATTR(sleepmode, RMI_RW_ATTR,
- rmi_fn_01_sleepmode_show, rmi_fn_01_sleepmode_store),
- __ATTR(nosleep, RMI_RW_ATTR,
- rmi_fn_01_nosleep_show, rmi_fn_01_nosleep_store),
- __ATTR(chargerinput, RMI_RW_ATTR,
- rmi_fn_01_chargerinput_show, rmi_fn_01_chargerinput_store),
- __ATTR(reportrate, RMI_RW_ATTR,
- rmi_fn_01_reportrate_show, rmi_fn_01_reportrate_store),
- /* We don't want arbitrary callers changing the interrupt enable mask,
- * so it's read only.
- */
- __ATTR(interrupt_enable, RMI_RO_ATTR,
- rmi_fn_01_interrupt_enable_show, NULL),
- __ATTR(doze_interval, RMI_RW_ATTR,
- rmi_fn_01_doze_interval_show, rmi_fn_01_doze_interval_store),
- __ATTR(wakeup_threshold, RMI_RW_ATTR,
- rmi_fn_01_wakeup_threshold_show,
- rmi_fn_01_wakeup_threshold_store),
- __ATTR(doze_holdoff, RMI_RW_ATTR,
- rmi_fn_01_doze_holdoff_show, rmi_fn_01_doze_holdoff_store),
-
- /* We make report rate RO, since the driver uses that to look for
- * resets. We don't want someone faking us out by changing that
- * bit.
- */
- __ATTR(configured, RMI_RO_ATTR,
- rmi_fn_01_configured_show, NULL),
-
- /* Command register access. */
- __ATTR(reset, RMI_WO_ATTR,
- NULL, rmi_fn_01_reset_store),
-
- /* STatus register access. */
- __ATTR(unconfigured, RMI_RO_ATTR,
- rmi_fn_01_unconfigured_show, NULL),
- __ATTR(flashprog, RMI_RO_ATTR,
- rmi_fn_01_flashprog_show, NULL),
- __ATTR(statuscode, RMI_RO_ATTR,
- rmi_fn_01_statuscode_show, NULL),
+#define RMI_F01_ATTR(_name) \
+ DEVICE_ATTR(_name, RMI_RW_ATTR, \
+ rmi_fn_01_##_name##_show, \
+ rmi_fn_01_##_name##_store)
+
+#define RMI_F01_RO_ATTR(_name) \
+ DEVICE_ATTR(_name, RMI_RO_ATTR, \
+ rmi_fn_01_##_name##_show, \
+ NULL)
+
+#define RMI_F01_WO_ATTR(_name) \
+ DEVICE_ATTR(_name, RMI_RO_ATTR, \
+ NULL, \
+ rmi_fn_01_##_name##_store)
+
+
+static RMI_F01_RO_ATTR(productinfo);
+static RMI_F01_RO_ATTR(productid);
+static RMI_F01_RO_ATTR(manufacturer);
+static RMI_F01_RO_ATTR(datecode);
+
+/* Control register access */
+static RMI_F01_ATTR(sleepmode);
+static RMI_F01_ATTR(nosleep);
+static RMI_F01_ATTR(chargerinput);
+static RMI_F01_ATTR(reportrate);
+
+/*
+ * We don't want arbitrary callers changing the interrupt enable mask,
+ * so it's read only.
+ */
+static RMI_F01_RO_ATTR(interrupt_enable);
+static RMI_F01_ATTR(doze_interval);
+static RMI_F01_ATTR(wakeup_threshold);
+static RMI_F01_ATTR(doze_holdoff);
+
+/*
+ * 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.
+ */
+static RMI_F01_RO_ATTR(configured);
+
+/* Command register access. */
+static RMI_F01_WO_ATTR(reset);
+
+/* Status register access. */
+static RMI_F01_RO_ATTR(unconfigured);
+static RMI_F01_RO_ATTR(flashprog);
+static RMI_F01_RO_ATTR(statuscode);
+
+static struct attribute *rmi_fn_01_attrs[] = {
+ &dev_attr_productinfo.attr,
+ &dev_attr_productid.attr,
+ &dev_attr_manufacturer.attr,
+ &dev_attr_datecode.attr,
+ &dev_attr_sleepmode.attr,
+ &dev_attr_nosleep.attr,
+ &dev_attr_chargerinput.attr,
+ &dev_attr_reportrate.attr,
+ &dev_attr_interrupt_enable.attr,
+ &dev_attr_doze_interval.attr,
+ &dev_attr_wakeup_threshold.attr,
+ &dev_attr_doze_holdoff.attr,
+ &dev_attr_configured.attr,
+ &dev_attr_reset.attr,
+ &dev_attr_unconfigured.attr,
+ &dev_attr_flashprog.attr,
+ &dev_attr_statuscode.attr,
+ NULL
+};
+
+static umode_t rmi_fn_01_attr_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+ struct f01_data *data = fc->data;
+ umode_t mode = attr->mode;
+
+ if (attr == &dev_attr_doze_interval.attr) {
+ if (!data->basic_queries.has_lts)
+ mode = 0;
+ } else if (attr == &dev_attr_wakeup_threshold.attr) {
+ if (!data->basic_queries.has_adjustable_doze)
+ mode = 0;
+ } else if (attr == &dev_attr_doze_holdoff.attr) {
+ if (!data->basic_queries.has_adjustable_doze_holdoff)
+ mode = 0;
+ }
+
+ return mode;
+}
+
+static struct attribute_group rmi_fn_01_attr_group = {
+ .is_visible = rmi_fn_01_attr_visible,
+ .attrs = rmi_fn_01_attrs,
};

static int rmi_f01_alloc_memory(struct rmi_function_container *fc,
- int num_of_irq_regs)
+ int num_of_irq_regs)
{
struct f01_data *f01;

@@ -891,7 +884,7 @@ static int rmi_f01_alloc_memory(struct rmi_function_container *fc,
static int rmi_f01_initialize(struct rmi_function_container *fc)
{
u8 temp;
- int retval;
+ int error;
u16 ctrl_base_addr;
struct rmi_device *rmi_dev = fc->rmi_dev;
struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
@@ -901,12 +894,12 @@ static int rmi_f01_initialize(struct rmi_function_container *fc)
/* Set the configured bit and (optionally) other important stuff
* in the device control register. */
ctrl_base_addr = fc->fd.control_base_addr;
- retval = rmi_read_block(rmi_dev, fc->fd.control_base_addr,
+ error = rmi_read_block(rmi_dev, fc->fd.control_base_addr,
&data->device_control.ctrl0,
sizeof(data->device_control.ctrl0));
- if (retval < 0) {
+ if (error < 0) {
dev_err(&fc->dev, "Failed to read F01 control.\n");
- return retval;
+ return error;
}
switch (pdata->power_management.nosleep) {
case RMI_F01_NOSLEEP_DEFAULT:
@@ -929,12 +922,12 @@ static int rmi_f01_initialize(struct rmi_function_container *fc)
}

data->device_control.ctrl0.configured = 1;
- retval = rmi_write_block(rmi_dev, fc->fd.control_base_addr,
+ error = rmi_write_block(rmi_dev, fc->fd.control_base_addr,
&data->device_control.ctrl0,
sizeof(data->device_control.ctrl0));
- if (retval < 0) {
+ if (error < 0) {
dev_err(&fc->dev, "Failed to write F01 control.\n");
- return retval;
+ return error;
}

data->irq_count = driver_data->irq_count;
@@ -942,36 +935,36 @@ static int rmi_f01_initialize(struct rmi_function_container *fc)
ctrl_base_addr += sizeof(struct f01_device_control_0);

data->interrupt_enable_addr = ctrl_base_addr;
- retval = rmi_read_block(rmi_dev, ctrl_base_addr,
+ error = rmi_read_block(rmi_dev, ctrl_base_addr,
data->device_control.interrupt_enable,
sizeof(u8)*(data->num_of_irq_regs));
- if (retval < 0) {
+ if (error < 0) {
dev_err(&fc->dev, "Failed to read F01 control interrupt enable register.\n");
goto error_exit;
}
ctrl_base_addr += data->num_of_irq_regs;

/* dummy read in order to clear irqs */
- retval = rmi_read(rmi_dev, fc->fd.data_base_addr + 1, &temp);
- if (retval < 0) {
+ 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 retval;
+ return error;
}

- retval = rmi_read_block(rmi_dev, fc->fd.query_base_addr,
+ error = rmi_read_block(rmi_dev, fc->fd.query_base_addr,
&data->basic_queries,
sizeof(data->basic_queries));
- if (retval < 0) {
+ if (error < 0) {
dev_err(&fc->dev, "Failed to read device query registers.\n");
- return retval;
+ return error;
}

- retval = rmi_read_block(rmi_dev,
+ error = rmi_read_block(rmi_dev,
fc->fd.query_base_addr + sizeof(data->basic_queries),
data->product_id, RMI_PRODUCT_ID_LENGTH);
- if (retval < 0) {
+ if (error < 0) {
dev_err(&fc->dev, "Failed to read product ID.\n");
- return retval;
+ return error;
}
data->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
dev_info(&fc->dev, "found RMI device, manufacturer: %s, product: %s\n",
@@ -987,16 +980,16 @@ static int rmi_f01_initialize(struct rmi_function_container *fc)
if (pdata->power_management.doze_interval) {
data->device_control.doze_interval =
pdata->power_management.doze_interval;
- retval = rmi_write(rmi_dev, data->doze_interval_addr,
+ error = rmi_write(rmi_dev, data->doze_interval_addr,
data->device_control.doze_interval);
- if (retval < 0) {
+ if (error < 0) {
dev_err(&fc->dev, "Failed to configure F01 doze interval register.\n");
goto error_exit;
}
} else {
- retval = rmi_read(rmi_dev, data->doze_interval_addr,
+ error = rmi_read(rmi_dev, data->doze_interval_addr,
&data->device_control.doze_interval);
- if (retval < 0) {
+ if (error < 0) {
dev_err(&fc->dev, "Failed to read F01 doze interval register.\n");
goto error_exit;
}
@@ -1008,16 +1001,16 @@ static int rmi_f01_initialize(struct rmi_function_container *fc)
if (pdata->power_management.wakeup_threshold) {
data->device_control.wakeup_threshold =
pdata->power_management.wakeup_threshold;
- retval = rmi_write(rmi_dev, data->wakeup_threshold_addr,
+ error = rmi_write(rmi_dev, data->wakeup_threshold_addr,
data->device_control.wakeup_threshold);
- if (retval < 0) {
+ if (error < 0) {
dev_err(&fc->dev, "Failed to configure F01 wakeup threshold register.\n");
goto error_exit;
}
} else {
- retval = rmi_read(rmi_dev, data->wakeup_threshold_addr,
+ error = rmi_read(rmi_dev, data->wakeup_threshold_addr,
&data->device_control.wakeup_threshold);
- if (retval < 0) {
+ if (error < 0) {
dev_err(&fc->dev, "Failed to read F01 wakeup threshold register.\n");
goto error_exit;
}
@@ -1031,25 +1024,25 @@ static int rmi_f01_initialize(struct rmi_function_container *fc)
if (pdata->power_management.doze_holdoff) {
data->device_control.doze_holdoff =
pdata->power_management.doze_holdoff;
- retval = rmi_write(rmi_dev, data->doze_holdoff_addr,
+ error = rmi_write(rmi_dev, data->doze_holdoff_addr,
data->device_control.doze_holdoff);
- if (retval < 0) {
+ if (error < 0) {
dev_err(&fc->dev, "Failed to configure F01 doze holdoff register.\n");
goto error_exit;
}
} else {
- retval = rmi_read(rmi_dev, data->doze_holdoff_addr,
+ error = rmi_read(rmi_dev, data->doze_holdoff_addr,
&data->device_control.doze_holdoff);
- if (retval < 0) {
+ if (error < 0) {
dev_err(&fc->dev, "Failed to read F01 doze holdoff register.\n");
goto error_exit;
}
}
}

- retval = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
+ error = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
&data->device_status, sizeof(data->device_status));
- if (retval < 0) {
+ if (error < 0) {
dev_err(&fc->dev, "Failed to read device status.\n");
goto error_exit;
}
@@ -1057,63 +1050,20 @@ static int rmi_f01_initialize(struct rmi_function_container *fc)
if (data->device_status.unconfigured) {
dev_err(&fc->dev, "Device reset during configuration process, status: %#02x!\n",
data->device_status.status_code);
- retval = -EINVAL;
+ error = -EINVAL;
goto error_exit;
}

- if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
- retval = setup_debugfs(fc);
- if (retval < 0)
- dev_warn(&fc->dev, "Failed to setup debugfs. Code: %d.\n",
- retval);
- }
+ error = setup_debugfs(fc);
+ if (error)
+ dev_warn(&fc->dev, "Failed to setup debugfs, error: %d.\n",
+ error);

- return retval;
+ return 0;

error_exit:
kfree(data);
- return retval;
-}
-
-static int rmi_f01_create_sysfs(struct rmi_function_container *fc)
-{
- int attr_count = 0;
- int retval = 0;
- struct f01_data *data = fc->data;
-
- dev_dbg(&fc->dev, "Creating sysfs files.");
- for (attr_count = 0; attr_count < ARRAY_SIZE(fn_01_attrs);
- attr_count++) {
- if (!strcmp("doze_interval", fn_01_attrs[attr_count].attr.name)
- && !data->basic_queries.has_lts) {
- continue;
- }
- if (!strcmp("wakeup_threshold",
- fn_01_attrs[attr_count].attr.name)
- && !data->basic_queries.has_adjustable_doze) {
- continue;
- }
- if (!strcmp("doze_holdoff", fn_01_attrs[attr_count].attr.name)
- && !data->basic_queries.has_adjustable_doze_holdoff) {
- continue;
- }
- retval = sysfs_create_file(&fc->dev.kobj,
- &fn_01_attrs[attr_count].attr);
- if (retval < 0) {
- dev_err(&fc->dev, "Failed to create sysfs file for %s.",
- fn_01_attrs[attr_count].attr.name);
- goto err_remove_sysfs;
- }
- }
-
- return 0;
-
-err_remove_sysfs:
- for (attr_count--; attr_count >= 0; attr_count--)
- sysfs_remove_file(&fc->dev.kobj,
- &fn_01_attrs[attr_count].attr);
-
- return retval;
+ return error;
}

static int rmi_f01_config(struct rmi_function_container *fc)
@@ -1177,15 +1127,15 @@ static int f01_device_init(struct rmi_function_container *fc)
int error;

error = rmi_f01_alloc_memory(fc, driver_data->num_of_irq_regs);
- if (error < 0)
+ if (error)
return error;

error = rmi_f01_initialize(fc);
- if (error < 0)
+ if (error)
return error;

- error = rmi_f01_create_sysfs(fc);
- if (error < 0)
+ error = sysfs_create_group(&fc->dev.kobj, &rmi_fn_01_attr_group);
+ if (error)
return error;

return 0;
@@ -1250,18 +1200,13 @@ static int rmi_f01_resume(struct rmi_function_container *fc)
}
#endif /* CONFIG_PM */

-static int f01_remove_device(struct device *dev)
+static int f01_remove(struct device *dev)
{
- int attr_count;
struct rmi_function_container *fc = to_rmi_function_container(dev);

- if (IS_ENABLED(CONFIG_RMI4_DEBUG))
- teardown_debugfs(fc->data);
+ teardown_debugfs(fc->data);
+ sysfs_remove_group(&fc->dev.kobj, &rmi_fn_01_attr_group);

- for (attr_count = 0; attr_count < ARRAY_SIZE(fn_01_attrs);
- attr_count++) {
- sysfs_remove_file(&fc->dev.kobj, &fn_01_attrs[attr_count].attr);
- }
return 0;
}

@@ -1288,7 +1233,7 @@ static int rmi_f01_attention(struct rmi_function_container *fc,
return 0;
}

-static __devinit int f01_probe(struct device *dev)
+static int f01_probe(struct device *dev)
{
struct rmi_function_container *fc;

@@ -1308,19 +1253,18 @@ static struct rmi_function_handler function_handler = {
.name = "rmi_f01",
.bus = &rmi_bus_type,
.probe = f01_probe,
- .remove = f01_remove_device,
+ .remove = f01_remove,
},
.func = FUNCTION_NUMBER,
.config = rmi_f01_config,
.attention = rmi_f01_attention,

-#ifdef CONFIG_PM
+#ifdef CONFIG_PM
.suspend = rmi_f01_suspend,
.resume = rmi_f01_resume,
#endif /* CONFIG_PM */
};

-
static int __init rmi_f01_module_init(void)
{
int error;
diff --git a/drivers/input/rmi4/rmi_f01.h b/drivers/input/rmi4/rmi_f01.h
index 6bc5724..8092b7f 100644
--- a/drivers/input/rmi4/rmi_f01.h
+++ b/drivers/input/rmi4/rmi_f01.h
@@ -66,25 +66,27 @@
struct f01_basic_queries {
u8 manufacturer_id:8;

- bool custom_map:1;
- bool non_compliant:1;
- bool has_lts:1;
- bool has_sensor_id:1;
- bool has_charger_input:1;
- bool has_adjustable_doze:1;
- bool has_adjustable_doze_holdoff:1;
- bool has_product_properties_2:1;
+ u8 custom_map:1;
+ u8 non_compliant:1;
+ u8 has_lts:1;
+ u8 has_sensor_id:1;
+ u8 has_charger_input:1;
+ u8 has_adjustable_doze:1;
+ u8 has_adjustable_doze_holdoff:1;
+ u8 has_product_properties_2:1;

u8 productinfo_1:7;
- bool q2_bit_7:1;
+ u8 q2_bit_7:1;
+
u8 productinfo_2:7;
- bool q3_bit_7:1;
+ u8 q3_bit_7:1;

u8 year:5;
u8 month:4;
u8 day:5;
- bool cp1:1;
- bool cp2:1;
+ u8 cp1:1;
+ u8 cp2:1;
+
u8 wafer_id1_lsb:8;
u8 wafer_id1_msb:8;
u8 wafer_id2_lsb:8;
@@ -118,10 +120,10 @@ enum rmi_device_status {
* @unconfigured - the device has lost its configuration for some reason.
*/
struct f01_device_status {
- enum rmi_device_status status_code:4;
+ u8 status_code:4;
u8 reserved:2;
- bool flash_prog:1;
- bool unconfigured:1;
+ u8 flash_prog:1;
+ u8 unconfigured:1;
} __attribute__((__packed__));

/* control register bits */
@@ -150,11 +152,11 @@ struct f01_device_status {
*/
struct f01_device_control_0 {
u8 sleep_mode:2;
- bool nosleep:1;
+ u8 nosleep:1;
u8 reserved:2;
- bool charger_input:1;
- bool report_rate:1;
- bool configured:1;
+ u8 charger_input:1;
+ u8 report_rate:1;
+ u8 configured:1;
} __attribute__((__packed__));

#endif
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
index b7d0a34..a41c874 100644
--- a/include/linux/rmi.h
+++ b/include/linux/rmi.h
@@ -376,7 +376,7 @@ struct rmi_function_handler {
};

#define to_rmi_function_handler(d) \
- container_of(d, struct rmi_function_handler, driver);
+ container_of(d, struct rmi_function_handler, driver)

/**
* struct rmi_function_container - represents the implementation of an RMI4
@@ -413,7 +413,7 @@ struct rmi_function_container {
};

#define to_rmi_function_container(d) \
- container_of(d, struct rmi_function_container, dev);
+ container_of(d, struct rmi_function_container, dev)

/**
* struct rmi_driver - driver for an RMI4 sensor on the RMI bus.

2012-11-26 18:41:28

by Benjamin Tissoires

[permalink] [raw]
Subject: Re: [RFC PATCH 02/06] input/rmi4: Core files

Hi Christopher,

On Sat, Nov 17, 2012 at 4:58 AM, Christopher Heiny <[email protected]> wrote:
> rmi_bus.c implements the basic functionality of the RMI bus. This file is
> greatly simplified compared to the previous patch - we've switched from
> "do it yourself" device/driver binding to using device_type to distinguish
> between the two kinds of devices on the bus (sensor devices and function
> specific devices) and using the standard bus implementation to manage devices
> and drivers.
>
>
> rmi_driver.c is a driver for the general functionality of the RMI sensor as a
> whole, managing those behaviors (including IRQ handling) that are not specific
> to any RMI4 function. It has some unavoidable dependencies on F01 behavior,
> though we have worked to minimize those as far as possible.
>
>
> The header file rmi_driver.h provides definitions that are shared among
> the modules of the RMI implementation, but not thought to be necessary
> outside it.
>
>
> Greg KH - Linus Walleij recommended that we seek your input on these core
> files, particularly the bus implementation.
>
>
> Signed-off-by: Christopher Heiny <[email protected]>
>
> Cc: Greg Kroah-Hartman <[email protected]>
> Cc: Dmitry Torokhov <[email protected]>
> Cc: Linus Walleij <[email protected]>
> Cc: Naveen Kumar Gaddipati <[email protected]>
> Cc: Joeri de Gram <[email protected]>
>
> ---
>
> drivers/input/rmi4/rmi_bus.c | 248 ++++++
> drivers/input/rmi4/rmi_driver.c | 1663 +++++++++++++++++++++++++++++++++++++++
> drivers/input/rmi4/rmi_driver.h | 139 ++++
> include/uapi/linux/input.h | 1 +
> 4 files changed, 2051 insertions(+), 0 deletions(-)
>
[snipped]
> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
> new file mode 100644
> index 0000000..05a73ae
> --- /dev/null
> +++ b/drivers/input/rmi4/rmi_driver.c
[snipped]
> +/* extract product ID */
> +void get_prod_id(struct rmi_device *rmi_dev, struct rmi_driver_data *drvdata)
> +{
> + struct device *dev = &rmi_dev->dev;
> + int retval;
> + int board = 0, rev = 0;
> + int i;
> + static const char * const pattern[] = {
> + "tm%4d-%d", "s%4d-%d", "s%4d-ver%1d"};
> + u8 product_id[RMI_PRODUCT_ID_LENGTH+1];
> +
> + retval = rmi_read_block(rmi_dev,
> + drvdata->f01_container->fd.query_base_addr+
> + sizeof(struct f01_basic_queries),
> + product_id, RMI_PRODUCT_ID_LENGTH);
> + if (retval < 0) {
> + dev_err(dev, "Failed to read product id, code=%d!", retval);
> + return;
> + }
> + product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
> +
> + for (i = 0; i < sizeof(product_id); i++)
> + product_id[i] = tolower(product_id[i]);
> +
> + for (i = 0; i < sizeof(pattern); i++) {

This should be ARRAY_SIZE(pattern).
It gave me a wonderful kernel oops :)

Cheers,
Benjamin

> + retval = sscanf(product_id, pattern[i], &board, &rev);
> + if (retval)
> + break;
> + }
> + /* save board and rev data in the rmi_driver_data */
> + drvdata->board = board;
> + drvdata->rev = rev;
> + dev_dbg(dev, "Rmi_driver getProdID, set board: %d rev: %d\n",
> + drvdata->board, drvdata->rev);
> +}
> +
[snipped]

2012-11-26 22:31:31

by Christopher Heiny

[permalink] [raw]
Subject: Re: [RFC PATCH 05/06] input/rmi4: F01 - device control

On 11/26/2012 01:40 AM, Dmitry Torokhov wrote:
> Hi Christopher,
>
> On Fri, Nov 16, 2012 at 07:58:53PM -0800, Christopher Heiny wrote:
>> RMI Function 01 implements basic device control and power management
>> behaviors for the RMI4 sensor.
>>
>> rmi_f01.h exports definitions that we expect to be used by other functionality
>> in the future (such as firmware reflash).
>
> Please see my comments below.

Hi Dmitry,

Thanks for the feedback and the patch. I've got just one question,
included below, with a bunch of snipping).

Chris

>
>>
>>
>> Signed-off-by: Christopher Heiny <[email protected]>
>>
>> Cc: Dmitry Torokhov <[email protected]>
>> Cc: Linus Walleij <[email protected]>
>> Cc: Naveen Kumar Gaddipati <[email protected]>
>> Cc: Joeri de Gram <[email protected]>
>>
>>
>> ---
>>
>> drivers/input/rmi4/rmi_f01.c | 1348 ++++++++++++++++++++++++++++++++++++++++++
>> drivers/input/rmi4/rmi_f01.h | 160 +++++
>> 2 files changed, 1508 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..038266c
>> --- /dev/null
>> +++ b/drivers/input/rmi4/rmi_f01.c
>> @@ -0,0 +1,1348 @@
>> +/*
>> + * Copyright (c) 2011-2012 Synaptics Incorporated
>> + * Copyright (c) 2011 Unixphere
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.

[snip]

>> +/**
>> + * @reset - set this bit to force a firmware reset of the sensor.
>> + */
>> +struct f01_device_commands {
>> + bool reset:1;
>> + u8 reserved:7;
>
> When specifying bitwise fields please use u8, u16, etc only.

Um, OK. Previously patch feedback suggested to use bool instead of u8
for single bit fields (see here:
http://www.spinics.net/lists/linux-input/msg22198.html). So I'm a
little confused. It's no big deal to change it back, but I'd like
confirmation that it is really what we should do.

[snip]

2012-11-26 22:54:27

by Christopher Heiny

[permalink] [raw]
Subject: Re: [RFC PATCH 02/06] input/rmi4: Core files

On 11/26/2012 10:41 AM, Benjamin Tissoires wrote:
> Hi Christopher,
>
> On Sat, Nov 17, 2012 at 4:58 AM, Christopher Heiny <[email protected]> wrote:
>> rmi_bus.c implements the basic functionality of the RMI bus. This file is
>> greatly simplified compared to the previous patch - we've switched from
>> "do it yourself" device/driver binding to using device_type to distinguish
>> between the two kinds of devices on the bus (sensor devices and function
>> specific devices) and using the standard bus implementation to manage devices
>> and drivers.
>>
>>
>> rmi_driver.c is a driver for the general functionality of the RMI sensor as a
>> whole, managing those behaviors (including IRQ handling) that are not specific
>> to any RMI4 function. It has some unavoidable dependencies on F01 behavior,
>> though we have worked to minimize those as far as possible.
>>
>>
>> The header file rmi_driver.h provides definitions that are shared among
>> the modules of the RMI implementation, but not thought to be necessary
>> outside it.
>>
>>
>> Greg KH - Linus Walleij recommended that we seek your input on these core
>> files, particularly the bus implementation.
>>
>>
>> Signed-off-by: Christopher Heiny <[email protected]>
>>
>> Cc: Greg Kroah-Hartman <[email protected]>
>> Cc: Dmitry Torokhov <[email protected]>
>> Cc: Linus Walleij <[email protected]>
>> Cc: Naveen Kumar Gaddipati <[email protected]>
>> Cc: Joeri de Gram <[email protected]>
>>
>> ---
>>
>> drivers/input/rmi4/rmi_bus.c | 248 ++++++
>> drivers/input/rmi4/rmi_driver.c | 1663 +++++++++++++++++++++++++++++++++++++++
>> drivers/input/rmi4/rmi_driver.h | 139 ++++
>> include/uapi/linux/input.h | 1 +
>> 4 files changed, 2051 insertions(+), 0 deletions(-)
>>
> [snipped]
>> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
>> new file mode 100644
>> index 0000000..05a73ae
>> --- /dev/null
>> +++ b/drivers/input/rmi4/rmi_driver.c
> [snipped]
>> +/* extract product ID */
>> +void get_prod_id(struct rmi_device *rmi_dev, struct rmi_driver_data *drvdata)
>> +{
>> + struct device *dev = &rmi_dev->dev;
>> + int retval;
>> + int board = 0, rev = 0;
>> + int i;
>> + static const char * const pattern[] = {
>> + "tm%4d-%d", "s%4d-%d", "s%4d-ver%1d"};
>> + u8 product_id[RMI_PRODUCT_ID_LENGTH+1];
>> +
>> + retval = rmi_read_block(rmi_dev,
>> + drvdata->f01_container->fd.query_base_addr+
>> + sizeof(struct f01_basic_queries),
>> + product_id, RMI_PRODUCT_ID_LENGTH);
>> + if (retval < 0) {
>> + dev_err(dev, "Failed to read product id, code=%d!", retval);
>> + return;
>> + }
>> + product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
>> +
>> + for (i = 0; i < sizeof(product_id); i++)
>> + product_id[i] = tolower(product_id[i]);
>> +
>> + for (i = 0; i < sizeof(pattern); i++) {
>
> This should be ARRAY_SIZE(pattern).
> It gave me a wonderful kernel oops :)

Yep, that's a bug! Oddly enough, it runs without barfing on my systems
(though who knows what horrible things are happening under the hood).
Thanks for letting us know.

Chris

>
> Cheers,
> Benjamin

[snip]

2012-11-27 09:29:20

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC PATCH 05/06] input/rmi4: F01 - device control

On Mon, Nov 26, 2012 at 02:31:27PM -0800, Christopher Heiny wrote:
> On 11/26/2012 01:40 AM, Dmitry Torokhov wrote:
> >Hi Christopher,
> >
> >On Fri, Nov 16, 2012 at 07:58:53PM -0800, Christopher Heiny wrote:
> >>RMI Function 01 implements basic device control and power management
> >>behaviors for the RMI4 sensor.
> >>
> >>rmi_f01.h exports definitions that we expect to be used by other functionality
> >>in the future (such as firmware reflash).
> >
> >Please see my comments below.
>
> Hi Dmitry,
>
> Thanks for the feedback and the patch. I've got just one question,
> included below, with a bunch of snipping).
>
> Chris
>
> >
> >>
> >>
> >>Signed-off-by: Christopher Heiny <[email protected]>
> >>
> >>Cc: Dmitry Torokhov <[email protected]>
> >>Cc: Linus Walleij <[email protected]>
> >>Cc: Naveen Kumar Gaddipati <[email protected]>
> >>Cc: Joeri de Gram <[email protected]>
> >>
> >>
> >>---
> >>
> >> drivers/input/rmi4/rmi_f01.c | 1348 ++++++++++++++++++++++++++++++++++++++++++
> >> drivers/input/rmi4/rmi_f01.h | 160 +++++
> >> 2 files changed, 1508 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..038266c
> >>--- /dev/null
> >>+++ b/drivers/input/rmi4/rmi_f01.c
> >>@@ -0,0 +1,1348 @@
> >>+/*
> >>+ * Copyright (c) 2011-2012 Synaptics Incorporated
> >>+ * Copyright (c) 2011 Unixphere
> >>+ *
> >>+ * This program is free software; you can redistribute it and/or modify
> >>+ * it under the terms of the GNU General Public License as published by
> >>+ * the Free Software Foundation; either version 2 of the License, or
> >>+ * (at your option) any later version.
>
> [snip]
>
> >>+/**
> >>+ * @reset - set this bit to force a firmware reset of the sensor.
> >>+ */
> >>+struct f01_device_commands {
> >>+ bool reset:1;
> >>+ u8 reserved:7;
> >
> >When specifying bitwise fields please use u8, u16, etc only.
>
> Um, OK. Previously patch feedback suggested to use bool instead of
> u8 for single bit fields (see here:
> http://www.spinics.net/lists/linux-input/msg22198.html). So I'm a
> little confused. It's no big deal to change it back, but I'd like
> confirmation that it is really what we should do.

I believe that it is better to specify exact bitness of the base type of
the bitfield so you do not surprised by the alignment.

Thanks.

--
Dmitry

2012-11-27 09:33:50

by Benjamin Tissoires

[permalink] [raw]
Subject: Re: [RFC PATCH 02/06] input/rmi4: Core files

On Mon, Nov 26, 2012 at 11:54 PM, Christopher Heiny
<[email protected]> wrote:
> On 11/26/2012 10:41 AM, Benjamin Tissoires wrote:
>>
>> Hi Christopher,
>>
>> On Sat, Nov 17, 2012 at 4:58 AM, Christopher Heiny <[email protected]>
>> wrote:
>>>
>>> rmi_bus.c implements the basic functionality of the RMI bus. This file
>>> is
>>> greatly simplified compared to the previous patch - we've switched from
>>> "do it yourself" device/driver binding to using device_type to
>>> distinguish
>>> between the two kinds of devices on the bus (sensor devices and function
>>> specific devices) and using the standard bus implementation to manage
>>> devices
>>> and drivers.
>>>
>>>
>>> rmi_driver.c is a driver for the general functionality of the RMI sensor
>>> as a
>>> whole, managing those behaviors (including IRQ handling) that are not
>>> specific
>>> to any RMI4 function. It has some unavoidable dependencies on F01
>>> behavior,
>>> though we have worked to minimize those as far as possible.
>>>
>>>
>>> The header file rmi_driver.h provides definitions that are shared among
>>> the modules of the RMI implementation, but not thought to be necessary
>>> outside it.
>>>
>>>
>>> Greg KH - Linus Walleij recommended that we seek your input on these core
>>> files, particularly the bus implementation.
>>>
>>>
>>> Signed-off-by: Christopher Heiny <[email protected]>
>>>
>>> Cc: Greg Kroah-Hartman <[email protected]>
>>> Cc: Dmitry Torokhov <[email protected]>
>>> Cc: Linus Walleij <[email protected]>
>>> Cc: Naveen Kumar Gaddipati <[email protected]>
>>> Cc: Joeri de Gram <[email protected]>
>>>
>>> ---
>>>
>>> drivers/input/rmi4/rmi_bus.c | 248 ++++++
>>> drivers/input/rmi4/rmi_driver.c | 1663
>>> +++++++++++++++++++++++++++++++++++++++
>>> drivers/input/rmi4/rmi_driver.h | 139 ++++
>>> include/uapi/linux/input.h | 1 +
>>> 4 files changed, 2051 insertions(+), 0 deletions(-)
>>>
>> [snipped]
>>>
>>> diff --git a/drivers/input/rmi4/rmi_driver.c
>>> b/drivers/input/rmi4/rmi_driver.c
>>> new file mode 100644
>>> index 0000000..05a73ae
>>> --- /dev/null
>>> +++ b/drivers/input/rmi4/rmi_driver.c
>>
>> [snipped]
>>>
>>> +/* extract product ID */
>>> +void get_prod_id(struct rmi_device *rmi_dev, struct rmi_driver_data
>>> *drvdata)
>>> +{
>>> + struct device *dev = &rmi_dev->dev;
>>> + int retval;
>>> + int board = 0, rev = 0;
>>> + int i;
>>> + static const char * const pattern[] = {
>>> + "tm%4d-%d", "s%4d-%d", "s%4d-ver%1d"};
>>> + u8 product_id[RMI_PRODUCT_ID_LENGTH+1];
>>> +
>>> + retval = rmi_read_block(rmi_dev,
>>> + drvdata->f01_container->fd.query_base_addr+
>>> + sizeof(struct f01_basic_queries),
>>> + product_id, RMI_PRODUCT_ID_LENGTH);
>>> + if (retval < 0) {
>>> + dev_err(dev, "Failed to read product id, code=%d!",
>>> retval);
>>> + return;
>>> + }
>>> + product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
>>> +
>>> + for (i = 0; i < sizeof(product_id); i++)
>>> + product_id[i] = tolower(product_id[i]);
>>> +
>>> + for (i = 0; i < sizeof(pattern); i++) {
>>
>>
>> This should be ARRAY_SIZE(pattern).
>> It gave me a wonderful kernel oops :)
>
>
> Yep, that's a bug! Oddly enough, it runs without barfing on my systems
> (though who knows what horrible things are happening under the hood). Thanks
> for letting us know.

That's because the product id advertised by the touchpad you sent to
me is 'DS4 R3.0'. So it's not compliant with the patterns that are
advertised: "tm%4d-%d", "s%4d-%d", "s%4d-ver%1d".

If you present one of these patterns, thanks to the break, you leave
the for loop. But as I'm not in this optimal case, I'm having i > 2,
which introduced segfault.

Cheers,
Benjamin

>
> Chris
>
>>
>> Cheers,
>> Benjamin
>
>
> [snip]

2012-11-27 13:01:42

by Benjamin Tissoires

[permalink] [raw]
Subject: Re: [RFC PATCH 06/06] input/rmi4: F11 - 2D touch interface

Hi Christopher,

I did not made a full review, but at least, there is a problem in your
rmi_f11_finger_handler function:

On Sat, Nov 17, 2012 at 4:58 AM, Christopher Heiny <[email protected]> wrote:
> rmi_f11.c is a driver for 2D touch sensors using the RMI4 protocol. It supports
> both touchscreen and touchpad input, in both absolute and relative formats.
> Support for Type-B multitouch is the default, Type-A support is included for
> certain legacy sensors.
>
>
> Signed-off-by: Christopher Heiny <[email protected]>
>
> To: Henrik Rydberg <[email protected]>
> Cc: Dmitry Torokhov <[email protected]>
> Cc: Linus Walleij <[email protected]>
> Cc: Naveen Kumar Gaddipati <[email protected]>
> Cc: Joeri de Gram <[email protected]>
>
> ---
>
> drivers/input/rmi4/rmi_f11.c | 2813 ++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 2813 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..b9a84bc
> --- /dev/null
> +++ b/drivers/input/rmi4/rmi_f11.c
[snipped]
> +static void rmi_f11_finger_handler(struct f11_data *f11,
> + struct f11_2d_sensor *sensor)
> +{
> + const u8 *f_state = sensor->data.f_state;
> + u8 finger_state;
> + u8 finger_pressed_count;
> + u8 i;
> +
> + for (i = 0, finger_pressed_count = 0; i < sensor->nbr_fingers; i++) {
> + /* Possible of having 4 fingers per f_statet register */
> + finger_state = (f_state[i / 4] >> (2 * (i % 4))) &
> + FINGER_STATE_MASK;
> + if (finger_state == F11_RESERVED) {
> + pr_err("%s: Invalid finger state[%d]:0x%02x.", __func__,
> + i, finger_state);
> + continue;
> + } else if ((finger_state == F11_PRESENT) ||
> + (finger_state == F11_INACCURATE)) {
> + finger_pressed_count++;
> + }
> +
> + if (sensor->data.abs_pos)
> + rmi_f11_abs_pos_report(f11, sensor, finger_state, i);
> +
> + if (sensor->data.rel_pos)
> + rmi_f11_rel_pos_report(sensor, i);
> + }
> + input_mt_sync(sensor->input);

This should be "input_mt_sync_frame(sensor->input);"
otherwise, the synaptics xorg driver won't handle the touchpad correctly.

Cheers,
Benjamin

> + input_sync(sensor->input);
> +}
> +
[snipped]

2012-11-28 02:52:59

by Christopher Heiny

[permalink] [raw]
Subject: Re: [RFC PATCH 05/06] input/rmi4: F01 - device control

On 11/27/2012 01:29 AM, Dmitry Torokhov wrote:
> On Mon, Nov 26, 2012 at 02:31:27PM -0800, Christopher Heiny wrote:
>> >On 11/26/2012 01:40 AM, Dmitry Torokhov wrote:
>>> > >Hi Christopher,
>>> > >
>>> > >On Fri, Nov 16, 2012 at 07:58:53PM -0800, Christopher Heiny wrote:
>>>> > >>RMI Function 01 implements basic device control and power management
>>>> > >>behaviors for the RMI4 sensor.
>>>> > >>
>>>> > >>rmi_f01.h exports definitions that we expect to be used by other functionality
>>>> > >>in the future (such as firmware reflash).
>>> > >
>>> > >Please see my comments below.
>> >
>> >Hi Dmitry,
>> >
>> >Thanks for the feedback and the patch. I've got just one question,
>> >included below, with a bunch of snipping).
>> >
>> > Chris
>> >
>>> > >
>>>> > >>
>>>> > >>
>>>> > >>Signed-off-by: Christopher Heiny<[email protected]>
>>>> > >>
>>>> > >>Cc: Dmitry Torokhov<[email protected]>
>>>> > >>Cc: Linus Walleij<[email protected]>
>>>> > >>Cc: Naveen Kumar Gaddipati<[email protected]>
>>>> > >>Cc: Joeri de Gram<[email protected]>
>>>> > >>
>>>> > >>
>>>> > >>---
>>>> > >>
>>>> > >> drivers/input/rmi4/rmi_f01.c | 1348 ++++++++++++++++++++++++++++++++++++++++++
>>>> > >> drivers/input/rmi4/rmi_f01.h | 160 +++++
>>>> > >> 2 files changed, 1508 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..038266c
>>>> > >>--- /dev/null
>>>> > >>+++ b/drivers/input/rmi4/rmi_f01.c
>>>> > >>@@ -0,0 +1,1348 @@
>>>> > >>+/*
>>>> > >>+ * Copyright (c) 2011-2012 Synaptics Incorporated
>>>> > >>+ * Copyright (c) 2011 Unixphere
>>>> > >>+ *
>>>> > >>+ * This program is free software; you can redistribute it and/or modify
>>>> > >>+ * it under the terms of the GNU General Public License as published by
>>>> > >>+ * the Free Software Foundation; either version 2 of the License, or
>>>> > >>+ * (at your option) any later version.
>> >
>> >[snip]
>> >
>>>> > >>+/**
>>>> > >>+ * @reset - set this bit to force a firmware reset of the sensor.
>>>> > >>+ */
>>>> > >>+struct f01_device_commands {
>>>> > >>+ bool reset:1;
>>>> > >>+ u8 reserved:7;
>>> > >
>>> > >When specifying bitwise fields please use u8, u16, etc only.
>> >
>> >Um, OK. Previously patch feedback suggested to use bool instead of
>> >u8 for single bit fields (see here:
>> >http://www.spinics.net/lists/linux-input/msg22198.html). So I'm a
>> >little confused. It's no big deal to change it back, but I'd like
>> >confirmation that it is really what we should do.
>
> I believe that it is better to specify exact bitness of the base type of
> the bitfield so you do not surprised by the alignment.

OK, thanks!