Merge tag 'ib-mfd-gpio-input-leds-power-v5.2' of git://git.kernel.org/pub/scm/linux...
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 10 May 2019 18:45:03 +0000 (11:45 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 10 May 2019 18:45:03 +0000 (11:45 -0700)
Sync up with MFD tree to bring in MAX77650 PMIC driver.

30 files changed:
Documentation/devicetree/bindings/input/gpio-vibrator.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/input/lpc32xx-key.txt
Documentation/devicetree/bindings/input/microchip,qt1050.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
Documentation/devicetree/bindings/input/touchscreen/goodix.txt
Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
drivers/hid/hid-input.c
drivers/input/evdev.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/atkbd.c
drivers/input/keyboard/qt1050.c [new file with mode: 0644]
drivers/input/keyboard/snvs_pwrkey.c
drivers/input/keyboard/sun4i-lradc-keys.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/gpio-vibra.c [new file with mode: 0644]
drivers/input/mouse/psmouse-base.c
drivers/input/rmi4/rmi_f54.c
drivers/input/serio/Kconfig
drivers/input/serio/hyperv-keyboard.c
drivers/input/serio/i8042.c
drivers/input/serio/libps2.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/edt-ft5x06.c
drivers/input/touchscreen/goodix.c
drivers/input/touchscreen/iqs5xx.c [new file with mode: 0644]
include/uapi/linux/input-event-codes.h

diff --git a/Documentation/devicetree/bindings/input/gpio-vibrator.yaml b/Documentation/devicetree/bindings/input/gpio-vibrator.yaml
new file mode 100644 (file)
index 0000000..903475f
--- /dev/null
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/bindings/input/gpio-vibrator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GPIO vibrator
+
+maintainers:
+  - Luca Weiss <luca@z3ntu.xyz>
+
+description: |+
+  Registers a GPIO device as vibrator, where the on/off capability is controlled by a GPIO.
+
+properties:
+  compatible:
+    const: gpio-vibrator
+
+  enable-gpios:
+    maxItems: 1
+
+  vcc-supply:
+    description: Regulator that provides power
+
+required:
+  - compatible
+  - enable-gpios
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    vibrator {
+        compatible = "gpio-vibrator";
+        enable-gpios = <&msmgpio 86 GPIO_ACTIVE_HIGH>;
+        vcc-supply = <&pm8941_l18>;
+    };
index bcf62f856358d095916ffe50de4f9d6e65abae50..2b075a080d303bab7fc2353093ef09f9770edc0f 100644 (file)
@@ -8,6 +8,7 @@ Required Properties:
 - reg: Physical base address of the controller and length of memory mapped
   region.
 - interrupts: The interrupt number to the cpu.
+- clocks: phandle to clock controller plus clock-specifier pair
 - nxp,debounce-delay-ms: Debounce delay in ms
 - nxp,scan-delay-ms: Repeated scan period in ms
 - linux,keymap: the key-code to be reported when the key is pressed
@@ -22,7 +23,9 @@ Example:
        key@40050000 {
                compatible = "nxp,lpc3220-key";
                reg = <0x40050000 0x1000>;
-               interrupts = <54 0>;
+               clocks = <&clk LPC32XX_CLK_KEY>;
+               interrupt-parent = <&sic1>;
+               interrupts = <22 IRQ_TYPE_LEVEL_HIGH>;
                keypad,num-rows = <1>;
                keypad,num-columns = <1>;
                nxp,debounce-delay-ms = <3>;
diff --git a/Documentation/devicetree/bindings/input/microchip,qt1050.txt b/Documentation/devicetree/bindings/input/microchip,qt1050.txt
new file mode 100644 (file)
index 0000000..80e75f9
--- /dev/null
@@ -0,0 +1,78 @@
+Microchip AT42QT1050 Five-channel Touch Sensor IC
+
+The AT42QT1050 (QT1050) is a QTouchADC sensor device. The device can sense from
+one to five keys, dependent on mode. The QT1050 includes all signal processing
+functions necessary to provide stable sensing under a wide variety of changing
+conditions, and the outputs are fully debounced.
+
+The touchkey device node should be placed inside an I2C bus node.
+
+Required properties:
+- compatible: Must be "microchip,qt1050"
+- reg: The I2C address of the device
+- interrupts: The sink for the touchpad's IRQ output,
+  see ../interrupt-controller/interrupts.txt
+
+Optional properties:
+- wakeup-source: touch keys can be used as a wakeup source
+
+Each button (key) is represented as a sub-node:
+
+Each not specified key or key with linux,code set to KEY_RESERVED gets disabled
+in HW.
+
+Subnode properties:
+- linux,code: Keycode to emit.
+- reg: The key number. Valid values: 0, 1, 2, 3, 4.
+
+Optional subnode-properties:
+
+If a optional property is missing or has a invalid value the default value is
+taken.
+
+- microchip,pre-charge-time-ns:
+  Each touchpad need some time to precharge. The value depends on the mechanical
+  layout.
+  Valid value range: 0 - 637500; values must be a multiple of 2500;
+  default is 0.
+- microchip,average-samples:
+  Number of data samples which are averaged for each read.
+  Valid values: 1, 4, 16, 64, 256, 1024, 4096, 16384; default is 1.
+- microchip,average-scaling:
+  The scaling factor which is used to scale the average-samples.
+  Valid values: 1, 2, 4, 8, 16, 32, 64, 128; default is 1.
+- microchip,threshold:
+  Number of counts to register a touch detection.
+  Valid value range: 0 - 255; default is 20.
+
+Example:
+QT1050 with 3 non continuous keys, key2 and key4 are disabled.
+
+touchkeys@41 {
+       compatible = "microchip,qt1050";
+       reg = <0x41>;
+       interrupt-parent = <&gpio0>;
+       interrupts = <17 IRQ_TYPE_EDGE_FALLING>;
+
+       up@0 {
+               reg = <0>;
+               linux,code = <KEY_UP>;
+               microchip,average-samples = <64>;
+               microchip,average-scaling = <16>;
+               microchip,pre-charge-time-ns = <10000>;
+       };
+
+       right@1 {
+               reg = <1>;
+               linux,code = <KEY_RIGHT>;
+               microchip,average-samples = <64>;
+               microchip,average-scaling = <8>;
+       };
+
+       down@3 {
+               reg = <3>;
+               linux,code = <KEY_DOWN>;
+               microchip,average-samples = <256>;
+               microchip,average-scaling = <16>;
+       };
+};
index 1458c3179a63a2e7ea223814169247b97f77ddc3..496125c6bfb7d28168f1afe203f0ba3c716a9563 100644 (file)
@@ -2,12 +2,14 @@ Allwinner sun4i low res adc attached tablet keys
 ------------------------------------------------
 
 Required properties:
- - compatible: "allwinner,sun4i-a10-lradc-keys"
+ - compatible: should be one of the following string:
+               "allwinner,sun4i-a10-lradc-keys"
+               "allwinner,sun8i-a83t-r-lradc"
  - reg: mmio address range of the chip
  - interrupts: interrupt to which the chip is connected
  - vref-supply: powersupply for the lradc reference voltage
 
-Each key is represented as a sub-node of "allwinner,sun4i-a10-lradc-keys":
+Each key is represented as a sub-node of the compatible mentioned above:
 
 Required subnode-properties:
        - label: Descriptive name of the key.
index 8cf0b4d38a7e374e3c5d3eb3cfbcaf28e03dd377..fc03ea4cf5ab395d530d966ba74ae3c1c08ffc54 100644 (file)
@@ -3,6 +3,7 @@ Device tree bindings for Goodix GT9xx series touchscreen controller
 Required properties:
 
  - compatible          : Should be "goodix,gt1151"
+                                or "goodix,gt5663"
                                 or "goodix,gt5688"
                                 or "goodix,gt911"
                                 or "goodix,gt9110"
@@ -19,6 +20,8 @@ Optional properties:
  - irq-gpios           : GPIO pin used for IRQ. The driver uses the
                          interrupt gpio pin as output to reset the device.
  - reset-gpios         : GPIO pin used for reset
+ - AVDD28-supply       : Analog power supply regulator on AVDD28 pin
+ - VDDIO-supply                : GPIO power supply regulator on VDDIO pin
  - touchscreen-inverted-x
  - touchscreen-inverted-y
  - touchscreen-size-x
diff --git a/Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt b/Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt
new file mode 100644 (file)
index 0000000..efa0820
--- /dev/null
@@ -0,0 +1,80 @@
+Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
+
+Required properties:
+
+- compatible                   : Must be equal to one of the following:
+                                 "azoteq,iqs550"
+                                 "azoteq,iqs572"
+                                 "azoteq,iqs525"
+
+- reg                          : I2C slave address for the device.
+
+- interrupts                   : GPIO to which the device's active-high RDY
+                                 output is connected (see [0]).
+
+- reset-gpios                  : GPIO to which the device's active-low NRST
+                                 input is connected (see [1]).
+
+Optional properties:
+
+- touchscreen-min-x            : See [2].
+
+- touchscreen-min-y            : See [2].
+
+- touchscreen-size-x           : See [2]. If this property is omitted, the
+                                 maximum x-coordinate is specified by the
+                                 device's "X Resolution" register.
+
+- touchscreen-size-y           : See [2]. If this property is omitted, the
+                                 maximum y-coordinate is specified by the
+                                 device's "Y Resolution" register.
+
+- touchscreen-max-pressure     : See [2]. Pressure is expressed as the sum of
+                                 the deltas across all channels impacted by a
+                                 touch event. A channel's delta is calculated
+                                 as its count value minus a reference, where
+                                 the count value is inversely proportional to
+                                 the channel's capacitance.
+
+- touchscreen-fuzz-x           : See [2].
+
+- touchscreen-fuzz-y           : See [2].
+
+- touchscreen-fuzz-pressure    : See [2].
+
+- touchscreen-inverted-x       : See [2]. Inversion is applied relative to that
+                                 which may already be specified by the device's
+                                 FLIP_X and FLIP_Y register fields.
+
+- touchscreen-inverted-y       : See [2]. Inversion is applied relative to that
+                                 which may already be specified by the device's
+                                 FLIP_X and FLIP_Y register fields.
+
+- touchscreen-swapped-x-y      : See [2]. Swapping is applied relative to that
+                                 which may already be specified by the device's
+                                 SWITCH_XY_AXIS register field.
+
+[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+[1]: Documentation/devicetree/bindings/gpio/gpio.txt
+[2]: Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
+
+Example:
+
+       &i2c1 {
+               /* ... */
+
+               touchscreen@74 {
+                       compatible = "azoteq,iqs550";
+                       reg = <0x74>;
+                       interrupt-parent = <&gpio>;
+                       interrupts = <17 4>;
+                       reset-gpios = <&gpio 27 1>;
+
+                       touchscreen-size-x = <640>;
+                       touchscreen-size-y = <480>;
+
+                       touchscreen-max-pressure = <16000>;
+               };
+
+               /* ... */
+       };
index 8162b0eb4b5063971f790c52dc8ac7c0857654cd..804bce168f49cd925746100bbab5917c312d5122 100644 (file)
@@ -54,6 +54,7 @@ avic  Shanghai AVIC Optoelectronics Co., Ltd.
 avnet  Avnet, Inc.
 axentia        Axentia Technologies AB
 axis   Axis Communications AB
+azoteq Azoteq (Pty) Ltd
 bananapi BIPAI KEJI LIMITED
 bhf    Beckhoff Automation GmbH & Co. KG
 bitmain        Bitmain Technologies
index b607286a0bc82f360a133b5dce204a8f2441ff5c..0579b8d3f9124db70c1cdecbb53133938fbe7b6f 100644 (file)
@@ -1051,6 +1051,8 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x28b: map_key_clear(KEY_FORWARDMAIL);     break;
                case 0x28c: map_key_clear(KEY_SEND);            break;
 
+               case 0x29d: map_key_clear(KEY_KBD_LAYOUT_NEXT); break;
+
                case 0x2c7: map_key_clear(KEY_KBDINPUTASSIST_PREV);             break;
                case 0x2c8: map_key_clear(KEY_KBDINPUTASSIST_NEXT);             break;
                case 0x2c9: map_key_clear(KEY_KBDINPUTASSIST_PREVGROUP);                break;
index f48369d6f3a0f36ef1ab412ac95a2b2b51d55b3d..ee8dd8b1b09e9513578682479227d5663fae770d 100644 (file)
@@ -503,14 +503,13 @@ static int evdev_open(struct inode *inode, struct file *file)
 {
        struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
        unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
-       unsigned int size = sizeof(struct evdev_client) +
-                                       bufsize * sizeof(struct input_event);
        struct evdev_client *client;
        int error;
 
-       client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
+       client = kzalloc(struct_size(client, buffer, bufsize),
+                        GFP_KERNEL | __GFP_NOWARN);
        if (!client)
-               client = vzalloc(size);
+               client = vzalloc(struct_size(client, buffer, bufsize));
        if (!client)
                return -ENOMEM;
 
index 52d7f55fca329c09c9788cb8bcd91509a5f07426..1fe039d7326bcf47fdb1fe27b5466d9496e2e88a 100644 (file)
@@ -137,6 +137,17 @@ config KEYBOARD_ATKBD_RDI_KEYCODES
          right-hand column will be interpreted as the key shown in the
          left-hand column.
 
+config KEYBOARD_QT1050
+       tristate "Microchip AT42QT1050 Touch Sensor Chip"
+       depends on I2C
+       select REGMAP_I2C
+       help
+         Say Y here if you want to use Microchip AT42QT1050 QTouch
+         Sensor chip as input device.
+
+         To compile this driver as a module, choose M here:
+         the module will be called qt1050
+
 config KEYBOARD_QT1070
        tristate "Atmel AT42QT1070 Touch Sensor Chip"
        depends on I2C
index 182e92985dbf69f18c783db7b04523fbc5ae56a3..f0291ca39f624270fd4f1396e361a05d7008546d 100644 (file)
@@ -50,6 +50,7 @@ obj-$(CONFIG_KEYBOARD_OPENCORES)      += opencores-kbd.o
 obj-$(CONFIG_KEYBOARD_PMIC8XXX)                += pmic8xxx-keypad.o
 obj-$(CONFIG_KEYBOARD_PXA27x)          += pxa27x_keypad.o
 obj-$(CONFIG_KEYBOARD_PXA930_ROTARY)   += pxa930_rotary.o
+obj-$(CONFIG_KEYBOARD_QT1050)           += qt1050.o
 obj-$(CONFIG_KEYBOARD_QT1070)           += qt1070.o
 obj-$(CONFIG_KEYBOARD_QT2160)          += qt2160.o
 obj-$(CONFIG_KEYBOARD_SAMSUNG)         += samsung-keypad.o
index 850bb259c20ebbba2c68f7dd88c6cfb83c9192a1..3ad93e3e2f4c7efcad2c3902e32e93ea066bdaaa 100644 (file)
@@ -401,6 +401,8 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
                if  (ps2_handle_response(&atkbd->ps2dev, data))
                        goto out;
 
+       pm_wakeup_event(&serio->dev, 0);
+
        if (!atkbd->enabled)
                goto out;
 
diff --git a/drivers/input/keyboard/qt1050.c b/drivers/input/keyboard/qt1050.c
new file mode 100644 (file)
index 0000000..403060d
--- /dev/null
@@ -0,0 +1,598 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Microchip AT42QT1050 QTouch Sensor Controller
+ *
+ *  Copyright (C) 2019 Pengutronix, Marco Felsch <kernel@pengutronix.de>
+ *
+ *  Base on AT42QT1070 driver by:
+ *  Bo Shen <voice.shen@atmel.com>
+ *  Copyright (C) 2011 Atmel
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+/* Chip ID */
+#define QT1050_CHIP_ID         0x00
+#define QT1050_CHIP_ID_VER     0x46
+
+/* Firmware version */
+#define QT1050_FW_VERSION      0x01
+
+/* Detection status */
+#define QT1050_DET_STATUS      0x02
+
+/* Key status */
+#define QT1050_KEY_STATUS      0x03
+
+/* Key Signals */
+#define QT1050_KEY_SIGNAL_0_MSB        0x06
+#define QT1050_KEY_SIGNAL_0_LSB        0x07
+#define QT1050_KEY_SIGNAL_1_MSB        0x08
+#define QT1050_KEY_SIGNAL_1_LSB        0x09
+#define QT1050_KEY_SIGNAL_2_MSB        0x0c
+#define QT1050_KEY_SIGNAL_2_LSB        0x0d
+#define QT1050_KEY_SIGNAL_3_MSB        0x0e
+#define QT1050_KEY_SIGNAL_3_LSB        0x0f
+#define QT1050_KEY_SIGNAL_4_MSB        0x10
+#define QT1050_KEY_SIGNAL_4_LSB        0x11
+
+/* Reference data */
+#define QT1050_REF_DATA_0_MSB  0x14
+#define QT1050_REF_DATA_0_LSB  0x15
+#define QT1050_REF_DATA_1_MSB  0x16
+#define QT1050_REF_DATA_1_LSB  0x17
+#define QT1050_REF_DATA_2_MSB  0x1a
+#define QT1050_REF_DATA_2_LSB  0x1b
+#define QT1050_REF_DATA_3_MSB  0x1c
+#define QT1050_REF_DATA_3_LSB  0x1d
+#define QT1050_REF_DATA_4_MSB  0x1e
+#define QT1050_REF_DATA_4_LSB  0x1f
+
+/* Negative threshold level */
+#define QT1050_NTHR_0          0x21
+#define QT1050_NTHR_1          0x22
+#define QT1050_NTHR_2          0x24
+#define QT1050_NTHR_3          0x25
+#define QT1050_NTHR_4          0x26
+
+/* Pulse / Scale  */
+#define QT1050_PULSE_SCALE_0   0x28
+#define QT1050_PULSE_SCALE_1   0x29
+#define QT1050_PULSE_SCALE_2   0x2b
+#define QT1050_PULSE_SCALE_3   0x2c
+#define QT1050_PULSE_SCALE_4   0x2d
+
+/* Detection integrator counter / AKS */
+#define QT1050_DI_AKS_0                0x2f
+#define QT1050_DI_AKS_1                0x30
+#define QT1050_DI_AKS_2                0x32
+#define QT1050_DI_AKS_3                0x33
+#define QT1050_DI_AKS_4                0x34
+
+/* Charge Share Delay */
+#define QT1050_CSD_0           0x36
+#define QT1050_CSD_1           0x37
+#define QT1050_CSD_2           0x39
+#define QT1050_CSD_3           0x3a
+#define QT1050_CSD_4           0x3b
+
+/* Low Power Mode */
+#define QT1050_LPMODE          0x3d
+
+/* Calibration and Reset */
+#define QT1050_RES_CAL         0x3f
+#define QT1050_RES_CAL_RESET           BIT(7)
+#define QT1050_RES_CAL_CALIBRATE       BIT(1)
+
+#define QT1050_MAX_KEYS                5
+#define QT1050_RESET_TIME      255
+
+struct qt1050_key_regs {
+       unsigned int nthr;
+       unsigned int pulse_scale;
+       unsigned int di_aks;
+       unsigned int csd;
+};
+
+struct qt1050_key {
+       u32 num;
+       u32 charge_delay;
+       u32 thr_cnt;
+       u32 samples;
+       u32 scale;
+       u32 keycode;
+};
+
+struct qt1050_priv {
+       struct i2c_client       *client;
+       struct input_dev        *input;
+       struct regmap           *regmap;
+       struct qt1050_key       keys[QT1050_MAX_KEYS];
+       unsigned short          keycodes[QT1050_MAX_KEYS];
+       u8                      reg_keys;
+       u8                      last_keys;
+};
+
+static const struct qt1050_key_regs qt1050_key_regs_data[] = {
+       {
+               .nthr = QT1050_NTHR_0,
+               .pulse_scale = QT1050_PULSE_SCALE_0,
+               .di_aks = QT1050_DI_AKS_0,
+               .csd = QT1050_CSD_0,
+       }, {
+               .nthr = QT1050_NTHR_1,
+               .pulse_scale = QT1050_PULSE_SCALE_1,
+               .di_aks = QT1050_DI_AKS_1,
+               .csd = QT1050_CSD_1,
+       }, {
+               .nthr = QT1050_NTHR_2,
+               .pulse_scale = QT1050_PULSE_SCALE_2,
+               .di_aks = QT1050_DI_AKS_2,
+               .csd = QT1050_CSD_2,
+       }, {
+               .nthr = QT1050_NTHR_3,
+               .pulse_scale = QT1050_PULSE_SCALE_3,
+               .di_aks = QT1050_DI_AKS_3,
+               .csd = QT1050_CSD_3,
+       }, {
+               .nthr = QT1050_NTHR_4,
+               .pulse_scale = QT1050_PULSE_SCALE_4,
+               .di_aks = QT1050_DI_AKS_4,
+               .csd = QT1050_CSD_4,
+       }
+};
+
+static bool qt1050_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case QT1050_DET_STATUS:
+       case QT1050_KEY_STATUS:
+       case QT1050_KEY_SIGNAL_0_MSB:
+       case QT1050_KEY_SIGNAL_0_LSB:
+       case QT1050_KEY_SIGNAL_1_MSB:
+       case QT1050_KEY_SIGNAL_1_LSB:
+       case QT1050_KEY_SIGNAL_2_MSB:
+       case QT1050_KEY_SIGNAL_2_LSB:
+       case QT1050_KEY_SIGNAL_3_MSB:
+       case QT1050_KEY_SIGNAL_3_LSB:
+       case QT1050_KEY_SIGNAL_4_MSB:
+       case QT1050_KEY_SIGNAL_4_LSB:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const struct regmap_range qt1050_readable_ranges[] = {
+       regmap_reg_range(QT1050_CHIP_ID, QT1050_KEY_STATUS),
+       regmap_reg_range(QT1050_KEY_SIGNAL_0_MSB, QT1050_KEY_SIGNAL_1_LSB),
+       regmap_reg_range(QT1050_KEY_SIGNAL_2_MSB, QT1050_KEY_SIGNAL_4_LSB),
+       regmap_reg_range(QT1050_REF_DATA_0_MSB, QT1050_REF_DATA_1_LSB),
+       regmap_reg_range(QT1050_REF_DATA_2_MSB, QT1050_REF_DATA_4_LSB),
+       regmap_reg_range(QT1050_NTHR_0, QT1050_NTHR_1),
+       regmap_reg_range(QT1050_NTHR_2, QT1050_NTHR_4),
+       regmap_reg_range(QT1050_PULSE_SCALE_0, QT1050_PULSE_SCALE_1),
+       regmap_reg_range(QT1050_PULSE_SCALE_2, QT1050_PULSE_SCALE_4),
+       regmap_reg_range(QT1050_DI_AKS_0, QT1050_DI_AKS_1),
+       regmap_reg_range(QT1050_DI_AKS_2, QT1050_DI_AKS_4),
+       regmap_reg_range(QT1050_CSD_0, QT1050_CSD_1),
+       regmap_reg_range(QT1050_CSD_2, QT1050_RES_CAL),
+};
+
+static const struct regmap_access_table qt1050_readable_table = {
+       .yes_ranges = qt1050_readable_ranges,
+       .n_yes_ranges = ARRAY_SIZE(qt1050_readable_ranges),
+};
+
+static const struct regmap_range qt1050_writeable_ranges[] = {
+       regmap_reg_range(QT1050_NTHR_0, QT1050_NTHR_1),
+       regmap_reg_range(QT1050_NTHR_2, QT1050_NTHR_4),
+       regmap_reg_range(QT1050_PULSE_SCALE_0, QT1050_PULSE_SCALE_1),
+       regmap_reg_range(QT1050_PULSE_SCALE_2, QT1050_PULSE_SCALE_4),
+       regmap_reg_range(QT1050_DI_AKS_0, QT1050_DI_AKS_1),
+       regmap_reg_range(QT1050_DI_AKS_2, QT1050_DI_AKS_4),
+       regmap_reg_range(QT1050_CSD_0, QT1050_CSD_1),
+       regmap_reg_range(QT1050_CSD_2, QT1050_RES_CAL),
+};
+
+static const struct regmap_access_table qt1050_writeable_table = {
+       .yes_ranges = qt1050_writeable_ranges,
+       .n_yes_ranges = ARRAY_SIZE(qt1050_writeable_ranges),
+};
+
+static struct regmap_config qt1050_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = QT1050_RES_CAL,
+
+       .cache_type = REGCACHE_RBTREE,
+
+       .wr_table = &qt1050_writeable_table,
+       .rd_table = &qt1050_readable_table,
+       .volatile_reg = qt1050_volatile_reg,
+};
+
+static bool qt1050_identify(struct qt1050_priv *ts)
+{
+       unsigned int val;
+       int err;
+
+       /* Read Chip ID */
+       regmap_read(ts->regmap, QT1050_CHIP_ID, &val);
+       if (val != QT1050_CHIP_ID_VER) {
+               dev_err(&ts->client->dev, "ID %d not supported\n", val);
+               return false;
+       }
+
+       /* Read firmware version */
+       err = regmap_read(ts->regmap, QT1050_FW_VERSION, &val);
+       if (err) {
+               dev_err(&ts->client->dev, "could not read the firmware version\n");
+               return false;
+       }
+
+       dev_info(&ts->client->dev, "AT42QT1050 firmware version %1d.%1d\n",
+                val >> 4, val & 0xf);
+
+       return true;
+}
+
+static irqreturn_t qt1050_irq_threaded(int irq, void *dev_id)
+{
+       struct qt1050_priv *ts = dev_id;
+       struct input_dev *input = ts->input;
+       unsigned long new_keys, changed;
+       unsigned int val;
+       int i, err;
+
+       /* Read the detected status register, thus clearing interrupt */
+       err = regmap_read(ts->regmap, QT1050_DET_STATUS, &val);
+       if (err) {
+               dev_err(&ts->client->dev, "Fail to read detection status: %d\n",
+                       err);
+               return IRQ_NONE;
+       }
+
+       /* Read which key changed, keys are not continuous */
+       err = regmap_read(ts->regmap, QT1050_KEY_STATUS, &val);
+       if (err) {
+               dev_err(&ts->client->dev,
+                       "Fail to determine the key status: %d\n", err);
+               return IRQ_NONE;
+       }
+       new_keys = (val & 0x70) >> 2 | (val & 0x6) >> 1;
+       changed = ts->last_keys ^ new_keys;
+       /* Report registered keys only */
+       changed &= ts->reg_keys;
+
+       for_each_set_bit(i, &changed, QT1050_MAX_KEYS)
+               input_report_key(input, ts->keys[i].keycode,
+                                test_bit(i, &new_keys));
+
+       ts->last_keys = new_keys;
+       input_sync(input);
+
+       return IRQ_HANDLED;
+}
+
+static const struct qt1050_key_regs *qt1050_get_key_regs(int key_num)
+{
+       return &qt1050_key_regs_data[key_num];
+}
+
+static int qt1050_set_key(struct regmap *map, int number, int on)
+{
+       const struct qt1050_key_regs *key_regs;
+
+       key_regs = qt1050_get_key_regs(number);
+
+       return regmap_update_bits(map, key_regs->di_aks, 0xfc,
+                                 on ? BIT(4) : 0x00);
+}
+
+static int qt1050_apply_fw_data(struct qt1050_priv *ts)
+{
+       struct regmap *map = ts->regmap;
+       struct qt1050_key *button = &ts->keys[0];
+       const struct qt1050_key_regs *key_regs;
+       int i, err;
+
+       /* Disable all keys and enable only the specified ones */
+       for (i = 0; i < QT1050_MAX_KEYS; i++) {
+               err = qt1050_set_key(map, i, 0);
+               if (err)
+                       return err;
+       }
+
+       for (i = 0; i < QT1050_MAX_KEYS; i++, button++) {
+               /* Keep KEY_RESERVED keys off */
+               if (button->keycode == KEY_RESERVED)
+                       continue;
+
+               err = qt1050_set_key(map, button->num, 1);
+               if (err)
+                       return err;
+
+               key_regs = qt1050_get_key_regs(button->num);
+
+               err = regmap_write(map, key_regs->pulse_scale,
+                                  (button->samples << 4) | (button->scale));
+               if (err)
+                       return err;
+               err = regmap_write(map, key_regs->csd, button->charge_delay);
+               if (err)
+                       return err;
+               err = regmap_write(map, key_regs->nthr, button->thr_cnt);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int qt1050_parse_fw(struct qt1050_priv *ts)
+{
+       struct device *dev = &ts->client->dev;
+       struct fwnode_handle *child;
+       int nbuttons;
+
+       nbuttons = device_get_child_node_count(dev);
+       if (nbuttons == 0 || nbuttons > QT1050_MAX_KEYS)
+               return -ENODEV;
+
+       device_for_each_child_node(dev, child) {
+               struct qt1050_key button;
+
+               /* Required properties */
+               if (fwnode_property_read_u32(child, "linux,code",
+                                            &button.keycode)) {
+                       dev_err(dev, "Button without keycode\n");
+                       goto err;
+               }
+               if (button.keycode >= KEY_MAX) {
+                       dev_err(dev, "Invalid keycode 0x%x\n",
+                               button.keycode);
+                       goto err;
+               }
+
+               if (fwnode_property_read_u32(child, "reg",
+                                            &button.num)) {
+                       dev_err(dev, "Button without pad number\n");
+                       goto err;
+               }
+               if (button.num < 0 || button.num > QT1050_MAX_KEYS - 1)
+                       goto err;
+
+               ts->reg_keys |= BIT(button.num);
+
+               /* Optional properties */
+               if (fwnode_property_read_u32(child,
+                                            "microchip,pre-charge-time-ns",
+                                            &button.charge_delay)) {
+                       button.charge_delay = 0;
+               } else {
+                       if (button.charge_delay % 2500 == 0)
+                               button.charge_delay =
+                                       button.charge_delay / 2500;
+                       else
+                               button.charge_delay = 0;
+               }
+
+               if (fwnode_property_read_u32(child, "microchip,average-samples",
+                                        &button.samples)) {
+                       button.samples = 0;
+               } else {
+                       if (is_power_of_2(button.samples))
+                               button.samples = ilog2(button.samples);
+                       else
+                               button.samples = 0;
+               }
+
+               if (fwnode_property_read_u32(child, "microchip,average-scaling",
+                                            &button.scale)) {
+                       button.scale = 0;
+               } else {
+                       if (is_power_of_2(button.scale))
+                               button.scale = ilog2(button.scale);
+                       else
+                               button.scale = 0;
+
+               }
+
+               if (fwnode_property_read_u32(child, "microchip,threshold",
+                                        &button.thr_cnt)) {
+                       button.thr_cnt = 20;
+               } else {
+                       if (button.thr_cnt > 255)
+                               button.thr_cnt = 20;
+               }
+
+               ts->keys[button.num] = button;
+       }
+
+       return 0;
+
+err:
+       fwnode_handle_put(child);
+       return -EINVAL;
+}
+
+static int qt1050_probe(struct i2c_client *client)
+{
+       struct qt1050_priv *ts;
+       struct input_dev *input;
+       struct device *dev = &client->dev;
+       struct regmap *map;
+       unsigned int status, i;
+       int err;
+
+       /* Check basic functionality */
+       err = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE);
+       if (!err) {
+               dev_err(&client->dev, "%s adapter not supported\n",
+                       dev_driver_string(&client->adapter->dev));
+               return -ENODEV;
+       }
+
+       if (!client->irq) {
+               dev_err(dev, "assign a irq line to this device\n");
+               return -EINVAL;
+       }
+
+       ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
+       if (!ts)
+               return -ENOMEM;
+
+       input = devm_input_allocate_device(dev);
+       if (!input)
+               return -ENOMEM;
+
+       map = devm_regmap_init_i2c(client, &qt1050_regmap_config);
+       if (IS_ERR(map))
+               return PTR_ERR(map);
+
+       ts->client = client;
+       ts->input = input;
+       ts->regmap = map;
+
+       i2c_set_clientdata(client, ts);
+
+       /* Identify the qt1050 chip */
+       if (!qt1050_identify(ts))
+               return -ENODEV;
+
+       /* Get pdata */
+       err = qt1050_parse_fw(ts);
+       if (err) {
+               dev_err(dev, "Failed to parse firmware: %d\n", err);
+               return err;
+       }
+
+       input->name = "AT42QT1050 QTouch Sensor";
+       input->dev.parent = &client->dev;
+       input->id.bustype = BUS_I2C;
+
+       /* Add the keycode */
+       input->keycode = ts->keycodes;
+       input->keycodesize = sizeof(ts->keycodes[0]);
+       input->keycodemax = QT1050_MAX_KEYS;
+
+       __set_bit(EV_KEY, input->evbit);
+       for (i = 0; i < QT1050_MAX_KEYS; i++) {
+               ts->keycodes[i] = ts->keys[i].keycode;
+               __set_bit(ts->keycodes[i], input->keybit);
+       }
+
+       /* Trigger re-calibration */
+       err = regmap_update_bits(ts->regmap, QT1050_RES_CAL, 0x7f,
+                                QT1050_RES_CAL_CALIBRATE);
+       if (err) {
+               dev_err(dev, "Trigger calibration failed: %d\n", err);
+               return err;
+       }
+       err = regmap_read_poll_timeout(ts->regmap, QT1050_DET_STATUS, status,
+                                status >> 7 == 1, 10000, 200000);
+       if (err) {
+               dev_err(dev, "Calibration failed: %d\n", err);
+               return err;
+       }
+
+       /* Soft reset to set defaults */
+       err = regmap_update_bits(ts->regmap, QT1050_RES_CAL,
+                                QT1050_RES_CAL_RESET, QT1050_RES_CAL_RESET);
+       if (err) {
+               dev_err(dev, "Trigger soft reset failed: %d\n", err);
+               return err;
+       }
+       msleep(QT1050_RESET_TIME);
+
+       /* Set pdata */
+       err = qt1050_apply_fw_data(ts);
+       if (err) {
+               dev_err(dev, "Failed to set firmware data: %d\n", err);
+               return err;
+       }
+
+       err = devm_request_threaded_irq(dev, client->irq, NULL,
+                                       qt1050_irq_threaded, IRQF_ONESHOT,
+                                       "qt1050", ts);
+       if (err) {
+               dev_err(&client->dev, "Failed to request irq: %d\n", err);
+               return err;
+       }
+
+       /* Clear #CHANGE line */
+       err = regmap_read(ts->regmap, QT1050_DET_STATUS, &status);
+       if (err) {
+               dev_err(dev, "Failed to clear #CHANGE line level: %d\n", err);
+               return err;
+       }
+
+       /* Register the input device */
+       err = input_register_device(ts->input);
+       if (err) {
+               dev_err(&client->dev, "Failed to register input device: %d\n",
+                       err);
+               return err;
+       }
+
+       return 0;
+}
+
+static int __maybe_unused qt1050_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct qt1050_priv *ts = i2c_get_clientdata(client);
+
+       disable_irq(client->irq);
+
+       /*
+        * Set measurement interval to 1s (125 x 8ms) if wakeup is allowed
+        * else turn off. The 1s interval seems to be a good compromise between
+        * low power and response time.
+        */
+       return regmap_write(ts->regmap, QT1050_LPMODE,
+                           device_may_wakeup(dev) ? 125 : 0);
+}
+
+static int __maybe_unused qt1050_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct qt1050_priv *ts = i2c_get_clientdata(client);
+
+       enable_irq(client->irq);
+
+       /* Set measurement interval back to 16ms (2 x 8ms) */
+       return regmap_write(ts->regmap, QT1050_LPMODE, 2);
+}
+
+static SIMPLE_DEV_PM_OPS(qt1050_pm_ops, qt1050_suspend, qt1050_resume);
+
+static const struct of_device_id __maybe_unused qt1050_of_match[] = {
+       { .compatible = "microchip,qt1050", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, qt1050_of_match);
+
+static struct i2c_driver qt1050_driver = {
+       .driver = {
+               .name = "qt1050",
+               .of_match_table = of_match_ptr(qt1050_of_match),
+               .pm = &qt1050_pm_ops,
+       },
+       .probe_new = qt1050_probe,
+};
+
+module_i2c_driver(qt1050_driver);
+
+MODULE_AUTHOR("Marco Felsch <kernel@pengutronix.de");
+MODULE_DESCRIPTION("Driver for AT42QT1050 QTouch sensor");
+MODULE_LICENSE("GPL v2");
index 4c67cf30a5d9ab14bff5f5c53d289ba347d241f4..5342d8d45f811b399bb740ec4a0158d50c502c44 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
 
@@ -167,28 +168,9 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
        }
 
        device_init_wakeup(&pdev->dev, pdata->wakeup);
-
-       return 0;
-}
-
-static int __maybe_unused imx_snvs_pwrkey_suspend(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
-
-       if (device_may_wakeup(&pdev->dev))
-               enable_irq_wake(pdata->irq);
-
-       return 0;
-}
-
-static int __maybe_unused imx_snvs_pwrkey_resume(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
-
-       if (device_may_wakeup(&pdev->dev))
-               disable_irq_wake(pdata->irq);
+       error = dev_pm_set_wake_irq(&pdev->dev, pdata->irq);
+       if (error)
+               dev_err(&pdev->dev, "irq wake enable failed.\n");
 
        return 0;
 }
@@ -199,13 +181,9 @@ static const struct of_device_id imx_snvs_pwrkey_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, imx_snvs_pwrkey_ids);
 
-static SIMPLE_DEV_PM_OPS(imx_snvs_pwrkey_pm_ops, imx_snvs_pwrkey_suspend,
-                               imx_snvs_pwrkey_resume);
-
 static struct platform_driver imx_snvs_pwrkey_driver = {
        .driver = {
                .name = "snvs_pwrkey",
-               .pm     = &imx_snvs_pwrkey_pm_ops,
                .of_match_table = imx_snvs_pwrkey_ids,
        },
        .probe = imx_snvs_pwrkey_probe,
index 57272df34cd5447373e32abba853ac694f5af3c0..df3eec72a9b236108ba7f298d5d4488216f0d786 100644 (file)
@@ -46,6 +46,7 @@
 #define CONTINUE_TIME_SEL(x)   ((x) << 16) /* 4 bits */
 #define KEY_MODE_SEL(x)                ((x) << 12) /* 2 bits */
 #define LEVELA_B_CNT(x)                ((x) << 8)  /* 4 bits */
+#define HOLD_KEY_EN(x)         ((x) << 7)
 #define HOLD_EN(x)             ((x) << 6)
 #define LEVELB_VOL(x)          ((x) << 4)  /* 2 bits */
 #define SAMPLE_RATE(x)         ((x) << 2)  /* 2 bits */
 #define        CHAN0_KEYDOWN_IRQ       BIT(1)
 #define CHAN0_DATA_IRQ         BIT(0)
 
+/* struct lradc_variant - Describe sun4i-a10-lradc-keys hardware variant
+ * @divisor_numerator:         The numerator of lradc Vref internally divisor
+ * @divisor_denominator:       The denominator of lradc Vref internally divisor
+ */
+struct lradc_variant {
+       u8 divisor_numerator;
+       u8 divisor_denominator;
+};
+
+static const struct lradc_variant lradc_variant_a10 = {
+       .divisor_numerator = 2,
+       .divisor_denominator = 3
+};
+
+static const struct lradc_variant r_lradc_variant_a83t = {
+       .divisor_numerator = 3,
+       .divisor_denominator = 4
+};
+
 struct sun4i_lradc_keymap {
        u32 voltage;
        u32 keycode;
@@ -74,6 +94,7 @@ struct sun4i_lradc_data {
        void __iomem *base;
        struct regulator *vref_supply;
        struct sun4i_lradc_keymap *chan0_map;
+       const struct lradc_variant *variant;
        u32 chan0_map_count;
        u32 chan0_keycode;
        u32 vref;
@@ -128,9 +149,9 @@ static int sun4i_lradc_open(struct input_dev *dev)
        if (error)
                return error;
 
-       /* lradc Vref internally is divided by 2/3 */
-       lradc->vref = regulator_get_voltage(lradc->vref_supply) * 2 / 3;
-
+       lradc->vref = regulator_get_voltage(lradc->vref_supply) *
+                     lradc->variant->divisor_numerator /
+                     lradc->variant->divisor_denominator;
        /*
         * Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to
         * stabilize on press, wait (1 + 1) * 4 ms for key release
@@ -222,6 +243,12 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
        if (error)
                return error;
 
+       lradc->variant = of_device_get_match_data(&pdev->dev);
+       if (!lradc->variant) {
+               dev_err(&pdev->dev, "Missing sun4i-a10-lradc-keys variant\n");
+               return -EINVAL;
+       }
+
        lradc->vref_supply = devm_regulator_get(dev, "vref");
        if (IS_ERR(lradc->vref_supply))
                return PTR_ERR(lradc->vref_supply);
@@ -265,7 +292,10 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
 }
 
 static const struct of_device_id sun4i_lradc_of_match[] = {
-       { .compatible = "allwinner,sun4i-a10-lradc-keys", },
+       { .compatible = "allwinner,sun4i-a10-lradc-keys",
+               .data = &lradc_variant_a10 },
+       { .compatible = "allwinner,sun8i-a83t-r-lradc",
+               .data = &r_lradc_variant_a83t },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match);
index 85bc675eecd3c4b1e4e6741950aa4581240a8907..54d36f98b426df4ea4479af7729500ca0308e038 100644 (file)
@@ -299,6 +299,18 @@ config INPUT_GPIO_DECODER
         To compile this driver as a module, choose M here: the module
         will be called gpio_decoder.
 
+config INPUT_GPIO_VIBRA
+       tristate "GPIO vibrator support"
+       depends on GPIOLIB || COMPILE_TEST
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here to get support for GPIO based vibrator devices.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the module will be
+         called gpio-vibra.
+
 config INPUT_IXP4XX_BEEPER
        tristate "IXP4XX Beeper support"
        depends on ARCH_IXP4XX
index ffd72161c79bd67ff1dbfeb9e33c34593df3a1cb..8fd187f314bdffd6715793a7e5b654f47e38d6b6 100644 (file)
@@ -36,6 +36,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS)   += drv2667.o
 obj-$(CONFIG_INPUT_GP2A)               += gp2ap002a00f.o
 obj-$(CONFIG_INPUT_GPIO_BEEPER)                += gpio-beeper.o
 obj-$(CONFIG_INPUT_GPIO_DECODER)       += gpio_decoder.o
+obj-$(CONFIG_INPUT_GPIO_VIBRA)         += gpio-vibra.o
 obj-$(CONFIG_INPUT_HISI_POWERKEY)      += hisi_powerkey.o
 obj-$(CONFIG_HP_SDC_RTC)               += hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_IMS_PCU)            += ims-pcu.o
diff --git a/drivers/input/misc/gpio-vibra.c b/drivers/input/misc/gpio-vibra.c
new file mode 100644 (file)
index 0000000..f79f755
--- /dev/null
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  GPIO vibrator driver
+ *
+ *  Copyright (C) 2019 Luca Weiss <luca@z3ntu.xyz>
+ *
+ *  Based on PWM vibrator driver:
+ *  Copyright (C) 2017 Collabora Ltd.
+ *
+ *  Based on previous work from:
+ *  Copyright (C) 2012 Dmitry Torokhov <dmitry.torokhov@gmail.com>
+ *
+ *  Based on PWM beeper driver:
+ *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+struct gpio_vibrator {
+       struct input_dev *input;
+       struct gpio_desc *gpio;
+       struct regulator *vcc;
+
+       struct work_struct play_work;
+       bool running;
+       bool vcc_on;
+};
+
+static int gpio_vibrator_start(struct gpio_vibrator *vibrator)
+{
+       struct device *pdev = vibrator->input->dev.parent;
+       int err;
+
+       if (!vibrator->vcc_on) {
+               err = regulator_enable(vibrator->vcc);
+               if (err) {
+                       dev_err(pdev, "failed to enable regulator: %d\n", err);
+                       return err;
+               }
+               vibrator->vcc_on = true;
+       }
+
+       gpiod_set_value_cansleep(vibrator->gpio, 1);
+
+       return 0;
+}
+
+static void gpio_vibrator_stop(struct gpio_vibrator *vibrator)
+{
+       gpiod_set_value_cansleep(vibrator->gpio, 0);
+
+       if (vibrator->vcc_on) {
+               regulator_disable(vibrator->vcc);
+               vibrator->vcc_on = false;
+       }
+}
+
+static void gpio_vibrator_play_work(struct work_struct *work)
+{
+       struct gpio_vibrator *vibrator =
+               container_of(work, struct gpio_vibrator, play_work);
+
+       if (vibrator->running)
+               gpio_vibrator_start(vibrator);
+       else
+               gpio_vibrator_stop(vibrator);
+}
+
+static int gpio_vibrator_play_effect(struct input_dev *dev, void *data,
+                                    struct ff_effect *effect)
+{
+       struct gpio_vibrator *vibrator = input_get_drvdata(dev);
+       int level;
+
+       level = effect->u.rumble.strong_magnitude;
+       if (!level)
+               level = effect->u.rumble.weak_magnitude;
+
+       vibrator->running = level;
+       schedule_work(&vibrator->play_work);
+
+       return 0;
+}
+
+static void gpio_vibrator_close(struct input_dev *input)
+{
+       struct gpio_vibrator *vibrator = input_get_drvdata(input);
+
+       cancel_work_sync(&vibrator->play_work);
+       gpio_vibrator_stop(vibrator);
+       vibrator->running = false;
+}
+
+static int gpio_vibrator_probe(struct platform_device *pdev)
+{
+       struct gpio_vibrator *vibrator;
+       int err;
+
+       vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL);
+       if (!vibrator)
+               return -ENOMEM;
+
+       vibrator->input = devm_input_allocate_device(&pdev->dev);
+       if (!vibrator->input)
+               return -ENOMEM;
+
+       vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
+       err = PTR_ERR_OR_ZERO(vibrator->vcc);
+       if (err) {
+               if (err != -EPROBE_DEFER)
+                       dev_err(&pdev->dev, "Failed to request regulator: %d\n",
+                               err);
+               return err;
+       }
+
+       vibrator->gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW);
+       err = PTR_ERR_OR_ZERO(vibrator->gpio);
+       if (err) {
+               if (err != -EPROBE_DEFER)
+                       dev_err(&pdev->dev, "Failed to request main gpio: %d\n",
+                               err);
+               return err;
+       }
+
+       INIT_WORK(&vibrator->play_work, gpio_vibrator_play_work);
+
+       vibrator->input->name = "gpio-vibrator";
+       vibrator->input->id.bustype = BUS_HOST;
+       vibrator->input->close = gpio_vibrator_close;
+
+       input_set_drvdata(vibrator->input, vibrator);
+       input_set_capability(vibrator->input, EV_FF, FF_RUMBLE);
+
+       err = input_ff_create_memless(vibrator->input, NULL,
+                                     gpio_vibrator_play_effect);
+       if (err) {
+               dev_err(&pdev->dev, "Couldn't create FF dev: %d\n", err);
+               return err;
+       }
+
+       err = input_register_device(vibrator->input);
+       if (err) {
+               dev_err(&pdev->dev, "Couldn't register input dev: %d\n", err);
+               return err;
+       }
+
+       platform_set_drvdata(pdev, vibrator);
+
+       return 0;
+}
+
+static int __maybe_unused gpio_vibrator_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct gpio_vibrator *vibrator = platform_get_drvdata(pdev);
+
+       cancel_work_sync(&vibrator->play_work);
+       if (vibrator->running)
+               gpio_vibrator_stop(vibrator);
+
+       return 0;
+}
+
+static int __maybe_unused gpio_vibrator_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct gpio_vibrator *vibrator = platform_get_drvdata(pdev);
+
+       if (vibrator->running)
+               gpio_vibrator_start(vibrator);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(gpio_vibrator_pm_ops,
+                        gpio_vibrator_suspend, gpio_vibrator_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id gpio_vibra_dt_match_table[] = {
+       { .compatible = "gpio-vibrator" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, gpio_vibra_dt_match_table);
+#endif
+
+static struct platform_driver gpio_vibrator_driver = {
+       .probe  = gpio_vibrator_probe,
+       .driver = {
+               .name   = "gpio-vibrator",
+               .pm     = &gpio_vibrator_pm_ops,
+               .of_match_table = of_match_ptr(gpio_vibra_dt_match_table),
+       },
+};
+module_platform_driver(gpio_vibrator_driver);
+
+MODULE_AUTHOR("Luca Weiss <luca@z3ntu.xy>");
+MODULE_DESCRIPTION("GPIO vibrator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio-vibrator");
index d3ff1fc09af712700507d05ac3548703e49173a1..94f7ca5ad0772a3ec367e0b326b740077eb36d03 100644 (file)
@@ -373,6 +373,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
                if  (ps2_handle_response(&psmouse->ps2dev, data))
                        goto out;
 
+       pm_wakeup_event(&serio->dev, 0);
+
        if (psmouse->state <= PSMOUSE_RESYNCING)
                goto out;
 
index a6f515bcab2228a8783f10dbf10fae30462fd852..516fea06ed5950ed2764d5480e89c4fb92addf66 100644 (file)
@@ -456,25 +456,15 @@ static int rmi_f54_vidioc_fmt(struct file *file, void *priv,
 static int rmi_f54_vidioc_enum_fmt(struct file *file, void *priv,
                                   struct v4l2_fmtdesc *fmt)
 {
+       struct f54_data *f54 = video_drvdata(file);
+
        if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
 
-       switch (fmt->index) {
-       case 0:
-               fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
-               break;
-
-       case 1:
-               fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD08;
-               break;
-
-       case 2:
-               fmt->pixelformat = V4L2_TCH_FMT_TU16;
-               break;
-
-       default:
+       if (fmt->index)
                return -EINVAL;
-       }
+
+       fmt->pixelformat = f54->format.pixelformat;
 
        return 0;
 }
@@ -692,6 +682,7 @@ static int rmi_f54_probe(struct rmi_function *fn)
                return -ENOMEM;
 
        rmi_f54_create_input_map(f54);
+       rmi_f54_set_input(f54, 0);
 
        /* register video device */
        strlcpy(f54->v4l2.name, F54_NAME, sizeof(f54->v4l2.name));
index c9c7224d5ae08517489c1d8ee8775c2630170514..bfe436ccb046e3eeb227751fa394c424eb2e2edf 100644 (file)
@@ -254,6 +254,7 @@ config SERIO_APBPS2
 
 config SERIO_OLPC_APSP
        tristate "OLPC AP-SP input support"
+       depends on ARCH_MMP || COMPILE_TEST
        help
          Say Y here if you want support for the keyboard and touchpad included
          in the OLPC XO-1.75 and XO-4 laptops.
index a8b9be3e28db709ef8769e29da2684c3d1e3bcf9..7935e52b54358dc4e3c0937a984d8b929b20531a 100644 (file)
@@ -440,5 +440,7 @@ static void __exit hv_kbd_exit(void)
 }
 
 MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Keyboard Driver");
+
 module_init(hv_kbd_init);
 module_exit(hv_kbd_exit);
index 95a78ccbd847035007bac3e5ea43e5ee04db35de..6462f1798fbb5ab5976e45071b085eb007d805ad 100644 (file)
@@ -573,9 +573,6 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
        port = &i8042_ports[port_no];
        serio = port->exists ? port->serio : NULL;
 
-       if (irq && serio)
-               pm_wakeup_event(&serio->dev, 0);
-
        filter_dbg(port->driver_bound, data, "<- i8042 (interrupt, %d, %d%s%s)\n",
                   port_no, irq,
                   dfl & SERIO_PARITY ? ", bad parity" : "",
index e6a07e68d1ff69d0ae16ac9d862c8816eb39cc05..22b8e05aa36ca5cc4cfaf2579931f0c841c05a97 100644 (file)
@@ -409,6 +409,7 @@ bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data)
                        ps2dev->nak = PS2_RET_ERR;
                        break;
                }
+               /* Fall through */
 
        /*
         * Workaround for mice which don't ACK the Get ID command.
index 7a4884ad198b8387f52dff71e4f4f2c1dfcf9a1c..a2029c3235af7e627ce7459fd6f7511ae7918c38 100644 (file)
@@ -1312,4 +1312,14 @@ config TOUCHSCREEN_ROHM_BU21023
          To compile this driver as a module, choose M here: the
          module will be called bu21023_ts.
 
+config TOUCHSCREEN_IQS5XX
+       tristate "Azoteq IQS550/572/525 trackpad/touchscreen controller"
+       depends on I2C
+       help
+         Say Y to enable support for the Azoteq IQS550/572/525
+         family of trackpad/touchscreen controllers.
+
+         To compile this driver as a module, choose M here: the
+         module will be called iqs5xx.
+
 endif
index fcc7605fba8dba99e88d6b319b0ab94bd538f734..084a596a0c8bda9e064498c1c736d9868814bfff 100644 (file)
@@ -110,3 +110,4 @@ obj-$(CONFIG_TOUCHSCREEN_ZFORCE)    += zforce_ts.o
 obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o
 obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW)       += raspberrypi-ts.o
+obj-$(CONFIG_TOUCHSCREEN_IQS5XX)       += iqs5xx.o
index 702bfda7ee777eb66fa75d2d33b8edacb01cbfa5..c639ebce914c07fb936684bd37d0321e51250f0c 100644 (file)
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de>
  * Daniel Wagener <daniel.wagener@kernelconcepts.de> (M09 firmware support)
  * Lothar Waßmann <LW@KARO-electronics.de> (DT support)
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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 library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 /*
@@ -39,7 +27,6 @@
 #include <linux/gpio/consumer.h>
 #include <linux/input/mt.h>
 #include <linux/input/touchscreen.h>
-#include <linux/of_device.h>
 
 #define WORK_REGISTER_THRESHOLD                0x00
 #define WORK_REGISTER_REPORT_RATE      0x08
@@ -1073,7 +1060,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
                return -ENOMEM;
        }
 
-       chip_data = of_device_get_match_data(&client->dev);
+       chip_data = device_get_match_data(&client->dev);
        if (!chip_data)
                chip_data = (const struct edt_i2c_chip_data *)id->driver_data;
        if (!chip_data || !chip_data->max_support_points) {
@@ -1254,7 +1241,6 @@ static const struct i2c_device_id edt_ft5x06_ts_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
 
-#ifdef CONFIG_OF
 static const struct of_device_id edt_ft5x06_of_match[] = {
        { .compatible = "edt,edt-ft5206", .data = &edt_ft5x06_data },
        { .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data },
@@ -1266,12 +1252,11 @@ static const struct of_device_id edt_ft5x06_of_match[] = {
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
-#endif
 
 static struct i2c_driver edt_ft5x06_ts_driver = {
        .driver = {
                .name = "edt_ft5x06",
-               .of_match_table = of_match_ptr(edt_ft5x06_of_match),
+               .of_match_table = edt_ft5x06_of_match,
                .pm = &edt_ft5x06_ts_pm_ops,
        },
        .id_table = edt_ft5x06_ts_id,
@@ -1283,4 +1268,4 @@ module_i2c_driver(edt_ft5x06_ts_driver);
 
 MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>");
 MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
index f57d82220a881cca3bef6f76be902e4792dbabda..f7c1d168dd897850368da8f6b915d9a0db148244 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/delay.h>
 #include <linux/irq.h>
 #include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/acpi.h>
 #include <linux/of.h>
@@ -47,6 +48,8 @@ struct goodix_ts_data {
        struct touchscreen_properties prop;
        unsigned int max_touch_num;
        unsigned int int_trigger_type;
+       struct regulator *avdd28;
+       struct regulator *vddio;
        struct gpio_desc *gpiod_int;
        struct gpio_desc *gpiod_rst;
        u16 id;
@@ -216,6 +219,7 @@ static const struct goodix_chip_data *goodix_get_chip_data(u16 id)
 {
        switch (id) {
        case 1151:
+       case 5663:
        case 5688:
                return &gt1x_chip_data;
 
@@ -532,6 +536,24 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
                return -EINVAL;
        dev = &ts->client->dev;
 
+       ts->avdd28 = devm_regulator_get(dev, "AVDD28");
+       if (IS_ERR(ts->avdd28)) {
+               error = PTR_ERR(ts->avdd28);
+               if (error != -EPROBE_DEFER)
+                       dev_err(dev,
+                               "Failed to get AVDD28 regulator: %d\n", error);
+               return error;
+       }
+
+       ts->vddio = devm_regulator_get(dev, "VDDIO");
+       if (IS_ERR(ts->vddio)) {
+               error = PTR_ERR(ts->vddio);
+               if (error != -EPROBE_DEFER)
+                       dev_err(dev,
+                               "Failed to get VDDIO regulator: %d\n", error);
+               return error;
+       }
+
        /* Get the interrupt GPIO pin number */
        gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN);
        if (IS_ERR(gpiod)) {
@@ -764,6 +786,14 @@ err_release_cfg:
        complete_all(&ts->firmware_loading_complete);
 }
 
+static void goodix_disable_regulators(void *arg)
+{
+       struct goodix_ts_data *ts = arg;
+
+       regulator_disable(ts->vddio);
+       regulator_disable(ts->avdd28);
+}
+
 static int goodix_ts_probe(struct i2c_client *client,
                           const struct i2c_device_id *id)
 {
@@ -789,6 +819,29 @@ static int goodix_ts_probe(struct i2c_client *client,
        if (error)
                return error;
 
+       /* power up the controller */
+       error = regulator_enable(ts->avdd28);
+       if (error) {
+               dev_err(&client->dev,
+                       "Failed to enable AVDD28 regulator: %d\n",
+                       error);
+               return error;
+       }
+
+       error = regulator_enable(ts->vddio);
+       if (error) {
+               dev_err(&client->dev,
+                       "Failed to enable VDDIO regulator: %d\n",
+                       error);
+               regulator_disable(ts->avdd28);
+               return error;
+       }
+
+       error = devm_add_action_or_reset(&client->dev,
+                                        goodix_disable_regulators, ts);
+       if (error)
+               return error;
+
        if (ts->gpiod_int && ts->gpiod_rst) {
                /* reset the controller */
                error = goodix_reset(ts);
@@ -945,6 +998,7 @@ MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
 #ifdef CONFIG_OF
 static const struct of_device_id goodix_of_match[] = {
        { .compatible = "goodix,gt1151" },
+       { .compatible = "goodix,gt5663" },
        { .compatible = "goodix,gt5688" },
        { .compatible = "goodix,gt911" },
        { .compatible = "goodix,gt9110" },
diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c
new file mode 100644 (file)
index 0000000..b832fe0
--- /dev/null
@@ -0,0 +1,1133 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
+ *
+ * Copyright (C) 2018
+ * Author: Jeff LaBundy <jeff@labundy.com>
+ *
+ * These devices require firmware exported from a PC-based configuration tool
+ * made available by the vendor. Firmware files may be pushed to the device's
+ * nonvolatile memory by writing the filename to the 'fw_file' sysfs control.
+ *
+ * Link to PC-based configuration tool and data sheet: http://www.azoteq.com/
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+#define IQS5XX_FW_FILE_LEN     64
+#define IQS5XX_NUM_RETRIES     10
+#define IQS5XX_NUM_POINTS      256
+#define IQS5XX_NUM_CONTACTS    5
+#define IQS5XX_WR_BYTES_MAX    2
+
+#define IQS5XX_PROD_NUM_IQS550 40
+#define IQS5XX_PROD_NUM_IQS572 58
+#define IQS5XX_PROD_NUM_IQS525 52
+#define IQS5XX_PROJ_NUM_A000   0
+#define IQS5XX_PROJ_NUM_B000   15
+#define IQS5XX_MAJOR_VER_MIN   2
+
+#define IQS5XX_RESUME          0x00
+#define IQS5XX_SUSPEND         0x01
+
+#define IQS5XX_SW_INPUT_EVENT  0x10
+#define IQS5XX_SETUP_COMPLETE  0x40
+#define IQS5XX_EVENT_MODE      0x01
+#define IQS5XX_TP_EVENT                0x04
+
+#define IQS5XX_FLIP_X          0x01
+#define IQS5XX_FLIP_Y          0x02
+#define IQS5XX_SWITCH_XY_AXIS  0x04
+
+#define IQS5XX_PROD_NUM                0x0000
+#define IQS5XX_ABS_X           0x0016
+#define IQS5XX_ABS_Y           0x0018
+#define IQS5XX_SYS_CTRL0       0x0431
+#define IQS5XX_SYS_CTRL1       0x0432
+#define IQS5XX_SYS_CFG0                0x058E
+#define IQS5XX_SYS_CFG1                0x058F
+#define IQS5XX_TOTAL_RX                0x063D
+#define IQS5XX_TOTAL_TX                0x063E
+#define IQS5XX_XY_CFG0         0x0669
+#define IQS5XX_X_RES           0x066E
+#define IQS5XX_Y_RES           0x0670
+#define IQS5XX_CHKSM           0x83C0
+#define IQS5XX_APP             0x8400
+#define IQS5XX_CSTM            0xBE00
+#define IQS5XX_PMAP_END                0xBFFF
+#define IQS5XX_END_COMM                0xEEEE
+
+#define IQS5XX_CHKSM_LEN       (IQS5XX_APP - IQS5XX_CHKSM)
+#define IQS5XX_APP_LEN         (IQS5XX_CSTM - IQS5XX_APP)
+#define IQS5XX_CSTM_LEN                (IQS5XX_PMAP_END + 1 - IQS5XX_CSTM)
+#define IQS5XX_PMAP_LEN                (IQS5XX_PMAP_END + 1 - IQS5XX_CHKSM)
+
+#define IQS5XX_REC_HDR_LEN     4
+#define IQS5XX_REC_LEN_MAX     255
+#define IQS5XX_REC_TYPE_DATA   0x00
+#define IQS5XX_REC_TYPE_EOF    0x01
+
+#define IQS5XX_BL_ADDR_MASK    0x40
+#define IQS5XX_BL_CMD_VER      0x00
+#define IQS5XX_BL_CMD_READ     0x01
+#define IQS5XX_BL_CMD_EXEC     0x02
+#define IQS5XX_BL_CMD_CRC      0x03
+#define IQS5XX_BL_BLK_LEN_MAX  64
+#define IQS5XX_BL_ID           0x0200
+#define IQS5XX_BL_STATUS_RESET 0x00
+#define IQS5XX_BL_STATUS_AVAIL 0xA5
+#define IQS5XX_BL_STATUS_NONE  0xEE
+#define IQS5XX_BL_CRC_PASS     0x00
+#define IQS5XX_BL_CRC_FAIL     0x01
+#define IQS5XX_BL_ATTEMPTS     3
+
+struct iqs5xx_private {
+       struct i2c_client *client;
+       struct input_dev *input;
+       struct gpio_desc *reset_gpio;
+       struct mutex lock;
+       u8 bl_status;
+};
+
+struct iqs5xx_dev_id_info {
+       __be16 prod_num;
+       __be16 proj_num;
+       u8 major_ver;
+       u8 minor_ver;
+       u8 bl_status;
+} __packed;
+
+struct iqs5xx_ihex_rec {
+       char start;
+       char len[2];
+       char addr[4];
+       char type[2];
+       char data[2];
+} __packed;
+
+struct iqs5xx_touch_data {
+       __be16 abs_x;
+       __be16 abs_y;
+       __be16 strength;
+       u8 area;
+} __packed;
+
+static int iqs5xx_read_burst(struct i2c_client *client,
+                            u16 reg, void *val, u16 len)
+{
+       __be16 reg_buf = cpu_to_be16(reg);
+       int ret, i;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = client->addr,
+                       .flags = 0,
+                       .len = sizeof(reg_buf),
+                       .buf = (u8 *)&reg_buf,
+               },
+               {
+                       .addr = client->addr,
+                       .flags = I2C_M_RD,
+                       .len = len,
+                       .buf = (u8 *)val,
+               },
+       };
+
+       /*
+        * The first addressing attempt outside of a communication window fails
+        * and must be retried, after which the device clock stretches until it
+        * is available.
+        */
+       for (i = 0; i < IQS5XX_NUM_RETRIES; i++) {
+               ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+               if (ret == ARRAY_SIZE(msg))
+                       return 0;
+
+               usleep_range(200, 300);
+       }
+
+       if (ret >= 0)
+               ret = -EIO;
+
+       dev_err(&client->dev, "Failed to read from address 0x%04X: %d\n",
+               reg, ret);
+
+       return ret;
+}
+
+static int iqs5xx_read_word(struct i2c_client *client, u16 reg, u16 *val)
+{
+       __be16 val_buf;
+       int error;
+
+       error = iqs5xx_read_burst(client, reg, &val_buf, sizeof(val_buf));
+       if (error)
+               return error;
+
+       *val = be16_to_cpu(val_buf);
+
+       return 0;
+}
+
+static int iqs5xx_read_byte(struct i2c_client *client, u16 reg, u8 *val)
+{
+       return iqs5xx_read_burst(client, reg, val, sizeof(*val));
+}
+
+static int iqs5xx_write_burst(struct i2c_client *client,
+                             u16 reg, const void *val, u16 len)
+{
+       int ret, i;
+       u16 mlen = sizeof(reg) + len;
+       u8 mbuf[sizeof(reg) + IQS5XX_WR_BYTES_MAX];
+
+       if (len > IQS5XX_WR_BYTES_MAX)
+               return -EINVAL;
+
+       put_unaligned_be16(reg, mbuf);
+       memcpy(mbuf + sizeof(reg), val, len);
+
+       /*
+        * The first addressing attempt outside of a communication window fails
+        * and must be retried, after which the device clock stretches until it
+        * is available.
+        */
+       for (i = 0; i < IQS5XX_NUM_RETRIES; i++) {
+               ret = i2c_master_send(client, mbuf, mlen);
+               if (ret == mlen)
+                       return 0;
+
+               usleep_range(200, 300);
+       }
+
+       if (ret >= 0)
+               ret = -EIO;
+
+       dev_err(&client->dev, "Failed to write to address 0x%04X: %d\n",
+               reg, ret);
+
+       return ret;
+}
+
+static int iqs5xx_write_word(struct i2c_client *client, u16 reg, u16 val)
+{
+       __be16 val_buf = cpu_to_be16(val);
+
+       return iqs5xx_write_burst(client, reg, &val_buf, sizeof(val_buf));
+}
+
+static int iqs5xx_write_byte(struct i2c_client *client, u16 reg, u8 val)
+{
+       return iqs5xx_write_burst(client, reg, &val, sizeof(val));
+}
+
+static void iqs5xx_reset(struct i2c_client *client)
+{
+       struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
+
+       gpiod_set_value_cansleep(iqs5xx->reset_gpio, 1);
+       usleep_range(200, 300);
+
+       gpiod_set_value_cansleep(iqs5xx->reset_gpio, 0);
+}
+
+static int iqs5xx_bl_cmd(struct i2c_client *client, u8 bl_cmd, u16 bl_addr)
+{
+       struct i2c_msg msg;
+       int ret;
+       u8 mbuf[sizeof(bl_cmd) + sizeof(bl_addr)];
+
+       msg.addr = client->addr ^ IQS5XX_BL_ADDR_MASK;
+       msg.flags = 0;
+       msg.len = sizeof(bl_cmd);
+       msg.buf = mbuf;
+
+       *mbuf = bl_cmd;
+
+       switch (bl_cmd) {
+       case IQS5XX_BL_CMD_VER:
+       case IQS5XX_BL_CMD_CRC:
+       case IQS5XX_BL_CMD_EXEC:
+               break;
+       case IQS5XX_BL_CMD_READ:
+               msg.len += sizeof(bl_addr);
+               put_unaligned_be16(bl_addr, mbuf + sizeof(bl_cmd));
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = i2c_transfer(client->adapter, &msg, 1);
+       if (ret != 1)
+               goto msg_fail;
+
+       switch (bl_cmd) {
+       case IQS5XX_BL_CMD_VER:
+               msg.len = sizeof(u16);
+               break;
+       case IQS5XX_BL_CMD_CRC:
+               msg.len = sizeof(u8);
+               /*
+                * This delay saves the bus controller the trouble of having to
+                * tolerate a relatively long clock-stretching period while the
+                * CRC is calculated.
+                */
+               msleep(50);
+               break;
+       case IQS5XX_BL_CMD_EXEC:
+               usleep_range(10000, 10100);
+               /* fall through */
+       default:
+               return 0;
+       }
+
+       msg.flags = I2C_M_RD;
+
+       ret = i2c_transfer(client->adapter, &msg, 1);
+       if (ret != 1)
+               goto msg_fail;
+
+       if (bl_cmd == IQS5XX_BL_CMD_VER &&
+           get_unaligned_be16(mbuf) != IQS5XX_BL_ID) {
+               dev_err(&client->dev, "Unrecognized bootloader ID: 0x%04X\n",
+                       get_unaligned_be16(mbuf));
+               return -EINVAL;
+       }
+
+       if (bl_cmd == IQS5XX_BL_CMD_CRC && *mbuf != IQS5XX_BL_CRC_PASS) {
+               dev_err(&client->dev, "Bootloader CRC failed\n");
+               return -EIO;
+       }
+
+       return 0;
+
+msg_fail:
+       if (ret >= 0)
+               ret = -EIO;
+
+       if (bl_cmd != IQS5XX_BL_CMD_VER)
+               dev_err(&client->dev,
+                       "Unsuccessful bootloader command 0x%02X: %d\n",
+                       bl_cmd, ret);
+
+       return ret;
+}
+
+static int iqs5xx_bl_open(struct i2c_client *client)
+{
+       int error, i, j;
+
+       /*
+        * The device opens a bootloader polling window for 2 ms following the
+        * release of reset. If the host cannot establish communication during
+        * this time frame, it must cycle reset again.
+        */
+       for (i = 0; i < IQS5XX_BL_ATTEMPTS; i++) {
+               iqs5xx_reset(client);
+
+               for (j = 0; j < IQS5XX_NUM_RETRIES; j++) {
+                       error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_VER, 0);
+                       if (!error || error == -EINVAL)
+                               return error;
+               }
+       }
+
+       dev_err(&client->dev, "Failed to open bootloader: %d\n", error);
+
+       return error;
+}
+
+static int iqs5xx_bl_write(struct i2c_client *client,
+                          u16 bl_addr, u8 *pmap_data, u16 pmap_len)
+{
+       struct i2c_msg msg;
+       int ret, i;
+       u8 mbuf[sizeof(bl_addr) + IQS5XX_BL_BLK_LEN_MAX];
+
+       if (pmap_len % IQS5XX_BL_BLK_LEN_MAX)
+               return -EINVAL;
+
+       msg.addr = client->addr ^ IQS5XX_BL_ADDR_MASK;
+       msg.flags = 0;
+       msg.len = sizeof(mbuf);
+       msg.buf = mbuf;
+
+       for (i = 0; i < pmap_len; i += IQS5XX_BL_BLK_LEN_MAX) {
+               put_unaligned_be16(bl_addr + i, mbuf);
+               memcpy(mbuf + sizeof(bl_addr), pmap_data + i,
+                      sizeof(mbuf) - sizeof(bl_addr));
+
+               ret = i2c_transfer(client->adapter, &msg, 1);
+               if (ret != 1)
+                       goto msg_fail;
+
+               usleep_range(10000, 10100);
+       }
+
+       return 0;
+
+msg_fail:
+       if (ret >= 0)
+               ret = -EIO;
+
+       dev_err(&client->dev, "Failed to write block at address 0x%04X: %d\n",
+               bl_addr + i, ret);
+
+       return ret;
+}
+
+static int iqs5xx_bl_verify(struct i2c_client *client,
+                           u16 bl_addr, u8 *pmap_data, u16 pmap_len)
+{
+       struct i2c_msg msg;
+       int ret, i;
+       u8 bl_data[IQS5XX_BL_BLK_LEN_MAX];
+
+       if (pmap_len % IQS5XX_BL_BLK_LEN_MAX)
+               return -EINVAL;
+
+       msg.addr = client->addr ^ IQS5XX_BL_ADDR_MASK;
+       msg.flags = I2C_M_RD;
+       msg.len = sizeof(bl_data);
+       msg.buf = bl_data;
+
+       for (i = 0; i < pmap_len; i += IQS5XX_BL_BLK_LEN_MAX) {
+               ret = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_READ, bl_addr + i);
+               if (ret)
+                       return ret;
+
+               ret = i2c_transfer(client->adapter, &msg, 1);
+               if (ret != 1)
+                       goto msg_fail;
+
+               if (memcmp(bl_data, pmap_data + i, sizeof(bl_data))) {
+                       dev_err(&client->dev,
+                               "Failed to verify block at address 0x%04X\n",
+                               bl_addr + i);
+                       return -EIO;
+               }
+       }
+
+       return 0;
+
+msg_fail:
+       if (ret >= 0)
+               ret = -EIO;
+
+       dev_err(&client->dev, "Failed to read block at address 0x%04X: %d\n",
+               bl_addr + i, ret);
+
+       return ret;
+}
+
+static int iqs5xx_set_state(struct i2c_client *client, u8 state)
+{
+       struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
+       int error1, error2;
+
+       if (iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
+               return 0;
+
+       mutex_lock(&iqs5xx->lock);
+
+       /*
+        * Addressing the device outside of a communication window prompts it
+        * to assert the RDY output, so disable the interrupt line to prevent
+        * the handler from servicing a false interrupt.
+        */
+       disable_irq(client->irq);
+
+       error1 = iqs5xx_write_byte(client, IQS5XX_SYS_CTRL1, state);
+       error2 = iqs5xx_write_byte(client, IQS5XX_END_COMM, 0);
+
+       usleep_range(50, 100);
+       enable_irq(client->irq);
+
+       mutex_unlock(&iqs5xx->lock);
+
+       if (error1)
+               return error1;
+
+       return error2;
+}
+
+static int iqs5xx_open(struct input_dev *input)
+{
+       struct iqs5xx_private *iqs5xx = input_get_drvdata(input);
+
+       return iqs5xx_set_state(iqs5xx->client, IQS5XX_RESUME);
+}
+
+static void iqs5xx_close(struct input_dev *input)
+{
+       struct iqs5xx_private *iqs5xx = input_get_drvdata(input);
+
+       iqs5xx_set_state(iqs5xx->client, IQS5XX_SUSPEND);
+}
+
+static int iqs5xx_axis_init(struct i2c_client *client)
+{
+       struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
+       struct touchscreen_properties prop;
+       struct input_dev *input;
+       int error;
+       u16 max_x, max_x_hw;
+       u16 max_y, max_y_hw;
+       u8 val;
+
+       if (!iqs5xx->input) {
+               input = devm_input_allocate_device(&client->dev);
+               if (!input)
+                       return -ENOMEM;
+
+               input->name = client->name;
+               input->id.bustype = BUS_I2C;
+               input->open = iqs5xx_open;
+               input->close = iqs5xx_close;
+
+               input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
+               input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
+               input_set_capability(input, EV_ABS, ABS_MT_PRESSURE);
+
+               error = input_mt_init_slots(input,
+                               IQS5XX_NUM_CONTACTS, INPUT_MT_DIRECT);
+               if (error) {
+                       dev_err(&client->dev,
+                               "Failed to initialize slots: %d\n", error);
+                       return error;
+               }
+
+               input_set_drvdata(input, iqs5xx);
+               iqs5xx->input = input;
+       }
+
+       touchscreen_parse_properties(iqs5xx->input, true, &prop);
+
+       error = iqs5xx_read_byte(client, IQS5XX_TOTAL_RX, &val);
+       if (error)
+               return error;
+       max_x_hw = (val - 1) * IQS5XX_NUM_POINTS;
+
+       error = iqs5xx_read_byte(client, IQS5XX_TOTAL_TX, &val);
+       if (error)
+               return error;
+       max_y_hw = (val - 1) * IQS5XX_NUM_POINTS;
+
+       error = iqs5xx_read_byte(client, IQS5XX_XY_CFG0, &val);
+       if (error)
+               return error;
+
+       if (val & IQS5XX_SWITCH_XY_AXIS)
+               swap(max_x_hw, max_y_hw);
+
+       if (prop.swap_x_y)
+               val ^= IQS5XX_SWITCH_XY_AXIS;
+
+       if (prop.invert_x)
+               val ^= prop.swap_x_y ? IQS5XX_FLIP_Y : IQS5XX_FLIP_X;
+
+       if (prop.invert_y)
+               val ^= prop.swap_x_y ? IQS5XX_FLIP_X : IQS5XX_FLIP_Y;
+
+       error = iqs5xx_write_byte(client, IQS5XX_XY_CFG0, val);
+       if (error)
+               return error;
+
+       if (prop.max_x > max_x_hw) {
+               dev_err(&client->dev, "Invalid maximum x-coordinate: %u > %u\n",
+                       prop.max_x, max_x_hw);
+               return -EINVAL;
+       } else if (prop.max_x == 0) {
+               error = iqs5xx_read_word(client, IQS5XX_X_RES, &max_x);
+               if (error)
+                       return error;
+
+               input_abs_set_max(iqs5xx->input,
+                                 prop.swap_x_y ? ABS_MT_POSITION_Y :
+                                                 ABS_MT_POSITION_X,
+                                 max_x);
+       } else {
+               max_x = (u16)prop.max_x;
+       }
+
+       if (prop.max_y > max_y_hw) {
+               dev_err(&client->dev, "Invalid maximum y-coordinate: %u > %u\n",
+                       prop.max_y, max_y_hw);
+               return -EINVAL;
+       } else if (prop.max_y == 0) {
+               error = iqs5xx_read_word(client, IQS5XX_Y_RES, &max_y);
+               if (error)
+                       return error;
+
+               input_abs_set_max(iqs5xx->input,
+                                 prop.swap_x_y ? ABS_MT_POSITION_X :
+                                                 ABS_MT_POSITION_Y,
+                                 max_y);
+       } else {
+               max_y = (u16)prop.max_y;
+       }
+
+       /*
+        * Write horizontal and vertical resolution to the device in case its
+        * original defaults were overridden or swapped as per the properties
+        * specified in the device tree.
+        */
+       error = iqs5xx_write_word(client,
+                                 prop.swap_x_y ? IQS5XX_Y_RES : IQS5XX_X_RES,
+                                 max_x);
+       if (error)
+               return error;
+
+       return iqs5xx_write_word(client,
+                                prop.swap_x_y ? IQS5XX_X_RES : IQS5XX_Y_RES,
+                                max_y);
+}
+
+static int iqs5xx_dev_init(struct i2c_client *client)
+{
+       struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
+       struct iqs5xx_dev_id_info *dev_id_info;
+       int error;
+       u8 val;
+       u8 buf[sizeof(*dev_id_info) + 1];
+
+       error = iqs5xx_read_burst(client, IQS5XX_PROD_NUM,
+                                 &buf[1], sizeof(*dev_id_info));
+       if (error)
+               return iqs5xx_bl_open(client);
+
+       /*
+        * A000 and B000 devices use 8-bit and 16-bit addressing, respectively.
+        * Querying an A000 device's version information with 16-bit addressing
+        * gives the appearance that the data is shifted by one byte; a nonzero
+        * leading array element suggests this could be the case (in which case
+        * the missing zero is prepended).
+        */
+       buf[0] = 0;
+       dev_id_info = (struct iqs5xx_dev_id_info *)&buf[(buf[1] > 0) ? 0 : 1];
+
+       switch (be16_to_cpu(dev_id_info->prod_num)) {
+       case IQS5XX_PROD_NUM_IQS550:
+       case IQS5XX_PROD_NUM_IQS572:
+       case IQS5XX_PROD_NUM_IQS525:
+               break;
+       default:
+               dev_err(&client->dev, "Unrecognized product number: %u\n",
+                       be16_to_cpu(dev_id_info->prod_num));
+               return -EINVAL;
+       }
+
+       switch (be16_to_cpu(dev_id_info->proj_num)) {
+       case IQS5XX_PROJ_NUM_A000:
+               dev_err(&client->dev, "Unsupported project number: %u\n",
+                       be16_to_cpu(dev_id_info->proj_num));
+               return iqs5xx_bl_open(client);
+       case IQS5XX_PROJ_NUM_B000:
+               break;
+       default:
+               dev_err(&client->dev, "Unrecognized project number: %u\n",
+                       be16_to_cpu(dev_id_info->proj_num));
+               return -EINVAL;
+       }
+
+       if (dev_id_info->major_ver < IQS5XX_MAJOR_VER_MIN) {
+               dev_err(&client->dev, "Unsupported major version: %u\n",
+                       dev_id_info->major_ver);
+               return iqs5xx_bl_open(client);
+       }
+
+       switch (dev_id_info->bl_status) {
+       case IQS5XX_BL_STATUS_AVAIL:
+       case IQS5XX_BL_STATUS_NONE:
+               break;
+       default:
+               dev_err(&client->dev,
+                       "Unrecognized bootloader status: 0x%02X\n",
+                       dev_id_info->bl_status);
+               return -EINVAL;
+       }
+
+       error = iqs5xx_axis_init(client);
+       if (error)
+               return error;
+
+       error = iqs5xx_read_byte(client, IQS5XX_SYS_CFG0, &val);
+       if (error)
+               return error;
+
+       val |= IQS5XX_SETUP_COMPLETE;
+       val &= ~IQS5XX_SW_INPUT_EVENT;
+       error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG0, val);
+       if (error)
+               return error;
+
+       val = IQS5XX_TP_EVENT | IQS5XX_EVENT_MODE;
+       error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG1, val);
+       if (error)
+               return error;
+
+       error = iqs5xx_write_byte(client, IQS5XX_END_COMM, 0);
+       if (error)
+               return error;
+
+       iqs5xx->bl_status = dev_id_info->bl_status;
+
+       /*
+        * Closure of the first communication window that appears following the
+        * release of reset appears to kick off an initialization period during
+        * which further communication is met with clock stretching. The return
+        * from this function is delayed so that further communication attempts
+        * avoid this period.
+        */
+       msleep(100);
+
+       return 0;
+}
+
+static irqreturn_t iqs5xx_irq(int irq, void *data)
+{
+       struct iqs5xx_private *iqs5xx = data;
+       struct iqs5xx_touch_data touch_data[IQS5XX_NUM_CONTACTS];
+       struct i2c_client *client = iqs5xx->client;
+       struct input_dev *input = iqs5xx->input;
+       int error, i;
+
+       /*
+        * This check is purely a precaution, as the device does not assert the
+        * RDY output during bootloader mode. If the device operates outside of
+        * bootloader mode, the input device is guaranteed to be allocated.
+        */
+       if (iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
+               return IRQ_NONE;
+
+       error = iqs5xx_read_burst(client, IQS5XX_ABS_X,
+                                 touch_data, sizeof(touch_data));
+       if (error)
+               return IRQ_NONE;
+
+       for (i = 0; i < ARRAY_SIZE(touch_data); i++) {
+               u16 pressure = be16_to_cpu(touch_data[i].strength);
+
+               input_mt_slot(input, i);
+               if (input_mt_report_slot_state(input, MT_TOOL_FINGER,
+                                              pressure != 0)) {
+                       input_report_abs(input, ABS_MT_POSITION_X,
+                                        be16_to_cpu(touch_data[i].abs_x));
+                       input_report_abs(input, ABS_MT_POSITION_Y,
+                                        be16_to_cpu(touch_data[i].abs_y));
+                       input_report_abs(input, ABS_MT_PRESSURE, pressure);
+               }
+       }
+
+       input_mt_sync_frame(input);
+       input_sync(input);
+
+       error = iqs5xx_write_byte(client, IQS5XX_END_COMM, 0);
+       if (error)
+               return IRQ_NONE;
+
+       /*
+        * Once the communication window is closed, a small delay is added to
+        * ensure the device's RDY output has been deasserted by the time the
+        * interrupt handler returns.
+        */
+       usleep_range(50, 100);
+
+       return IRQ_HANDLED;
+}
+
+static int iqs5xx_fw_file_parse(struct i2c_client *client,
+                               const char *fw_file, u8 *pmap)
+{
+       const struct firmware *fw;
+       struct iqs5xx_ihex_rec *rec;
+       size_t pos = 0;
+       int error, i;
+       u16 rec_num = 1;
+       u16 rec_addr;
+       u8 rec_len, rec_type, rec_chksm, chksm;
+       u8 rec_hdr[IQS5XX_REC_HDR_LEN];
+       u8 rec_data[IQS5XX_REC_LEN_MAX];
+
+       /*
+        * Firmware exported from the vendor's configuration tool deviates from
+        * standard ihex as follows: (1) the checksum for records corresponding
+        * to user-exported settings is not recalculated, and (2) an address of
+        * 0xFFFF is used for the EOF record.
+        *
+        * Because the ihex2fw tool tolerates neither (1) nor (2), the slightly
+        * nonstandard ihex firmware is parsed directly by the driver.
+        */
+       error = request_firmware(&fw, fw_file, &client->dev);
+       if (error) {
+               dev_err(&client->dev, "Failed to request firmware %s: %d\n",
+                       fw_file, error);
+               return error;
+       }
+
+       do {
+               if (pos + sizeof(*rec) > fw->size) {
+                       dev_err(&client->dev, "Insufficient firmware size\n");
+                       error = -EINVAL;
+                       break;
+               }
+               rec = (struct iqs5xx_ihex_rec *)(fw->data + pos);
+               pos += sizeof(*rec);
+
+               if (rec->start != ':') {
+                       dev_err(&client->dev, "Invalid start at record %u\n",
+                               rec_num);
+                       error = -EINVAL;
+                       break;
+               }
+
+               error = hex2bin(rec_hdr, rec->len, sizeof(rec_hdr));
+               if (error) {
+                       dev_err(&client->dev, "Invalid header at record %u\n",
+                               rec_num);
+                       break;
+               }
+
+               rec_len = *rec_hdr;
+               rec_addr = get_unaligned_be16(rec_hdr + sizeof(rec_len));
+               rec_type = *(rec_hdr + sizeof(rec_len) + sizeof(rec_addr));
+
+               if (pos + rec_len * 2 > fw->size) {
+                       dev_err(&client->dev, "Insufficient firmware size\n");
+                       error = -EINVAL;
+                       break;
+               }
+               pos += (rec_len * 2);
+
+               error = hex2bin(rec_data, rec->data, rec_len);
+               if (error) {
+                       dev_err(&client->dev, "Invalid data at record %u\n",
+                               rec_num);
+                       break;
+               }
+
+               error = hex2bin(&rec_chksm,
+                               rec->data + rec_len * 2, sizeof(rec_chksm));
+               if (error) {
+                       dev_err(&client->dev, "Invalid checksum at record %u\n",
+                               rec_num);
+                       break;
+               }
+
+               chksm = 0;
+               for (i = 0; i < sizeof(rec_hdr); i++)
+                       chksm += rec_hdr[i];
+               for (i = 0; i < rec_len; i++)
+                       chksm += rec_data[i];
+               chksm = ~chksm + 1;
+
+               if (chksm != rec_chksm && rec_addr < IQS5XX_CSTM) {
+                       dev_err(&client->dev,
+                               "Incorrect checksum at record %u\n",
+                               rec_num);
+                       error = -EINVAL;
+                       break;
+               }
+
+               switch (rec_type) {
+               case IQS5XX_REC_TYPE_DATA:
+                       if (rec_addr < IQS5XX_CHKSM ||
+                           rec_addr > IQS5XX_PMAP_END) {
+                               dev_err(&client->dev,
+                                       "Invalid address at record %u\n",
+                                       rec_num);
+                               error = -EINVAL;
+                       } else {
+                               memcpy(pmap + rec_addr - IQS5XX_CHKSM,
+                                      rec_data, rec_len);
+                       }
+                       break;
+               case IQS5XX_REC_TYPE_EOF:
+                       break;
+               default:
+                       dev_err(&client->dev, "Invalid type at record %u\n",
+                               rec_num);
+                       error = -EINVAL;
+               }
+
+               if (error)
+                       break;
+
+               rec_num++;
+               while (pos < fw->size) {
+                       if (*(fw->data + pos) == ':')
+                               break;
+                       pos++;
+               }
+       } while (rec_type != IQS5XX_REC_TYPE_EOF);
+
+       release_firmware(fw);
+
+       return error;
+}
+
+static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
+{
+       struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
+       int error;
+       u8 *pmap;
+
+       if (iqs5xx->bl_status == IQS5XX_BL_STATUS_NONE)
+               return -EPERM;
+
+       pmap = kzalloc(IQS5XX_PMAP_LEN, GFP_KERNEL);
+       if (!pmap)
+               return -ENOMEM;
+
+       error = iqs5xx_fw_file_parse(client, fw_file, pmap);
+       if (error)
+               goto err_kfree;
+
+       mutex_lock(&iqs5xx->lock);
+
+       /*
+        * Disable the interrupt line in case the first attempt(s) to enter the
+        * bootloader don't happen quickly enough, in which case the device may
+        * assert the RDY output until the next attempt.
+        */
+       disable_irq(client->irq);
+
+       iqs5xx->bl_status = IQS5XX_BL_STATUS_RESET;
+
+       error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_VER, 0);
+       if (error) {
+               error = iqs5xx_bl_open(client);
+               if (error)
+                       goto err_reset;
+       }
+
+       error = iqs5xx_bl_write(client, IQS5XX_CHKSM, pmap, IQS5XX_PMAP_LEN);
+       if (error)
+               goto err_reset;
+
+       error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_CRC, 0);
+       if (error)
+               goto err_reset;
+
+       error = iqs5xx_bl_verify(client, IQS5XX_CSTM,
+                                pmap + IQS5XX_CHKSM_LEN + IQS5XX_APP_LEN,
+                                IQS5XX_CSTM_LEN);
+       if (error)
+               goto err_reset;
+
+       error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_EXEC, 0);
+
+err_reset:
+       if (error) {
+               iqs5xx_reset(client);
+               usleep_range(10000, 10100);
+       }
+
+       error = iqs5xx_dev_init(client);
+       if (!error && iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
+               error = -EINVAL;
+
+       enable_irq(client->irq);
+
+       mutex_unlock(&iqs5xx->lock);
+
+err_kfree:
+       kfree(pmap);
+
+       return error;
+}
+
+static ssize_t fw_file_store(struct device *dev, struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
+       struct i2c_client *client = iqs5xx->client;
+       size_t len = count;
+       bool input_reg = !iqs5xx->input;
+       char fw_file[IQS5XX_FW_FILE_LEN + 1];
+       int error;
+
+       if (!len)
+               return -EINVAL;
+
+       if (buf[len - 1] == '\n')
+               len--;
+
+       if (len > IQS5XX_FW_FILE_LEN)
+               return -ENAMETOOLONG;
+
+       memcpy(fw_file, buf, len);
+       fw_file[len] = '\0';
+
+       error = iqs5xx_fw_file_write(client, fw_file);
+       if (error)
+               return error;
+
+       /*
+        * If the input device was not allocated already, it is guaranteed to
+        * be allocated by this point and can finally be registered.
+        */
+       if (input_reg) {
+               error = input_register_device(iqs5xx->input);
+               if (error) {
+                       dev_err(&client->dev,
+                               "Failed to register device: %d\n",
+                               error);
+                       return error;
+               }
+       }
+
+       return count;
+}
+
+static DEVICE_ATTR_WO(fw_file);
+
+static struct attribute *iqs5xx_attrs[] = {
+       &dev_attr_fw_file.attr,
+       NULL,
+};
+
+static const struct attribute_group iqs5xx_attr_group = {
+       .attrs = iqs5xx_attrs,
+};
+
+static int __maybe_unused iqs5xx_suspend(struct device *dev)
+{
+       struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
+       struct input_dev *input = iqs5xx->input;
+       int error = 0;
+
+       if (!input)
+               return error;
+
+       mutex_lock(&input->mutex);
+
+       if (input->users)
+               error = iqs5xx_set_state(iqs5xx->client, IQS5XX_SUSPEND);
+
+       mutex_unlock(&input->mutex);
+
+       return error;
+}
+
+static int __maybe_unused iqs5xx_resume(struct device *dev)
+{
+       struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
+       struct input_dev *input = iqs5xx->input;
+       int error = 0;
+
+       if (!input)
+               return error;
+
+       mutex_lock(&input->mutex);
+
+       if (input->users)
+               error = iqs5xx_set_state(iqs5xx->client, IQS5XX_RESUME);
+
+       mutex_unlock(&input->mutex);
+
+       return error;
+}
+
+static SIMPLE_DEV_PM_OPS(iqs5xx_pm, iqs5xx_suspend, iqs5xx_resume);
+
+static int iqs5xx_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct iqs5xx_private *iqs5xx;
+       int error;
+
+       iqs5xx = devm_kzalloc(&client->dev, sizeof(*iqs5xx), GFP_KERNEL);
+       if (!iqs5xx)
+               return -ENOMEM;
+
+       dev_set_drvdata(&client->dev, iqs5xx);
+
+       i2c_set_clientdata(client, iqs5xx);
+       iqs5xx->client = client;
+
+       iqs5xx->reset_gpio = devm_gpiod_get(&client->dev,
+                                           "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(iqs5xx->reset_gpio)) {
+               error = PTR_ERR(iqs5xx->reset_gpio);
+               dev_err(&client->dev, "Failed to request GPIO: %d\n", error);
+               return error;
+       }
+
+       mutex_init(&iqs5xx->lock);
+
+       iqs5xx_reset(client);
+       usleep_range(10000, 10100);
+
+       error = iqs5xx_dev_init(client);
+       if (error)
+               return error;
+
+       error = devm_request_threaded_irq(&client->dev, client->irq,
+                                         NULL, iqs5xx_irq, IRQF_ONESHOT,
+                                         client->name, iqs5xx);
+       if (error) {
+               dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
+               return error;
+       }
+
+       error = devm_device_add_group(&client->dev, &iqs5xx_attr_group);
+       if (error) {
+               dev_err(&client->dev, "Failed to add attributes: %d\n", error);
+               return error;
+       }
+
+       if (iqs5xx->input) {
+               error = input_register_device(iqs5xx->input);
+               if (error)
+                       dev_err(&client->dev,
+                               "Failed to register device: %d\n",
+                               error);
+       }
+
+       return error;
+}
+
+static const struct i2c_device_id iqs5xx_id[] = {
+       { "iqs550", 0 },
+       { "iqs572", 1 },
+       { "iqs525", 2 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, iqs5xx_id);
+
+static const struct of_device_id iqs5xx_of_match[] = {
+       { .compatible = "azoteq,iqs550" },
+       { .compatible = "azoteq,iqs572" },
+       { .compatible = "azoteq,iqs525" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, iqs5xx_of_match);
+
+static struct i2c_driver iqs5xx_i2c_driver = {
+       .driver = {
+               .name           = "iqs5xx",
+               .of_match_table = iqs5xx_of_match,
+               .pm             = &iqs5xx_pm,
+       },
+       .id_table       = iqs5xx_id,
+       .probe          = iqs5xx_probe,
+};
+module_i2c_driver(iqs5xx_i2c_driver);
+
+MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
+MODULE_DESCRIPTION("Azoteq IQS550/572/525 Trackpad/Touchscreen Controller");
+MODULE_LICENSE("GPL");
index 64cee116928ebd92d97acec08c273e02c89958ab..85387c76c24f384ac31a934ca14f0e76faf1ba3a 100644 (file)
 #define KEY_SCREENSAVER                0x245   /* AL Screen Saver */
 #define KEY_VOICECOMMAND               0x246   /* Listening Voice Command */
 #define KEY_ASSISTANT          0x247   /* AL Context-aware desktop assistant */
+#define KEY_KBD_LAYOUT_NEXT    0x248   /* AC Next Keyboard Layout Select */
 
 #define KEY_BRIGHTNESS_MIN             0x250   /* Set Brightness to Minimum */
 #define KEY_BRIGHTNESS_MAX             0x251   /* Set Brightness to Maximum */