All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] dt-bindings: input: Add bindings for Azoteq IQS269A
@ 2020-04-19 23:47 Jeff LaBundy
  2020-04-19 23:47 ` [PATCH 2/2] input: misc: Add support " Jeff LaBundy
  2020-04-30 15:11 ` [PATCH 1/2] dt-bindings: input: Add bindings " Rob Herring
  0 siblings, 2 replies; 7+ messages in thread
From: Jeff LaBundy @ 2020-04-19 23:47 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, Jeff LaBundy

This patch adds device tree bindings for the Azoteq IQS269A
capacitive touch controller.

Signed-off-by: Jeff LaBundy <jeff@labundy.com>
---
 .../devicetree/bindings/input/iqs269a.yaml         | 591 +++++++++++++++++++++
 1 file changed, 591 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/iqs269a.yaml

diff --git a/Documentation/devicetree/bindings/input/iqs269a.yaml b/Documentation/devicetree/bindings/input/iqs269a.yaml
new file mode 100644
index 0000000..7f4fcdc
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/iqs269a.yaml
@@ -0,0 +1,591 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/iqs269a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Azoteq IQS269A Capacitive Touch Controller
+
+maintainers:
+  - Jeff LaBundy <jeff@labundy.com>
+
+description: |
+  The Azoteq IQS269A is an 8-channel capacitive touch controller that features
+  additional Hall-effect and inductive sensing capabilities.
+
+  Link to datasheet: https://www.azoteq.com/
+
+properties:
+  compatible:
+    const: azoteq,iqs269a
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  azoteq,hall-enable:
+    type: boolean
+    description:
+      Enables Hall-effect sensing on channels 6 and 7. In this case, keycodes
+      assigned to channel 6 are ignored and keycodes assigned to channel 7 are
+      interpreted as switch codes. Refer to the datasheet for requirements im-
+      posed on channels 6 and 7 by Hall-effect sensing.
+
+  azoteq,suspend-mode:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - enum: [0, 1, 2, 3]
+        default: 0
+    description: |
+      Specifies the power mode during suspend as follows:
+      0: Automatic (same as normal runtime, i.e. suspend/resume disabled)
+      1: Low power (all sensing at a reduced reporting rate)
+      2: Ultra-low power (channel 0 proximity sensing)
+      3: Halt (no sensing)
+
+  azoteq,clk-div:
+    type: boolean
+    description: Divides the device's core clock by a factor of 4.
+
+  azoteq,ulp-update:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 0
+        maximum: 7
+        default: 3
+    description: Specifies the ultra-low-power mode update rate.
+
+  azoteq,reseed-offset:
+    type: boolean
+    description:
+      Applies an 8-count offset to all long-term averages upon either ATI or
+      reseed events.
+
+  azoteq,filt-str-lp-lta:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - enum: [0, 1, 2, 3]
+        default: 0
+    description:
+      Specifies the long-term average filter strength during low-power mode.
+
+  azoteq,filt-str-lp-cnt:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - enum: [0, 1, 2, 3]
+        default: 0
+    description:
+      Specifies the raw count filter strength during low-power mode.
+
+  azoteq,filt-str-np-lta:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - enum: [0, 1, 2, 3]
+        default: 0
+    description:
+      Specifies the long-term average filter strength during normal-power mode.
+
+  azoteq,filt-str-np-cnt:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - enum: [0, 1, 2, 3]
+        default: 0
+    description:
+      Specifies the raw count filter strength during normal-power mode.
+
+  azoteq,rate-np-ms:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 0
+        maximum: 255
+        default: 16
+    description: Specifies the report rate (in ms) during normal-power mode.
+
+  azoteq,rate-lp-ms:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 0
+        maximum: 255
+        default: 160
+    description: Specifies the report rate (in ms) during low-power mode.
+
+  azoteq,rate-ulp-ms:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - multipleOf: 16
+        minimum: 0
+        maximum: 4080
+        default: 160
+    description: Specifies the report rate (in ms) during ultra-low-power mode.
+
+  azoteq,timeout-pwr-ms:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - multipleOf: 512
+        minimum: 0
+        maximum: 130560
+        default: 2560
+    description:
+      Specifies the length of time (in ms) to wait for an event during normal-
+      power mode before transitioning to low-power mode.
+
+  azoteq,timeout-lta-ms:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - multipleOf: 512
+        minimum: 0
+        maximum: 130560
+        default: 32768
+    description:
+      Specifies the length of time (in ms) to wait before resetting the long-
+      term average of all channels. Specify the maximum timeout to disable it
+      altogether.
+
+  azoteq,ati-band-disable:
+    type: boolean
+    description: Disables the ATI band check.
+
+  azoteq,ati-lp-only:
+    type: boolean
+    description: Limits automatic ATI to low-power mode.
+
+  azoteq,ati-band-tighten:
+    type: boolean
+    description: Tightens the ATI band from 1/8 to 1/16 of the desired target.
+
+  azoteq,filt-disable:
+    type: boolean
+    description: Disables all raw count filtering.
+
+  azoteq,gpio3-select:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 0
+        maximum: 7
+        default: 0
+    description:
+      Selects the channel for which the GPIO3 pin represents touch state.
+
+  azoteq,dual-direction:
+    type: boolean
+    description:
+      Specifies that long-term averages are to freeze in the presence of either
+      increasing or decreasing counts, thereby permitting events to be reported
+      in either direction.
+
+  azoteq,tx-freq:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - enum: [0, 1, 2, 3]
+        default: 0
+    description: |
+      Specifies the inductive sensing excitation frequency as follows (paren-
+      thesized numbers represent the frequency if 'azoteq,clk-div' is present):
+      0: 16 MHz (4 MHz)
+      1: 8 MHz (2 MHz)
+      2: 4 MHz (1 MHz)
+      3: 2 MHz (500 kHz)
+
+  azoteq,global-cap-increase:
+    type: boolean
+    description: Increases the global capacitance adder from 0.5 pF to 1.5 pF.
+
+  azoteq,reseed-select:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - enum: [0, 1, 2, 3]
+        default: 0
+    description: |
+      Specifies the event(s) that prompt the device to reseed (i.e. reset the
+      long-term average) of an associated channel as follows:
+      0: None
+      1: Proximity
+      2: Proximity or touch
+      3: Proximity, touch or deep touch
+
+  azoteq,tracking-enable:
+    type: boolean
+    description:
+      Enables all associated channels to track their respective reference
+      channels.
+
+  azoteq,filt-str-slider:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - enum: [0, 1, 2, 3]
+        default: 1
+    description: Specifies the slider coordinate filter strength.
+
+patternProperties:
+  "^channel@[0-7]$":
+    type: object
+    description:
+      Represents a single sensing channel. A channel is active if defined and
+      inactive otherwise.
+
+    properties:
+      reg:
+        minimum: 0
+        maximum: 7
+        description: Index of the channel.
+
+      azoteq,reseed-disable:
+        type: boolean
+        description:
+          Prevents the channel from being reseeded if the long-term average
+          timeout (defined in 'azoteq,timeout-lta') expires.
+
+      azoteq,blocking-enable:
+        type: boolean
+        description: Specifies that the channel is a blocking channel.
+
+      azoteq,slider0-select:
+        type: boolean
+        description: Specifies that the channel participates in slider 0.
+
+      azoteq,slider1-select:
+        type: boolean
+        description: Specifies that the channel participates in slider 1.
+
+      azoteq,rx-enable:
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32-array
+          - minItems: 1
+            maxItems: 8
+            items:
+              minimum: 0
+              maximum: 7
+        description:
+          Specifies the CRX pin(s) associated with the channel. By default, only
+          the CRX pin corresponding to the channel's index is enabled (e.g. CRX0
+          for channel 0).
+
+      azoteq,tx-enable:
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32-array
+          - minItems: 1
+            maxItems: 8
+            items:
+              minimum: 0
+              maximum: 7
+            default: [0, 1, 2, 3, 4, 5, 6, 7]
+        description: Specifies the TX pin(s) associated with the channel.
+
+      azoteq,meas-cap-decrease:
+        type: boolean
+        description:
+          Decreases the internal measurement capacitance from 60 pF to 15 pF.
+
+      azoteq,rx-float-inactive:
+        type: boolean
+        description: Floats any inactive CRX pins instead of grounding them.
+
+      azoteq,local-cap-size:
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32
+          - enum: [0, 1, 2]
+            default: 0
+        description: |
+          Specifies the capacitance to be added to the channel as follows:
+          0: None
+          1: Global adder (based on 'azoteq,global-cap-increase')
+          2: Global adder + 0.5 pF
+
+      azoteq,invert-enable:
+        type: boolean
+        description:
+          Inverts the polarity of the states reported for proximity, touch and
+          deep-touch events relative to their respective thresholds.
+
+      azoteq,proj-bias:
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32
+          - enum: [0, 1, 2, 3]
+            default: 2
+        description: |
+          Specifies the bias current applied during projected-capacitance
+          sensing as follows:
+          0: 2.5 uA
+          1: 5 uA
+          2: 10 uA
+          3: 20 uA
+
+      azoteq,sense-mode:
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32
+          - enum: [0, 1, 9, 14, 15]
+            default: 0
+        description: |
+          Specifies the channel's sensing mode as follows:
+          0:  Self capacitance
+          1:  Projected capacitance
+          9:  Self or mutual inductance
+          14: Hall effect
+          15: Temperature
+
+      azoteq,sense-freq:
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32
+          - enum: [0, 1, 2, 3]
+            default: 1
+        description: |
+          Specifies the channel's sensing frequency as follows (parenthesized
+          numbers represent the frequency if 'azoteq,clk-div' is present):
+          0: 4 MHz (1 MHz)
+          1: 2 MHz (500 kHz)
+          2: 1 MHz (250 kHz)
+          3: 500 kHz (125 kHz)
+
+      azoteq,static-enable:
+        type: boolean
+        description: Enables the static front-end for the channel.
+
+      azoteq,ati-mode:
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32
+          - enum: [0, 1, 2, 3]
+            default: 3
+        description: |
+          Specifies the channel's ATI mode as follows:
+          0: Disabled
+          1: Semi-partial
+          2: Partial
+          3: Full
+
+      azoteq,ati-base:
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32
+          - enum: [75, 100, 150, 200]
+            default: 100
+        description: Specifies the channel's ATI base.
+
+      azoteq,ati-target:
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32
+          - multipleOf: 32
+            minimum: 0
+            maximum: 2016
+            default: 512
+        description: Specifies the channel's ATI target.
+
+      azoteq,assoc-select:
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32-array
+          - minItems: 1
+            maxItems: 8
+            items:
+              minimum: 0
+              maximum: 7
+        description:
+          Specifies the associated channels for which the channel serves as a
+          reference channel. By default, no channels are selected.
+
+      azoteq,assoc-weight:
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32
+          - minimum: 0
+            maximum: 255
+            default: 0
+        description:
+          Specifies the channel's impact weight if it acts as an associated
+          channel (0 = 0% impact, 255 = 200% impact).
+
+    patternProperties:
+      "^event-prox(-alt)?$":
+        type: object
+        description:
+          Represents a proximity event reported by the channel in response to
+          a decrease in counts. Node names suffixed with '-alt' instead corre-
+          spond to an increase in counts.
+
+          By default, the long-term average tracks an increase in counts such
+          that only events corresponding to a decrease in counts are reported
+          (refer to the datasheet for more information).
+
+          Specify 'azoteq,dual-direction' to freeze the long-term average when
+          the counts increase or decrease such that events of either direction
+          can be reported. Alternatively, specify 'azoteq,invert-enable' to in-
+          vert the polarity of the states reported by the channel.
+
+          Complementary events (e.g. event-touch and event-touch-alt) can both
+          be present and specify different key or switch codes, but not differ-
+          ent thresholds or hysteresis (if applicable).
+
+        properties:
+          azoteq,thresh:
+            allOf:
+              - $ref: /schemas/types.yaml#/definitions/uint32
+              - minimum: 0
+                maximum: 255
+                default: 10
+            description: Specifies the threshold for the event.
+
+          linux,code:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description: Numeric key or switch code associated with the event.
+
+        additionalProperties: false
+
+      "^event-touch(-alt)?$":
+        type: object
+        description: Represents a touch event reported by the channel.
+
+        properties:
+          azoteq,thresh:
+            allOf:
+              - $ref: /schemas/types.yaml#/definitions/uint32
+              - minimum: 0
+                maximum: 255
+                default: 8
+            description: Specifies the threshold for the event.
+
+          azoteq,hyst:
+            allOf:
+              - $ref: /schemas/types.yaml#/definitions/uint32
+              - minimum: 0
+                maximum: 15
+                default: 4
+            description: Specifies the hysteresis for the event.
+
+          linux,code:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description: Numeric key or switch code associated with the event.
+
+        additionalProperties: false
+
+      "^event-deep(-alt)?$":
+        type: object
+        description: Represents a deep-touch event reported by the channel.
+
+        properties:
+          azoteq,thresh:
+            allOf:
+              - $ref: /schemas/types.yaml#/definitions/uint32
+              - minimum: 0
+                maximum: 255
+                default: 26
+            description: Specifies the threshold for the event.
+
+          azoteq,hyst:
+            allOf:
+              - $ref: /schemas/types.yaml#/definitions/uint32
+              - minimum: 0
+                maximum: 15
+                default: 0
+            description: Specifies the hysteresis for the event.
+
+          linux,code:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description: Numeric key or switch code associated with the event.
+
+        additionalProperties: false
+
+    required:
+      - reg
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - "#address-cells"
+  - "#size-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/input/input.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            iqs269a@44 {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+
+                    compatible = "azoteq,iqs269a";
+                    reg = <0x44>;
+                    interrupt-parent = <&gpio>;
+                    interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
+
+                    azoteq,hall-enable;
+                    azoteq,suspend-mode = <2>;
+
+                    channel@0 {
+                            reg = <0x0>;
+
+                            event-prox {
+                                    linux,code = <KEY_POWER>;
+                            };
+                    };
+
+                    channel@1 {
+                            reg = <0x1>;
+                            azoteq,slider0-select;
+                    };
+
+                    channel@2 {
+                            reg = <0x2>;
+                            azoteq,slider0-select;
+                    };
+
+                    channel@3 {
+                            reg = <0x3>;
+                            azoteq,slider0-select;
+                    };
+
+                    channel@4 {
+                            reg = <0x4>;
+                            azoteq,slider0-select;
+                    };
+
+                    channel@5 {
+                            reg = <0x5>;
+                            azoteq,slider0-select;
+                    };
+
+                    channel@6 {
+                            reg = <0x6>;
+                            azoteq,invert-enable;
+                            azoteq,static-enable;
+                            azoteq,reseed-disable;
+                            azoteq,rx-enable = <0>;
+                            azoteq,sense-freq = <0x0>;
+                            azoteq,sense-mode = <0xE>;
+                            azoteq,ati-mode = <0x0>;
+                            azoteq,ati-base = <200>;
+                            azoteq,ati-target = <320>;
+                    };
+
+                    channel@7 {
+                            reg = <0x7>;
+                            azoteq,invert-enable;
+                            azoteq,static-enable;
+                            azoteq,reseed-disable;
+                            azoteq,rx-enable = <0>, <6>;
+                            azoteq,sense-freq = <0x0>;
+                            azoteq,sense-mode = <0xE>;
+                            azoteq,ati-mode = <0x3>;
+                            azoteq,ati-base = <200>;
+                            azoteq,ati-target = <320>;
+
+                            event-touch {
+                                    linux,code = <SW_LID>;
+                            };
+                    };
+            };
+    };
+
+...
-- 
2.7.4


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

* [PATCH 2/2] input: misc: Add support for Azoteq IQS269A
  2020-04-19 23:47 [PATCH 1/2] dt-bindings: input: Add bindings for Azoteq IQS269A Jeff LaBundy
@ 2020-04-19 23:47 ` Jeff LaBundy
  2020-04-20 17:09   ` Dmitry Torokhov
  2020-04-30 15:11 ` [PATCH 1/2] dt-bindings: input: Add bindings " Rob Herring
  1 sibling, 1 reply; 7+ messages in thread
From: Jeff LaBundy @ 2020-04-19 23:47 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, Jeff LaBundy

This patch adds support for the Azoteq IQS269A capacitive touch
controller.

Signed-off-by: Jeff LaBundy <jeff@labundy.com>
---
 drivers/input/misc/Kconfig   |   10 +
 drivers/input/misc/Makefile  |    1 +
 drivers/input/misc/iqs269a.c | 1833 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1844 insertions(+)
 create mode 100644 drivers/input/misc/iqs269a.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 7e2e658..0d56308 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -739,6 +739,16 @@ config INPUT_IMS_PCU
 	  To compile this driver as a module, choose M here: the module will be
 	  called ims_pcu.
 
+config INPUT_IQS269A
+	tristate "Azoteq IQS269A capacitive touch controller"
+	select REGMAP_I2C
+	help
+	  Say Y to enable support for the Azoteq IQS269A capacitive
+	  touch controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called iqs269a.
+
 config INPUT_CMA3000
 	tristate "VTI CMA3000 Tri-axis accelerometer"
 	help
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 8fd187f..2e679a9 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -40,6 +40,7 @@ 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
+obj-$(CONFIG_INPUT_IQS269A)		+= iqs269a.o
 obj-$(CONFIG_INPUT_IXP4XX_BEEPER)	+= ixp4xx-beeper.o
 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)	+= keyspan_remote.o
 obj-$(CONFIG_INPUT_KXTJ9)		+= kxtj9.o
diff --git a/drivers/input/misc/iqs269a.c b/drivers/input/misc/iqs269a.c
new file mode 100644
index 0000000..6699eb1
--- /dev/null
+++ b/drivers/input/misc/iqs269a.c
@@ -0,0 +1,1833 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Azoteq IQS269A Capacitive Touch Controller
+ *
+ * Copyright (C) 2020 Jeff LaBundy <jeff@labundy.com>
+ *
+ * This driver registers up to 3 input devices: one representing capacitive or
+ * inductive keys as well as Hall-effect switches, and one for each of the two
+ * axial sliders presented by the device.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define IQS269_VER_INFO				0x00
+#define IQS269_VER_INFO_PROD_NUM		0x4F
+
+#define IQS269_SYS_FLAGS			0x02
+#define IQS269_SYS_FLAGS_SHOW_RESET		BIT(15)
+#define IQS269_SYS_FLAGS_PWR_MODE_MASK		GENMASK(12, 11)
+#define IQS269_SYS_FLAGS_PWR_MODE_SHIFT		11
+#define IQS269_SYS_FLAGS_IN_ATI			BIT(10)
+
+#define IQS269_CHx_COUNTS			0x08
+
+#define IQS269_SLIDER_X				0x30
+
+#define IQS269_CAL_DATA_A			0x35
+#define IQS269_CAL_DATA_A_HALL_BIN_L_MASK	GENMASK(15, 12)
+#define IQS269_CAL_DATA_A_HALL_BIN_L_SHIFT	12
+#define IQS269_CAL_DATA_A_HALL_BIN_R_MASK	GENMASK(11, 8)
+#define IQS269_CAL_DATA_A_HALL_BIN_R_SHIFT	8
+
+#define IQS269_SYS_SETTINGS			0x80
+#define IQS269_SYS_SETTINGS_CLK_DIV		BIT(15)
+#define IQS269_SYS_SETTINGS_ULP_AUTO		BIT(14)
+#define IQS269_SYS_SETTINGS_DIS_AUTO		BIT(13)
+#define IQS269_SYS_SETTINGS_PWR_MODE_MASK	GENMASK(12, 11)
+#define IQS269_SYS_SETTINGS_PWR_MODE_SHIFT	11
+#define IQS269_SYS_SETTINGS_PWR_MODE_MAX	3
+#define IQS269_SYS_SETTINGS_ULP_UPDATE_MASK	GENMASK(10, 8)
+#define IQS269_SYS_SETTINGS_ULP_UPDATE_SHIFT	8
+#define IQS269_SYS_SETTINGS_ULP_UPDATE_MAX	7
+#define IQS269_SYS_SETTINGS_RESEED_OFFSET	BIT(6)
+#define IQS269_SYS_SETTINGS_EVENT_MODE		BIT(5)
+#define IQS269_SYS_SETTINGS_EVENT_MODE_LP	BIT(4)
+#define IQS269_SYS_SETTINGS_REDO_ATI		BIT(2)
+#define IQS269_SYS_SETTINGS_ACK_RESET		BIT(0)
+
+#define IQS269_FILT_STR_LP_LTA_MASK		GENMASK(7, 6)
+#define IQS269_FILT_STR_LP_LTA_SHIFT		6
+#define IQS269_FILT_STR_LP_CNT_MASK		GENMASK(5, 4)
+#define IQS269_FILT_STR_LP_CNT_SHIFT		4
+#define IQS269_FILT_STR_NP_LTA_MASK		GENMASK(3, 2)
+#define IQS269_FILT_STR_NP_LTA_SHIFT		2
+#define IQS269_FILT_STR_NP_CNT_MASK		GENMASK(1, 0)
+#define IQS269_FILT_STR_MAX			3
+
+#define IQS269_EVENT_MASK_SYS			BIT(6)
+#define IQS269_EVENT_MASK_DEEP			BIT(2)
+#define IQS269_EVENT_MASK_TOUCH			BIT(1)
+#define IQS269_EVENT_MASK_PROX			BIT(0)
+
+#define IQS269_RATE_NP_MS_MAX			255
+#define IQS269_RATE_LP_MS_MAX			255
+#define IQS269_RATE_ULP_MS_MAX			4080
+#define IQS269_TIMEOUT_PWR_MS_MAX		130560
+#define IQS269_TIMEOUT_LTA_MS_MAX		130560
+
+#define IQS269_MISC_A_ATI_BAND_DISABLE		BIT(15)
+#define IQS269_MISC_A_ATI_LP_ONLY		BIT(14)
+#define IQS269_MISC_A_ATI_BAND_TIGHTEN		BIT(13)
+#define IQS269_MISC_A_FILT_DISABLE		BIT(12)
+#define IQS269_MISC_A_GPIO3_SELECT_MASK		GENMASK(10, 8)
+#define IQS269_MISC_A_GPIO3_SELECT_SHIFT	8
+#define IQS269_MISC_A_DUAL_DIR			BIT(6)
+#define IQS269_MISC_A_TX_FREQ_MASK		GENMASK(5, 4)
+#define IQS269_MISC_A_TX_FREQ_SHIFT		4
+#define IQS269_MISC_A_TX_FREQ_MAX		3
+#define IQS269_MISC_A_GLOBAL_CAP_SIZE		BIT(0)
+
+#define IQS269_MISC_B_RESEED_UI_SEL_MASK	GENMASK(7, 6)
+#define IQS269_MISC_B_RESEED_UI_SEL_SHIFT	6
+#define IQS269_MISC_B_RESEED_UI_SEL_MAX		3
+#define IQS269_MISC_B_TRACKING_UI_ENABLE	BIT(4)
+#define IQS269_MISC_B_FILT_STR_SLIDER		GENMASK(1, 0)
+
+#define IQS269_CHx_SETTINGS			0x8C
+
+#define IQS269_CHx_ENG_A_MEAS_CAP_SIZE		BIT(15)
+#define IQS269_CHx_ENG_A_RX_GND_INACTIVE	BIT(13)
+#define IQS269_CHx_ENG_A_LOCAL_CAP_SIZE		BIT(12)
+#define IQS269_CHx_ENG_A_ATI_MODE_MASK		GENMASK(9, 8)
+#define IQS269_CHx_ENG_A_ATI_MODE_SHIFT		8
+#define IQS269_CHx_ENG_A_ATI_MODE_MAX		3
+#define IQS269_CHx_ENG_A_INV_LOGIC		BIT(7)
+#define IQS269_CHx_ENG_A_PROJ_BIAS_MASK		GENMASK(6, 5)
+#define IQS269_CHx_ENG_A_PROJ_BIAS_SHIFT	5
+#define IQS269_CHx_ENG_A_PROJ_BIAS_MAX		3
+#define IQS269_CHx_ENG_A_SENSE_MODE_MASK	GENMASK(3, 0)
+#define IQS269_CHx_ENG_A_SENSE_MODE_MAX		15
+
+#define IQS269_CHx_ENG_B_LOCAL_CAP_ENABLE	BIT(13)
+#define IQS269_CHx_ENG_B_SENSE_FREQ_MASK	GENMASK(10, 9)
+#define IQS269_CHx_ENG_B_SENSE_FREQ_SHIFT	9
+#define IQS269_CHx_ENG_B_SENSE_FREQ_MAX		3
+#define IQS269_CHx_ENG_B_STATIC_ENABLE		BIT(8)
+#define IQS269_CHx_ENG_B_ATI_BASE_MASK		GENMASK(7, 6)
+#define IQS269_CHx_ENG_B_ATI_BASE_75		0x00
+#define IQS269_CHx_ENG_B_ATI_BASE_100		0x40
+#define IQS269_CHx_ENG_B_ATI_BASE_150		0x80
+#define IQS269_CHx_ENG_B_ATI_BASE_200		0xC0
+#define IQS269_CHx_ENG_B_ATI_TARGET_MASK	GENMASK(5, 0)
+#define IQS269_CHx_ENG_B_ATI_TARGET_MAX		2016
+
+#define IQS269_CHx_WEIGHT_MAX			255
+#define IQS269_CHx_THRESH_MAX			255
+#define IQS269_CHx_HYST_DEEP_MASK		GENMASK(7, 4)
+#define IQS269_CHx_HYST_DEEP_SHIFT		4
+#define IQS269_CHx_HYST_TOUCH_MASK		GENMASK(3, 0)
+#define IQS269_CHx_HYST_MAX			15
+
+#define IQS269_CHx_HALL_INACTIVE		6
+#define IQS269_CHx_HALL_ACTIVE			7
+
+#define IQS269_HALL_PAD_R			BIT(0)
+#define IQS269_HALL_PAD_L			BIT(1)
+#define IQS269_HALL_PAD_INV			BIT(6)
+
+#define IQS269_HALL_UI				0xF5
+#define IQS269_HALL_UI_ENABLE			BIT(15)
+
+#define IQS269_MAX_REG				0xFF
+
+#define IQS269_NUM_CH				8
+#define IQS269_NUM_SL				2
+
+#define IQS269_ATI_POLL_SLEEP_US		(iqs269->delay_mult * 10000)
+#define IQS269_ATI_POLL_TIMEOUT_US		(iqs269->delay_mult * 500000)
+#define IQS269_ATI_STABLE_DELAY_MS		(iqs269->delay_mult * 150)
+
+#define IQS269_PWR_MODE_POLL_SLEEP_US		IQS269_ATI_POLL_SLEEP_US
+#define IQS269_PWR_MODE_POLL_TIMEOUT_US		IQS269_ATI_POLL_TIMEOUT_US
+
+#define iqs269_irq_wait()			usleep_range(100, 150)
+
+enum iqs269_local_cap_size {
+	IQS269_LOCAL_CAP_SIZE_0,
+	IQS269_LOCAL_CAP_SIZE_GLOBAL_ONLY,
+	IQS269_LOCAL_CAP_SIZE_GLOBAL_0pF5,
+};
+
+enum iqs269_st_offs {
+	IQS269_ST_OFFS_PROX,
+	IQS269_ST_OFFS_DIR,
+	IQS269_ST_OFFS_TOUCH,
+	IQS269_ST_OFFS_DEEP,
+};
+
+enum iqs269_th_offs {
+	IQS269_TH_OFFS_PROX,
+	IQS269_TH_OFFS_TOUCH,
+	IQS269_TH_OFFS_DEEP,
+};
+
+enum iqs269_event_id {
+	IQS269_EVENT_PROX_DN,
+	IQS269_EVENT_PROX_UP,
+	IQS269_EVENT_TOUCH_DN,
+	IQS269_EVENT_TOUCH_UP,
+	IQS269_EVENT_DEEP_DN,
+	IQS269_EVENT_DEEP_UP,
+};
+
+struct iqs269_switch_desc {
+	unsigned int code;
+	bool enabled;
+};
+
+struct iqs269_event_desc {
+	const char *name;
+	enum iqs269_st_offs st_offs;
+	enum iqs269_th_offs th_offs;
+	bool dir_up;
+	u8 mask;
+};
+
+static const struct iqs269_event_desc iqs269_events[] = {
+	[IQS269_EVENT_PROX_DN] = {
+		.name = "event-prox",
+		.st_offs = IQS269_ST_OFFS_PROX,
+		.th_offs = IQS269_TH_OFFS_PROX,
+		.mask = IQS269_EVENT_MASK_PROX,
+	},
+	[IQS269_EVENT_PROX_UP] = {
+		.name = "event-prox-alt",
+		.st_offs = IQS269_ST_OFFS_PROX,
+		.th_offs = IQS269_TH_OFFS_PROX,
+		.dir_up = true,
+		.mask = IQS269_EVENT_MASK_PROX,
+	},
+	[IQS269_EVENT_TOUCH_DN] = {
+		.name = "event-touch",
+		.st_offs = IQS269_ST_OFFS_TOUCH,
+		.th_offs = IQS269_TH_OFFS_TOUCH,
+		.mask = IQS269_EVENT_MASK_TOUCH,
+	},
+	[IQS269_EVENT_TOUCH_UP] = {
+		.name = "event-touch-alt",
+		.st_offs = IQS269_ST_OFFS_TOUCH,
+		.th_offs = IQS269_TH_OFFS_TOUCH,
+		.dir_up = true,
+		.mask = IQS269_EVENT_MASK_TOUCH,
+	},
+	[IQS269_EVENT_DEEP_DN] = {
+		.name = "event-deep",
+		.st_offs = IQS269_ST_OFFS_DEEP,
+		.th_offs = IQS269_TH_OFFS_DEEP,
+		.mask = IQS269_EVENT_MASK_DEEP,
+	},
+	[IQS269_EVENT_DEEP_UP] = {
+		.name = "event-deep-alt",
+		.st_offs = IQS269_ST_OFFS_DEEP,
+		.th_offs = IQS269_TH_OFFS_DEEP,
+		.dir_up = true,
+		.mask = IQS269_EVENT_MASK_DEEP,
+	},
+};
+
+struct iqs269_ver_info {
+	u8 prod_num;
+	u8 sw_num;
+	u8 hw_num;
+	u8 padding;
+} __packed;
+
+struct iqs269_sys_reg {
+	__be16 general;
+	u8 active;
+	u8 filter;
+	u8 reseed;
+	u8 event_mask;
+	u8 rate_np;
+	u8 rate_lp;
+	u8 rate_ulp;
+	u8 timeout_pwr;
+	u8 timeout_rdy;
+	u8 timeout_lta;
+	__be16 misc_a;
+	__be16 misc_b;
+	u8 blocking;
+	u8 padding;
+	u8 slider_select[IQS269_NUM_SL];
+	u8 timeout_tap;
+	u8 timeout_swipe;
+	u8 thresh_swipe;
+	u8 redo_ati;
+} __packed;
+
+struct iqs269_ch_reg {
+	u8 rx_enable;
+	u8 tx_enable;
+	__be16 engine_a;
+	__be16 engine_b;
+	__be16 ati_comp;
+	u8 thresh[3];
+	u8 hyst;
+	u8 assoc_select;
+	u8 assoc_weight;
+} __packed;
+
+struct iqs269_flags {
+	__be16 system;
+	u8 gesture;
+	u8 padding;
+	u8 states[4];
+} __packed;
+
+struct iqs269_private {
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct mutex lock;
+	struct iqs269_switch_desc switches[ARRAY_SIZE(iqs269_events)];
+	struct iqs269_ch_reg ch_reg[IQS269_NUM_CH];
+	struct iqs269_sys_reg sys_reg;
+	struct input_dev *keypad;
+	struct input_dev *slider[IQS269_NUM_SL];
+	unsigned int keycode[ARRAY_SIZE(iqs269_events) * IQS269_NUM_CH];
+	unsigned int suspend_mode;
+	unsigned int delay_mult;
+	unsigned int ch_num;
+	bool hall_enable;
+	bool ati_current;
+};
+
+static int iqs269_ati_mode_set(struct iqs269_private *iqs269,
+			       unsigned int ch_num, unsigned int mode)
+{
+	u16 engine_a;
+
+	if (ch_num >= IQS269_NUM_CH)
+		return -EINVAL;
+
+	if (mode > IQS269_CHx_ENG_A_ATI_MODE_MAX)
+		return -EINVAL;
+
+	mutex_lock(&iqs269->lock);
+
+	engine_a = be16_to_cpu(iqs269->ch_reg[ch_num].engine_a);
+
+	engine_a &= ~IQS269_CHx_ENG_A_ATI_MODE_MASK;
+	engine_a |= (mode << IQS269_CHx_ENG_A_ATI_MODE_SHIFT);
+
+	iqs269->ch_reg[ch_num].engine_a = cpu_to_be16(engine_a);
+	iqs269->ati_current = false;
+
+	mutex_unlock(&iqs269->lock);
+
+	return 0;
+}
+
+static int iqs269_ati_mode_get(struct iqs269_private *iqs269,
+			       unsigned int ch_num, unsigned int *mode)
+{
+	u16 engine_a;
+
+	if (ch_num >= IQS269_NUM_CH)
+		return -EINVAL;
+
+	mutex_lock(&iqs269->lock);
+	engine_a = be16_to_cpu(iqs269->ch_reg[ch_num].engine_a);
+	mutex_unlock(&iqs269->lock);
+
+	engine_a &= IQS269_CHx_ENG_A_ATI_MODE_MASK;
+	*mode = (engine_a >> IQS269_CHx_ENG_A_ATI_MODE_SHIFT);
+
+	return 0;
+}
+
+static int iqs269_ati_base_set(struct iqs269_private *iqs269,
+			       unsigned int ch_num, unsigned int base)
+{
+	u16 engine_b;
+
+	if (ch_num >= IQS269_NUM_CH)
+		return -EINVAL;
+
+	switch (base) {
+	case 75:
+		base = IQS269_CHx_ENG_B_ATI_BASE_75;
+		break;
+
+	case 100:
+		base = IQS269_CHx_ENG_B_ATI_BASE_100;
+		break;
+
+	case 150:
+		base = IQS269_CHx_ENG_B_ATI_BASE_150;
+		break;
+
+	case 200:
+		base = IQS269_CHx_ENG_B_ATI_BASE_200;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	mutex_lock(&iqs269->lock);
+
+	engine_b = be16_to_cpu(iqs269->ch_reg[ch_num].engine_b);
+
+	engine_b &= ~IQS269_CHx_ENG_B_ATI_BASE_MASK;
+	engine_b |= base;
+
+	iqs269->ch_reg[ch_num].engine_b = cpu_to_be16(engine_b);
+	iqs269->ati_current = false;
+
+	mutex_unlock(&iqs269->lock);
+
+	return 0;
+}
+
+static int iqs269_ati_base_get(struct iqs269_private *iqs269,
+			       unsigned int ch_num, unsigned int *base)
+{
+	u16 engine_b;
+
+	if (ch_num >= IQS269_NUM_CH)
+		return -EINVAL;
+
+	mutex_lock(&iqs269->lock);
+	engine_b = be16_to_cpu(iqs269->ch_reg[ch_num].engine_b);
+	mutex_unlock(&iqs269->lock);
+
+	switch (engine_b & IQS269_CHx_ENG_B_ATI_BASE_MASK) {
+	case IQS269_CHx_ENG_B_ATI_BASE_75:
+		*base = 75;
+		return 0;
+
+	case IQS269_CHx_ENG_B_ATI_BASE_100:
+		*base = 100;
+		return 0;
+
+	case IQS269_CHx_ENG_B_ATI_BASE_150:
+		*base = 150;
+		return 0;
+
+	case IQS269_CHx_ENG_B_ATI_BASE_200:
+		*base = 200;
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int iqs269_ati_target_set(struct iqs269_private *iqs269,
+				 unsigned int ch_num, unsigned int target)
+{
+	u16 engine_b;
+
+	if (ch_num >= IQS269_NUM_CH)
+		return -EINVAL;
+
+	if (target > IQS269_CHx_ENG_B_ATI_TARGET_MAX)
+		return -EINVAL;
+
+	mutex_lock(&iqs269->lock);
+
+	engine_b = be16_to_cpu(iqs269->ch_reg[ch_num].engine_b);
+
+	engine_b &= ~IQS269_CHx_ENG_B_ATI_TARGET_MASK;
+	engine_b |= target / 32;
+
+	iqs269->ch_reg[ch_num].engine_b = cpu_to_be16(engine_b);
+	iqs269->ati_current = false;
+
+	mutex_unlock(&iqs269->lock);
+
+	return 0;
+}
+
+static int iqs269_ati_target_get(struct iqs269_private *iqs269,
+				 unsigned int ch_num, unsigned int *target)
+{
+	u16 engine_b;
+
+	if (ch_num >= IQS269_NUM_CH)
+		return -EINVAL;
+
+	mutex_lock(&iqs269->lock);
+	engine_b = be16_to_cpu(iqs269->ch_reg[ch_num].engine_b);
+	mutex_unlock(&iqs269->lock);
+
+	*target = (engine_b & IQS269_CHx_ENG_B_ATI_TARGET_MASK) * 32;
+
+	return 0;
+}
+
+static int iqs269_parse_mask(const struct fwnode_handle *fwnode,
+			     const char *propname, u8 *mask)
+{
+	unsigned int val[IQS269_NUM_CH];
+	int count, error, i;
+
+	count = fwnode_property_count_u32(fwnode, propname);
+	if (count < 0)
+		return 0;
+
+	if (count > IQS269_NUM_CH)
+		return -EINVAL;
+
+	error = fwnode_property_read_u32_array(fwnode, propname, val, count);
+	if (error)
+		return error;
+
+	*mask = 0;
+
+	for (i = 0; i < count; i++) {
+		if (val[i] >= IQS269_NUM_CH)
+			return -EINVAL;
+
+		*mask |= BIT(val[i]);
+	}
+
+	return 0;
+}
+
+static int iqs269_parse_chan(struct iqs269_private *iqs269,
+			     const struct fwnode_handle *ch_node)
+{
+	struct i2c_client *client = iqs269->client;
+	struct fwnode_handle *ev_node;
+	struct iqs269_ch_reg *ch_reg;
+	u16 engine_a, engine_b;
+	unsigned int reg, val;
+	int error, i;
+
+	error = fwnode_property_read_u32(ch_node, "reg", &reg);
+	if (error) {
+		dev_err(&client->dev, "Failed to read channel number: %d\n",
+			error);
+		return error;
+	} else if (reg >= IQS269_NUM_CH) {
+		dev_err(&client->dev, "Invalid channel number: %u\n", reg);
+		return -EINVAL;
+	}
+
+	iqs269->sys_reg.active |= BIT(reg);
+	if (!fwnode_property_present(ch_node, "azoteq,reseed-disable"))
+		iqs269->sys_reg.reseed |= BIT(reg);
+
+	if (fwnode_property_present(ch_node, "azoteq,blocking-enable"))
+		iqs269->sys_reg.blocking |= BIT(reg);
+
+	if (fwnode_property_present(ch_node, "azoteq,slider0-select"))
+		iqs269->sys_reg.slider_select[0] |= BIT(reg);
+
+	if (fwnode_property_present(ch_node, "azoteq,slider1-select"))
+		iqs269->sys_reg.slider_select[1] |= BIT(reg);
+
+	ch_reg = &iqs269->ch_reg[reg];
+
+	error = regmap_raw_read(iqs269->regmap,
+				IQS269_CHx_SETTINGS + reg * sizeof(*ch_reg) / 2,
+				ch_reg, sizeof(*ch_reg));
+	if (error)
+		return error;
+
+	error = iqs269_parse_mask(ch_node, "azoteq,rx-enable",
+				  &ch_reg->rx_enable);
+	if (error) {
+		dev_err(&client->dev, "Invalid channel %u RX enable mask: %d\n",
+			reg, error);
+		return error;
+	}
+
+	error = iqs269_parse_mask(ch_node, "azoteq,tx-enable",
+				  &ch_reg->tx_enable);
+	if (error) {
+		dev_err(&client->dev, "Invalid channel %u TX enable mask: %d\n",
+			reg, error);
+		return error;
+	}
+
+	engine_a = be16_to_cpu(ch_reg->engine_a);
+	engine_b = be16_to_cpu(ch_reg->engine_b);
+
+	engine_a |= IQS269_CHx_ENG_A_MEAS_CAP_SIZE;
+	if (fwnode_property_present(ch_node, "azoteq,meas-cap-decrease"))
+		engine_a &= ~IQS269_CHx_ENG_A_MEAS_CAP_SIZE;
+
+	engine_a |= IQS269_CHx_ENG_A_RX_GND_INACTIVE;
+	if (fwnode_property_present(ch_node, "azoteq,rx-float-inactive"))
+		engine_a &= ~IQS269_CHx_ENG_A_RX_GND_INACTIVE;
+
+	engine_a &= ~IQS269_CHx_ENG_A_LOCAL_CAP_SIZE;
+	engine_b &= ~IQS269_CHx_ENG_B_LOCAL_CAP_ENABLE;
+	if (!fwnode_property_read_u32(ch_node, "azoteq,local-cap-size", &val)) {
+		switch (val) {
+		case IQS269_LOCAL_CAP_SIZE_0:
+			break;
+
+		case IQS269_LOCAL_CAP_SIZE_GLOBAL_0pF5:
+			engine_a |= IQS269_CHx_ENG_A_LOCAL_CAP_SIZE;
+
+			/* fall through */
+
+		case IQS269_LOCAL_CAP_SIZE_GLOBAL_ONLY:
+			engine_b |= IQS269_CHx_ENG_B_LOCAL_CAP_ENABLE;
+			break;
+
+		default:
+			dev_err(&client->dev,
+				"Invalid channel %u local cap. size: %u\n", reg,
+				val);
+			return -EINVAL;
+		}
+	}
+
+	engine_a &= ~IQS269_CHx_ENG_A_INV_LOGIC;
+	if (fwnode_property_present(ch_node, "azoteq,invert-enable"))
+		engine_a |= IQS269_CHx_ENG_A_INV_LOGIC;
+
+	if (!fwnode_property_read_u32(ch_node, "azoteq,proj-bias", &val)) {
+		if (val > IQS269_CHx_ENG_A_PROJ_BIAS_MAX) {
+			dev_err(&client->dev,
+				"Invalid channel %u bias current: %u\n", reg,
+				val);
+			return -EINVAL;
+		}
+
+		engine_a &= ~IQS269_CHx_ENG_A_PROJ_BIAS_MASK;
+		engine_a |= (val << IQS269_CHx_ENG_A_PROJ_BIAS_SHIFT);
+	}
+
+	if (!fwnode_property_read_u32(ch_node, "azoteq,sense-mode", &val)) {
+		if (val > IQS269_CHx_ENG_A_SENSE_MODE_MAX) {
+			dev_err(&client->dev,
+				"Invalid channel %u sensing mode: %u\n", reg,
+				val);
+			return -EINVAL;
+		}
+
+		engine_a &= ~IQS269_CHx_ENG_A_SENSE_MODE_MASK;
+		engine_a |= val;
+	}
+
+	if (!fwnode_property_read_u32(ch_node, "azoteq,sense-freq", &val)) {
+		if (val > IQS269_CHx_ENG_B_SENSE_FREQ_MAX) {
+			dev_err(&client->dev,
+				"Invalid channel %u sensing frequency: %u\n",
+				reg, val);
+			return -EINVAL;
+		}
+
+		engine_b &= ~IQS269_CHx_ENG_B_SENSE_FREQ_MASK;
+		engine_b |= (val << IQS269_CHx_ENG_B_SENSE_FREQ_SHIFT);
+	}
+
+	engine_b &= ~IQS269_CHx_ENG_B_STATIC_ENABLE;
+	if (fwnode_property_present(ch_node, "azoteq,static-enable"))
+		engine_b |= IQS269_CHx_ENG_B_STATIC_ENABLE;
+
+	ch_reg->engine_a = cpu_to_be16(engine_a);
+	ch_reg->engine_b = cpu_to_be16(engine_b);
+
+	if (!fwnode_property_read_u32(ch_node, "azoteq,ati-mode", &val)) {
+		error = iqs269_ati_mode_set(iqs269, reg, val);
+		if (error) {
+			dev_err(&client->dev,
+				"Invalid channel %u ATI mode: %u\n", reg, val);
+			return error;
+		}
+	}
+
+	if (!fwnode_property_read_u32(ch_node, "azoteq,ati-base", &val)) {
+		error = iqs269_ati_base_set(iqs269, reg, val);
+		if (error) {
+			dev_err(&client->dev,
+				"Invalid channel %u ATI base: %u\n", reg, val);
+			return error;
+		}
+	}
+
+	if (!fwnode_property_read_u32(ch_node, "azoteq,ati-target", &val)) {
+		error = iqs269_ati_target_set(iqs269, reg, val);
+		if (error) {
+			dev_err(&client->dev,
+				"Invalid channel %u ATI target: %u\n", reg,
+				val);
+			return error;
+		}
+	}
+
+	error = iqs269_parse_mask(ch_node, "azoteq,assoc-select",
+				  &ch_reg->assoc_select);
+	if (error) {
+		dev_err(&client->dev, "Invalid channel %u association: %d\n",
+			reg, error);
+		return error;
+	}
+
+	if (!fwnode_property_read_u32(ch_node, "azoteq,assoc-weight", &val)) {
+		if (val > IQS269_CHx_WEIGHT_MAX) {
+			dev_err(&client->dev,
+				"Invalid channel %u associated weight: %u\n",
+				reg, val);
+			return -EINVAL;
+		}
+
+		ch_reg->assoc_weight = val;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(iqs269_events); i++) {
+		ev_node = fwnode_get_named_child_node(ch_node,
+						      iqs269_events[i].name);
+		if (!ev_node)
+			continue;
+
+		if (!fwnode_property_read_u32(ev_node, "azoteq,thresh", &val)) {
+			if (val > IQS269_CHx_THRESH_MAX) {
+				dev_err(&client->dev,
+					"Invalid channel %u threshold: %u\n",
+					reg, val);
+				return -EINVAL;
+			}
+
+			ch_reg->thresh[iqs269_events[i].th_offs] = val;
+		}
+
+		if (!fwnode_property_read_u32(ev_node, "azoteq,hyst", &val)) {
+			u8 *hyst = &ch_reg->hyst;
+
+			if (val > IQS269_CHx_HYST_MAX) {
+				dev_err(&client->dev,
+					"Invalid channel %u hysteresis: %u\n",
+					reg, val);
+				return -EINVAL;
+			}
+
+			if (i == IQS269_EVENT_DEEP_DN ||
+			    i == IQS269_EVENT_DEEP_UP) {
+				*hyst &= ~IQS269_CHx_HYST_DEEP_MASK;
+				*hyst |= (val << IQS269_CHx_HYST_DEEP_SHIFT);
+			} else if (i == IQS269_EVENT_TOUCH_DN ||
+				   i == IQS269_EVENT_TOUCH_UP) {
+				*hyst &= ~IQS269_CHx_HYST_TOUCH_MASK;
+				*hyst |= val;
+			}
+		}
+
+		if (fwnode_property_read_u32(ev_node, "linux,code", &val))
+			continue;
+
+		switch (reg) {
+		case IQS269_CHx_HALL_ACTIVE:
+			if (iqs269->hall_enable) {
+				iqs269->switches[i].code = val;
+				iqs269->switches[i].enabled = true;
+			}
+
+			/* fall through */
+
+		case IQS269_CHx_HALL_INACTIVE:
+			if (iqs269->hall_enable)
+				break;
+
+			/* fall through */
+
+		default:
+			iqs269->keycode[i * IQS269_NUM_CH + reg] = val;
+		}
+
+		iqs269->sys_reg.event_mask &= ~iqs269_events[i].mask;
+	}
+
+	return 0;
+}
+
+static int iqs269_parse_prop(struct iqs269_private *iqs269)
+{
+	struct iqs269_sys_reg *sys_reg = &iqs269->sys_reg;
+	struct i2c_client *client = iqs269->client;
+	struct fwnode_handle *ch_node;
+	u16 general, misc_a, misc_b;
+	unsigned int val;
+	int error;
+
+	iqs269->hall_enable = device_property_present(&client->dev,
+						      "azoteq,hall-enable");
+
+	if (!device_property_read_u32(&client->dev, "azoteq,suspend-mode",
+				      &val)) {
+		if (val > IQS269_SYS_SETTINGS_PWR_MODE_MAX) {
+			dev_err(&client->dev, "Invalid suspend mode: %u\n",
+				val);
+			return -EINVAL;
+		}
+
+		iqs269->suspend_mode = val;
+	}
+
+	error = regmap_raw_read(iqs269->regmap, IQS269_SYS_SETTINGS, sys_reg,
+				sizeof(*sys_reg));
+	if (error)
+		return error;
+
+	if (!device_property_read_u32(&client->dev, "azoteq,filt-str-lp-lta",
+				      &val)) {
+		if (val > IQS269_FILT_STR_MAX) {
+			dev_err(&client->dev, "Invalid filter strength: %u\n",
+				val);
+			return -EINVAL;
+		}
+
+		sys_reg->filter &= ~IQS269_FILT_STR_LP_LTA_MASK;
+		sys_reg->filter |= (val << IQS269_FILT_STR_LP_LTA_SHIFT);
+	}
+
+	if (!device_property_read_u32(&client->dev, "azoteq,filt-str-lp-cnt",
+				      &val)) {
+		if (val > IQS269_FILT_STR_MAX) {
+			dev_err(&client->dev, "Invalid filter strength: %u\n",
+				val);
+			return -EINVAL;
+		}
+
+		sys_reg->filter &= ~IQS269_FILT_STR_LP_CNT_MASK;
+		sys_reg->filter |= (val << IQS269_FILT_STR_LP_CNT_SHIFT);
+	}
+
+	if (!device_property_read_u32(&client->dev, "azoteq,filt-str-np-lta",
+				      &val)) {
+		if (val > IQS269_FILT_STR_MAX) {
+			dev_err(&client->dev, "Invalid filter strength: %u\n",
+				val);
+			return -EINVAL;
+		}
+
+		sys_reg->filter &= ~IQS269_FILT_STR_NP_LTA_MASK;
+		sys_reg->filter |= (val << IQS269_FILT_STR_NP_LTA_SHIFT);
+	}
+
+	if (!device_property_read_u32(&client->dev, "azoteq,filt-str-np-cnt",
+				      &val)) {
+		if (val > IQS269_FILT_STR_MAX) {
+			dev_err(&client->dev, "Invalid filter strength: %u\n",
+				val);
+			return -EINVAL;
+		}
+
+		sys_reg->filter &= ~IQS269_FILT_STR_NP_CNT_MASK;
+		sys_reg->filter |= val;
+	}
+
+	if (!device_property_read_u32(&client->dev, "azoteq,rate-np-ms",
+				      &val)) {
+		if (val > IQS269_RATE_NP_MS_MAX) {
+			dev_err(&client->dev, "Invalid report rate: %u\n", val);
+			return -EINVAL;
+		}
+
+		sys_reg->rate_np = val;
+	}
+
+	if (!device_property_read_u32(&client->dev, "azoteq,rate-lp-ms",
+				      &val)) {
+		if (val > IQS269_RATE_LP_MS_MAX) {
+			dev_err(&client->dev, "Invalid report rate: %u\n", val);
+			return -EINVAL;
+		}
+
+		sys_reg->rate_lp = val;
+	}
+
+	if (!device_property_read_u32(&client->dev, "azoteq,rate-ulp-ms",
+				      &val)) {
+		if (val > IQS269_RATE_ULP_MS_MAX) {
+			dev_err(&client->dev, "Invalid report rate: %u\n", val);
+			return -EINVAL;
+		}
+
+		sys_reg->rate_ulp = val / 16;
+	}
+
+	if (!device_property_read_u32(&client->dev, "azoteq,timeout-pwr-ms",
+				      &val)) {
+		if (val > IQS269_TIMEOUT_PWR_MS_MAX) {
+			dev_err(&client->dev, "Invalid timeout: %u\n", val);
+			return -EINVAL;
+		}
+
+		sys_reg->timeout_pwr = val / 512;
+	}
+
+	if (!device_property_read_u32(&client->dev, "azoteq,timeout-lta-ms",
+				      &val)) {
+		if (val > IQS269_TIMEOUT_LTA_MS_MAX) {
+			dev_err(&client->dev, "Invalid timeout: %u\n", val);
+			return -EINVAL;
+		}
+
+		sys_reg->timeout_lta = val / 512;
+	}
+
+	misc_a = be16_to_cpu(sys_reg->misc_a);
+	misc_b = be16_to_cpu(sys_reg->misc_b);
+
+	misc_a &= ~IQS269_MISC_A_ATI_BAND_DISABLE;
+	if (device_property_present(&client->dev, "azoteq,ati-band-disable"))
+		misc_a |= IQS269_MISC_A_ATI_BAND_DISABLE;
+
+	misc_a &= ~IQS269_MISC_A_ATI_LP_ONLY;
+	if (device_property_present(&client->dev, "azoteq,ati-lp-only"))
+		misc_a |= IQS269_MISC_A_ATI_LP_ONLY;
+
+	misc_a &= ~IQS269_MISC_A_ATI_BAND_TIGHTEN;
+	if (device_property_present(&client->dev, "azoteq,ati-band-tighten"))
+		misc_a |= IQS269_MISC_A_ATI_BAND_TIGHTEN;
+
+	misc_a &= ~IQS269_MISC_A_FILT_DISABLE;
+	if (device_property_present(&client->dev, "azoteq,filt-disable"))
+		misc_a |= IQS269_MISC_A_FILT_DISABLE;
+
+	if (!device_property_read_u32(&client->dev, "azoteq,gpio3-select",
+				      &val)) {
+		if (val >= IQS269_NUM_CH) {
+			dev_err(&client->dev, "Invalid GPIO3 selection: %u\n",
+				val);
+			return -EINVAL;
+		}
+
+		misc_a &= ~IQS269_MISC_A_GPIO3_SELECT_MASK;
+		misc_a |= (val << IQS269_MISC_A_GPIO3_SELECT_SHIFT);
+	}
+
+	misc_a &= ~IQS269_MISC_A_DUAL_DIR;
+	if (device_property_present(&client->dev, "azoteq,dual-direction"))
+		misc_a |= IQS269_MISC_A_DUAL_DIR;
+
+	if (!device_property_read_u32(&client->dev, "azoteq,tx-freq", &val)) {
+		if (val > IQS269_MISC_A_TX_FREQ_MAX) {
+			dev_err(&client->dev,
+				"Invalid excitation frequency: %u\n", val);
+			return -EINVAL;
+		}
+
+		misc_a &= ~IQS269_MISC_A_TX_FREQ_MASK;
+		misc_a |= (val << IQS269_MISC_A_TX_FREQ_SHIFT);
+	}
+
+	misc_a &= ~IQS269_MISC_A_GLOBAL_CAP_SIZE;
+	if (device_property_present(&client->dev, "azoteq,global-cap-increase"))
+		misc_a |= IQS269_MISC_A_GLOBAL_CAP_SIZE;
+
+	if (!device_property_read_u32(&client->dev, "azoteq,reseed-select",
+				      &val)) {
+		if (val > IQS269_MISC_B_RESEED_UI_SEL_MAX) {
+			dev_err(&client->dev, "Invalid reseed selection: %u\n",
+				val);
+			return -EINVAL;
+		}
+
+		misc_b &= ~IQS269_MISC_B_RESEED_UI_SEL_MASK;
+		misc_b |= (val << IQS269_MISC_B_RESEED_UI_SEL_SHIFT);
+	}
+
+	misc_b &= ~IQS269_MISC_B_TRACKING_UI_ENABLE;
+	if (device_property_present(&client->dev, "azoteq,tracking-enable"))
+		misc_b |= IQS269_MISC_B_TRACKING_UI_ENABLE;
+
+	if (!device_property_read_u32(&client->dev, "azoteq,filt-str-slider",
+				      &val)) {
+		if (val > IQS269_FILT_STR_MAX) {
+			dev_err(&client->dev, "Invalid filter strength: %u\n",
+				val);
+			return -EINVAL;
+		}
+
+		misc_b &= ~IQS269_MISC_B_FILT_STR_SLIDER;
+		misc_b |= val;
+	}
+
+	sys_reg->misc_a = cpu_to_be16(misc_a);
+	sys_reg->misc_b = cpu_to_be16(misc_b);
+
+	sys_reg->active = 0;
+	sys_reg->reseed = 0;
+
+	sys_reg->blocking = 0;
+
+	sys_reg->slider_select[0] = 0;
+	sys_reg->slider_select[1] = 0;
+
+	sys_reg->event_mask = ~((u8)IQS269_EVENT_MASK_SYS);
+
+	device_for_each_child_node(&client->dev, ch_node) {
+		error = iqs269_parse_chan(iqs269, ch_node);
+		if (error) {
+			fwnode_handle_put(ch_node);
+			return error;
+		}
+	}
+
+	/*
+	 * Volunteer all active channels to participate in ATI when REDO-ATI is
+	 * manually triggered.
+	 */
+	sys_reg->redo_ati = sys_reg->active;
+
+	general = be16_to_cpu(sys_reg->general);
+
+	if (device_property_present(&client->dev, "azoteq,clk-div")) {
+		general |= IQS269_SYS_SETTINGS_CLK_DIV;
+		iqs269->delay_mult = 4;
+	} else {
+		general &= ~IQS269_SYS_SETTINGS_CLK_DIV;
+		iqs269->delay_mult = 1;
+	}
+
+	/*
+	 * Configure the device to automatically switch between normal and low-
+	 * power modes as a function of sensing activity. Ultra-low-power mode,
+	 * if enabled, is reserved for suspend.
+	 */
+	general &= ~IQS269_SYS_SETTINGS_ULP_AUTO;
+	general &= ~IQS269_SYS_SETTINGS_DIS_AUTO;
+	general &= ~IQS269_SYS_SETTINGS_PWR_MODE_MASK;
+
+	if (!device_property_read_u32(&client->dev, "azoteq,ulp-update",
+				      &val)) {
+		if (val > IQS269_SYS_SETTINGS_ULP_UPDATE_MAX) {
+			dev_err(&client->dev, "Invalid update rate: %u\n", val);
+			return -EINVAL;
+		}
+
+		general &= ~IQS269_SYS_SETTINGS_ULP_UPDATE_MASK;
+		general |= (val << IQS269_SYS_SETTINGS_ULP_UPDATE_SHIFT);
+	}
+
+	general &= ~IQS269_SYS_SETTINGS_RESEED_OFFSET;
+	if (device_property_present(&client->dev, "azoteq,reseed-offset"))
+		general |= IQS269_SYS_SETTINGS_RESEED_OFFSET;
+
+	general |= IQS269_SYS_SETTINGS_EVENT_MODE;
+
+	/*
+	 * As per the datasheet, enable streaming during normal-power mode if
+	 * either slider is in use. In that case, the device returns to event
+	 * mode during low-power mode.
+	 */
+	if (sys_reg->slider_select[0] || sys_reg->slider_select[1])
+		general |= IQS269_SYS_SETTINGS_EVENT_MODE_LP;
+
+	general |= IQS269_SYS_SETTINGS_REDO_ATI;
+	general |= IQS269_SYS_SETTINGS_ACK_RESET;
+
+	sys_reg->general = cpu_to_be16(general);
+
+	return 0;
+}
+
+static int iqs269_dev_init(struct iqs269_private *iqs269)
+{
+	struct iqs269_sys_reg *sys_reg = &iqs269->sys_reg;
+	struct iqs269_ch_reg *ch_reg;
+	unsigned int val;
+	int error, i;
+
+	mutex_lock(&iqs269->lock);
+
+	error = regmap_update_bits(iqs269->regmap, IQS269_HALL_UI,
+				   IQS269_HALL_UI_ENABLE,
+				   iqs269->hall_enable ? ~0 : 0);
+	if (error)
+		goto err_mutex;
+
+	for (i = 0; i < IQS269_NUM_CH; i++) {
+		if (!(sys_reg->active & BIT(i)))
+			continue;
+
+		ch_reg = &iqs269->ch_reg[i];
+
+		error = regmap_raw_write(iqs269->regmap,
+					 IQS269_CHx_SETTINGS + i *
+					 sizeof(*ch_reg) / 2, ch_reg,
+					 sizeof(*ch_reg));
+		if (error)
+			goto err_mutex;
+	}
+
+	/*
+	 * The REDO-ATI and ATI channel selection fields must be written in the
+	 * same block write, so every field between registers 0x80 through 0x8B
+	 * (inclusive) must be written as well.
+	 */
+	error = regmap_raw_write(iqs269->regmap, IQS269_SYS_SETTINGS, sys_reg,
+				 sizeof(*sys_reg));
+	if (error)
+		goto err_mutex;
+
+	error = regmap_read_poll_timeout(iqs269->regmap, IQS269_SYS_FLAGS, val,
+					!(val & IQS269_SYS_FLAGS_IN_ATI),
+					 IQS269_ATI_POLL_SLEEP_US,
+					 IQS269_ATI_POLL_TIMEOUT_US);
+	if (error)
+		goto err_mutex;
+
+	msleep(IQS269_ATI_STABLE_DELAY_MS);
+	iqs269->ati_current = true;
+
+err_mutex:
+	mutex_unlock(&iqs269->lock);
+
+	return error;
+}
+
+static int iqs269_input_init(struct iqs269_private *iqs269)
+{
+	struct i2c_client *client = iqs269->client;
+	struct iqs269_flags flags;
+	unsigned int sw_code, keycode;
+	int error, i, j;
+	u8 dir_mask, state;
+
+	iqs269->keypad = devm_input_allocate_device(&client->dev);
+	if (!iqs269->keypad)
+		return -ENOMEM;
+
+	iqs269->keypad->keycodemax = ARRAY_SIZE(iqs269->keycode);
+	iqs269->keypad->keycode = iqs269->keycode;
+	iqs269->keypad->keycodesize = sizeof(*iqs269->keycode);
+
+	iqs269->keypad->name = "iqs269a_keypad";
+	iqs269->keypad->id.bustype = BUS_I2C;
+
+	if (iqs269->hall_enable) {
+		error = regmap_raw_read(iqs269->regmap, IQS269_SYS_FLAGS,
+					&flags, sizeof(flags));
+		if (error) {
+			dev_err(&client->dev,
+				"Failed to read initial status: %d\n", error);
+			return error;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(iqs269_events); i++) {
+		dir_mask = flags.states[IQS269_ST_OFFS_DIR];
+		if (!iqs269_events[i].dir_up)
+			dir_mask = ~dir_mask;
+
+		state = flags.states[iqs269_events[i].st_offs] & dir_mask;
+
+		sw_code = iqs269->switches[i].code;
+
+		for (j = 0; j < IQS269_NUM_CH; j++) {
+			keycode = iqs269->keycode[i * IQS269_NUM_CH + j];
+
+			/*
+			 * Hall-effect sensing repurposes a pair of dedicated
+			 * channels, only one of which reports events.
+			 */
+			switch (j) {
+			case IQS269_CHx_HALL_ACTIVE:
+				if (iqs269->hall_enable &&
+				    iqs269->switches[i].enabled) {
+					input_set_capability(iqs269->keypad,
+							     EV_SW, sw_code);
+					input_report_switch(iqs269->keypad,
+							    sw_code,
+							    state & BIT(j));
+				}
+
+				/* fall through */
+
+			case IQS269_CHx_HALL_INACTIVE:
+				if (iqs269->hall_enable)
+					continue;
+
+				/* fall through */
+
+			default:
+				if (keycode != KEY_RESERVED)
+					input_set_capability(iqs269->keypad,
+							     EV_KEY, keycode);
+			}
+		}
+	}
+
+	input_sync(iqs269->keypad);
+
+	error = input_register_device(iqs269->keypad);
+	if (error) {
+		dev_err(&client->dev, "Failed to register keypad: %d\n", error);
+		return error;
+	}
+
+	for (i = 0; i < IQS269_NUM_SL; i++) {
+		if (!iqs269->sys_reg.slider_select[i])
+			continue;
+
+		iqs269->slider[i] = devm_input_allocate_device(&client->dev);
+		if (!iqs269->slider[i])
+			return -ENOMEM;
+
+		iqs269->slider[i]->name = i ? "iqs269a_slider_1"
+					    : "iqs269a_slider_0";
+		iqs269->slider[i]->id.bustype = BUS_I2C;
+
+		input_set_capability(iqs269->slider[i], EV_KEY, BTN_TOUCH);
+		input_set_abs_params(iqs269->slider[i], ABS_X, 0, 255, 0, 0);
+
+		error = input_register_device(iqs269->slider[i]);
+		if (error) {
+			dev_err(&client->dev,
+				"Failed to register slider %d: %d\n", i, error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static int iqs269_report(struct iqs269_private *iqs269)
+{
+	struct i2c_client *client = iqs269->client;
+	struct iqs269_flags flags;
+	unsigned int sw_code, keycode;
+	int error, i, j;
+	u8 slider_x[IQS269_NUM_SL];
+	u8 dir_mask, state;
+
+	error = regmap_raw_read(iqs269->regmap, IQS269_SYS_FLAGS, &flags,
+				sizeof(flags));
+	if (error) {
+		dev_err(&client->dev, "Failed to read device status: %d\n",
+			error);
+		return error;
+	}
+
+	/*
+	 * The device resets itself if its own watchdog bites, which can happen
+	 * in the event of an I2C communication error. In this case, the device
+	 * asserts a SHOW_RESET interrupt and all registers must be restored.
+	 */
+	if (be16_to_cpu(flags.system) & IQS269_SYS_FLAGS_SHOW_RESET) {
+		dev_err(&client->dev, "Unexpected device reset\n");
+
+		error = iqs269_dev_init(iqs269);
+		if (error)
+			dev_err(&client->dev,
+				"Failed to re-initialize device: %d\n", error);
+
+		return error;
+	}
+
+	error = regmap_raw_read(iqs269->regmap, IQS269_SLIDER_X, slider_x,
+				sizeof(slider_x));
+	if (error) {
+		dev_err(&client->dev, "Failed to read slider position: %d\n",
+			error);
+		return error;
+	}
+
+	for (i = 0; i < IQS269_NUM_SL; i++) {
+		if (!iqs269->sys_reg.slider_select[i])
+			continue;
+
+		/*
+		 * Report BTN_TOUCH if any channel that participates in the
+		 * slider is in a state of touch.
+		 */
+		if (flags.states[IQS269_ST_OFFS_TOUCH] &
+		    iqs269->sys_reg.slider_select[i]) {
+			input_report_key(iqs269->slider[i], BTN_TOUCH, 1);
+			input_report_abs(iqs269->slider[i], ABS_X, slider_x[i]);
+		} else {
+			input_report_key(iqs269->slider[i], BTN_TOUCH, 0);
+		}
+
+		input_sync(iqs269->slider[i]);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(iqs269_events); i++) {
+		dir_mask = flags.states[IQS269_ST_OFFS_DIR];
+		if (!iqs269_events[i].dir_up)
+			dir_mask = ~dir_mask;
+
+		state = flags.states[iqs269_events[i].st_offs] & dir_mask;
+
+		sw_code = iqs269->switches[i].code;
+
+		for (j = 0; j < IQS269_NUM_CH; j++) {
+			keycode = iqs269->keycode[i * IQS269_NUM_CH + j];
+
+			switch (j) {
+			case IQS269_CHx_HALL_ACTIVE:
+				if (iqs269->hall_enable &&
+				    iqs269->switches[i].enabled)
+					input_report_switch(iqs269->keypad,
+							    sw_code,
+							    state & BIT(j));
+
+				/* fall through */
+
+			case IQS269_CHx_HALL_INACTIVE:
+				if (iqs269->hall_enable)
+					continue;
+
+				/* fall through */
+
+			default:
+				input_report_key(iqs269->keypad, keycode,
+						 state & BIT(j));
+			}
+		}
+	}
+
+	input_sync(iqs269->keypad);
+
+	return 0;
+}
+
+static irqreturn_t iqs269_irq(int irq, void *context)
+{
+	struct iqs269_private *iqs269 = context;
+
+	if (iqs269_report(iqs269))
+		return IRQ_NONE;
+
+	/*
+	 * The device does not deassert its interrupt (RDY) pin until shortly
+	 * after receiving an I2C stop condition; the following delay ensures
+	 * the interrupt handler does not return before this time.
+	 */
+	iqs269_irq_wait();
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t counts_show(struct device *dev,
+			   struct device_attribute *attr, char *buf)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	struct i2c_client *client = iqs269->client;
+	__le16 counts;
+	int error;
+
+	if (!iqs269->ati_current || iqs269->hall_enable)
+		return -EPERM;
+
+	/*
+	 * Unsolicited I2C communication prompts the device to assert its RDY
+	 * pin, so disable the interrupt line until the operation is finished
+	 * and RDY has been deasserted.
+	 */
+	disable_irq(client->irq);
+
+	error = regmap_raw_read(iqs269->regmap,
+				IQS269_CHx_COUNTS + iqs269->ch_num * 2,
+				&counts, sizeof(counts));
+
+	iqs269_irq_wait();
+	enable_irq(client->irq);
+
+	if (error)
+		return error;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", le16_to_cpu(counts));
+}
+
+static ssize_t hall_bin_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	struct i2c_client *client = iqs269->client;
+	unsigned int val;
+	int error;
+
+	disable_irq(client->irq);
+
+	error = regmap_read(iqs269->regmap, IQS269_CAL_DATA_A, &val);
+
+	iqs269_irq_wait();
+	enable_irq(client->irq);
+
+	if (error)
+		return error;
+
+	switch (iqs269->ch_reg[IQS269_CHx_HALL_ACTIVE].rx_enable &
+		iqs269->ch_reg[IQS269_CHx_HALL_INACTIVE].rx_enable) {
+	case IQS269_HALL_PAD_R:
+		val &= IQS269_CAL_DATA_A_HALL_BIN_R_MASK;
+		val >>= IQS269_CAL_DATA_A_HALL_BIN_R_SHIFT;
+		break;
+
+	case IQS269_HALL_PAD_L:
+		val &= IQS269_CAL_DATA_A_HALL_BIN_L_MASK;
+		val >>= IQS269_CAL_DATA_A_HALL_BIN_L_SHIFT;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t hall_enable_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", iqs269->hall_enable);
+}
+
+static ssize_t hall_enable_store(struct device *dev,
+				 struct device_attribute *attr, const char *buf,
+				 size_t count)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	mutex_lock(&iqs269->lock);
+
+	iqs269->hall_enable = val;
+	iqs269->ati_current = false;
+
+	mutex_unlock(&iqs269->lock);
+
+	return count;
+}
+
+static ssize_t ch_number_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", iqs269->ch_num);
+}
+
+static ssize_t ch_number_store(struct device *dev,
+			       struct device_attribute *attr, const char *buf,
+			       size_t count)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	if (val >= IQS269_NUM_CH)
+		return -EINVAL;
+
+	iqs269->ch_num = val;
+
+	return count;
+}
+
+static ssize_t rx_enable_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 iqs269->ch_reg[iqs269->ch_num].rx_enable);
+}
+
+static ssize_t rx_enable_store(struct device *dev,
+			       struct device_attribute *attr, const char *buf,
+			       size_t count)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	if (val > 0xFF)
+		return -EINVAL;
+
+	mutex_lock(&iqs269->lock);
+
+	iqs269->ch_reg[iqs269->ch_num].rx_enable = val;
+	iqs269->ati_current = false;
+
+	mutex_unlock(&iqs269->lock);
+
+	return count;
+}
+
+static ssize_t ati_mode_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = iqs269_ati_mode_get(iqs269, iqs269->ch_num, &val);
+	if (error)
+		return error;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t ati_mode_store(struct device *dev,
+			      struct device_attribute *attr, const char *buf,
+			      size_t count)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	error = iqs269_ati_mode_set(iqs269, iqs269->ch_num, val);
+	if (error)
+		return error;
+
+	return count;
+}
+
+static ssize_t ati_base_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = iqs269_ati_base_get(iqs269, iqs269->ch_num, &val);
+	if (error)
+		return error;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t ati_base_store(struct device *dev,
+			      struct device_attribute *attr, const char *buf,
+			      size_t count)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	error = iqs269_ati_base_set(iqs269, iqs269->ch_num, val);
+	if (error)
+		return error;
+
+	return count;
+}
+
+static ssize_t ati_target_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = iqs269_ati_target_get(iqs269, iqs269->ch_num, &val);
+	if (error)
+		return error;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t ati_target_store(struct device *dev,
+				struct device_attribute *attr, const char *buf,
+				size_t count)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	error = iqs269_ati_target_set(iqs269, iqs269->ch_num, val);
+	if (error)
+		return error;
+
+	return count;
+}
+
+static ssize_t ati_trigger_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", iqs269->ati_current);
+}
+
+static ssize_t ati_trigger_store(struct device *dev,
+				 struct device_attribute *attr, const char *buf,
+				 size_t count)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	struct i2c_client *client = iqs269->client;
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	if (!val)
+		return count;
+
+	disable_irq(client->irq);
+
+	error = iqs269_dev_init(iqs269);
+
+	iqs269_irq_wait();
+	enable_irq(client->irq);
+
+	if (error)
+		return error;
+
+	return count;
+}
+
+static DEVICE_ATTR_RO(counts);
+static DEVICE_ATTR_RO(hall_bin);
+static DEVICE_ATTR_RW(hall_enable);
+static DEVICE_ATTR_RW(ch_number);
+static DEVICE_ATTR_RW(rx_enable);
+static DEVICE_ATTR_RW(ati_mode);
+static DEVICE_ATTR_RW(ati_base);
+static DEVICE_ATTR_RW(ati_target);
+static DEVICE_ATTR_RW(ati_trigger);
+
+static struct attribute *iqs269_attrs[] = {
+	&dev_attr_counts.attr,
+	&dev_attr_hall_bin.attr,
+	&dev_attr_hall_enable.attr,
+	&dev_attr_ch_number.attr,
+	&dev_attr_rx_enable.attr,
+	&dev_attr_ati_mode.attr,
+	&dev_attr_ati_base.attr,
+	&dev_attr_ati_target.attr,
+	&dev_attr_ati_trigger.attr,
+	NULL,
+};
+
+static const struct attribute_group iqs269_attr_group = {
+	.attrs = iqs269_attrs,
+};
+
+static const struct regmap_config iqs269_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 16,
+	.max_register = IQS269_MAX_REG,
+};
+
+static int iqs269_probe(struct i2c_client *client)
+{
+	struct iqs269_ver_info ver_info;
+	struct iqs269_private *iqs269;
+	int error;
+
+	iqs269 = devm_kzalloc(&client->dev, sizeof(*iqs269), GFP_KERNEL);
+	if (!iqs269)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, iqs269);
+	iqs269->client = client;
+
+	iqs269->regmap = devm_regmap_init_i2c(client, &iqs269_regmap_config);
+	if (IS_ERR(iqs269->regmap)) {
+		error = PTR_ERR(iqs269->regmap);
+		dev_err(&client->dev, "Failed to initialize register map: %d\n",
+			error);
+		return error;
+	}
+
+	mutex_init(&iqs269->lock);
+
+	error = regmap_raw_read(iqs269->regmap, IQS269_VER_INFO, &ver_info,
+				sizeof(ver_info));
+	if (error)
+		return error;
+
+	if (ver_info.prod_num != IQS269_VER_INFO_PROD_NUM) {
+		dev_err(&client->dev, "Unrecognized product number: 0x%02X\n",
+			ver_info.prod_num);
+		return -EINVAL;
+	}
+
+	error = iqs269_parse_prop(iqs269);
+	if (error)
+		return error;
+
+	error = iqs269_dev_init(iqs269);
+	if (error) {
+		dev_err(&client->dev, "Failed to initialize device: %d\n",
+			error);
+		return error;
+	}
+
+	error = iqs269_input_init(iqs269);
+	if (error)
+		return error;
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, iqs269_irq, IRQF_ONESHOT,
+					  client->name, iqs269);
+	if (error) {
+		dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
+		return error;
+	}
+
+	error = devm_device_add_group(&client->dev, &iqs269_attr_group);
+	if (error)
+		dev_err(&client->dev, "Failed to add attributes: %d\n", error);
+
+	return error;
+}
+
+static int __maybe_unused iqs269_suspend(struct device *dev)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	struct i2c_client *client = iqs269->client;
+	unsigned int val;
+	int error;
+
+	if (!iqs269->suspend_mode)
+		return 0;
+
+	disable_irq(client->irq);
+
+	/*
+	 * Automatic power mode switching must be disabled before the device is
+	 * forced into any particular power mode. In this case, the device will
+	 * transition into normal-power mode.
+	 */
+	error = regmap_update_bits(iqs269->regmap, IQS269_SYS_SETTINGS,
+				   IQS269_SYS_SETTINGS_DIS_AUTO, ~0);
+	if (error)
+		goto err_irq;
+
+	/*
+	 * The following check ensures the device has completed its transition
+	 * into normal-power mode before a manual mode switch is performed.
+	 */
+	error = regmap_read_poll_timeout(iqs269->regmap, IQS269_SYS_FLAGS, val,
+					!(val & IQS269_SYS_FLAGS_PWR_MODE_MASK),
+					 IQS269_PWR_MODE_POLL_SLEEP_US,
+					 IQS269_PWR_MODE_POLL_TIMEOUT_US);
+	if (error)
+		goto err_irq;
+
+	error = regmap_update_bits(iqs269->regmap, IQS269_SYS_SETTINGS,
+				   IQS269_SYS_SETTINGS_PWR_MODE_MASK,
+				   iqs269->suspend_mode <<
+				   IQS269_SYS_SETTINGS_PWR_MODE_SHIFT);
+	if (error)
+		goto err_irq;
+
+	/*
+	 * This last check ensures the device has completed its transition into
+	 * the desired power mode to prevent any spurious interrupts from being
+	 * triggered after iqs269_suspend has already returned.
+	 */
+	error = regmap_read_poll_timeout(iqs269->regmap, IQS269_SYS_FLAGS, val,
+					 (val & IQS269_SYS_FLAGS_PWR_MODE_MASK)
+					 == (iqs269->suspend_mode <<
+					     IQS269_SYS_FLAGS_PWR_MODE_SHIFT),
+					 IQS269_PWR_MODE_POLL_SLEEP_US,
+					 IQS269_PWR_MODE_POLL_TIMEOUT_US);
+
+err_irq:
+	iqs269_irq_wait();
+	enable_irq(client->irq);
+
+	return error;
+}
+
+static int __maybe_unused iqs269_resume(struct device *dev)
+{
+	struct iqs269_private *iqs269 = dev_get_drvdata(dev);
+	struct i2c_client *client = iqs269->client;
+	unsigned int val;
+	int error;
+
+	if (!iqs269->suspend_mode)
+		return 0;
+
+	disable_irq(client->irq);
+
+	error = regmap_update_bits(iqs269->regmap, IQS269_SYS_SETTINGS,
+				   IQS269_SYS_SETTINGS_PWR_MODE_MASK, 0);
+	if (error)
+		goto err_irq;
+
+	/*
+	 * This check ensures the device has returned to normal-power mode
+	 * before automatic power mode switching is re-enabled.
+	 */
+	error = regmap_read_poll_timeout(iqs269->regmap, IQS269_SYS_FLAGS, val,
+					!(val & IQS269_SYS_FLAGS_PWR_MODE_MASK),
+					 IQS269_PWR_MODE_POLL_SLEEP_US,
+					 IQS269_PWR_MODE_POLL_TIMEOUT_US);
+	if (error)
+		goto err_irq;
+
+	error = regmap_update_bits(iqs269->regmap, IQS269_SYS_SETTINGS,
+				   IQS269_SYS_SETTINGS_DIS_AUTO, 0);
+	if (error)
+		goto err_irq;
+
+	/*
+	 * This step reports any events that may have been "swallowed" as a
+	 * result of polling PWR_MODE (which automatically acknowledges any
+	 * pending interrupts).
+	 */
+	error = iqs269_report(iqs269);
+
+err_irq:
+	iqs269_irq_wait();
+	enable_irq(client->irq);
+
+	return error;
+}
+
+static SIMPLE_DEV_PM_OPS(iqs269_pm, iqs269_suspend, iqs269_resume);
+
+static const struct of_device_id iqs269_of_match[] = {
+	{ .compatible = "azoteq,iqs269a" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, iqs269_of_match);
+
+static struct i2c_driver iqs269_i2c_driver = {
+	.driver = {
+		.name = "iqs269a",
+		.of_match_table = iqs269_of_match,
+		.pm = &iqs269_pm,
+	},
+	.probe_new = iqs269_probe,
+};
+module_i2c_driver(iqs269_i2c_driver);
+
+MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
+MODULE_DESCRIPTION("Azoteq IQS269A Capacitive Touch Controller");
+MODULE_LICENSE("GPL");
-- 
2.7.4


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

* Re: [PATCH 2/2] input: misc: Add support for Azoteq IQS269A
  2020-04-19 23:47 ` [PATCH 2/2] input: misc: Add support " Jeff LaBundy
@ 2020-04-20 17:09   ` Dmitry Torokhov
  2020-05-17 21:21     ` Jeff LaBundy
  0 siblings, 1 reply; 7+ messages in thread
From: Dmitry Torokhov @ 2020-04-20 17:09 UTC (permalink / raw)
  To: Jeff LaBundy; +Cc: robh+dt, linux-input, devicetree

On Sun, Apr 19, 2020 at 06:47:48PM -0500, Jeff LaBundy wrote:
> This patch adds support for the Azoteq IQS269A capacitive touch
> controller.
> 
> Signed-off-by: Jeff LaBundy <jeff@labundy.com>

This looks good, so just need to get Rob's take on the binding...

Thanks.

-- 
Dmitry

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

* Re: [PATCH 1/2] dt-bindings: input: Add bindings for Azoteq IQS269A
  2020-04-19 23:47 [PATCH 1/2] dt-bindings: input: Add bindings for Azoteq IQS269A Jeff LaBundy
  2020-04-19 23:47 ` [PATCH 2/2] input: misc: Add support " Jeff LaBundy
@ 2020-04-30 15:11 ` Rob Herring
  2020-05-01  1:23   ` Jeff LaBundy
  1 sibling, 1 reply; 7+ messages in thread
From: Rob Herring @ 2020-04-30 15:11 UTC (permalink / raw)
  To: Jeff LaBundy; +Cc: dmitry.torokhov, linux-input, devicetree

On Sun, Apr 19, 2020 at 06:47:47PM -0500, Jeff LaBundy wrote:
> This patch adds device tree bindings for the Azoteq IQS269A
> capacitive touch controller.
> 
> Signed-off-by: Jeff LaBundy <jeff@labundy.com>
> ---
>  .../devicetree/bindings/input/iqs269a.yaml         | 591 +++++++++++++++++++++
>  1 file changed, 591 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/iqs269a.yaml

Kind of a lot of properties compared to other devices. Why so many? That 
said, nothing looks to be obviously something that doesn't belong in DT.

No interdependencies between properties? If there are, use 
'dependencies'.

One other comment below.

> 
> diff --git a/Documentation/devicetree/bindings/input/iqs269a.yaml b/Documentation/devicetree/bindings/input/iqs269a.yaml
> new file mode 100644
> index 0000000..7f4fcdc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/iqs269a.yaml
> @@ -0,0 +1,591 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/input/iqs269a.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Azoteq IQS269A Capacitive Touch Controller
> +
> +maintainers:
> +  - Jeff LaBundy <jeff@labundy.com>
> +
> +description: |
> +  The Azoteq IQS269A is an 8-channel capacitive touch controller that features
> +  additional Hall-effect and inductive sensing capabilities.
> +
> +  Link to datasheet: https://www.azoteq.com/
> +
> +properties:
> +  compatible:
> +    const: azoteq,iqs269a
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  "#address-cells":
> +    const: 1
> +
> +  "#size-cells":
> +    const: 0
> +
> +  azoteq,hall-enable:
> +    type: boolean
> +    description:
> +      Enables Hall-effect sensing on channels 6 and 7. In this case, keycodes
> +      assigned to channel 6 are ignored and keycodes assigned to channel 7 are
> +      interpreted as switch codes. Refer to the datasheet for requirements im-
> +      posed on channels 6 and 7 by Hall-effect sensing.
> +
> +  azoteq,suspend-mode:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - enum: [0, 1, 2, 3]
> +        default: 0
> +    description: |
> +      Specifies the power mode during suspend as follows:
> +      0: Automatic (same as normal runtime, i.e. suspend/resume disabled)
> +      1: Low power (all sensing at a reduced reporting rate)
> +      2: Ultra-low power (channel 0 proximity sensing)
> +      3: Halt (no sensing)
> +
> +  azoteq,clk-div:
> +    type: boolean
> +    description: Divides the device's core clock by a factor of 4.
> +
> +  azoteq,ulp-update:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 0
> +        maximum: 7
> +        default: 3
> +    description: Specifies the ultra-low-power mode update rate.
> +
> +  azoteq,reseed-offset:
> +    type: boolean
> +    description:
> +      Applies an 8-count offset to all long-term averages upon either ATI or
> +      reseed events.
> +
> +  azoteq,filt-str-lp-lta:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - enum: [0, 1, 2, 3]
> +        default: 0
> +    description:
> +      Specifies the long-term average filter strength during low-power mode.
> +
> +  azoteq,filt-str-lp-cnt:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - enum: [0, 1, 2, 3]
> +        default: 0
> +    description:
> +      Specifies the raw count filter strength during low-power mode.
> +
> +  azoteq,filt-str-np-lta:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - enum: [0, 1, 2, 3]
> +        default: 0
> +    description:
> +      Specifies the long-term average filter strength during normal-power mode.
> +
> +  azoteq,filt-str-np-cnt:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - enum: [0, 1, 2, 3]
> +        default: 0
> +    description:
> +      Specifies the raw count filter strength during normal-power mode.
> +
> +  azoteq,rate-np-ms:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32

With a unit suffix, you can drop the type $ref.

> +      - minimum: 0
> +        maximum: 255
> +        default: 16
> +    description: Specifies the report rate (in ms) during normal-power mode.
> +
> +  azoteq,rate-lp-ms:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 0
> +        maximum: 255
> +        default: 160
> +    description: Specifies the report rate (in ms) during low-power mode.
> +
> +  azoteq,rate-ulp-ms:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - multipleOf: 16
> +        minimum: 0
> +        maximum: 4080
> +        default: 160
> +    description: Specifies the report rate (in ms) during ultra-low-power mode.
> +
> +  azoteq,timeout-pwr-ms:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - multipleOf: 512
> +        minimum: 0
> +        maximum: 130560
> +        default: 2560
> +    description:
> +      Specifies the length of time (in ms) to wait for an event during normal-
> +      power mode before transitioning to low-power mode.
> +
> +  azoteq,timeout-lta-ms:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - multipleOf: 512
> +        minimum: 0
> +        maximum: 130560
> +        default: 32768
> +    description:
> +      Specifies the length of time (in ms) to wait before resetting the long-
> +      term average of all channels. Specify the maximum timeout to disable it
> +      altogether.
> +
> +  azoteq,ati-band-disable:
> +    type: boolean
> +    description: Disables the ATI band check.
> +
> +  azoteq,ati-lp-only:
> +    type: boolean
> +    description: Limits automatic ATI to low-power mode.
> +
> +  azoteq,ati-band-tighten:
> +    type: boolean
> +    description: Tightens the ATI band from 1/8 to 1/16 of the desired target.
> +
> +  azoteq,filt-disable:
> +    type: boolean
> +    description: Disables all raw count filtering.
> +
> +  azoteq,gpio3-select:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 0
> +        maximum: 7
> +        default: 0
> +    description:
> +      Selects the channel for which the GPIO3 pin represents touch state.
> +
> +  azoteq,dual-direction:
> +    type: boolean
> +    description:
> +      Specifies that long-term averages are to freeze in the presence of either
> +      increasing or decreasing counts, thereby permitting events to be reported
> +      in either direction.
> +
> +  azoteq,tx-freq:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - enum: [0, 1, 2, 3]
> +        default: 0
> +    description: |
> +      Specifies the inductive sensing excitation frequency as follows (paren-
> +      thesized numbers represent the frequency if 'azoteq,clk-div' is present):
> +      0: 16 MHz (4 MHz)
> +      1: 8 MHz (2 MHz)
> +      2: 4 MHz (1 MHz)
> +      3: 2 MHz (500 kHz)
> +
> +  azoteq,global-cap-increase:
> +    type: boolean
> +    description: Increases the global capacitance adder from 0.5 pF to 1.5 pF.
> +
> +  azoteq,reseed-select:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - enum: [0, 1, 2, 3]
> +        default: 0
> +    description: |
> +      Specifies the event(s) that prompt the device to reseed (i.e. reset the
> +      long-term average) of an associated channel as follows:
> +      0: None
> +      1: Proximity
> +      2: Proximity or touch
> +      3: Proximity, touch or deep touch
> +
> +  azoteq,tracking-enable:
> +    type: boolean
> +    description:
> +      Enables all associated channels to track their respective reference
> +      channels.
> +
> +  azoteq,filt-str-slider:
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - enum: [0, 1, 2, 3]
> +        default: 1
> +    description: Specifies the slider coordinate filter strength.
> +
> +patternProperties:
> +  "^channel@[0-7]$":
> +    type: object
> +    description:
> +      Represents a single sensing channel. A channel is active if defined and
> +      inactive otherwise.
> +
> +    properties:
> +      reg:
> +        minimum: 0
> +        maximum: 7
> +        description: Index of the channel.
> +
> +      azoteq,reseed-disable:
> +        type: boolean
> +        description:
> +          Prevents the channel from being reseeded if the long-term average
> +          timeout (defined in 'azoteq,timeout-lta') expires.
> +
> +      azoteq,blocking-enable:
> +        type: boolean
> +        description: Specifies that the channel is a blocking channel.
> +
> +      azoteq,slider0-select:
> +        type: boolean
> +        description: Specifies that the channel participates in slider 0.
> +
> +      azoteq,slider1-select:
> +        type: boolean
> +        description: Specifies that the channel participates in slider 1.
> +
> +      azoteq,rx-enable:
> +        allOf:
> +          - $ref: /schemas/types.yaml#/definitions/uint32-array
> +          - minItems: 1
> +            maxItems: 8
> +            items:
> +              minimum: 0
> +              maximum: 7
> +        description:
> +          Specifies the CRX pin(s) associated with the channel. By default, only
> +          the CRX pin corresponding to the channel's index is enabled (e.g. CRX0
> +          for channel 0).
> +
> +      azoteq,tx-enable:
> +        allOf:
> +          - $ref: /schemas/types.yaml#/definitions/uint32-array
> +          - minItems: 1
> +            maxItems: 8
> +            items:
> +              minimum: 0
> +              maximum: 7
> +            default: [0, 1, 2, 3, 4, 5, 6, 7]
> +        description: Specifies the TX pin(s) associated with the channel.
> +
> +      azoteq,meas-cap-decrease:
> +        type: boolean
> +        description:
> +          Decreases the internal measurement capacitance from 60 pF to 15 pF.
> +
> +      azoteq,rx-float-inactive:
> +        type: boolean
> +        description: Floats any inactive CRX pins instead of grounding them.
> +
> +      azoteq,local-cap-size:
> +        allOf:
> +          - $ref: /schemas/types.yaml#/definitions/uint32
> +          - enum: [0, 1, 2]
> +            default: 0
> +        description: |
> +          Specifies the capacitance to be added to the channel as follows:
> +          0: None
> +          1: Global adder (based on 'azoteq,global-cap-increase')
> +          2: Global adder + 0.5 pF
> +
> +      azoteq,invert-enable:
> +        type: boolean
> +        description:
> +          Inverts the polarity of the states reported for proximity, touch and
> +          deep-touch events relative to their respective thresholds.
> +
> +      azoteq,proj-bias:
> +        allOf:
> +          - $ref: /schemas/types.yaml#/definitions/uint32
> +          - enum: [0, 1, 2, 3]
> +            default: 2
> +        description: |
> +          Specifies the bias current applied during projected-capacitance
> +          sensing as follows:
> +          0: 2.5 uA
> +          1: 5 uA
> +          2: 10 uA
> +          3: 20 uA
> +
> +      azoteq,sense-mode:
> +        allOf:
> +          - $ref: /schemas/types.yaml#/definitions/uint32
> +          - enum: [0, 1, 9, 14, 15]
> +            default: 0
> +        description: |
> +          Specifies the channel's sensing mode as follows:
> +          0:  Self capacitance
> +          1:  Projected capacitance
> +          9:  Self or mutual inductance
> +          14: Hall effect
> +          15: Temperature
> +
> +      azoteq,sense-freq:
> +        allOf:
> +          - $ref: /schemas/types.yaml#/definitions/uint32
> +          - enum: [0, 1, 2, 3]
> +            default: 1
> +        description: |
> +          Specifies the channel's sensing frequency as follows (parenthesized
> +          numbers represent the frequency if 'azoteq,clk-div' is present):
> +          0: 4 MHz (1 MHz)
> +          1: 2 MHz (500 kHz)
> +          2: 1 MHz (250 kHz)
> +          3: 500 kHz (125 kHz)
> +
> +      azoteq,static-enable:
> +        type: boolean
> +        description: Enables the static front-end for the channel.
> +
> +      azoteq,ati-mode:
> +        allOf:
> +          - $ref: /schemas/types.yaml#/definitions/uint32
> +          - enum: [0, 1, 2, 3]
> +            default: 3
> +        description: |
> +          Specifies the channel's ATI mode as follows:
> +          0: Disabled
> +          1: Semi-partial
> +          2: Partial
> +          3: Full
> +
> +      azoteq,ati-base:
> +        allOf:
> +          - $ref: /schemas/types.yaml#/definitions/uint32
> +          - enum: [75, 100, 150, 200]
> +            default: 100
> +        description: Specifies the channel's ATI base.
> +
> +      azoteq,ati-target:
> +        allOf:
> +          - $ref: /schemas/types.yaml#/definitions/uint32
> +          - multipleOf: 32
> +            minimum: 0
> +            maximum: 2016
> +            default: 512
> +        description: Specifies the channel's ATI target.
> +
> +      azoteq,assoc-select:
> +        allOf:
> +          - $ref: /schemas/types.yaml#/definitions/uint32-array
> +          - minItems: 1
> +            maxItems: 8
> +            items:
> +              minimum: 0
> +              maximum: 7
> +        description:
> +          Specifies the associated channels for which the channel serves as a
> +          reference channel. By default, no channels are selected.
> +
> +      azoteq,assoc-weight:
> +        allOf:
> +          - $ref: /schemas/types.yaml#/definitions/uint32
> +          - minimum: 0
> +            maximum: 255
> +            default: 0
> +        description:
> +          Specifies the channel's impact weight if it acts as an associated
> +          channel (0 = 0% impact, 255 = 200% impact).
> +
> +    patternProperties:
> +      "^event-prox(-alt)?$":
> +        type: object
> +        description:
> +          Represents a proximity event reported by the channel in response to
> +          a decrease in counts. Node names suffixed with '-alt' instead corre-
> +          spond to an increase in counts.
> +
> +          By default, the long-term average tracks an increase in counts such
> +          that only events corresponding to a decrease in counts are reported
> +          (refer to the datasheet for more information).
> +
> +          Specify 'azoteq,dual-direction' to freeze the long-term average when
> +          the counts increase or decrease such that events of either direction
> +          can be reported. Alternatively, specify 'azoteq,invert-enable' to in-
> +          vert the polarity of the states reported by the channel.
> +
> +          Complementary events (e.g. event-touch and event-touch-alt) can both
> +          be present and specify different key or switch codes, but not differ-
> +          ent thresholds or hysteresis (if applicable).
> +
> +        properties:
> +          azoteq,thresh:
> +            allOf:
> +              - $ref: /schemas/types.yaml#/definitions/uint32
> +              - minimum: 0
> +                maximum: 255
> +                default: 10
> +            description: Specifies the threshold for the event.
> +
> +          linux,code:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            description: Numeric key or switch code associated with the event.
> +
> +        additionalProperties: false
> +
> +      "^event-touch(-alt)?$":
> +        type: object
> +        description: Represents a touch event reported by the channel.
> +
> +        properties:
> +          azoteq,thresh:
> +            allOf:
> +              - $ref: /schemas/types.yaml#/definitions/uint32
> +              - minimum: 0
> +                maximum: 255
> +                default: 8
> +            description: Specifies the threshold for the event.
> +
> +          azoteq,hyst:
> +            allOf:
> +              - $ref: /schemas/types.yaml#/definitions/uint32
> +              - minimum: 0
> +                maximum: 15
> +                default: 4
> +            description: Specifies the hysteresis for the event.
> +
> +          linux,code:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            description: Numeric key or switch code associated with the event.
> +
> +        additionalProperties: false
> +
> +      "^event-deep(-alt)?$":
> +        type: object
> +        description: Represents a deep-touch event reported by the channel.
> +
> +        properties:
> +          azoteq,thresh:
> +            allOf:
> +              - $ref: /schemas/types.yaml#/definitions/uint32
> +              - minimum: 0
> +                maximum: 255
> +                default: 26
> +            description: Specifies the threshold for the event.
> +
> +          azoteq,hyst:
> +            allOf:
> +              - $ref: /schemas/types.yaml#/definitions/uint32
> +              - minimum: 0
> +                maximum: 15
> +                default: 0
> +            description: Specifies the hysteresis for the event.
> +
> +          linux,code:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            description: Numeric key or switch code associated with the event.
> +
> +        additionalProperties: false
> +
> +    required:
> +      - reg
> +
> +    additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - "#address-cells"
> +  - "#size-cells"
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/input/input.h>
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +
> +    i2c {
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +
> +            iqs269a@44 {
> +                    #address-cells = <1>;
> +                    #size-cells = <0>;
> +
> +                    compatible = "azoteq,iqs269a";
> +                    reg = <0x44>;
> +                    interrupt-parent = <&gpio>;
> +                    interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
> +
> +                    azoteq,hall-enable;
> +                    azoteq,suspend-mode = <2>;
> +
> +                    channel@0 {
> +                            reg = <0x0>;
> +
> +                            event-prox {
> +                                    linux,code = <KEY_POWER>;
> +                            };
> +                    };
> +
> +                    channel@1 {
> +                            reg = <0x1>;
> +                            azoteq,slider0-select;
> +                    };
> +
> +                    channel@2 {
> +                            reg = <0x2>;
> +                            azoteq,slider0-select;
> +                    };
> +
> +                    channel@3 {
> +                            reg = <0x3>;
> +                            azoteq,slider0-select;
> +                    };
> +
> +                    channel@4 {
> +                            reg = <0x4>;
> +                            azoteq,slider0-select;
> +                    };
> +
> +                    channel@5 {
> +                            reg = <0x5>;
> +                            azoteq,slider0-select;
> +                    };
> +
> +                    channel@6 {
> +                            reg = <0x6>;
> +                            azoteq,invert-enable;
> +                            azoteq,static-enable;
> +                            azoteq,reseed-disable;
> +                            azoteq,rx-enable = <0>;
> +                            azoteq,sense-freq = <0x0>;
> +                            azoteq,sense-mode = <0xE>;
> +                            azoteq,ati-mode = <0x0>;
> +                            azoteq,ati-base = <200>;
> +                            azoteq,ati-target = <320>;
> +                    };
> +
> +                    channel@7 {
> +                            reg = <0x7>;
> +                            azoteq,invert-enable;
> +                            azoteq,static-enable;
> +                            azoteq,reseed-disable;
> +                            azoteq,rx-enable = <0>, <6>;
> +                            azoteq,sense-freq = <0x0>;
> +                            azoteq,sense-mode = <0xE>;
> +                            azoteq,ati-mode = <0x3>;
> +                            azoteq,ati-base = <200>;
> +                            azoteq,ati-target = <320>;
> +
> +                            event-touch {
> +                                    linux,code = <SW_LID>;
> +                            };
> +                    };
> +            };
> +    };
> +
> +...
> -- 
> 2.7.4
> 

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

* Re: [PATCH 1/2] dt-bindings: input: Add bindings for Azoteq IQS269A
  2020-04-30 15:11 ` [PATCH 1/2] dt-bindings: input: Add bindings " Rob Herring
@ 2020-05-01  1:23   ` Jeff LaBundy
  2020-05-01 12:51     ` Rob Herring
  0 siblings, 1 reply; 7+ messages in thread
From: Jeff LaBundy @ 2020-05-01  1:23 UTC (permalink / raw)
  To: Rob Herring; +Cc: dmitry.torokhov, linux-input, devicetree

Hi Rob,

Thank you for your thorough review.

On Thu, Apr 30, 2020 at 10:11:08AM -0500, Rob Herring wrote:
> On Sun, Apr 19, 2020 at 06:47:47PM -0500, Jeff LaBundy wrote:
> > This patch adds device tree bindings for the Azoteq IQS269A
> > capacitive touch controller.
> > 
> > Signed-off-by: Jeff LaBundy <jeff@labundy.com>
> > ---
> >  .../devicetree/bindings/input/iqs269a.yaml         | 591 +++++++++++++++++++++
> >  1 file changed, 591 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/input/iqs269a.yaml
> 
> Kind of a lot of properties compared to other devices. Why so many? That 
> said, nothing looks to be obviously something that doesn't belong in DT.

I don't disagree; this device simply has a lot of knobs for accommodating
multiple sensing modes across different applications. Once I added support
for the ones I expected to be most commonly used, however, I didn't see a
reason to exclude the remaining minority and risk having to add something
later.

> 
> No interdependencies between properties? If there are, use 
> 'dependencies'.

Strictly speaking, no; each property can be specified independently of any
other property and the device's registers will be updated accordingly.

That being said, a couple of properties do impose restrictions on others
within specific channels for certain applications. It wasn't clear if/how
'dependencies' could describe these conditional relationships, so I opted
to include a note in the descriptions where applicable.

[...]

> > +
> > +  azoteq,rate-np-ms:
> > +    allOf:
> > +      - $ref: /schemas/types.yaml#/definitions/uint32
> 
> With a unit suffix, you can drop the type $ref.

Sure thing, thank you for catching these. Once I remove $ref in these cases,
is an 'allOf' still required above the remaining minimum/maximum/etc.?

> 
> > +      - minimum: 0
> > +        maximum: 255
> > +        default: 16
> > +    description: Specifies the report rate (in ms) during normal-power mode.
> > +

On a related note, should all items under an 'allOf' be preceded by a hyphen?

Kind regards,
Jeff LaBundy

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

* Re: [PATCH 1/2] dt-bindings: input: Add bindings for Azoteq IQS269A
  2020-05-01  1:23   ` Jeff LaBundy
@ 2020-05-01 12:51     ` Rob Herring
  0 siblings, 0 replies; 7+ messages in thread
From: Rob Herring @ 2020-05-01 12:51 UTC (permalink / raw)
  To: Jeff LaBundy; +Cc: Dmitry Torokhov, Linux Input, devicetree

On Thu, Apr 30, 2020 at 8:23 PM Jeff LaBundy <jeff@labundy.com> wrote:
>
> Hi Rob,
>
> Thank you for your thorough review.
>
> On Thu, Apr 30, 2020 at 10:11:08AM -0500, Rob Herring wrote:
> > On Sun, Apr 19, 2020 at 06:47:47PM -0500, Jeff LaBundy wrote:
> > > This patch adds device tree bindings for the Azoteq IQS269A
> > > capacitive touch controller.
> > >
> > > Signed-off-by: Jeff LaBundy <jeff@labundy.com>
> > > ---
> > >  .../devicetree/bindings/input/iqs269a.yaml         | 591 +++++++++++++++++++++
> > >  1 file changed, 591 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/input/iqs269a.yaml
> >
> > Kind of a lot of properties compared to other devices. Why so many? That
> > said, nothing looks to be obviously something that doesn't belong in DT.
>
> I don't disagree; this device simply has a lot of knobs for accommodating
> multiple sensing modes across different applications. Once I added support
> for the ones I expected to be most commonly used, however, I didn't see a
> reason to exclude the remaining minority and risk having to add something
> later.
>
> >
> > No interdependencies between properties? If there are, use
> > 'dependencies'.
>
> Strictly speaking, no; each property can be specified independently of any
> other property and the device's registers will be updated accordingly.
>
> That being said, a couple of properties do impose restrictions on others
> within specific channels for certain applications. It wasn't clear if/how
> 'dependencies' could describe these conditional relationships, so I opted
> to include a note in the descriptions where applicable.
>
> [...]
>
> > > +
> > > +  azoteq,rate-np-ms:
> > > +    allOf:
> > > +      - $ref: /schemas/types.yaml#/definitions/uint32
> >
> > With a unit suffix, you can drop the type $ref.
>
> Sure thing, thank you for catching these. Once I remove $ref in these cases,
> is an 'allOf' still required above the remaining minimum/maximum/etc.?

No. Actually, you can now drop 'allOf' even with $ref. With
json-schema draft 2019.9, the behavior has changed such that $ref can
be mixed with other schema. So now that most everyone has learned the
old way, we're switching. You can use either form, but without 'allOf'
you'll need dt-schema to be up to date (2020.4).

> > > +      - minimum: 0
> > > +        maximum: 255
> > > +        default: 16
> > > +    description: Specifies the report rate (in ms) during normal-power mode.
> > > +
>
> On a related note, should all items under an 'allOf' be preceded by a hyphen?

No. It's logically equivalent, but the error reporting is worse.

Rob

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

* Re: [PATCH 2/2] input: misc: Add support for Azoteq IQS269A
  2020-04-20 17:09   ` Dmitry Torokhov
@ 2020-05-17 21:21     ` Jeff LaBundy
  0 siblings, 0 replies; 7+ messages in thread
From: Jeff LaBundy @ 2020-05-17 21:21 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: robh+dt, linux-input, devicetree

Hi Dmitry,

On Mon, Apr 20, 2020 at 10:09:30AM -0700, Dmitry Torokhov wrote:
> On Sun, Apr 19, 2020 at 06:47:48PM -0500, Jeff LaBundy wrote:
> > This patch adds support for the Azoteq IQS269A capacitive touch
> > controller.
> > 
> > Signed-off-by: Jeff LaBundy <jeff@labundy.com>
> 
> This looks good, so just need to get Rob's take on the binding...

Thank you for the quick turn-around. Last week Rob reviewed the
binding for v2 [1]; the driver has not changed since your first
review. Would you consider taking v2 for this cycle?

> 
> Thanks.
> 
> -- 
> Dmitry

[1] https://patchwork.kernel.org/patch/11522939/

Kind regards,
Jeff LaBundy

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

end of thread, other threads:[~2020-05-17 21:21 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-19 23:47 [PATCH 1/2] dt-bindings: input: Add bindings for Azoteq IQS269A Jeff LaBundy
2020-04-19 23:47 ` [PATCH 2/2] input: misc: Add support " Jeff LaBundy
2020-04-20 17:09   ` Dmitry Torokhov
2020-05-17 21:21     ` Jeff LaBundy
2020-04-30 15:11 ` [PATCH 1/2] dt-bindings: input: Add bindings " Rob Herring
2020-05-01  1:23   ` Jeff LaBundy
2020-05-01 12:51     ` Rob Herring

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.